namespace ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian; /// /// The historian sink contract — where qualifying alarm events land. Phase 7 plan /// decision #17: ingestion routes through Galaxy.Host's pipe so we reuse the /// already-loaded aahClientManaged DLLs without loading 32-bit native code /// in the main .NET 10 server. Tests use an in-memory fake; production uses /// . /// /// /// /// is fire-and-forget from the engine's perspective — /// the sink MUST NOT block the emitting thread. Production implementations /// () persist to a local SQLite queue /// first, then drain asynchronously to the actual historian. Per Phase 7 plan /// decision #16, failed downstream writes replay with exponential backoff; /// operator actions are never blocked waiting on the historian. /// /// /// exposes queue depth + drain rate + last error /// for the Admin UI /alarms/historian diagnostics page (Stream F). /// /// public interface IAlarmHistorianSink { /// Durably enqueue the event. Returns as soon as the queue row is committed. Task EnqueueAsync(AlarmHistorianEvent evt, CancellationToken cancellationToken); /// Snapshot of current queue depth + drain health. HistorianSinkStatus GetStatus(); } /// No-op default for tests or deployments that don't historize alarms. public sealed class NullAlarmHistorianSink : IAlarmHistorianSink { public static readonly NullAlarmHistorianSink Instance = new(); public Task EnqueueAsync(AlarmHistorianEvent evt, CancellationToken cancellationToken) => Task.CompletedTask; public HistorianSinkStatus GetStatus() => new( QueueDepth: 0, DeadLetterDepth: 0, LastDrainUtc: null, LastSuccessUtc: null, LastError: null, DrainState: HistorianDrainState.Disabled); } /// Diagnostic snapshot surfaced to the Admin UI + /healthz endpoints. public sealed record HistorianSinkStatus( long QueueDepth, long DeadLetterDepth, DateTime? LastDrainUtc, DateTime? LastSuccessUtc, string? LastError, HistorianDrainState DrainState); /// Where the drain worker is in its state machine. public enum HistorianDrainState { Disabled, Idle, Draining, BackingOff, } /// Signaled by the Galaxy.Host-side handler when it fails a batch — drain worker uses this to decide retry cadence. public enum HistorianWriteOutcome { /// Successfully persisted to the historian. Remove from queue. Ack, /// Transient failure (historian disconnected, timeout, busy). Leave queued; retry after backoff. RetryPlease, /// Permanent failure (malformed event, unrecoverable SDK error). Move to dead-letter table. PermanentFail, } /// What the drain worker delegates writes to — Stream G wires this to the Galaxy.Host IPC client. public interface IAlarmHistorianWriter { /// Push a batch of events to the historian. Returns one outcome per event, same order. Task> WriteBatchAsync( IReadOnlyList batch, CancellationToken cancellationToken); }