code wiki / (root) / nx_ac_monitor_test.nx

nx_ac_monitor_test.nx source

↩ module page · 123 lines · 4621 B

1// nx_ac_monitor_test.nx -- gate for the unified product brain. 2// 3// Proves the full pipeline (reading -> trust -> FDD + drift -> alert) and, 4// critically, that the LIAR-KILLER GATES the diagnosis: an impossible or 5// conflicted reading yields alert=UNTRUSTED with NO fault emitted (the monitor 6// cannot raise a false alarm from a lying sensor). 7// 8// expect_exit: 0 9// license_tier: ORIGINAL 10 11import "nx_syscalls_x86_64.nx" 12import "nx_ac_monitor.nx" 13 14// healthy reading with outdoor + refrigerant channels -> PLAUSIBLE / NOMINAL 15func mon_reset_rd(r: *AcReading) -> i64 { 16 r.mode = NX_HVAC_MODE_COOL 17 r.t_supply_mC = 12000 18 r.rh_supply_pm = 900 19 r.t_return_mC = 24000 20 r.rh_return_pm = 500 21 r.airflow_cfm = 1200 22 r.rho_gm3 = 1200 23 r.t_suction_mC = 13000 24 r.t_sat_evap_mC = 5000 25 r.t_liquid_mC = 39000 26 r.t_sat_cond_mC = 44000 27 r.t_outdoor_mC = 35000 28 r.p_elec_w = 3000 29 r.t_return_b_mC = NX_ACV_ABSENT 30 r.return_a_src = 1 31 r.return_b_src = 0 32 r.return_tol_mC = 500 33 return 0 34} 35 36func mon_reset_th(t: *FddThresh) -> i64 { 37 t.target_superheat_mC = 8333 38 t.superheat_band_mC = 2778 39 t.target_subcool_mC = 5556 40 t.subcool_band_mC = 2778 41 t.split_ceiling_mC = 13889 42 t.cond_over_ambient_ceiling_mC = 25000 43 t.cfm_per_ton_floor = 350 44 t.max_cph_x10 = 35 45 t.freeze_suction_sat_mC = 0 46 return 0 47} 48 49func main() -> i64 { 50 if nx_alert_is_valid(NX_ALERT_CRITICAL) != 1 { return 1 } 51 if nx_alert_is_valid(NX_ALERT_N) != 0 { return 2 } 52 53 let r: *AcReading = sys_mmap(256) as *AcReading 54 let av: *AcVerify = sys_mmap(128) as *AcVerify 55 let fin: *FddInput = sys_mmap(128) as *FddInput 56 let th: *FddThresh = sys_mmap(128) as *FddThresh 57 let fout: *FddOut = sys_mmap(64) as *FddOut 58 let o: *MonitorResult = sys_mmap(64) as *MonitorResult 59 mon_reset_th(th) 60 61 // ===== healthy -> PLAUSIBLE / NOMINAL / OK ====================== 62 mon_reset_rd(r) 63 nx_ac_monitor(r, av, fin, th, fout, 360, 150, 80, o) 64 if o.trust_verdict != NX_ACV_PLAUSIBLE { return 10 } 65 if o.fdd_verdict != NX_FDD_NOMINAL { return 11 } 66 if o.alert_level != NX_ALERT_OK { return 12 } 67 if o.cop_x100 < 300 { return 13 } 68 if o.cop_x100 > 420 { return 14 } 69 70 // ===== undercharge -> PLAUSIBLE / UNDERCHARGE / WARN ============ 71 mon_reset_rd(r) 72 r.t_suction_mC = 20000 73 r.t_sat_evap_mC = 3000 // superheat 17000 (high) 74 r.t_liquid_mC = 42000 75 r.t_sat_cond_mC = 44000 // subcool 2000 (low) 76 nx_ac_monitor(r, av, fin, th, fout, 360, 150, 80, o) 77 if o.trust_verdict != NX_ACV_PLAUSIBLE { return 20 } 78 if o.fdd_verdict != NX_FDD_UNDERCHARGE { return 21 } 79 if o.alert_level != NX_ALERT_WARN { return 22 } 80 81 // ===== frozen coil -> CRITICAL (severity beats everything) ====== 82 mon_reset_rd(r) 83 r.t_sat_evap_mC = -2000 // evaporator saturation below freezing 84 nx_ac_monitor(r, av, fin, th, fout, 360, 150, 80, o) 85 if o.trust_verdict != NX_ACV_PLAUSIBLE { return 30 } 86 if o.fdd_verdict != NX_FDD_FROZEN_COIL { return 31 } 87 if o.alert_level != NX_ALERT_CRITICAL { return 32 } 88 89 // ===== efficiency drift -> WARN (no fault, just degraded) ======= 90 // healthy unit, but baseline COP 4.60 vs current ~3.58 = ~22% worse 91 mon_reset_rd(r) 92 nx_ac_monitor(r, av, fin, th, fout, 460, 150, 80, o) 93 if o.fdd_verdict != NX_FDD_NOMINAL { return 40 } 94 if o.alert_level != NX_ALERT_WARN { return 41 } 95 if o.efficiency_drift_pm < 150 { return 42 } 96 97 // ===== efficiency drift -> INFO (mild) ========================== 98 mon_reset_rd(r) 99 nx_ac_monitor(r, av, fin, th, fout, 400, 150, 80, o) 100 if o.alert_level != NX_ALERT_INFO { return 45 } 101 102 // ===== UNTRUSTED: impossible reading -> NO fault emitted ======== 103 // The liar-killer GATES the diagnosis: a 150% RH reading cannot raise 104 // a fault alert; it is flagged UNTRUSTED and the FDD is not run. 105 mon_reset_rd(r) 106 r.rh_return_pm = 1500 107 nx_ac_monitor(r, av, fin, th, fout, 360, 150, 80, o) 108 if o.trust_verdict != NX_ACV_IMPLAUSIBLE_PHYSICS { return 50 } 109 if o.alert_level != NX_ALERT_UNTRUSTED { return 51 } 110 if o.fdd_verdict != NX_FDD_INSUFFICIENT_DATA { return 52 } 111 112 // ===== UNTRUSTED: conflicting independent sensors =============== 113 mon_reset_rd(r) 114 r.t_return_b_mC = 27000 115 r.return_a_src = 1 116 r.return_b_src = 2 117 nx_ac_monitor(r, av, fin, th, fout, 360, 150, 80, o) 118 if o.trust_verdict != NX_ACV_CONFLICTED { return 60 } 119 if o.alert_level != NX_ALERT_UNTRUSTED { return 61 } 120 if o.fdd_verdict != NX_FDD_INSUFFICIENT_DATA { return 62 } 121 122 return 0 123}