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);
}