Native AVEVA alarm events on an attribute are not readable per source (source is <Object>.<Attribute>, folder notifier is the object) #882

Closed
opened 2026-11-12 10:12:21 -05:00 by dohertj2 · 1 comment
Owner

HistoryReadEvents for the source DevWinPlatform.CPULoadAvg returns GoodNoData, although HistorianGateway returns 70 such events in the window with that source name. Object-level events (DevWinPlatform: System.Stop/Start) are returned correctly.

The gateway names attribute alarms <Object>.<Attribute>, but OtOpcUa's equipment-folder event notifier filters on the object-level name. Native attribute alarms therefore can't be reached from an equipment folder's event history.

To decide: should a folder's event history include <Object>.* sources (prefix match), or should attribute-level notifiers be exposed? Check the ACL implications, since per-folder event ACL (#826/#838) keys on the folder. Coordinate with historiangw if the source naming should change instead.

Evidence: lmxopcua#801 AVEVA history gate (comment 11735), OtOpcUa master 008f0b36 docs/plans/evidence/2026-11-10-opcua2/aveva-history-801.md; harness AvevaGatewayHistoryLiveGateTests (f607ee75). Reproduces identically on SDK 1.5.378.182 (efb9197a) and SDK 2.0.1, so it is not an SDK2 regression.

