code wiki / (root) / nx_print_telemetry.nx

nx_print_telemetry.nx source

↩ module page · 149 lines · 5135 B

1// nx_print_telemetry.nx -- unified telemetry-event bridge for 2// the print runtime monitor. 3// 4// Lets operator submit ONE event struct per telemetry tick 5// (from Moonraker JSON, serial-port read, fake source for 6// testing, etc.) and have it fanned into all 5 step* surfaces 7// on the runtime monitor. 8// 9// has_* validity masks let the operator populate only the 10// fields they actually have (e.g. Qidi without chamber heater 11// leaves has_thermal = 1 but chamber_setpoint_c = -1; an MCU 12// without mcu_position scaling leaves has_shift = 0). 13// 14// Pump returns the FIRST non-NONE verdict in priority order: 15// 1. LAYER_SHIFT (most critical mid-print) 16// 2. Z_BACKWARD (via step(), takes precedence over STUCK) 17// 3. STUCK_LAYER (via step()) 18// 4. FLOW_ANOMALY (via step()) 19// 5. EXTRUDER_RUNAWAY (via step()) 20// 6. BED_TEMP_DROP (via step_thermal()) 21// 7. HOTEND_TEMP_DROP (via step_thermal()) 22// 8. CHAMBER_TEMP_DROP (via step_thermal()) 23// 9. FILAMENT_RUNOUT (via step_runout()) 24// 25// Note: step* surfaces independently increment n_issues_total, 26// so if multiple kinds fire on one tick, the count tracks every 27// fire even though the returned verdict is just the first one. 28// 29// license_tier: ORIGINAL 30 31import "nx_syscalls.nx" 32import "nx_print_runtime_monitor.nx" 33 34struct NxPrintTelemetry { 35 event_time_ms: i64, 36 37 // Motion (consumed by step()) 38 z_q14: i64, 39 e_q14: i64, 40 xy_delta_q14: i64, 41 42 // Thermal (consumed by step_thermal()) 43 bed_actual_c: i64, 44 bed_setpoint_c: i64, 45 hotend_actual_c: i64, 46 hotend_setpoint_c: i64, 47 chamber_actual_c: i64, 48 chamber_setpoint_c: i64, 49 50 // Material (consumed by step_runout()) 51 runout_triggered: i64, 52 53 // Kinematics (consumed by step_layer_shift()) 54 commanded_x_q14: i64, 55 actual_x_q14: i64, 56 commanded_y_q14: i64, 57 actual_y_q14: i64, 58 59 // Validity masks: 1 = corresponding step* should be called 60 has_motion: i64, 61 has_thermal: i64, 62 has_runout: i64, 63 has_shift: i64, 64} 65 66const NX_PRINT_TELEMETRY_BYTES: i64 = 152 // 19 fields × 8 67 68// Allocate + zero-init. All has_* default to 0 (no detection on 69// pump) so partial population is safe. 70func nx_print_telemetry_new() -> *NxPrintTelemetry { 71 let ev: *NxPrintTelemetry = (sys_mmap(NX_PRINT_TELEMETRY_BYTES)) as *NxPrintTelemetry 72 if (ev as i64) == 0 { return 0 as *NxPrintTelemetry } 73 ev.event_time_ms = 0 74 ev.z_q14 = 0 75 ev.e_q14 = 0 76 ev.xy_delta_q14 = 0 77 ev.bed_actual_c = 0 78 ev.bed_setpoint_c = -1 79 ev.hotend_actual_c = 0 80 ev.hotend_setpoint_c = -1 81 ev.chamber_actual_c = 0 82 ev.chamber_setpoint_c = -1 83 ev.runout_triggered = 0 84 ev.commanded_x_q14 = 0 85 ev.actual_x_q14 = 0 86 ev.commanded_y_q14 = 0 87 ev.actual_y_q14 = 0 88 ev.has_motion = 0 89 ev.has_thermal = 0 90 ev.has_runout = 0 91 ev.has_shift = 0 92 return ev 93} 94 95// Fan one telemetry event into all enabled step* surfaces. 96// Returns first non-NONE verdict in priority order (see top 97// comment). m.n_issues_total still tracks every fire. 98func nx_runtime_monitor_pump_telemetry( 99 m: *NxRuntimeMonitor, 100 ev: *NxPrintTelemetry 101) -> i64 { 102 if (m as i64) == 0 { return NX_RT_ISSUE_NONE } 103 if (ev as i64) == 0 { return NX_RT_ISSUE_NONE } 104 105 var first: i64 = NX_RT_ISSUE_NONE 106 107 // 1. Layer shift (highest priority -- catches lost-step cascade 108 // before any other detector can downstream-symptom on it). 109 if ev.has_shift != 0 { 110 let v_s: i64 = nx_runtime_monitor_step_layer_shift(m, 111 ev.commanded_x_q14, ev.actual_x_q14, 112 ev.commanded_y_q14, ev.actual_y_q14) 113 if v_s != NX_RT_ISSUE_NONE { 114 if first == NX_RT_ISSUE_NONE { first = v_s } 115 } 116 } 117 118 // 2. Motion (covers Z_BACKWARD, STUCK_LAYER, FLOW_ANOMALY, 119 // EXTRUDER_RUNAWAY with Z_BACKWARD precedence already in step()). 120 if ev.has_motion != 0 { 121 let v_m: i64 = nx_runtime_monitor_step(m, 122 ev.z_q14, ev.e_q14, ev.xy_delta_q14, ev.event_time_ms) 123 if v_m != NX_RT_ISSUE_NONE { 124 if first == NX_RT_ISSUE_NONE { first = v_m } 125 } 126 } 127 128 // 3. Thermal (BED -> HOTEND -> CHAMBER priority inside step_thermal). 129 if ev.has_thermal != 0 { 130 let v_t: i64 = nx_runtime_monitor_step_thermal(m, 131 ev.bed_actual_c, ev.bed_setpoint_c, 132 ev.hotend_actual_c, ev.hotend_setpoint_c, 133 ev.chamber_actual_c, ev.chamber_setpoint_c, 134 ev.event_time_ms) 135 if v_t != NX_RT_ISSUE_NONE { 136 if first == NX_RT_ISSUE_NONE { first = v_t } 137 } 138 } 139 140 // 4. Material (runout edge-detector). 141 if ev.has_runout != 0 { 142 let v_r: i64 = nx_runtime_monitor_step_runout(m, ev.runout_triggered) 143 if v_r != NX_RT_ISSUE_NONE { 144 if first == NX_RT_ISSUE_NONE { first = v_r } 145 } 146 } 147 148 return first 149}