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:
@@ -74,7 +74,11 @@ public static class AggregateEvaluator
|
||||
return Calc(r.Good, r.Good == r.Total ? HistorianStatusCodes.Good : HistorianStatusCodes.UncertainDataSubNormal);
|
||||
}
|
||||
if (r.Usable == 0) return NoData(bucketStart, now);
|
||||
if (r.NonNumeric > 0) return Calc(null, HistorianStatusCodes.BadAggregateInvalidInputs);
|
||||
// #684 review: Delta reads usable picks, so any usable String/DateTime sample refuses it. The Good-value aggregates
|
||||
// refuse only a GOOD non-numeric sample — an Uncertain one is not part of their value set (it only degrades the
|
||||
// status, as the SDK's skipped/uncastable value does).
|
||||
if ((aggregate == HistoryAggregateType.Delta ? r.NonNumeric : r.GoodNonNumericOrAll) > 0)
|
||||
return Calc(null, HistorianStatusCodes.BadAggregateInvalidInputs);
|
||||
|
||||
switch (aggregate)
|
||||
{
|
||||
@@ -122,7 +126,8 @@ public static class AggregateEvaluator
|
||||
/// #684 — the Part 13 status of Minimum / Maximum / Range / *ActualTime (Tables 57–62): <c>Uncertain_DataSubNormal</c>
|
||||
/// when a Bad sample exists in the interval, or when an Uncertain value is less than the minimum Good value
|
||||
/// (<paramref name="low"/>) / greater than the maximum Good value (<paramref name="high"/>); else Good. An Uncertain
|
||||
/// value inside the Good range leaves it Good.
|
||||
/// value inside the Good range leaves it Good. An Uncertain String/DateTime sample (no order against the Good values)
|
||||
/// also makes it Uncertain — the SDK's <c>ComputeMinMax</c> flags a value it cannot cast exactly that way.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The SDK's <c>ComputeMinMax</c> (1.5.378) applies only the Bad half: it collects the Uncertain extremes and never
|
||||
@@ -131,7 +136,8 @@ public static class AggregateEvaluator
|
||||
/// Uncertain value EQUAL to the Good extreme does not degrade it.
|
||||
/// </remarks>
|
||||
private static uint MinMaxStatus(BucketRow r, double? goodMin, double? goodMax, bool low, bool high) =>
|
||||
r.Bad > 0 || (low && r.UncertainMin < goodMin) || (high && r.UncertainMax > goodMax)
|
||||
r.Bad > 0 || r.NonNumeric > r.GoodNonNumericOrAll
|
||||
|| (low && r.UncertainMin < goodMin) || (high && r.UncertainMax > goodMax)
|
||||
? HistorianStatusCodes.UncertainDataSubNormal
|
||||
: HistorianStatusCodes.Good;
|
||||
|
||||
|
||||
@@ -32,9 +32,20 @@ public sealed class BucketRow
|
||||
/// <summary>Bad (not usable) samples.</summary>
|
||||
public int Bad { get; set; }
|
||||
|
||||
/// <summary>Usable String / DateTime samples.</summary>
|
||||
/// <summary>Usable String / DateTime samples (Delta's refusal check).</summary>
|
||||
public int NonNumeric { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <b>Good</b> String / DateTime samples — the refusal check of the Good-value aggregates (Average / Minimum / Maximum /
|
||||
/// Range / *ActualTime, #684 review): an Uncertain non-numeric sample is not part of their value set. Filled by the
|
||||
/// Simple and ActualTime queries; null (a row that does not carry it) falls back to <see cref="NonNumeric"/>, the
|
||||
/// pre-#684 (stricter) check.
|
||||
/// </summary>
|
||||
public int? GoodNonNumeric { get; set; }
|
||||
|
||||
/// <summary><see cref="GoodNonNumeric"/>, or <see cref="NonNumeric"/> when the row does not carry it.</summary>
|
||||
internal int GoodNonNumericOrAll => GoodNonNumeric ?? NonNumeric;
|
||||
|
||||
/// <summary>Average of the Good numeric values (null when none — #684).</summary>
|
||||
public double? Avg { get; set; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user