HistoryReadEvents for the source `DevWinPlatform.CPULoadAvg` returns `GoodNoData`, although HistorianGateway returns 70 such events in the window with that source name. Object-level events (`DevWinPlatform`: System.Stop/Start) are returned correctly. The gateway names attribute alarms `<Object>.<Attribute>`, but OtOpcUa's equipment-folder event notifier filters on the object-level name. Native attribute alarms therefore can't be reached from an equipment folder's event history. To decide: should a folder's event history include `<Object>.*` sources (prefix match), or should attribute-level notifiers be exposed? Check the ACL implications, since per-folder event ACL (#826/#838) keys on the folder. Coordinate with historiangw if the source naming should change instead. Evidence: lmxopcua#801 AVEVA history gate (comment 11735), OtOpcUa master `008f0b36` `docs/plans/evidence/2026-11-10-opcua2/aveva-history-801.md`; harness `AvevaGatewayHistoryLiveGateTests` (`f607ee75`). Reproduces identically on SDK 1.5.378.182 (`efb9197a`) and SDK 2.0.1, so it is not an SDK2 regression.
dohertj2 added the type:bugpriority:p2cross-repo labels 2026-11-12 10:12:21 -05:00
Author
Owner

Fixed on master da948e5f, OtOpcUa only. No HistorianGateway wire change.

Root cause. OtOpcUa sends a per-source ReadEvents as Source_Object = <source>. The gateway reports each row's SourceName as Source_ProcessVariable, falling back to Source_Object. A read-only probe of vd03's gateway ea9f21c for Source_Object = DevWinPlatform over the #801 window returned 72 rows: 2 DevWinPlatform (System.Stop/Start) and 70 DevWinPlatform.CPULoadAvg (Alarm.Set/Clear, source_object=DevWinPlatform, source_conditionvariable=DevWinPlatform.CPULoadHiAlarm). The gateway was already returning the attribute alarms in one request. OtOpcUa's defensive client-side filter in GatewayHistorianDataSource compared SourceName for equality, so it threw 70 of them away. A read for Source_Object = DevWinPlatform.CPULoadAvg returns 0 rows, as expected, because that name is not an object.

Design (the default from the issue). A notifier folder's event history includes events whose source is the folder's source or <source>. + anything. The match is on the . boundary and ordinal (Mapping/EventSourceMatch). DevWinPlatform never matches DevWinPlatform2, DevWinPlatform2.X or DevWinPlatform_X. The rule applies to the page filter and to the past-the-cap "more events" signal. It still makes one request per page.

  • Live parity. A native condition fires live on its Raw parent folder and every referencing UNS equipment folder, so an object's folder already shows its attribute alarms live. History now agrees.
  • SourceName / SourceNode. Each event keeps the source the historian reported (the attribute's, for an attribute alarm). SourceNode is unchanged: it is the condition the stored alarm id names, so it is null for AVEVA rows.
  • Not added. No attribute-level notifier exists. A folder named after an attribute still answers GoodNoData.
  • Other backends. SqlServer and QuestDB keep an exact match, because they hold only OtOpcUa's own events.

ACL. The attribute events come from the notifier's own HistoryRead, so the per-folder verdict governs them (AuthorizeHistoryRead, #826). With Security:Acl:Enabled, a session denied the folder gets BadUserAccessDenied with no HistoryData, and the backend is never called (fail closed). No per-event path bypasses it.

Tests (rebased on #881 b27b5b9c; Gateway backend still declares Events):

  • GatewayAttributeEventSourceTests, 16 tests:
    • a 13-case boundary theory;
    • object read keeps attribute rows and drops DevWinPlatform2 / DevWinPlatform2.X / DevWinPlatformX.X. The sibling rows are asserted to have reached the filter (negative control);
    • an attribute row past the cap signals more, and a sibling row doesn't;
    • an attribute-named read stays exact.
  • AttributeEventHistoryWireTests, 2 tests, over a real SDK session with the production Gateway backend on an AVEVA-shaped deterministic gateway that filters loosely, so the sibling reaches OtOpcUa:
    • ACL off: object + attribute events with their own SourceName, null SourceNode, sibling excluded, the same 4 across 1-event pages, one request on the object;
    • ACL on: a denied folder gets BadUserAccessDenied, none of its events, and zero backend requests. Positive control: the same session on its granted folder gets that object's attribute alarm.
  • Mutation checks:
    • Restoring the exact match fails both wire tests.
    • Dropping the . boundary fails the wire sibling check and 6 unit tests.
    • Both mutations were restored.
  • Suites: Gateway.Tests 539 passed / 6 skipped / 0 failed. OpcUaServer.Tests HistoryReadEvents + CapabilityRefresh 53/0. IntegrationTests event history + #881 capabilities + history authority 29/0.

Live re-run (vd03 HistorianGateway ea9f21c, read-only, no WinRM, API key read over SMB and not printed, local copy deleted). AvevaGatewayHistoryLiveGateTests now asserts #882:

  • Folder DevWinPlatform: 2 → 72 events (whole read, 72 one-event pages, reverse), exactly the unfiltered read's rows named DevWinPlatform or DevWinPlatform.*.
  • DevWinPlatform.CPULoadAvg folder: still GoodNoData.
  • Against the #801 SDK2 baseline: 221 of 225 shared cases are identical, and the 4 that differ are exactly the DevWinPlatform event cases. Harness failures: 0.
  • Recorded in docs/plans/evidence/2026-11-10-opcua2/aveva-history-801.md § "#882 re-run".

Not covered: filed #886. A production folder's source is its NodeId identifier (a RawPath or an EquipmentId), never a Galaxy tag_name. So AVEVA's own A&E history reaches a folder only where an EquipmentId happens to equal the object name. The #801 harness names the folder after the object. #886 also covers AVEVA's lower-case property keys: priority and alarm_id are not mapped, so Severity is 1 and ConditionName/SourceNode are null on AVEVA rows.

Cross-link. historiangw#133 (closed) carried a "should the gateway naming change?" note. It doesn't need to: the gateway's Source_Object filter is the right granularity.

Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi

**Fixed on master `da948e5f`, OtOpcUa only. No HistorianGateway wire change.** **Root cause.** OtOpcUa sends a per-source `ReadEvents` as `Source_Object = <source>`. The gateway reports each row's `SourceName` as `Source_ProcessVariable`, falling back to `Source_Object`. A read-only probe of vd03's gateway `ea9f21c` for `Source_Object = DevWinPlatform` over the #801 window returned **72 rows**: 2 `DevWinPlatform` (System.Stop/Start) and 70 `DevWinPlatform.CPULoadAvg` (Alarm.Set/Clear, `source_object=DevWinPlatform`, `source_conditionvariable=DevWinPlatform.CPULoadHiAlarm`). The gateway was already returning the attribute alarms in one request. OtOpcUa's defensive client-side filter in `GatewayHistorianDataSource` compared `SourceName` for equality, so it threw 70 of them away. A read for `Source_Object = DevWinPlatform.CPULoadAvg` returns 0 rows, as expected, because that name is not an object. **Design (the default from the issue).** A notifier folder's event history includes events whose source is the folder's source **or `<source>.` + anything**. The match is on the `.` boundary and ordinal (`Mapping/EventSourceMatch`). `DevWinPlatform` never matches `DevWinPlatform2`, `DevWinPlatform2.X` or `DevWinPlatform_X`. The rule applies to the page filter and to the past-the-cap "more events" signal. It still makes one request per page. - **Live parity.** A native condition fires live on its Raw parent folder and every referencing UNS equipment folder, so an object's folder already shows its attribute alarms live. History now agrees. - **SourceName / SourceNode.** Each event keeps the source the historian reported (the attribute's, for an attribute alarm). SourceNode is unchanged: it is the condition the stored alarm id names, so it is null for AVEVA rows. - **Not added.** No attribute-level notifier exists. A folder named after an attribute still answers `GoodNoData`. - **Other backends.** SqlServer and QuestDB keep an exact match, because they hold only OtOpcUa's own events. **ACL.** The attribute events come from the notifier's own HistoryRead, so the per-folder verdict governs them (`AuthorizeHistoryRead`, #826). With `Security:Acl:Enabled`, a session denied the folder gets `BadUserAccessDenied` with no HistoryData, and the backend is **never called** (fail closed). No per-event path bypasses it. **Tests** (rebased on #881 `b27b5b9c`; Gateway backend still declares Events): - `GatewayAttributeEventSourceTests`, 16 tests: - a 13-case boundary theory; - object read keeps attribute rows and drops `DevWinPlatform2` / `DevWinPlatform2.X` / `DevWinPlatformX.X`. The sibling rows are asserted to have reached the filter (negative control); - an attribute row past the cap signals more, and a sibling row doesn't; - an attribute-named read stays exact. - `AttributeEventHistoryWireTests`, 2 tests, over a real SDK session with the production Gateway backend on an AVEVA-shaped deterministic gateway that filters loosely, so the sibling reaches OtOpcUa: - ACL off: object + attribute events with their own SourceName, null SourceNode, sibling excluded, the same 4 across 1-event pages, one request on the object; - ACL on: a denied folder gets `BadUserAccessDenied`, none of its events, and zero backend requests. Positive control: the same session on its granted folder gets that object's attribute alarm. - **Mutation checks:** - Restoring the exact match fails both wire tests. - Dropping the `.` boundary fails the wire sibling check and 6 unit tests. - Both mutations were restored. - **Suites:** Gateway.Tests 539 passed / 6 skipped / 0 failed. OpcUaServer.Tests HistoryReadEvents + CapabilityRefresh 53/0. IntegrationTests event history + #881 capabilities + history authority 29/0. **Live re-run** (vd03 HistorianGateway `ea9f21c`, read-only, no WinRM, API key read over SMB and not printed, local copy deleted). `AvevaGatewayHistoryLiveGateTests` now asserts #882: - Folder `DevWinPlatform`: **2 → 72 events** (whole read, 72 one-event pages, reverse), exactly the unfiltered read's rows named `DevWinPlatform` or `DevWinPlatform.*`. - `DevWinPlatform.CPULoadAvg` folder: still `GoodNoData`. - Against the #801 SDK2 baseline: 221 of 225 shared cases are identical, and the 4 that differ are exactly the `DevWinPlatform` event cases. Harness failures: 0. - Recorded in `docs/plans/evidence/2026-11-10-opcua2/aveva-history-801.md` § "#882 re-run". **Not covered: filed #886.** A production folder's source is its NodeId identifier (a RawPath or an EquipmentId), never a Galaxy `tag_name`. So AVEVA's own A&E history reaches a folder only where an EquipmentId happens to equal the object name. The #801 harness names the folder after the object. #886 also covers AVEVA's lower-case property keys: `priority` and `alarm_id` are not mapped, so Severity is 1 and ConditionName/SourceNode are null on AVEVA rows. **Cross-link.** historiangw#133 (closed) carried a "should the gateway naming change?" note. It doesn't need to: the gateway's `Source_Object` filter is the right granularity. Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#882