nx_print_runtime_zback_test.nx source
↩ module page · 97 lines · 4734 B
1// nx_print_runtime_zback_test.nx -- Z-backward jump detection.
2//
3// Spaghetti progenitor: head crashes a tower mid-print, Klipper
4// recovers by rehoming, Z snaps from current layer back to ~0.
5// When the print resumes it does so off-position = spaghetti.
6//
7// Closed-form invariants:
8// (a) Normal layer progression: no fire
9// (b) Tiny Z-hop during travel (< tolerance): no fire
10// (c) Z reverse > tolerance: NX_RT_ISSUE_Z_BACKWARD fires
11// (d) Z_BACKWARD takes precedence over STUCK_LAYER
12// (e) After backward event, monitor recovers (next normal event no fire)
13// (f) Setter rejects negative tolerance
14// (g) Tighter tolerance catches smaller backward jumps
15//
16// expect_exit: 0
17// license_tier: ORIGINAL
18
19import "nx_syscalls.nx"
20import "nx_print_runtime_monitor.nx"
21
22const Q14: i64 = 16384
23
24func main() -> i64 {
25 // ===== (a) Normal layer progression: no fire =====
26 let m: *NxRuntimeMonitor = nx_runtime_monitor_new(5000, 3, 5 * Q14, 2 * Q14)
27 if (m as i64) == 0 { return 5 }
28 nx_runtime_monitor_step(m, 200 * Q14 / 1000, 0, 0, 0)
29 let v1: i64 = nx_runtime_monitor_step(m,
30 400 * Q14 / 1000, 10 * Q14, 50 * Q14, 5000)
31 if v1 != NX_RT_ISSUE_NONE { return 10 }
32
33 // ===== (b) Tiny Z-hop during travel (0.4mm reverse, default 5mm tol): no fire
34 // Default tolerance is 5mm; 0.4mm back-hop should be silent.
35 let v2: i64 = nx_runtime_monitor_step(m,
36 (400 - 400) * Q14 / 1000, // z = 0.0mm (down 0.4mm from 0.4mm)
37 20 * Q14, 50 * Q14, 6000)
38 if v2 != NX_RT_ISSUE_NONE { return 20 }
39
40 // ===== (c) Z reverse > tolerance fires Z_BACKWARD =====
41 // Build up to z = 12mm via normal layers, then crash-rehome to z=0.
42 let m2: *NxRuntimeMonitor = nx_runtime_monitor_new(5000, 3, 5 * Q14, 2 * Q14)
43 nx_runtime_monitor_step(m2, 0, 0, 0, 0)
44 nx_runtime_monitor_step(m2, 4 * Q14, 10 * Q14, 50 * Q14, 5000)
45 nx_runtime_monitor_step(m2, 8 * Q14, 20 * Q14, 50 * Q14, 10000)
46 nx_runtime_monitor_step(m2, 12 * Q14, 30 * Q14, 50 * Q14, 15000)
47 if m2.n_issues_total != 0 { return 30 }
48 // Now crash-rehome: z snaps from 12mm to 0mm (12mm back > 5mm tol)
49 let v_back: i64 = nx_runtime_monitor_step(m2,
50 0, // z = 0
51 30 * Q14, 0, 16000) // no extrusion, no XY (homing)
52 if v_back != NX_RT_ISSUE_Z_BACKWARD { return 31 }
53 if m2.n_issues_total != 1 { return 32 }
54
55 // ===== (d) Z_BACKWARD takes precedence over STUCK_LAYER =====
56 // Build a state where both would fire; precedence rule says Z_BACKWARD wins.
57 let m3: *NxRuntimeMonitor = nx_runtime_monitor_new(5000, 3, 5 * Q14, 2 * Q14)
58 nx_runtime_monitor_step(m3, 0, 0, 0, 0)
59 nx_runtime_monitor_step(m3, 5 * Q14, 10 * Q14, 50 * Q14, 1000)
60 // Long time gap (30s past last layer change) + Z reverse:
61 // STUCK_LAYER threshold = 5000 × 3 = 15000ms; we're at 31000ms.
62 // Z reverse from 5mm to 0 = 5mm exactly (NOT > 5mm tol). Use 10mm reverse.
63 nx_runtime_monitor_step(m3, 10 * Q14, 20 * Q14, 50 * Q14, 5000)
64 // Now at z=10mm. Long gap + reverse to 0:
65 let v_pre: i64 = nx_runtime_monitor_step(m3,
66 0, // z = 0 (10mm reverse, > 5mm tol)
67 20 * Q14, 0, 40000) // 35s past last layer change (STUCK threshold)
68 if v_pre != NX_RT_ISSUE_Z_BACKWARD { return 40 }
69
70 // ===== (e) After backward event, monitor continues =====
71 // Resumed print: Z advances normally from 0. No fire on next event.
72 let v_resume: i64 = nx_runtime_monitor_step(m2,
73 200 * Q14 / 1000, 31 * Q14, 5 * Q14, 17000)
74 if v_resume != NX_RT_ISSUE_NONE { return 50 }
75
76 // ===== (f) Setter rejects negative tolerance =====
77 let bad: i64 = nx_runtime_monitor_set_z_backward_tolerance(m, -1)
78 if bad != NX_RT_BAD_INPUT { return 60 }
79 let null_bad: i64 = nx_runtime_monitor_set_z_backward_tolerance(
80 0 as *NxRuntimeMonitor, 1000)
81 if null_bad != NX_RT_BAD_INPUT { return 61 }
82
83 // ===== (g) Tighter tolerance catches smaller backward jumps =====
84 let m4: *NxRuntimeMonitor = nx_runtime_monitor_new(5000, 3, 5 * Q14, 2 * Q14)
85 // Tighten tolerance to 0.5mm
86 let setok: i64 = nx_runtime_monitor_set_z_backward_tolerance(m4, Q14 / 2)
87 if setok != NX_RT_OK { return 70 }
88 nx_runtime_monitor_step(m4, 0, 0, 0, 0)
89 nx_runtime_monitor_step(m4, 2 * Q14, 10 * Q14, 50 * Q14, 5000)
90 // Reverse 1mm > 0.5mm tol -> fires
91 let v_tight: i64 = nx_runtime_monitor_step(m4,
92 Q14, // z = 1mm (1mm back from 2mm)
93 15 * Q14, 5 * Q14, 6000)
94 if v_tight != NX_RT_ISSUE_Z_BACKWARD { return 71 }
95
96 return 0
97}