fix(alarms): populate ConditionClassId/ConditionClassName on condition events (#475) #476
Reference in New Issue
Block a user
Delete Branch "fix/alarm-condition-class-fields"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes #475.
The bug
MaterialiseAlarmConditionnever assigned the mandatory Part 9ConditionTypeclassification fields, so every condition event — native and scripted — shipped them unset. Same mechanism as #473:Create()builds the mandatory children from the type's embedded definition but leaves.Valueunset, and nothing downstream synthesises them (ReportEvent/InstanceStateSnapshotcopy children verbatim).Observed on the wire against the pre-fix server (probe on a live condition event via a real client subscription):
An HMI bucketing alarms by condition class — the standard OPC UA categorization — dropped every OtOpcUa alarm into an unclassified bin.
The fix (2 lines)
Why
BaseConditionClassType, deliberately notProcessConditionClassTypeBaseConditionClassTypeis Part 9's "this server does not model condition classes" value, and it is the honest report: we hold no classification at the materialise seam. It fixes the real defect — a null that breaks conformant clients — by giving them a valid, resolvable class NodeId, without asserting a classification we cannot back.ProcessConditionClassType(the SDKAlarmConditionServersample's choice) was rejected on purpose: it would be actively wrong for a Galaxy alarm whose upstream category is Safety or Diagnostics. That trades a detectable null for an undetectable lie, which is strictly worse.Real per-alarm classification is a separate future feature, not built here. It needs the driver's
AlarmCategory, which today lives only on the runtimeAlarmEventArgstransition — not on the authored composition this deploy-time seam sees — and onlyGalaxyDriverpopulates it. Wiring it needs a category → ConditionClass mapping plus a way to carry the category to materialise. Until then theIAlarmSource.cs:79doc comment claiming the category "maps toConditionClassNamedownstream" describes an intent, not the implementation; the docs now say so explicitly.Tests — TDD, both observed RED first
Every test was written and watched fail against the pre-fix server before the production change existed (no stash needed — the fix genuinely was not in the file yet).
Wire-level (
NativeAlarmEventIdentityFieldDeliveryTests) — RED:The event did arrive and the field was delivered — it just carried
NodeId.Null. So this is a genuine value bug, not a select-clause artifact.Node-level (
NodeManagerAlarmSourceFieldsTests) — RED on both realms, native (Raw) and scripted (Uns), sameshould be i=11163 but was i=0.Two deliberate test-design notes:
EventCollector's message index a ctor param, since the two clauses placeMessagedifferently.ConditionClassId/ConditionClassNameare declared onConditionType, notBaseEventType, so they are selected against that type — selecting them offBaseEventTypereturns no value regardless of the fix. Documented for client authors.Verification
Docs
docs/AlarmTracking.md— the "Condition event identity fields" section gains both fields in the table, the BaseConditionClassType-vs-Process rationale, and theConditionType-not-BaseEventTypeselect-clause gotcha.Incidental finding:
Quality— NOT the same bug, do not fold inThe #474 review flagged that
Qualitymay be similarly unset. Investigated empirically; reporting only, deliberately not fixed here.It is never assigned anywhere in the tree (confirmed by grep — the sole
.Qualityhit insrc/is an unrelated variable status code inVirtualTagHostActor). But it does not arrive null on the wire:default(StatusCode)is0, which isStatusCodes.Good— so it is accidentally-valid rather than broken. That makes it a different and lesser defect than #473/#475: conformant clients handle it fine, nothing null-derefs.The real (smaller) issue: it hardcodes Good for every condition regardless of the backing tag's actual quality — a condition whose source tag is Bad/Uncertain still reports Good. That is a semantic correctness gap needing the driver's quality plumbed to the materialise/write seam, not a 2-line default, so it does not belong in this PR. Worth a separate issue at lower priority.
(Also observed while probing, no action implied:
ClientUserIdarrives null pre-ack, which is defensible per Part 9 — it is set when a condition method is invoked.)View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.