From 6208304a44d9d47de4f5cfb5172401b41ab30dbc Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Thu, 11 Jun 2026 13:24:46 -0400 Subject: [PATCH] docs(historian): HistorizeToAveva opt-out semantics + config knobs + startup validation --- docs/AlarmTracking.md | 18 +++++++++++++++--- docs/ScriptedAlarms.md | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/docs/AlarmTracking.md b/docs/AlarmTracking.md index 53e392f6..cc836c98 100644 --- a/docs/AlarmTracking.md +++ b/docs/AlarmTracking.md @@ -175,13 +175,25 @@ AB CIP ALMD) route to AVEVA Historian via the Wonderware sidecar: 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. + silent data loss without log scraping. The drain cadence, queue + capacity, and dead-letter retention are tunable via the `AlarmHistorian` + config section (`DrainIntervalSeconds`, `Capacity`, + `DeadLetterRetentionDays`); `AlarmHistorianOptions.Validate()` logs a + startup warning for an empty `SharedSecret`, a relative `DatabasePath`, + or a non-positive knob. - `HistorianAdapterActor` (`src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Historian/HistorianAdapterActor.cs`) subscribes to the cluster `alerts` DPS topic, translates each `AlarmTransitionEvent` → `AlarmHistorianEvent`, and calls - `EnqueueAsync` fire-and-forget. Primary-gated — only the Primary node - historizes, giving exactly-once writes across a redundant pair. See + `EnqueueAsync` fire-and-forget. The durable write is gated two ways: + (1) **Primary-gated** — only the Primary node historizes, giving + exactly-once writes across a redundant pair; (2) **per-alarm** — a + transition whose `HistorizeToAveva` is `false` is skipped (the flag + rides on `AlarmTransitionEvent` as a nullable bool; missing/`null`/`true` + historize, only an explicit `false` suppresses, so a cross-version + rolling restart defaults to historizing rather than dropping an audit + row). Neither gate touches the live `alerts` publish — the `/alerts` + UI always sees every transition. See [AlarmHistorian.md §Configuration](AlarmHistorian.md#configuration) for the `AlarmHistorian` appsettings section that enables the real sink. diff --git a/docs/ScriptedAlarms.md b/docs/ScriptedAlarms.md index 881d818b..ea07808c 100644 --- a/docs/ScriptedAlarms.md +++ b/docs/ScriptedAlarms.md @@ -17,7 +17,7 @@ This file covers the engine internals — predicate evaluation, state machine, p | `Severity` | `AlarmSeverity` enum (`Low` / `Medium` / `High` / `Critical`), defined in `Core.Abstractions/IAlarmSource.cs`. Static per decision #13 — the predicate does not compute severity. The publish path bands the configured value into this four-value enum before materialising the `ScriptedAlarmDefinition`. | | `MessageTemplate` | String with `{TagPath}` placeholders, resolved at emission time. See below. | | `PredicateScriptSource` | Roslyn C# script returning `bool`. `true` = condition active; `false` = cleared. | -| `HistorizeToAveva` | When true, every emission is enqueued to `IAlarmHistorianSink`. Default true. Galaxy-native alarms default false since Galaxy historises them directly. | +| `HistorizeToAveva` | Per-alarm opt-out of **durable** historization. When `false`, `HistorianAdapterActor` skips the `IAlarmHistorianSink` write for this alarm's transitions; the live `/alerts` fan-out is unaffected (the UI still shows every transition). Default true. Galaxy-native alarms default false since Galaxy historises them directly. | | `Retain` | Part 9 retain flag — keep the condition visible after clear while un-acked/un-confirmed transitions remain. Default true. | Illustrative definition: