code wiki / _hdl_build / nx_batt_facility_test.nx

nx_batt_facility_test.nx source

↩ module page · 103 lines · 6242 B

1import "nx_gate_gn.nx" 2// nx_batt_facility_test.nx -- gate for R3. Proves every interlock refusal + admit + auto-isolation + the R1->R3 3// chain (an unsafe pack can never get a charging bay) + liar-kill (disarm thermal -> admit flips to refuse). 4// expect_exit: 0 5import "nx_syscalls.nx" 6import "nx_batt_intake.nx" 7import "nx_batt_facility.nx" 8 9func gp(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 10func mk_fac() -> *FacilitySpec { 11 let s: *FacilitySpec = (sys_mmap(128)) as *FacilitySpec 12 s.n_bays = 6; s.bay_max_energy_wh = 1500; s.facility_max_energy_wh = 8000; s.cabinet_capacity_wh = 4000 13 s.station_max_charge_ma = 2000; s.storage_cap_lb = 220; s.thermal_isolation_armed = 1 14 return s 15} 16func mk_ispec() -> *IntakeSpec { 17 let s: *IntakeSpec = (sys_mmap(128)) as *IntakeSpec 18 s.unsafe_cell_floor_mv = 2000; s.healthy_cell_min_mv = 3000; s.healthy_cell_max_mv = 4250 19 s.soh_retire_permille = 700; s.max_imbalance_mv = 100; s.max_ir_mohm = 150 20 s.temp_min_mc = 0; s.temp_max_mc = 45000; s.recell_max_bad = 3 21 return s 22} 23func mk_pack() -> *PackState { 24 let p: *PackState = (sys_mmap(128)) as *PackState 25 p.n_cells = 10; p.min_cell_mv = 3700; p.max_cell_mv = 3720; p.n_bad_cells = 0 26 p.rated_capacity_mah = 2500; p.measured_capacity_mah = 2400; p.max_ir_mohm = 40 27 p.pack_temp_mc = 25000; p.terminal_mv = 37000; p.swollen = 0 28 return p 29} 30 31func main() -> i64 { 32 var pass: i64=0; var fail: i64=0 33 gp("=== nx_batt_facility_test: R3 fire-safe facility + self-service interlock ===\n" as *u8) 34 35 // T1 ADMIT a safe session within all caps 36 let a1: i64 = nx_fac_admit(mk_fac(), NX_BI_RECONDITION, 2, 3000, 800, 1500, 100) 37 gp(" T1 safe session -> " as *u8); gn(a1); gp(" (exp 0 ADMIT)\n" as *u8) 38 if a1==NX_FAC_ADMIT { pass=pass+1 } else { fail=fail+1 } 39 40 // T2 unsafe pack -> never admitted to a bay 41 let a2: i64 = nx_fac_admit(mk_fac(), NX_BI_UNSAFE_REJECT, 2, 3000, 800, 1500, 100) 42 gp(" T2 unsafe pack -> " as *u8); gn(a2); gp(" (exp 1 PACK_UNSAFE)\n" as *u8) 43 if a2==NX_FAC_REFUSE_PACK_UNSAFE { pass=pass+1 } else { fail=fail+1 } 44 45 // T3 thermal monitor disarmed -> NO charging (never-ignite), even for a safe pack 46 let fnt: *FacilitySpec = mk_fac(); fnt.thermal_isolation_armed = 0 47 let a3: i64 = nx_fac_admit(fnt, NX_BI_RECONDITION, 0, 0, 800, 1500, 100) 48 gp(" T3 thermal disarmed -> " as *u8); gn(a3); gp(" (exp 5 NO_THERMAL)\n" as *u8) 49 if a3==NX_FAC_REFUSE_NO_THERMAL { pass=pass+1 } else { fail=fail+1 } 50 51 // T4 all bays occupied 52 let a4: i64 = nx_fac_admit(mk_fac(), NX_BI_RECONDITION, 6, 3000, 800, 1500, 100) 53 gp(" T4 bays full -> " as *u8); gn(a4); gp(" (exp 2 BAY_FULL)\n" as *u8) 54 if a4==NX_FAC_REFUSE_BAY_FULL { pass=pass+1 } else { fail=fail+1 } 55 56 // T5 requested charge rate over station cap 57 let a5: i64 = nx_fac_admit(mk_fac(), NX_BI_RECONDITION, 2, 3000, 800, 2500, 100) 58 gp(" T5 over station rate -> " as *u8); gn(a5); gp(" (exp 4 STATION_RATE)\n" as *u8) 59 if a5==NX_FAC_REFUSE_STATION_RATE { pass=pass+1 } else { fail=fail+1 } 60 61 // T6 per-bay energy / fire-load cap 62 let a6: i64 = nx_fac_admit(mk_fac(), NX_BI_RECONDITION, 2, 3000, 1600, 1500, 100) 63 gp(" T6 over bay energy -> " as *u8); gn(a6); gp(" (exp 3 ENERGY_CAP)\n" as *u8) 64 if a6==NX_FAC_REFUSE_ENERGY_CAP { pass=pass+1 } else { fail=fail+1 } 65 66 // T7 facility total energy / fire-load cap 67 let a7: i64 = nx_fac_admit(mk_fac(), NX_BI_RECONDITION, 3, 7500, 800, 1500, 100) 68 gp(" T7 over facility energy-> " as *u8); gn(a7); gp(" (exp 3 ENERGY_CAP)\n" as *u8) 69 if a7==NX_FAC_REFUSE_ENERGY_CAP { pass=pass+1 } else { fail=fail+1 } 70 71 // T8 regulatory Li-ion storage over VSQG (220 lb) limit 72 let a8: i64 = nx_fac_admit(mk_fac(), NX_BI_RECONDITION, 1, 1000, 500, 1500, 250) 73 gp(" T8 over VSQG storage -> " as *u8); gn(a8); gp(" (exp 6 STORAGE_CAP)\n" as *u8) 74 if a8==NX_FAC_REFUSE_STORAGE_CAP { pass=pass+1 } else { fail=fail+1 } 75 76 // T9 auto-isolation: over-temp bay cut from power; normal bay not 77 let i_hot: i64 = nx_fac_isolate(65000, 60000) 78 let i_ok: i64 = nx_fac_isolate(40000, 60000) 79 gp(" T9 isolate hot=" as *u8); gn(i_hot); gp(" ok=" as *u8); gn(i_ok); gp(" (exp 1 / 0)\n" as *u8) 80 if i_hot==1 { if i_ok==0 { pass=pass+1 } else { fail=fail+1 } } else { fail=fail+1 } 81 82 // T10 FULL R1->R3 CHAIN: a swollen pack triages UNSAFE -> facility refuses the bay; a healthy pack -> admitted 83 let isp: *IntakeSpec = mk_ispec() 84 let bad: *PackState = mk_pack(); bad.swollen = 1 85 let vbad: i64 = nx_batt_triage(bad, isp) 86 let cbad: i64 = nx_fac_admit(mk_fac(), vbad, 2, 3000, 800, 1500, 100) 87 let good: *PackState = mk_pack(); good.min_cell_mv = 3500; good.max_cell_mv = 3750 88 let vgood: i64 = nx_batt_triage(good, isp) 89 let cgood: i64 = nx_fac_admit(mk_fac(), vgood, 2, 3000, 800, 1500, 100) 90 gp(" T10 chain: swollen triage=" as *u8); gn(vbad); gp("->admit=" as *u8); gn(cbad); gp("(refuse) ; healthy triage=" as *u8); gn(vgood); gp("->admit=" as *u8); gn(cgood); gp("(admit)\n" as *u8) 91 if vbad==NX_BI_UNSAFE_REJECT { if cbad==NX_FAC_REFUSE_PACK_UNSAFE { if cgood==NX_FAC_ADMIT { pass=pass+1 } else { fail=fail+1 } } else { fail=fail+1 } } else { fail=fail+1 } 92 93 // LIAR-KILL: same safe session admits; disarm thermal -> MUST flip to refuse (interlock load-bearing) 94 let la: i64 = nx_fac_admit(mk_fac(), NX_BI_RECONDITION, 2, 3000, 800, 1500, 100) 95 let fld: *FacilitySpec = mk_fac(); fld.thermal_isolation_armed = 0 96 let lb: i64 = nx_fac_admit(fld, NX_BI_RECONDITION, 2, 3000, 800, 1500, 100) 97 gp(" LIAR armed=" as *u8); gn(la); gp(" disarmed=" as *u8); gn(lb); gp(" (must be 0 then 5)\n" as *u8) 98 if la==NX_FAC_ADMIT { if lb==NX_FAC_REFUSE_NO_THERMAL { pass=pass+1 } else { fail=fail+1; gp(" FAIL liar\n" as *u8) } } else { fail=fail+1; gp(" FAIL liar-base\n" as *u8) } 99 100 gp("BATT-FACILITY pass=" as *u8); gn(pass); gp(" fail=" as *u8); gn(fail) 101 if fail==0 { gp(" verdict=GREEN (every interlock + auto-isolation + R1->R3 chain + liar-kill; never-ignite at the facility level)\n" as *u8); sys_exit(0); return 0 } 102 gp(" verdict=RED\n" as *u8); sys_exit(1); return 1 103}