dae520b9c0
HostStatusAggregator merges transport + per-platform host entries with change-event diffing (re-asserting same state is a no-op so a stable ScanState=Running burst doesn't fan out duplicates). PerPlatformProbeWatcher ports the legacy GalaxyRuntimeProbeManager state machine onto the gw subscription path: SubscribeBulk for `<tag>.ScanState`, idempotent SyncPlatformsAsync (subscribe new, unsubscribe dropped), and a DecodeState helper pinning bool/int/string ScanState values + bad-quality fallback. HostConnectivityForwarder is the skeleton for the gw-6 StreamSessionHealth signal — until that mxaccessgw RPC ships, PR 4.5's ReconnectSupervisor pushes transport state by calling SetTransport on session connect/disconnect. GalaxyDriver wiring (implement IHostConnectivityProbe, route OnDataChange to PerPlatformProbeWatcher, expose GetHostStatuses() / OnHostStatusChanged, push transport from supervisor) is deferred to PR 4.W to avoid conflict with the rest of the Phase 4 deferred wiring (4.5 supervisor + 4.6 DeployWatcher). Tests: 19 new - HostStatusAggregatorTests (9): empty snapshot, new-host change with Unknown predecessor, same-state silence, transition diff, snapshot reflects every host, case-insensitive host names, Remove returns true for tracked, Remove false for unknown, concurrent updates don't corrupt. - HostConnectivityForwarderTests (5): SetTransport routes under client name, transitions fire change, repeated same-state silent, empty client name throws, post-dispose throws. - PerPlatformProbeWatcherTests (5 + theory pinning DecodeState's full truth table): subscribe N platforms, idempotent re-sync, removed platforms unsubscribed + dropped from aggregator, OnProbeValueChanged routing for Running/Stopped/bad-quality/foreign-ref, Dispose unsubscribes everything. NOTE: build is currently broken because mxaccessgw/clients/dotnet/ has been removed from C:\Users\dohertj2\Desktop\mxaccessgw — this PR's source is internally consistent and isolated from the missing dependency, but the existing Driver.Galaxy code (PRs 4.1–4.6) can't compile until the .NET client is restored. Once it is, expect 116 + 19 = 135 tests in the Driver.Galaxy.Tests project. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
59 lines
2.5 KiB
C#
59 lines
2.5 KiB
C#
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Health;
|
|
|
|
/// <summary>
|
|
/// Pushes the synthetic top-level transport-health entry into the
|
|
/// <see cref="HostStatusAggregator"/>. Each driver instance has one entry under its
|
|
/// <c>MxAccess.ClientName</c> reflecting the gateway transport state — useful for
|
|
/// dashboards that want a single "Galaxy is up" signal independent of any individual
|
|
/// platform's ScanState.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The eventual production source for this signal is the gateway's <c>StreamSessionHealth</c>
|
|
/// RPC (mxaccessgw issue gw-6). Until that ships, the driver-side reconnect supervisor
|
|
/// (PR 4.5) calls <see cref="SetTransport"/> on transport state transitions:
|
|
/// <see cref="HostState.Running"/> when the gw session re-Registers, <see cref="HostState.Stopped"/>
|
|
/// when the supervisor moves to <c>TransportLost</c>. The forwarder is intentionally
|
|
/// stateless beyond the cached client name + last-pushed value so the supervisor can
|
|
/// drive it without any back-pressure plumbing.
|
|
/// </remarks>
|
|
public sealed class HostConnectivityForwarder : IDisposable
|
|
{
|
|
private readonly string _clientName;
|
|
private readonly HostStatusAggregator _aggregator;
|
|
private readonly ILogger _logger;
|
|
private bool _disposed;
|
|
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Push a transport state into the aggregator. Idempotent at the aggregator layer —
|
|
/// repeated calls with the same state don't fan out duplicate transitions.
|
|
/// </summary>
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|