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