Galaxy StatusCodeMap: Warning/Pending status rows and the detected_by comms-error rule (#866 review residuals) #872

Closed
opened 2026-11-11 15:14:35 -05:00 by dohertj2 · 6 comments
Owner

Residuals from the #866 review (StatusCodeMap.FromMxStatus); neither is a regression and neither has live evidence yet.

  1. Warning / Pending categories. A status row whose category is Warning or Pending with a non-zero detail is not IsSuccess() and falls to plain Bad (with a once-per-pair warning). An MXAccess Warning is arguably Uncertain rather than a failure. Capture a real Warning/Pending row before choosing.
  2. RawDetectedBy != 0 ⇒ BadCommunicationError (detail 0 failure rows). The rule assumes non-zero detected_by means a pre-MXAccess transport failure, but in the worker converter's numbering 0 is RequestingLmx and 1–5 are LMX/NMX/automation-object sources. A failure the responding automation object detected becomes a comms error. Re-derive the rule from the gateway's MapSource and fix it with the test that pins it (FromMxStatus_SuccessZero_DetailZero_DetectedByNonZero_IsCommunicationError).
Residuals from the #866 review (`StatusCodeMap.FromMxStatus`); neither is a regression and neither has live evidence yet. 1. **Warning / Pending categories.** A status row whose category is `Warning` or `Pending` with a non-zero detail is not `IsSuccess()` and falls to plain `Bad` (with a once-per-pair warning). An MXAccess Warning is arguably Uncertain rather than a failure. Capture a real Warning/Pending row before choosing. 2. **`RawDetectedBy != 0 ⇒ BadCommunicationError`** (detail 0 failure rows). The rule assumes non-zero `detected_by` means a pre-MXAccess transport failure, but in the worker converter's numbering 0 is `RequestingLmx` and 1–5 are LMX/NMX/automation-object sources. A failure the responding automation object detected becomes a comms error. Re-derive the rule from the gateway's `MapSource` and fix it with the test that pins it (`FromMxStatus_SuccessZero_DetailZero_DetectedByNonZero_IsCommunicationError`).
dohertj2 added the type:followuppriority:p3 labels 2026-11-11 15:14:35 -05:00
Author
Owner

Item 2 fixed in 1a08b4f7 (master 363f0d81). Every raw detected_by value names an MXAccess layer: 0 requesting LMX, 1 responding LMX, 2 and 3 NMX, 4 and 5 automation object. The gateway's own failures arrive as ProtocolStatus, so the "non-zero source = transport" rule had no basis. Failure rows with detail 0 now route on their category. Item 1 (Warning/Pending categories) stays open until a real row is captured.

Item 2 fixed in `1a08b4f7` (master `363f0d81`). Every raw `detected_by` value names an MXAccess layer: 0 requesting LMX, 1 responding LMX, 2 and 3 NMX, 4 and 5 automation object. The gateway's own failures arrive as `ProtocolStatus`, so the "non-zero source = transport" rule had no basis. Failure rows with detail 0 now route on their category. Item 1 (Warning/Pending categories) stays open until a real row is captured.
Author
Owner

Part 2 (detected_by rule): the rule itself was already removed on master by 1a08b4f7 — detail-0 failure rows now map on category only. 633f4317 adds per-source coverage (raw -1, 0–5, 77 × 7 categories) plus the live-captured shape from mxgw docs/AlarmProbeFindings.md:242-244 (OperationalError detected by RespondingAutomationObject must not become BadCommunicationError); mutation-checked against the old rule (10 cases fail). Source: mxgw MxStatusProxyConverter.MapSource (Worker/Conversion/MxStatusProxyConverter.cs:244-256); proto says branch on category. Done.

Part 1 (Warning/Pending → currently Bad): unchanged. No live Warning/Pending row exists and neither the worker (only maps raw 1→Pending, 2→Warning) nor the docs define their semantics. Leaving this issue open + blocked on a live capture.

Part 2 (detected_by rule): the rule itself was already removed on master by 1a08b4f7 — detail-0 failure rows now map on category only. 633f4317 adds per-source coverage (raw -1, 0–5, 77 × 7 categories) plus the live-captured shape from mxgw docs/AlarmProbeFindings.md:242-244 (OperationalError detected by RespondingAutomationObject must not become BadCommunicationError); mutation-checked against the old rule (10 cases fail). Source: mxgw MxStatusProxyConverter.MapSource (Worker/Conversion/MxStatusProxyConverter.cs:244-256); proto says branch on category. **Done.** Part 1 (Warning/Pending → currently Bad): unchanged. No live Warning/Pending row exists and neither the worker (only maps raw 1→Pending, 2→Warning) nor the docs define their semantics. Leaving this issue open + blocked on a live capture.
dohertj2 added the blocked label 2026-11-12 07:18:27 -05:00
Author
Owner

Item 1: live capture attempt on windev (DEV Galaxy through mxaccessgw 447070a5, 2026-11-13 UTC). Evidence in mxaccessgw docs/LiveCaptures-2026-11-13.md §3 (commit 9e4be258).

No Warning row (raw category 2) was produced by any of these:

  • out-of-range writes (1004)
  • type mismatches (8001)
  • NaN/Inf
  • array resize
  • off-scan, undeployed (detail 8) and unconnected-I/O objects (quality 32→4 with an Ok row)
  • read-only (1007), secured and verified writes (1008 / 8018)
  • alarm-config writes
  • Suspend/Activate edge cases

Pending (raw 1) was captured, but only as the synchronous return of Suspend/Activate on an advised, resolved item: success = -1, detail 0, RequestingLmx. MXAccess then confirms it with an OperationComplete Ok row. It never appeared on an OnDataChange, OnWriteComplete or write-reply row, which are the rows the Galaxy driver feeds to StatusCodeMap.FromMxStatus. OtOpcUa never calls Suspend/Activate. So a Pending row with non-zero detail, the case this item names, was not observed, and today no Pending row can reach FromMxStatus at all.

Two observations from the same capture (no change made):

  • Detail 8001 (ConfigurationError, write rejected for type conversion, e.g. a string written to a double) and 8018 (SecurityError, verified write with the writer as verifier) are automation-object-specific codes outside the vocabulary table. They fall through to the category: BadConfigurationError and BadUserAccessDenied. 8001 arguably wants BadTypeMismatch.
  • Pending carries success = -1 (VARIANT_TRUE). MXAccess treats it as accepted-in-progress, not as a failure.

Proposed mapping (owner decides; StatusCodeMap not changed):

  1. Recommended: Pending (any detail) → Uncertain (0x40000000), Warning (any detail) → Uncertain, with the existing once-per-pair warning log kept. Both are "not a failure, not confirmed good". This matches the MXAccess success flag and avoids flagging values Bad on a category MXAccess does not treat as an error. Best fit if a Warning row ever does arrive with a value attached.
  2. Pending → BadWaitingForInitialData (0x80320000), Warning → Uncertain. More conservative for a data value, but it contradicts success = -1.
  3. Keep plain Bad for both (status quo). Safe, but it hides the distinction if either ever reaches a data row.

Leaving open and blocked: a real Warning row, or a Pending row on a data/write path, is still uncaptured. Producing one probably needs a source that reports degraded-but-usable status, such as an OI server item with a flaky device, or a remote platform mid-startup. This rig has neither.

Item 1: live capture attempt on windev (DEV Galaxy through mxaccessgw `447070a5`, 2026-11-13 UTC). Evidence in mxaccessgw `docs/LiveCaptures-2026-11-13.md` §3 (commit `9e4be258`). **No `Warning` row (raw category 2) was produced** by any of these: - out-of-range writes (1004) - type mismatches (8001) - NaN/Inf - array resize - off-scan, undeployed (detail 8) and unconnected-I/O objects (quality 32→4 with an Ok row) - read-only (1007), secured and verified writes (1008 / 8018) - alarm-config writes - Suspend/Activate edge cases **`Pending` (raw 1) was captured, but only as the synchronous return of `Suspend`/`Activate`** on an advised, resolved item: success = -1, **detail 0**, RequestingLmx. MXAccess then confirms it with an `OperationComplete` Ok row. It never appeared on an `OnDataChange`, `OnWriteComplete` or write-reply row, which are the rows the Galaxy driver feeds to `StatusCodeMap.FromMxStatus`. OtOpcUa never calls Suspend/Activate. So a Pending row with non-zero detail, the case this item names, was not observed, and today no Pending row can reach `FromMxStatus` at all. Two observations from the same capture (no change made): - Detail **8001** (ConfigurationError, write rejected for type conversion, e.g. a string written to a double) and **8018** (SecurityError, verified write with the writer as verifier) are automation-object-specific codes outside the vocabulary table. They fall through to the category: BadConfigurationError and BadUserAccessDenied. 8001 arguably wants `BadTypeMismatch`. - `Pending` carries success = -1 (VARIANT_TRUE). MXAccess treats it as accepted-in-progress, not as a failure. **Proposed mapping (owner decides; StatusCodeMap not changed):** 1. **Recommended:** Pending (any detail) → `Uncertain` (`0x40000000`), Warning (any detail) → `Uncertain`, with the existing once-per-pair warning log kept. Both are "not a failure, not confirmed good". This matches the MXAccess success flag and avoids flagging values Bad on a category MXAccess does not treat as an error. Best fit if a Warning row ever does arrive with a value attached. 2. Pending → `BadWaitingForInitialData` (`0x80320000`), Warning → `Uncertain`. More conservative for a data value, but it contradicts success = -1. 3. Keep plain `Bad` for both (status quo). Safe, but it hides the distinction if either ever reaches a data row. Leaving open and `blocked`: a real Warning row, or a Pending row on a data/write path, is still uncaptured. Producing one probably needs a source that reports degraded-but-usable status, such as an OI server item with a flaky device, or a remote platform mid-startup. This rig has neither.
Author
Owner

Owner decision 2026-11-13: Pending and Warning (any detail) → Uncertain (0x40000000), keeping the once-per-pair warning log; detail 8001 → BadTypeMismatch (8018 stays BadUserAccessDenied). Decided on the existing capture evidence, no further live row needed. Unblocked; fix queued.

Owner decision 2026-11-13: Pending and Warning (any detail) → `Uncertain` (0x40000000), keeping the once-per-pair warning log; detail 8001 → `BadTypeMismatch` (8018 stays `BadUserAccessDenied`). Decided on the existing capture evidence, no further live row needed. Unblocked; fix queued.
dohertj2 removed the blocked label 2026-11-13 01:57:52 -05:00
Author
Owner

Item 1 + 8001 fixed in 2470f82e (master), per the 2026-11-13 owner decision.

Mapping (StatusCodeMap.FromMxStatus / FromCategory)

  • Pending or Warning row → Uncertain (0x40000000) for any detail, checked before the detail vocabulary. The once-per-(detail, category) warning log is kept.
  • Detail 8001 → BadTypeMismatch (vocabulary table, next to 13/18/19/1005). 8018 stays BadUserAccessDenied via its SecurityError category, now pinned by a test.

Paths

  • Data change (EventPump.ToSnapshot): IsSuccess() is category == Ok, so a Pending (success = -1) or Warning row reaches the map and the value is published Uncertain (was Bad).
  • Write reply (GatewayGalaxyDataWriter.TranslateReply): result is Uncertain, never Good. The server commits only a Good write result (DriverInstanceActor.IsGoodStatus), so the write is reported as not committed and the node reverts. If MXAccess does commit later, the value arrives as a normal data change. This is deliberately different from the empty-statuses provisional Good, where no verdict arrived at all. Documented in code and in docs/drivers/Galaxy.md.
  • Host status: unchanged. The probe decode treats Uncertain and Bad alike (quality < 192 → Unknown). No alarm path calls this map; it has only these two callers.

Tests

  • New tests: StatusCodeMapTests (Pending/Warning × detail 0, 1, 2, 1004, 1007, 8001, 2001 → Uncertain; Pending log-once; 8001 → BadTypeMismatch; 8018 → BadUserAccessDenied), EventPumpQualityTests (4 Pending/Warning rows → Uncertain on a Good-quality data change), GatewayGalaxyDataWriterFailClosedTests (4 Pending/Warning write replies → Uncertain; 8001 write reply → BadTypeMismatch). No existing test pinned Bad for Pending/Warning.
  • Mutation check against the old src: 20 of 21 new cases fail. The one that passes is the 8018 pin, which pins existing behaviour.
  • Galaxy driver tests: 626 passed, 0 failed, 12 skipped (live). dotnet build ZB.MOM.WW.OtOpcUa.slnx -c Release: 0 warnings, 0 errors.

Not deployed.

Item 1 + 8001 fixed in `2470f82e` (master), per the 2026-11-13 owner decision. **Mapping (`StatusCodeMap.FromMxStatus` / `FromCategory`)** - `Pending` or `Warning` row → `Uncertain` (0x40000000) for **any** detail, checked before the detail vocabulary. The once-per-(detail, category) warning log is kept. - Detail **8001** → `BadTypeMismatch` (vocabulary table, next to 13/18/19/1005). **8018** stays `BadUserAccessDenied` via its SecurityError category, now pinned by a test. **Paths** - Data change (`EventPump.ToSnapshot`): `IsSuccess()` is category == Ok, so a Pending (success = -1) or Warning row reaches the map and the value is published **Uncertain** (was Bad). - Write reply (`GatewayGalaxyDataWriter.TranslateReply`): result is **Uncertain**, never Good. The server commits only a Good write result (`DriverInstanceActor.IsGoodStatus`), so the write is reported as not committed and the node reverts. If MXAccess does commit later, the value arrives as a normal data change. This is deliberately different from the empty-statuses provisional Good, where no verdict arrived at all. Documented in code and in `docs/drivers/Galaxy.md`. - Host status: unchanged. The probe decode treats Uncertain and Bad alike (quality < 192 → Unknown). No alarm path calls this map; it has only these two callers. **Tests** - New tests: `StatusCodeMapTests` (Pending/Warning × detail 0, 1, 2, 1004, 1007, 8001, 2001 → Uncertain; Pending log-once; 8001 → BadTypeMismatch; 8018 → BadUserAccessDenied), `EventPumpQualityTests` (4 Pending/Warning rows → Uncertain on a Good-quality data change), `GatewayGalaxyDataWriterFailClosedTests` (4 Pending/Warning write replies → Uncertain; 8001 write reply → BadTypeMismatch). No existing test pinned Bad for Pending/Warning. - Mutation check against the old `src`: **20 of 21 new cases fail**. The one that passes is the 8018 pin, which pins existing behaviour. - Galaxy driver tests: **626 passed, 0 failed, 12 skipped (live)**. `dotnet build ZB.MOM.WW.OtOpcUa.slnx -c Release`: 0 warnings, 0 errors. Not deployed.
Author
Owner

Deployed to DEV vd03 + QA vq03/vq04 in c9640f5c on 2026-11-13. Not exercised live: neither tier has a Galaxy driver.

Record: docs/deploy/2026-11-13-vd03-qa-to-c9640f5c.md (c0ba450d).

Deployed to DEV vd03 + QA vq03/vq04 in `c9640f5c` on 2026-11-13. Not exercised live: neither tier has a Galaxy driver. Record: `docs/deploy/2026-11-13-vd03-qa-to-c9640f5c.md` (`c0ba450d`).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#872