Files
lmxopcua/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client/WonderwareHistorianDriverProbe.cs
T
Joseph Doherty c19d124e89 feat(drivers): TCP-connect IDriverProbe for all 9 driver types
Cheap-and-fast probe: open TCP socket to the configured endpoint,
close immediately. Surfaces SocketError on failure, latency on
success, "timed out" on caller cancel. Sufficient for the AdminUI
Test Connect "can we reach the host?" question. Richer protocol-
level probes (OPC UA session open, FOCAS handshake, gRPC ping)
are a documented follow-up. Each probe registered as
AddSingleton<IDriverProbe, X> in DriverFactoryBootstrap so they
flow through DI into AdminOperationsActor.

Historian.Wonderware returns a clean "TCP probe not applicable"
result because it communicates over a Windows named pipe, not TCP.
Also adds OpcUaClient + Historian.Wonderware.Client project
references to Host.csproj (both were missing from the driver
ItemGroup).
2026-05-28 10:53:42 -04:00

48 lines
2.1 KiB
C#

using System.Text.Json;
using System.Text.Json.Serialization;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client;
/// <summary>
/// Driver probe for the <see cref="WonderwareHistorianClientOptions"/>-shaped driver config.
/// The Wonderware Historian client communicates over a Windows named pipe (not a TCP socket),
/// so a cheap TCP-connect probe is not applicable for this transport. This probe always
/// returns a well-formed "not applicable" result so the AdminUI can display a meaningful
/// message instead of a red error. A full named-pipe connect + Hello-frame probe is a
/// documented follow-up.
/// </summary>
public sealed class WonderwareHistorianDriverProbe : IDriverProbe
{
private static readonly JsonSerializerOptions _opts = new()
{
PropertyNameCaseInsensitive = true,
UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip,
};
/// <inheritdoc />
public string DriverType => "Historian.Wonderware";
/// <inheritdoc />
public Task<DriverProbeResult> ProbeAsync(string configJson, TimeSpan timeout, CancellationToken ct)
{
// Validate the config JSON can at least be parsed — surface bad JSON immediately.
WonderwareHistorianClientOptions? opts;
try { opts = JsonSerializer.Deserialize<WonderwareHistorianClientOptions>(configJson, _opts); }
catch (Exception ex)
{
return Task.FromResult(new DriverProbeResult(false, $"Config JSON is invalid: {ex.Message}", null));
}
if (opts is null)
return Task.FromResult(new DriverProbeResult(false, "Config JSON deserialized to null.", null));
// The Wonderware Historian sidecar communicates over a Windows named pipe; there is no
// TCP endpoint to connect to. A full pipe connect + Hello-frame probe is a follow-up.
return Task.FromResult(new DriverProbeResult(
false,
"TCP probe not applicable for this transport — the Historian sidecar uses a named pipe. " +
"A full named-pipe probe is a documented follow-up.",
null));
}
}