agentic-numerical-control 0.1.5 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,134 +1,134 @@
1
- // Preloaded into CNCjs (`node --require sim/virtual-port.cjs`) to add a
2
- // "GRBL-SIM" serial port backed by the grblHAL simulator (sim/build-sim.sh) over stdio. Real serial ports keep
3
- // working unchanged. No virtual COM driver is needed.
4
-
5
- const { spawn } = require("node:child_process");
6
- const fs = require("node:fs");
7
- const path = require("node:path");
8
- const Module = require("node:module");
9
- // Resolved from the serialport package so it is the same copy CNCjs uses.
10
- const { SerialPortStream } = require(require.resolve("@serialport/stream", { paths: [path.dirname(require.resolve("serialport"))] }));
11
-
12
- const SIM_PATH = "GRBL-SIM";
13
- const SIM_BIN = path.join(__dirname, ".build", "grbl_sim");
14
- // Set GRBL_SIM_LOG to a file path to record the serial traffic (for debugging).
15
- const SIM_LOG = process.env.GRBL_SIM_LOG;
16
- const trace = (dir, data) => {
17
- if (SIM_LOG) fs.appendFileSync(SIM_LOG, `${new Date().toISOString()} ${dir} ${JSON.stringify(data.toString())}
18
- `);
19
- };
20
-
21
- /** The simulator is a Linux/macOS program; on Windows it runs inside WSL. */
22
- function simCommand() {
23
- // -n: plain Grbl replies (no comment prefix); step/block traces discarded.
24
- const args = ["-n", "-s", "/dev/null", "-b", "/dev/null"];
25
- if (process.platform !== "win32") return [SIM_BIN, args];
26
- const wslPath = SIM_BIN.replace(/^([A-Za-z]):/, (_, d) => `/mnt/${d.toLowerCase()}`).replace(/\\/g, "/");
27
- return ["wsl.exe", ["--", wslPath, ...args]];
28
- }
29
-
30
- class CanceledError extends Error {
31
- constructor(message) {
32
- super(message);
33
- this.canceled = true;
34
- }
35
- }
36
-
37
- class SimPortBinding {
38
- constructor(openOptions) {
39
- this.openOptions = openOptions;
40
- this.isOpen = true;
41
- this.data = Buffer.alloc(0);
42
- this.pendingRead = null;
43
- const [cmd, args] = simCommand();
44
- // cwd: the simulator keeps its EEPROM.DAT (Grbl settings) there.
45
- this.child = spawn(cmd, args, { cwd: path.dirname(SIM_BIN), stdio: ["pipe", "pipe", "inherit"], windowsHide: true });
46
- this.child.stdout.on("data", (chunk) => {
47
- trace("<<", chunk);
48
- this.data = Buffer.concat([this.data, chunk]);
49
- const wake = this.pendingRead;
50
- this.pendingRead = null;
51
- if (wake) wake();
52
- });
53
- this.child.on("exit", (code, signal) => {
54
- trace("--", `simulator exited (${code ?? signal})`);
55
- this.isOpen = false;
56
- const wake = this.pendingRead;
57
- this.pendingRead = null;
58
- if (wake) wake(new CanceledError("simulator exited"));
59
- });
60
- }
61
-
62
- async close() {
63
- this.isOpen = false;
64
- this.child.kill();
65
- const wake = this.pendingRead;
66
- this.pendingRead = null;
67
- if (wake) wake(new CanceledError("port is closed"));
68
- }
69
-
70
- async read(buffer, offset, length) {
71
- if (!this.isOpen) throw new CanceledError("Read canceled");
72
- if (this.data.length === 0) {
73
- await new Promise((resolve, reject) => {
74
- this.pendingRead = (err) => (err ? reject(err) : resolve());
75
- });
76
- }
77
- const bytesRead = this.data.copy(buffer, offset, 0, Math.min(length, this.data.length));
78
- this.data = this.data.subarray(bytesRead);
79
- return { bytesRead, buffer };
80
- }
81
-
82
- async write(buffer) {
83
- if (!this.isOpen) throw new Error("Port is not open");
84
- trace(">>", buffer);
85
- await new Promise((resolve, reject) => this.child.stdin.write(buffer, (err) => (err ? reject(err) : resolve())));
86
- }
87
-
88
- async update(options) {
89
- this.openOptions.baudRate = options.baudRate;
90
- }
91
- async set() {}
92
- async get() {
93
- return { cts: true, dsr: true, dcd: true };
94
- }
95
- async getBaudRate() {
96
- return { baudRate: this.openOptions.baudRate };
97
- }
98
- async flush() {
99
- this.data = Buffer.alloc(0);
100
- }
101
- async drain() {}
102
- }
103
-
104
- function withSimPort(serialport) {
105
- const real = serialport.SerialPort.binding;
106
- const binding = {
107
- async list() {
108
- const ports = await real.list();
109
- if (fs.existsSync(SIM_BIN)) ports.push({ path: SIM_PATH, manufacturer: "grblHAL simulator (Agentic Numerical Control)" });
110
- return ports;
111
- },
112
- async open(options) {
113
- if (options.path === SIM_PATH) return new SimPortBinding({ ...options });
114
- return real.open(options);
115
- },
116
- };
117
- class SerialPort extends SerialPortStream {
118
- constructor(options, openCallback) {
119
- super({ binding, ...options }, openCallback);
120
- }
121
- }
122
- SerialPort.list = binding.list;
123
- SerialPort.binding = binding;
124
- return { ...serialport, SerialPort };
125
- }
126
-
127
- let patched = null;
128
- const load = Module._load;
129
- Module._load = function (request, parent, isMain) {
130
- const mod = load.call(this, request, parent, isMain);
131
- if (request !== "serialport") return mod;
132
- patched ??= withSimPort(mod);
133
- return patched;
134
- };
1
+ // Preloaded into CNCjs (`node --require sim/virtual-port.cjs`) to add a
2
+ // "GRBL-SIM" serial port backed by the grblHAL simulator (sim/build-sim.sh) over stdio. Real serial ports keep
3
+ // working unchanged. No virtual COM driver is needed.
4
+
5
+ const { spawn } = require("node:child_process");
6
+ const fs = require("node:fs");
7
+ const path = require("node:path");
8
+ const Module = require("node:module");
9
+ // Resolved from the serialport package so it is the same copy CNCjs uses.
10
+ const { SerialPortStream } = require(require.resolve("@serialport/stream", { paths: [path.dirname(require.resolve("serialport"))] }));
11
+
12
+ const SIM_PATH = "GRBL-SIM";
13
+ const SIM_BIN = path.join(__dirname, ".build", "grbl_sim");
14
+ // Set GRBL_SIM_LOG to a file path to record the serial traffic (for debugging).
15
+ const SIM_LOG = process.env.GRBL_SIM_LOG;
16
+ const trace = (dir, data) => {
17
+ if (SIM_LOG) fs.appendFileSync(SIM_LOG, `${new Date().toISOString()} ${dir} ${JSON.stringify(data.toString())}
18
+ `);
19
+ };
20
+
21
+ /** The simulator is a Linux/macOS program; on Windows it runs inside WSL. */
22
+ function simCommand() {
23
+ // -n: plain Grbl replies (no comment prefix); step/block traces discarded.
24
+ const args = ["-n", "-s", "/dev/null", "-b", "/dev/null"];
25
+ if (process.platform !== "win32") return [SIM_BIN, args];
26
+ const wslPath = SIM_BIN.replace(/^([A-Za-z]):/, (_, d) => `/mnt/${d.toLowerCase()}`).replace(/\\/g, "/");
27
+ return ["wsl.exe", ["--", wslPath, ...args]];
28
+ }
29
+
30
+ class CanceledError extends Error {
31
+ constructor(message) {
32
+ super(message);
33
+ this.canceled = true;
34
+ }
35
+ }
36
+
37
+ class SimPortBinding {
38
+ constructor(openOptions) {
39
+ this.openOptions = openOptions;
40
+ this.isOpen = true;
41
+ this.data = Buffer.alloc(0);
42
+ this.pendingRead = null;
43
+ const [cmd, args] = simCommand();
44
+ // cwd: the simulator keeps its EEPROM.DAT (Grbl settings) there.
45
+ this.child = spawn(cmd, args, { cwd: path.dirname(SIM_BIN), stdio: ["pipe", "pipe", "inherit"], windowsHide: true });
46
+ this.child.stdout.on("data", (chunk) => {
47
+ trace("<<", chunk);
48
+ this.data = Buffer.concat([this.data, chunk]);
49
+ const wake = this.pendingRead;
50
+ this.pendingRead = null;
51
+ if (wake) wake();
52
+ });
53
+ this.child.on("exit", (code, signal) => {
54
+ trace("--", `simulator exited (${code ?? signal})`);
55
+ this.isOpen = false;
56
+ const wake = this.pendingRead;
57
+ this.pendingRead = null;
58
+ if (wake) wake(new CanceledError("simulator exited"));
59
+ });
60
+ }
61
+
62
+ async close() {
63
+ this.isOpen = false;
64
+ this.child.kill();
65
+ const wake = this.pendingRead;
66
+ this.pendingRead = null;
67
+ if (wake) wake(new CanceledError("port is closed"));
68
+ }
69
+
70
+ async read(buffer, offset, length) {
71
+ if (!this.isOpen) throw new CanceledError("Read canceled");
72
+ if (this.data.length === 0) {
73
+ await new Promise((resolve, reject) => {
74
+ this.pendingRead = (err) => (err ? reject(err) : resolve());
75
+ });
76
+ }
77
+ const bytesRead = this.data.copy(buffer, offset, 0, Math.min(length, this.data.length));
78
+ this.data = this.data.subarray(bytesRead);
79
+ return { bytesRead, buffer };
80
+ }
81
+
82
+ async write(buffer) {
83
+ if (!this.isOpen) throw new Error("Port is not open");
84
+ trace(">>", buffer);
85
+ await new Promise((resolve, reject) => this.child.stdin.write(buffer, (err) => (err ? reject(err) : resolve())));
86
+ }
87
+
88
+ async update(options) {
89
+ this.openOptions.baudRate = options.baudRate;
90
+ }
91
+ async set() {}
92
+ async get() {
93
+ return { cts: true, dsr: true, dcd: true };
94
+ }
95
+ async getBaudRate() {
96
+ return { baudRate: this.openOptions.baudRate };
97
+ }
98
+ async flush() {
99
+ this.data = Buffer.alloc(0);
100
+ }
101
+ async drain() {}
102
+ }
103
+
104
+ function withSimPort(serialport) {
105
+ const real = serialport.SerialPort.binding;
106
+ const binding = {
107
+ async list() {
108
+ const ports = await real.list();
109
+ if (fs.existsSync(SIM_BIN)) ports.push({ path: SIM_PATH, manufacturer: "grblHAL simulator (Agentic Numerical Control)" });
110
+ return ports;
111
+ },
112
+ async open(options) {
113
+ if (options.path === SIM_PATH) return new SimPortBinding({ ...options });
114
+ return real.open(options);
115
+ },
116
+ };
117
+ class SerialPort extends SerialPortStream {
118
+ constructor(options, openCallback) {
119
+ super({ binding, ...options }, openCallback);
120
+ }
121
+ }
122
+ SerialPort.list = binding.list;
123
+ SerialPort.binding = binding;
124
+ return { ...serialport, SerialPort };
125
+ }
126
+
127
+ let patched = null;
128
+ const load = Module._load;
129
+ Module._load = function (request, parent, isMain) {
130
+ const mod = load.call(this, request, parent, isMain);
131
+ if (request !== "serialport") return mod;
132
+ patched ??= withSimPort(mod);
133
+ return patched;
134
+ };