namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions; /// /// Driver capability for alarm events. Optional — only drivers whose backends expose /// alarm conditions implement this. Currently: Galaxy (MxAccess alarms), FOCAS /// (CNC alarms), OPC UA Client (A&C events from upstream server), MTConnect /// (CONDITION data items, Gitea #626). /// public interface IAlarmSource { /// /// Subscribe to alarm events for a node-set (typically: a folder or equipment subtree). /// The driver fires for every alarm transition. /// /// The driver node IDs to subscribe to. /// Cancellation token for the operation. /// A handle identifying the created subscription. Task SubscribeAlarmsAsync( IReadOnlyList sourceNodeIds, CancellationToken cancellationToken); /// Cancel an alarm subscription returned by . /// The subscription handle returned from . /// Cancellation token for the operation. /// A task that represents the asynchronous operation. Task UnsubscribeAlarmsAsync(IAlarmSubscriptionHandle handle, CancellationToken cancellationToken); /// Acknowledge one or more active alarms by source node ID + condition ID. /// The batch of alarm acknowledgement requests. /// Cancellation token for the operation. /// A task that represents the asynchronous operation. Task AcknowledgeAsync( IReadOnlyList acknowledgements, CancellationToken cancellationToken); /// Server-pushed alarm transition (raise / clear / change). event EventHandler? OnAlarmEvent; } /// Opaque alarm-subscription identity returned by . public interface IAlarmSubscriptionHandle { /// Driver-internal subscription identifier (for diagnostics + post-mortem). string DiagnosticId { get; } } /// One alarm acknowledgement in a batch. /// Driver-side identifier for the alarm source. /// Stable id correlating raise / ack / clear of the same condition. /// Operator-supplied comment forwarded to the upstream alarm system. /// Authenticated principal performing the ack; null on the raw/non-OPC-UA path. public sealed record AlarmAcknowledgeRequest( string SourceNodeId, string ConditionId, string? Comment, string? OperatorUser = null); /// Event payload for . /// Subscription this event belongs to. /// Driver-side identifier for the alarm source. /// Stable id correlating raise / ack / clear of the same condition. /// Driver-defined alarm type name (e.g. AnalogLimitAlarm.HiHi). /// Human-readable alarm description. /// Four-bucket severity ladder. /// When this transition occurred. /// /// Operator-supplied comment recorded by the upstream alarm system on Acknowledge /// transitions. Null on raise / clear, or when the upstream path can't surface /// the comment (the Galaxy sub-attribute fallback path collapses comments into a /// single string write — null on that path; the driver-native gateway path /// populates this). /// /// /// When the alarm originally entered the active state. Preserved across /// Acknowledge transitions so OPC UA Part 9 conditions keep the original raise /// time in Time. Null when the upstream path doesn't surface it. /// /// /// Upstream alarm taxonomy bucket (e.g. Process / Safety / /// Diagnostics). Maps to OPC UA ConditionClassName downstream when /// a class mapping is configured. Null when the upstream path doesn't carry it. /// /// /// The alarm transition kind (raise / acknowledge / clear / retrigger). Lets a /// consumer derive OPC UA Part 9 active/ack state without inferring it from /// other fields. when the upstream /// path doesn't surface a distinct kind. /// /// /// Optional exact OPC UA severity (1..1000) for a source whose own scale is finer than the four /// buckets (Gitea #626: MTConnect Warning = 500, Fault = 800). When set it /// supersedes the bucket on the projected Part 9 condition (clamped to 1..1000); null — every /// pre-#626 source — keeps the bucket mapping. must still carry the nearest /// bucket for consumers that only read the bucket. /// /// /// Optional status code of the condition's SOURCE data (Part 9 ConditionType.Quality), for a /// source that can report "no value for this condition" per alarm rather than per connection (Gitea /// #626: MTConnect Unavailable). null keeps the pre-#626 behaviour (projected Good; /// driver-wide comms loss is annotated separately by the host). An event whose is /// and which carries a quality is a /// quality-only annotation (): the host updates (via its Quality-only node path) the condition's /// Quality without touching its Active/Acked state and publishes no alerts row for it. /// /// /// Gitea #656 — true when the source is RE-STATING upstream's current state rather than reporting a transition it /// watched happen: a reconnect snapshot entry (Galaxy) or the first event for a condition after a driver start or /// deploy respawn (OpcUaClient). It governs history only, never node state: the host still projects the /// state, but writes no alerts row when the re-statement changes nothing the condition shows (a Retrigger on /// an Active+unacked condition with the same message and severity; a Clear on an already-inactive condition; an /// Acknowledge on an already-acknowledged condition) — or /// when a flagged update moves the message/severity of an /// already-active condition (node updated, one condition event, active/acked kept, no row). AbCip ALMA sets the /// flag for the last shape (the governing limit changed, no new occurrence). OpcUaClient (Gitea #657) re-states a /// first-seen inactive+acked condition as a flagged Clear then a flagged Acknowledge, so a host already showing /// that writes nothing and a host retaining it Active gets a real "Cleared". false (the default, every source that does not /// opt in) keeps every transition's row. /// public sealed record AlarmEventArgs( IAlarmSubscriptionHandle SubscriptionHandle, string SourceNodeId, string ConditionId, string AlarmType, string Message, AlarmSeverity Severity, DateTime SourceTimestampUtc, string? OperatorComment = null, DateTime? OriginalRaiseTimestampUtc = null, string? AlarmCategory = null, AlarmTransitionKind Kind = AlarmTransitionKind.Unspecified, int? OpcUaSeverity = null, uint? QualityStatusCode = null, bool IsRestatement = false) { /// /// True when this event only re-annotates the condition's source-data quality — a /// with an kind. No /// alarm-state change happened, so the host must not publish it as an alarm transition. /// public bool IsQualityOnly => Kind == AlarmTransitionKind.Unspecified && QualityStatusCode is not null; } /// Mirrors the NodePermissions alarm-severity enum in docs/v2/acl-design.md. public enum AlarmSeverity { Low, Medium, High, Critical } /// /// Kind of alarm state change carried by , letting a /// consumer derive OPC UA Part 9 active/ack state. Mirrors the driver-side /// transition-kind enums (e.g. Galaxy's GalaxyAlarmTransitionKind). /// (Gitea #656) means "the condition is active AND acknowledged", applied by the /// host as ONE projection — never Raise-then-Acknowledge, which would show clients an Active+unacked intermediate. /// (Gitea #657) is its mirror: "the condition is inactive AND unacknowledged" /// (it went active and cleared unacknowledged), applied as ONE projection — never Raise-then-Clear, which showed /// clients the condition Active again for an instant. The host writes rows only for what actually changed: /// "Activated" + "Cleared" when the condition was acknowledged (a new occurrence must have happened), "Cleared" /// alone when it was Active+unacked, none when it already showed inactive+unacked. /// Members are append-only: their numeric values must not move. /// public enum AlarmTransitionKind { Unspecified = 0, Raise, Acknowledge, Clear, Retrigger, ActiveAcknowledged, InactiveUnacknowledged }