code wiki / (root) / nx_machine_arch_test.nx

nx_machine_arch_test.nx source

↩ module page · 128 lines · 6532 B

1// nx_machine_arch_test.nx -- verify the machine-graph + kinematics 2// + process-class abstractions are reusable across any additive 3// machinery, with FDM polymer (Qidi X-Max 3) as the reference impl. 4// 5// Operator directive (2026-05-20, verbatim): 6// "we are focused on qidi but i wanted the architecture mapped out 7// so it was reusable on any additive machinery" 8// 9// Closed-form invariants: 10// (a) Existing FDM factories (Qidi/Prusa/Voron/Bambu) ALL carry 11// process_class == NX_PROCESS_FDM_POLYMER (architecture 12// compliance with the multi-process taxonomy) 13// (b) Each FDM factory has a valid kinematics class 14// (c) Kinematics predicates classify all 8 classes consistently: 15// - finite_z: only CONVEYOR is false (belt = unbounded Z) 16// - xy_decoupled: Cartesian/Gantry/SCARA/Robot/Conveyor true; 17// CoreXY/HBot/Delta false 18// - 5_axis_or_more: only Robot_6DOF 19// - bed_translates: Conveyor + Cartesian (bed-slinger) 20// (d) nx_machine_set_process accepts valid processes, rejects invalid 21// (e) Hypothetical DIW concrete machine can be constructed from the 22// same NxMachineGraph primitive by composing a generic 23// allocation + setter chain -- proves the substrate is NOT 24// FDM-specific 25// 26// expect_exit: 0 27// license_tier: ORIGINAL 28 29import "nx_syscalls.nx" 30import "nx_print_process.nx" 31import "nx_machine_graph.nx" 32 33func main() -> i64 { 34 // --- (a) Every shipped FDM factory carries the right process class --- 35 let qidi: *NxMachineGraph = nx_machine_graph_qidi_xmax3() 36 let prusa: *NxMachineGraph = nx_machine_graph_prusa_mk4() 37 let voron: *NxMachineGraph = nx_machine_graph_voron_2_4() 38 let bambu: *NxMachineGraph = nx_machine_graph_bambu_x1c() 39 40 if qidi.process_class != NX_PROCESS_FDM_POLYMER { return 10 } 41 if prusa.process_class != NX_PROCESS_FDM_POLYMER { return 11 } 42 if voron.process_class != NX_PROCESS_FDM_POLYMER { return 12 } 43 if bambu.process_class != NX_PROCESS_FDM_POLYMER { return 13 } 44 45 // --- (b) Kinematics class is valid for every factory --- 46 if nx_kin_is_valid(qidi.kinematics) != 1 { return 20 } 47 if nx_kin_is_valid(prusa.kinematics) != 1 { return 21 } 48 if nx_kin_is_valid(voron.kinematics) != 1 { return 22 } 49 if nx_kin_is_valid(bambu.kinematics) != 1 { return 23 } 50 51 // --- (c) Kinematics-class predicates classify all 8 classes --- 52 // finite_z: only CONVEYOR is false 53 if nx_kinematics_finite_z(NX_KIN_CARTESIAN) != 1 { return 30 } 54 if nx_kinematics_finite_z(NX_KIN_COREXY) != 1 { return 31 } 55 if nx_kinematics_finite_z(NX_KIN_DELTA) != 1 { return 32 } 56 if nx_kinematics_finite_z(NX_KIN_GANTRY) != 1 { return 33 } 57 if nx_kinematics_finite_z(NX_KIN_CONVEYOR) != 0 { return 34 } 58 59 // xy_decoupled: Cartesian + Gantry + SCARA + Robot + Conveyor are true 60 if nx_kinematics_xy_decoupled(NX_KIN_CARTESIAN) != 1 { return 40 } 61 if nx_kinematics_xy_decoupled(NX_KIN_GANTRY) != 1 { return 41 } 62 if nx_kinematics_xy_decoupled(NX_KIN_SCARA) != 1 { return 42 } 63 if nx_kinematics_xy_decoupled(NX_KIN_ROBOT_6DOF) != 1 { return 43 } 64 // CoreXY + HBot + Delta couple motors 65 if nx_kinematics_xy_decoupled(NX_KIN_COREXY) != 0 { return 44 } 66 if nx_kinematics_xy_decoupled(NX_KIN_HBOT) != 0 { return 45 } 67 if nx_kinematics_xy_decoupled(NX_KIN_DELTA) != 0 { return 46 } 68 69 // 5_axis_or_more: only Robot_6DOF 70 if nx_kinematics_5_axis_or_more(NX_KIN_ROBOT_6DOF) != 1 { return 50 } 71 if nx_kinematics_5_axis_or_more(NX_KIN_CARTESIAN) != 0 { return 51 } 72 if nx_kinematics_5_axis_or_more(NX_KIN_COREXY) != 0 { return 52 } 73 if nx_kinematics_5_axis_or_more(NX_KIN_DELTA) != 0 { return 53 } 74 75 // bed_translates: Conveyor (belt) + Cartesian (i3-class moving bed) 76 if nx_kinematics_bed_translates(NX_KIN_CONVEYOR) != 1 { return 60 } 77 if nx_kinematics_bed_translates(NX_KIN_CARTESIAN) != 1 { return 61 } 78 if nx_kinematics_bed_translates(NX_KIN_COREXY) != 0 { return 62 } // bed stationary 79 if nx_kinematics_bed_translates(NX_KIN_GANTRY) != 0 { return 63 } // bed stationary 80 81 // --- (d) Setter accepts valid, rejects invalid --- 82 if nx_machine_set_process(qidi, NX_PROCESS_FDM_METAL) != 0 { return 70 } 83 if qidi.process_class != NX_PROCESS_FDM_METAL { return 71 } 84 // Reset to FDM_POLYMER (the canonical Qidi mode) 85 if nx_machine_set_process(qidi, NX_PROCESS_FDM_POLYMER) != 0 { return 72 } 86 // Reject bad process 87 if nx_machine_set_process(qidi, -1) == 0 { return 73 } 88 if nx_machine_set_process(qidi, 999) == 0 { return 74 } 89 90 // --- (e) Hypothetical DIW concrete machine (LARGE GANTRY, 91 // DIW_CEMENT process) constructable from the SAME 92 // NxMachineGraph primitive --- 93 let diw: *NxMachineGraph = nx_machine_graph_alloc() 94 diw.build_x_mm = 5000 // 5m construction-scale 95 diw.build_y_mm = 5000 96 diw.build_z_mm = 3000 97 diw.kinematics = NX_KIN_GANTRY 98 diw.max_velocity_xy_mms = 200 // slow for cement 99 diw.max_velocity_z_mms = 50 100 diw.max_velocity_e_mms = 100 // pump rate 101 diw.max_accel_mms2 = 1000 102 diw.max_jerk_mms = 10 103 diw.hotend_max_c = 0 // no heater for cement 104 diw.hotend_min_c = 0 105 diw.bed_max_c = 0 106 diw.chamber_max_c = 0 107 diw.nozzle_dia_um = 25000 // 25 mm bead for cement 108 diw.filament_dia_um = 0 109 diw.extruder_type = 0 110 diw.max_flow_mm3s = 5000 // 5 cm³/s pump 111 diw.has_auto_bed_mesh = 0 112 diw.has_z_probe = 0 113 diw.has_input_shaping = 0 114 if nx_machine_set_process(diw, NX_PROCESS_DIW_CEMENT) != 0 { return 80 } 115 116 // Verify the architecture treats DIW machine consistently: 117 if diw.process_class != NX_PROCESS_DIW_CEMENT { return 81 } 118 if nx_kin_is_valid(diw.kinematics) != 1 { return 82 } 119 if nx_kinematics_finite_z(diw.kinematics) != 1 { return 83 } 120 if nx_kinematics_xy_decoupled(diw.kinematics) != 1 { return 84 } 121 if nx_process_is_valid(diw.process_class) != 1 { return 85 } 122 if nx_process_uses_powder_bed(diw.process_class) != 0 { return 86 } 123 if nx_process_needs_post_cure(diw.process_class) != 1 { return 87 } 124 if nx_process_is_direct_write(diw.process_class) != 1 { return 88 } 125 if nx_process_robot_pickup_ready(diw.process_class) != 0 { return 89 } 126 127 return 0 128}