using Opc.Ua; namespace ZB.MOM.WW.OtOpcUa.OpcUaServer; /// /// The Part 13 processed reads are evaluated under, and the rule for a /// client-supplied one in ReadProcessedDetails. /// /// /// /// The aggregate evaluators (AggregateEvaluator / TimeWeightedAggregator in /// Driver.Historian.Sql.Common) are written against fixed settings, not parameterised: Uncertain time counts as /// Good ( = false) in TimeAverage2 / Total2, while PercentGood / DurationGood count /// Good time only and PercentBad / DurationBad Bad time only โ€” Uncertain in neither (Part 13 ยง5.4.3.31-34, Mantis /// 0006166); the per-sample Average / Minimum / /// Maximum / Range / *ActualTime / Count are defined by Part 13 over Good values whatever this setting, as the /// SDK computes them under it; an interval is Good only when all of /// it is ( = 100) and Bad only when none of it is non-Bad /// ( = 100), otherwise UncertainDataSubNormal; the value past the last sample /// is held (stepped, = false). Part 13's AggregateConfiguration defaults are /// TreatUncertainAsBad = false, PercentDataBad = PercentDataGood = 100, UseSlopedExtrapolation = false (from the /// spec as recalled, not re-read offline). The SDK matched these until Opc.Ua.Server 1.5.378.182, which flipped /// TreatUncertainAsBad to true: that is a DELIBERATE DIVERGENCE we do not follow (consumers such as /// ScadaBridge read these aggregates). OtOpcUaSdkServer.CreateAggregateManager pushes these values into /// the SDK manager so live AggregateFilter subscriptions agree with history reads; a test pins both sides. /// /// /// The live calculators are the application's frozen 1.5.378.182 copies (Compatibility/Aggregates), whose /// ComputeDurationGoodBad counts Uncertain time as Good under = false. That /// made live PercentGood / DurationGood disagree with HistoryRead whenever Uncertain data was present. #809 /// (owner-approved 2026-11-11) overrides exactly those two (GoodOnlyStatusAggregateCalculator), so live and /// HistoryRead now agree on them โ€” pinned by /// SdkAggregateParityTests.Live_AggregateFilter_PercentGood_and_DurationGood_equal_HistoryRead_over_Uncertain_samples. /// Other known live-vs-HistoryRead differences (e.g. #821, an Uncertain first sample in a late-start interval) are /// documented in docs/Historian.md. /// /// /// A request asking for UseServerCapabilitiesDefaults, or spelling out exactly these values, is served; /// any other configuration is refused per node with BadAggregateConfigurationRejected rather than silently /// answered under settings the client did not ask for (it used to be ignored). /// /// public static class ServerAggregateConfiguration { /// Uncertain data is not treated as Bad. public const bool TreatUncertainAsBad = false; /// An interval is Bad only when 100 % of it is Bad. public const byte PercentDataBad = 100; /// An interval is Good only when 100 % of it is Good. public const byte PercentDataGood = 100; /// The value past the last sample is held (stepped), not sloped. public const bool UseSlopedExtrapolation = false; /// The server defaults as an (explicit values, not "use defaults"). public static AggregateConfiguration Defaults => new() { UseServerCapabilitiesDefaults = false, TreatUncertainAsBad = TreatUncertainAsBad, PercentDataBad = PercentDataBad, PercentDataGood = PercentDataGood, UseSlopedExtrapolation = UseSlopedExtrapolation, }; /// /// True when a processed read with can be served: none supplied, /// UseServerCapabilitiesDefaults, or every setting equal to the server's. /// /// The request's configuration (may be null). /// Whether to serve; false โ‡’ BadAggregateConfigurationRejected. public static bool IsServable(AggregateConfiguration? requested) => requested is null || requested.UseServerCapabilitiesDefaults || (requested.TreatUncertainAsBad == TreatUncertainAsBad && requested.PercentDataBad == PercentDataBad && requested.PercentDataGood == PercentDataGood && requested.UseSlopedExtrapolation == UseSlopedExtrapolation); }