Gateway historian: Count and Average mapped to AVEVA modes with different semantics (Counter delta, time-weighted average) #679

Closed
opened 2026-10-11 10:40:36 -04:00 by dohertj2 · 2 comments
Owner

Found while investigating #677 (2026-10-11). The Gateway historian backend maps two OPC UA aggregates onto AVEVA retrieval modes with different semantics, so Gateway results disagree with the SqlServer/QuestDB backends for the same aggregate:

  • AggregateModeMapper.cs:36 — Count → RetrievalMode.Counter. AVEVA's Counter mode is the rollover-counter delta, not a sample count. OPC UA Count is the number of raw values in the interval.
  • AggregateModeMapper.cs:32 — Average → TimeWeightedAverage. OPC UA Average is the arithmetic mean of the raw values (what the SQL backends compute); TimeAverage is the time-weighted one.

Fix direction: map Count and Average to the AVEVA modes that match (or return BadAggregateNotSupported where none matches), and map TimeAverage → TimeWeightedAverage. Needs the VPN to wonder-sql-vd03 to verify against the real historian (a constant/known-sample sandbox tag, compare against the SQL backends).

Related: #677 (IntegralDivisor for Total).

Found while investigating #677 (2026-10-11). The Gateway historian backend maps two OPC UA aggregates onto AVEVA retrieval modes with different semantics, so Gateway results disagree with the SqlServer/QuestDB backends for the same aggregate: - `AggregateModeMapper.cs:36` — `Count → RetrievalMode.Counter`. AVEVA's Counter mode is the **rollover-counter delta**, not a sample count. OPC UA `Count` is the number of raw values in the interval. - `AggregateModeMapper.cs:32` — `Average → TimeWeightedAverage`. OPC UA `Average` is the **arithmetic mean** of the raw values (what the SQL backends compute); `TimeAverage` is the time-weighted one. Fix direction: map `Count` and `Average` to the AVEVA modes that match (or return `BadAggregateNotSupported` where none matches), and map `TimeAverage` → `TimeWeightedAverage`. Needs the VPN to `wonder-sql-vd03` to verify against the real historian (a constant/known-sample sandbox tag, compare against the SQL backends). Related: #677 (IntegralDivisor for `Total`).
Author
Owner

Fixed — fb5ec80f (merged 19a4c093).

AVEVA's 15 retrieval modes include no arithmetic mean and no sample count, so the Gateway backend no longer returns a different statistic under the requested name:

Aggregate Before Now
Average AVEVA TimeWeightedAverage BadAggregateNotSupported (no gateway call, not counted as a backend fault)
Count AVEVA Counter (rollover-counter increase) BadAggregateNotSupported
TimeAverage not supported → TimeWeightedAverage (the number Average used to return)
Minimum / Maximum / Total unchanged unchanged

⚠️ Client-visible: a client reading Average on a Gateway node must switch to TimeAverage for the old number; there is no Gateway sample count. ScadaBridge does not issue processed reads.

Verification: mapper + data-source tests (red first, falsified by restoring the old mapping); live gate 8 D4 on docker-dev with an unreachable gateway: Average/Count ⇒ 0x80D50000 with zero backend-fault logs, TimeAverage/Minimum/Maximum/Total reach the backend.

Not verified (VPN to wonder-sql-vd03 needed): TimeAverage magnitude, NULL-gap handling and bucket timestamp (Gateway stamps the interval END). Follow-ups: #681 (compute per-sample aggregates — true Average/Count — from raw reads on Gateway), #682 (advertise only the aggregates the backend serves).

**Fixed — `fb5ec80f` (merged `19a4c093`).** AVEVA's 15 retrieval modes include no arithmetic mean and no sample count, so the Gateway backend no longer returns a different statistic under the requested name: | Aggregate | Before | Now | |---|---|---| | `Average` | AVEVA `TimeWeightedAverage` | `BadAggregateNotSupported` (no gateway call, not counted as a backend fault) | | `Count` | AVEVA `Counter` (rollover-counter increase) | `BadAggregateNotSupported` | | `TimeAverage` | not supported | → `TimeWeightedAverage` (the number `Average` used to return) | | Minimum / Maximum / Total | unchanged | unchanged | ⚠️ Client-visible: a client reading `Average` on a Gateway node must switch to `TimeAverage` for the old number; there is no Gateway sample count. ScadaBridge does not issue processed reads. **Verification:** mapper + data-source tests (red first, falsified by restoring the old mapping); live gate 8 D4 on docker-dev with an unreachable gateway: `Average`/`Count` ⇒ `0x80D50000` with zero backend-fault logs, `TimeAverage`/`Minimum`/`Maximum`/`Total` reach the backend. **Not verified (VPN to wonder-sql-vd03 needed):** TimeAverage magnitude, NULL-gap handling and bucket timestamp (Gateway stamps the interval END). Follow-ups: #681 (compute per-sample aggregates — true Average/Count — from raw reads on Gateway), #682 (advertise only the aggregates the backend serves).
Author
Owner

Live AVEVA leg (2026-10-12, VPN). TimeAverage → AVEVA TimeWeightedAverage: magnitude PASS — AVEVA's TWA on a Float tag is sloped (trapezoidal; 22.5 where stepped would be 17.5), identical to the SqlServer/QuestDB TimeAverage on all-Good spans; the bucket stamp is the interval start (verified the histsdk tail+43 orientation). NULL-gap handling differs from the SQL backends (AVEVA excludes gap time; SQL bridges it, Uncertain) — documented as part of #685.

End-to-end results through OtOpcUa are currently broken by the gateway interval bug (historiangw#27) and #685 (pre-window row, Uncertain value dropped). Since #681 Gateway also serves a true Average/Count from raw. Closing.

**Live AVEVA leg (2026-10-12, VPN).** `TimeAverage` → AVEVA `TimeWeightedAverage`: **magnitude PASS** — AVEVA's TWA on a Float tag is sloped (trapezoidal; 22.5 where stepped would be 17.5), identical to the SqlServer/QuestDB `TimeAverage` on all-Good spans; the bucket stamp is the interval start (verified the histsdk `tail+43` orientation). NULL-gap handling differs from the SQL backends (AVEVA excludes gap time; SQL bridges it, Uncertain) — documented as part of #685. End-to-end results through OtOpcUa are currently broken by the gateway interval bug (historiangw#27) and #685 (pre-window row, Uncertain value dropped). Since #681 Gateway also serves a true `Average`/`Count` from raw. Closing.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#679