nx_print_runtime_compose_test.nx source
↩ module page · 150 lines · 6512 B
1// nx_print_runtime_compose_test.nx -- composition smoke for the
2// 9-progenitor runtime monitor. Walks one monitor through a
3// synthetic 1-hour print timeline, deliberately triggering each
4// of the 9 issue kinds at least once and verifying
5// n_issues_total counts every fire across all five step* surfaces:
6// nx_runtime_monitor_step (3 issue kinds)
7// nx_runtime_monitor_step_thermal (3 issue kinds)
8// nx_runtime_monitor_step_runout (1)
9// nx_runtime_monitor_step_layer_shift (1)
10// (Z_BACKWARD also via step() 1)
11// Total: 9 distinct kinds.
12//
13// expect_exit: 0
14// license_tier: ORIGINAL
15
16import "nx_syscalls.nx"
17import "nx_print_runtime_monitor.nx"
18
19const Q14: i64 = 16384
20
21func expect_kind(actual: i64, expected: i64, code: i64) -> i64 {
22 if actual != expected { return code }
23 return 0
24}
25
26func main() -> i64 {
27 let m: *NxRuntimeMonitor = nx_runtime_monitor_new(5000, 3, 5 * Q14, 2 * Q14)
28 if (m as i64) == 0 { return 5 }
29 // 2°C tolerance, 2s alarm duration on thermals
30 nx_runtime_monitor_set_thermal(m, 2, 2000)
31 // 0.3mm tolerance on layer shift
32 nx_runtime_monitor_set_layer_shift_tolerance(m, (3 * Q14) / 10)
33
34 // ===== Phase 1: clean startup (no issues) =====
35 // First event primes state.
36 if nx_runtime_monitor_step(m, 0, 0, 0, 0) != NX_RT_ISSUE_NONE { return 10 }
37 if nx_runtime_monitor_step_thermal(m, 60, 60, 210, 210, 30, 30, 0) != NX_RT_ISSUE_NONE { return 11 }
38 if nx_runtime_monitor_step_runout(m, 0) != NX_RT_ISSUE_NONE { return 12 }
39 if nx_runtime_monitor_step_layer_shift(m, 0, 0, 0, 0) != NX_RT_ISSUE_NONE { return 13 }
40 if m.n_issues_total != 0 { return 14 }
41
42 // ===== Phase 2: normal printing (3 layers) =====
43 var t: i64 = 0
44 var layer: i64 = 1
45 while layer <= 3 {
46 t = t + 5000
47 let z: i64 = layer * Q14 / 5 // 0.2 mm/layer
48 let e_acc: i64 = layer * 10 * Q14
49 if nx_runtime_monitor_step(m, z, e_acc, 50 * Q14, t) != NX_RT_ISSUE_NONE { return 20 }
50 if nx_runtime_monitor_step_thermal(m, 60, 60, 210, 210, 30, 30, t) != NX_RT_ISSUE_NONE { return 21 }
51 layer = layer + 1
52 }
53 if m.n_issues_total != 0 { return 22 }
54
55 // ===== Phase 3: deliberately trigger STUCK_LAYER =====
56 // Don't advance Z, wait > 15s (3 × 5s expected)
57 t = t + 20000
58 let v_stuck: i64 = nx_runtime_monitor_step(m, 3 * Q14 / 5, 40 * Q14, 1 * Q14, t)
59 let r1: i64 = expect_kind(v_stuck, NX_RT_ISSUE_STUCK_LAYER, 30)
60 if r1 != 0 { return r1 }
61
62 // ===== Phase 4: BED_TEMP_DROP =====
63 // Bed drops 5°C (> 2°C tol) for > 2s
64 t = t + 1000
65 if nx_runtime_monitor_step_thermal(m, 55, 60, 210, 210, 30, 30, t) != NX_RT_ISSUE_NONE { return 40 }
66 t = t + 2500
67 let v_bed: i64 = nx_runtime_monitor_step_thermal(m, 55, 60, 210, 210, 30, 30, t)
68 let r2: i64 = expect_kind(v_bed, NX_RT_ISSUE_BED_TEMP_DROP, 41)
69 if r2 != 0 { return r2 }
70
71 // ===== Phase 5: HOTEND_TEMP_DROP =====
72 // Recover bed, then drop hotend
73 t = t + 1000
74 if nx_runtime_monitor_step_thermal(m, 60, 60, 200, 210, 30, 30, t) != NX_RT_ISSUE_NONE { return 50 }
75 t = t + 2500
76 let v_hot: i64 = nx_runtime_monitor_step_thermal(m, 60, 60, 200, 210, 30, 30, t)
77 let r3: i64 = expect_kind(v_hot, NX_RT_ISSUE_HOTEND_TEMP_DROP, 51)
78 if r3 != 0 { return r3 }
79
80 // ===== Phase 6: CHAMBER_TEMP_DROP =====
81 t = t + 1000
82 if nx_runtime_monitor_step_thermal(m, 60, 60, 210, 210, 25, 30, t) != NX_RT_ISSUE_NONE { return 60 }
83 t = t + 2500
84 let v_cham: i64 = nx_runtime_monitor_step_thermal(m, 60, 60, 210, 210, 25, 30, t)
85 let r4: i64 = expect_kind(v_cham, NX_RT_ISSUE_CHAMBER_TEMP_DROP, 61)
86 if r4 != 0 { return r4 }
87
88 // Thermal recovery
89 t = t + 1000
90 if nx_runtime_monitor_step_thermal(m, 60, 60, 210, 210, 30, 30, t) != NX_RT_ISSUE_NONE { return 62 }
91
92 // ===== Phase 7: FILAMENT_RUNOUT (edge) =====
93 let v_run: i64 = nx_runtime_monitor_step_runout(m, 1)
94 let r5: i64 = expect_kind(v_run, NX_RT_ISSUE_FILAMENT_RUNOUT, 70)
95 if r5 != 0 { return r5 }
96 // Don't re-fire on steady-1
97 if nx_runtime_monitor_step_runout(m, 1) != NX_RT_ISSUE_NONE { return 71 }
98 // Operator reloads
99 if nx_runtime_monitor_step_runout(m, 0) != NX_RT_ISSUE_NONE { return 72 }
100
101 // ===== Phase 8: LAYER_SHIFT =====
102 let v_shift: i64 = nx_runtime_monitor_step_layer_shift(m,
103 100 * Q14, 100 * Q14 - Q14, // 1mm drift > 0.3mm tol
104 200 * Q14, 200 * Q14)
105 let r6: i64 = expect_kind(v_shift, NX_RT_ISSUE_LAYER_SHIFT, 80)
106 if r6 != 0 { return r6 }
107
108 // ===== Phase 9: FLOW_ANOMALY =====
109 // Need a fresh state because cumulative_xy_q14 is mid-layer
110 // and dz must be 0; the monitor's last_layer was at phase 3.
111 // We're well past the STUCK threshold now (last_layer_change was
112 // ~5 minutes ago in synthetic time). So a step at the same Z with
113 // big e and small xy would fire STUCK first. To isolate FLOW,
114 // use a second monitor.
115 let m2: *NxRuntimeMonitor = nx_runtime_monitor_new(5000, 3, 5 * Q14, 2 * Q14)
116 nx_runtime_monitor_step(m2, 200 * Q14 / 1000, 0, 0, 0)
117 let v_flow: i64 = nx_runtime_monitor_step(m2,
118 200 * Q14 / 1000, // same Z (dz=0)
119 10 * Q14, // big e
120 1 * Q14, // small xy
121 100)
122 let r7: i64 = expect_kind(v_flow, NX_RT_ISSUE_FLOW_ANOMALY, 90)
123 if r7 != 0 { return r7 }
124
125 // ===== Phase 10: EXTRUDER_RUNAWAY =====
126 let m3: *NxRuntimeMonitor = nx_runtime_monitor_new(5000, 3, 5 * Q14, 2 * Q14)
127 nx_runtime_monitor_step(m3, 0, 0, 0, 0)
128 nx_runtime_monitor_step(m3, 4 * Q14 / 10, 10 * Q14, 50 * Q14, 5000)
129 // Stay at same Z; e jumps by 100mm with xy only 5mm
130 let v_run2: i64 = nx_runtime_monitor_step(m3,
131 4 * Q14 / 10,
132 110 * Q14, 5 * Q14, 6000)
133 let r8: i64 = expect_kind(v_run2, NX_RT_ISSUE_EXTRUDER_RUNAWAY, 100)
134 if r8 != 0 { return r8 }
135
136 // ===== Phase 11: Z_BACKWARD =====
137 let m4: *NxRuntimeMonitor = nx_runtime_monitor_new(5000, 3, 5 * Q14, 2 * Q14)
138 nx_runtime_monitor_step(m4, 0, 0, 0, 0)
139 nx_runtime_monitor_step(m4, 12 * Q14, 30 * Q14, 50 * Q14, 5000)
140 let v_back: i64 = nx_runtime_monitor_step(m4,
141 0, 30 * Q14, 0, 6000)
142 let r9: i64 = expect_kind(v_back, NX_RT_ISSUE_Z_BACKWARD, 110)
143 if r9 != 0 { return r9 }
144
145 // ===== Final tally on primary monitor m =====
146 // m fired: STUCK + BED + HOTEND + CHAMBER + RUNOUT + SHIFT = 6
147 if m.n_issues_total != 6 { return 120 }
148
149 return 0
150}