namespace ZB.MOM.WW.OtOpcUa.Server.Redundancy;
///
/// Latest observed reachability of the peer node per the Phase 6.3 Stream B.1/B.2 two-layer
/// probe model. HTTP layer is the fast-fail; UA layer is authoritative.
///
///
/// Fed into the as peerHttpHealthy +
/// peerUaHealthy. The concrete probe loops (PeerHttpProbeLoop +
/// PeerUaProbeLoop) live in a Stream B runtime follow-up — this type is the
/// contract the publisher reads; probers write via
/// .
///
public sealed record PeerReachability(bool HttpHealthy, bool UaHealthy)
{
public static readonly PeerReachability Unknown = new(false, false);
public static readonly PeerReachability FullyHealthy = new(true, true);
/// True when both probes report healthy — the ServiceLevelCalculator's peerReachable gate.
public bool BothHealthy => HttpHealthy && UaHealthy;
}
///
/// Thread-safe holder of the latest per peer NodeId. Probe
/// loops call ; the reads via
/// .
///
public sealed class PeerReachabilityTracker
{
private readonly System.Collections.Concurrent.ConcurrentDictionary _byPeer =
new(StringComparer.OrdinalIgnoreCase);
public void Update(string peerNodeId, PeerReachability reachability)
{
ArgumentException.ThrowIfNullOrWhiteSpace(peerNodeId);
_byPeer[peerNodeId] = reachability ?? throw new ArgumentNullException(nameof(reachability));
}
/// Current reachability for a peer. Returns when not yet probed.
public PeerReachability Get(string peerNodeId) =>
_byPeer.TryGetValue(peerNodeId, out var r) ? r : PeerReachability.Unknown;
}