Per-driver counters surfaced via DriverHealth.Diagnostics for the driver-diagnostics RPC. New OpcUaClientDiagnostics tracks PublishRequestCount, NotificationCount, NotificationsPerSecond (5s-half-life EWMA), MissingPublishRequestCount, DroppedNotificationCount, SessionResetCount and LastReconnectUtcTicks via Interlocked on the hot path. DriverHealth gains an optional IReadOnlyDictionary<string,double>? Diagnostics parameter (defaulted null for back-compat with the seven other drivers' constructors). OpcUaClientDriver wires Session.Notification + Session.PublishError on connect and on reconnect-complete (recording a session-reset there); GetHealth snapshots the counters on every poll so the RPC sees fresh values without a tick source. Tests: 11 new OpcUaClientDiagnosticsTests cover counter increments, EWMA convergence, snapshot shape, GetHealth integration, and DriverHealth back-compat. Full OpcUaClient.Tests 115/115 green. Closes #276 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
55 lines
2.3 KiB
C#
55 lines
2.3 KiB
C#
namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
|
|
/// <summary>
|
|
/// Health snapshot a driver returns to the Core. Drives the status dashboard,
|
|
/// ServiceLevel computation, and Bad-quality fan-out decisions.
|
|
/// </summary>
|
|
/// <param name="State">Current driver-instance state.</param>
|
|
/// <param name="LastSuccessfulRead">Timestamp of the most recent successful equipment read; null if never.</param>
|
|
/// <param name="LastError">Most recent error message; null when state is Healthy.</param>
|
|
/// <param name="Diagnostics">
|
|
/// Optional driver-attributable counters/metrics surfaced for the <c>driver-diagnostics</c>
|
|
/// RPC (introduced for Modbus task #154). Drivers populate the dictionary with stable,
|
|
/// well-known keys (e.g. <c>PublishRequestCount</c>, <c>NotificationsPerSecond</c>);
|
|
/// Core treats it as opaque metadata. Defaulted to an empty read-only dictionary so
|
|
/// existing drivers and call-sites that don't construct this field stay back-compat.
|
|
/// </param>
|
|
public sealed record DriverHealth(
|
|
DriverState State,
|
|
DateTime? LastSuccessfulRead,
|
|
string? LastError,
|
|
IReadOnlyDictionary<string, double>? Diagnostics = null)
|
|
{
|
|
/// <summary>Driver-attributable counters, empty when the driver doesn't surface any.</summary>
|
|
public IReadOnlyDictionary<string, double> DiagnosticsOrEmpty
|
|
=> Diagnostics ?? EmptyDiagnostics;
|
|
|
|
private static readonly IReadOnlyDictionary<string, double> EmptyDiagnostics
|
|
= new Dictionary<string, double>(0);
|
|
}
|
|
|
|
/// <summary>Driver-instance lifecycle state.</summary>
|
|
public enum DriverState
|
|
{
|
|
/// <summary>Driver has not been initialized yet.</summary>
|
|
Unknown,
|
|
|
|
/// <summary>Driver is in the middle of <see cref="IDriver.InitializeAsync"/> or <see cref="IDriver.ReinitializeAsync"/>.</summary>
|
|
Initializing,
|
|
|
|
/// <summary>Driver is connected and serving data.</summary>
|
|
Healthy,
|
|
|
|
/// <summary>Driver is connected but reporting degraded data (e.g. some equipment unreachable, some tags Bad).</summary>
|
|
Degraded,
|
|
|
|
/// <summary>Driver lost connection to its data source; reconnecting in the background.</summary>
|
|
Reconnecting,
|
|
|
|
/// <summary>
|
|
/// Driver hit an unrecoverable error and stopped trying.
|
|
/// Operator must reinitialize via Admin UI; nodes report Bad quality.
|
|
/// </summary>
|
|
Faulted,
|
|
}
|