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 }