From 2c938ea6f786b7b20068a7fdc44a30f673508744 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Wed, 3 Jun 2026 15:40:37 -0400 Subject: [PATCH] =?UTF-8?q?docs(audit):=20AlarmTracking.md=20=E2=80=94=20a?= =?UTF-8?q?ccuracy=20+=20orphan=20resolution?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ORPHAN DECISION: Keep as live doc (path: keep-and-fix). Rationale: the file carries unique v2 current content describing the alarms-over-gateway epic architecture; docs/ScriptedAlarms.md cross-references it explicitly. The orphan symptom is that docs/README.md still indexes docs/v1/AlarmTracking.md — wiring this top-level file into README.md is a follow-up task. STRUCTURAL (dimension 2): - docs/AlarmTracking.md line 138: Security.md → security.md (CASE-MISMATCH from links-report.md rows 1–2). Verified: docs/security.md exists (inode 77517627); docs/Security.md is the same file on APFS case-insensitive FS, but the checker requires exact on-disk casing. check_links.py: zero rows for docs/AlarmTracking.md after fix. CODE-REALITY (dimension 4): - line 16 table: `Phase7EngineComposer` / `Phase7EngineComposer.RouteToHistorianAsync` → no such class exists. Real class is `Phase7Composer` (src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Phase7Composer.cs). Scripted-alarm historian routing goes through ScriptedAlarmActor → HistorianAdapterActor → IAlarmHistorianSink, not a RouteToHistorianAsync method. Fixed to: Phase7Composer / ScriptedAlarmActor transitions → HistorianAdapterActor → IAlarmHistorianSink. - lines 107–123 "Historian write-back" section: referenced `Phase7Composer.ResolveHistorianSink` (method doesn't exist in current Phase7Composer.cs), `GalaxyProxyDriver` / `GalaxyHistorianWriter` (retired in PR 7.2 — no such class in codebase), and `aahClientManaged` as a direct call (now mediated through WonderwareHistorianClient). Current architecture: NullAlarmHistorianSink default registered in ServiceCollectionExtensions.AddOtOpcUaRuntime(); production override is SqliteStoreAndForwardSink wrapping WonderwareHistorianClient; bridge is HistorianAdapterActor (src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Historian/ HistorianAdapterActor.cs). Section rewritten to match code reality. - line 108: "Program.cs" as NullAlarmHistorianSink registration site → actual site is ServiceCollectionExtensions.cs, not Program.cs. STALE-STATUS (dimension 3): no blocked/pending/not-yet banners found in the top-level file; it was already written as current-state fact. Galaxy native alarms work end-to-end (verified 2026-05-31) and the doc correctly describes that as delivered. CODE-BUG-FLAGS: none. All stale references were doc-side errors; the production code is correct. UNVERIFIABLE CLAIMS: AlarmConditionService, DriverNodeManager, ConditionSink, DriverAlarmSourceAcknowledger, DriverWritableAcknowledger — these are mentioned by name in the doc but their .cs files were not found in the search. They may live under a path not searched, or may be internal implementation details within existing files. These claims are plausible given the architecture and were not changed. --- docs/AlarmTracking.md | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/docs/AlarmTracking.md b/docs/AlarmTracking.md index 03f03d62..54bfa527 100644 --- a/docs/AlarmTracking.md +++ b/docs/AlarmTracking.md @@ -13,7 +13,7 @@ historical reference. |----------------------------------|--------------------------|------| | **Galaxy MxAccess (driver-native)** | `GalaxyDriver : IAlarmSource` | gateway → worker → MxAccess alarm sink → `MX_EVENT_FAMILY_ON_ALARM_TRANSITION` → `EventPump` → driver `OnAlarmEvent` → `AlarmConditionService` | | **Galaxy sub-attribute fallback** | `IWritable` writes to `$Alarm*` sub-attributes | gateway data subscription → driver `OnDataChange` → `DriverNodeManager` ConditionSink → `AlarmConditionService` | -| **Scripted alarms** | `Phase7EngineComposer` | server-side script evaluator → `Phase7EngineComposer.RouteToHistorianAsync` + `AlarmConditionService` | +| **Scripted alarms** | `Phase7Composer` | server-side script evaluator → `ScriptedAlarmActor` transitions → `HistorianAdapterActor` → `IAlarmHistorianSink` | All three converge on the alarm-state actor — in v2 the OPC UA Part 9 state machine lives inside `ScriptedAlarmActor` @@ -104,23 +104,25 @@ calls. Scripted alarms (and any future non-Galaxy `IAlarmSource` like AB CIP ALMD) route to AVEVA Historian via the Wonderware sidecar: -- `Phase7Composer.ResolveHistorianSink` resolves an - `IAlarmHistorianWriter` from either a driver that natively - implements it or the DI-registered `WonderwareHistorianClient` - (the sidecar IPC client). Driver-provided wins when both are - present. +- `IAlarmHistorianSink` is the DI-registered intake contract. The + default binding is `NullAlarmHistorianSink` (registered in + `ServiceCollectionExtensions.AddOtOpcUaRuntime`). Production + deployments override it with `SqliteStoreAndForwardSink` wrapping + `WonderwareHistorianClient` (the AVEVA Historian sidecar IPC client) + — see [ServiceHosting.md](ServiceHosting.md) for the sidecar setup. - `SqliteStoreAndForwardSink` queues each transition to a local - SQLite database and drains in the background via the resolved - writer. **The durability guarantee is bounded**: the queue capacity - defaults to 1,000,000 rows; under a sustained historian outage, - older non-dead-lettered rows are evicted (oldest first) to make - room for new events. The `HistorianSinkStatus.EvictedCount` counter - surfaces lifetime eviction events to the Admin UI - `/alarms/historian` diagnostics page so operators can detect silent - data loss without log scraping. -- Sidecar (PR C.1 + C.2) forwards the events to `aahClientManaged`'s - alarm-event write API; the live SDK call site is pinned during - PR D.1's deploy-rig validation. + SQLite database and drains in the background via an + `IAlarmHistorianWriter`. **The durability guarantee is bounded**: the + queue capacity defaults to 1,000,000 rows; under a sustained + historian outage, older non-dead-lettered rows are evicted (oldest + first) to make room for new events. The `HistorianSinkStatus.EvictedCount` + counter surfaces lifetime eviction events so operators can detect + silent data loss without log scraping. +- `HistorianAdapterActor` + (`src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Historian/HistorianAdapterActor.cs`) + bridges Akka cluster messages from `ScriptedAlarmActor` into the + sink's `EnqueueAsync`; fire-and-forget so the actor loop is never + blocked on historian reachability. Galaxy-native alarms with `$Alarm*` extensions reach AVEVA Historian directly via System Platform's `HistorizeToAveva` toggle on the @@ -133,4 +135,4 @@ exclusively for non-Galaxy alarm producers. - v1 archive: [docs/v1/AlarmTracking.md](v1/AlarmTracking.md) - Galaxy driver: [docs/drivers/Galaxy.md](drivers/Galaxy.md) - Phase 7 scripting + alarming: [docs/v2/implementation/phase-7-scripting-and-alarming.md](v2/implementation/phase-7-scripting-and-alarming.md) -- Security + ACL: [docs/Security.md](Security.md) +- Security + ACL: [docs/security.md](security.md)