feat(alarms): scripted condition Quality from worst-of-input tag quality (#478) #480

Merged
dohertj2 merged 2 commits from feat/scripted-alarm-quality-478 into master 2026-07-17 16:08:09 -04:00
2 changed files with 21 additions and 0 deletions
Showing only changes of commit 043e237dba - Show all commits
+10
View File
@@ -158,6 +158,16 @@ condition's state?") — mirroring the native OT semantic:
guard's job) — it contributes `Good`, so scripted conditions don't flash `Bad` at every deploy. The first
actually-`Bad` published value flips the bucket and annotates.
**Coverage boundary (#478 as shipped).** Scripted quality tracks input tags whose driver **publishes a
data change carrying a Bad/Uncertain `StatusCode`** (e.g. an OpcUaClient input forwarding a server's
per-item Bad). It does **not** yet cover a driver **comms loss**: a poll driver (Modbus/S7) whose device
goes unreachable emits only `ConnectivityChanged` and goes *silent* on the value feed (see
`DriverInstanceActor.Reconnecting`), so the scripted engine keeps the last-known Good value and the
condition stays `Good`. Bridging driver connectivity into scripted inputs — the symmetric of the native
`OnDriverConnectivityChanged` path above, plus resolving the null-value/cold-start asymmetry (a runtime
`Bad` with a null value is currently indistinguishable from cold start and contributes `Good`) — is tracked
as the Layer-4 follow-up (#481).
Guards: `ScriptedAlarmEngineTests` (transition carries `Uncertain`; `Bad` input with no transition emits
`QualityChanged(Bad)`; restore emits `QualityChanged(Good)`; unchanged bucket emits nothing),
`ScriptedAlarmSourceTests.QualityChanged_emission_raises_no_alarm_event`,
@@ -174,3 +174,14 @@ So even a `Bad` driver value reached the scripted engine as Good — Layer 3 has
`ScriptedAlarmSource``QualityChanged` raises no `OnAlarmEvent`. Mux — `DependencyValueChanged` carries the
published quality. Host — `Bad` dependency → `AlarmQualityUpdate(Bad)`, no `/alerts` publish; `ToSnapshot`
maps the event's worst quality.
**Coverage boundary → Layer 4 (#481).** L3 covers inputs whose driver **publishes a Bad/Uncertain-status
data change** (the mux quality path). It does **not** cover a driver **comms loss**: a poll driver
(Modbus/S7) whose device goes unreachable emits only `ConnectivityChanged` and goes silent on the value feed
(`DriverInstanceActor.Reconnecting`), so the scripted engine keeps the last-known Good value and the
condition stays Good — the same silent-feed problem native solved in L2, but native's `OnDriverConnectivityChanged`
bridge fans only to **native** condition nodes (`_alarmNodeIdByDriverRef`), not into the mux the scripted
engine reads. Bridging connectivity into scripted inputs — plus resolving the null-value/cold-start
asymmetry (a runtime `Bad` with a null value is currently indistinguishable from cold start and contributes
`Good`) and its ripple into virtual-tag quality — is **Gitea #481 (Layer 4)**. Found by the post-implementation
code review; the code as shipped faithfully implements #478's written scope (mux-delivered input quality).