@@ -184,14 +184,108 @@ public sealed record HistoryReadResult(
|
||||
IReadOnlyList<DataValueSnapshot> Samples,
|
||||
byte[]? ContinuationPoint);
|
||||
|
||||
/// <summary>Aggregate function for processed history reads. Mirrors OPC UA Part 13 standard aggregates.</summary>
|
||||
/// <summary>
|
||||
/// Aggregate function for processed history reads. Mirrors the OPC UA Part 13 §5
|
||||
/// standard aggregate catalog. Each value maps 1:1 onto an
|
||||
/// <c>Opc.Ua.ObjectIds.AggregateFunction_*</c> NodeId — the OPC UA Client driver does the
|
||||
/// translation in <c>OpcUaClientDriver.MapAggregateToNodeId</c>; other drivers either
|
||||
/// evaluate the aggregate locally (Galaxy historian) or surface
|
||||
/// <c>BadAggregateNotSupported</c> for the values their backend can't honour.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// <b>Stable ordinals.</b> The first 5 values (<see cref="Average"/>..<see cref="Count"/>)
|
||||
/// carry ordinals 0-4 from the original PR — additions are appended to keep prior
|
||||
/// persisted enums (config files, Admin UI dropdowns) compatible.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b>Server-side support.</b> Not every upstream OPC UA server implements every
|
||||
/// Part 13 aggregate. Implementations advertise their support through
|
||||
/// <c>AggregateConfiguration</c> on the Server object; clients can probe it at runtime.
|
||||
/// Aggregates that the upstream rejects come back with
|
||||
/// <c>StatusCode=BadAggregateNotSupported</c> on the per-row HistoryRead result —
|
||||
/// the driver passes that through verbatim (cascading-quality rule, Part 11 §8).
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public enum HistoryAggregateType
|
||||
{
|
||||
// ---- Original 5 (ordinals 0-4 — keep stable) ----
|
||||
/// <summary>Average of all values in the interval. Part 13 §5.4.</summary>
|
||||
Average,
|
||||
/// <summary>Minimum value in the interval. Part 13 §5.5.</summary>
|
||||
Minimum,
|
||||
/// <summary>Maximum value in the interval. Part 13 §5.6.</summary>
|
||||
Maximum,
|
||||
/// <summary>Sum of values in the interval (numeric only). Part 13 §5.10.</summary>
|
||||
Total,
|
||||
/// <summary>Count of Good-quality samples in the interval. Part 13 §5.18.</summary>
|
||||
Count,
|
||||
|
||||
// ---- Time-weighted averages (Part 13 §5.4) ----
|
||||
/// <summary>Time-weighted average — values held until next sample. Part 13 §5.4.2.</summary>
|
||||
TimeAverage,
|
||||
/// <summary>Time-weighted average using simple-bounds extrapolation. Part 13 §5.4.3.</summary>
|
||||
TimeAverage2,
|
||||
|
||||
// ---- Interpolation (Part 13 §5.3) ----
|
||||
/// <summary>Interpolated value at each interval boundary. Part 13 §5.3.</summary>
|
||||
Interpolative,
|
||||
|
||||
// ---- Min/Max with timestamps and range (Part 13 §5.5–§5.7) ----
|
||||
/// <summary>Timestamp of the minimum-value sample. Part 13 §5.5.4.</summary>
|
||||
MinimumActualTime,
|
||||
/// <summary>Timestamp of the maximum-value sample. Part 13 §5.6.4.</summary>
|
||||
MaximumActualTime,
|
||||
/// <summary>Maximum minus minimum across the interval. Part 13 §5.7.</summary>
|
||||
Range,
|
||||
/// <summary>Range computed using simple-bounds extrapolation. Part 13 §5.7.</summary>
|
||||
Range2,
|
||||
|
||||
// ---- Annotation / duration / quality coverage (Part 13 §5.16–§5.21) ----
|
||||
/// <summary>Number of annotations attached to samples in the interval. Part 13 §5.21.</summary>
|
||||
AnnotationCount,
|
||||
/// <summary>Total time (ms) covered by Good-quality data. Part 13 §5.16.</summary>
|
||||
DurationGood,
|
||||
/// <summary>Total time (ms) covered by Bad-quality data. Part 13 §5.16.</summary>
|
||||
DurationBad,
|
||||
/// <summary>Percent of the interval covered by Good-quality data (0-100). Part 13 §5.17.</summary>
|
||||
PercentGood,
|
||||
/// <summary>Percent of the interval covered by Bad-quality data (0-100). Part 13 §5.17.</summary>
|
||||
PercentBad,
|
||||
/// <summary>Worst (most-severe) quality code seen in the interval. Part 13 §5.20.</summary>
|
||||
WorstQuality,
|
||||
/// <summary>Worst-quality code using simple-bounds extrapolation. Part 13 §5.20.</summary>
|
||||
WorstQuality2,
|
||||
|
||||
// ---- Statistical (Part 13 §5.13) ----
|
||||
/// <summary>Sample-population standard deviation (n-1 divisor). Part 13 §5.13.</summary>
|
||||
StandardDeviationSample,
|
||||
/// <summary>Whole-population standard deviation (n divisor). Part 13 §5.13.</summary>
|
||||
StandardDeviationPopulation,
|
||||
/// <summary>Sample-population variance (n-1 divisor). Part 13 §5.13.</summary>
|
||||
VarianceSample,
|
||||
/// <summary>Whole-population variance (n divisor). Part 13 §5.13.</summary>
|
||||
VariancePopulation,
|
||||
|
||||
// ---- State-based (Part 13 §5.12, §5.19) ----
|
||||
/// <summary>Number of value transitions observed in the interval. Part 13 §5.12.</summary>
|
||||
NumberOfTransitions,
|
||||
/// <summary>Total time (ms) the value was 0 (state Zero). Part 13 §5.19.</summary>
|
||||
DurationInStateZero,
|
||||
/// <summary>Total time (ms) the value was non-zero (state NonZero). Part 13 §5.19.</summary>
|
||||
DurationInStateNonZero,
|
||||
|
||||
// ---- Interval bounds and deltas (Part 13 §5.8–§5.9, §5.11) ----
|
||||
/// <summary>First Good-quality sample at or after the interval start. Part 13 §5.8.</summary>
|
||||
Start,
|
||||
/// <summary>Last Good-quality sample at or before the interval end. Part 13 §5.9.</summary>
|
||||
End,
|
||||
/// <summary>End sample minus Start sample. Part 13 §5.11.</summary>
|
||||
Delta,
|
||||
/// <summary>Boundary value (extrapolated) at the interval start. Part 13 §5.8.</summary>
|
||||
StartBound,
|
||||
/// <summary>Boundary value (extrapolated) at the interval end. Part 13 §5.9.</summary>
|
||||
EndBound,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2700,14 +2700,67 @@ public sealed class OpcUaClientDriver(OpcUaClientDriverOptions options, string d
|
||||
finally { _gate.Release(); }
|
||||
}
|
||||
|
||||
/// <summary>Map <see cref="HistoryAggregateType"/> to the OPC UA Part 13 standard aggregate NodeId.</summary>
|
||||
/// <summary>
|
||||
/// Map <see cref="HistoryAggregateType"/> to the OPC UA Part 13 standard aggregate
|
||||
/// NodeId. Each enum value resolves to <c>Opc.Ua.ObjectIds.AggregateFunction_*</c>;
|
||||
/// the upstream server may still reject individual aggregates with
|
||||
/// <c>BadAggregateNotSupported</c> on the per-row HistoryRead result — that's a
|
||||
/// server-capability signal, not a driver-side error, so callers should treat the
|
||||
/// mapping itself as best-effort.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// The supplied enum value is outside the declared <see cref="HistoryAggregateType"/>
|
||||
/// range — most likely a future-extension value the driver hasn't been recompiled for.
|
||||
/// </exception>
|
||||
internal static NodeId MapAggregateToNodeId(HistoryAggregateType aggregate) => aggregate switch
|
||||
{
|
||||
// ---- Original 5 (ordinals 0-4) ----
|
||||
HistoryAggregateType.Average => ObjectIds.AggregateFunction_Average,
|
||||
HistoryAggregateType.Minimum => ObjectIds.AggregateFunction_Minimum,
|
||||
HistoryAggregateType.Maximum => ObjectIds.AggregateFunction_Maximum,
|
||||
HistoryAggregateType.Total => ObjectIds.AggregateFunction_Total,
|
||||
HistoryAggregateType.Count => ObjectIds.AggregateFunction_Count,
|
||||
|
||||
// ---- Time-weighted averages ----
|
||||
HistoryAggregateType.TimeAverage => ObjectIds.AggregateFunction_TimeAverage,
|
||||
HistoryAggregateType.TimeAverage2 => ObjectIds.AggregateFunction_TimeAverage2,
|
||||
|
||||
// ---- Interpolation ----
|
||||
HistoryAggregateType.Interpolative => ObjectIds.AggregateFunction_Interpolative,
|
||||
|
||||
// ---- Min/Max with timestamps + range ----
|
||||
HistoryAggregateType.MinimumActualTime => ObjectIds.AggregateFunction_MinimumActualTime,
|
||||
HistoryAggregateType.MaximumActualTime => ObjectIds.AggregateFunction_MaximumActualTime,
|
||||
HistoryAggregateType.Range => ObjectIds.AggregateFunction_Range,
|
||||
HistoryAggregateType.Range2 => ObjectIds.AggregateFunction_Range2,
|
||||
|
||||
// ---- Annotation / duration / quality coverage ----
|
||||
HistoryAggregateType.AnnotationCount => ObjectIds.AggregateFunction_AnnotationCount,
|
||||
HistoryAggregateType.DurationGood => ObjectIds.AggregateFunction_DurationGood,
|
||||
HistoryAggregateType.DurationBad => ObjectIds.AggregateFunction_DurationBad,
|
||||
HistoryAggregateType.PercentGood => ObjectIds.AggregateFunction_PercentGood,
|
||||
HistoryAggregateType.PercentBad => ObjectIds.AggregateFunction_PercentBad,
|
||||
HistoryAggregateType.WorstQuality => ObjectIds.AggregateFunction_WorstQuality,
|
||||
HistoryAggregateType.WorstQuality2 => ObjectIds.AggregateFunction_WorstQuality2,
|
||||
|
||||
// ---- Statistical ----
|
||||
HistoryAggregateType.StandardDeviationSample => ObjectIds.AggregateFunction_StandardDeviationSample,
|
||||
HistoryAggregateType.StandardDeviationPopulation => ObjectIds.AggregateFunction_StandardDeviationPopulation,
|
||||
HistoryAggregateType.VarianceSample => ObjectIds.AggregateFunction_VarianceSample,
|
||||
HistoryAggregateType.VariancePopulation => ObjectIds.AggregateFunction_VariancePopulation,
|
||||
|
||||
// ---- State-based ----
|
||||
HistoryAggregateType.NumberOfTransitions => ObjectIds.AggregateFunction_NumberOfTransitions,
|
||||
HistoryAggregateType.DurationInStateZero => ObjectIds.AggregateFunction_DurationInStateZero,
|
||||
HistoryAggregateType.DurationInStateNonZero => ObjectIds.AggregateFunction_DurationInStateNonZero,
|
||||
|
||||
// ---- Interval bounds and deltas ----
|
||||
HistoryAggregateType.Start => ObjectIds.AggregateFunction_Start,
|
||||
HistoryAggregateType.End => ObjectIds.AggregateFunction_End,
|
||||
HistoryAggregateType.Delta => ObjectIds.AggregateFunction_Delta,
|
||||
HistoryAggregateType.StartBound => ObjectIds.AggregateFunction_StartBound,
|
||||
HistoryAggregateType.EndBound => ObjectIds.AggregateFunction_EndBound,
|
||||
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(aggregate), aggregate, null),
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user