feat(adminops): IDriverProbe + TestDriverConnect actor handler

- IDriverProbe abstraction in Core.Abstractions; one impl per driver
  type, resolved by DriverType string. Phase 7.3 + 7.4 add concrete
  probes for the 9 supported driver types.
- TestDriverConnect / TestDriverConnectResult messages.
- AdminOperationsActor.HandleTestDriverConnectAsync looks up the probe
  by DriverType, runs it with a [1,60]s clamped timeout, and returns
  success/latency or failure/message. Probes that throw or time out
  surface as soft failures.
This commit is contained in:
Joseph Doherty
2026-05-28 10:44:00 -04:00
parent 4584612a1a
commit f3f328c25c
6 changed files with 119 additions and 6 deletions
@@ -0,0 +1,27 @@
namespace ZB.MOM.WW.OtOpcUa.Commons.Messages.Admin;
/// <summary>
/// AdminUI → AdminOperationsActor request: probe one driver type's connection using
/// the supplied JSON config. Routed through <c>IAdminOperationsClient</c>; reply is
/// <see cref="TestDriverConnectResult"/>.
/// </summary>
/// <param name="DriverType">Must match an installed <c>IDriverProbe.DriverType</c>.</param>
/// <param name="ConfigJson">Driver config as JSON (same shape as <c>DriverInstance.DriverConfig</c>).</param>
/// <param name="TimeoutSeconds">Per-probe timeout; server clamps to [1, 60].</param>
/// <param name="CorrelationId">Round-trip correlation token.</param>
public sealed record TestDriverConnect(
string DriverType,
string ConfigJson,
int TimeoutSeconds,
Guid CorrelationId);
/// <summary>Reply for <see cref="TestDriverConnect"/>.</summary>
/// <param name="Ok">True iff the probe succeeded.</param>
/// <param name="Message">Failure reason; null on success.</param>
/// <param name="LatencyMs">Round-trip latency in milliseconds; null on failure or timeout.</param>
/// <param name="CorrelationId">Echoes the request's correlation token.</param>
public sealed record TestDriverConnectResult(
bool Ok,
string? Message,
double? LatencyMs,
Guid CorrelationId);
@@ -0,0 +1,27 @@
namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions;
/// <summary>
/// Test-connect probe for one driver type. Implementations deserialize a driver-config
/// JSON, attempt a cheap connection (TCP open, OPC UA session, gRPC ping — whatever the
/// driver's native protocol supports), and report success/failure with latency. Probes
/// MUST NOT mutate any persistent state; the AdminUI invokes them against transient
/// config from the typed form, NOT against the persisted DriverInstance row.
/// </summary>
public interface IDriverProbe
{
/// <summary>DriverInstance.DriverType string this probe handles. Used for DI lookup.</summary>
string DriverType { get; }
/// <summary>
/// Run the probe with the supplied config + timeout. Honour <paramref name="ct"/> for
/// timeout cancellation. Never throw on connection failure; instead return a result
/// with <c>Ok = false</c> + a message.
/// </summary>
Task<DriverProbeResult> ProbeAsync(string configJson, TimeSpan timeout, CancellationToken ct);
}
/// <summary>Outcome of a single <see cref="IDriverProbe.ProbeAsync"/> call.</summary>
/// <param name="Ok">True iff the probe reached its target and the handshake succeeded.</param>
/// <param name="Message">Human-readable status; null on success.</param>
/// <param name="Latency">Wall-clock duration of the successful probe; null on failure.</param>
public sealed record DriverProbeResult(bool Ok, string? Message, TimeSpan? Latency);