code wiki / (root) / nx_maint_rul.nx

nx_maint_rul.nx source

↩ module page · 108 lines · 4048 B

1// nx_maint_rul.nx -- remaining-useful-life (RUL) PROGNOSTICS: the learning loop's 2// prediction. From an asset's health-metric HISTORY (the R4 ledger), estimate the 3// wear rate (first-order system identification) and PREDICT time-to-failure, so 4// the platform acts BEFORE failure -- proactive, not reactive. 5// 6// Operator (2026-06-23): "full learning loop ... close the feedback loops on 7// physical things for wear and tear." Researcher-grounded (knowledge/fetched/ 8// maint_c_prognostics, maint_c_condmon, maint_e_sysid, maint_e_bathtub): 9// prognostics/RUL estimate condition + remaining life from the trend. 10// 11// THE EXCEED: OBD / Sense report the PRESENT (reactive -- "it's broken now"). We 12// predict the FUTURE from the trend (proactive -- "it will hit failure in ~N"). 13// Augury does this at enterprise price/cloud; we do it sovereign + cheap + broad. 14// 15// Method (integer/no-float): wear_rate = decline / span (first-order); RUL = 16// (current - failure_threshold) / wear_rate. A flat/improving trend -> STABLE 17// (no predicted failure). Honest: first-order linear extrapolation; nonlinear 18// degradation (bathtub end-of-life knee) + Kalman state estimation = next. 19// 20// NEVER-BRICK (#26): reads a metric series, predicts; no syscalls, no writes. 21// 22// genealogy_id: project-maintenance-platform-sclass-2026-06-23 (RUL prognostics; researcher-grounded) 23// license_tier: ORIGINAL 24 25const NX_RUL_INSUFFICIENT: i64 = 0 26const NX_RUL_STABLE: i64 = 1 // not wearing -> no predicted failure 27const NX_RUL_PLENTY: i64 = 2 // long life remaining 28const NX_RUL_PLAN: i64 = 3 // plan the maintenance 29const NX_RUL_SOON: i64 = 4 // schedule soon 30const NX_RUL_IMMINENT: i64 = 5 // failure imminent 31const NX_RUL_FAILED: i64 = 6 // already past the failure threshold 32const NX_RUL_BAD_ARG: i64 = 7 33const NX_RUL_N: i64 = 8 34 35func nx_rul_verdict_is_valid(v: i64) -> i64 { 36 if v < 0 { return 0 } 37 if v >= NX_RUL_N { return 0 } 38 return 1 39} 40 41struct RulSpec { 42 fail_threshold: i64, // metric value that = failure (e.g. min response rate) 43 imminent_window: i64, // RUL below this = IMMINENT (time units) 44 soon_window: i64, // RUL below this = SOON 45 plan_window: i64, // RUL below this = PLAN 46} 47 48struct RulEst { 49 wear_rate_milli: i64, // decline per time unit x1000 (telemetry) 50 current: i64, // latest metric value 51 rul: i64, // estimated time-to-failure; -1 = stable, 0 = failed 52 verdict: i64, 53} 54 55// metric[i] = a declining-with-wear health metric (e.g. response rate); t[i] = 56// time/cycle, ascending. (Caller orients the metric so wear = decline.) 57func nx_rul_estimate(metric: *i64, t: *i64, n: i64, spec: *RulSpec, out: *RulEst) -> i64 { 58 out.wear_rate_milli = 0 59 out.current = 0 60 out.rul = 0 61 out.verdict = NX_RUL_INSUFFICIENT 62 63 if n < 2 { 64 out.verdict = NX_RUL_INSUFFICIENT 65 return out.verdict 66 } 67 let last: i64 = n - 1 68 let span: i64 = t[last] - t[0] 69 if span <= 0 { 70 out.verdict = NX_RUL_BAD_ARG 71 return out.verdict 72 } 73 74 let decline: i64 = metric[0] - metric[last] 75 out.current = metric[last] 76 77 if decline <= 0 { 78 // flat or improving -> not wearing -> no predicted failure 79 out.wear_rate_milli = 0 80 out.rul = 0 - 1 81 out.verdict = NX_RUL_STABLE 82 return out.verdict 83 } 84 out.wear_rate_milli = (decline * 1000) / span 85 86 if out.current <= spec.fail_threshold { 87 out.rul = 0 88 out.verdict = NX_RUL_FAILED 89 return out.verdict 90 } 91 92 out.rul = ((out.current - spec.fail_threshold) * span) / decline 93 94 if out.rul < spec.imminent_window { 95 out.verdict = NX_RUL_IMMINENT 96 return out.verdict 97 } 98 if out.rul < spec.soon_window { 99 out.verdict = NX_RUL_SOON 100 return out.verdict 101 } 102 if out.rul < spec.plan_window { 103 out.verdict = NX_RUL_PLAN 104 return out.verdict 105 } 106 out.verdict = NX_RUL_PLENTY 107 return out.verdict 108}