fix(historian): per-sample Average/Min/Max/Range/ActualTime use Good samples only, Part 13 min/max status (#684)
Part 13 defines Average/Minimum/Maximum/Range/*ActualTime over Good raw values (independent of TreatUncertainAsBad) and the SDK's calculators skip Uncertain values under the server's TreatUncertainAsBad=false. SqlServer/QuestDB value columns now use the good flag (g) instead of usable (u); new UncMin/UncMax columns feed the Part 13 Minimum/Maximum/Range status rule (Uncertain on a Bad sample or an Uncertain value beyond the Good extreme -- the SDK applies only the Bad half). An Uncertain-only interval is BadNoData. Uncertain-bearing parity datasets (in-memory + real SQL) match the SDK unmasked; the beyond-extreme divergence is pinned explicitly. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,11 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Sql.Common;
|
||||
/// <remarks>
|
||||
/// Sample classes: <i>usable</i> = severity Good or Uncertain AND a stored value (an Empty sample is an outage marker
|
||||
/// and counts as Bad whatever its code); <i>good</i> = usable with severity Good; <i>bad</i> = not usable.
|
||||
/// Every value field is computed over usable samples only, except <see cref="FirstAny"/> / <see cref="LastAny"/>.
|
||||
/// <see cref="Avg"/> / <see cref="Min"/> / <see cref="Max"/> / <see cref="Sum"/> / <see cref="Extreme"/> are computed over
|
||||
/// <b>Good</b> numeric samples only (#684 — Part 13 defines Average / Minimum / Maximum / Range / *ActualTime over Good
|
||||
/// raw values, as the SDK's calculators do); <see cref="UncertainMin"/> / <see cref="UncertainMax"/> over the Uncertain
|
||||
/// ones (the Part 13 Minimum/Maximum/Range status rule); <see cref="First"/> / <see cref="Last"/> over usable samples;
|
||||
/// <see cref="FirstAny"/> / <see cref="LastAny"/> over every sample.
|
||||
/// </remarks>
|
||||
public sealed class BucketRow
|
||||
{
|
||||
@@ -31,16 +35,16 @@ public sealed class BucketRow
|
||||
/// <summary>Usable String / DateTime samples.</summary>
|
||||
public int NonNumeric { get; set; }
|
||||
|
||||
/// <summary>Average of the usable numeric values (null when none).</summary>
|
||||
/// <summary>Average of the Good numeric values (null when none — #684).</summary>
|
||||
public double? Avg { get; set; }
|
||||
|
||||
/// <summary>Minimum of the usable numeric values (null when none).</summary>
|
||||
/// <summary>Minimum of the Good numeric values (null when none — #684).</summary>
|
||||
public double? Min { get; set; }
|
||||
|
||||
/// <summary>Maximum of the usable numeric values (null when none).</summary>
|
||||
/// <summary>Maximum of the Good numeric values (null when none — #684).</summary>
|
||||
public double? Max { get; set; }
|
||||
|
||||
/// <summary>Sum of the usable numeric values (null when none).</summary>
|
||||
/// <summary>Sum of the Good numeric values (null when none — #684).</summary>
|
||||
public double? Sum { get; set; }
|
||||
|
||||
/// <summary>First usable sample's value by time (Empty when none).</summary>
|
||||
@@ -49,12 +53,25 @@ public sealed class BucketRow
|
||||
/// <summary>Last usable sample's value by time (Empty when none).</summary>
|
||||
public HistorianValue Last { get; set; }
|
||||
|
||||
/// <summary>Extreme (min or max) usable numeric value (null when none).</summary>
|
||||
/// <summary>Extreme (min or max) Good numeric value (null when none — #684).</summary>
|
||||
public double? Extreme { get; set; }
|
||||
|
||||
/// <summary>Time of <see cref="Extreme"/> (ties ⇒ earliest; null when none).</summary>
|
||||
public DateTime? ExtremeTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum of the bucket's <b>Uncertain</b> (usable, not Good) numeric values (null when none). Not a result value:
|
||||
/// Part 13 makes Minimum / MinimumActualTime / Range <c>Uncertain_DataSubNormal</c> when an Uncertain value is less
|
||||
/// than the minimum Good value (#684).
|
||||
/// </summary>
|
||||
public double? UncertainMin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum of the bucket's Uncertain numeric values (null when none): Maximum / MaximumActualTime / Range are
|
||||
/// Uncertain when one is greater than the maximum Good value (#684).
|
||||
/// </summary>
|
||||
public double? UncertainMax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The bucket's first sample by time of <b>any</b> quality (null when the bucket is empty): Start returns it raw, and
|
||||
/// Delta is Uncertain when it is not usable (#680, Part 13 Tables 73/74 — the SDK's <c>StartEndAggregateCalculator</c>).
|
||||
|
||||
Reference in New Issue
Block a user