fix(historian): #681 re-review — OpcQuality low-byte comparison, no-data boundary quality is 1

This commit is contained in:
Joseph Doherty
2026-10-12 10:06:54 -04:00
parent 5f225f0b44
commit 9d5eef658f
2 changed files with 4 additions and 4 deletions
+1 -1
View File
@@ -787,7 +787,7 @@ and evicts its shared historian session (see the HistorianGateway issue text in
(histsdk evidence: `Quality = 133`, `OpcQuality = 192`) — the value in effect there, not a stored sample. Starting 1 ms early puts
that row outside every bucket. A pre-window row only proves "the tag has earlier data" (start-of-data Partial, Count's in-range
zero), and only when its **OPC quality is not Bad** — a "no earlier data" NULL row still carries a value on the wire (histsdk sets
`NumericValue = 0` on every numeric row and `StringValue = ""` on a string one) but with OPC quality 0.
`NumericValue = 0` on every numeric row and `StringValue = ""` on a string one) but with a Bad OPC quality (captured as 1, no-data; histsdk string-read-layout.md).
- **Earlier-data probe.** When the data range matters (every per-sample aggregate but `Average`) and no non-Bad pre-window row
arrived, a 2-row read `[1970-01-01, start)` asks whether the tag has data before the window. Normally this fires only for a tag
whose history really begins inside the window.
@@ -30,7 +30,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway;
/// (1 ms) BEFORE the window, so that row lands outside every bucket and can never be counted as a sample. A row
/// before the window counts only as evidence that the tag has earlier data, and only when its OPC quality is not
/// Bad — a "no earlier data" NULL row still arrives WITH a value (histsdk sets <c>NumericValue = 0</c> on every
/// numeric row, <c>StringValue = ""</c> on a string one) but with OPC quality 0.
/// numeric row, <c>StringValue = ""</c> on a string one) but with a Bad OPC quality (captured: 1, no-data).
/// </para>
/// <para>
/// <b>The data range.</b> The start/end-of-data Partial rules and Count's in-range empty interval need the tag's
@@ -142,7 +142,7 @@ internal static class RawAggregateReader
// latest at or after its earlier edge. A row before the window (the AVEVA boundary row, or a stored sample in
// the margin) proves earlier data only when its OPC quality is not Bad: AVEVA's boundary row for a tag with no
// earlier data is a NULL row, which arrives WITH a value (histsdk sets NumericValue = 0 / StringValue "") and
// OPC quality 0.
// a Bad OPC quality (captured: 1, no-data).
if (t >= plan.StartUtc || IsNotBadOnTheWire(raw))
{
if (t <= plan.EndUtc && (earliest is null || t < earliest)) earliest = t;
@@ -199,7 +199,7 @@ internal static class RawAggregateReader
/// <see cref="SampleMapper.ToStatus"/>'s fallback to the AVEVA <c>quality</c> field when <c>opc_quality</c> is 0, which
/// is also the OPC DA Bad value a NULL row carries.
/// </summary>
private static bool IsNotBadOnTheWire(HistorianSample raw) => raw.OpcQuality >= 64;
private static bool IsNotBadOnTheWire(HistorianSample raw) => (byte)raw.OpcQuality >= 64; // low byte, as SampleMapper.ToStatus reads it
private static HistoryReadLimitExceededException LimitExceeded(string tag, int maxWindowRows) =>
new(maxWindowRows,