fix(historian): #684 review — Uncertain non-numeric samples do not refuse the Good-value aggregates; real-SQL Uncertain-above-max test

- Average/Minimum/Maximum/Range/*ActualTime refuse (BadAggregateInvalidInputs) only a GOOD
  String/DateTime sample (new GoodNonNumericCount / good_non_numeric_count column); an Uncertain
  one is skipped and makes the min/max family Uncertain, matching the SDK (Average skips it,
  ComputeMinMax flags the uncastable value). Delta keeps its usable-sample refusal.
- ProcessedConformance: an Uncertain value above the Good maximum (pins UncMax/unc_max through
  the real SQL) and an Uncertain String through the real SQL.
- The_uncertain_dataset_is_not_vacuous also proves the min/max STATUS discrimination.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-10-12 09:54:51 -04:00
parent a11ab0dc08
commit c0d6bacd9c
13 changed files with 240 additions and 27 deletions
@@ -566,6 +566,7 @@ public sealed class SqlServerHistorianDataSource : IHistorianDataSource
row.Sum = D(reader, f + 3);
row.UncertainMin = D(reader, f + 4);
row.UncertainMax = D(reader, f + 5);
row.GoodNonNumeric = reader.IsDBNull(f + 6) ? 0 : reader.GetInt32(f + 6);
break;
case AggregateFamily.StartEnd:
row.First = V(reader, f);
@@ -578,6 +579,7 @@ public sealed class SqlServerHistorianDataSource : IHistorianDataSource
row.ExtremeTime = reader.IsDBNull(f + 1) ? null : SqlHistorianConnection.GetUtc(reader, f + 1);
row.UncertainMin = D(reader, f + 2);
row.UncertainMax = D(reader, f + 3);
row.GoodNonNumeric = reader.IsDBNull(f + 4) ? 0 : reader.GetInt32(f + 4);
break;
}
return row;