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