fix(opcuaclient): derive AlarmTransitionKind from upstream ActiveState/AckedState (#619 review)

NativeAlarmProjector derives Active/Acked ONLY from AlarmEventArgs.Kind, and this driver
never set it - every routed event projected Unspecified, leaving the condition inactive+acked
(only Message/Severity moved). The live-gate expectation 'toggling Active as opc-plc cycles'
could not have passed. Select ActiveState/Id + AckedState/Id (two-element browse paths) and
emit the Kind sequence that moves the projector from the state last told to the host to the
event's (Raise+Acknowledge when a notification carries both).

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
Joseph Doherty
2026-10-08 17:04:48 -04:00
parent 3de2cc3ba8
commit 454775c67c
3 changed files with 135 additions and 5 deletions
+4 -1
View File
@@ -97,7 +97,10 @@ rather than surfacing as a runtime surprise.
> `ConditionId`. Author `nodeId` as the upstream **condition** node for a 1:1 mapping (required when one source carries
> several conditions, e.g. Hi + HiHi); authoring the **source** node folds every condition of that source into one
> tag. `Acknowledge` is called on the condition node of the last event seen for the RawPath **with that event's
> EventId** (Part 9 §5.7.3). Index keys are server-stable (`nsu=`), so a namespace reorder across a reconnect keeps
> EventId** (Part 9 §5.7.3). The event's `ActiveState/Id` + `AckedState/Id` are selected and turned into the
> `Raise`/`Acknowledge`/`Clear`/`Retrigger` kinds the host projector consumes (without a kind no upstream alarm could ever
> show active). No `ConditionRefresh` is issued on subscribe, so an upstream alarm already active when the subscription
> opens appears at its next transition. Index keys are server-stable (`nsu=`), so a namespace reorder across a reconnect keeps
> matching. docker-dev seeds an opc-plc driver (`MAIN-opcua`); the alarm tag is
> created only when `@OpcPlcAlarmSourceNodeId` is set in `docker-dev/seed/seed-clusters.sql`.