using System.Collections.Concurrent;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
namespace ZB.MOM.WW.OtOpcUa.Runtime.Drivers;
/// The last health snapshot a driver child published, as kept by .
/// The driver's reported state.
/// When the driver last read successfully, if ever.
/// The driver's last error, if any.
/// Per-host connectivity for a multi-host driver, or null when the driver has none.
/// When the snapshot was published — the child's dedup means this is the last CHANGE.
public sealed record ChildHealthRecord(
DriverState State,
DateTime? LastSuccessfulReadUtc,
string? LastError,
IReadOnlyList? HostStatuses,
DateTime PublishedUtc);
///
/// An decorator that remembers the last snapshot per driver instance
/// and forwards every publish to the real publisher. wraps the publisher it
/// hands its children in one of these so GetDiagnostics can answer from the children's own health
/// (state, host counts, last change) instead of a placeholder — the children publish on their own actor
/// threads, hence the concurrent map.
///
///
/// Deliberately NOT a message to the host: the children already publish through this seam on every
/// change, so a parallel Tell would double every health transition's traffic for one consumer.
/// Entries are removed by the host when it stops a child; a late publish from a stopping child may re-add
/// one, which the host filters against its live child set when it answers.
///
public sealed class ChildHealthLedger : IDriverHealthPublisher
{
private readonly IDriverHealthPublisher _inner;
private readonly ConcurrentDictionary _last = new(StringComparer.Ordinal);
/// Wraps .
/// The publisher every snapshot is forwarded to.
public ChildHealthLedger(IDriverHealthPublisher inner)
{
ArgumentNullException.ThrowIfNull(inner);
_inner = inner;
}
///
public void Publish(
string clusterId,
string driverInstanceId,
DriverHealth health,
int errorCount5Min,
DateTime? rediscoveryNeededUtc = null,
string? rediscoveryReason = null,
IReadOnlyList? hostStatuses = null,
SkippedTagReport? skippedTags = null,
IReadOnlyDictionary? diagnostics = null)
{
_last[driverInstanceId] = new ChildHealthRecord(
health.State, health.LastSuccessfulRead, health.LastError, hostStatuses, DateTime.UtcNow);
_inner.Publish(
clusterId, driverInstanceId, health, errorCount5Min, rediscoveryNeededUtc, rediscoveryReason,
hostStatuses, skippedTags, diagnostics);
}
/// The last snapshot published for , or null if none yet.
/// The driver instance.
/// The record, or null.
public ChildHealthRecord? TryGet(string driverInstanceId) =>
_last.TryGetValue(driverInstanceId, out var record) ? record : null;
/// Forgets 's snapshot (the child was stopped).
/// The driver instance.
public void Forget(string driverInstanceId) => _last.TryRemove(driverInstanceId, out _);
}