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;
}