code wiki / (root) / nx_print_loop_e2e_test.nx

nx_print_loop_e2e_test.nx source

↩ module page · 164 lines · 6017 B

1// nx_print_loop_e2e_test.nx -- end-to-end substrate loop: 2// simulated telemetry -> pump -> verdict -> response wire bytes. 3// 4// Proves the full substrate-level closure: 5// 6// telemetry_event (simulated; in prod from Moonraker JSON) 7// | 8// v 9// nx_runtime_monitor_pump_telemetry() (input side, shipped) 10// | 11// v 12// issue verdict (NX_RT_ISSUE_*) 13// | 14// v 15// nx_print_build_response_for_issue() (output side, shipped) 16// | 17// v 18// HTTP POST bytes ready for nx_mr_post_ipv4 (network shipped) 19// 20// Three scenarios per simulated print: 21// 22// A. Quiet tick (no issue) -> dispatcher returns 0 bytes 23// B. PAUSE-class issue (BED_TEMP_DROP) -> wire bytes contain 24// "POST /printer/print/pause" 25// C. HARD_STOP issue (LAYER_SHIFT) -> wire bytes contain 26// "POST /printer/print/cancel" 27// 28// expect_exit: 0 29// license_tier: ORIGINAL 30 31import "nx_syscalls.nx" 32import "nx_print_runtime_monitor.nx" 33import "nx_print_telemetry.nx" 34import "nx_print_response.nx" 35 36const Q14: i64 = 16384 37 38func contains_substring(hay: *u8, hay_len: i64, needle: *u8) -> i64 { 39 var n_len: i64 = 0 40 while needle[n_len] != 0 { n_len = n_len + 1 } 41 if n_len > hay_len { return 0 } 42 var i: i64 = 0 43 while i <= hay_len - n_len { 44 var j: i64 = 0 45 var ok: i64 = 1 46 while j < n_len { 47 if hay[i + j] != needle[j] { ok = 0; j = n_len } 48 j = j + 1 49 } 50 if ok == 1 { return 1 } 51 i = i + 1 52 } 53 return 0 54} 55 56func main() -> i64 { 57 let m: *NxRuntimeMonitor = nx_runtime_monitor_new(5000, 3, 5 * Q14, 2 * Q14) 58 if (m as i64) == 0 { return 5 } 59 nx_runtime_monitor_set_thermal(m, 2, 2000) 60 nx_runtime_monitor_set_layer_shift_tolerance(m, (3 * Q14) / 10) 61 62 let host: *u8 = "192.168.1.123:7125" 63 let host_len: i64 = 18 64 let buf: *u8 = sys_mmap(2048) 65 66 // Prime monitor with first event so subsequent ticks have last_z to diff 67 let prime: *NxPrintTelemetry = nx_print_telemetry_new() 68 prime.event_time_ms = 0 69 prime.has_motion = 1 70 prime.bed_actual_c = 60 71 prime.bed_setpoint_c = 60 72 prime.hotend_actual_c = 210 73 prime.hotend_setpoint_c = 210 74 prime.chamber_setpoint_c = -1 75 prime.has_thermal = 1 76 nx_runtime_monitor_pump_telemetry(m, prime) 77 78 // ===== Scenario A: Quiet tick -- no issue, no response bytes ===== 79 let evA: *NxPrintTelemetry = nx_print_telemetry_new() 80 evA.event_time_ms = 5000 81 evA.z_q14 = Q14 / 5 82 evA.e_q14 = 10 * Q14 83 evA.xy_delta_q14 = 50 * Q14 84 evA.has_motion = 1 85 evA.bed_actual_c = 60 86 evA.bed_setpoint_c = 60 87 evA.hotend_actual_c = 210 88 evA.hotend_setpoint_c = 210 89 evA.chamber_setpoint_c = -1 90 evA.has_thermal = 1 91 let vA: i64 = nx_runtime_monitor_pump_telemetry(m, evA) 92 if vA != NX_RT_ISSUE_NONE { return 10 } 93 let nA: i64 = nx_print_build_response_for_issue( 94 vA, host, host_len, 0 as *u8, 0, buf, 2048) 95 if nA != 0 { return 11 } 96 97 // ===== Scenario B: BED_TEMP_DROP -> PAUSE ===== 98 let evB1: *NxPrintTelemetry = nx_print_telemetry_new() 99 evB1.event_time_ms = 7000 100 evB1.bed_actual_c = 55 101 evB1.bed_setpoint_c = 60 102 evB1.hotend_actual_c = 210 103 evB1.hotend_setpoint_c = 210 104 evB1.chamber_setpoint_c = -1 105 evB1.has_thermal = 1 106 nx_runtime_monitor_pump_telemetry(m, evB1) // start timer 107 let evB2: *NxPrintTelemetry = nx_print_telemetry_new() 108 evB2.event_time_ms = 9500 109 evB2.bed_actual_c = 55 110 evB2.bed_setpoint_c = 60 111 evB2.hotend_actual_c = 210 112 evB2.hotend_setpoint_c = 210 113 evB2.chamber_setpoint_c = -1 114 evB2.has_thermal = 1 115 let vB: i64 = nx_runtime_monitor_pump_telemetry(m, evB2) 116 if vB != NX_RT_ISSUE_BED_TEMP_DROP { return 20 } 117 let nB: i64 = nx_print_build_response_for_issue( 118 vB, host, host_len, 0 as *u8, 0, buf, 2048) 119 if nB <= 0 { return 21 } 120 if contains_substring(buf, nB, "POST /printer/print/pause") != 1 { return 22 } 121 122 // ===== Scenario C: LAYER_SHIFT -> HARD_STOP (cancel) ===== 123 let evC: *NxPrintTelemetry = nx_print_telemetry_new() 124 evC.event_time_ms = 10000 125 evC.commanded_x_q14 = 100 * Q14 126 evC.actual_x_q14 = 100 * Q14 - Q14 // 1mm drift > 0.3mm tol 127 evC.commanded_y_q14 = 100 * Q14 128 evC.actual_y_q14 = 100 * Q14 129 evC.has_shift = 1 130 let vC: i64 = nx_runtime_monitor_pump_telemetry(m, evC) 131 if vC != NX_RT_ISSUE_LAYER_SHIFT { return 30 } 132 let nC: i64 = nx_print_build_response_for_issue( 133 vC, host, host_len, 0 as *u8, 0, buf, 2048) 134 if nC <= 0 { return 31 } 135 if contains_substring(buf, nC, "POST /printer/print/cancel") != 1 { return 32 } 136 137 // ===== Scenario D: CHAMBER_TEMP_DROP -> NOTIFY (no bytes) ===== 138 let evD1: *NxPrintTelemetry = nx_print_telemetry_new() 139 evD1.event_time_ms = 11000 140 evD1.bed_actual_c = 60 141 evD1.bed_setpoint_c = 60 142 evD1.hotend_actual_c = 210 143 evD1.hotend_setpoint_c = 210 144 evD1.chamber_actual_c = 25 145 evD1.chamber_setpoint_c = 30 146 evD1.has_thermal = 1 147 nx_runtime_monitor_pump_telemetry(m, evD1) // start chamber timer 148 let evD2: *NxPrintTelemetry = nx_print_telemetry_new() 149 evD2.event_time_ms = 13500 150 evD2.bed_actual_c = 60 151 evD2.bed_setpoint_c = 60 152 evD2.hotend_actual_c = 210 153 evD2.hotend_setpoint_c = 210 154 evD2.chamber_actual_c = 25 155 evD2.chamber_setpoint_c = 30 156 evD2.has_thermal = 1 157 let vD: i64 = nx_runtime_monitor_pump_telemetry(m, evD2) 158 if vD != NX_RT_ISSUE_CHAMBER_TEMP_DROP { return 40 } 159 let nD: i64 = nx_print_build_response_for_issue( 160 vD, host, host_len, 0 as *u8, 0, buf, 2048) 161 if nD != 0 { return 41 } // NOTIFY-only 162 163 return 0 164}