using System.Threading.Channels;
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Alerts;
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Drivers;
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Logging;
namespace ZB.MOM.WW.OtOpcUa.Runtime.Telemetry;
///
/// A single live-telemetry item flowing through the node-local hub. A closed union over the four
/// wire-facing telemetry records — carried as the domain records themselves, NOT as proto (the
/// gRPC projection happens at the streaming-service seam, one layer out).
///
///
/// Two of the four are snapshot-style (a last-value-per-key view: keyed
/// by DriverInstanceId, keyed by (DriverInstanceId, HostName))
/// and are cached so a newly-attaching subscriber is primed immediately. The other two
/// (, ) are append-style logs and are forwarded live-only.
///
public abstract record TelemetryItem
{
private TelemetryItem() { }
/// An append-style alarm transition. Live-forwarded only; never cached/replayed.
public sealed record Alarm(AlarmTransitionEvent E) : TelemetryItem;
/// An append-style script-log line. Live-forwarded only; never cached/replayed.
public sealed record Script(ScriptLogEntry E) : TelemetryItem;
/// A snapshot-style driver-health change, cached last-value per DriverInstanceId.
public sealed record Health(DriverHealthChanged E) : TelemetryItem;
/// A snapshot-style resilience-status change, cached last-value per (DriverInstanceId, HostName).
public sealed record Resilience(DriverResilienceStatusChanged E) : TelemetryItem;
}
///
/// One attached reader on the . Disposing detaches it from the hub
/// and completes its channel; the reader first yields the cached snapshots, then live deltas.
///
public interface ITelemetrySubscription : IDisposable
{
/// The bounded reader this subscription drains. Completed on .
ChannelReader Reader { get; }
}
///
/// Process-wide, node-local fan-out hub sitting between this node's own telemetry producers and the
/// (Phase-5) gRPC streaming service central dials into.
///
///
///
/// CRITICAL INVARIANT — this hub carries ONLY this node's own telemetry. It is fed
/// directly, in-process, by the node's producers and MUST NEVER subscribe to cluster
/// DistributedPubSub. On the single mesh, DPS delivers every node's events to every node; if the
/// hub read DPS it would stream peers' events and central — which dials each node individually —
/// would double-count.
///
///
/// Fan-out is lossy under backpressure by design: each subscriber owns a bounded channel with
/// , so a slow consumer sheds its own oldest items
/// and can never block the producer / node-actor threads.
///
///
public interface ITelemetryLocalHub
{
///
/// Fans to every currently-attached subscriber (non-blocking, drop-oldest
/// on a full channel) and, for the two snapshot-style items, updates the last-value cache that
/// primes future subscribers.
///
void Emit(TelemetryItem item);
///
/// Attaches a new subscriber. Its reader first yields the cached snapshots (all cached
/// , then all cached ),
/// then live deltas — with no delta lost across the attach boundary. Dispose to detach.
///
///
/// Per-subscriber channel capacity (must be positive). The snapshot prelude is written into this
/// same bounded/DropOldest channel before returns, so
/// should comfortably exceed this node's live driver-instance +
/// (instance, host) count — otherwise the earliest cached snapshots are silently evicted during
/// priming before the reader ever drains them. The node-local cache holds only THIS node's
/// instances (a handful), so a normal large capacity leaves ample headroom.
///
ITelemetrySubscription Subscribe(int boundedCapacity);
}