Galaxy alarm severity: MxAccessSeverityMapper treats a higher AVEVA priority as more severe, but AVEVA priority 1 is the most urgent #887

Closed
opened 2026-11-12 11:03:37 -05:00 by dohertj2 · 2 comments
Owner

Found while working #886.

What. MxAccessSeverityMapper (Galaxy driver, live alarms) maps the raw value mxaccessgw sends as severity onto OPC UA Part 9 severity: < 250 → 250 (Low), < 500 → 500, < 750 → 750, else 1000 (Critical). mxaccessgw fills that field from the alarm record's Priority (AlarmDispatcher: Severity = record.Priority). In AVEVA System Platform, alarm priority runs 1–999 and 1 is the most urgent. So a priority-1 alarm projects Severity 250 (Low) and a priority-999 one projects 1000 (Critical).

Evidence (read-only, vd03 HistorianGateway ea9f21c, 2026-11-12). AVEVA stores its own coarse severity next to priority on every A&E row:

  • DevWinPlatform.CPULoadHiAlarm Alarm.Set/Clear: priority=500, severity=2 (AVEVA's High band is 251–500).
  • DevWinPlatform System.Stop / System.Start (not alarms): priority=999, severity=0.

With the current mapper a System.Start would read Severity 1000.

Scope. #886 maps AVEVA history rows' priority through the same table (shared as GalaxyAlarmPriority in Core.Abstractions) so history agrees with live. Fixing the direction belongs in that one place and changes live and history together. It is consumer-visible (ScadaBridge and any OPC UA client that ranks by Severity), so it needs an owner decision, and probably a check of how the v1 GalaxyAlarmTracker ladder was meant to be read.

Suggested mapping if inverted: priority p (1–999) → 1000 - p + 1 clamped to 1–1000, or the four AVEVA bands (1–250 Critical 1000, 251–500 High 750, 501–750 Medium 500, 751–999 Low 250).

Found while working #886. **What.** `MxAccessSeverityMapper` (Galaxy driver, live alarms) maps the raw value mxaccessgw sends as `severity` onto OPC UA Part 9 severity: `< 250` → 250 (Low), `< 500` → 500, `< 750` → 750, else 1000 (Critical). mxaccessgw fills that field from the alarm record's **Priority** (`AlarmDispatcher`: `Severity = record.Priority`). In AVEVA System Platform, alarm priority runs 1–999 and **1 is the most urgent**. So a priority-1 alarm projects Severity 250 (Low) and a priority-999 one projects 1000 (Critical). **Evidence (read-only, vd03 HistorianGateway `ea9f21c`, 2026-11-12).** AVEVA stores its own coarse `severity` next to `priority` on every A&E row: - `DevWinPlatform.CPULoadHiAlarm` Alarm.Set/Clear: `priority=500`, `severity=2` (AVEVA's High band is 251–500). - `DevWinPlatform` System.Stop / System.Start (not alarms): `priority=999`, `severity=0`. With the current mapper a System.Start would read Severity 1000. **Scope.** #886 maps AVEVA history rows' `priority` through the same table (shared as `GalaxyAlarmPriority` in Core.Abstractions) so history agrees with live. Fixing the direction belongs in that one place and changes live and history together. It is consumer-visible (ScadaBridge and any OPC UA client that ranks by Severity), so it needs an owner decision, and probably a check of how the v1 `GalaxyAlarmTracker` ladder was meant to be read. Suggested mapping if inverted: priority p (1–999) → `1000 - p + 1` clamped to 1–1000, or the four AVEVA bands (1–250 Critical 1000, 251–500 High 750, 501–750 Medium 500, 751–999 Low 250).
dohertj2 added the priority:p2type:bug labels 2026-11-12 11:03:41 -05:00
Author
Owner

Context: #886 (master 3a312ffd) maps AVEVA history rows' priority through the same table (GalaxyAlarmPriority in Core.Abstractions; MxAccessSeverityMapper now delegates to it). A fix here changes live and history severity together. On vd03 the AVEVA rows now read 750 for alarms with priority 500/700 and 1000 for priority-999 system/user events.

Context: #886 (master `3a312ffd`) maps AVEVA history rows' `priority` through the same table (`GalaxyAlarmPriority` in Core.Abstractions; `MxAccessSeverityMapper` now delegates to it). A fix here changes live and history severity together. On vd03 the AVEVA rows now read 750 for alarms with priority 500/700 and 1000 for priority-999 system/user events.
Author
Owner

Fixed on master b61f3e05. AVEVA priority 1 is now the most urgent, on live Galaxy alarms and AVEVA history alike. Design: docs/Historian.md § "Galaxy alarm priority → Severity (lmxopcua#887)".

Band table (GalaxyAlarmPriority, the one table both paths use):

AVEVA priority AVEVA severity Bucket OPC UA Severity
1–250 1 Critical Critical 1000
251–500 2 High High 750
501–750 3 Medium Medium 500
751–999 4 Low Low 250
any (history only), AVEVA severity 0: System.*, User.* 0 (not an alarm) – 1
  • Out-of-range priorities clamp: 0 or below reads Critical; above 999 reads Low.

  • Evidence (vd03 Runtime.dbo.Events, read-only SQL). These match AVEVA's documented defaults:

    • priority 1 and 100 alarms carry AVEVA severity 1;
    • 400 and 500 carry severity 2;
    • 600 and 700 carry severity 3;
    • every priority-999 System.* / User.* row carries severity 0 with IsAlarm 0.

    vd03 has no alarm in 751–999, so the Low edge rests on AVEVA's documentation. A Galaxy can reconfigure the ranges. The live feed carries only the priority, so both paths assume the defaults and always agree.

  • Live and history now carry the same number. Before, GalaxyDriver passed only the bucket, so NativeAlarmProjector re-mapped it through its generic 200/500/700/900 ladder, while history used 250/500/750/1000. The driver now sets AlarmEventArgs.OpcUaSeverity from the table.

  • System and user events: AVEVA severity 0 marks an event that is not an alarm, and it now reads Severity 1, below every alarm band. For alarms, AVEVA's own 1–4 severity is still never used as the value: live cannot see it, and history must match live.

Tests

  • Updated (they pinned the inverted table):
    • MxAccessSeverityMapperTests, rewritten: every band edge, clamps, a monotonic sweep over 1–999, and an equality check with the shared table.
    • GatewayGalaxyAlarmFeedTests.
    • EventMapperTests.
    • GalaxyEquipmentEventHistoryWireTests.
  • New:
    • NativeAlarmRestatementEndToEndTests.Galaxy_live_condition_severity_is_the_AVEVA_priority_band (8 rows). It runs the real feed → GalaxyDriver → host → projector, and pins the same rows EventMapperTests pins on the history path.
    • EventMapperTests.Aveva_severity_zero_marks_an_event_that_is_not_an_alarm, including a positive control: a priority-999 alarm reads 250.
    • A GalaxyDriverAlarmSourceTests pass-through assertion.
  • Mutation check: 8 mutants, all killed:
    • the inverted table;
    • the driver dropping OpcUaSeverity;
    • no not-an-alarm rule;
    • each band edge off by one;
    • history ignoring priority;
    • a missing AVEVA severity read as 0.
  • Suites: Core.Abstractions 745, Galaxy 584, Historian.Gateway 559, Runtime 1037, OpcUaServer 1275 and OpcUaServer.IntegrationTests 318 all passed with 0 failed. After the rebase onto fa811feb (#878, unrelated files) the build and the event-history and alarm tests are green again.

Live re-run: read-only, network-only, no WinRM. It ran the #801/#886 harness against vd03 HistorianGateway ea9f21c. The key was read over SMB from the OtOpcUa registry export, never printed, and deleted afterwards.

  • Against the #886 run, 224 of 233 cases are identical. The other 9 differ only in Severity.
  • On EQ-886-ZipsenLeft:
    • 70 priority-500 CPULoadHiAlarm rows stay at 750;
    • 3 priority-700 FRR_Warning rows go 750 → 500;
    • 19 system/user events go 1000 → 1.
  • Record: docs/plans/evidence/2026-11-10-opcua2/aveva-history-801.md § "#887 re-run".

Consumer impact. This is client-visible: every OPC UA client that ranks, filters or colours Galaxy alarms by Severity sees the order reversed, both live and in HistoryReadEvents. Not deployed.

  • ScadaBridge, OPC UA adapter / NativeAlarmActor: nothing depends on the old inverted values. Its consumers already treat a higher Severity as worse:

    • ErrorSeverityThreshold = 700;
    • Alarm Summary's worst = max and its >= filter.

    So this fixes Galaxy-via-OtOpcUa there. No deployed ScadaBridge reads Galaxy alarms through OtOpcUa: vd03 ScadaBridge has no OPC UA link to OtOpcUa, and vd03 OtOpcUa has no Galaxy driver.

  • ScadaBridge, MxGateway adapter (which bypasses OtOpcUa): it is not correct today. MxGatewayAlarmMapper clamps the raw value, which is the AVEVA priority, into Severity, so that path ranks lower = more urgent while the consumers read higher = worse. Computed alarms do the same with PriorityLevel.

  • The MES 900–999 band: it is set on the Galaxy priority, so it works only on the MxGateway path. On the OtOpcUa path it never matched, before or after this fix. It is also AVEVA's Low band.

    Filed as ScadaBridge#129 (owner decision: one Severity direction plus where the native MES flag lives). Nothing changed in ScadaBridge here.

  • mxaccessgw: no change. Its proto comment ("Mapping to OPC UA 0-1000 happens server-side in lmxopcua via MxAccessSeverityMapper") stays true.

**Fixed on master `b61f3e05`.** AVEVA priority 1 is now the most urgent, on live Galaxy alarms and AVEVA history alike. Design: `docs/Historian.md` § "Galaxy alarm priority → Severity (lmxopcua#887)". **Band table** (`GalaxyAlarmPriority`, the one table both paths use): | AVEVA priority | AVEVA severity | Bucket | OPC UA Severity | |---|---|---|---| | 1–250 | 1 Critical | `Critical` | 1000 | | 251–500 | 2 High | `High` | 750 | | 501–750 | 3 Medium | `Medium` | 500 | | 751–999 | 4 Low | `Low` | 250 | | any (history only), AVEVA `severity` 0: `System.*`, `User.*` | 0 (not an alarm) | – | 1 | - **Out-of-range priorities clamp:** 0 or below reads Critical; above 999 reads Low. - **Evidence (vd03 `Runtime.dbo.Events`, read-only SQL).** These match AVEVA's documented defaults: - priority 1 and 100 alarms carry AVEVA severity 1; - 400 and 500 carry severity 2; - 600 and 700 carry severity 3; - every priority-999 `System.*` / `User.*` row carries severity 0 with `IsAlarm` 0. vd03 has no alarm in 751–999, so the Low edge rests on AVEVA's documentation. A Galaxy can reconfigure the ranges. The live feed carries only the priority, so both paths assume the defaults and always agree. - **Live and history now carry the same number.** Before, `GalaxyDriver` passed only the bucket, so `NativeAlarmProjector` re-mapped it through its generic 200/500/700/900 ladder, while history used 250/500/750/1000. The driver now sets `AlarmEventArgs.OpcUaSeverity` from the table. - **System and user events:** AVEVA `severity` 0 marks an event that is not an alarm, and it now reads Severity 1, below every alarm band. For alarms, AVEVA's own 1–4 severity is still never used as the value: live cannot see it, and history must match live. **Tests** - **Updated (they pinned the inverted table):** - `MxAccessSeverityMapperTests`, rewritten: every band edge, clamps, a monotonic sweep over 1–999, and an equality check with the shared table. - `GatewayGalaxyAlarmFeedTests`. - `EventMapperTests`. - `GalaxyEquipmentEventHistoryWireTests`. - **New:** - `NativeAlarmRestatementEndToEndTests.Galaxy_live_condition_severity_is_the_AVEVA_priority_band` (8 rows). It runs the real feed → `GalaxyDriver` → host → projector, and pins the same rows `EventMapperTests` pins on the history path. - `EventMapperTests.Aveva_severity_zero_marks_an_event_that_is_not_an_alarm`, including a positive control: a priority-999 alarm reads 250. - A `GalaxyDriverAlarmSourceTests` pass-through assertion. - **Mutation check:** 8 mutants, all killed: - the inverted table; - the driver dropping `OpcUaSeverity`; - no not-an-alarm rule; - each band edge off by one; - history ignoring priority; - a missing AVEVA severity read as 0. - **Suites:** Core.Abstractions 745, Galaxy 584, Historian.Gateway 559, Runtime 1037, OpcUaServer 1275 and OpcUaServer.IntegrationTests 318 all passed with 0 failed. After the rebase onto `fa811feb` (#878, unrelated files) the build and the event-history and alarm tests are green again. **Live re-run: read-only, network-only, no WinRM.** It ran the #801/#886 harness against vd03 HistorianGateway `ea9f21c`. The key was read over SMB from the OtOpcUa registry export, never printed, and deleted afterwards. - Against the #886 run, 224 of 233 cases are identical. The other 9 differ only in Severity. - On `EQ-886-ZipsenLeft`: - 70 priority-500 `CPULoadHiAlarm` rows stay at 750; - 3 priority-700 `FRR_Warning` rows go 750 → **500**; - 19 system/user events go 1000 → **1**. - Record: `docs/plans/evidence/2026-11-10-opcua2/aveva-history-801.md` § "#887 re-run". **Consumer impact.** This is client-visible: every OPC UA client that ranks, filters or colours Galaxy alarms by Severity sees the order reversed, both live and in HistoryReadEvents. Not deployed. - **ScadaBridge, OPC UA adapter / `NativeAlarmActor`:** nothing depends on the old inverted values. Its consumers already treat a higher Severity as worse: - `ErrorSeverityThreshold = 700`; - Alarm Summary's worst = max and its `>=` filter. So this fixes Galaxy-via-OtOpcUa there. No deployed ScadaBridge reads Galaxy alarms through OtOpcUa: vd03 ScadaBridge has no OPC UA link to OtOpcUa, and vd03 OtOpcUa has no Galaxy driver. - **ScadaBridge, MxGateway adapter (which bypasses OtOpcUa):** it is not correct today. `MxGatewayAlarmMapper` clamps the raw value, which is the AVEVA priority, into `Severity`, so that path ranks lower = more urgent while the consumers read higher = worse. Computed alarms do the same with `PriorityLevel`. - **The MES 900–999 band:** it is set on the Galaxy priority, so it works only on the MxGateway path. On the OtOpcUa path it never matched, before or after this fix. It is also AVEVA's Low band. Filed as **ScadaBridge#129** (owner decision: one Severity direction plus where the native MES flag lives). Nothing changed in ScadaBridge here. - **mxaccessgw:** no change. Its proto comment ("Mapping to OPC UA 0-1000 happens server-side in lmxopcua via MxAccessSeverityMapper") stays true.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#887