25ad4b1929
Phase 7 plan decisions #16, #17, #19, #21 implementation. Durable local SQLite queue absorbs every qualifying alarm event; drain worker forwards batches to Galaxy.Host (reusing the already-loaded 32-bit aahClientManaged DLLs) on an exponential-backoff cadence; operator acks never block on the historian being reachable. ## New project Core.AlarmHistorian (net10) - AlarmHistorianEvent — source-agnostic event shape (scripted alarms + Galaxy-native + AB CIP ALMD + any future IAlarmSource) - IAlarmHistorianSink / NullAlarmHistorianSink — interface + disabled default - IAlarmHistorianWriter — per-event outcome (Ack / RetryPlease / PermanentFail); Stream G wires the Galaxy.Host IPC client implementation - SqliteStoreAndForwardSink — full implementation: - Queue table with AttemptCount / LastError / DeadLettered columns - DrainOnceAsync serialised via SemaphoreSlim - BackoffLadder 1s → 2s → 5s → 15s → 60s (cap) - DefaultCapacity 1,000,000 rows — overflow evicts oldest non-dead-lettered - DefaultDeadLetterRetention 30 days — sweeper purges on every drain tick - RetryDeadLettered operator action reattaches dead-letters to the regular queue - Writer-side exceptions treated as whole-batch RetryPlease (no data loss) ## New IPC contracts in Driver.Galaxy.Shared - HistorianAlarmEventRequest — batched up to 100 events/request per plan Stream D.5 - HistorianAlarmEventResponse — per-event outcome (1:1 with request order) - HistorianAlarmEventOutcomeDto enum (byte on the wire — Ack/RetryPlease/PermanentFail) - HistorianAlarmEventDto — mirrors Core.AlarmHistorian.AlarmHistorianEvent - HistorianConnectivityStatusNotification — Host pushes proactively when the SDK session drops so /alarms/historian flips red without waiting for the next drain - MessageKind additions: 0x80 HistorianAlarmEventRequest / 0x81 HistorianAlarmEventResponse / 0x82 HistorianConnectivityStatus ## Tests — 14/14 SqliteStoreAndForwardSinkTests covers: enqueue→drain→Ack round-trip, empty-queue no-op, RetryPlease bumps backoff + keeps row, Ack after Retry resets backoff, PermanentFail dead-letters one row without blocking neighbors, writer exception treated as whole-batch retry with error surfaced in status, capacity eviction drops oldest non-dead-lettered, dead-letters purged past retention window, RetryDeadLettered requeues, ladder caps at 60s after 10 retries, Null sink reports Disabled status, null sink swallows enqueue, ctor argument validation, disposed sink rejects enqueue. ## Totals Full Phase 7 tests: 160 green (63 Scripting + 36 VirtualTags + 47 ScriptedAlarms + 14 AlarmHistorian). Stream G wires this into the real Galaxy.Host IPC pipe.
76 lines
2.6 KiB
C#
76 lines
2.6 KiB
C#
namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Shared.Contracts;
|
|
|
|
/// <summary>
|
|
/// Length-prefixed framing per decision #28. Each IPC frame is:
|
|
/// <c>[4-byte big-endian length][1-byte message kind][MessagePack body]</c>.
|
|
/// Length is the body size only; the kind byte is not part of the prefixed length.
|
|
/// </summary>
|
|
public static class Framing
|
|
{
|
|
public const int LengthPrefixSize = 4;
|
|
public const int KindByteSize = 1;
|
|
|
|
/// <summary>
|
|
/// Maximum permitted body length (16 MiB). Protects the receiver from a hostile or
|
|
/// misbehaving peer sending an oversized length prefix.
|
|
/// </summary>
|
|
public const int MaxFrameBodyBytes = 16 * 1024 * 1024;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wire identifier for each contract. Values are stable — new contracts append.
|
|
/// </summary>
|
|
public enum MessageKind : byte
|
|
{
|
|
Hello = 0x01,
|
|
HelloAck = 0x02,
|
|
Heartbeat = 0x03,
|
|
HeartbeatAck = 0x04,
|
|
|
|
OpenSessionRequest = 0x10,
|
|
OpenSessionResponse = 0x11,
|
|
CloseSessionRequest = 0x12,
|
|
|
|
DiscoverHierarchyRequest = 0x20,
|
|
DiscoverHierarchyResponse = 0x21,
|
|
|
|
ReadValuesRequest = 0x30,
|
|
ReadValuesResponse = 0x31,
|
|
WriteValuesRequest = 0x32,
|
|
WriteValuesResponse = 0x33,
|
|
|
|
SubscribeRequest = 0x40,
|
|
SubscribeResponse = 0x41,
|
|
UnsubscribeRequest = 0x42,
|
|
OnDataChangeNotification = 0x43,
|
|
|
|
AlarmSubscribeRequest = 0x50,
|
|
AlarmEvent = 0x51,
|
|
AlarmAckRequest = 0x52,
|
|
|
|
HistoryReadRequest = 0x60,
|
|
HistoryReadResponse = 0x61,
|
|
HistoryReadProcessedRequest = 0x62,
|
|
HistoryReadProcessedResponse = 0x63,
|
|
HistoryReadAtTimeRequest = 0x64,
|
|
HistoryReadAtTimeResponse = 0x65,
|
|
HistoryReadEventsRequest = 0x66,
|
|
HistoryReadEventsResponse = 0x67,
|
|
|
|
HostConnectivityStatus = 0x70,
|
|
RuntimeStatusChange = 0x71,
|
|
|
|
// Phase 7 Stream D — historian alarm sink. Main server → Galaxy.Host batched
|
|
// writes into the Aveva Historian alarm schema via the already-loaded
|
|
// aahClientManaged DLLs. HistorianConnectivityStatus fires proactively from the
|
|
// Host when the SDK session transitions so diagnostics flip promptly.
|
|
HistorianAlarmEventRequest = 0x80,
|
|
HistorianAlarmEventResponse = 0x81,
|
|
HistorianConnectivityStatus = 0x82,
|
|
|
|
RecycleHostRequest = 0xF0,
|
|
RecycleStatusResponse = 0xF1,
|
|
|
|
ErrorResponse = 0xFE,
|
|
}
|