fix(historian): #687 Gateway raw reads drop AVEVA's synthetic start-boundary row
An AVEVA Full raw read leads with a row stamped at the query start (the value in effect, Quality=133, or a no-data NULL row); it was returned as a stored sample. Raw reads now use the #681 classification (RawAggregateReader.RequestStartFor / IsWindowSample — one authority, by timestamp, since a stored NULL looks the same on the wire): request from 1 ms early with NumValuesPerNode + 16 rows, keep rows at or after StartTime, drain the whole stream. A full page still holds exactly the cap, so #669 paging/resume/tie-cluster reads are unchanged; a margin that swallows the allowance fails the node rather than ending paging early. ReturnBounds is not plumbed on any backend (documented).
This commit is contained in:
@@ -74,6 +74,31 @@ internal static class RawAggregateReader
|
||||
/// <summary>Rows the earlier-data probe asks for (a possible boundary row plus the first stored sample).</summary>
|
||||
internal const int ProbeRows = 2;
|
||||
|
||||
/// <summary>
|
||||
/// The <c>ReadRaw</c> start for a window that begins at <paramref name="windowStartUtc"/>: <see cref="BoundaryMargin"/>
|
||||
/// earlier, so the AVEVA start-boundary row — stamped exactly at the QUERY start, carrying the value in effect there
|
||||
/// (<c>Quality = 133</c>) or, with no earlier data, a NULL row (value 0, Bad OPC quality) — lands BEFORE the window
|
||||
/// (<see cref="IsWindowSample"/>). The one place both the raw-evaluated aggregates (#681) and a raw HistoryRead
|
||||
/// (#687) decide what the request start is. No margin when the window starts within one margin of
|
||||
/// <see cref="DateTime.MinValue"/>.
|
||||
/// </summary>
|
||||
/// <param name="windowStartUtc">The window's (inclusive) start.</param>
|
||||
/// <returns>The start to request.</returns>
|
||||
public static DateTime RequestStartFor(DateTime windowStartUtc) =>
|
||||
windowStartUtc.Ticks > BoundaryMargin.Ticks ? windowStartUtc - BoundaryMargin : windowStartUtc;
|
||||
|
||||
/// <summary>
|
||||
/// True when a row of a stream requested from <see cref="RequestStartFor"/> is a stored sample of the window that
|
||||
/// begins at <paramref name="windowStartUtc"/>: it is stamped at or after the window start. A row before it is the
|
||||
/// AVEVA boundary row (whatever its value or quality — a stored NULL looks the same on the wire, so the timestamp is
|
||||
/// the evidence) or a stored sample inside the margin; a row without a timestamp is never a sample.
|
||||
/// </summary>
|
||||
/// <param name="raw">The gateway row.</param>
|
||||
/// <param name="windowStartUtc">The window's (inclusive) start.</param>
|
||||
/// <returns>Whether the row belongs to the window.</returns>
|
||||
public static bool IsWindowSample(HistorianSample raw, DateTime windowStartUtc) =>
|
||||
raw.Timestamp is { } ts && ts.ToDateTime() >= windowStartUtc;
|
||||
|
||||
/// <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>
|
||||
@@ -113,7 +138,7 @@ internal static class RawAggregateReader
|
||||
{
|
||||
if (plan.BucketCount == 0) return [];
|
||||
var needsRange = AggregateEvaluator.NeedsDataRange(aggregate, plan);
|
||||
var requestStart = plan.StartUtc.Ticks > BoundaryMargin.Ticks ? plan.StartUtc - BoundaryMargin : plan.StartUtc;
|
||||
var requestStart = RequestStartFor(plan.StartUtc);
|
||||
var maxValues = MaxValuesFor(maxWindowRows);
|
||||
|
||||
var buckets = new RawBucketAccumulator(aggregate == HistoryAggregateType.MaximumActualTime);
|
||||
|
||||
Reference in New Issue
Block a user