docs: document inbound alarm ack/shelve (AlarmAck gate, alarm-commands, AdminUI/CLI) + remove scratch files
Records T17-T22 as shipped: RoleCarryingUserIdentity, Part 9 method handlers gated on AlarmAck role, alarm-commands DPS topic, ScriptedAlarmHostActor dispatch, WriteAlarmCondition delta-gate, AdminUI /alerts Acknowledge/Shelve/Unshelve buttons via AdminOperationsActor singleton, and Client.CLI ack/confirm/shelve commands. Corrects stale "Not started" / "Partial" entries in phase-7-status.md (Stream G OPC UA method binding row and C.6 row and Gap 1 body) and adds the alarm-commands topic to Runtime.md. Removes untracked scratch files resume.md and pending.md.
This commit is contained in:
+37
-1
@@ -105,7 +105,43 @@ Every mutation the state machine produces is immediately persisted inside the en
|
||||
Two mapping notes specific to this adapter:
|
||||
|
||||
- `SubscribeAlarmsAsync` accepts a list of source-node-id filters, interpreted as Equipment-path prefixes. Empty list matches every alarm. Each emission is matched against every live subscription — the adapter keeps no per-subscription cursor.
|
||||
- `IAlarmSource.AcknowledgeAsync` does not carry a user identity. The adapter defaults the audit user to `"opcua-client"` so callers using the base interface still produce an audit entry. The server's Part 9 method handlers (Stream G) call the engine's richer `AcknowledgeAsync` / `ConfirmAsync` / `OneShotShelveAsync` / `TimedShelveAsync` / `UnshelveAsync` / `AddCommentAsync` directly with the authenticated principal instead.
|
||||
- `IAlarmSource.AcknowledgeAsync` does not carry a user identity. The adapter defaults the audit user to `"opcua-client"` so callers using the base interface still produce an audit entry. The server's Part 9 method handlers call the engine's richer `AcknowledgeAsync` / `ConfirmAsync` / `OneShotShelveAsync` / `TimedShelveAsync` / `UnshelveAsync` / `AddCommentAsync` directly with the authenticated principal instead.
|
||||
|
||||
## Inbound operator ack/shelve
|
||||
|
||||
Operators interact with active scripted alarms through two surfaces — both converge on the same `alarm-commands` DPS topic consumed by `ScriptedAlarmHostActor`.
|
||||
|
||||
### AlarmAck gate (OPC UA method path)
|
||||
|
||||
`OtOpcUaNodeManager` wires the OPC UA Part 9 condition methods (Acknowledge / Confirm / AddComment / OneShotShelve / TimedShelve / Unshelve) on each `AlarmConditionState` node. Every method call is gated on the `AlarmAck` LDAP role — fail-closed: a session with no resolved roles or no `AlarmAck` group membership receives `BadUserAccessDenied` immediately without reaching the engine. The role is carried on the session by `RoleCarryingUserIdentity` (a `UserIdentity` subclass that preserves the LDAP-resolved role set past `OpcUaApplicationHost`).
|
||||
|
||||
On allow, the handler publishes a `Commons.OpcUa.AlarmCommand` (containing command kind, condition id, comment, and operator principal) onto the `alarm-commands` DPS topic. The node manager itself stays Akka-free: the dispatch action is a settable `Action<AlarmCommand>` injected at boot by the hosted service.
|
||||
|
||||
`OnTimedUnshelve` (the SDK's internal auto-unshelve timer) bypasses the client gate — it is system-initiated and not subject to operator role checks.
|
||||
|
||||
### Delta-gate de-duplication
|
||||
|
||||
`WriteAlarmCondition` fires a Part 9 condition event only when the incoming state differs from the node's current live state. This suppresses the double-emit that would otherwise occur when the OPC UA SDK auto-applies the acked state on the node and the engine's re-projection then attempts to fire a duplicate event.
|
||||
|
||||
### AdminUI path
|
||||
|
||||
The AdminUI `/alerts` page (`Alerts.razor`) shows per-row **Acknowledge / Shelve / Unshelve** buttons. These are gated by the `DriverOperator` AdminUI policy and routed through the `AdminOperationsActor` cluster singleton (`AcknowledgeAlarmCommand` / `ShelveAlarmCommand`), which publishes onto the same `alarm-commands` topic. Cross-node routing is handled by the cluster singleton — the command always reaches the driver-role node hosting the engine that owns the alarm regardless of which AdminUI instance the operator is connected to.
|
||||
|
||||
### Client.CLI path
|
||||
|
||||
The Client.CLI supports `ack`, `confirm`, and `shelve` commands that call the Part 9 condition methods directly on the OPC UA server:
|
||||
|
||||
```bash
|
||||
dotnet run --project src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI -- ack -u opc.tcp://localhost:4840 -c "ns=2;s=Plant/Line1/Oven::OverTemp" -m "Acknowledged by ops"
|
||||
dotnet run --project src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI -- confirm -u opc.tcp://localhost:4840 -c "ns=2;s=Plant/Line1/Oven::OverTemp" -m "Confirmed"
|
||||
dotnet run --project src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI -- shelve -u opc.tcp://localhost:4840 -c "ns=2;s=Plant/Line1/Oven::OverTemp" --timed 300000
|
||||
```
|
||||
|
||||
TimedShelve duration is sent as OPC UA `Duration` (milliseconds). The CLI uses `IOpcUaClientService.ConfirmAlarmAsync` / `ShelveAlarmAsync`.
|
||||
|
||||
### ScriptedAlarmHostActor dispatch
|
||||
|
||||
`ScriptedAlarmHostActor` subscribes to the `alarm-commands` DPS topic, ownership-filters each command (each node only acts on the alarms it owns), and dispatches to the matching `ScriptedAlarmEngine` operation (`AcknowledgeAsync` / `ConfirmAsync` / `OneShotShelveAsync` / `TimedShelveAsync` / `UnshelveAsync` / `EnableAsync` / `DisableAsync` / `AddCommentAsync`). No explicit re-projection is needed — the engine's existing `OnEvent` callback updates the OPC UA node after the transition.
|
||||
|
||||
Emissions map into `AlarmEventArgs` as `AlarmType = Kind.ToString()`, `SourceNodeId = EquipmentPath`, `ConditionId = AlarmId`, `Message = resolved template string`, `Severity` carried verbatim, `SourceTimestampUtc = emission time`.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user