code wiki / (root) / nx_maint_simloop.nx

nx_maint_simloop.nx source

↩ module page · 98 lines · 4411 B

1// nx_maint_simloop.nx -- the CLOSED-LOOP DIGITAL-TWIN SIMULATOR + learning loop. 2// Proves the whole maintenance platform IN SOFTWARE, reproducibly, with NO IRL 3// spend -- the operator's real goal: simulate first, prove via modeling, then 4// (optionally) validate on hardware. 5// 6// Operator (2026-06-23): "use software to simulate these things ... most systems 7// have an emission loop ... we want a full learning loop ... a good example is 8// OBD [which] just tells you errors but then you need 3rd parties to make the 9// data meaningful, actionable, reproducible." Grounded in the sovereign research 10// (knowledge/fetched/maint_*: digital_twin, control_loop, feedback, predictive_ 11// maintenance, prognostics, on-board_diagnostics): S-class CLOSES the loop with a 12// digital twin + feedback control; OBD is OPEN-loop. 13// 14// THE LOOP (per cycle): twin wears -> generate a sensor reading -> run the REAL 15// pipeline (nx_hvac_analyze_core -> nx_maint_severity -> nx_plan_for) -> CLOSE 16// THE LOOP: a fix/replace action feeds back and RESETS the wear -> the next 17// cycle SELF-VERIFIES the asset recovered. Open-loop (no feedback) just degrades 18// and stays broken (OBD); the closed loop self-maintains = the measured EXCEED. 19// 20// NEVER-BRICK (#26): pure simulation; no syscalls, no device writes. 21// 22// genealogy_id: project-maintenance-platform-sclass-2026-06-23 (closed-loop sim; researcher-grounded) 23// license_tier: ORIGINAL 24 25import "nx_maint_action.nx" // the real pipeline: kernels + nx_maint_severity + nx_plan_for + enums 26const K_MAGIC_1800: i64 = 1800 27const K_MAGIC_19000: i64 = 19000 28const K_MAGIC_5000: i64 = 5000 29 30// A digital twin of a thermal asset (furnace): a fouling/wear level that grows 31// each cycle and drops the effective response rate; a fix resets it. 32struct TwinThermal { 33 fouling: i64, // 0..1000 wear (grows with use; >625 -> response under floor) 34 wear_per_cycle: i64, // fouling growth per cycle 35 base_resp: i64, // healthy response rate (mC/min) 36 cycles_run: i64, 37 fixes_applied: i64, 38 failures_seen: i64, // cycles that reached DEGRADED or worse 39} 40 41func nx_twin_init(tw: *TwinThermal, base_resp: i64, wear_per_cycle: i64) -> i64 { 42 tw.fouling = 0 43 tw.wear_per_cycle = wear_per_cycle 44 tw.base_resp = base_resp 45 tw.cycles_run = 0 46 tw.fixes_applied = 0 47 tw.failures_seen = 0 48 return 0 49} 50 51// Run n_cycles of the closed (apply_fix=1) or open (apply_fix=0) loop. The 52// caller supplies scratch buffers for the trace + outputs. setpoint is set high 53// so the wear signal (declining response rate) is isolated -- no overshoot/ 54// short-cycle artifacts (those axes are gated separately in the kernel test). 55func nx_sim_run(tw: *TwinThermal, apply_fix: i64, n_cycles: i64, 56 t: *i64, indoor: *i64, outdoor: *i64, on: *i64, 57 out: *HvacEff, plan: *ActionPlan, 58 setpoint: i64, min_resp: i64) -> i64 { 59 var c: i64 = 0 60 while c < n_cycles { 61 var eff: i64 = (tw.base_resp * (1000 - tw.fouling)) / 1000 62 if eff < 0 { eff = 0 } 63 64 // generate the simulated heating run (resp == eff) 65 t[0] = 0 66 t[1] = K_MAGIC_1800 67 indoor[0] = K_MAGIC_19000 68 indoor[1] = K_MAGIC_19000 + eff * 30 69 outdoor[0] = K_MAGIC_5000 70 outdoor[1] = K_MAGIC_5000 71 on[0] = 1 72 on[1] = 1 73 nx_hvac_analyze_core(t, indoor, outdoor, on, 2, setpoint, NX_HVAC_MODE_HEAT, 35, min_resp, 600, out) 74 let verdict: i64 = out.verdict 75 let sev: i64 = nx_maint_severity(NX_MK_THERMAL, verdict) 76 nx_plan_for(NX_MK_THERMAL, verdict, NX_TREND_STABLE, plan) 77 78 tw.cycles_run = tw.cycles_run + 1 79 if sev >= NX_HEALTH_DEGRADED { tw.failures_seen = tw.failures_seen + 1 } 80 81 // CLOSE THE LOOP: a fix/replace action feeds back and resets the wear. 82 var fixed: i64 = 0 83 if apply_fix == 1 { 84 if plan.action == NX_ACT_FIX_DIY { fixed = 1 } 85 if plan.action == NX_ACT_FIX_PRO { fixed = 1 } 86 if plan.action == NX_ACT_REPLACE_CONSUMABLE { fixed = 1 } 87 if plan.action == NX_ACT_REPLACE_COMPONENT { fixed = 1 } 88 } 89 if fixed == 1 { 90 tw.fouling = 0 91 tw.fixes_applied = tw.fixes_applied + 1 92 } else { 93 tw.fouling = tw.fouling + tw.wear_per_cycle 94 } 95 c = c + 1 96 } 97 return tw.fixes_applied 98}