namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions; /// /// Driver capability for historical-data reads (OPC UA HistoryRead). Optional — /// only drivers whose backends carry historian data implement this. Currently: /// Galaxy (Wonderware Historian via the optional plugin), OPC UA Client (forward /// to upstream server). /// public interface IHistoryProvider { /// /// Read raw historical samples for a single attribute over a time range. /// The Core wraps this with continuation-point handling. /// Task ReadRawAsync( string fullReference, DateTime startUtc, DateTime endUtc, uint maxValuesPerNode, CancellationToken cancellationToken); /// /// Read processed (aggregated) samples — interval-bucketed average / min / max / etc. /// Optional — drivers that only support raw history can throw . /// Task ReadProcessedAsync( string fullReference, DateTime startUtc, DateTime endUtc, TimeSpan interval, HistoryAggregateType aggregate, CancellationToken cancellationToken); /// /// Read one sample per requested timestamp — OPC UA HistoryReadAtTime service. The /// driver interpolates (or returns the prior-boundary sample) when no exact match /// exists. Optional; drivers that can't interpolate throw . /// /// /// Default implementation throws. Drivers opt in by overriding; keeps existing /// IHistoryProvider implementations compiling without forcing a ReadAtTime path /// they may not have a backend for. /// Task ReadAtTimeAsync( string fullReference, IReadOnlyList timestampsUtc, CancellationToken cancellationToken) => throw new NotSupportedException( $"{GetType().Name} does not implement ReadAtTimeAsync. " + "Drivers whose backends support at-time reads override this method."); /// /// Read historical alarm/event records — OPC UA HistoryReadEvents service. Distinct /// from the live event stream — historical rows come from an event historian (Galaxy's /// Alarm Provider history log, etc.) rather than the driver's active subscription. /// /// /// Optional filter: null means "all sources", otherwise restrict to events from that /// source-object name. Drivers may ignore the filter if the backend doesn't support it. /// /// Inclusive lower bound on EventTimeUtc. /// Exclusive upper bound on EventTimeUtc. /// Upper cap on returned events — the driver's backend enforces this. /// Request cancellation. /// /// Default implementation throws. Only drivers with an event historian (Galaxy via the /// Wonderware Alarm & Events log) override. Modbus / the OPC UA Client driver stay /// with the default and let callers see BadHistoryOperationUnsupported. /// Task ReadEventsAsync( string? sourceName, DateTime startUtc, DateTime endUtc, int maxEvents, CancellationToken cancellationToken) => throw new NotSupportedException( $"{GetType().Name} does not implement ReadEventsAsync. " + "Drivers whose backends have an event historian override this method."); /// /// Filter-aware historical event read — OPC UA HistoryReadEvents service with full /// EventFilter support (SelectClauses + WhereClause). Distinct from the simpler /// /// overload which is sufficient for "give me the standard BaseEventType fields" /// queries; this overload is for clients that send a custom EventFilter on the /// wire (per-select-clause Variant population, where-filter evaluation). /// /// /// Driver-specific node identifier. May be a notifier object (e.g. the driver-root /// folder) — drivers that support cluster-wide queries treat it as /// "all sources in the namespace". /// /// Filter spec — time range + select clauses + optional where clause. /// Request cancellation. /// /// /// Default implementation throws — drivers opt in by overriding. Existing drivers /// that only handle the parameterless overload stay green; new drivers that need /// filter-aware event history (OPC UA Client passthrough, future event-historian /// backends) override this method. /// /// /// The OPC UA Client driver implements this by translating /// into ReadEventDetails and calling Session.HistoryReadAsync against /// the upstream server. /// /// Task ReadEventsAsync( string fullReference, EventHistoryRequest request, CancellationToken cancellationToken) => throw new NotSupportedException( $"{GetType().Name} does not implement filter-aware ReadEventsAsync(EventHistoryRequest). " + "Drivers whose backends carry historical events with EventFilter support override this method."); } /// /// Filter spec for the filter-aware /// overload. Mirrors the OPC UA ReadEventDetails wire shape (StartTime, EndTime, /// NumValuesPerNode, EventFilter) but transport-neutral so non-UA drivers can implement it /// without taking a dependency on the UA SDK type. /// /// Inclusive lower bound on event time. /// Exclusive upper bound on event time. /// Maximum events per node (0 = no driver-side cap, server may still apply one). /// /// Per-field projection. Each entry names a BaseEventType-rooted field (or a /// typed-path field via ) the caller /// wants returned. null means "use the driver's default field set" — typically /// EventId, SourceName, Time, Message, Severity, ReceiveTime. /// /// /// Optional content-filter restriction (e.g. EventType OfType AlarmConditionType). /// Drivers may ignore the where clause if their backend doesn't support it; that's a /// best-effort projection rather than a hard error. /// public sealed record EventHistoryRequest( DateTime StartTime, DateTime EndTime, uint NumValuesPerNode, IReadOnlyList? SelectClauses, ContentFilterSpec? WhereClause); /// /// Transport-neutral mirror of OPC UA's SimpleAttributeOperand — picks one field /// from a node by typed browse path. is the OPC UA NodeId /// of the type that the path is rooted at (e.g. BaseEventType); /// is a sequence of QualifiedName-style segments ("ns:Name" or just "Name" /// when ns=0). An empty means "the node itself". /// /// /// Type the path is rooted at. null defaults to the OPC UA BaseEventType /// when the driver has a UA mapping. Format is driver-specific NodeId text (e.g. /// "i=2041" for BaseEventType). /// /// Browse-path segments. Empty list = the typed node itself. /// /// Stable key the driver uses when populating . The /// server-side dispatcher uses this to align the returned values with the wire-side /// SelectClause order, even when a driver doesn't honour the BrowsePath verbatim. /// public sealed record SimpleAttributeSpec( string? TypeDefinitionId, IReadOnlyList BrowsePath, string FieldName); /// /// Transport-neutral mirror of OPC UA's ContentFilter. The current shape carries the /// raw filter operands as opaque OPC UA ExtensionObject bytes — drivers that need to /// evaluate the filter (Galaxy historian) parse it themselves; the OPC UA Client driver /// forwards it untouched. A future PR may replace this with a structured AST when more /// than one driver needs to evaluate where-clauses locally. /// /// /// Optional binary-encoded ContentFilter from the wire. null when no /// where-clause was supplied. /// public sealed record ContentFilterSpec(byte[]? EncodedOperands); /// Result of a HistoryRead call. /// Returned samples in chronological order. /// Opaque token for the next call when more samples are available; null when complete. public sealed record HistoryReadResult( IReadOnlyList Samples, byte[]? ContinuationPoint); /// /// Aggregate function for processed history reads. Mirrors the OPC UA Part 13 §5 /// standard aggregate catalog. Each value maps 1:1 onto an /// Opc.Ua.ObjectIds.AggregateFunction_* NodeId — the OPC UA Client driver does the /// translation in OpcUaClientDriver.MapAggregateToNodeId; other drivers either /// evaluate the aggregate locally (Galaxy historian) or surface /// BadAggregateNotSupported for the values their backend can't honour. /// /// /// /// Stable ordinals. The first 5 values (..) /// carry ordinals 0-4 from the original PR — additions are appended to keep prior /// persisted enums (config files, Admin UI dropdowns) compatible. /// /// /// Server-side support. Not every upstream OPC UA server implements every /// Part 13 aggregate. Implementations advertise their support through /// AggregateConfiguration on the Server object; clients can probe it at runtime. /// Aggregates that the upstream rejects come back with /// StatusCode=BadAggregateNotSupported on the per-row HistoryRead result — /// the driver passes that through verbatim (cascading-quality rule, Part 11 §8). /// /// public enum HistoryAggregateType { // ---- Original 5 (ordinals 0-4 — keep stable) ---- /// Average of all values in the interval. Part 13 §5.4. Average, /// Minimum value in the interval. Part 13 §5.5. Minimum, /// Maximum value in the interval. Part 13 §5.6. Maximum, /// Sum of values in the interval (numeric only). Part 13 §5.10. Total, /// Count of Good-quality samples in the interval. Part 13 §5.18. Count, // ---- Time-weighted averages (Part 13 §5.4) ---- /// Time-weighted average — values held until next sample. Part 13 §5.4.2. TimeAverage, /// Time-weighted average using simple-bounds extrapolation. Part 13 §5.4.3. TimeAverage2, // ---- Interpolation (Part 13 §5.3) ---- /// Interpolated value at each interval boundary. Part 13 §5.3. Interpolative, // ---- Min/Max with timestamps and range (Part 13 §5.5–§5.7) ---- /// Timestamp of the minimum-value sample. Part 13 §5.5.4. MinimumActualTime, /// Timestamp of the maximum-value sample. Part 13 §5.6.4. MaximumActualTime, /// Maximum minus minimum across the interval. Part 13 §5.7. Range, /// Range computed using simple-bounds extrapolation. Part 13 §5.7. Range2, // ---- Annotation / duration / quality coverage (Part 13 §5.16–§5.21) ---- /// Number of annotations attached to samples in the interval. Part 13 §5.21. AnnotationCount, /// Total time (ms) covered by Good-quality data. Part 13 §5.16. DurationGood, /// Total time (ms) covered by Bad-quality data. Part 13 §5.16. DurationBad, /// Percent of the interval covered by Good-quality data (0-100). Part 13 §5.17. PercentGood, /// Percent of the interval covered by Bad-quality data (0-100). Part 13 §5.17. PercentBad, /// Worst (most-severe) quality code seen in the interval. Part 13 §5.20. WorstQuality, /// Worst-quality code using simple-bounds extrapolation. Part 13 §5.20. WorstQuality2, // ---- Statistical (Part 13 §5.13) ---- /// Sample-population standard deviation (n-1 divisor). Part 13 §5.13. StandardDeviationSample, /// Whole-population standard deviation (n divisor). Part 13 §5.13. StandardDeviationPopulation, /// Sample-population variance (n-1 divisor). Part 13 §5.13. VarianceSample, /// Whole-population variance (n divisor). Part 13 §5.13. VariancePopulation, // ---- State-based (Part 13 §5.12, §5.19) ---- /// Number of value transitions observed in the interval. Part 13 §5.12. NumberOfTransitions, /// Total time (ms) the value was 0 (state Zero). Part 13 §5.19. DurationInStateZero, /// Total time (ms) the value was non-zero (state NonZero). Part 13 §5.19. DurationInStateNonZero, // ---- Interval bounds and deltas (Part 13 §5.8–§5.9, §5.11) ---- /// First Good-quality sample at or after the interval start. Part 13 §5.8. Start, /// Last Good-quality sample at or before the interval end. Part 13 §5.9. End, /// End sample minus Start sample. Part 13 §5.11. Delta, /// Boundary value (extrapolated) at the interval start. Part 13 §5.8. StartBound, /// Boundary value (extrapolated) at the interval end. Part 13 §5.9. EndBound, } /// /// One row returned by the fixed-field /// /// overload — a historical alarm/event record, not the OPC UA live-event stream. Fields /// match the minimum set the Server needs to populate a HistoryEventFieldList /// for HistoryReadEvents responses. /// /// Stable unique id for the event — driver-specific format. /// Source object that emitted the event. May differ from the sourceName filter the caller passed (fuzzy matches). /// Process-side timestamp — when the event actually occurred. /// Historian-side timestamp — when the historian persisted the row; may lag by the historian's buffer flush cadence. /// Human-readable message text. /// OPC UA severity (1-1000). Drivers map their native priority scale onto this range. public sealed record HistoricalEvent( string EventId, string? SourceName, DateTime EventTimeUtc, DateTime ReceivedTimeUtc, string? Message, ushort Severity); /// Result of a call. /// Events in chronological order by EventTimeUtc. /// Opaque token for the next call when more events are available; null when complete. public sealed record HistoricalEventsResult( IReadOnlyList Events, byte[]? ContinuationPoint); /// /// One row returned by the filter-aware /// /// overload. Carries an open-ended bag keyed by /// (or a stable default name when no /// SelectClauses were supplied) so the server-side dispatcher can re-align fields with /// the client's requested order — without forcing every driver to honour the entire /// OPC UA EventFilter shape verbatim. /// /// /// SelectClause results. Keys match the FieldName on the corresponding /// ; values are the raw .NET payload (string, /// DateTime, severity int, etc.). null values are legitimate (the /// upstream had a missing field). /// /// /// Wall-clock event time — convenience for ordering / windowing without picking a key /// out of . Drivers populate this from the underlying event row. /// public sealed record HistoricalEventRow( IReadOnlyDictionary Fields, DateTimeOffset OccurrenceTime); /// /// Result of the filter-aware /// /// overload. Mirrors but carries /// instead of the fixed-shape /// — the server-side dispatcher unpacks the keyed fields /// into a HistoryEventFieldList aligned with the client's SelectClauses. /// /// Events in chronological order by . /// Opaque token for the next call when more events are available; null when complete. public sealed record HistoricalEventBatch( IReadOnlyList Events, byte[]? ContinuationPoint);