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:
Joseph Doherty
2026-06-11 07:49:41 -04:00
parent d860b0d8a6
commit 87e433871e
5 changed files with 116 additions and 10 deletions
+9 -4
View File
@@ -82,11 +82,16 @@ Engine wiring (subscription publishing, ApplyDelta diff, bad-quality-on-disconne
## VirtualTagActor / ScriptedAlarmActor
Skeleton state machines + message handlers. Engine work:
Both are fully wired in production (F8 + F9 shipped). `VirtualTagActor` compiles and evaluates expressions; `ScriptedAlarmActor` owns the per-alarm Part 9 state machine and persists `ScriptedAlarmState` to the config DB.
- `VirtualTagEngine.Evaluate()` not yet called from `VirtualTagActor.DependencyValueChanged` (F8).
- `AlarmConditionService` not yet called from `ScriptedAlarmActor` (F9).
- `ScriptedAlarmState` DB persistence on `PreRestart` not wired (F9).
### alarm-commands topic (inbound operator ack/shelve)
`ScriptedAlarmHostActor` subscribes to the `alarm-commands` DPS topic. Two surfaces publish onto this topic:
- **OPC UA Part 9 method path** — `OtOpcUaNodeManager` handles Acknowledge / Confirm / AddComment / OneShotShelve / TimedShelve / Unshelve calls from external OPC UA clients. Each call is gated on the `AlarmAck` LDAP role (fail-closed); on allow, a `Commons.OpcUa.AlarmCommand` is published onto the topic.
- **AdminUI `/alerts` path** — `AdminOperationsActor` (cluster singleton) publishes `AcknowledgeAlarmCommand` / `ShelveAlarmCommand` from the AdminUI operator buttons.
`ScriptedAlarmHostActor` ownership-filters incoming commands (each node acts only on its own alarms) and dispatches to the matching `ScriptedAlarmEngine` operation. The engine's `OnEvent` callback handles the resulting OPC UA condition-node update.
## OpcUaPublishActor
+11 -4
View File
@@ -54,7 +54,7 @@ Shipped as PR #180 (36 tests).
| C.3 — Predicate evaluation on input change; activate/clear transitions | **Done** | `ScriptedAlarmEngine.ReevaluateAsync`; `_alarmsReferencing` inverse index |
| C.4 — Startup recovery (`ActiveState` re-derived; Enabled/Ack/Confirm/Shelve loaded from store) | **Done** | `ScriptedAlarmEngine.LoadAsync`; `IAlarmStateStore.LoadAsync` |
| C.5 — Template substitution (`{TagPath}` tokens resolved at emission time) | **Done** | `MessageTemplate.cs`; `MessageTemplateTests.cs` |
| C.6 — OPC UA method binding (Acknowledge / Confirm / AddComment / OneShotShelve / TimedShelve / Unshelve) | **Partial** | Engine methods exist and are tested. `ScriptedAlarmSource.AcknowledgeAsync` defaults the user to `"opcua-client"`. The plan's Stream G wiring of these methods to OPC UA `MethodCall` dispatch on the condition nodes (so OPC UA client method calls reach the engine with the authenticated principal) is noted in the e2e smoke doc as "not yet wired through `DriverNodeManager.MethodCall` dispatch." Operators acknowledge through Admin UI today; the Part 9 method-call path is a follow-up. |
| C.6 — OPC UA method binding (Acknowledge / Confirm / AddComment / OneShotShelve / TimedShelve / Unshelve) | **Done** | Engine methods exist and are tested. `OtOpcUaNodeManager` wires all six Part 9 condition methods on each `AlarmConditionState` node, gated on the `AlarmAck` LDAP role. On allow, a `Commons.OpcUa.AlarmCommand` is published onto the `alarm-commands` DPS topic; `ScriptedAlarmHostActor` dispatches to the engine with the authenticated principal. (T18T19, `feat/scriptlog-alarm-ack`.) |
| C.7 — `IAlarmSource` implementation / fan-out registration | **Done** | `ScriptedAlarmSource.cs` |
| C.8 — Tests: all state transitions, startup recovery, template substitution, shelving timer expiry | **Done** | `Part9StateMachineTests.cs`, `ScriptedAlarmEngineTests.cs`, `ScriptedAlarmSourceTests.cs`, `MessageTemplateTests.cs` — 5 test files |
@@ -108,9 +108,9 @@ Shipped as PR #185 (13 Admin service tests; UI completeness is partial — see g
| G.2 — `DriverNodeManager` dispatch routes reads by source; writes to non-Driver rejected with `BadUserAccessDenied` | **Done** | PR #186 follow-up; `OpcUaApplicationHost.SetPhase7Sources` threads `_virtualReadable` + `_scriptedAlarmReadable` into the node manager |
| G.3 — `AlarmTracker` composition (`ScriptedAlarmEngine` registers as additional `IAlarmSource`) | **Done** | `ScriptedAlarmSource` adapts engine to `IAlarmSource`; `Phase7EngineComposer.Compose` wires it |
| G.4 — Tests: mixed equipment folder browsable via Client.CLI; read/subscribe round-trip; alarm transitions in event stream | **Done** | `Phase7ComposerMappingTests.cs`, `Phase7EngineComposerTests.cs`, `ScriptedAlarmReadableTests.cs`, `CachedTagUpstreamSourceTests.cs`, `DriverSubscriptionBridgeTests.cs` — 6 test files in `Server.Tests/Phase7/` |
| OPC UA method binding for alarm Ack/Confirm/Shelve | **Not started** | Noted explicitly in `phase-7-e2e-smoke.md` §"Known limitations": `DriverNodeManager.MethodCall` dispatch for scripted alarm methods is not wired. Engine has the methods; the OPC UA call path does not reach them. |
| OPC UA method binding for alarm Ack/Confirm/Shelve | **Done** | `OtOpcUaNodeManager` wires Acknowledge / Confirm / AddComment / OneShotShelve / TimedShelve / Unshelve on each `AlarmConditionState` node; all gated on `AlarmAck` LDAP role (T18). `ScriptedAlarmHostActor` subscribes to the `alarm-commands` DPS topic and dispatches to the engine (T19). AdminUI `/alerts` Acknowledge/Shelve/Unshelve buttons route through `AdminOperationsActor` onto the same topic (T21). Client.CLI `ack`/`confirm`/`shelve` commands call the Part 9 methods directly (T22). Live-verified on docker-dev 2026-06-11. |
Shipped across PRs #184 + #186 (5 + 7 tests).
Shipped across PRs #184 + #186 (5 + 7 tests). Inbound ack/shelve path shipped as T17T22 on branch `feat/scriptlog-alarm-ack`.
### Stream H — Exit gate
@@ -140,7 +140,14 @@ These are real open items, not issues with the plan reconciliation.
### Gap 1 — OPC UA method-call dispatch for scripted alarm methods (Stream G / C.6) — CLOSED
All Part 9 alarm methods now route to the `ScriptedAlarmEngine`. `Acknowledge` / `Confirm` / `AddComment` route via `DriverNodeManager.RouteScriptedAlarmMethodCalls` (task #24 + follow-up); `AddComment` gates at the `AlarmAcknowledge` tier. `OneShotShelve` / `TimedShelve` / `Unshelve` route via the native `AlarmConditionState.OnShelve` / `OnTimedUnshelve` hooks wired in `MarkAsAlarmCondition`, with the per-instance shelve method NodeIds indexed so the Call gate resolves them to `OpcUaOperation.AlarmShelve`.
All Part 9 alarm methods now route to the `ScriptedAlarmEngine` (shipped T17T22, branch `feat/scriptlog-alarm-ack`):
- `OtOpcUaNodeManager` wires Acknowledge / Confirm / AddComment / OneShotShelve / TimedShelve / Unshelve on each `AlarmConditionState` node, gated on the `AlarmAck` LDAP role (`RoleCarryingUserIdentity` carries roles onto the session identity). On allow, a `Commons.OpcUa.AlarmCommand` is published onto the `alarm-commands` DPS topic.
- `ScriptedAlarmHostActor` subscribes to `alarm-commands`, ownership-filters, and dispatches to the matching engine operation. `OnTimedUnshelve` (SDK auto-unshelve) bypasses the client gate.
- `WriteAlarmCondition` delta-gates condition events to suppress the inbound-ack double-emit.
- AdminUI `/alerts` Acknowledge / Shelve / Unshelve buttons route through `AdminOperationsActor` singleton onto the same `alarm-commands` topic (gated by `DriverOperator` AdminUI policy).
- Client.CLI `ack` / `confirm` / `shelve` commands call the Part 9 methods directly.
- Live-verified on docker-dev 2026-06-11.
### Gap 2 — Admin UI: no `/virtual-tags` tab or form (Stream F.2)