code wiki / _hdl_build / nx_printer_ctl.nx

nx_printer_ctl.nx source

↩ module page · 252 lines · 10151 B

1// nx_printer_ctl.nx -- sovereign 3D-printer / home-IoT control ORGAN (CLI + MCP tool). 2// 3// The operator's ask: integrate the 3D printers + home electronics into the 4// existing website behind the opaque secure login. The pasted reference design 5// used Node/Express + nginx + http-proxy-middleware + WAN mTLS. This organ is 6// the SOVEREIGN realization on the nishi fabric (no third-party stack, per the 7// ecosystem-only + api-first doctrine): the sovereign edge already does opaque 8// cap-token auth + reverse-proxy; this organ is the cap-gated LAN control leg. 9// 10// Verbs (JSON on stdout = the MCP/API/agent interchange): 11// nx_printer_ctl info <ip> <port> [apikey] -- GET /printer/info 12// nx_printer_ctl estop <ip> <port> [apikey] -- POST /printer/emergency_stop 13// nx_printer_ctl pause <ip> <port> [apikey] -- POST /printer/print/pause 14// nx_printer_ctl resume <ip> <port> [apikey] -- POST /printer/print/resume 15// nx_printer_ctl cancel <ip> <port> [apikey] -- POST /printer/print/cancel 16// nx_printer_ctl contract -- emit the security/routing contract (no network) 17// 18// Composes nx_printer_ctl_lib (parse/emit/allowlist) over the shipped Moonraker 19// primitives. NEVER-BRICK by construction: fixed safe-endpoint allowlist, no 20// raw-gcode passthrough (see the lib header + nx_printer_ctl_gate). 21// 22// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 23import "nx_syscalls.nx" 24import "nx_printer_ctl_lib.nx" 25 26const PC_STDERR: i64 = 2 27const PC_EXIT_USAGE: i64 = 2 28 29func pc_werr(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(PC_STDERR, s, n); return 0 } 30 31func main(argc: i64, argv: *i64) -> i64 { 32 if argc < 2 { 33 pc_werr("usage: nx_printer_ctl info|status|ready|files|progress|estop|pause|resume|cancel <ip> <port> [apikey] | printstart <ip> <port> <file> | upload <ip> <port> <local> <remote> | contract | discover <a.b.c> [port] | survey <a.b.c> | monitor <a.b.c>\n" as *u8) 34 sys_exit(PC_EXIT_USAGE) 35 return PC_EXIT_USAGE 36 } 37 let verb: *u8 = argv[1] as *u8 38 let id: i64 = pctl_verb_id(verb) 39 if id == PCTL_V_BAD { 40 pc_werr("usage: nx_printer_ctl info|status|ready|files|progress|estop|pause|resume|cancel <ip> <port> [apikey] | printstart <ip> <port> <file> | upload <ip> <port> <local> <remote> | contract | discover <a.b.c> [port] | survey <a.b.c> | monitor <a.b.c>\n" as *u8) 41 sys_exit(PC_EXIT_USAGE) 42 return PC_EXIT_USAGE 43 } 44 45 let out: *u8 = sys_mmap(PCTL_OUT) 46 47 // contract verb needs no network / no args 48 if id == PCTL_V_CONTRACT { 49 let co: i64 = pctl_emit_contract(out) 50 sys_write(1, out, co) 51 sys_exit(0) 52 return 0 53 } 54 55 // discover verb: <subnet-prefix a.b.c> [port] (LAN sweep, no single host) 56 if id == PCTL_V_DISCOVER { 57 if argc < 3 { 58 pc_werr("usage: nx_printer_ctl discover <subnet-prefix a.b.c> [port]\n" as *u8) 59 sys_exit(PC_EXIT_USAGE) 60 return PC_EXIT_USAGE 61 } 62 let prefix: *u8 = argv[2] as *u8 63 let base24: i64 = pctl_parse_prefix(prefix) 64 if base24 < 0 { 65 pc_werr("bad subnet prefix (expect a.b.c, e.g. 192.168.8)\n" as *u8) 66 sys_exit(PC_EXIT_USAGE) 67 return PC_EXIT_USAGE 68 } 69 var dport: i64 = 7125 70 if argc > 3 { 71 let pp: i64 = pctl_parse_port(argv[3] as *u8) 72 if pp > 0 { dport = pp } 73 } 74 let ips: *i64 = sys_mmap(254 * 8) as *i64 75 // 250ms single-window budget: responsive devices (a live printer <50ms) are found; 76 // unreachable hosts are not waited on -> the full /24 sweep fits the MCP call window. 77 let cnt: i64 = pctl_discover_scan(base24, dport, 250, ips, 254) 78 let do2: i64 = pctl_emit_discover(out, prefix, dport, ips, cnt) 79 sys_write(1, out, do2) 80 sys_exit(0) 81 return 0 82 } 83 84 // survey verb: <subnet-prefix a.b.c> (multi-port home-device inventory) 85 if id == PCTL_V_SURVEY { 86 if argc < 3 { 87 pc_werr("usage: nx_printer_ctl survey <subnet-prefix a.b.c>\n" as *u8) 88 sys_exit(PC_EXIT_USAGE) 89 return PC_EXIT_USAGE 90 } 91 let sprefix: *u8 = argv[2] as *u8 92 let sbase24: i64 = pctl_parse_prefix(sprefix) 93 if sbase24 < 0 { 94 pc_werr("bad subnet prefix (expect a.b.c, e.g. 192.168.8)\n" as *u8) 95 sys_exit(PC_EXIT_USAGE) 96 return PC_EXIT_USAGE 97 } 98 let mask: *i64 = sys_mmap(256 * 8) as *i64 99 pctl_survey_scan(sbase24, LSCAN_BUDGET_MS, mask) 100 let so: i64 = pctl_emit_survey(out, sprefix, sbase24, mask) 101 sys_write(1, out, so) 102 sys_exit(0) 103 return 0 104 } 105 106 // monitor verb: <subnet-prefix a.b.c> (whole dashboard JSON in ONE sovereign call; 107 // replaces the beat's shell grep/date -- the beat just launches this + publishes) 108 if id == PCTL_V_MONITOR { 109 if argc < 3 { 110 pc_werr("usage: nx_printer_ctl monitor <subnet-prefix a.b.c>\n" as *u8) 111 sys_exit(PC_EXIT_USAGE) 112 return PC_EXIT_USAGE 113 } 114 let mprefix: *u8 = argv[2] as *u8 115 let mbase24: i64 = pctl_parse_prefix(mprefix) 116 if mbase24 < 0 { 117 pc_werr("bad subnet prefix (expect a.b.c, e.g. 192.168.8)\n" as *u8) 118 sys_exit(PC_EXIT_USAGE) 119 return PC_EXIT_USAGE 120 } 121 let mout: *u8 = sys_mmap(65536) 122 let mo: i64 = pctl_build_monitor(mout, mprefix, mbase24) 123 sys_write(1, mout, mo) 124 sys_exit(0) 125 return 0 126 } 127 128 // printstart verb: <ip> <port> <filename> (start a NAMED pre-sliced file; never raw gcode) 129 if id == PCTL_V_PRINTSTART { 130 if argc < 5 { 131 pc_werr("usage: nx_printer_ctl printstart <ip> <port> <filename>\n" as *u8) 132 sys_exit(PC_EXIT_USAGE) 133 return PC_EXIT_USAGE 134 } 135 let ps_ip: *u8 = argv[2] as *u8 136 let ps_ipv4: i64 = pctl_parse_ip(ps_ip) 137 if ps_ipv4 < 0 { 138 pc_werr("bad ip (expect dotted IPv4 a.b.c.d)\n" as *u8) 139 sys_exit(PC_EXIT_USAGE) 140 return PC_EXIT_USAGE 141 } 142 let ps_port: i64 = pctl_parse_port(argv[3] as *u8) 143 if ps_port < 0 { 144 pc_werr("bad port (expect 1..65535)\n" as *u8) 145 sys_exit(PC_EXIT_USAGE) 146 return PC_EXIT_USAGE 147 } 148 let ps_fn: *u8 = argv[4] as *u8 149 let ps_fnlen: i64 = pctl_slen(ps_fn) 150 let ps_host: *u8 = sys_mmap(64) 151 let ps_hl: i64 = pctl_build_host(ps_ip, ps_port, ps_host) 152 let ps_out: *u8 = sys_mmap(PCTL_OUT) 153 let ps_o: i64 = pctl_printstart(ps_out, ps_ipv4, ps_port, ps_host, ps_hl, ps_fn, ps_fnlen) 154 sys_write(1, ps_out, ps_o) 155 sys_exit(0) 156 return 0 157 } 158 159 // upload verb: <ip> <port> <local-gcode-path> <remote-filename> (multipart -> printer files) 160 if id == PCTL_V_UPLOAD { 161 if argc < 6 { 162 pc_werr("usage: nx_printer_ctl upload <ip> <port> <local-gcode-path> <remote-filename>\n" as *u8) 163 sys_exit(PC_EXIT_USAGE) 164 return PC_EXIT_USAGE 165 } 166 let up_ip: *u8 = argv[2] as *u8 167 let up_ipv4: i64 = pctl_parse_ip(up_ip) 168 if up_ipv4 < 0 { 169 pc_werr("bad ip (expect dotted IPv4)\n" as *u8) 170 sys_exit(PC_EXIT_USAGE) 171 return PC_EXIT_USAGE 172 } 173 let up_port: i64 = pctl_parse_port(argv[3] as *u8) 174 if up_port < 0 { 175 pc_werr("bad port\n" as *u8) 176 sys_exit(PC_EXIT_USAGE) 177 return PC_EXIT_USAGE 178 } 179 let up_local: *u8 = argv[4] as *u8 180 let up_remote: *u8 = argv[5] as *u8 181 let up_lenp: *i64 = sys_mmap(8) as *i64 182 let up_gcode: *u8 = sys_read_file(up_local, up_lenp) 183 let up_out: *u8 = sys_mmap(4096) 184 if (up_gcode as i64) == 0 { 185 let uo: i64 = pctl_cat(up_out, 0, "{\"verb\":\"upload\",\"result\":\"READ_FAIL\",\"note\":\"local gcode path unreadable\"}\n" as *u8) 186 sys_write(1, up_out, uo) 187 sys_exit(1) 188 return 1 189 } 190 let up_glen: i64 = up_lenp[0] 191 let up_host: *u8 = sys_mmap(64) 192 let up_hl: i64 = pctl_build_host(up_ip, up_port, up_host) 193 let up_o: i64 = pctl_upload_send(up_out, up_ipv4, up_port, up_host, up_remote, up_gcode, up_glen) 194 sys_write(1, up_out, up_o) 195 sys_exit(0) 196 return 0 197 } 198 199 // network verbs need <ip> <port> 200 if argc < 4 { 201 pc_werr("usage: nx_printer_ctl <verb> <ip> <port> [apikey]\n" as *u8) 202 sys_exit(PC_EXIT_USAGE) 203 return PC_EXIT_USAGE 204 } 205 let ip_s: *u8 = argv[2] as *u8 206 let port_s: *u8 = argv[3] as *u8 207 let ipv4: i64 = pctl_parse_ip(ip_s) 208 if ipv4 < 0 { 209 pc_werr("bad ip (expect dotted IPv4 a.b.c.d)\n" as *u8) 210 sys_exit(PC_EXIT_USAGE) 211 return PC_EXIT_USAGE 212 } 213 let port: i64 = pctl_parse_port(port_s) 214 if port < 0 { 215 pc_werr("bad port (expect 1..65535)\n" as *u8) 216 sys_exit(PC_EXIT_USAGE) 217 return PC_EXIT_USAGE 218 } 219 var api_key: *u8 = "\x00" as *u8 220 var api_key_len: i64 = 0 221 if argc > 4 { 222 api_key = argv[4] as *u8 223 api_key_len = pctl_slen(api_key) 224 } 225 226 let host: *u8 = sys_mmap(64) 227 let host_len: i64 = pctl_build_host(ip_s, port, host) 228 let scratch: *u8 = sys_mmap(PCTL_SCRATCH) 229 let resp: *u8 = sys_mmap(PCTL_RESP) 230 let rn: *i64 = sys_mmap(8) as *i64 231 rn[0] = 0 232 233 let status: i64 = pctl_call(id, ipv4, port, host, host_len, api_key, api_key_len, scratch, resp, rn) 234 let n: i64 = rn[0] 235 var eo: i64 = 0 236 if id == PCTL_V_STATUS { 237 eo = pctl_emit_status(out, host, status, resp, n) 238 } else { 239 if id == PCTL_V_READY { 240 eo = pctl_emit_ready(out, host, status, resp, n) 241 } else { 242 if id == PCTL_V_PROGRESS { 243 eo = pctl_emit_progress(out, host, status, resp, n) 244 } else { 245 eo = pctl_emit_result(out, id, host, status, resp, n) 246 } 247 } 248 } 249 sys_write(1, out, eo) 250 sys_exit(0) 251 return 0 252}