namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions;
///
/// Optional driver capability: expose a small bag of operator-facing diagnostic key/value pairs
/// (e.g. S7's negotiated PDU size) that ride the driver-health snapshot to the AdminUI /hosts
/// page (Gitea #640).
///
///
///
/// Why a bag rather than fields on . The facts worth showing are
/// driver-specific and do not change the driver's state; a typed field per driver would
/// widen the core record and the telemetry proto for every driver. Same additive-never-a-state-change
/// rule as .
///
///
/// Values are strings so the telemetry proto stays a plain map<string,string>. Keep the
/// bag small ( entries, text truncated to
/// ): it is published on every state change.
///
///
public interface IDriverDiagnostics
{
///
/// Snapshot of the driver's diagnostics. Must be a pure in-memory read with no I/O — it is called on
/// the health-publish path, deliberately NOT through the resilience pipeline.
///
/// The key/value pairs; empty when there is nothing to show yet.
IReadOnlyDictionary GetDiagnostics();
}
/// Bounds and helpers shared by every consumer of .
public static class DriverDiagnostics
{
/// Maximum number of entries carried on a health snapshot; extras are dropped (ordinal key order).
public const int MaxEntries = 16;
/// Maximum length of a key or value carried on a health snapshot; longer text is truncated.
public const int MaxValueLength = 200;
///
/// Normalises a driver's bag for publishing: ordinal key order, capped entry count, truncated text.
/// Deterministic so the same facts always produce the same fingerprint digest.
///
/// The bag the driver returned.
/// A bounded, ordered copy.
public static IReadOnlyDictionary Bound(IReadOnlyDictionary raw)
{
var result = new SortedDictionary(StringComparer.Ordinal);
foreach (var kv in raw.OrderBy(k => k.Key, StringComparer.Ordinal))
{
if (result.Count >= MaxEntries) break;
result[Truncate(kv.Key ?? "")] = Truncate(kv.Value ?? "");
}
return result;
}
private static string Truncate(string s) => s.Length <= MaxValueLength ? s : s[..MaxValueLength];
}