namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions; /// /// The one worst-of-input rule every derived-value seam uses — scripted alarms, virtual tags and /// the Calculation driver. /// /// The contract it implements (archreview R3-02, "the stale-Good family"): a value /// consumer never reports a quality better than its source's. A node derived from driver values /// carries, at worst, the worst status among its inputs — a Bad input makes the derived node Bad /// with that input's sub-code, an Uncertain input makes it Uncertain, and a source that has /// stopped communicating is a Bad input for everything downstream of it. /// /// Why it lives here. Core.Abstractions is the one assembly all three seams already /// reference (Commons, Runtime and the driver assemblies all sit above it), and /// — the shape the rule reads — is declared beside it. Stating /// the rule three times is how the three seams drifted apart in the first place. /// /// Severity, not equality. Comparison is on the OPC UA Part 4 severity bits (the top /// two: 00 Good, 01 Uncertain, 10/11 Bad); the sub-code below them is /// carried through verbatim because the sub-code is the diagnostic /// (BadNoCommunication = go look at the device, BadWaitingForInitialData = it never /// arrived, BadInternalError = the script). /// public static class InputQuality { /// /// No value has ever been received for this input — the status a fresh node is materialised /// with, and what reports for a dependency slot that holds no snapshot /// at all. Spelled here rather than taken from Commons.OpcUa.OpcUaStatus because /// Core.Abstractions sits below Commons and deliberately references nothing. /// public const uint BadWaitingForInitialData = 0x80320000u; /// /// The worst status among : the highest Part 4 severity, carrying /// that input's own sub-code. A entry is a dependency that has never /// been pushed and reports — reporting Good for it is /// the laundering this helper exists to stop. An empty set is Good: there is nothing to be /// worse than. /// /// The dependency snapshots, in a stable order. /// The worst status code observed. /// /// Among equal severity the first entry seen wins, so a re-evaluation over the same /// inputs reports the same sub-code instead of oscillating between two equally-bad codes. /// public static uint WorstOf(IEnumerable inputs) { ArgumentNullException.ThrowIfNull(inputs); var worst = 0u; var worstSeverity = 0u; foreach (var input in inputs) { var status = input?.StatusCode ?? BadWaitingForInitialData; var severity = status >> 30; if (severity <= worstSeverity) continue; worstSeverity = severity; worst = status; } return worst; } /// /// True when a consumer may evaluate against a value carrying . /// Good and Uncertain are usable — an Uncertain value is present and a script can make a /// qualified decision from it; Bad is not, and a consumer holds instead (the alarm engine's /// long-standing policy, generalised). /// /// The status code to classify. /// for Good or Uncertain severity. public static bool IsUsable(uint statusCode) => (statusCode >> 30) < 2; }