Gateway historian: compute per-sample aggregates (Average, Count, Start/End, Delta, PercentGood...) from raw reads #681
Reference in New Issue
Block a user
Delete Branch "%!s()"
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?
Follow-up to #679. AVEVA's 15 retrieval modes include no arithmetic mean and no sample count, so since #679 the Gateway backend answers
AverageandCountwithBadAggregateNotSupported(TimeAverage → TimeWeightedAverage is served instead).Proposal: serve the per-sample aggregates (Average, Count, Minimum/Maximum incl. *ActualTime, Range, Start, End, Delta, PercentGood/PercentBad) on Gateway by reading raw samples (
ReadRawAsync, capped likeMaxTimeWeightedRawRows⇒BadTooManyOperations) and evaluating them with the sharedDriver.Historian.Sql.Commonevaluator (AggregateEvaluator,BucketPlan,AggregateFamilies) — not SQL-specific; Gateway already referencesCore.AlarmHistorian, Sql.Common's only extra dependency.Gains: exact parity with SqlServer/QuestDB (incl. the #680 SDK-parity fixes), no reliance on unverified AVEVA mode semantics. Cost: raw-read volume per processed read.
Related Gateway differences to settle at the same time:
SampleMapper.ToAggregateSnapshotstamps buckets with the interval END (EndTime ?? StartTime); Part 13 / SQL backends stamp the interval start on forward reads.MinimumWithTime/MaximumWithTimerows lose the actual time of the extreme.Needs the VPN to wonder-sql-vd03 for a live leg.
Implemented — merged
197246c3. The Gateway backend now serves the ten per-sample aggregates (Average, Count, Minimum, Maximum, Range, Start, End, Delta, MinimumActualTime, MaximumActualTime) by reading raw samples and evaluating them with the sharedDriver.Historian.Sql.Commonevaluator — the same code (and #680/#684 semantics) as SqlServer/QuestDB, incl. interval-start stamps, Calculated/Uncertain/Partial bits and #677 reverse reads. TimeAverage/Total stay native AVEVA modes (exact raw evaluation needs the real prior sample, which AVEVA replaces with an initial-value row); TimeAverage2/Total2/Interpolative/PercentGood/PercentBad stayBadAggregateNotSupported. Served set =GatewayHistorianDataSource.ServedAggregates(12), advertised via #682.⚠️ Client-visible: Average/Count work again on Gateway (true mean / Good-sample count); Minimum/Maximum are now computed from raw instead of AVEVA
MinimumWithTime/MaximumWithTime; per-sample reads stream raw rows, capped byMaxTimeWeightedRawRows⇒BadTooManyOperations(a month of 1 Hz data exceeds the default 1,000,000).Design notes (two review rounds): earlier-data evidence = a pre-window row with a non-Bad OPC quality (AVEVA's no-data boundary row arrives WITH a value but Bad quality); every gateway stream is drained to completion (no early break — an abandoned stream evicts the gateway's shared historian session, filed upstream as historiangw#26); separate 2-row look-ahead and 1970 probe reads; gateway ceiling rejection ⇒
BadTooManyOperations; caller cancellation not counted as a backend fault.Verification: parity suite (10 aggregates × 13 datasets × 21 forward/reverse windows, with and without the real AVEVA boundary-row shape) against an independent oracle; falsified 9 ways; live gate 9 G4 (unreachable gateway): all 12 served aggregates reach the backend, 25 unserved refused without a call, deadline cancel not counted.
Live AVEVA leg still open (VPN to wonder-sql-vd03): boundary-row quality with/without earlier data; no terminal row at the query end / past now; cheap 2-row reads from 1970 and to now+1 d; ceiling-rejection text; cap drain without session eviction; Min/Max vs the old native modes; TimeAverage/Total magnitude and the
tail+43interval-start stamp. Leaving open for that leg.Live AVEVA leg PASSED (2026-10-12, VPN, local HistorianGateway → wonder-sql-vd03, new known-value tag
HistGW.LiveTest.Gate681).tail+43= interval startCategory=LiveIntegrationsuiteThe raw-evaluated per-sample path is verified. Native
TimeAverage/Totalend to end FAIL for reasons outside this issue: the gateway's aggregate interval is 10,000× too large (historiangw#27) and OtOpcUa's native-row handling (#685). Other findings: #686 (reverse/end-only raw reads on Gateway), #687 (raw reads return AVEVA's synthetic boundary row). Closing #681.