fix(alarms): #647 re-review — re-statement needs the same message; CNC refusal is not comms loss; worst-of health
R1: NativeAlarmProjector treats Raise-on-Active as a re-statement only when the message matches what
the condition shows; a different alarm appearing during a respawn un-acks and writes its alerts row.
FOCAS condition messages are a deterministic function of the matched set (tested across fresh driver
instances and reordered lists); Runtime E2E now covers both the same alarm and a new one during a
respawn. Residual (source message determinism) documented in AlarmTracking.md.
R2: a non-OK cnc_rdalmmsg2 return is thrown as FocasWireException{Rc} and classified as a CNC
refusal: conditions get a quality-only BadNotSupported once, one Warning, polling continues, and
driver health is untouched (no 90 s watchdog reconnect loop on a production control). Only transport
faults degrade (falsification control in the test).
R3: alarm-path health is tracked apart from the value path and combined in GetHealth: worst state,
later LastSuccessfulRead. A good value read can no longer mask a failing alarm read (no per-poll
/hosts flip) and a good alarm read cannot mask a value Degraded.
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
+15
-3
@@ -405,16 +405,28 @@ it, then author `ALARM:T<code>`. Number ranges are series-independent.
|
||||
- **…but an unreachable source says so.** The first failed read of an outage annotates every subscribed condition
|
||||
on that device `BadNoCommunication` through the #626 quality-only path (no state change, no `/alerts` row) and
|
||||
logs ONE Warning naming `cnc_rdalmmsg2`; the next successful read lifts it to Good (before any transition it
|
||||
also finds, e.g. Good then Clear for an alarm that cleared during the outage). Failed alarm reads also reach
|
||||
also finds, e.g. Good then Clear for an alarm that cleared during the outage). Transport failures also reach
|
||||
driver health: `Degraded` with `LastSuccessfulRead` frozen (only a successful read advances it), so a driver
|
||||
whose only tags are `ALARM:` tags trips the comms-loss watchdog (#527) and the host's driver-wide #477
|
||||
annotation like any other.
|
||||
- **A CNC refusal is not comms loss.** A non-OK FOCAS return from `cnc_rdalmmsg2` (the CNC answered and declined,
|
||||
e.g. `EW_FUNC`) marks the conditions `BadNotSupported` (quality-only, once), logs one Warning, and **leaves driver
|
||||
health alone** — polling continues at the normal cadence. Treating it as comms loss would make the #527 watchdog
|
||||
reconnect a production control every 90 s forever. Only a transport fault (session down, wire fault, malformed
|
||||
reply) degrades.
|
||||
- **Health is the worst of the value path and the alarm path, computed together** (`GetHealth`). A failing alarm
|
||||
read keeps a mixed driver `Degraded` however many value reads succeed (so `/hosts` does not flip per poll), and a
|
||||
good alarm read cannot mask a value-path `Degraded`. `LastSuccessfulRead` is the later of the two paths' last
|
||||
successes, so the watchdog fires only when both have stopped.
|
||||
- **Diffed against what the server was TOLD**, recorded only while a handler is attached. The record lives on the
|
||||
driver instance: a reconnect (same-set re-subscribe) and an in-place re-initialise (Force Reconnect, #630) re-raise
|
||||
nothing. **A deploy that changes the driver's config respawns it** (the planner stops + recreates the driver, a new
|
||||
instance with an empty record), and its first poll sends `Raise` for every still-active alarm. The host treats a
|
||||
`Raise` on a condition it already shows Active as a **re-statement**: the acknowledgement is kept and no second
|
||||
"Activated" `/alerts` row is written (`NativeAlarmProjector`, #647 review). Only `Retrigger` — or inactive →
|
||||
`Raise` on a condition it already shows Active **with the same message** as a **re-statement**: the
|
||||
acknowledgement is kept and no second "Activated" `/alerts` row is written (`NativeAlarmProjector`, #647 review).
|
||||
The condition message is a deterministic function of the matched set (winner, type code, axes, `[+N more]`), so a
|
||||
true re-statement matches exactly, while an alarm set that changed during the respawn — including one that only
|
||||
LOST a member — reads as a new statement and un-acks (the conservative direction). Only `Retrigger` — or inactive →
|
||||
active — un-acknowledges, which is why a genuinely new CNC alarm in a matched set is sent as `Retrigger`.
|
||||
- **Polling:** one `cnc_rdalmmsg2` per `AlarmProjection.PollInterval` (default 2 s) for each device that owns a
|
||||
**subscribed** alarm tag; devices without one are never polled for alarms.
|
||||
|
||||
Reference in New Issue
Block a user