fix(historian): #687 re-review — a row at/before the Unix epoch is never a stored sample
With StartTime at or before the epoch, RequestStartFor has no margin left, so AVEVA's synthetic no-data/boundary row is stamped exactly at StartTime and was kept: raw reads returned a fabricated Bad sample at 1970-01-01 and the per-sample aggregates put it in bucket 0 (Start/End fabricated, Min/Max status Uncertain). IsWindowSample and the aggregate loop now drop any row with t <= ProbeFloor (RawAggregateReader.IsAfterFloor), matching the #686 walk-back's assumption.
This commit is contained in:
@@ -104,9 +104,18 @@ internal static class RawAggregateReader
|
||||
/// <param name="windowEndUtc">The window's end: exclusive, or inclusive when it equals the start.</param>
|
||||
/// <returns>Whether the row belongs to the window.</returns>
|
||||
public static bool IsWindowSample(HistorianSample raw, DateTime windowStartUtc, DateTime windowEndUtc) =>
|
||||
raw.Timestamp is { } ts && ts.ToDateTime() is var t && t >= windowStartUtc
|
||||
raw.Timestamp is { } ts && ts.ToDateTime() is var t && IsAfterFloor(t) && t >= windowStartUtc
|
||||
&& (t < windowEndUtc || (t == windowEndUtc && windowStartUtc == windowEndUtc));
|
||||
|
||||
/// <summary>
|
||||
/// A row stamped at or before <see cref="ProbeFloor"/> (the Unix epoch) is never a stored sample: AVEVA stores nothing
|
||||
/// there (the server's #686 walk-back assumes the same), and a window starting at or before the epoch gets no 1 ms
|
||||
/// margin (<see cref="RequestStartFor"/>), so AVEVA's synthetic boundary / no-data row lands exactly at its start.
|
||||
/// </summary>
|
||||
/// <param name="t">The row's timestamp.</param>
|
||||
/// <returns>Whether the row can be a stored sample.</returns>
|
||||
internal static bool IsAfterFloor(DateTime t) => t > ProbeFloor;
|
||||
|
||||
/// <summary>True for the aggregates this reader serves: the per-sample families of <see cref="AggregateFamilies"/>.</summary>
|
||||
/// <param name="aggregate">Requested aggregate.</param>
|
||||
/// <returns>Whether the aggregate is evaluated from raw samples.</returns>
|
||||
@@ -184,6 +193,7 @@ internal static class RawAggregateReader
|
||||
if (overCap || raw.Timestamp is null) continue;
|
||||
var s = ToStored(raw);
|
||||
var t = s.TimeUtc;
|
||||
if (!IsAfterFloor(t)) continue; // the boundary row of a window starting at/before the epoch, never a sample
|
||||
if (plan.IndexOf(t) is { } bucket)
|
||||
{
|
||||
if (maxWindowRows > 0 && ++windowRows > maxWindowRows)
|
||||
|
||||
Reference in New Issue
Block a user