feat(historian): serve DurationGood/DurationBad on the SQL backends; pin the #821 late-start facts (#820, #821)

#820: SqlServer + QuestDB now serve Part 13 DurationGood / DurationBad from the
same Good / Bad region totals TimeWeightedAggregator already computes for
PercentGood / PercentBad — reported in milliseconds (OPC UA Duration, the SDK's
unit), Uncertain time in neither (Mantis 0006166), same BadNoData / status /
Partial rules. AggregateFamilies.Served grows 17 -> 19, so i=11201 and the
up-front refusal follow automatically; Gateway still refuses both (explicit
served set, unchanged). Client.Shared AggregateType / mapper / CLI gain both.
The #796 oracle answers DurationGood through the Good-only subclass as well.
QualityMix conformance asserts both directly (Q and QP, hand values, ms, and
= PercentGood x interval), replaces the "refused up front" test, and adds
Duration* to the forward/reverse/truncated oracle comparisons.

#821 (facts only, no decision): SdkAggregateParityTests pins an Uncertain-first
late-start interval (QP over [0,20), both directions) against ours (Part 13
Total 163.96, TimeAverage 10.93), the frozen 1.5.378.182 calculators (188.96,
9.448), SDK 2.0.1's stock calculators (188.96, 9.448) and a Good-first control
(all 163.96); LiveAggregateLateStartFactsTests pins the same through the booted
server's runtime AggregateManager (live AggregateFilter) incl. DurationGood
15000 / PercentGood 75 there vs 8000 / 40 in HistoryRead (#809). QualityMix
adds the reverse late-start variant (reverse TW pin + reverse oracle windows).

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-11-11 01:28:54 -05:00
parent 5bbe1e3c7a
commit e7b485c0a2
18 changed files with 328 additions and 59 deletions
@@ -132,10 +132,12 @@ public class ModelConstructionTests
((int)ConnectionState.Reconnecting).ShouldBe(3);
}
/// <summary>AggregateType holds the seventeen served aggregates (the parity test pins which).</summary>
/// <summary>AggregateType holds the nineteen served aggregates (the parity test pins which).</summary>
[Fact]
public void AggregateType_Enum_HasExpectedValues()
{
Enum.GetValues<AggregateType>().Length.ShouldBe(17);
Enum.GetValues<AggregateType>().Length.ShouldBe(19);
((int)AggregateType.DurationGood).ShouldBe(18);
((int)AggregateType.DurationBad).ShouldBe(19);
}
}