Merge b640-641: native alarm state survives rebuild; projector pruned; AbCip ConditionId=RawPath (#641)

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
Joseph Doherty
2026-10-08 17:26:47 -04:00
23 changed files with 1060 additions and 50 deletions
+4
View File
@@ -264,6 +264,10 @@ analog alarms are a follow-up) and **disabled by default** — shops running FT
Alarm & Events should keep it off and take alarms through the native route, since
the projection semantics don't exactly mirror Rockwell FT A&E.
Each event carries `ConditionId` = the subscribed reference (the alarm tag's RawPath — the key the host
routes native transitions on) and `Kind` = `Raise` / `Clear`. Before Gitea #641 the projection sent
`"{ref}#active"` with no kind, so the host dropped every ALMD transition as unrouted.
## Testing
- **Unit tests** — `tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/` cover the driver, host-address parser, UDT planner, and alarm projection via fake tag runtimes.
+7
View File
@@ -363,6 +363,13 @@ Every alarm transition fires `OnAlarmEvent` with:
Cleared alarms fire a second event with `" (cleared)"` appended to the message
so downstream consumers can ignore the clear if they only care about raises.
> ⚠️ **Not routable to a v3 raw condition (found under Gitea #641).** The server routes a native transition
> only when `ConditionId` equals an alarm-bearing raw tag's RawPath, and the host subscribes those RawPaths —
> but this projection filters devices by **host address** (so a RawPath list matches no device and nothing is
> polled) and emits `"{host}#{Type}:{AlarmNumber}"` with no `Kind`. FOCAS native alarms therefore reach no
> Part 9 condition today. Fixing it needs a mapping decision (the CNC exposes one flat alarm list per device,
> not one condition per tag), so it is tracked separately rather than patched.
## Handle recycling
FANUC CNCs have a finite FWLIB handle pool (~5–10 concurrent connections) and
+6 -5
View File
@@ -475,11 +475,12 @@ and records a state only when an alarm handler was attached to receive it. Conse
condition the driver owns `BadNoCommunication` on the connectivity edge (#477) and keeps its Active
state; the reconnect edge re-annotates Good, after which the driver re-asserts any still-`Unavailable`
condition's Bad quality on its re-subscribe.
- A re-subscribe with a **changed** alarm set clears the whole told record: only an alarm-bearing raw-tag
delta changes the set, the server applies that as a full rebuild, and a rebuild recreates every
condition node inactive — so every active condition is re-asserted onto its new node. The **same** set
(a reconnect) keeps the record. (A rebuild that leaves this driver's alarm set unchanged triggers no
re-subscribe; that is a host-wide gap shared with every native source.)
- A re-subscribe with a **changed** alarm set drops the told record of only the conditions that **left**
the set. The server carries every native condition's state (acknowledgement included) across a full
rebuild itself (Gitea #641 — see `docs/AlarmTracking.md`), so re-raising a surviving condition would
only un-acknowledge it; a condition that left and later comes back was materialised fresh and is told
again. The **same** set (a reconnect) keeps the whole record. A rebuild that leaves the alarm set
unchanged triggers no re-subscribe, and needs none — the server-side carry-over covers it.
- An alarm-only driver (no value tags) still runs the `/sample` pump.
**EVENT controlled vocabularies as OPC UA enumerations — not built (parked, low priority).** EVENT values (`Execution`,