fix(historian): T10 review — tick-exact bucketing over the full DateTime range; AtTime outage keeps its Bad code
Important 1: bucket offsets are now 100 ns ticks as bigint
(DATEDIFF_BIG(microsecond)*10 plus the sub-microsecond tick digits), so a
1601→9999 window no longer overflows (SQL 535) and nothing scales the interval
to nanoseconds in C#. Interval wider than the window clamps to one bucket and
the ceiling division cannot overflow. Exactness to the tick is unchanged.
Important 2 (spec §4 ruling d6afb394): a Bad previous neighbour means the time
is inside an outage — null value with that neighbour's Bad code, never
Uncertain. Usable previous + Bad next still holds the previous value Uncertain.
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
+10
-5
@@ -4,7 +4,11 @@ using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.SqlServer.IntegrationTests;
|
||||
|
||||
/// <summary>AtTime rules (spec §4): exact, linear, stepped, no-prior, Bad neighbour (no bridging).</summary>
|
||||
/// <summary>
|
||||
/// AtTime rules (spec §4, as amended after the T10 review): exact, linear, stepped, no-prior, and the two
|
||||
/// Bad-neighbour cases — inside an outage (Bad previous) ⇒ null with the outage's own Bad code; before an
|
||||
/// outage (usable previous, Bad next) ⇒ previous value held, Uncertain. Never bridges.
|
||||
/// </summary>
|
||||
[Collection(SqlHistorianCollection.Name)]
|
||||
public sealed class AtTimeReadTests(SqlHistorianFixture fx)
|
||||
{
|
||||
@@ -75,13 +79,14 @@ public sealed class AtTimeReadTests(SqlHistorianFixture fx)
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_bad_previous_neighbour_yields_no_value_uncertain()
|
||||
public async Task A_bad_previous_neighbour_is_inside_an_outage_and_keeps_its_bad_code()
|
||||
{
|
||||
var db = await ReadTestDb.CreateAsync(fx);
|
||||
await db.SeedAAsync();
|
||||
var s = (await db.Source().ReadAtTimeAsync("A", [At(25)], Ct)).Samples.Single();
|
||||
s.Value.ShouldBeNull(); // NOT 35 (30→40) and NOT 30 (a Bad value)
|
||||
s.StatusCode.ShouldBe(UncertainInterpolated);
|
||||
s.Value.ShouldBeNull(); // NOT 35 (30→40) and NOT 30 (a Bad value)
|
||||
s.StatusCode.ShouldBe(0x80000000u); // the previous sample's Bad code — never upgraded to Uncertain
|
||||
s.SourceTimestampUtc.ShouldBe(At(25));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -96,7 +101,7 @@ public sealed class AtTimeReadTests(SqlHistorianFixture fx)
|
||||
s[0].Value.ShouldBe(10d); // bridging 10→30 would give 15
|
||||
s[0].StatusCode.ShouldBe(UncertainInterpolated);
|
||||
s[1].Value.ShouldBeNull(); // bridging would give 25
|
||||
s[1].StatusCode.ShouldBe(UncertainInterpolated);
|
||||
s[1].StatusCode.ShouldBe(0x80310000u); // inside the outage: BadNoCommunication, as the marker says
|
||||
s[2].Value.ShouldBeNull(); // exact hit on the marker: as stored
|
||||
s[2].StatusCode.ShouldBe(0x80310000u);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user