nx_print_runtime_monitor_test.nx source
↩ module page · 97 lines · 4955 B
1// nx_print_runtime_monitor_test.nx -- exercise the runtime spaghetti
2// monitor against synthetic telemetry sequences.
3//
4// Closed-form invariants:
5// (a) Null/bad constructor inputs -> null monitor
6// (b) First event primes state; no issue
7// (c) Normal layer progression (Z advancing every ~expected_time):
8// NO issues raised
9// (d) STUCK_LAYER: same Z for > expected × multiple ms -> fires
10// (e) FLOW_ANOMALY: extruder advances but XY doesn't, Z static -> fires
11// (f) EXTRUDER_RUNAWAY: e advances WAY more than xy -> fires
12// (g) After issue fires, n_issues_total increments
13//
14// expect_exit: 0
15// license_tier: ORIGINAL
16
17import "nx_syscalls.nx"
18import "nx_print_runtime_monitor.nx"
19
20const Q14: i64 = 16384
21
22func main() -> i64 {
23 // ===== (a) Bad inputs =====
24 let m_bad: *NxRuntimeMonitor = nx_runtime_monitor_new(0, 3, Q14, Q14)
25 if (m_bad as i64) != 0 { return 10 }
26 let m_bad2: *NxRuntimeMonitor = nx_runtime_monitor_new(1000, 1, Q14, Q14)
27 if (m_bad2 as i64) != 0 { return 11 }
28
29 // ===== Build a monitor: expect 5s per layer, alarm at 3× (15s).
30 // Flow anomaly: e > 5mm advance + xy < 2mm + same Z -> alarm.
31 let m: *NxRuntimeMonitor = nx_runtime_monitor_new(
32 5000, // expected_layer_time_ms
33 3, // stuck_layer_multiple
34 5 * Q14, // flow_anomaly_e_mm_q14
35 2 * Q14) // flow_anomaly_xy_mm_q14
36 if (m as i64) == 0 { return 20 }
37
38 // ===== (b) First event: no issue =====
39 let v0: i64 = nx_runtime_monitor_step(m, 200 * Q14 / 1000, 0, 0, 0) // z=0.2mm, t=0
40 if v0 != NX_RT_ISSUE_NONE { return 30 }
41
42 // ===== (c) Normal layer progression: NO issues =====
43 // 5 layers, each advancing Z by 0.2mm at 5s intervals
44 let v1: i64 = nx_runtime_monitor_step(m, 400 * Q14 / 1000, 10 * Q14, 50 * Q14, 5000)
45 if v1 != NX_RT_ISSUE_NONE { return 40 }
46 let v2: i64 = nx_runtime_monitor_step(m, 600 * Q14 / 1000, 20 * Q14, 50 * Q14, 10000)
47 if v2 != NX_RT_ISSUE_NONE { return 41 }
48 let v3: i64 = nx_runtime_monitor_step(m, 800 * Q14 / 1000, 30 * Q14, 50 * Q14, 15000)
49 if v3 != NX_RT_ISSUE_NONE { return 42 }
50 if m.n_issues_total != 0 { return 43 }
51
52 // ===== (d) STUCK_LAYER: stay on same Z for > 15s (3 × 5s) =====
53 // Same Z=0.8mm but big time advance (16s past last layer change).
54 let v_stuck: i64 = nx_runtime_monitor_step(m, 800 * Q14 / 1000,
55 35 * Q14, // small extrusion
56 1 * Q14, // small XY
57 15000 + 16000) // 16s past last layer
58 if v_stuck != NX_RT_ISSUE_STUCK_LAYER { return 50 }
59 if nx_runtime_monitor_last_issue(m) != NX_RT_ISSUE_STUCK_LAYER { return 51 }
60 if m.n_issues_total != 1 { return 52 }
61
62 // ===== (e) FLOW_ANOMALY: extruder advances but XY barely, Z same =====
63 // Fresh monitor for clean test.
64 let m2: *NxRuntimeMonitor = nx_runtime_monitor_new(5000, 3, 5 * Q14, 2 * Q14)
65 nx_runtime_monitor_step(m2, 200 * Q14 / 1000, 0, 0, 0)
66 // Same Z; e advanced 10mm (> 5mm threshold); xy advanced 1mm (< 2mm threshold)
67 let v_flow: i64 = nx_runtime_monitor_step(m2, 200 * Q14 / 1000,
68 10 * Q14, // big e
69 1 * Q14, // small XY
70 100)
71 if v_flow != NX_RT_ISSUE_FLOW_ANOMALY { return 60 }
72
73 // ===== (f) EXTRUDER_RUNAWAY: e advances >10× XY =====
74 let m3: *NxRuntimeMonitor = nx_runtime_monitor_new(5000, 3, 5 * Q14, 2 * Q14)
75 nx_runtime_monitor_step(m3, 200 * Q14 / 1000, 0, 0, 0)
76 // Z advances (so STUCK_LAYER doesn't fire; FLOW_ANOMALY needs dz=0).
77 // XY accumulates only 5mm across this step, but e advanced 100mm.
78 // Ratio = 100/5 = 20× > 10× threshold.
79 // BUT the per-layer reset on Z change wipes cumulative_xy_q14 to 0
80 // before runaway check. So the test must advance Z FIRST (resets
81 // cumulative xy), THEN have e shoot up at the same Z.
82 nx_runtime_monitor_step(m3, 400 * Q14 / 1000, 10 * Q14, 50 * Q14, 5000) // normal layer
83 // Now at z=0.4mm. Stay on this layer, accumulate XY=5mm + huge e=100mm.
84 let v_runaway: i64 = nx_runtime_monitor_step(m3, 400 * Q14 / 1000,
85 110 * Q14, // e jumps by 100mm
86 5 * Q14, // XY only 5mm
87 6000)
88 // FLOW_ANOMALY would fire first (de=100 > 5, xy=5 > 2 threshold so NOT
89 // FLOW; check runaway logic). de=100, xy_total=5 (since last Z change),
90 // 10*xy=50; de(100) > 50 + de > Q14 -> RUNAWAY.
91 if v_runaway != NX_RT_ISSUE_EXTRUDER_RUNAWAY { return 70 }
92
93 // ===== (g) n_issues_total increments per issue =====
94 if m3.n_issues_total != 1 { return 80 }
95
96 return 0
97}