using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using ZB.MOM.WW.OtOpcUa.Core.Abstractions; namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Health; /// /// Pushes the synthetic top-level transport-health entry into the /// . Each driver instance has one entry under its /// MxAccess.ClientName reflecting the gateway transport state — useful for /// dashboards that want a single "Galaxy is up" signal independent of any individual /// platform's ScanState. /// /// /// The eventual production source for this signal is the gateway's StreamSessionHealth /// RPC (mxaccessgw issue gw-6). Until that ships, the driver-side reconnect supervisor /// (PR 4.5) calls on transport state transitions: /// when the gw session re-Registers, /// when the supervisor moves to TransportLost. The forwarder is intentionally /// stateless beyond the cached client name + last-pushed value so the supervisor can /// drive it without any back-pressure plumbing. /// public sealed class HostConnectivityForwarder : IDisposable { private readonly string _clientName; private readonly HostStatusAggregator _aggregator; private readonly ILogger _logger; private bool _disposed; /// Initializes a new instance of HostConnectivityForwarder with the given client name, aggregator, and optional logger. /// The client name for the MxAccess connection. /// The host status aggregator to push connectivity state to. /// The optional logger for diagnostic messages. public HostConnectivityForwarder(string clientName, HostStatusAggregator aggregator, ILogger? logger = null) { ArgumentException.ThrowIfNullOrWhiteSpace(clientName); _clientName = clientName; _aggregator = aggregator ?? throw new ArgumentNullException(nameof(aggregator)); _logger = logger ?? NullLogger.Instance; } /// /// Push a transport state into the aggregator. Idempotent at the aggregator layer — /// repeated calls with the same state don't fan out duplicate transitions. /// /// The host connectivity state to push. public void SetTransport(HostState state) { ObjectDisposedException.ThrowIf(_disposed, this); var status = new HostConnectivityStatus(_clientName, state, DateTime.UtcNow); _aggregator.Update(status); _logger.LogDebug( "GalaxyDriver transport state for {ClientName}: {State}", _clientName, state); } /// Disposes the forwarder and marks it as disposed. public void Dispose() { // No-op today; reserved for the eventual gw-6 StreamSessionHealth consumer that // will own a long-running task this method tears down. _disposed = true; } }