using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Health;
///
/// Pure-logic merger for the per-host connectivity entries that
/// surfaces. Holds the current set of host
/// statuses (one synthetic top-level transport entry plus one entry per
/// $WinPlatform/$AppEngine probe) and emits
/// only when an upsert actually changes a host's
/// — re-asserting the same state is a no-op so a stable
/// ScanState=Running burst doesn't fan out duplicate transitions.
///
///
/// This class owns the de-dup + diff logic that lived in
/// GalaxyProxyDriver.OnHostConnectivityUpdate in v1. The watcher
/// () and the transport forwarder
/// () both feed this aggregator; the
/// consumes from
/// IHostConnectivityProbe.GetHostStatuses() and re-raises
/// as the driver-level event (wired in PR 4.W).
///
public sealed class HostStatusAggregator
{
private readonly object _lock = new();
private readonly Dictionary _byHost =
new(StringComparer.OrdinalIgnoreCase);
///
/// Fires when an call either introduces a new host or
/// transitions an existing host's . Handlers run
/// outside the internal lock so they can safely re-enter the aggregator
/// (e.g. the driver re-broadcasting through IHostConnectivityProbe).
///
public event EventHandler? OnHostStatusChanged;
///
/// Snapshot the current host set. Suitable as the body of
/// IHostConnectivityProbe.GetHostStatuses().
///
public IReadOnlyList Snapshot()
{
lock (_lock)
{
return [.. _byHost.Values];
}
}
///
/// Upsert the supplied status by .
/// Raises when the host is newly tracked
/// (previous state reported as ) or when its
/// state value differs from the last cached entry. Re-asserting the same
/// state is silent.
///
public void Update(HostConnectivityStatus status)
{
ArgumentNullException.ThrowIfNull(status);
HostState previous;
bool changed;
lock (_lock)
{
if (_byHost.TryGetValue(status.HostName, out var existing))
{
previous = existing.State;
changed = existing.State != status.State;
}
else
{
previous = HostState.Unknown;
changed = true;
}
_byHost[status.HostName] = status;
}
if (changed)
{
OnHostStatusChanged?.Invoke(this,
new HostStatusChangedEventArgs(status.HostName, previous, status.State));
}
}
///
/// Drop a host entirely (e.g. after a redeploy removes a Platform). No event
/// is fired — observers only react to live transitions, not topology
/// reductions. Returns true when the host was tracked.
///
public bool Remove(string hostName)
{
ArgumentException.ThrowIfNullOrWhiteSpace(hostName);
lock (_lock)
{
return _byHost.Remove(hostName);
}
}
}