code wiki / _hdl_build / nx_batt_recondition_test.nx
nx_batt_recondition_test.nx source
↩ module page · 102 lines · 7405 B
1import "nx_gate_gn.nx"
2// nx_batt_recondition_test.nx -- gate for R2. Proves: each verdict drives the right staged restore; rejects/retires
3// are NEVER charged; an over-rate or over-voltage plan is REFUSED by the spine (never-ignite enforced in the
4// WORKFLOW, not just the primitive); the refusal IS the spine's refusal (delegation proven); and the full R1->R2
5// chain works (triage -> recondition). expect_exit: 0
6import "nx_syscalls.nx"
7import "nx_intent.nx"
8import "nx_battery_safety.nx"
9import "nx_batt_intake.nx"
10import "nx_batt_recondition.nx"
11
12func gp(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
13// safe envelope: 2A charge, 5A discharge, 60C, 3.0-4.2V
14func mk_env() -> *NxBatterySafetyEnvelope { return nx_battery_safety_envelope_new(1, 2000, 5000, 60000, 3000, 4200) }
15func mk_log() -> *NxBatteryCommandLog { return nx_battery_command_log_new(16) }
16func mk_spec() -> *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 let st: *i64 = sys_mmap(8) as *i64
34 gp("=== nx_batt_recondition_test: R2 staged restore, every step routed through the never-ignite spine ===\n" as *u8)
35
36 // T1 RECONDITION safe -> RESTORED, 4 steps
37 let r1: i64 = nx_recondition_run(NX_BI_RECONDITION, mk_env(), mk_log(), 1500, 3700, 1000, st)
38 gp(" T1 RECONDITION safe -> verdict " as *u8); gn(r1); gp(" steps " as *u8); gn(st[0]); gp(" (exp 0 / 4)\n" as *u8)
39 if r1==NX_RW_DONE_RESTORED { if st[0]==4 { pass=pass+1 } else { fail=fail+1 } } else { fail=fail+1 }
40
41 // T2 BMS_RESET safe -> RESTORED, 1 step
42 let r2: i64 = nx_recondition_run(NX_BI_BMS_RESET, mk_env(), mk_log(), 800, 3700, 1000, st)
43 gp(" T2 BMS_RESET safe -> verdict " as *u8); gn(r2); gp(" steps " as *u8); gn(st[0]); gp(" (exp 0 / 1)\n" as *u8)
44 if r2==NX_RW_DONE_RESTORED { if st[0]==1 { pass=pass+1 } else { fail=fail+1 } } else { fail=fail+1 }
45
46 // T3 RECELL safe -> RESTORED, 6 steps
47 let r3: i64 = nx_recondition_run(NX_BI_RECELL, mk_env(), mk_log(), 1500, 3700, 1000, st)
48 gp(" T3 RECELL safe -> verdict " as *u8); gn(r3); gp(" steps " as *u8); gn(st[0]); gp(" (exp 0 / 6)\n" as *u8)
49 if r3==NX_RW_DONE_RESTORED { if st[0]==6 { pass=pass+1 } else { fail=fail+1 } } else { fail=fail+1 }
50
51 // T4 UNSAFE_REJECT -> RETIRED, NEVER charged (0 steps) -- the key safety proof
52 let r4: i64 = nx_recondition_run(NX_BI_UNSAFE_REJECT, mk_env(), mk_log(), 1500, 3700, 1000, st)
53 gp(" T4 UNSAFE_REJECT -> verdict " as *u8); gn(r4); gp(" steps " as *u8); gn(st[0]); gp(" (exp 1 / 0 = retired, never charged)\n" as *u8)
54 if r4==NX_RW_DONE_RETIRED { if st[0]==0 { pass=pass+1 } else { fail=fail+1 } } else { fail=fail+1 }
55
56 // T5 RETIRE_RECYCLE -> RETIRED, 0 steps
57 let r5: i64 = nx_recondition_run(NX_BI_RETIRE_RECYCLE, mk_env(), mk_log(), 1500, 3700, 1000, st)
58 gp(" T5 RETIRE_RECYCLE -> verdict " as *u8); gn(r5); gp(" steps " as *u8); gn(st[0]); gp(" (exp 1 / 0)\n" as *u8)
59 if r5==NX_RW_DONE_RETIRED { if st[0]==0 { pass=pass+1 } else { fail=fail+1 } } else { fail=fail+1 }
60
61 // T6 PASS_HEALTHY -> RESTORED, 0 steps
62 let r6: i64 = nx_recondition_run(NX_BI_PASS_HEALTHY, mk_env(), mk_log(), 1500, 3700, 1000, st)
63 gp(" T6 PASS_HEALTHY -> verdict " as *u8); gn(r6); gp(" steps " as *u8); gn(st[0]); gp(" (exp 0 / 0)\n" as *u8)
64 if r6==NX_RW_DONE_RESTORED { if st[0]==0 { pass=pass+1 } else { fail=fail+1 } } else { fail=fail+1 }
65
66 // T7 NEVER-IGNITE: RECONDITION but charge 3000mA > 2000 ceiling -> REFUSED_UNSAFE, 0 steps completed
67 let r7: i64 = nx_recondition_run(NX_BI_RECONDITION, mk_env(), mk_log(), 3000, 3700, 1000, st)
68 gp(" T7 over-rate charge -> verdict " as *u8); gn(r7); gp(" steps " as *u8); gn(st[0]); gp(" (exp 2 / 0 = refused at the wall)\n" as *u8)
69 if r7==NX_RW_REFUSED_UNSAFE { if st[0]==0 { pass=pass+1 } else { fail=fail+1 } } else { fail=fail+1 }
70
71 // T8 voltage wall: RECONDITION at 4500mV > 4200 -> REFUSED_UNSAFE
72 let r8: i64 = nx_recondition_run(NX_BI_RECONDITION, mk_env(), mk_log(), 1500, 4500, 1000, st)
73 gp(" T8 over-voltage -> verdict " as *u8); gn(r8); gp(" (exp 2 REFUSED_UNSAFE)\n" as *u8)
74 if r8==NX_RW_REFUSED_UNSAFE { pass=pass+1 } else { fail=fail+1 }
75
76 // T9 composition/delegation: the spine refuses the over-rate directly, and the workflow's refusal IS that refusal
77 let direct: i64 = nx_battery_validate_command(mk_env(), NX_INTENT_DEFENSIVE, NX_BC_CHARGE, 3000, 3700, mk_log(), 1000)
78 gp(" T9 delegate: spine direct=" as *u8); gn(direct); gp(" (exp " as *u8); gn(NX_BS_REFUSED_RATE_CEILING); gp("), workflow=" as *u8); gn(r7); gp("\n" as *u8)
79 if direct==NX_BS_REFUSED_RATE_CEILING { if r7==NX_RW_REFUSED_UNSAFE { pass=pass+1 } else { fail=fail+1 } } else { fail=fail+1 }
80
81 // T10 FULL R1->R2 CHAIN: a swollen pack triages UNSAFE -> reconditions to RETIRED (never charged);
82 // a healthy-imbalanced pack triages RECONDITION -> reconditions to RESTORED.
83 let spec: *IntakeSpec = mk_spec()
84 let bad: *PackState = mk_pack(); bad.swollen = 1
85 let vbad: i64 = nx_batt_triage(bad, spec)
86 let rbad: i64 = nx_recondition_run(vbad, mk_env(), mk_log(), 1500, 3700, 1000, st)
87 let imb: *PackState = mk_pack(); imb.min_cell_mv = 3500; imb.max_cell_mv = 3750
88 let vimb: i64 = nx_batt_triage(imb, spec)
89 let rimb: i64 = nx_recondition_run(vimb, mk_env(), mk_log(), 1500, 3700, 1000, st)
90 gp(" T10 chain: swollen triage=" as *u8); gn(vbad); gp("->recon=" as *u8); gn(rbad); gp("(retire) ; imbalance triage=" as *u8); gn(vimb); gp("->recon=" as *u8); gn(rimb); gp("(restore)\n" as *u8)
91 if vbad==NX_BI_UNSAFE_REJECT { if rbad==NX_RW_DONE_RETIRED { if vimb==NX_BI_RECONDITION { if rimb==NX_RW_DONE_RESTORED { pass=pass+1 } else { fail=fail+1 } } else { fail=fail+1 } } else { fail=fail+1 } } else { fail=fail+1 }
92
93 // LIAR-KILL: same RECONDITION verdict, safe vs over-rate -> verdict MUST flip RESTORED->REFUSED
94 let la: i64 = nx_recondition_run(NX_BI_RECONDITION, mk_env(), mk_log(), 1500, 3700, 1000, st)
95 let lb: i64 = nx_recondition_run(NX_BI_RECONDITION, mk_env(), mk_log(), 9000, 3700, 1000, st)
96 gp(" LIAR safe=" as *u8); gn(la); gp(" over-rate=" as *u8); gn(lb); gp(" (must be 0 then 2)\n" as *u8)
97 if la==NX_RW_DONE_RESTORED { if lb==NX_RW_REFUSED_UNSAFE { pass=pass+1 } else { fail=fail+1; gp(" FAIL liar\n" as *u8) } } else { fail=fail+1; gp(" FAIL liar-base\n" as *u8) }
98
99 gp("BATT-RECONDITION pass=" as *u8); gn(pass); gp(" fail=" as *u8); gn(fail)
100 if fail==0 { gp(" verdict=GREEN (staged restore + rejects-never-charged + over-rate/voltage refused by the spine + R1->R2 chain)\n" as *u8); sys_exit(0); return 0 }
101 gp(" verdict=RED\n" as *u8); sys_exit(1); return 1
102}