///|
/// Monitor: consecutive timeout threshold.
/// Emits Fail when consecutive_timeouts >= max, Warn when > 0.
pub fn monitor_consecutive_timeouts(rt : Runtime) -> MonitorResult {
let name = "consecutive_timeouts"
if rt.consecutive_timeouts >= rt.max_consecutive_timeouts {
{
name,
verdict: Fail(
"consecutive timeouts " +
rt.consecutive_timeouts.to_string() +
" >= threshold " +
rt.max_consecutive_timeouts.to_string(),
),
}
} else if rt.consecutive_timeouts > 0 {
{
name,
verdict: Warn(
"consecutive timeouts " + rt.consecutive_timeouts.to_string(),
),
}
} else {
{ name, verdict: Pass }
}
}
///|
/// Monitor: WKC error threshold.
/// Emits Fail when consecutive_wkc_errors >= max, Warn when > 0.
pub fn monitor_wkc_errors(rt : Runtime) -> MonitorResult {
let name = "wkc_errors"
if rt.consecutive_wkc_errors >= rt.max_wkc_errors {
{
name,
verdict: Fail(
"consecutive WKC errors " +
rt.consecutive_wkc_errors.to_string() +
" >= threshold " +
rt.max_wkc_errors.to_string(),
),
}
} else if rt.consecutive_wkc_errors > 0 {
{
name,
verdict: Warn(
"consecutive WKC errors " + rt.consecutive_wkc_errors.to_string(),
),
}
} else {
{ name, verdict: Pass }
}
}
///|
/// Monitor: DC drift detection.
/// Emits Fail when dc_state is Drifting.
pub fn monitor_dc_drift(rt : Runtime) -> MonitorResult {
let name = "dc_drift"
match rt.dc_state {
Drifting(delta_ns) =>
{
name,
verdict: Fail("DC drift detected: delta_ns=" + delta_ns.to_string()),
}
_ => { name, verdict: Pass }
}
}
///|
/// Monitor: startup state verification.
/// Emits Block when runtime is in Error state, Warn when not Running.
pub fn monitor_startup_state(rt : Runtime) -> MonitorResult {
let name = "startup_state"
match rt.state {
Error(e) =>
{ name, verdict: Block("runtime in error state: " + @debug.to_string(e)) }
Running(_) => { name, verdict: Pass }
Idle => { name, verdict: Warn("runtime is idle, not yet started") }
Scanning => { name, verdict: Warn("runtime is scanning") }
Configuring => { name, verdict: Warn("runtime is configuring") }
Stopping => { name, verdict: Warn("runtime is stopping") }
}
}
///|
/// Monitor: PDO configuration readiness.
/// Emits Block when PDO context is not configured,
/// Warn when expected_wkc is zero.
pub fn monitor_pdo_configured(rt : Runtime) -> MonitorResult {
let name = "pdo_configured"
match rt.pdo_ctx {
None => { name, verdict: Block("PDO context not configured") }
Some(_) =>
if rt.expected_wkc == 0U {
{ name, verdict: Warn("expected WKC is zero") }
} else {
{ name, verdict: Pass }
}
}
}
///|
/// Monitor: cycle success rate.
/// Emits Fail when wkc_errors / cycles exceeds threshold.
pub fn monitor_cycle_success_rate(
rt : Runtime,
min_success_ratio_pct : Int,
) -> MonitorResult {
let name = "cycle_success_rate"
if rt.telemetry.cycles == 0L {
return { name, verdict: Pass }
}
let total = rt.telemetry.cycles
let errors = rt.telemetry.wkc_errors + rt.telemetry.timeouts
let success_pct = ((total - errors) * 100L / total).to_int()
if success_pct < min_success_ratio_pct {
{
name,
verdict: Fail(
"success rate " +
success_pct.to_string() +
"% < " +
min_success_ratio_pct.to_string() +
"%",
),
}
} else {
{ name, verdict: Pass }
}
}
///|
/// Run all built-in monitors against the current runtime state.
/// Returns individual results plus the aggregate worst verdict.
pub fn run_monitors(rt : Runtime) -> (Array[MonitorResult], Verdict) {
let results = [
monitor_consecutive_timeouts(rt),
monitor_wkc_errors(rt),
monitor_dc_drift(rt),
monitor_startup_state(rt),
monitor_pdo_configured(rt),
monitor_cycle_success_rate(rt, 80),
]
let worst = worst_verdict(results)
(results, worst)
}