using ZB.MOM.WW.OtOpcUa.Core.Abstractions; namespace ZB.MOM.WW.OtOpcUa.Core.Observability; /// /// Domain-layer health aggregation for Phase 6.1 Stream C. Pure functions over the driver /// fleet — given each driver's , produce a /// that maps to HTTP status codes at the endpoint layer. /// /// /// State matrix per docs/v2/implementation/phase-6-1-resilience-and-observability.md /// §Stream C.1: /// /// / /// → /readyz 503 (not yet ready). /// → /readyz 200. /// → /readyz 200 with flagged driver IDs. /// → /readyz 503. /// /// The overall verdict is computed across the fleet: any Faulted → Faulted; any /// Unknown/Initializing → NotReady; any Degraded → Degraded; else Healthy. An empty fleet /// is Healthy (nothing to degrade). /// public static class DriverHealthReport { /// Compute the fleet-wide readiness verdict from per-driver states. public static ReadinessVerdict Aggregate(IReadOnlyList drivers) { ArgumentNullException.ThrowIfNull(drivers); if (drivers.Count == 0) return ReadinessVerdict.Healthy; var anyFaulted = drivers.Any(d => d.State == DriverState.Faulted); if (anyFaulted) return ReadinessVerdict.Faulted; var anyInitializing = drivers.Any(d => d.State == DriverState.Unknown || d.State == DriverState.Initializing); if (anyInitializing) return ReadinessVerdict.NotReady; // Reconnecting = driver alive but not serving live data; report as Degraded so /readyz // stays 200 (the fleet can still serve cached / last-good data) while operators see the // affected driver in the body. var anyDegraded = drivers.Any(d => d.State == DriverState.Degraded || d.State == DriverState.Reconnecting); if (anyDegraded) return ReadinessVerdict.Degraded; return ReadinessVerdict.Healthy; } /// /// Map a to the HTTP status the /readyz endpoint should /// return per the Stream C.1 state matrix. /// public static int HttpStatus(ReadinessVerdict verdict) => verdict switch { ReadinessVerdict.Healthy => 200, ReadinessVerdict.Degraded => 200, ReadinessVerdict.NotReady => 503, ReadinessVerdict.Faulted => 503, _ => 500, }; } /// Per-driver snapshot fed into . /// Driver instance identifier (from IDriver.DriverInstanceId). /// Current from IDriver.GetHealth. /// Optional driver-supplied detail (e.g. "primary PLC unreachable"). public sealed record DriverHealthSnapshot( string DriverInstanceId, DriverState State, string? DetailMessage = null); /// Overall fleet readiness — derived from driver states by . public enum ReadinessVerdict { /// All drivers Healthy (or fleet is empty). Healthy, /// At least one driver Degraded; none Faulted / NotReady. Degraded, /// At least one driver Unknown / Initializing; none Faulted. NotReady, /// At least one driver Faulted. Faulted, }