按规划持续工作

This commit is contained in:
2026-06-09 06:13:28 +08:00
parent 902c6ecaea
commit 2658ee3b72
255 changed files with 12487 additions and 150 deletions

View File

@@ -12,6 +12,12 @@ The primary native validation command is:
wasm-port/tests/native/verify_native_probes.sh wasm-port/tests/native/verify_native_probes.sh
``` ```
The native LinuxCNC `nc_files` basic/example validation command is:
```bash
wasm-port/tests/native/verify_nc_files.sh
```
The current WASM smoke validation command is: The current WASM smoke validation command is:
```bash ```bash
@@ -24,6 +30,18 @@ The current WASM interpreter-core smoke validation command is:
wasm-port/tests/wasm/node/verify_interp_wasm.sh wasm-port/tests/wasm/node/verify_interp_wasm.sh
``` ```
The current WASM sim-config smoke validation command is:
```bash
wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh
```
The current WASM `nc_files` smoke validation command is:
```bash
wasm-port/tests/wasm/node/verify_nc_files_wasm.sh
```
The current WASM trajectory-planner smoke validation command is: The current WASM trajectory-planner smoke validation command is:
```bash ```bash
@@ -74,10 +92,58 @@ The native validation script runs these checks in order:
5. `tools/build_native_probes.sh` 5. `tools/build_native_probes.sh`
Builds native source probes and standalone harnesses from vendored Builds native source probes and standalone harnesses from vendored
LinuxCNC source plus narrow runtime wrappers. LinuxCNC source plus narrow runtime wrappers.
6. `tests/native/verify_native_probes.sh` 6. `tests/native/verify_sim_configs.sh`
Runs LinuxCNC `configs/sim` `.ngc` programs through the upstream
`../linuxcnc/bin/rs274` standalone entry point, using nearest or explicit
sim INI/tool-table mappings and classifying main programs, macro-load
checks, and remap subroutines separately.
7. `tests/native/verify_nc_files.sh`
Runs the basic/example subset of upstream `linuxcnc/nc_files` through
`../linuxcnc/bin/rs274`, with complete programs executed strictly and
macro/library files wrapped only for load/parse validation.
8. `tests/native/verify_native_probes.sh`
Checks probe exit codes, source-probe coverage, harness stdout, canonical Checks probe exit codes, source-probe coverage, harness stdout, canonical
fixture events, and expected error behavior. fixture events, and expected error behavior.
Current `nc_files` basic-suite baseline:
```text
total: 107
pass: 101
fail: 6
timeout: 0
expected_fail: 6
unexpected_fail: 0
```
The six expected failures are LinuxCNC-native entry-point/context edges:
`cone.ngc` needs a W-axis 5-axis machine context; `g76.ngc` and
`lathe-g76.ngc` need lathe cutter-compensation/tool context;
`lathe_g70_71_demo.ngc` needs lathe profile/tool context; `nestedcall.ngc`
uses an upstream O-word `callsub` syntax edge; and `tool-length-probe.ngc`
needs probe runtime context.
The same runner also supports exploratory full-directory inventory:
```bash
wasm-port/tests/native/verify_nc_files.sh --all
```
Current full-directory `nc_files` inventory:
```text
total: 247
pass: 219
fail: 28
timeout: 0
expected_fail: 28
unexpected_fail: 0
```
The additional expected failures are probe/plasmac runtime-context files and
one NURBS sample using `G2.2`, which the current upstream `bin/rs274`
standalone entry point reports as `Unknown g code used`.
The WASM INI smoke script builds `runtime/ui/ini-panel/linuxcnc_ini.js` and The WASM INI smoke script builds `runtime/ui/ini-panel/linuxcnc_ini.js` and
`linuxcnc_ini.wasm` from vendored LinuxCNC `inifile.cc`, then loads that `linuxcnc_ini.wasm` from vendored LinuxCNC `inifile.cc`, then loads that
module through `runtime/sdk/src/index.js` in Node and verifies INI string and module through `runtime/sdk/src/index.js` in Node and verifies INI string and
@@ -109,8 +175,10 @@ Emscripten filesystem through the SDK and runs them through LinuxCNC
`Interp::open()`, `Interp::read()`, and `Interp::execute()` to validate the `Interp::open()`, `Interp::read()`, and `Interp::execute()` to validate the
file execution path. It also writes the vendored LinuxCNC `xyzac-trt` and file execution path. It also writes the vendored LinuxCNC `xyzac-trt` and
`xyzbc-trt` table-rotary-tilting sample machine INI, `remap_subs/*.ngc`, and `xyzbc-trt` table-rotary-tilting sample machine INI, `remap_subs/*.ngc`, and
`xyzac_switchkins.ngc`/`xyzbc_switchkins.ngc`, `boat-xyzac.ngc`, `xyzac_switchkins.ngc`/`xyzbc_switchkins.ngc`,
`boat-xyzbc.ngc`, and `impeller-7bl-xyzac.ngc` demo files into the Emscripten `xyzac_switchkins_test_1.ngc`, `xyzac_switchkins_test_2.ngc`,
`xyzac_switchkins_test_3.ngc`, `boat-xyzac.ngc`, `boat-xyzbc.ngc`, and
`impeller-7bl-xyzac.ngc` demo files into the Emscripten
filesystem. It also writes the vendored LinuxCNC `xyzab-tdr` table-dual-rotary filesystem. It also writes the vendored LinuxCNC `xyzab-tdr` table-dual-rotary
INI, tool table, `remap_subs/*.ngc`, and `xyzab-tdr-demo.ngc` into the INI, tool table, `remap_subs/*.ngc`, and `xyzab-tdr-demo.ngc` into the
Emscripten filesystem. It also writes the vendored LinuxCNC bridge-mill Emscripten filesystem. It also writes the vendored LinuxCNC bridge-mill
@@ -119,7 +187,52 @@ Emscripten filesystem. These checks validate the WASM C ABI/SDK path for
vendored LinuxCNC `REMAP` parsing, vendored LinuxCNC tool-table loading from vendored LinuxCNC `REMAP` parsing, vendored LinuxCNC tool-table loading from
the machine INI, O-word remap execution, `M68`/`M66` HAL synchronization, and the machine INI, O-word remap execution, `M68`/`M66` HAL synchronization, and
file `open()`/`read()`/`execute()` completion without JavaScript M-code or file `open()`/`read()`/`execute()` completion without JavaScript M-code or
kinematics semantics. The same Node smoke writes kinematics semantics. The same Node smoke writes the vendored LinuxCNC
`tests/remap/duplicate-o-word`, NGC-only `tests/remap/fail/args.0`,
`tests/remap/fail/args.1`, `tests/remap/fail/args.2`,
`tests/remap/fail/body-ngc`, `tests/remap/m30-interaction`,
`tests/remap/nested-remaps-oword`, `tests/remap/posargs.0`, and
`tests/remap/sequencing` INI, program, and remap subroutines into the
Emscripten filesystem and validates those
upstream NGC remap regressions through the generic `runRemapFile()` C ABI/SDK
path, which only reads LinuxCNC INI `SUBROUTINE_PATH`, `REMAP`, and
`OWORD_NARGS` entries and calls vendored `Interp::parse_remap()`, `open()`,
`read()`, and `execute()`. The NGC-only failure cases that match upstream
`rs274 -n 0` flow use the sibling `runRemapFileContinueOnError()` C ABI/SDK
path, which records LinuxCNC error text and continues the same vendored
`open()`/`read()`/`execute()` loop without implementing failure semantics in
JavaScript. The same Node smoke also writes the NGC-only LinuxCNC
`tests/remap/remap-io/test-ngc.ini` and `io_*.ngc` subroutines into the
Emscripten filesystem, then calls `runRemapIoMdiSequence()` to feed the
upstream test-driver MDI sequence into vendored `Interp::execute()`. The SDK
does not implement M62-M68, M66 input, or REMAP semantics; the standalone
boundary only pre-seeds deterministic external input values and captures
LinuxCNC canonical events. The same WASM interpreter smoke also writes minimal `M110` and
`M111` fixtures into the Emscripten filesystem, marks the `M1xx` files
executable, and validates that the standalone machine-config boundary mirrors
LinuxCNC task-layer `[DISPLAY]PROGRAM_PREFIX` plus `[RS274NGC]USER_M_PATH`
lookup by registering `USER_DEFINED_FUNCTION` entries and recording
deterministic `USER_M_COMMAND` events instead of spawning host processes. The
same Node smoke writes the vendored LinuxCNC
`tests/interp/do-while-break`, `tests/interp/oword-bug315`,
`tests/interp/oword-bug315-p2`, `tests/interp/exists`,
`tests/interp/return-value`, `tests/interp/subs-follow-main`,
`tests/interp/fractional-linenumbers`, `tests/interp/namedparam-bug424`,
selected `tests/interp/rotation` pure interpreter cases, `tests/interp/iniparam`,
`tests/interp/iniparam-failassign`,
`tests/interp/sub-call-from-sub`, `tests/interp/sequence-number`, and
`tests/interp/nested-sub-error`, `tests/interp/nested-sub-in-file-error`, and
`tests/interp/abort-hot-comment`
files into the Emscripten filesystem and validates those upstream interpreter
regressions through `runFile()` or
`runFileWithIni()`, which only reads LinuxCNC INI `SUBROUTINE_PATH` where
needed and calls vendored LinuxCNC `Interp::open()`, `read()`, and `execute()`.
The `iniparam` fixtures validate vendored LinuxCNC `_ini[...]` lookup, missing
INI-parameter error text, and read-only named-parameter assignment rejection
through the `INI_FILE_NAME` runtime edge. It checks LinuxCNC canonical messages,
canonical motion/events, and final interpreter state without JavaScript O-word,
subroutine lookup, INI-variable, or read-only-parameter semantics. The
same Node smoke writes
LinuxCNC-format parameter files into the Emscripten filesystem and validates LinuxCNC-format parameter files into the Emscripten filesystem and validates
vendored `Interp::restore_parameters()` and `Interp::save_parameters()`, vendored `Interp::restore_parameters()` and `Interp::save_parameters()`,
including the saved parameter values, missing required numeric parameter including the saved parameter values, missing required numeric parameter
@@ -203,14 +316,59 @@ override INI-derived names before OPFS text is copied into the LinuxCNC-backed
WASM filesystem, and that absent INI file-name values use the default OPFS WASM filesystem, and that absent INI file-name values use the default OPFS
parameter/tool-table paths in a real browser session. It also writes vendored parameter/tool-table paths in a real browser session. It also writes vendored
LinuxCNC `xyzac-trt`/`xyzbc-trt` INI, remap subroutines, and switchkins demo LinuxCNC `xyzac-trt`/`xyzbc-trt` INI, remap subroutines, and switchkins demo
files, including the larger `boat-xyzac.ngc`, `boat-xyzbc.ngc`, and files, including `xyzac_switchkins_test_1.ngc`,
`impeller-7bl-xyzac.ngc` demos, into the browser WASM filesystem. It also `xyzac_switchkins_test_2.ngc`, `xyzac_switchkins_test_3.ngc`, and the larger
`boat-xyzac.ngc`, `boat-xyzbc.ngc`, and `impeller-7bl-xyzac.ngc` demos, into
the browser WASM filesystem. It also
writes vendored LinuxCNC `xyzab-tdr` machine files and `xyzab-tdr-demo.ngc` writes vendored LinuxCNC `xyzab-tdr` machine files and `xyzab-tdr-demo.ngc`
into the browser WASM filesystem. It also writes vendored LinuxCNC bridge-mill into the browser WASM filesystem. It also writes vendored LinuxCNC bridge-mill
machine files and `5axisgui.ngc` into the browser WASM filesystem. The browser machine files and `5axisgui.ngc` into the browser WASM filesystem. The browser
smoke verifies `runFiveAxisRemapFile()` for the table-rotary-tilting, smoke verifies `runFiveAxisRemapFile()` for the table-rotary-tilting,
table-dual-rotary, and bridge-mill sample machines through the exported table-dual-rotary, and bridge-mill sample machines through the exported
LinuxCNC remap/tool-table/file execution path. LinuxCNC remap/tool-table/file execution path. It also verifies the vendored
LinuxCNC `configs/sim/axis/foam/foam.ngc`, `axis/geometry/xyzc.ngc`, and
`axis/external_offsets/dyn_demo.ngc` programs through `runSimConfigProgram()`,
which writes the browser WASM filesystem files and forwards to
`runFileWithIni()`. It also verifies the bridge-mill
`axis/vismach/5axis/bridgemill/5axisgui.ngc` program through
`runSimConfigProgram()` with `executionMode: "fiveAxisRemap"`, which forwards
to `runFiveAxisRemapFile()`. These paths use the real vendored executable
`M110` and `M111` files for `USER_M_PATH` registration without spawning host
processes. It
also verifies the vendored
LinuxCNC `tests/remap/duplicate-o-word`, NGC-only `tests/remap/fail/args.0`,
`tests/remap/fail/args.1`, `tests/remap/fail/args.2`,
`tests/remap/fail/body-ngc`, `tests/remap/m30-interaction`,
`tests/remap/nested-remaps-oword`, `tests/remap/posargs.0`, and
`tests/remap/sequencing` regressions through `runRemapFile()` or
`runRemapFileContinueOnError()`, using the browser WASM filesystem and
vendored LinuxCNC REMAP/O-word/file execution path without JavaScript remap
semantics. It also writes the NGC-only vendored LinuxCNC
`tests/remap/remap-io/test-ngc.ini` and `io_*.ngc` subroutines into the
browser WASM filesystem and validates `runRemapIoMdiSequence()` through
vendored LinuxCNC REMAP parsing and MDI execution without JavaScript M-code or
I/O semantics. It also writes vendored LinuxCNC `tests/interp/do-while-break`,
`tests/interp/oword-bug315`, `tests/interp/oword-bug315-p2`,
`tests/interp/exists`, `tests/interp/return-value`,
`tests/interp/subs-follow-main`, `tests/interp/fractional-linenumbers`,
`tests/interp/namedparam-bug424`, and selected `tests/interp/rotation`
`.ngc` files plus
`tests/interp/sub-call-from-sub` `test.ini`, `test.ngc`, and `subs/*.ngc`
files and `tests/interp/sequence-number` `test.ini`, `test.ngc`, and
`rm400.ngc` plus `tests/interp/nested-sub-error` `test.ini`, `test.ngc`, and
`subs/nested.ngc` and `tests/interp/nested-sub-in-file-error` `test.ini`,
`test.ngc`, and `subs/sequential.ngc`, plus `tests/interp/abort-hot-comment`
`test.ini` and `test.ngc`, plus `tests/interp/m19` `test.ini` and `test.ngc`,
plus `tests/interp/magic_comments/param_format_printing` `test.ngc`,
plus selected pure-interpreter `tests/interp/m98m99` cases covering
Fanuc-style `M98/M99`, parameter scope, nested numbered subprograms,
subprograms after main program text, leading-zero O-word lookup, named and
numbered main programs, and expression-based O-sub/M98 calls,
into the browser WASM filesystem and
validates those upstream
interpreter regressions through `runFile()` or `runFileWithIni()`, without
JavaScript O-word, parameter, line-number, spindle-speed, spindle-orient,
magic-comment formatting, M98/M99, subroutine lookup, or branch semantics.
The aggregate host smoke script builds the INI, interpreter-core, and The aggregate host smoke script builds the INI, interpreter-core, and
trajectory-planner WASM artifacts once, then runs the Node WASM smokes, the trajectory-planner WASM artifacts once, then runs the Node WASM smokes, the
@@ -240,7 +398,8 @@ The validation fails if:
| `linuxcnc_interp_init_harness` | Validates vendored LinuxCNC `Interp::init()` emits canonical initialization boundaries, reads metric/inch machine units, and synchronizes current/selected tool slots through standalone status adapters. | | `linuxcnc_interp_init_harness` | Validates vendored LinuxCNC `Interp::init()` emits canonical initialization boundaries, reads metric/inch machine units, and synchronizes current/selected tool slots through standalone status adapters. |
| `linuxcnc_indexer_harness` | Validates vendored LinuxCNC single-axis rotary indexer dispatch emits lock/unlock and motion boundaries through the standalone event sink. | | `linuxcnc_indexer_harness` | Validates vendored LinuxCNC single-axis rotary indexer dispatch emits lock/unlock and motion boundaries through the standalone event sink. |
| `linuxcnc_remap_hal_sync_harness` | Validates vendored LinuxCNC `M68`/`M66` execution can drive the standalone HAL adapter boundary used by 5-axis switchkins remap files, including `_hal[motion.switchkins-type]` readback. | | `linuxcnc_remap_hal_sync_harness` | Validates vendored LinuxCNC `M68`/`M66` execution can drive the standalone HAL adapter boundary used by 5-axis switchkins remap files, including `_hal[motion.switchkins-type]` readback. |
| `linuxcnc_5axis_remap_execute_harness` | Validates vendored LinuxCNC `REMAP` parsing plus NGC remap execution for the 5-axis `M429 -> M428 -> M430 -> M429` switchkins path in the `xyzac-trt` and `xyzbc-trt` sample machines, the two-remap `M429 -> M428 -> M429` path in the `xyzab-tdr` sample machine, and the bridge-mill `M429 -> M428 -> M430 -> M429` path where M428 selects the default bridge-mill kinematics. It loads vendored machine tool tables through LinuxCNC `tooldata_load()`, then runs vendored `xyzac_switchkins.ngc`, `xyzbc_switchkins.ngc`, `xyzac_switchkins_test_1.ngc`, `xyzac_switchkins_test_3.ngc`, `boat-xyzac.ngc`, `boat-xyzbc.ngc`, `impeller-7bl-xyzac.ngc`, `xyzab-tdr-demo.ngc`, and `5axisgui.ngc` through the LinuxCNC file `open/read/execute` path. | | `linuxcnc_5axis_remap_execute_harness` | Validates vendored LinuxCNC `REMAP` parsing plus NGC remap execution for the 5-axis `M429 -> M428 -> M430 -> M429` switchkins path in the `xyzac-trt` and `xyzbc-trt` sample machines, the two-remap `M429 -> M428 -> M429` path in the `xyzab-tdr` sample machine, and the bridge-mill `M429 -> M428 -> M430 -> M429` path where M428 selects the default bridge-mill kinematics. It loads vendored machine tool tables through LinuxCNC `tooldata_load()`, then runs vendored `xyzac_switchkins.ngc`, `xyzbc_switchkins.ngc`, `xyzac_switchkins_test_1.ngc`, `xyzac_switchkins_test_2.ngc`, `xyzac_switchkins_test_3.ngc`, `boat-xyzac.ngc`, `boat-xyzbc.ngc`, `impeller-7bl-xyzac.ngc`, `xyzab-tdr-demo.ngc`, and `5axisgui.ngc` through the LinuxCNC file `open/read/execute` path. |
| `linuxcnc_duplicate_oword_remap_harness` | Validates vendored LinuxCNC upstream `tests/remap/duplicate-o-word`, NGC-only `tests/remap/fail/args.0`, `tests/remap/fail/args.1`, `tests/remap/fail/args.2`, `tests/remap/fail/body-ngc`, `tests/remap/m30-interaction`, `tests/remap/nested-remaps-oword`, `tests/remap/posargs.0`, `tests/remap/sequencing`, and the NGC-only `tests/remap/remap-io/test-ngc.ini` branch through LinuxCNC `REMAP` parsing, O-word remap dispatch, `OWORD_NARGS`, error-text reporting, G/M remap sequencing, remapped M62-M68/M66 MDI execution, and file `open/read/execute`. The standalone harness only supplies INI/file path setup, deterministic external input values, and canonical-event capture; it can continue after LinuxCNC errors for upstream `rs274 -n 0` style tests and does not implement duplicate-label, O-word, M30, positional-argument, failure, sequencing, M62-M68, M66 input, or remap semantics. |
| `linuxcnc_tp_api_probe` | Validates vendored LinuxCNC trajectory planner calls for linear, arc, and queued motion paths. | | `linuxcnc_tp_api_probe` | Validates vendored LinuxCNC trajectory planner calls for linear, arc, and queued motion paths. |
| `linuxcnc_kinematics_probe` | Validates vendored LinuxCNC `trivkins.c` plus `kins_util.c` initialize and perform identity forward/inverse mapping through the standalone HAL/RTAPI boundary. | | `linuxcnc_kinematics_probe` | Validates vendored LinuxCNC `trivkins.c` plus `kins_util.c` initialize and perform identity forward/inverse mapping through the standalone HAL/RTAPI boundary. |
| `linuxcnc_5axis_kinematics_probe` | Validates vendored LinuxCNC `5axiskins.c` through `switchkins.c`, including 5-axis forward/inverse round-trip behavior and switching to identity kinematics. | | `linuxcnc_5axis_kinematics_probe` | Validates vendored LinuxCNC `5axiskins.c` through `switchkins.c`, including 5-axis forward/inverse round-trip behavior and switching to identity kinematics. |
@@ -265,11 +424,13 @@ The validation fails if:
| Harness | Purpose | | Harness | Purpose |
| --- | --- | | --- | --- |
| `tests/wasm/node/verify_ini_wasm.sh` | Validates the browser-facing INI WASM module can be built from vendored LinuxCNC `inifile.cc`, loaded through the JS SDK in Node, and queried through the exported C ABI, including LinuxCNC-backed boolean conversion and machine-session file-name string lookup. | | `tests/wasm/node/verify_ini_wasm.sh` | Validates the browser-facing INI WASM module can be built from vendored LinuxCNC `inifile.cc`, loaded through the JS SDK in Node, and queried through the exported C ABI, including LinuxCNC-backed boolean conversion and machine-session file-name string lookup. |
| `tests/wasm/node/verify_interp_wasm.sh` | Validates the interpreter-core WASM module can be built from vendored LinuxCNC interpreter/remap source, loaded through the interpreter JS SDK, run the first fixture group through `Interp::execute()` and selected file fixtures through `Interp::open()`/`read()`/`execute()`, match the native canonical event plus required state readback fixtures, run vendored `xyzac-trt`/`xyzbc-trt` switchkins remap demo files through the WASM C ABI/SDK path, and run parameter-file restore/save through vendored LinuxCNC `Interp::restore_parameters()` and `Interp::save_parameters()`. | | `tests/wasm/node/verify_interp_wasm.sh` | Validates the interpreter-core WASM module can be built from vendored LinuxCNC interpreter/remap source, loaded through the interpreter JS SDK, run the first fixture group through `Interp::execute()` and selected file fixtures plus vendored upstream `tests/interp` regression files through `Interp::open()`/`read()`/`execute()`, match the native canonical event plus required state readback fixtures, run vendored `xyzac-trt`/`xyzbc-trt` switchkins remap demo files through the WASM C ABI/SDK path, and run parameter-file restore/save through vendored LinuxCNC `Interp::restore_parameters()` and `Interp::save_parameters()`. |
| `tests/wasm/node/verify_sim_configs_wasm.sh` | Validates representative vendored LinuxCNC `configs/sim` programs in Node WASM through `runSimConfigProgram()`, copying `axis/foam`, `axis/geometry`, and `axis/external_offsets` INI/G-code/user-M assets into the Emscripten filesystem before forwarding to `runFileWithIni()`, plus bridge-mill `5axis.ini`, tool table, `remap_subs/*.ngc`, and `5axisgui.ngc` before forwarding to `runFiveAxisRemapFile()`. This covers INI-driven `U/V/W` axis mask handling, real `USER_M_PATH` registration for executable `M110`/`M111`, and vendored LinuxCNC bridge-mill NGC remap execution without spawning host processes. |
| `tests/wasm/node/verify_nc_files_wasm.sh` | Validates representative vendored LinuxCNC `nc_files` examples in Node WASM by copying `arcspiral.ngc`, `hole-circle.ngc`, `factorial.ngc`, and `m6demo.ngc` into the Emscripten filesystem and forwarding to the LinuxCNC-backed `Interp::open()`/`read()`/`execute()` path. JavaScript only stages files and checks LinuxCNC output; it does not implement G-code, O-word, tool-change, or M-code behavior. |
| `tests/wasm/node/verify_tp_wasm.sh` | Validates a standalone trajectory-planner WASM module can be built from vendored LinuxCNC TP/TC/Ruckig support source, loaded in Node, and run the same linear, arc, and queued-line planner probe paths covered by the native TP harness. | | `tests/wasm/node/verify_tp_wasm.sh` | Validates a standalone trajectory-planner WASM module can be built from vendored LinuxCNC TP/TC/Ruckig support source, loaded in Node, and run the same linear, arc, and queued-line planner probe paths covered by the native TP harness. |
| `tests/opfs/node/verify_file_service.sh` | Validates the host-owned OPFS text-file adapter, path model, session snapshot store including custom filenames and envelope/path rejection paths, machine file store, G-code text store including filename rejection paths, OPFS-to-WASM parameter/tool-table bridges, and grouped machine-session loading without moving file persistence, parameter semantics, or tool-table semantics into the WASM core. | | `tests/opfs/node/verify_file_service.sh` | Validates the host-owned OPFS text-file adapter, path model, session snapshot store including custom filenames and envelope/path rejection paths, machine file store, G-code text store including filename rejection paths, OPFS-to-WASM parameter/tool-table bridges, and grouped machine-session loading without moving file persistence, parameter semantics, or tool-table semantics into the WASM core. |
| `tests/browser/verify_ini_panel_browser.sh` | Validates the INI SDK, INI/interpreter WASM module loading, LinuxCNC-backed INI machine-session file-name string lookup, OPFS text-file round trip, generic session snapshot round trip plus custom filename and envelope/path rejection paths, machine file text round trip, G-code text round trip plus filename rejection paths, and the INI panel UI's machine-session load with default OPFS parameter/tool-table file mapping, G-code run, canonical-event display paths, and 5-axis remap demo action in a real browser runtime. | | `tests/browser/verify_ini_panel_browser.sh` | Validates the INI SDK, INI/interpreter WASM module loading, LinuxCNC-backed INI machine-session file-name string lookup, OPFS text-file round trip, generic session snapshot round trip plus custom filename and envelope/path rejection paths, machine file text round trip, G-code text round trip plus filename rejection paths, and the INI panel UI's machine-session load with default OPFS parameter/tool-table file mapping, G-code run, canonical-event display paths, and 5-axis remap demo action in a real browser runtime. |
| `tests/browser/verify_interp_browser.sh` | Validates the interpreter-core WASM module loads through the interpreter JS SDK in a real browser runtime and runs selected positive and negative canonical fixtures through vendored LinuxCNC `Interp::execute()` plus `Interp::open()`/`read()`/`execute()` via the exported C ABI, including direct browser SDK and OPFS-backed parameter-file restore/save, non-random/random tool-table load/save through vendored LinuxCNC source, and vendored `xyzac-trt`/`xyzbc-trt` switchkins remap demo execution. | | `tests/browser/verify_interp_browser.sh` | Validates the interpreter-core WASM module loads through the interpreter JS SDK in a real browser runtime and runs selected positive and negative canonical fixtures plus vendored upstream `tests/interp` regression files through vendored LinuxCNC `Interp::execute()` and `Interp::open()`/`read()`/`execute()` via the exported C ABI, including direct browser SDK and OPFS-backed parameter-file restore/save, non-random/random tool-table load/save through vendored LinuxCNC source, vendored `xyzac-trt`/`xyzbc-trt` switchkins remap demo execution, representative vendored `configs/sim` `foam`, `geometry`, `external_offsets`, and bridge-mill programs through `runSimConfigProgram()`, and representative vendored `nc_files` examples through `runFile()`. |
| `tests/host/verify_host_smokes.sh` | Runs the current host-side Node, WASM interpreter-core, WASM trajectory-planner, OPFS, and browser smoke validation with shared WASM builds. | | `tests/host/verify_host_smokes.sh` | Runs the current host-side Node, WASM interpreter-core, WASM trajectory-planner, OPFS, and browser smoke validation with shared WASM builds. |
## Fixture Coverage ## Fixture Coverage
@@ -593,7 +754,15 @@ comparable canonical runtime edge and program-end cleanup calls. The
standalone vendored-source harness, Node WASM smoke, and browser smoke also standalone vendored-source harness, Node WASM smoke, and browser smoke also
pin `coordinate_offsets` MDI/file-path, `feed_control_modes`, the pin `coordinate_offsets` MDI/file-path, `feed_control_modes`, the
`position_params` MDI/file-path, `tool_semantics`, and `canon_runtime_edges` `position_params` MDI/file-path, `tool_semantics`, and `canon_runtime_edges`
post-program modal, override, spindle, mist, and flood `_setup` state. The same upstream baseline also post-program modal, override, spindle, mist, and flood `_setup` state. Native
and WASM standalone checks also cover the user M-code registration boundary for
INI-declared `M110`/`M111` handlers while keeping process execution outside the
browser/WASM runtime. Node and browser WASM sim-config coverage now run the real
vendored `axis/geometry/xyzc.ngc` and
`axis/external_offsets/dyn_demo.ngc` programs with their executable
`M110`/`M111` files, `axis/foam/foam.ngc` with its `U/V` machine
configuration, and bridge-mill `5axisgui.ngc` with its `W` machine
coordinates. The same upstream baseline also
validates the current negative fixture error text and absent canonical-event validates the current negative fixture error text and absent canonical-event
constraints. Fixtures that depend on standalone-only runtime constraints. Fixtures that depend on standalone-only runtime
adapters, HAL state, upstream `rs274` output gaps such as adapters, HAL state, upstream `rs274` output gaps such as
@@ -633,3 +802,38 @@ resulting `_setup.m_remapped` descriptors for each machine's LinuxCNC-defined
`M428`/`M429`/`M430` set. Python callbacks remain runtime boundaries. Native `M428`/`M429`/`M430` set. Python callbacks remain runtime boundaries. Native
and WASM validation now execute the NGC remap/file paths through vendored and WASM validation now execute the NGC remap/file paths through vendored
LinuxCNC O-word dispatch and the standalone HAL adapter boundary. LinuxCNC O-word dispatch and the standalone HAL adapter boundary.
`linuxcnc_duplicate_oword_remap_harness` vendors LinuxCNC's upstream
`tests/remap/duplicate-o-word` and `tests/remap/m30-interaction` files
unchanged and runs each `test.ngc` with its `test.ini` through vendored
`Interp::parse_remap()`, `open()`, `read()`, and `execute()`. Native, Node
WASM, and browser interpreter validation assert the same successful LinuxCNC
regression paths, including the remapped `M207`/`M208` messages and final
`FINISH()` event for duplicate O-word, and the `M400 M30` remap-level
interaction with `PROGRAM_END`.
`linuxcnc_interp_minimal_harness` now also runs selected vendored LinuxCNC
upstream `tests/interp/*/test.ngc` files through vendored `Interp::open()`,
`read()`, and `execute()`, using LinuxCNC INI `SUBROUTINE_PATH` for
`tests/interp/sub-call-from-sub`, `tests/interp/sequence-number`, and
`tests/interp/nested-sub-error`, `tests/interp/nested-sub-in-file-error`, and
`tests/interp/abort-hot-comment`, plus LinuxCNC `[RS274NGC]ORIENT_OFFSET` for
`tests/interp/m19`.
Native, Node WASM, and browser interpreter validation assert the LinuxCNC
canonical messages, canonical events, error texts, and interpreter state for
O-word loop/break/subroutine paths, dynamic O-word calls, `EXISTS[]`,
subroutine return values, subroutines after main programs, fractional line
numbers, named-parameter parsing, M19 spindle-orient offset and wait timeout
canonical events, magic-comment parameter formatting, valid external subroutine calls from another
external subroutine, external-subroutine `#<_line>` reporting,
nested-subroutine-definition rejection, and blocked forward seek to a later
numbered subroutine in the same external file, selected `tests/interp/rotation`
absolute-position `#<_abs_x>`, `#<_abs_y>`, and `#<_abs_z>` reporting under
G54, G92, XY rotation, and unit changes plus rotated-coordinate `G28` and
`G53` endpoint behavior, selected `tests/interp/m98m99`
Fanuc-style `M98/M99` subprogram calls, parameter-scope differences,
nested numbered subprograms, subprograms after main program text,
leading-zero O-word lookup, named/numbered main programs, and O-expression
calls, plus `(ABORT,...)` hot-comment numbered, named, and INI-parameter
expansion, with skipped branch/subroutine messages and post-abort program end
asserted absent where applicable.

View File

@@ -45,6 +45,7 @@ semantic rewrites:
| Go math C/C++ linkage | `genserkins` runtime probing compiles vendored `gomath.c` through a narrow C++ wrapper so LinuxCNC `genserfuncs.c` can link to the upstream Go math symbols without editing vendored source. | | Go math C/C++ linkage | `genserkins` runtime probing compiles vendored `gomath.c` through a narrow C++ wrapper so LinuxCNC `genserfuncs.c` can link to the upstream Go math symbols without editing vendored source. |
| Switchkins iterative forward | `genhexkins` runtime probing follows LinuxCNC switchkins iterative-forward behavior, including the first-call warmup path before asserting roundtrip convergence. | | Switchkins iterative forward | `genhexkins` runtime probing follows LinuxCNC switchkins iterative-forward behavior, including the first-call warmup path before asserting roundtrip convergence. |
| Browser storage | OPFS remains outside the native core; `runtime/opfs/file-service.js` owns browser text-file persistence, `runtime/opfs/path-model.js` owns host-side storage paths for INI, tool table, parameter, G-code, preview-cache, and session-snapshot content, `runtime/opfs/snapshot-store.js` owns generic JSON session snapshot persistence, `runtime/opfs/machine-file-store.js` owns pure-text machine-file and G-code persistence, `runtime/opfs/linuxcnc-parameter-bridge.js` only copies parameter files between OPFS text storage and the LinuxCNC-backed WASM parameter-file ABI, `runtime/opfs/linuxcnc-tool-table-bridge.js` only copies tool tables between OPFS text storage and the LinuxCNC-backed WASM tool-table ABI, and `runtime/opfs/linuxcnc-machine-session-bridge.js` groups INI, parameter, and tool-table loading while using the LinuxCNC-backed INI SDK for `[EMCIO]RANDOM_TOOLCHANGER`, `[RS274NGC]PARAMETER_FILE`, and `[EMCIO]TOOL_TABLE` when available; explicit host session file-name options take precedence over INI-derived file names, missing INI file-name values fall back to host default parameter/tool-table paths, and path validation remains owned by the OPFS path model, including rejection of traversal or nested path segments from INI-derived file names. | | Browser storage | OPFS remains outside the native core; `runtime/opfs/file-service.js` owns browser text-file persistence, `runtime/opfs/path-model.js` owns host-side storage paths for INI, tool table, parameter, G-code, preview-cache, and session-snapshot content, `runtime/opfs/snapshot-store.js` owns generic JSON session snapshot persistence, `runtime/opfs/machine-file-store.js` owns pure-text machine-file and G-code persistence, `runtime/opfs/linuxcnc-parameter-bridge.js` only copies parameter files between OPFS text storage and the LinuxCNC-backed WASM parameter-file ABI, `runtime/opfs/linuxcnc-tool-table-bridge.js` only copies tool tables between OPFS text storage and the LinuxCNC-backed WASM tool-table ABI, and `runtime/opfs/linuxcnc-machine-session-bridge.js` groups INI, parameter, and tool-table loading while using the LinuxCNC-backed INI SDK for `[EMCIO]RANDOM_TOOLCHANGER`, `[RS274NGC]PARAMETER_FILE`, and `[EMCIO]TOOL_TABLE` when available; explicit host session file-name options take precedence over INI-derived file names, missing INI file-name values fall back to host default parameter/tool-table paths, and path validation remains owned by the OPFS path model, including rejection of traversal or nested path segments from INI-derived file names. |
| Sim-config SDK staging | `runtime/sdk/src/linuxcnc-interp.js` exposes `runSimConfigProgram()` as a host-boundary convenience only. It writes caller-provided text files into the Emscripten filesystem, applies executable bits for user M-code files, and forwards to existing LinuxCNC-backed C ABI entry points such as `runFileWithIni()` or `runFiveAxisRemapFile()`; it does not implement interpreter, axis, remap, or user M-code semantics. |
## Enforced Non-Drift Rules ## Enforced Non-Drift Rules
@@ -78,7 +79,9 @@ semantic rewrites:
paths. paths.
- Host-side smoke validation is aggregated by - Host-side smoke validation is aggregated by
`tests/host/verify_host_smokes.sh` so Node, WASM, OPFS, and browser checks `tests/host/verify_host_smokes.sh` so Node, WASM, OPFS, and browser checks
run from one command. run from one command. The current aggregate run passes with
`host_wasm_opfs_browser_smokes=ok`, including the Node and browser
sim-config paths staged through `runSimConfigProgram()`.
- Identity/trivial, `5axiskins`, TRT `xyzac`/`xyzbc`, delta, SCARA, PUMA, - Identity/trivial, `5axiskins`, TRT `xyzac`/`xyzbc`, delta, SCARA, PUMA,
serial, hexapod, pentapod, and related kinematics sources now have native serial, hexapod, pentapod, and related kinematics sources now have native
source-probe coverage. source-probe coverage.

View File

@@ -0,0 +1,165 @@
# LinuxCNC Test Porting Tracker
## Purpose
This file tracks the continuing work to validate this simulation port against
LinuxCNC behavior by using LinuxCNC's own source files and test assets.
Hard rule: CNC semantics must come directly from LinuxCNC source. Project-owned
code may provide build logic, runtime shims, filesystem adapters, C ABI, SDK,
browser UI, and test runners, but must not reimplement interpreter, remap, tool,
parameter, kinematics, planner, or machine-control behavior.
## Fixed Validation Gates
Run these checks as the baseline for every substantial porting step:
```bash
wasm-port/tools/verify_upstream_baseline.sh
wasm-port/tools/verify_vendor_sync.sh
wasm-port/tests/native/verify_native_probes.sh
wasm-port/tests/native/verify_nc_files.sh
wasm-port/tests/wasm/node/verify_ini_wasm.sh
wasm-port/tests/wasm/node/verify_interp_wasm.sh
wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh
wasm-port/tests/wasm/node/verify_nc_files_wasm.sh
wasm-port/tests/wasm/node/verify_tp_wasm.sh
wasm-port/tests/opfs/node/verify_file_service.sh
wasm-port/tests/browser/verify_ini_panel_browser.sh
wasm-port/tests/browser/verify_interp_browser.sh
wasm-port/tests/host/verify_host_smokes.sh
```
## Status Legend
| Status | Meaning |
| --- | --- |
| Done | Native, WASM, browser, docs, and source-sync validation are complete for the current scope. |
| In progress | Test assets or runners are present, but coverage still needs extension or verification. |
| Planned | The item is selected for future work. |
| Blocked | The item needs missing LinuxCNC source, runtime boundary work, or upstream baseline clarification. |
## Continuing Steps
| Step | Status | Tracking notes |
| --- | --- | --- |
| Lock LinuxCNC upstream baseline | Done | Baseline commit is recorded in `wasm-port/tools/upstream-baseline.txt`; keep `docs/scope-and-baseline.md` synchronized when it changes. |
| Verify vendored source byte identity | Done | `tools/verify_vendor_sync.sh` must pass for every file in `tools/source-manifest.txt`. |
| Reject standalone CNC semantics | Done | `tools/verify_no_standalone_cnc_semantics.sh` guards project-owned `Interp::...` definitions outside documented runtime-edge stubs. |
| Maintain source reuse map | Done | Current sim-config, remap, interpreter, kinematics, TP, user-M, tool, named-parameter, and OPFS runtime boundaries are recorded in `docs/source-reuse-map.md`; keep updating it whenever LinuxCNC source, test assets, or runtime boundaries are added. |
| Maintain compatibility validation notes | Done | Current native, Node WASM, browser, OPFS, and host aggregate checks are recorded in `docs/compatibility-validation.md`; keep updating it whenever a validation command or fixture class is added. |
| Build LinuxCNC test inventory | In progress | `linuxcnc/tests/remap` has been inventoried by dependency class; the first broader `linuxcnc/tests` pass now records full-process, HAL/realtime, linuxcncrsh, Python-binding, and external-component families in the blocked table. |
| Prefer pure interpreter tests first | In progress | Continue with tests that can run through `Interp::open()`, `read()`, and `execute()` without realtime hardware or GUI. |
| Add upstream-output comparison | In progress | Use upstream `../linuxcnc/bin/rs274` or original `test.sh` output where practical, then compare against native/WASM/browser output. |
| Promote tests through three layers | In progress | Add each selected test first to native harnesses, then WASM Node, then browser smoke where meaningful. |
| Record blocked tests | In progress | First broader family-level blocked batch is recorded below; continue expanding it as each `linuxcnc/tests` family is inspected. |
| Add aggregate smoke coverage | Done | `tests/host/verify_host_smokes.sh` now runs stable Node WASM, sim-config WASM, `nc_files` WASM, OPFS, and browser smoke checks with shared builds. Current run passed with `host_wasm_opfs_browser_smokes=ok`. |
| Add `linuxcnc/nc_files` basic examples | Done | `tests/native/verify_nc_files.sh` now runs the basic/example subset of upstream `linuxcnc/nc_files` through LinuxCNC `bin/rs274`; current native baseline is 107 total, 101 pass, 6 expected context/entry-point edges, 0 unexpected failures. Exploratory `--all` inventory is 247 total, 219 pass, 28 expected context/entry-point edges, 0 unexpected failures. A representative vendored subset now passes Node WASM and browser execution through LinuxCNC-backed `Interp::open()`/`read()`/`execute()`. |
## LinuxCNC Test Asset Queue
| LinuxCNC test path | Status | Current target | Notes |
| --- | --- | --- | --- |
| `nc_files` basic/example programs | Done | Native, WASM Node, browser | Native coverage is tracked by `wasm-port/tests/native/verify_nc_files.sh`; complete programs execute strictly, macro/library files are wrapped with temporary `M2` for load/parse validation, and six known LinuxCNC-native context edges are classified explicitly. `--all` also inventories probe/plasmac/NURBS files and currently classifies all 28 non-passing files as expected runtime-context or entry-point edges. The representative vendored WASM/browser subset covers `arcspiral.ngc`, `hole-circle.ngc`, `factorial.ngc`, and `m6demo.ngc` through LinuxCNC-backed file execution only. |
| `tests/remap/duplicate-o-word` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/remap/duplicate-o-word`; validates duplicate O-word behavior through vendored LinuxCNC remap/O-word/file execution paths. |
| `tests/remap/fail/args.0` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/remap/fail/args.0`; validates required `P`/`Q` remap arguments when both are present through vendored LinuxCNC remap argument handling. |
| `tests/remap/fail/args.1` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/remap/fail/args.1`; validates LinuxCNC missing-`Q` remap error text and upstream-style continue-after-error flow. |
| `tests/remap/fail/args.2` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/remap/fail/args.2`; validates LinuxCNC missing-`P,Q` remap error text and upstream-style continue-after-error flow. |
| `tests/remap/fail/body-ngc` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/remap/fail/body-ngc`; validates LinuxCNC NGC remap body failure text and remap level restoration after continue-on-error execution. |
| `tests/remap/m30-interaction` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/remap/m30-interaction`; validates LinuxCNC remap/M30 interaction through vendored interpreter paths. |
| `tests/remap/nested-remaps-oword` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/remap/nested-remaps-oword`; validates nested NGC remaps through vendored LinuxCNC O-word dispatch. |
| `tests/remap/posargs.0` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/remap/posargs.0`; validates LinuxCNC positional remap arguments and `OWORD_NARGS` through vendored interpreter paths. |
| `tests/remap/sequencing` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/remap/sequencing`; validates LinuxCNC G/M remap execution ordering through vendored interpreter paths. |
| `tests/remap/remap-io` NGC-only branch | Done | Native, WASM Node, browser | Vendored `test-ngc.ini` plus `io_*.ngc` subroutines validate LinuxCNC NGC remaps for M62-M68 and M66 immediate input through vendored `Interp::parse_remap()` and `Interp::execute()`; the standalone boundary only stages inputs and captures canonical events. |
| `tests/interp/do-while-break` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/do-while-break`; validates LinuxCNC O-word do/while/break flow through vendored `Interp::open()`, `read()`, and `execute()` paths. |
| `tests/interp/oword-bug315` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/oword-bug315`; validates LinuxCNC O-word break-out-of-subroutine behavior through vendored file execution paths. |
| `tests/interp/oword-bug315-p2` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/oword-bug315-p2`; validates LinuxCNC dynamic O-word subroutine call selection through vendored file execution paths. |
| `tests/interp/exists` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/exists`; validates LinuxCNC `EXISTS[]` parameter handling through vendored file execution paths. |
| `tests/interp/return-value` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/return-value`; validates LinuxCNC O-word return-value and `#<_value>` behavior through vendored file execution paths. |
| `tests/interp/subs-follow-main` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/subs-follow-main`; validates LinuxCNC subroutines after main program text through vendored file execution paths. |
| `tests/interp/fractional-linenumbers` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/fractional-linenumbers`; validates LinuxCNC fractional line-number parsing through vendored file execution paths. |
| `tests/interp/namedparam-bug424` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/namedparam-bug424`; validates LinuxCNC named-parameter parsing regression behavior through vendored file execution paths. |
| `tests/interp/rotation` selected pure interpreter cases | Done | Native, WASM Node, browser | Vendored selected upstream cases under `wasm-port/vendor/linuxcnc/tests/interp/rotation`; validates LinuxCNC machine-coordinate `G53` absolute-position named parameters `#<_abs_x>`, `#<_abs_y>`, and `#<_abs_z>` across G54 offsets, G92 offsets, XY rotation, and inch/millimeter modes, plus rotated-coordinate `G28` and `G53` endpoint behavior through vendored file execution paths. |
| `tests/interp/iniparam` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/iniparam`; validates LinuxCNC `_ini[...]` lookup, missing INI-parameter error text, and continued file execution through vendored file execution paths and `INI_FILE_NAME`. |
| `tests/interp/iniparam-failassign` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/iniparam-failassign`; validates LinuxCNC `_ini[...]` read-only named-parameter assignment rejection through vendored file execution paths and `INI_FILE_NAME`. |
| `tests/interp/m19` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/m19`; validates LinuxCNC `M19` spindle orient canonical events, wait timeout, and `[RS274NGC]ORIENT_OFFSET` handling through vendored file execution paths. |
| `tests/interp/magic_comments/param_format_printing` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/magic_comments/param_format_printing`; validates LinuxCNC `(DEBUG,...)` magic-comment parameter formatting for integer, default float, and explicit precision formats through vendored file execution paths. |
| `tests/interp/m98m99` selected pure interpreter cases | Done | Native, WASM Node, browser | Vendored selected upstream cases under `wasm-port/vendor/linuxcnc/tests/interp/m98m99`; validates LinuxCNC Fanuc-style `M98/M99`, parameter scope, nested numbered subprograms, subprograms after main program text, leading-zero O-word lookup, named and numbered main programs, and expression-based O-sub/M98 calls through vendored file execution paths. |
| `tests/interp/sub-call-from-sub` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/sub-call-from-sub`; validates LinuxCNC external subroutine calls from another external subroutine through vendored file execution paths and LinuxCNC INI `SUBROUTINE_PATH`. |
| `tests/interp/sequence-number` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/sequence-number`; validates LinuxCNC `#<_line>` reporting in main files and external subroutines through vendored file execution paths and LinuxCNC INI `SUBROUTINE_PATH`. |
| `tests/interp/nested-sub-error` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/nested-sub-error`; validates LinuxCNC nested-subroutine-definition rejection through vendored file execution paths and LinuxCNC INI `SUBROUTINE_PATH`. |
| `tests/interp/nested-sub-in-file-error` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/nested-sub-in-file-error`; validates LinuxCNC rejection of forward seek to a later numbered subroutine in the same external subroutine file through vendored file execution paths and LinuxCNC INI `SUBROUTINE_PATH`. |
| `tests/interp/abort-hot-comment` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/abort-hot-comment`; validates LinuxCNC `(ABORT,...)` hot-comment parameter expansion for numbered, named, and INI named parameters through vendored file execution paths and `INI_FILE_NAME`. |
| `configs/sim/axis/foam/foam.ngc` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/configs/sim/axis/foam`; validates LinuxCNC INI-driven `U/V` axis mask handling through `axis_foam.ini` and vendored file execution paths. |
| `configs/sim/axis/geometry/xyzc.ngc` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/configs/sim/axis/geometry`; validates LinuxCNC `USER_M_PATH` registration with the real executable `M110` while process execution remains a deterministic standalone `USER_M_COMMAND` boundary. |
| `configs/sim/axis/external_offsets/dyn_demo.ngc` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets`; validates LinuxCNC `USER_M_PATH` registration with the real executable `M111` against an upstream sim program while process execution remains a deterministic standalone boundary. |
| More `tests/remap/*` interpreter-only cases | Planned | Native first | Select cases that do not require Python behavior beyond documented runtime-edge stubs unless the required LinuxCNC source path is ported. |
| More `tests/interp/*` interpreter-only cases | Planned | Native first | Select upstream interpreter cases that can run through vendored file execution without Python, HAL, realtime motion, or GUI. |
| `tests/rs274ngc/*` candidates | Planned | Native first | Use upstream `rs274` as the baseline where output is comparable. |
| Tool-table regression assets | Planned | Native and WASM Node | Must call vendored `tooldata_common.cc`; project code may only provide storage callbacks and filesystem boundaries. |
| Kinematics tests | Planned | Native probes first | Add runtime probes only around LinuxCNC kinematics entry points; HAL lifecycle remains a shim boundary. |
| Trajectory-planner tests | Planned | Native then WASM Node | Must call vendored LinuxCNC TP APIs; project code may only seed deterministic status/config state. |
## Per-Test Intake Checklist
Use this checklist when adding each LinuxCNC upstream test:
- Record the upstream test path.
- Copy or extract the original LinuxCNC files into `wasm-port/vendor/linuxcnc/`.
- Add the copied files to the relevant manifest or vendor-sync rule.
- Keep `.ini`, `.ngc`, `expected`, and original support files unchanged.
- Identify the LinuxCNC source files that own the behavior being tested.
- Add native runner coverage that calls vendored LinuxCNC code.
- Add WASM Node coverage through the exported C ABI and SDK filesystem bridge.
- Add browser coverage if the behavior crosses SDK, OPFS, or UI boundaries.
- Compare against upstream LinuxCNC output where practical.
- Update `docs/source-reuse-map.md`.
- Update `docs/compatibility-validation.md`.
- Add any blocked dependency to the blocked-test table below.
## Blocked Test Table
| LinuxCNC test path | Status | Blocking dependency | Next action |
| --- | --- | --- | --- |
| `tests/remap/introspect` | Blocked | Python remap modules and Python interpreter introspection. | Port or expose the required LinuxCNC Python remap runtime boundary before executing. |
| `tests/remap/fail/body-py` | Blocked | Python remap body error path. | Port the LinuxCNC Python remap body boundary before executing. |
| `tests/remap/fail/canon_error` | Blocked | Python prolog plus `emccanon.CANON_ERROR`. | Port LinuxCNC Python prolog and canonical-error boundary before executing. |
| `tests/remap/fail/epilog` | Blocked | Python epilog failure path. | Port LinuxCNC Python epilog boundary before executing. |
| `tests/remap/fail/prolog` | Blocked | Python prolog failure path. | Port LinuxCNC Python prolog boundary before executing. |
| `tests/remap/oword-pycall` | Blocked | Python O-word callable modules plus tool-change path. | Port the LinuxCNC Python O-word call boundary and required tool-change runtime edges. |
| `tests/remap/predefined-named-params` | Blocked | Python predefined named parameters. | Port the LinuxCNC Python named-parameter registration boundary. |
| `tests/remap/remap-io` full process/UI branch | Blocked | Full LinuxCNC process, HAL pins, motion I/O status propagation, and Python UI/test driver. | Keep the full process test upstream-only until those runtime boundaries exist; the NGC-only `test-ngc.ini` subpath is covered separately above. |
| `tests/remap/remap-reentry` | Blocked | Python generator remaps, HAL/motion runtime, and remap reentry. | Port LinuxCNC Python generator remap boundary and motion/HAL synchronization first. |
| `tests/remap/spindle` | Blocked | Python remap body and spindle runtime state. | Port the LinuxCNC Python remap boundary and spindle status adapter. |
| `tests/remap/variable-injection` | Blocked | Python prolog/epilog variable injection. | Port LinuxCNC Python prolog/epilog boundary before executing. |
| `tests/abort/*` | Blocked | Full LinuxCNC task/motion process, abort/stop handling, HAL files, and machine state after interruption. | Keep as upstream process reference until task/motion abort boundaries are available; do not reduce to standalone interpreter execution. |
| `tests/ccomp/early-exit` | Blocked | Full LinuxCNC process and motion-position validation after cutter-compensation early exit. | Use interpreter-only `tests/ccomp/*` cases first; revisit after motion state comparison is available. |
| `tests/classicladder/estop` | Blocked | ClassicLadder component, full LinuxCNC process, HAL files, and UI driver. | Keep as upstream-only reference until ClassicLadder/HAL process boundaries are represented. |
| `tests/halui/*` | Blocked | Full LinuxCNC process with `halui`, postgui HAL files, and UI/test driver interaction. | Establish HALUI/task runtime boundaries before porting. |
| `tests/hard-limits` | Blocked | Full LinuxCNC machine process, HAL limit wiring, and motion limit behavior. | Keep as upstream process reference until motion/HAL limit boundaries exist. |
| `tests/inifile/python_bindings` | Blocked | LinuxCNC Python INI bindings, not just vendored C++ `inifile.cc`. | Add only after a Python binding boundary is intentionally exposed, or keep covered by upstream. |
| `tests/io-startup/*` | Blocked | Full LinuxCNC `io` startup, task process, HAL files, and postgui setup. | Revisit after IO/task startup runtime boundaries are represented. |
| `tests/linuxcncrsh` | Blocked | Full LinuxCNC process plus `linuxcncrsh` socket protocol server. | Keep as upstream integration reference until linuxcncrsh/server boundaries are intentionally ported. |
| `tests/linuxcncrsh-tcp` | Blocked | Full LinuxCNC process plus TCP socket service and network timing. | Keep as upstream integration reference until linuxcncrsh/server boundaries are intentionally ported. |
| `tests/mdi-queue/*` | Blocked | Full LinuxCNC task/MDI queue behavior through linuxcncrsh, tool changes, and queued command synchronization. | Port only after task queue and linuxcncrsh boundaries are available. |
| `tests/motion/*` | Blocked | Full LinuxCNC sim process, HAL pins, linuxcncrsh, halsampler, and realtime motion behavior. | Keep as upstream motion integration reference; prefer standalone TP/kinematics probes for now. |
| `tests/motion-logger/*` | Blocked | Full LinuxCNC process plus motion-logger userspace program and mock-motion HAL setup. | Revisit after motion logging/runtime process boundaries are represented. |
| `tests/halcompile/*` | Blocked | LinuxCNC HAL component build/install flow, often including userspace or realtime component loading. | Keep as build-system/HAL integration reference; do not treat as interpreter semantics. |
| `tests/module-loading/*` | Blocked | HAL realtime module loading and RTAPI lifecycle through `halrun`/`loadrt`. | Covered only by narrow source/native probes today; full HAL module loading remains out of scope. |
| `tests/mb2hal/*` | Blocked | External `mb2hal` userspace component, Modbus I/O behavior, and HAL process wiring. | Keep as external-component integration reference until a host I/O boundary exists. |
| `tests/t0/*` | Blocked | Full LinuxCNC process, linuxcncrsh display, tool table startup, random/nonrandom toolchanger behavior, and HAL files. | Continue validating tool tables through vendored `tooldata_common.cc`; revisit full T0 startup through task/IO boundaries later. |
| `tests/tool-info/*` | Blocked | Full LinuxCNC task/IO startup with HALUI, startup tool state, and random/nonrandom toolchanger process behavior. | Keep tool parsing/formatting in standalone probes; revisit process startup behavior later. |
| `tests/toolchanger/*` | Blocked | Full LinuxCNC tool-change process, MDI/task behavior, IO state, and subroutine/user-M paths. | Continue standalone interpreter/tooldata coverage first; revisit after task/IO tool-change boundaries exist. |
| `tests/trajectory-planner/circular-arcs` | Blocked | Full LinuxCNC sim configs with task, HAL/Tcl files, GUI/display settings, and motion execution logs. | Keep using standalone TP API probes for planner code; full circular-arc machine tests require task/HAL/motion boundaries. |
| `tests/lowlevel/emcmot-error-mpsc` | Blocked | RIP-only build against LinuxCNC motion sources and low-level motion error driver. | Revisit only if low-level motion process test drivers are intentionally ported. |
| `tests/uspace/spawnv-root` | Blocked | Sudo-installed userspace HAL component and `spawnv` root behavior. | Keep upstream-only unless HAL component install/runtime support is added. |
## Next Work Batch
1. Continue inventorying `linuxcnc/tests/interp/*` for additional pure
interpreter cases that can run through vendored file execution without full
task/HAL/motion process state.
2. Keep adding skipped upstream families to the blocked-test table with a
concrete dependency reason before attempting implementation.
3. Run `wasm-port/tests/browser/verify_interp_browser.sh` and then
`wasm-port/tests/host/verify_host_smokes.sh` after each stable batch.

View File

@@ -809,24 +809,33 @@ Current five-axis switchkins status:
bridge-mill kinematics, loads vendored machine tool tables through bridge-mill kinematics, loads vendored machine tool tables through
LinuxCNC `tooldata_load()`, and runs vendored `xyzac_switchkins.ngc`, LinuxCNC `tooldata_load()`, and runs vendored `xyzac_switchkins.ngc`,
`xyzbc_switchkins.ngc`, `xyzbc_switchkins.ngc`,
`xyzac_switchkins_test_1.ngc`, `xyzac_switchkins_test_3.ngc`, `xyzac_switchkins_test_1.ngc`, `xyzac_switchkins_test_2.ngc`,
`boat-xyzac.ngc`, `boat-xyzbc.ngc`, `impeller-7bl-xyzac.ngc`, and `xyzac_switchkins_test_3.ngc`, `boat-xyzac.ngc`, `boat-xyzbc.ngc`,
`xyzab-tdr-demo.ngc`, plus bridge-mill `5axisgui.ngc` through LinuxCNC `impeller-7bl-xyzac.ngc`, and `xyzab-tdr-demo.ngc`, plus bridge-mill
remap/file execution paths. `5axisgui.ngc` through LinuxCNC remap/file execution paths.
7. The WASM interpreter core now links vendored `interp_remap.cc` plus the 7. The WASM interpreter core now links vendored `interp_remap.cc` plus the
Python-only runtime edge stub, and the Node WASM smoke runs vendored Python-only runtime edge stub, and the Node WASM smoke runs vendored
`xyzac_switchkins.ngc`, `xyzbc_switchkins.ngc`, `boat-xyzac.ngc`, and `xyzac_switchkins.ngc`, `xyzbc_switchkins.ngc`,
`boat-xyzbc.ngc`, `impeller-7bl-xyzac.ngc`, `xyzab-tdr-demo.ngc`, and `xyzac_switchkins_test_1.ngc`, `xyzac_switchkins_test_2.ngc`,
`5axisgui.ngc` through the SDK C ABI without JavaScript M-code or `xyzac_switchkins_test_3.ngc`, `boat-xyzac.ngc`, `boat-xyzbc.ngc`,
kinematics semantics. `impeller-7bl-xyzac.ngc`, `xyzab-tdr-demo.ngc`, and `5axisgui.ngc`
through the SDK C ABI without JavaScript M-code or kinematics semantics.
8. Browser smoke and the INI panel UI now expose the same validated 5-axis 8. Browser smoke and the INI panel UI now expose the same validated 5-axis
remap execution path by copying vendored LinuxCNC sample-machine files into remap execution path by copying vendored LinuxCNC sample-machine files into
the interpreter WASM filesystem and calling the SDK C ABI. the interpreter WASM filesystem and calling the SDK C ABI.
9. LinuxCNC upstream `tests/remap/duplicate-o-word` and
`tests/remap/m30-interaction` are now vendored unchanged and validated
through the generic remap file execution path. Native, Node WASM, and
browser interpreter smokes load the vendored INI, program, and remap
subroutines, then call vendored `Interp::parse_remap()`, `open()`,
`read()`, and `execute()`; standalone code only supplies the INI/file path
boundary.
Next work: Next work:
1. Continue broadening five-axis fixture coverage through the same 1. Continue broadening source-backed remap coverage through LinuxCNC upstream
source-backed execution boundary. tests that can run through NGC REMAP/O-word/file execution without Python
semantics.
2. Evaluate additional vendored LinuxCNC five-axis sample machines and demos 2. Evaluate additional vendored LinuxCNC five-axis sample machines and demos
after the TRT boat/impeller, TDR demo, and bridge-mill demo paths are after the TRT boat/impeller, TDR demo, and bridge-mill demo paths are
stable in native, Node WASM, browser, and UI remap paths. stable in native, Node WASM, browser, and UI remap paths.

View File

@@ -0,0 +1,486 @@
# LinuxCNC Sim Config Completion Plan
This plan tracks the work needed to run the LinuxCNC programs under
`linuxcnc/configs/sim` through the standalone native/WASM simulation runtime.
LinuxCNC source remains the semantic source of truth. Port code must live under
`wasm-port/`; do not edit `linuxcnc/` in place.
## Current Baseline
The latest strict harness run used `linuxcnc/configs/sim` as the program source,
selected the nearest INI/tool table for each `.ngc` unless an explicit sim
mapping is required, and ran `rs274` from the INI directory so relative
`SUBROUTINE_PATH` and `REMAP` entries resolve like a real sim config.
Result file:
```text
wasm-port/build/native/sim-configs/summary.tsv
```
Baseline:
```text
total: 159
pass: 151
fail: 8
timeout: 0
expected_fail: 8
unexpected_fail: 0
main: PASS 40, FAIL 8
macro_load: PASS 46, FAIL 0
remap_subroutine: PASS 65, FAIL 0
```
Remaining failures are explicit LinuxCNC-native baseline edges:
- `axis/external_offsets/*`: 4 failures from sim-only user M-codes `M111`.
- `axis/geometry/xyzc.ngc`: 1 failure from sim-only user M-code `M110`.
- `axis/foam/foam.ngc`: 1 failure from missing `U/V` axis support in the
LinuxCNC `bin/rs274` entry point.
- `axis/vismach/5axis/bridgemill/5axisgui.ngc`: 1 failure from missing `W`
axis support in the LinuxCNC `bin/rs274` entry point.
- `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_g533.ngc`:
1 failure from an upstream demo line that uses bare `X/Y/Z` words after
`G53.6` without an explicit motion G-code.
## Target
The first target is not a browser UI feature. It is a repeatable native and
WASM simulation harness that can load and execute all `linuxcnc/configs/sim`
programs that are meaningful in a standalone non-hardware simulation context.
Acceptance criteria:
- The native sim-config harness reports all executable sim programs as `PASS`.
- Macro/remap files are validated by the correct entry point: direct execution
only for real programs, load/parse or remap-call validation for subroutines.
- WASM tests cover the same representative classes after the native harness is
green.
- Any remaining unsupported files are explicitly classified as native GUI or
hardware-only and excluded by rule, not by accident.
## Phase 1: Make The Harness A First-Class Test
Status: complete. The tracked script exists and is wired into the native
validation entry point.
1. Add a tracked script:
```text
wasm-port/tests/native/verify_sim_configs.sh
```
2. Move the ad hoc test logic into the script:
- scan only `linuxcnc/configs/sim`;
- identify `.ngc`, nearest `.ini`, nearest `.tbl`;
- run from the INI directory;
- write `summary.tsv`, stdout, stderr, and interpreter output under
`wasm-port/build/native/sim-configs/`;
- classify programs as `main`, `macro_load`, `remap_subroutine`, or
`unsupported_runtime_edge`;
- fail the script only on unexpected failures.
3. Add deterministic classification rules:
- files under `remap_subs/` and `nc_subroutines/` are not standalone main
programs;
- files with no `M2`, `M30`, or `%` are load/parse fixtures unless the INI
references them through `REMAP`;
- user M-code files are tested through `USER_M_PATH` resolution, not by
pretending LinuxCNC native process services exist.
4. Wire the script into existing validation:
```text
wasm-port/tests/native/verify_native_probes.sh
```
5. Completion check:
```bash
wasm-port/tests/native/verify_sim_configs.sh
```
Latest result:
```text
total: 159
pass: 151
fail: 8
timeout: 0
expected_fail: 8
unexpected_fail: 0
skipped: 0
```
## Phase 2: Load Machine Axes From INI
Status: partially complete for the port runtime. The standalone native/WASM
runtime now parses `[TRAJ] COORDINATES`, updates the standalone external axis
mask used by LinuxCNC `GET_EXTERNAL_AXIS_MASK()`, and applies the same
`Interp::_readers` filtering that upstream `rs274ngc_pre.cc` uses. Native
regression coverage verifies that `axis_foam.ini` enables `U/V` readers and
`bridgemill/5axis.ini` enables the `W` reader.
The `verify_sim_configs.sh` baseline still records the LinuxCNC `bin/rs274`
entry-point failures as expected failures. That harness is intentionally kept
as a LinuxCNC-native baseline while the port runtime coverage tracks this
phase's standalone behavior.
Current failures:
- `axis/foam/foam.ngc`: `Bad character 'u' used`
- `axis/vismach/5axis/bridgemill/5axisgui.ngc`: `Bad character 'w' used`
Root cause:
The current standalone interpreter initialization does not configure the active
axis mask from `[TRAJ] COORDINATES` / `[KINS] KINEMATICS`. Native LinuxCNC
accepts `U/V/W` only when the machine config declares those axes.
Implementation steps:
1. Add a machine-config loader in `runtime/core/linuxcnc_wrap/`, for example:
```text
linuxcnc_machine_config.hh
linuxcnc_machine_config.cpp
```
2. Reuse vendored `inifile.cc` to parse:
- `[TRAJ] COORDINATES`
- `[KINS] KINEMATICS`
- `[DISPLAY] GEOMETRY`
- `[RS274NGC] PARAMETER_FILE`
- `[RS274NGC] SUBROUTINE_PATH`
- `[RS274NGC] USER_M_PATH`
- `[EMCIO] TOOL_TABLE`
3. Extend `initialize_minimal_interp()` / the runtime equivalent to set the
interpreter setup fields from the parsed machine config instead of hardcoded
`XYZ`.
4. Add focused native fixtures:
- `axis_foam.ini` + `foam.ngc` accepts `U/V`.
- `bridgemill/5axis.ini` + `5axisgui.ngc` accepts `W`.
5. Add the same coverage to the WASM SDK once native is green.
Completion check:
```bash
wasm-port/tests/native/verify_sim_configs.sh --only axis/foam/foam.ngc
wasm-port/tests/native/verify_sim_configs.sh --only axis/vismach/5axis/bridgemill/5axisgui.ngc
```
## Phase 3: Implement Standalone User M-Code Dispatch
Status: complete for the standalone native/WASM runtime boundary. The port now
reads `[DISPLAY] PROGRAM_PREFIX` and `[RS274NGC] USER_M_PATH`, searches
executable `M100` through `M199` files using the same order as LinuxCNC task
initialization, registers them in LinuxCNC's `USER_DEFINED_FUNCTION` table, and
records `USER_M_COMMAND` canonical boundary events instead of spawning host
processes. Native probes cover `axis/geometry` `M110` and
`axis/external_offsets` `M111`; Node WASM covers minimal `M110` and `M111`
fixtures through the Emscripten filesystem.
The `verify_sim_configs.sh` baseline still records these files as expected
failures because it intentionally runs LinuxCNC `bin/rs274`, not the standalone
task/runtime adapter.
Current failures:
- `axis/external_offsets/*.ngc`: unknown `M111`
- `axis/geometry/xyzc.ngc`: unknown `M110`
Root cause:
Native LinuxCNC resolves user M-codes through `[DISPLAY] PROGRAM_PREFIX` and
`[RS274NGC] USER_M_PATH` during task initialization, registers matching
executable `M100..M199` handlers with the interpreter, and later runs external
scripts through the task process boundary. The standalone runtime must register
the same interpreter boundary without running host processes in WASM/browser.
Implementation steps:
1. Locate the upstream M-code dispatch path in LinuxCNC and vendor the minimum
source needed to preserve semantics, or add a narrow runtime-edge adapter if
the upstream path is process-bound. Done: the adapter mirrors
`src/emc/task/emctask.cc` search/registration behavior and keeps process
execution outside CNC semantics.
2. Add `USER_M_PATH` parsing to the machine-config loader. Done.
3. For native standalone tests. Done:
- resolve `M100` through `M199` against configured search paths;
- register only executable files;
- emit deterministic `USER_M_COMMAND` boundary events.
4. For WASM/browser. Done for Node WASM:
- do not spawn host processes;
- use a host-boundary user-M adapter that registers deterministic
message-emitting handlers for sim-only notification M-codes;
- document this as a runtime edge, not CNC semantics.
5. Add tests. Done:
- `M110` from `axis/geometry`;
- `M111` from `axis/external_offsets`;
- minimal WASM `M110` and `M111` fixtures.
Completion check:
```bash
wasm-port/tests/native/verify_sim_configs.sh --only axis/external_offsets
wasm-port/tests/native/verify_sim_configs.sh --only axis/geometry/xyzc.ngc
```
Standalone runtime checks:
```bash
wasm-port/tests/native/verify_native_probes.sh
wasm-port/tests/wasm/node/verify_interp_wasm.sh
```
## Phase 4: Bring Up Python Remap Runtime Edges
Status: complete for the native sim-config harness. Current checks:
```text
gmoccapy: total 38, pass 38, fail 0
axis/laser: total 3, pass 3, fail 0
```
Root cause:
The configs use LinuxCNC Python remap modules. Earlier harness runs failed while
initializing `python/toplevel.py`; current native runs now resolve and execute
these configs under the LinuxCNC `rs274` baseline.
Implementation steps:
1. Preserve upstream behavior where possible:
- inspect vendored LinuxCNC Python plugin interfaces;
- identify exact APIs used by:
- `configs/sim/gmoccapy/python/toplevel.py`
- `configs/sim/gmoccapy/python/remap.py`
- `configs/sim/gmoccapy/python/stdglue.py`
- `configs/sim/axis/laser/python/toplevel.py`
- `configs/sim/axis/laser/python/remap.py`
2. Split the implementation into two layers:
- native harness support using the host Python runtime, if available;
- WASM support using a documented adapter boundary or a Python-free
equivalent only for runtime edges, not G-code semantics.
3. Add runtime state adapters required by Python remap:
- selected tool and pocket;
- tool table access;
- interpreter status object fields used by stdglue;
- message/error reporting;
- canonical tool-change side effects needed by `M6`, `M61`, and laser
remaps.
4. Make Python module path resolution match LinuxCNC:
- INI directory is the base directory;
- `PYTHONPATH` includes config `python/` directories;
- remap modules load relative to the sim config.
5. Add narrowly-scoped tests before running all 38 gmoccapy programs. Done:
- one gmoccapy tool-change macro;
- one gmoccapy lathe macro;
- one gmoccapy 5-axis example;
- one laser raster/vector program.
Completion check:
```bash
wasm-port/tests/native/verify_sim_configs.sh --only gmoccapy
wasm-port/tests/native/verify_sim_configs.sh --only axis/laser
```
## Phase 5: Support TWP Remaps Including G69
Status: mapping complete. The harness now maps
`axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/*` through the
explicit `xyzacb-trsrn_twp/xyzacb-trsrn.ini` machine config instead of falling
back to `axis/axis.ini`. That loads the TWP `G69` remap declarations correctly,
and 14 of the 15 TWP demo/remap programs now pass.
Current failures:
- `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_g533.ngc`
fails in the LinuxCNC `rs274` baseline because line 10 uses bare
`x50y50z150` after `G53.6`; same-directory demos use explicit `G0` motion
words at this point.
Root cause:
The demo programs rely on table/spindle rotary TWP remaps. The original test
mapping fell back to `axis/axis.ini` for these demo files because the actual
INI is in a child directory:
```text
axis/vismach/5axis/table-rotary_spindle-rotary-nutating/
xyzacb-trsrn_twp/xyzacb-trsrn.ini
xyzbca-trsrn_twp/xyzbca-trsrn.ini
```
Implementation steps:
1. Improve sim-config mapping. Done:
- if a `.ngc` is under a `demos/` sibling of machine-specific INI
directories, map it to the appropriate TWP INI instead of walking upward
to `axis/axis.ini`;
- encode this as explicit metadata in the harness, not a fragile heuristic.
2. Ensure `SUBROUTINE_PATH` includes `../remap_subs:../demos` as declared by
the TWP INIs.
3. Verify remap subroutines:
- `g69remap.ngc`
- `g531remap.ngc`
- `g533remap.ngc`
- `g536remap.ngc`
4. Add a native test for each failing demo.
Latest result:
```text
total: 15
pass: 14
fail: 1
timeout: 0
expected_fail: 1
unexpected_fail: 0
skipped: 0
```
Completion check:
```bash
wasm-port/tests/native/verify_sim_configs.sh --only table-rotary_spindle-rotary-nutating
```
## Phase 6: Promote Native Coverage To WASM
Status: complete for representative Node and browser WASM coverage. Dedicated
Node and browser smokes now pass a representative vendored `configs/sim` subset
through the SDK `runSimConfigProgram()` host boundary, which copies files into
the Emscripten filesystem and forwards execution to existing LinuxCNC-backed C
ABI paths. They cover:
- `axis/foam/foam.ngc` with `axis_foam.ini`, verifying INI-driven `U/V` axis
mask handling in WASM.
- `axis/vismach/5axis/bridgemill/5axisgui.ngc` with `5axis.ini`, verifying
INI-driven `W` axis mask handling and bridge-mill NGC remap execution in
WASM.
- `axis/geometry/xyzc.ngc` with `xyzc.ini` and real executable `M110`, verifying
`USER_M_PATH` registration in WASM.
- `axis/external_offsets/dyn_demo.ngc` with `dynamic_offsets.ini` and real
executable `M111`, verifying the same user-M boundary against an upstream sim
program.
Completion checks:
```bash
wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh
wasm-port/tests/browser/verify_interp_browser.sh
```
Remaining work:
1. Extend `runtime/core/linuxcnc_wrap/linuxcnc_interp_wasm.cpp` with the same
machine-config entry point used by native tests. Done for the existing
`runFileWithIni()` file-execution path.
2. Extend `runtime/sdk/src/linuxcnc-interp.js` with a host-boundary method such
as:
```js
runSimConfigProgram({ iniPath, programPath, files })
```
Done. The method only writes caller-provided text files into the
Emscripten filesystem, applies executable bits for user M-code files, and
forwards execution to the existing LinuxCNC-backed C ABI path. It does not
implement CNC semantics in JavaScript.
3. Copy required sim config files into the Emscripten FS:
- INI;
- tool table;
- parameter file;
- `.ngc` program;
- `SUBROUTINE_PATH` files;
- `USER_M_PATH` handler files or registered adapter handlers.
Done for the representative Node and browser subsets.
4. Add Node WASM coverage. Done:
```text
wasm-port/tests/wasm/node/verify_sim_configs_wasm.mjs
wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh
```
5. Add representative browser coverage. Done in
`wasm-port/tests/browser/interp_smoke.html` through the existing browser
interpreter smoke and the same `runSimConfigProgram()` SDK boundary as Node.
## Phase 7: Documentation And Drift Control
Status: complete for the current representative sim-config WASM/browser
coverage. The documentation and manifest now record the native, Node WASM,
browser, source reuse, and vendor-sync state for the selected sim-config
programs.
1. Update `docs/compatibility-validation.md` with the sim-config matrix. Done.
2. Update `docs/source-reuse-map.md` for newly vendored source files. Done.
3. Update `tools/source-manifest.txt` and `tools/verify_vendor_sync.sh` if new
LinuxCNC files are copied into `wasm-port/vendor/linuxcnc`. Done for the
representative `axis/foam`, `axis/geometry`, and `axis/external_offsets`
sim-config files; no `verify_vendor_sync.sh` logic change was needed.
4. Preserve the latest result summary as a machine-readable artifact, but do
not commit generated logs unless they are intentionally used as fixtures.
Done: generated logs remain under build/test output directories, while the
tracked documentation records only the current summary values and validation
commands.
## Recommended Work Order
1. Harness first: make `verify_sim_configs.sh` reproducible.
2. Axis config next: unblock `U/V/W` failures.
3. TWP mapping: complete; remaining TWP demo failures are Python/HAL remap
runtime edges.
4. User M-code dispatch: small runtime-edge feature, unblocks five files.
5. Python remap runtime: largest block; do it after the harness and smaller
runtime edges are stable.
6. WASM promotion after native parity.
## Non-Goals
- Do not implement a new JavaScript or project-authored G-code interpreter.
- Do not edit `linuxcnc/configs/sim` or any upstream `linuxcnc/` file to make
tests pass.
- Do not fake path, modal, kinematic, tool, or parameter semantics in the SDK.
- Do not treat gmoccapy native GUI code as browser UI implementation. Only its
simulation/remap behavior is relevant to this runtime.

View File

@@ -40,13 +40,18 @@ Current validation is intentionally mechanical:
| Identity/trivial kinematics | `src/emc/kinematics/kinematics.h`, `cubic.h`, `kins_util.c`, `trivkins.c` | Copy unchanged | HAL component lifecycle and RTAPI module metadata are replaced by standalone shims; forward/inverse mapping behavior remains LinuxCNC source | Vendor byte sync, per-file source probes, `linuxcnc_kinematics_probe` | | Identity/trivial kinematics | `src/emc/kinematics/kinematics.h`, `cubic.h`, `kins_util.c`, `trivkins.c` | Copy unchanged | HAL component lifecycle and RTAPI module metadata are replaced by standalone shims; forward/inverse mapping behavior remains LinuxCNC source | Vendor byte sync, per-file source probes, `linuxcnc_kinematics_probe` |
| Switchable 5-axis bridge kinematics | `src/emc/kinematics/5axiskins.c`, `switchkins.c`, `switchkins.h`, `userkfuncs.c`, plus `src/rtapi/rtapi_ctype.h` | Copy unchanged | HAL pin allocation, HAL component lifecycle, and RTAPI module metadata are standalone runtime edges; switchable 5-axis forward/inverse behavior remains LinuxCNC source | Vendor byte sync, per-file source probes, `linuxcnc_5axis_kinematics_probe` | | Switchable 5-axis bridge kinematics | `src/emc/kinematics/5axiskins.c`, `switchkins.c`, `switchkins.h`, `userkfuncs.c`, plus `src/rtapi/rtapi_ctype.h` | Copy unchanged | HAL pin allocation, HAL component lifecycle, and RTAPI module metadata are standalone runtime edges; switchable 5-axis forward/inverse behavior remains LinuxCNC source | Vendor byte sync, per-file source probes, `linuxcnc_5axis_kinematics_probe` |
| TRT table-rotary kinematics | `src/emc/kinematics/trtfuncs.c`, `xyzac-trt-kins.c`, `xyzbc-trt-kins.c` | Copy unchanged | HAL pin allocation and switchkins lifecycle stay runtime boundaries; XYZAC/XYZBC TRT forward/inverse behavior remains LinuxCNC source | Vendor byte sync, per-file source probes, `linuxcnc_xyzac_trt_kinematics_probe`, `linuxcnc_xyzbc_trt_kinematics_probe` | | TRT table-rotary kinematics | `src/emc/kinematics/trtfuncs.c`, `xyzac-trt-kins.c`, `xyzbc-trt-kins.c` | Copy unchanged | HAL pin allocation and switchkins lifecycle stay runtime boundaries; XYZAC/XYZBC TRT forward/inverse behavior remains LinuxCNC source | Vendor byte sync, per-file source probes, `linuxcnc_xyzac_trt_kinematics_probe`, `linuxcnc_xyzbc_trt_kinematics_probe` |
| Five-axis switchkins machine configs and M428/M429/M430 remaps | `configs/sim/axis/vismach/5axis/bridgemill/*`, `table-dual-rotary/*`, and `table-rotary-tilting/*` selected INI, HAL, tool-table, XML, demo, and `remap_subs/*.ngc` files plus `src/emc/rs274ngc/interp_remap.cc` | Copy unchanged | `M428`, `M429`, and `M430` remain LinuxCNC `REMAP` entries that call LinuxCNC NGC subroutines using `M68`, `M66`, `_hal[motion.switchkins-type]`, and the INI/HAL `motion.analog-out-03 => motion.switchkins-type` link; standalone remap descriptor parsing routes through vendored `Interp::parse_remap()` and `find_ngc_file()`, and NGC remap/file execution routes through vendored LinuxCNC O-word and `open()`/`read()`/`execute()` paths while HAL synchronization stays a runtime adapter boundary | Vendor byte sync, `linuxcnc_5axis_remap_asset_probe`, `linuxcnc_remap_parse_harness`, `linuxcnc_remap_hal_sync_harness`, `linuxcnc_5axis_remap_execute_harness`, `tests/wasm/node/verify_interp_wasm.sh`, `tests/browser/verify_interp_browser.sh`, `tests/browser/verify_ini_panel_browser.sh` | | Five-axis switchkins machine configs and M428/M429/M430 remaps | `configs/sim/axis/vismach/5axis/bridgemill/*`, `table-dual-rotary/*`, and `table-rotary-tilting/*` selected INI, HAL, tool-table, XML, demo, and `remap_subs/*.ngc` files plus `src/emc/rs274ngc/interp_remap.cc` | Copy unchanged | `M428`, `M429`, and `M430` remain LinuxCNC `REMAP` entries that call LinuxCNC NGC subroutines using `M68`, `M66`, `_hal[motion.switchkins-type]`, and the INI/HAL `motion.analog-out-03 => motion.switchkins-type` link; standalone remap descriptor parsing routes through vendored `Interp::parse_remap()` and `find_ngc_file()`, and NGC remap/file execution routes through vendored LinuxCNC O-word and `open()`/`read()`/`execute()` paths while HAL synchronization stays a runtime adapter boundary | Vendor byte sync, `linuxcnc_5axis_remap_asset_probe`, `linuxcnc_remap_parse_harness`, `linuxcnc_remap_hal_sync_harness`, `linuxcnc_5axis_remap_execute_harness`, `tests/wasm/node/verify_interp_wasm.sh`, `tests/wasm/node/verify_sim_configs_wasm.sh`, `tests/browser/verify_interp_browser.sh`, `tests/browser/verify_ini_panel_browser.sh` |
| Representative sim-config machine programs | `configs/sim/axis/foam/*`, `configs/sim/axis/geometry/M110`, `xyzc.ini`, `xyzc.ngc`, `configs/sim/axis/external_offsets/M111`, `dyn_demo.ngc`, `dynamic_offsets.ini`, `eoffset.tbl`, and `configs/sim/axis/sim.tbl` | Copy unchanged | The `foam`, `xyzc`, and dynamic external-offset sample programs remain LinuxCNC sim-config assets. The standalone boundary only copies INI, tool table, G-code, and executable user-M files into the native or WASM filesystem, then calls vendored LinuxCNC INI parsing and `Interp::open()`/`read()`/`execute()` through the existing machine-config adapter. The JS SDK `runSimConfigProgram()` is only the host file-staging helper for this boundary: it writes caller-provided text files to the Emscripten filesystem, applies executable bits, and forwards to LinuxCNC-backed C ABI entry points. `M110`/`M111` process execution stays a runtime boundary represented by deterministic `USER_M_COMMAND` canonical events | Vendor byte sync, `tests/native/verify_sim_configs.sh`, `tests/wasm/node/verify_sim_configs_wasm.sh`, `tests/browser/verify_interp_browser.sh` |
| Representative `nc_files` examples | `nc_files/arcspiral.ngc`, `nc_files/factorial.ngc`, `nc_files/hole-circle.ngc`, `nc_files/m6demo.ngc` | Copy unchanged | These upstream `linuxcnc/nc_files` examples are copied byte-for-byte into the WASM vendor tree after the native `nc_files` harness classifies the broader directory. Node and browser tests only stage the original `.ngc` text in the Emscripten filesystem and call the LinuxCNC-backed `Interp::open()`, `read()`, and `execute()` path through the existing SDK; no G-code, O-word, tool-change, or M-code behavior is implemented in JavaScript | Vendor byte sync, `tests/native/verify_nc_files.sh`, `tests/wasm/node/verify_nc_files_wasm.sh`, `tests/browser/verify_interp_browser.sh`, `tests/host/verify_host_smokes.sh` |
| LinuxCNC remap regression fixtures | `tests/remap/duplicate-o-word/*`, `tests/remap/fail/args.0/*`, `tests/remap/fail/args.1/*`, `tests/remap/fail/args.2/*`, `tests/remap/fail/body-ngc/*`, `tests/remap/m30-interaction/*`, `tests/remap/nested-remaps-oword/*`, `tests/remap/posargs.0/*`, `tests/remap/sequencing/*`, selected NGC-only `tests/remap/remap-io/test-ngc.ini` plus `io_*.ngc`, and `src/emc/rs274ngc/interp_remap.cc` plus `interp_o_word.cc` | Copy unchanged | The upstream duplicate O-word, NGC-only remap failure, M30/remap-level interaction, nested O-word remap, positional-argument remap, G/M remap sequencing, and remap-IO NGC subroutine regressions remain LinuxCNC REMAP/O-word/file or MDI execution tests. The standalone boundary only copies the upstream test files into native or WASM filesystems, reads LinuxCNC INI `SUBROUTINE_PATH`, `REMAP`, and `OWORD_NARGS` entries, and calls vendored `Interp::parse_remap()`, `open()`, `read()`, and `execute()` or feeds the upstream remap-IO MDI sequence into vendored `Interp::execute()`; the continue-on-error runner path only mirrors LinuxCNC `rs274 -n 0` test execution and does not implement duplicate-label, O-word, nested remap, M30, positional-argument, failure, sequencing, M62-M68, M66 input, or remap semantics | Vendor byte sync, `linuxcnc_duplicate_oword_remap_harness`, `tests/wasm/node/verify_interp_wasm.sh`, `tests/browser/verify_interp_browser.sh` |
| LinuxCNC interpreter regression fixtures | `tests/interp/do-while-break/*`, `tests/interp/oword-bug315/*`, `tests/interp/oword-bug315-p2/*`, `tests/interp/exists/*`, `tests/interp/return-value/*`, `tests/interp/subs-follow-main/*`, `tests/interp/fractional-linenumbers/*`, `tests/interp/namedparam-bug424/*`, selected `tests/interp/rotation/*` pure interpreter cases, `tests/interp/iniparam/*`, `tests/interp/iniparam-failassign/*`, `tests/interp/m19/*`, `tests/interp/magic_comments/param_format_printing/*`, selected `tests/interp/m98m99/*` pure interpreter cases, `tests/interp/sub-call-from-sub/*`, `tests/interp/sequence-number/*`, `tests/interp/nested-sub-error/*`, `tests/interp/nested-sub-in-file-error/*`, `tests/interp/abort-hot-comment/*`, plus vendored interpreter/O-word sources including `interp_o_word.cc`, `interp_read.cc`, `interp_execute.cc`, `interp_find.cc`, `interp_namedparams.cc`, and `rs274ngc_pre.cc` | Copy unchanged | The upstream do/while/break, O-word bug315, `EXISTS[]`, subroutine return-value, subs-after-main, fractional line-number, named-parameter, selected rotation/G53/G28 absolute-position and endpoint, INI named-parameter lookup/read-only protection, M19 spindle-orient offset/timeout handling, magic-comment parameter formatting, selected Fanuc `M98/M99` and O-expression call regressions, external-subroutine-call, external-subroutine line-number, nested-subroutine-definition rejection, blocked forward-seek-to-later-numbered-sub, and `(ABORT,...)` hot-comment parameter-expansion regressions remain LinuxCNC interpreter file-execution tests. The standalone boundary only vendors the original upstream assets, copies `*.ngc`, `test.ini`, `test.tbl`, `subs/*.ngc`, and referenced external subroutine `.ngc` files into native or WASM filesystems where applicable, sets the LinuxCNC `INI_FILE_NAME` runtime edge for `_ini[...]`, applies LinuxCNC INI machine settings such as `[RS274NGC]ORIENT_OFFSET`, reads LinuxCNC INI `SUBROUTINE_PATH`, captures LinuxCNC `(PRINT,...)` output through the existing stdout/Emscripten `print` boundary, and calls vendored `Interp::open()`, `read()`, and `execute()`; it does not implement O-word loop, break, subroutine, dynamic call, `M98/M99`, `EXISTS[]`, named-parameter, absolute-position named-parameter, G28/G53 endpoint, INI-variable, read-only parameter, line-number, spindle-speed, spindle-orient, magic-comment formatting, subroutine lookup, ABORT hot-comment, error, or branch semantics | Vendor byte sync, `linuxcnc_interp_minimal_harness`, `tests/wasm/node/verify_interp_wasm.sh`, `tests/browser/verify_interp_browser.sh` |
| Additional non-switchable kinematics | `src/emc/kinematics/corexykins.c`, `rotatekins.c`, `rosekins.c`, `maxkins.c`, `lineardeltakins.c`, `lineardeltakins-common.h`, `rotarydeltakins.c`, `rotarydeltakins-common.h`, `scorbot-kins.c`, `tripodkins.c`, `scarakins.c`, `pumakins.c`, `pumakins.h`, `genhexkins.c`, `genhexkins.h`, `genserfuncs.c`, `genserkins.c`, `genserkins.h`, `ugenserkins.c`, `pentakins.c`, `pentakins.h`, `cubic.c` | Copy unchanged | HAL pin allocation, HAL parameter allocation, HAL component lifecycle, RTAPI module metadata, Go math C/C++ linkage, switchkins iterative-forward warmup, and userspace test-program process entry remain standalone runtime edges; forward/inverse behavior remains LinuxCNC source where the module exposes it | Vendor byte sync, per-file source probes, `linuxcnc_corexy_kinematics_probe`, `linuxcnc_rotate_kinematics_probe`, `linuxcnc_rose_kinematics_probe`, `linuxcnc_max_kinematics_probe`, `linuxcnc_lineardelta_kinematics_probe`, `linuxcnc_rotarydelta_kinematics_probe`, `linuxcnc_scorbot_kinematics_probe`, `linuxcnc_tripod_kinematics_probe`, `linuxcnc_scara_kinematics_probe`, `linuxcnc_puma_kinematics_probe`, `linuxcnc_genser_kinematics_probe`, `linuxcnc_genhex_kinematics_probe`, `linuxcnc_pentakins_kinematics_probe` | | Additional non-switchable kinematics | `src/emc/kinematics/corexykins.c`, `rotatekins.c`, `rosekins.c`, `maxkins.c`, `lineardeltakins.c`, `lineardeltakins-common.h`, `rotarydeltakins.c`, `rotarydeltakins-common.h`, `scorbot-kins.c`, `tripodkins.c`, `scarakins.c`, `pumakins.c`, `pumakins.h`, `genhexkins.c`, `genhexkins.h`, `genserfuncs.c`, `genserkins.c`, `genserkins.h`, `ugenserkins.c`, `pentakins.c`, `pentakins.h`, `cubic.c` | Copy unchanged | HAL pin allocation, HAL parameter allocation, HAL component lifecycle, RTAPI module metadata, Go math C/C++ linkage, switchkins iterative-forward warmup, and userspace test-program process entry remain standalone runtime edges; forward/inverse behavior remains LinuxCNC source where the module exposes it | Vendor byte sync, per-file source probes, `linuxcnc_corexy_kinematics_probe`, `linuxcnc_rotate_kinematics_probe`, `linuxcnc_rose_kinematics_probe`, `linuxcnc_max_kinematics_probe`, `linuxcnc_lineardelta_kinematics_probe`, `linuxcnc_rotarydelta_kinematics_probe`, `linuxcnc_scorbot_kinematics_probe`, `linuxcnc_tripod_kinematics_probe`, `linuxcnc_scara_kinematics_probe`, `linuxcnc_puma_kinematics_probe`, `linuxcnc_genser_kinematics_probe`, `linuxcnc_genhex_kinematics_probe`, `linuxcnc_pentakins_kinematics_probe` |
| Trajectory planner | `src/emc/tp/tp.c`, `tc.c`, `tcq.c`, `spherical_arc.c`, `blendmath.c`, `sp_scurve.c`, `ruckig_wrapper.c`, plus matching `*.h` files | Copy unchanged | Native realtime scheduling and motion process state are replaced by standalone probe setup; the WASM TP probe uses the same deterministic status/config boundary and calls vendored TP APIs through a narrow C ABI | Vendor byte sync, per-file source probes, `linuxcnc_tp_api_probe`, `tests/wasm/node/verify_tp_wasm.sh` | | Trajectory planner | `src/emc/tp/tp.c`, `tc.c`, `tcq.c`, `spherical_arc.c`, `blendmath.c`, `sp_scurve.c`, `ruckig_wrapper.c`, plus matching `*.h` files | Copy unchanged | Native realtime scheduling and motion process state are replaced by standalone probe setup; the WASM TP probe uses the same deterministic status/config boundary and calls vendored TP APIs through a narrow C ABI | Vendor byte sync, per-file source probes, `linuxcnc_tp_api_probe`, `tests/wasm/node/verify_tp_wasm.sh` |
| Ruckig C planner support | Selected `src/emc/tp/cruckig/*.c` and `*.h` files in the manifest | Copy unchanged | Used as LinuxCNC planner support code through vendored TP sources | Vendor byte sync, per-file source probes, `tests/wasm/node/verify_tp_wasm.sh` | | Ruckig C planner support | Selected `src/emc/tp/cruckig/*.c` and `*.h` files in the manifest | Copy unchanged | Used as LinuxCNC planner support code through vendored TP sources | Vendor byte sync, per-file source probes, `tests/wasm/node/verify_tp_wasm.sh` |
| Posemath | `src/libnml/posemath/posemath.cc`, `_posemath.c`, `gomath.c`, `sincos.c`, and matching headers | Copy unchanged | `gomath.c` is compiled as C; `rtapi.h` shim is C/C++ compatible for this boundary | Vendor byte sync, per-file source probes, TP native and WASM probes | | Posemath | `src/libnml/posemath/posemath.cc`, `_posemath.c`, `gomath.c`, `sincos.c`, and matching headers | Copy unchanged | `gomath.c` is compiled as C; `rtapi.h` shim is C/C++ compatible for this boundary | Vendor byte sync, per-file source probes, TP native and WASM probes |
| RS274 interpreter state and parser | `src/emc/rs274ngc/modal_state.*`, `interp_internal.*`, `interp_read.cc`, `interp_check.cc`, `interp_execute.cc`, `interp_find.cc`, `interp_array.cc`, `interp_queue.*`, `rs274ngc*`, `units.h` | Copy unchanged | Python/remap/runtime edges are isolated in standalone wrappers and shims; parser and execution logic remain LinuxCNC source | Vendor byte sync, per-file source probes, interpreter harness fixtures | | RS274 interpreter state and parser | `src/emc/rs274ngc/modal_state.*`, `interp_internal.*`, `interp_read.cc`, `interp_check.cc`, `interp_execute.cc`, `interp_find.cc`, `interp_array.cc`, `interp_queue.*`, `rs274ngc*`, `units.h` | Copy unchanged | Python/remap/runtime edges are isolated in standalone wrappers and shims; parser and execution logic remain LinuxCNC source | Vendor byte sync, per-file source probes, interpreter harness fixtures |
| RS274 conversion semantics | `src/emc/rs274ngc/interp_convert.cc`, `interp_arc.cc`, `interp_inverse.cc`, `interp_cycles.cc`, `interp_g7x.cc`, `interp_o_word.cc`, `interp_write.cc` | Copy unchanged | Canonical calls are captured by standalone event sink functions; conversion behavior stays in vendored LinuxCNC files; feed-rate state is read back through the canonical runtime boundary during length-unit conversion; single-axis rotary indexer lock/unlock dispatch remains vendored LinuxCNC `issue_straight_index()` behavior | Vendor byte sync, per-file source probes, canonical fixture suite, no standalone `Interp::convert_g()` guard, `linuxcnc_indexer_harness`, `tests/wasm/node/verify_interp_wasm.sh` and `tests/browser/verify_interp_browser.sh` rotary-indexer assertions | | RS274 conversion semantics | `src/emc/rs274ngc/interp_convert.cc`, `interp_arc.cc`, `interp_inverse.cc`, `interp_cycles.cc`, `interp_g7x.cc`, `interp_o_word.cc`, `interp_write.cc` | Copy unchanged | Canonical calls are captured by standalone event sink functions; conversion behavior stays in vendored LinuxCNC files; feed-rate state is read back through the canonical runtime boundary during length-unit conversion; single-axis rotary indexer lock/unlock dispatch remains vendored LinuxCNC `issue_straight_index()` behavior | Vendor byte sync, per-file source probes, canonical fixture suite, no standalone `Interp::convert_g()` guard, `linuxcnc_indexer_harness`, `tests/wasm/node/verify_interp_wasm.sh` and `tests/browser/verify_interp_browser.sh` rotary-indexer assertions |
| User M-code registration boundary | `src/emc/task/emctask.cc`, `src/emc/rs274ngc/interp_convert.cc`, `src/emc/rs274ngc/interp_queue.cc` | Runtime-edge adapter | Vendored interpreter conversion still checks LinuxCNC `USER_DEFINED_FUNCTION` before accepting `M100..M199` and queues through vendored `enqueue_M_USER_COMMAND()`. The standalone machine-config adapter mirrors LinuxCNC task search order for `[DISPLAY]PROGRAM_PREFIX` and `[RS274NGC]USER_M_PATH`, registers executable M-code files in `USER_DEFINED_FUNCTION`, calls the canonical `FINISH()` boundary, and records deterministic `USER_M_COMMAND` events instead of spawning host processes in WASM/browser. | `linuxcnc_interp_minimal_harness` sim `M110`/`M111` probes, `tests/wasm/node/verify_interp_wasm.sh` minimal `M110`/`M111` fixtures |
| Named parameters and tool slot status | `src/emc/rs274ngc/interp_namedparams.cc`, related interpreter headers | Copy unchanged | `_ini[...]` and `_hal[...]` resolve through standalone INI/HAL adapters while lookup order stays LinuxCNC-derived; current/selected tool slot reads for vendored `Interp::synch()` come from the standalone tool adapter | Vendor byte sync, source probe, `linuxcnc_namedparam_harness`, `linuxcnc_interp_init_harness`, `tests/wasm/node/verify_interp_wasm.sh` and `tests/browser/verify_interp_browser.sh` `Interp::synch()` tool-slot assertions plus `Interp::init_named_parameters()`/`find_named_param()` named-parameter lookup assertions | | Named parameters and tool slot status | `src/emc/rs274ngc/interp_namedparams.cc`, related interpreter headers | Copy unchanged | `_ini[...]` and `_hal[...]` resolve through standalone INI/HAL adapters while lookup order stays LinuxCNC-derived; current/selected tool slot reads for vendored `Interp::synch()` come from the standalone tool adapter | Vendor byte sync, source probe, `linuxcnc_namedparam_harness`, `linuxcnc_interp_init_harness`, `tests/wasm/node/verify_interp_wasm.sh` and `tests/browser/verify_interp_browser.sh` `Interp::synch()` tool-slot assertions plus `Interp::init_named_parameters()`/`find_named_param()` named-parameter lookup assertions |
| Tool table parsing and formatting | `src/emc/tooldata/tooldata_common.cc` | Copy unchanged plus standalone storage callbacks | Native file parsing/formatting stays in vendored LinuxCNC source; the standalone tool adapter supplies `tooldata_get()`/`tooldata_put()` storage and index lookup, the WASM boundary only selects the LinuxCNC non-random or random-toolchanger branch via `tooldata_init()`, and OPFS remains a host-side persistence boundary | Vendor byte sync, `linuxcnc_tooldata_common_source_probe`, `tests/wasm/node/verify_interp_wasm.sh` non-random/random load/save tool-table assertions | | Tool table parsing and formatting | `src/emc/tooldata/tooldata_common.cc` | Copy unchanged plus standalone storage callbacks | Native file parsing/formatting stays in vendored LinuxCNC source; the standalone tool adapter supplies `tooldata_get()`/`tooldata_put()` storage and index lookup, the WASM boundary only selects the LinuxCNC non-random or random-toolchanger branch via `tooldata_init()`, and OPFS remains a host-side persistence boundary | Vendor byte sync, `linuxcnc_tooldata_common_source_probe`, `tests/wasm/node/verify_interp_wasm.sh` non-random/random load/save tool-table assertions |
| Dynamic interpreter base | `src/emc/rs274ngc/interp_base.*` | Copy unchanged | `interp_base.cc` source probe uses standalone `EMC2_HOME` compile-time path boundary for LinuxCNC dynamic interpreter lookup | Vendor byte sync, `linuxcnc_interp_base_source_probe` | | Dynamic interpreter base | `src/emc/rs274ngc/interp_base.*` | Copy unchanged | `interp_base.cc` source probe uses standalone `EMC2_HOME` compile-time path boundary for LinuxCNC dynamic interpreter lookup | Vendor byte sync, `linuxcnc_interp_base_source_probe` |
@@ -59,8 +64,9 @@ Current validation is intentionally mechanical:
| RTAPI | `rtapi_*.h`, TP, posemath, motion headers | Minimal standalone shim in `runtime/core/shims/rtapi.h` | | RTAPI | `rtapi_*.h`, TP, posemath, motion headers | Minimal standalone shim in `runtime/core/shims/rtapi.h` |
| NML transport | `emc.hh`, motion/NML type headers | Transport is not ported; only the status/type edges needed by vendored compute code are exposed through standalone shims and probes | | NML transport | `emc.hh`, motion/NML type headers | Transport is not ported; only the status/type edges needed by vendored compute code are exposed through standalone shims and probes |
| HAL runtime | named parameter lookup, kinematics component lifecycle, and runtime status edges | Standalone HAL adapter under `runtime/core/linuxcnc_wrap/` | | HAL runtime | named parameter lookup, kinematics component lifecycle, and runtime status edges | Standalone HAL adapter under `runtime/core/linuxcnc_wrap/` |
| Python/remap | `rs274ngc_pre.cc`, `interp_o_word.cc`, `interp_remap.cc`, remap hooks, selected LinuxCNC `configs/sim/axis/vismach/5axis/*/remap_subs/*.ngc` files | Python calls remain stubbed at the runtime boundary today; five-axis M428/M429/M430 source assets are vendored unchanged, the NGC remap descriptor path parses through vendored LinuxCNC code, and native validation now executes the switchkins NGC remap files plus selected demo programs through vendored LinuxCNC O-word and file execution paths while `_hal[...]` synchronization is supplied by the standalone HAL adapter | | User M-code process execution | `emctask.cc`, `interp_convert.cc`, `interp_queue.cc` | Search and registration are mirrored at the standalone machine-config boundary; native/WASM tests record deterministic `USER_M_COMMAND` events and do not spawn host processes |
| Canonical machine actions | `interp_convert.cc`, `interp_execute.cc`, `interp_queue.cc` | Captured by standalone canonical event sink functions for regression fixtures | | Python/remap | `rs274ngc_pre.cc`, `interp_o_word.cc`, `interp_remap.cc`, remap hooks, selected LinuxCNC `configs/sim/axis/vismach/5axis/*/remap_subs/*.ngc` files, `tests/remap/duplicate-o-word/*`, `tests/remap/fail/args.0/*`, `tests/remap/fail/args.1/*`, `tests/remap/fail/args.2/*`, `tests/remap/fail/body-ngc/*`, `tests/remap/m30-interaction/*`, `tests/remap/nested-remaps-oword/*`, `tests/remap/posargs.0/*`, `tests/remap/sequencing/*`, and NGC-only `tests/remap/remap-io/test-ngc.ini` plus `io_*.ngc` | Python calls remain stubbed at the runtime boundary today; five-axis M428/M429/M430 source assets and the upstream NGC remap regression files are vendored unchanged. NGC remap descriptor paths parse through vendored LinuxCNC code, native/WASM/browser validation executes the NGC remap files through vendored LinuxCNC O-word and file execution paths, and the remap-IO NGC-only branch feeds the upstream MDI sequence into vendored LinuxCNC `Interp::execute()` while external M66 input values and `_hal[...]` synchronization remain standalone runtime adapter boundaries |
| Canonical machine actions | `interp_convert.cc`, `interp_execute.cc`, `interp_queue.cc`, selected `tests/interp/*` regression assets | Captured by standalone canonical event sink functions for regression fixtures; vendored upstream interpreter test assets remain unchanged and are executed through LinuxCNC file execution |
| Realtime scheduling | TP and motion headers | Not ported; native TP probes seed deterministic status/config state | | Realtime scheduling | TP and motion headers | Not ported; native TP probes seed deterministic status/config state |
| GUI | None used as implementation | Native LinuxCNC GUI remains reference-only | | GUI | None used as implementation | Native LinuxCNC GUI remains reference-only |

View File

@@ -362,6 +362,12 @@ int main()
"xyzac_switchkins_test_1.ngc", "xyzac_switchkins_test_1.ngc",
0.0, 0.0,
}, },
{
"xyzac_switchkins_test_2",
"configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/"
"xyzac_switchkins_test_2.ngc",
0.0,
},
{ {
"xyzac_switchkins_test_3", "xyzac_switchkins_test_3",
"configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/" "configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/"
@@ -404,10 +410,11 @@ int main()
ok &= execute_demo_file(cases[0], demos[2]); ok &= execute_demo_file(cases[0], demos[2]);
ok &= execute_demo_file(cases[0], demos[3]); ok &= execute_demo_file(cases[0], demos[3]);
ok &= execute_demo_file(cases[0], demos[4]); ok &= execute_demo_file(cases[0], demos[4]);
ok &= execute_demo_file(cases[1], demos[5]); ok &= execute_demo_file(cases[0], demos[5]);
ok &= execute_demo_file(cases[0], demos[6]); ok &= execute_demo_file(cases[1], demos[6]);
ok &= execute_demo_file(cases[2], demos[7]); ok &= execute_demo_file(cases[0], demos[7]);
ok &= execute_demo_file(cases[3], demos[8]); ok &= execute_demo_file(cases[2], demos[8]);
ok &= execute_demo_file(cases[3], demos[9]);
print_bool("fiveaxis_linuxcnc_remap_execute_path", ok); print_bool("fiveaxis_linuxcnc_remap_execute_path", ok);
return ok ? 0 : 1; return ok ? 0 : 1;

View File

@@ -0,0 +1,363 @@
#define private public
#include "emc/rs274ngc/rs274ngc_interp.hh"
#undef private
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <iostream>
#include <sstream>
#include <string>
#include "canon_event_sink.hh"
#include "emc/ini/inifile.hh"
#include "emc/rs274ngc/interp_internal.hh"
#include "linuxcnc_hal_adapter.hh"
#include "linuxcnc_tool_adapter.hh"
#ifndef LINUXCNC_VENDOR_DIR
#error "LINUXCNC_VENDOR_DIR must point at wasm-port/vendor/linuxcnc"
#endif
namespace {
std::filesystem::path vendor_path(const std::string &rel)
{
return std::filesystem::path(LINUXCNC_VENDOR_DIR) / rel;
}
void print_bool(const std::string &name, bool value)
{
std::cout << name << "=" << (value ? 1 : 0) << "\n";
}
void print_error_text(Interp &interp, const std::string &name, int rc)
{
char error_buf[LINELEN] = {0};
interp.error_text(rc, error_buf, sizeof(error_buf));
std::cout << name << error_buf << "\n";
}
void initialize_interp(Interp &interp)
{
standalone::reset_hal_adapter();
standalone::reset_tool_adapter();
standalone::reset_canon_events();
interp._setup.length_units = CANON_UNITS_MM;
interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE;
interp._setup.ijk_distance_mode = DISTANCE_MODE::ABSOLUTE;
interp._setup.feed_mode = FEED_MODE::UNITS_PER_MINUTE;
interp._setup.plane = CANON_PLANE::XY;
interp._setup.motion_mode = G_0;
interp._setup.percent_flag = false;
interp._setup.sequence_number = 0;
interp._setup.parameter_occurrence = 0;
interp._setup.num_spindles = 1;
interp._setup.feature_set = FEATURE_INI_VARS | FEATURE_HAL_PIN_VARS;
interp._setup.parameter_g73_peck_clearance = 1.0;
interp._setup.parameter_g83_peck_clearance = 1.0;
interp._setup.parameters[5599] = 1.0;
std::memcpy(interp._readers, Interp::default_readers, sizeof(Interp::default_readers));
interp.init_named_parameters();
}
std::filesystem::path subroutine_path(const std::filesystem::path &machine_dir,
const std::string &entry)
{
const std::filesystem::path path(entry);
if (path.is_absolute()) {
return path;
}
return machine_dir / path;
}
bool configure_remaps(Interp &interp,
const std::filesystem::path &ini_path,
const char *label,
int expected_remaps)
{
linuxcnc::IniFile ini(ini_path.string());
const bool opened = static_cast<bool>(ini);
print_bool(std::string(label) + "_ini_open", opened);
if (!opened) {
return false;
}
const auto machine_dir = ini_path.parent_path();
std::snprintf(interp._setup.program_prefix, sizeof(interp._setup.program_prefix), "%s",
machine_dir.c_str());
int subroutine_count = 0;
if (auto subroutine_path_text = ini.findString("SUBROUTINE_PATH", "RS274NGC")) {
std::stringstream paths(*subroutine_path_text);
std::string entry;
while (std::getline(paths, entry, ':') && subroutine_count < MAX_SUB_DIRS) {
const auto resolved = subroutine_path(machine_dir, entry);
interp._setup.subroutines[subroutine_count++] = strstore(resolved.c_str());
}
}
std::cout << label << "_subroutine_path_count=" << subroutine_count << "\n";
if (ini.findBoolV("OWORD_NARGS", "RS274NGC", false)) {
interp._setup.feature_set |= FEATURE_OWORD_N_ARGS;
}
bool ok = subroutine_count > 0;
int remap_count = 0;
while (auto remap = ini.findString(++remap_count, "REMAP", "RS274NGC")) {
const int line_number = ini.lineOf(remap_count, "REMAP", "RS274NGC").second;
const int rc = interp.parse_remap(remap->c_str(), line_number);
std::cout << label << "_parse_remap_" << remap_count << "=" << rc << "\n";
ok &= rc == INTERP_OK;
}
remap_count -= 1;
std::cout << label << "_parsed_remap_count=" << remap_count << "\n";
ok &= remap_count == expected_remaps;
print_bool(std::string(label) + "_remaps_ready", ok);
return ok;
}
bool execute_file(Interp &interp,
const std::filesystem::path &program_path,
const char *label,
bool continue_on_error,
bool expect_error)
{
bool ok = true;
const int open_rc = interp.open(program_path.c_str());
std::cout << label << "_file_open=" << open_rc << "\n";
if (open_rc != INTERP_OK) {
return false;
}
constexpr int max_file_steps = 20000;
int read_count = 0;
int execute_count = 0;
int finish_count = 0;
int final_rc = INTERP_OK;
bool reached_exit = false;
bool reached_endfile = false;
bool saw_error = false;
while (read_count < max_file_steps && execute_count < max_file_steps) {
const int read_rc = interp.read();
if (read_rc == INTERP_ENDFILE) {
final_rc = read_rc;
reached_endfile = true;
break;
}
++read_count;
if ((read_rc != INTERP_OK) && (read_rc != INTERP_EXECUTE_FINISH)) {
std::cout << label << "_file_read_" << read_count << "=" << read_rc << "\n";
print_error_text(interp, std::string(label) + "_file_error_text=", read_rc);
final_rc = read_rc;
saw_error = true;
if (!expect_error) {
ok = false;
}
if (!continue_on_error) {
break;
}
continue;
}
const int execute_rc = interp.execute();
++execute_count;
final_rc = execute_rc;
if (execute_rc == INTERP_EXECUTE_FINISH) {
++finish_count;
}
if ((execute_rc != INTERP_OK) &&
(execute_rc != INTERP_EXECUTE_FINISH) &&
(execute_rc != INTERP_EXIT)) {
std::cout << label << "_file_execute_" << execute_count << "=" << execute_rc << "\n";
print_error_text(interp, std::string(label) + "_file_error_text=", execute_rc);
saw_error = true;
if (!expect_error) {
ok = false;
}
if (!continue_on_error) {
break;
}
continue;
}
if (execute_rc == INTERP_EXIT) {
reached_exit = true;
break;
}
}
std::cout << label << "_file_read_count=" << read_count << "\n";
std::cout << label << "_file_execute_count=" << execute_count << "\n";
std::cout << label << "_file_finish_count=" << finish_count << "\n";
std::cout << label << "_file_final_rc=" << final_rc << "\n";
print_bool(std::string(label) + "_file_reached_exit", reached_exit);
print_bool(std::string(label) + "_file_reached_endfile", reached_endfile);
print_bool(std::string(label) + "_file_saw_error", saw_error);
std::cout << label << "_canon_event_count=" << standalone::canon_events().size() << "\n";
for (const auto &event : standalone::canon_events()) {
std::cout << label << "_canon_event=" << event << "\n";
}
ok &= read_count > 0;
ok &= execute_count > 0;
ok &= read_count < max_file_steps;
ok &= execute_count < max_file_steps;
ok &= reached_exit || reached_endfile;
ok &= expect_error ? saw_error : !saw_error;
print_bool(std::string(label) + "_linuxcnc_remap_file_execute", ok);
return ok;
}
std::string command_label(const char *command)
{
std::string value = command ? command : "";
while (!value.empty() && (value.back() == '\n' || value.back() == '\r')) {
value.pop_back();
}
return value;
}
bool execute_mdi(Interp &interp, const char *label, const char *command)
{
int rc = interp.execute(command);
std::cout << label << "_mdi_" << command_label(command) << "_execute_0=" << rc << "\n";
int finish_count = 0;
while (rc == INTERP_EXECUTE_FINISH && finish_count < 16) {
++finish_count;
rc = interp.execute();
std::cout << label << "_mdi_" << command_label(command)
<< "_execute_" << finish_count << "=" << rc << "\n";
}
std::cout << label << "_mdi_" << command_label(command)
<< "_execute_finish_count=" << finish_count << "\n";
return rc == INTERP_OK;
}
bool execute_remap_io_mdi_case()
{
const auto test_dir = vendor_path("tests/remap/remap-io");
const auto ini_path = test_dir / "test-ngc.ini";
setenv("INI_FILE_NAME", ini_path.c_str(), 1);
Interp interp;
initialize_interp(interp);
bool ok = configure_remaps(interp, ini_path, "remap_io_ngc", 7);
const struct MdiStep {
const char *command;
bool digital_input;
int digital_value;
bool analog_input;
double analog_value;
} steps[] = {
{"M62 P1\n", false, 0, false, 0.0},
{"G0 X1.0\n", false, 0, false, 0.0},
{"M63 P1\n", false, 0, false, 0.0},
{"G0 X2.0\n", false, 0, false, 0.0},
{"M62 P1\n", false, 0, false, 0.0},
{"G0 X3.0\n", false, 0, false, 0.0},
{"M63 P1\n", false, 0, false, 0.0},
{"G0 X4.0\n", false, 0, false, 0.0},
{"M64 P1\n", false, 0, false, 0.0},
{"M65 P1\n", false, 0, false, 0.0},
{"M64 P1\n", false, 0, false, 0.0},
{"M65 P1\n", false, 0, false, 0.0},
{"M66 P1\n", true, 1, false, 0.0},
{"M66 P1\n", true, 0, false, 0.0},
{"M66 E1 L0\n", false, 0, true, 42.13},
{"M66 E1 L0\n", false, 0, true, -13.42},
{"M67 E1 Q42.13\n", false, 0, false, 0.0},
{"G0 X-1.0\n", false, 0, false, 0.0},
{"M67 E1 Q-13.42\n", false, 0, false, 0.0},
{"G0 X-2.0\n", false, 0, false, 0.0},
{"M67 E1 Q0.0\n", false, 0, false, 0.0},
{"G0 X-3.0\n", false, 0, false, 0.0},
{"M68 E1 Q42.13\n", false, 0, false, 0.0},
{"M68 E1 Q-13.42\n", false, 0, false, 0.0},
{"M68 E1 Q0.0\n", false, 0, false, 0.0},
};
for (const auto &step : steps) {
if (step.digital_input) {
standalone::set_external_digital_input(0, step.digital_value);
}
if (step.analog_input) {
standalone::set_external_analog_input(0, step.analog_value);
}
ok &= execute_mdi(interp, "remap_io_ngc", step.command);
std::cout << "remap_io_ngc_parameter_5399=" << interp._setup.parameters[5399] << "\n";
std::cout << "remap_io_ngc_parameter_100=" << interp._setup.parameters[100] << "\n";
}
std::cout << "remap_io_ngc_canon_event_count=" << standalone::canon_events().size() << "\n";
for (const auto &event : standalone::canon_events()) {
std::cout << "remap_io_ngc_canon_event=" << event << "\n";
}
ok &= std::fabs(interp._setup.parameters[5399] - -13.42) < 1e-9;
ok &= std::fabs(interp._setup.parameters[100] - -13.42) < 1e-9;
print_bool("remap_io_ngc_linuxcnc_mdi_sequence", ok);
unsetenv("INI_FILE_NAME");
return ok;
}
} // namespace
int main()
{
struct RegressionCase {
const char *label;
const char *rel;
int expected_remaps;
bool continue_on_error;
bool expect_error;
};
const RegressionCase cases[] = {
{"duplicate_oword", "tests/remap/duplicate-o-word", 2, false, false},
{"fail_args_0", "tests/remap/fail/args.0", 1, false, false},
{"fail_args_1", "tests/remap/fail/args.1", 1, true, true},
{"fail_args_2", "tests/remap/fail/args.2", 1, true, true},
{"fail_body_ngc", "tests/remap/fail/body-ngc", 1, true, true},
{"m30_interaction", "tests/remap/m30-interaction", 1, false, false},
{"nested_remaps_oword", "tests/remap/nested-remaps-oword", 4, false, false},
{"posargs_0", "tests/remap/posargs.0", 1, false, false},
{"sequencing", "tests/remap/sequencing", 7, false, false},
};
bool all_ok = true;
for (const auto &test : cases) {
const auto test_dir = vendor_path(test.rel);
const auto ini_path = test_dir / "test.ini";
const auto program_path = test_dir / "test.ngc";
setenv("INI_FILE_NAME", ini_path.c_str(), 1);
Interp interp;
initialize_interp(interp);
bool ok = configure_remaps(interp, ini_path, test.label, test.expected_remaps);
ok &= execute_file(interp,
program_path,
test.label,
test.continue_on_error,
test.expect_error);
print_bool(std::string(test.label) + "_linuxcnc_regression_path", ok);
all_ok &= ok;
unsetenv("INI_FILE_NAME");
}
all_ok &= execute_remap_io_mdi_case();
print_bool("linuxcnc_upstream_remap_regression_path", all_ok);
return all_ok ? 0 : 1;
}

View File

@@ -59,6 +59,18 @@ std::unordered_map<std::string, std::vector<std::string>> &pin_aliases()
return values; return values;
} }
std::unordered_map<int, int> &digital_inputs()
{
static std::unordered_map<int, int> values;
return values;
}
std::unordered_map<int, double> &analog_inputs()
{
static std::unordered_map<int, double> values;
return values;
}
int lookup_hal_value(const char *name, std::unordered_map<std::string, HalEntry> &values, int lookup_hal_value(const char *name, std::unordered_map<std::string, HalEntry> &values,
hal_type_t *type, hal_data_u **ptr, bool *connected) hal_type_t *type, hal_data_u **ptr, bool *connected)
{ {
@@ -89,6 +101,8 @@ void reset_hal_adapter()
signals().clear(); signals().clear();
params().clear(); params().clear();
pin_aliases().clear(); pin_aliases().clear();
digital_inputs().clear();
analog_inputs().clear();
} }
void set_hal_value(HalValueKind kind, const char *name, hal_type_t type, hal_data_u value, void set_hal_value(HalValueKind kind, const char *name, hal_type_t type, hal_data_u value,
@@ -130,6 +144,28 @@ void sync_motion_analog_output(int index, double value)
} }
} }
void set_external_digital_input(int index, int value)
{
digital_inputs()[index] = value ? 1 : 0;
}
void set_external_analog_input(int index, double value)
{
analog_inputs()[index] = value;
}
int external_digital_input(int index, int def)
{
auto it = digital_inputs().find(index);
return it == digital_inputs().end() ? def : it->second;
}
double external_analog_input(int index, double def)
{
auto it = analog_inputs().find(index);
return it == analog_inputs().end() ? def : it->second;
}
} // namespace standalone } // namespace standalone
int hal_init(const char *) int hal_init(const char *)

View File

@@ -15,5 +15,9 @@ void set_hal_value(HalValueKind kind, const char *name, hal_type_t type, hal_dat
bool connected = true); bool connected = true);
void link_hal_pin_alias(const char *source, const char *alias); void link_hal_pin_alias(const char *source, const char *alias);
void sync_motion_analog_output(int index, double value); void sync_motion_analog_output(int index, double value);
void set_external_digital_input(int index, int value);
void set_external_analog_input(int index, double value);
int external_digital_input(int index, int def);
double external_analog_input(int index, double def);
} // namespace standalone } // namespace standalone

View File

@@ -3,13 +3,19 @@
#undef private #undef private
#include <cstring> #include <cstring>
#include <cstdlib>
#include <filesystem>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <sstream>
#include <string> #include <string>
#include <vector> #include <vector>
#include "canon_event_sink.hh" #include "canon_event_sink.hh"
#include "emc/ini/inifile.hh"
#include "linuxcnc_hal_adapter.hh" #include "linuxcnc_hal_adapter.hh"
#include "linuxcnc_machine_config.hh"
#include "linuxcnc_runtime_state.hh"
#include "linuxcnc_tool_adapter.hh" #include "linuxcnc_tool_adapter.hh"
namespace { namespace {
@@ -70,6 +76,7 @@ void initialize_minimal_interp(Interp &interp)
{ {
seed_hal_values(); seed_hal_values();
seed_tool_values(); seed_tool_values();
standalone::reset_external_axis_mask();
interp._setup.length_units = CANON_UNITS_MM; interp._setup.length_units = CANON_UNITS_MM;
interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE; interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE;
interp._setup.ijk_distance_mode = DISTANCE_MODE::ABSOLUTE; interp._setup.ijk_distance_mode = DISTANCE_MODE::ABSOLUTE;
@@ -85,7 +92,45 @@ void initialize_minimal_interp(Interp &interp)
interp._setup.parameter_g83_peck_clearance = 1.0; interp._setup.parameter_g83_peck_clearance = 1.0;
interp._setup.parameters[5599] = 1.0; interp._setup.parameters[5599] = 1.0;
interp.load_tool_table(); interp.load_tool_table();
std::memcpy(interp._readers, Interp::default_readers, sizeof(Interp::default_readers)); standalone::apply_axis_mask_to_interp(interp, standalone::axis_mask_from_coordinates("XYZABCUVW"));
standalone::reset_user_m_code_registry();
}
void apply_ini_search_paths(Interp &interp, const char *ini_path)
{
if (!ini_path || ini_path[0] == '\0') {
return;
}
linuxcnc::IniFile ini(ini_path);
if (!ini) {
return;
}
const auto machine_dir = std::filesystem::path(ini_path).parent_path();
standalone::apply_machine_axis_config(interp, ini);
standalone::load_tool_table_from_ini(interp, ini, machine_dir.c_str());
standalone::register_user_m_codes_from_ini(ini, machine_dir.c_str());
std::snprintf(interp._setup.program_prefix, sizeof(interp._setup.program_prefix), "%s",
machine_dir.c_str());
int subroutine_count = 0;
if (auto subroutine_path_text = ini.findString("SUBROUTINE_PATH", "RS274NGC")) {
std::stringstream paths(*subroutine_path_text);
std::string entry;
while (std::getline(paths, entry, ':') && subroutine_count < MAX_SUB_DIRS) {
std::filesystem::path resolved(entry);
if (resolved.is_relative()) {
resolved = machine_dir / resolved;
}
interp._setup.subroutines[subroutine_count++] = strstore(resolved.c_str());
}
}
if (ini.findBoolV("OWORD_NARGS", "RS274NGC", false)) {
interp._setup.feature_set |= FEATURE_OWORD_N_ARGS;
}
} }
std::vector<std::string> load_program(int argc, char **argv) std::vector<std::string> load_program(int argc, char **argv)
@@ -117,11 +162,25 @@ std::vector<std::string> load_program(int argc, char **argv)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
bool continue_on_error = false;
int arg_offset = 1;
if (argc > 1 && std::strcmp(argv[1], "--continue-on-error") == 0) {
continue_on_error = true;
arg_offset = 2;
}
Interp interp; Interp interp;
standalone::reset_canon_events(); standalone::reset_canon_events();
const char *ini_path = argc > arg_offset + 1 ? argv[arg_offset + 1] : nullptr;
if (ini_path && ini_path[0] != '\0') {
setenv("INI_FILE_NAME", ini_path, 1);
} else {
unsetenv("INI_FILE_NAME");
}
initialize_minimal_interp(interp); initialize_minimal_interp(interp);
apply_ini_search_paths(interp, ini_path);
const std::vector<std::string> program = load_program(argc, argv); const std::vector<std::string> program = load_program(argc - arg_offset + 1, argv + arg_offset - 1);
if (program.empty()) { if (program.empty()) {
std::cerr << "empty fixture\n"; std::cerr << "empty fixture\n";
return 2; return 2;
@@ -138,6 +197,7 @@ int main(int argc, char **argv)
const int read_rc = interp.read(program.front().c_str()); const int read_rc = interp.read(program.front().c_str());
initialize_minimal_interp(interp); initialize_minimal_interp(interp);
apply_ini_search_paths(interp, ini_path);
for (std::size_t idx = 0; idx < program.size(); ++idx) { for (std::size_t idx = 0; idx < program.size(); ++idx) {
interp._setup.sequence_number = static_cast<int>(idx + 1); interp._setup.sequence_number = static_cast<int>(idx + 1);
const int execute_rc = interp.execute(program[idx].c_str()); const int execute_rc = interp.execute(program[idx].c_str());
@@ -152,10 +212,13 @@ int main(int argc, char **argv)
int file_open_rc = INTERP_FILE_NOT_OPEN; int file_open_rc = INTERP_FILE_NOT_OPEN;
int file_read_count = 0; int file_read_count = 0;
int file_execute_count = 0; int file_execute_count = 0;
if (argc > 1) { bool file_saw_error = false;
if (argc > arg_offset) {
Interp file_interp; Interp file_interp;
standalone::reset_canon_events();
initialize_minimal_interp(file_interp); initialize_minimal_interp(file_interp);
file_open_rc = file_interp.open(argv[1]); apply_ini_search_paths(file_interp, ini_path);
file_open_rc = file_interp.open(argv[arg_offset]);
if (file_open_rc == INTERP_OK) { if (file_open_rc == INTERP_OK) {
while (true) { while (true) {
const int file_read_rc = file_interp.read(); const int file_read_rc = file_interp.read();
@@ -166,8 +229,17 @@ int main(int argc, char **argv)
++file_read_count; ++file_read_count;
std::cout << "file_read_" << file_read_count << "=" << file_read_rc << "\n"; std::cout << "file_read_" << file_read_count << "=" << file_read_rc << "\n";
if ((file_read_rc != INTERP_OK) && (file_read_rc != INTERP_EXECUTE_FINISH)) { if ((file_read_rc != INTERP_OK) && (file_read_rc != INTERP_EXECUTE_FINISH)) {
if (file_read_rc > INTERP_MIN_ERROR) {
char error_buf[LINELEN] = {0};
file_interp.error_text(file_read_rc, error_buf, sizeof(error_buf));
std::cout << "file_error_text=" << error_buf << "\n";
}
file_saw_error = true;
if (!continue_on_error) {
break; break;
} }
continue;
}
const int file_execute_rc = file_interp.execute(); const int file_execute_rc = file_interp.execute();
++file_execute_count; ++file_execute_count;
@@ -180,6 +252,7 @@ int main(int argc, char **argv)
if ((file_execute_rc != INTERP_OK) && if ((file_execute_rc != INTERP_OK) &&
(file_execute_rc != INTERP_EXECUTE_FINISH) && (file_execute_rc != INTERP_EXECUTE_FINISH) &&
(file_execute_rc != INTERP_EXIT)) { (file_execute_rc != INTERP_EXIT)) {
file_saw_error = true;
break; break;
} }
if (file_execute_rc == INTERP_EXIT) { if (file_execute_rc == INTERP_EXIT) {
@@ -200,6 +273,7 @@ int main(int argc, char **argv)
std::cout << "file_open=" << file_open_rc << "\n"; std::cout << "file_open=" << file_open_rc << "\n";
std::cout << "file_read_count=" << file_read_count << "\n"; std::cout << "file_read_count=" << file_read_count << "\n";
std::cout << "file_execute_count=" << file_execute_count << "\n"; std::cout << "file_execute_count=" << file_execute_count << "\n";
std::cout << "file_saw_error=" << (file_saw_error ? 1 : 0) << "\n";
std::cout << "raw_line=" << raw_line << "\n"; std::cout << "raw_line=" << raw_line << "\n";
std::cout << "cooked_line=" << cooked_line << "\n"; std::cout << "cooked_line=" << cooked_line << "\n";
std::cout << "line_length=" << length << "\n"; std::cout << "line_length=" << length << "\n";

View File

@@ -13,6 +13,7 @@
#include "emc/rs274ngc/rs274ngc_return.hh" #include "emc/rs274ngc/rs274ngc_return.hh"
#include "canon_event_sink.hh" #include "canon_event_sink.hh"
#include "linuxcnc_hal_adapter.hh" #include "linuxcnc_hal_adapter.hh"
#include "linuxcnc_runtime_state.hh"
#include "linuxcnc_tool_adapter.hh" #include "linuxcnc_tool_adapter.hh"
#include "nml_intf/emc.hh" #include "nml_intf/emc.hh"
@@ -20,6 +21,7 @@ namespace standalone {
static std::vector<std::string> g_canon_events; static std::vector<std::string> g_canon_events;
static double g_external_feed_rate = 0.0; static double g_external_feed_rate = 0.0;
static int g_external_axis_mask = Interp::AXIS_MASK_X | Interp::AXIS_MASK_Y | Interp::AXIS_MASK_Z;
void reset_canon_events() void reset_canon_events()
{ {
@@ -36,6 +38,23 @@ const std::vector<std::string> &canon_events()
return g_canon_events; return g_canon_events;
} }
void reset_external_axis_mask()
{
g_external_axis_mask = Interp::AXIS_MASK_X | Interp::AXIS_MASK_Y | Interp::AXIS_MASK_Z;
}
void set_external_axis_mask(int axis_mask)
{
g_external_axis_mask = axis_mask == 0
? (Interp::AXIS_MASK_X | Interp::AXIS_MASK_Y | Interp::AXIS_MASK_Z)
: axis_mask;
}
int external_axis_mask()
{
return g_external_axis_mask;
}
} // namespace standalone } // namespace standalone
bool GET_BLOCK_DELETE(void) { return false; } bool GET_BLOCK_DELETE(void) { return false; }
@@ -523,9 +542,15 @@ int GET_EXTERNAL_FEED_OVERRIDE_ENABLE() { return 1; }
int GET_EXTERNAL_SPINDLE_OVERRIDE_ENABLE(int) { return 1; } int GET_EXTERNAL_SPINDLE_OVERRIDE_ENABLE(int) { return 1; }
int GET_EXTERNAL_ADAPTIVE_FEED_ENABLE() { return 1; } int GET_EXTERNAL_ADAPTIVE_FEED_ENABLE() { return 1; }
int GET_EXTERNAL_FEED_HOLD_ENABLE() { return 1; } int GET_EXTERNAL_FEED_HOLD_ENABLE() { return 1; }
int GET_EXTERNAL_DIGITAL_INPUT(int, int def) { return def; } int GET_EXTERNAL_DIGITAL_INPUT(int index, int def)
double GET_EXTERNAL_ANALOG_INPUT(int, double def) { return def; } {
int GET_EXTERNAL_AXIS_MASK() { return 7; } return standalone::external_digital_input(index, def);
}
double GET_EXTERNAL_ANALOG_INPUT(int index, double def)
{
return standalone::external_analog_input(index, def);
}
int GET_EXTERNAL_AXIS_MASK() { return standalone::external_axis_mask(); }
int GET_EXTERNAL_OFFSET_APPLIED() { return 0; } int GET_EXTERNAL_OFFSET_APPLIED() { return 0; }
EmcPose GET_EXTERNAL_OFFSETS() { return {{0, 0, 0}, 0, 0, 0, 0, 0, 0}; } EmcPose GET_EXTERNAL_OFFSETS() { return {{0, 0, 0}, 0, 0, 0, 0, 0, 0}; }

View File

@@ -2,6 +2,7 @@
#include "emc/rs274ngc/rs274ngc_interp.hh" #include "emc/rs274ngc/rs274ngc_interp.hh"
#undef private #undef private
#include <cmath>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <sstream> #include <sstream>
@@ -13,6 +14,8 @@
#include "canon_event_sink.hh" #include "canon_event_sink.hh"
#include "emc/ini/inifile.hh" #include "emc/ini/inifile.hh"
#include "linuxcnc_hal_adapter.hh" #include "linuxcnc_hal_adapter.hh"
#include "linuxcnc_machine_config.hh"
#include "linuxcnc_runtime_state.hh"
#include "linuxcnc_tool_adapter.hh" #include "linuxcnc_tool_adapter.hh"
#include "nml_intf/emc.hh" #include "nml_intf/emc.hh"
#include "tooldata.hh" #include "tooldata.hh"
@@ -105,6 +108,7 @@ void initialize_minimal_interp(Interp &interp)
{ {
seed_hal_values(); seed_hal_values();
seed_tool_values(); seed_tool_values();
standalone::reset_external_axis_mask();
interp._setup.length_units = CANON_UNITS_MM; interp._setup.length_units = CANON_UNITS_MM;
interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE; interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE;
interp._setup.ijk_distance_mode = DISTANCE_MODE::ABSOLUTE; interp._setup.ijk_distance_mode = DISTANCE_MODE::ABSOLUTE;
@@ -120,13 +124,15 @@ void initialize_minimal_interp(Interp &interp)
interp._setup.parameter_g83_peck_clearance = 1.0; interp._setup.parameter_g83_peck_clearance = 1.0;
interp._setup.parameters[5599] = 1.0; interp._setup.parameters[5599] = 1.0;
interp.load_tool_table(); interp.load_tool_table();
std::memcpy(interp._readers, Interp::default_readers, sizeof(Interp::default_readers)); standalone::apply_axis_mask_to_interp(interp, standalone::axis_mask_from_coordinates("XYZABCUVW"));
standalone::reset_user_m_code_registry();
} }
void initialize_fiveaxis_remap_interp(Interp &interp) void initialize_fiveaxis_remap_interp(Interp &interp)
{ {
seed_switchkins_hal(); seed_switchkins_hal();
seed_tool_values(); seed_tool_values();
standalone::reset_external_axis_mask();
interp._setup.length_units = CANON_UNITS_MM; interp._setup.length_units = CANON_UNITS_MM;
interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE; interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE;
interp._setup.ijk_distance_mode = DISTANCE_MODE::INCREMENTAL; interp._setup.ijk_distance_mode = DISTANCE_MODE::INCREMENTAL;
@@ -142,7 +148,8 @@ void initialize_fiveaxis_remap_interp(Interp &interp)
interp._setup.parameter_g83_peck_clearance = 1.0; interp._setup.parameter_g83_peck_clearance = 1.0;
interp._setup.parameters[5599] = 1.0; interp._setup.parameters[5599] = 1.0;
interp.load_tool_table(); interp.load_tool_table();
std::memcpy(interp._readers, Interp::default_readers, sizeof(Interp::default_readers)); standalone::apply_axis_mask_to_interp(interp, standalone::axis_mask_from_coordinates("XYZABCUVW"));
standalone::reset_user_m_code_registry();
interp.init_named_parameters(); interp.init_named_parameters();
} }
@@ -274,6 +281,15 @@ void append_named_value(std::ostringstream &output, Interp &interp, const char *
output << name << ": rc=" << rc << " found=" << status << " value=" << value << "\n"; output << name << ": rc=" << rc << " found=" << status << " value=" << value << "\n";
} }
std::string command_label(const char *command)
{
std::string value = command ? command : "";
while (!value.empty() && (value.back() == '\n' || value.back() == '\r')) {
value.pop_back();
}
return value;
}
std::string parent_path(const char *path) std::string parent_path(const char *path)
{ {
std::string value = path ? path : ""; std::string value = path ? path : "";
@@ -329,6 +345,88 @@ bool parse_machine_remaps(Interp &interp, std::ostringstream &output, const char
return ok; return ok;
} }
std::string remap_subroutine_path(const std::string &machine_dir, const std::string &entry)
{
if (!entry.empty() && entry[0] == '/') {
return entry;
}
if (machine_dir == "/") {
return machine_dir + entry;
}
return machine_dir + "/" + entry;
}
int apply_ini_search_paths(Interp &interp, const linuxcnc::IniFile &ini, const std::string &machine_dir)
{
standalone::apply_machine_axis_config(interp, ini);
standalone::load_tool_table_from_ini(interp, ini, machine_dir.c_str());
standalone::register_user_m_codes_from_ini(ini, machine_dir.c_str());
std::snprintf(interp._setup.program_prefix, sizeof(interp._setup.program_prefix), "%s",
machine_dir.c_str());
int subroutine_count = 0;
if (auto subroutine_path_text = ini.findString("SUBROUTINE_PATH", "RS274NGC")) {
std::istringstream paths(*subroutine_path_text);
std::string entry;
while (std::getline(paths, entry, ':') && subroutine_count < MAX_SUB_DIRS) {
const std::string resolved = remap_subroutine_path(machine_dir, entry);
interp._setup.subroutines[subroutine_count++] = strstore(resolved.c_str());
}
}
if (ini.findBoolV("OWORD_NARGS", "RS274NGC", false)) {
interp._setup.feature_set |= FEATURE_OWORD_N_ARGS;
}
return subroutine_count;
}
bool apply_ini_search_paths(Interp &interp, const char *ini_path)
{
if (!ini_path || ini_path[0] == '\0') {
return true;
}
linuxcnc::IniFile ini(ini_path);
if (!ini) {
return false;
}
apply_ini_search_paths(interp, ini, parent_path(ini_path));
return true;
}
bool parse_remaps_from_ini(Interp &interp, std::ostringstream &output, const char *ini_path)
{
const std::string machine_dir = parent_path(ini_path);
linuxcnc::IniFile ini(ini_path ? ini_path : "");
const bool opened = static_cast<bool>(ini);
output << "remap_ini_open=" << (opened ? 1 : 0) << "\n";
if (!opened) {
return false;
}
const int subroutine_count = apply_ini_search_paths(interp, ini, machine_dir);
output << "remap_subroutine_path_count=" << subroutine_count << "\n";
bool ok = subroutine_count > 0;
int remap_count = 0;
while (auto remap = ini.findString(++remap_count, "REMAP", "RS274NGC")) {
const int line_number = ini.lineOf(remap_count, "REMAP", "RS274NGC").second;
const int rc = interp.parse_remap(remap->c_str(), line_number);
output << "remap_parse_remap_" << remap_count << "=" << rc << "\n";
ok &= rc == INTERP_OK;
}
remap_count -= 1;
output << "remap_parsed_remap_count=" << remap_count << "\n";
ok &= remap_count > 0;
output << "remap_remaps_ready=" << (ok ? 1 : 0) << "\n";
return ok;
}
void apply_parameter_assignments(Interp &interp, const char *assignments) void apply_parameter_assignments(Interp &interp, const char *assignments)
{ {
std::istringstream input(assignments ? assignments : ""); std::istringstream input(assignments ? assignments : "");
@@ -371,6 +469,7 @@ char *lcinterp_run_program_with_ini(const char *program_text, const char *ini_pa
Interp interp; Interp interp;
standalone::reset_canon_events(); standalone::reset_canon_events();
initialize_minimal_interp(interp); initialize_minimal_interp(interp);
apply_ini_search_paths(interp, ini_path);
const std::vector<std::string> program = split_program_lines(program_text); const std::vector<std::string> program = split_program_lines(program_text);
std::ostringstream output; std::ostringstream output;
@@ -401,8 +500,7 @@ char *lcinterp_run_program(const char *program_text)
return lcinterp_run_program_with_ini(program_text, nullptr); return lcinterp_run_program_with_ini(program_text, nullptr);
} }
EMSCRIPTEN_KEEPALIVE char *run_file_with_ini_internal(const char *path, const char *ini_path, bool continue_on_error)
char *lcinterp_run_file_with_ini(const char *path, const char *ini_path)
{ {
if (ini_path && ini_path[0] != '\0') { if (ini_path && ini_path[0] != '\0') {
setenv("INI_FILE_NAME", ini_path, 1); setenv("INI_FILE_NAME", ini_path, 1);
@@ -415,6 +513,8 @@ char *lcinterp_run_file_with_ini(const char *path, const char *ini_path)
initialize_minimal_interp(interp); initialize_minimal_interp(interp);
std::ostringstream output; std::ostringstream output;
apply_ini_search_paths(interp, ini_path);
const int open_rc = interp.open(path); const int open_rc = interp.open(path);
output << "file_open=" << open_rc << "\n"; output << "file_open=" << open_rc << "\n";
if (open_rc != INTERP_OK) { if (open_rc != INTERP_OK) {
@@ -429,6 +529,7 @@ char *lcinterp_run_file_with_ini(const char *path, const char *ini_path)
int file_read_count = 0; int file_read_count = 0;
int file_execute_count = 0; int file_execute_count = 0;
bool saw_error = false;
while (true) { while (true) {
const int read_rc = interp.read(); const int read_rc = interp.read();
if (read_rc == INTERP_ENDFILE) { if (read_rc == INTERP_ENDFILE) {
@@ -444,8 +545,12 @@ char *lcinterp_run_file_with_ini(const char *path, const char *ini_path)
interp.error_text(read_rc, error_buf, sizeof(error_buf)); interp.error_text(read_rc, error_buf, sizeof(error_buf));
output << "file_error_text=" << error_buf << "\n"; output << "file_error_text=" << error_buf << "\n";
} }
saw_error = true;
if (!continue_on_error) {
break; break;
} }
continue;
}
const int execute_rc = interp.execute(); const int execute_rc = interp.execute();
++file_execute_count; ++file_execute_count;
@@ -458,6 +563,7 @@ char *lcinterp_run_file_with_ini(const char *path, const char *ini_path)
if ((execute_rc != INTERP_OK) && if ((execute_rc != INTERP_OK) &&
(execute_rc != INTERP_EXECUTE_FINISH) && (execute_rc != INTERP_EXECUTE_FINISH) &&
(execute_rc != INTERP_EXIT)) { (execute_rc != INTERP_EXIT)) {
saw_error = true;
break; break;
} }
if (execute_rc == INTERP_EXIT) { if (execute_rc == INTERP_EXIT) {
@@ -467,10 +573,23 @@ char *lcinterp_run_file_with_ini(const char *path, const char *ini_path)
output << "file_read_count=" << file_read_count << "\n"; output << "file_read_count=" << file_read_count << "\n";
output << "file_execute_count=" << file_execute_count << "\n"; output << "file_execute_count=" << file_execute_count << "\n";
output << "file_saw_error=" << (saw_error ? 1 : 0) << "\n";
append_events_and_state(output, interp); append_events_and_state(output, interp);
return copy_result(output.str()); return copy_result(output.str());
} }
EMSCRIPTEN_KEEPALIVE
char *lcinterp_run_file_with_ini(const char *path, const char *ini_path)
{
return run_file_with_ini_internal(path, ini_path, false);
}
EMSCRIPTEN_KEEPALIVE
char *lcinterp_run_file_with_ini_continue_on_error(const char *path, const char *ini_path)
{
return run_file_with_ini_internal(path, ini_path, true);
}
EMSCRIPTEN_KEEPALIVE EMSCRIPTEN_KEEPALIVE
char *lcinterp_run_file(const char *path) char *lcinterp_run_file(const char *path)
{ {
@@ -573,6 +692,230 @@ char *lcinterp_run_fiveaxis_remap_file(const char *path, const char *ini_path)
return copy_result(output.str()); return copy_result(output.str());
} }
char *run_remap_file_internal(const char *path,
const char *ini_path,
bool continue_on_error,
bool expect_error)
{
if (ini_path && ini_path[0] != '\0') {
setenv("INI_FILE_NAME", ini_path, 1);
} else {
unsetenv("INI_FILE_NAME");
}
Interp interp;
standalone::reset_canon_events();
initialize_minimal_interp(interp);
interp.init_named_parameters();
std::ostringstream output;
bool ok = parse_remaps_from_ini(interp, output, ini_path);
const int open_rc = interp.open(path);
output << "remap_file_open=" << open_rc << "\n";
if (open_rc != INTERP_OK) {
append_error_text(output, interp, "remap_file_error_text=", open_rc);
output << "remap_linuxcnc_file_execute=0\n";
unsetenv("INI_FILE_NAME");
return copy_result(output.str());
}
constexpr int max_file_steps = 20000;
int file_read_count = 0;
int file_execute_count = 0;
int file_finish_count = 0;
int final_rc = INTERP_OK;
bool reached_exit = false;
bool reached_endfile = false;
bool saw_error = false;
while ((file_read_count < max_file_steps) && (file_execute_count < max_file_steps)) {
const int read_rc = interp.read();
if (read_rc == INTERP_ENDFILE) {
output << "remap_file_read_eof=" << read_rc << "\n";
final_rc = read_rc;
reached_endfile = true;
break;
}
++file_read_count;
if ((read_rc != INTERP_OK) && (read_rc != INTERP_EXECUTE_FINISH)) {
output << "remap_file_read_" << file_read_count << "=" << read_rc << "\n";
append_error_text(output, interp, "remap_file_error_text=", read_rc);
final_rc = read_rc;
saw_error = true;
if (!expect_error) {
ok = false;
}
if (!continue_on_error) {
break;
}
continue;
}
const int execute_rc = interp.execute();
++file_execute_count;
final_rc = execute_rc;
if (execute_rc == INTERP_EXECUTE_FINISH) {
++file_finish_count;
}
if ((execute_rc != INTERP_OK) && (execute_rc != INTERP_EXIT) &&
(execute_rc != INTERP_EXECUTE_FINISH)) {
output << "remap_file_execute_" << file_execute_count << "=" << execute_rc << "\n";
append_error_text(output, interp, "remap_file_error_text=", execute_rc);
saw_error = true;
if (!expect_error) {
ok = false;
}
if (!continue_on_error) {
break;
}
continue;
}
if (execute_rc == INTERP_EXIT) {
reached_exit = true;
break;
}
}
output << "remap_file_read_count=" << file_read_count << "\n";
output << "remap_file_execute_count=" << file_execute_count << "\n";
output << "remap_file_finish_count=" << file_finish_count << "\n";
output << "remap_file_final_rc=" << final_rc << "\n";
output << "remap_file_reached_exit=" << (reached_exit ? 1 : 0) << "\n";
output << "remap_file_reached_endfile=" << (reached_endfile ? 1 : 0) << "\n";
output << "remap_file_saw_error=" << (saw_error ? 1 : 0) << "\n";
for (const auto &event : standalone::canon_events()) {
output << "remap_canon_event=" << event << "\n";
}
ok &= file_read_count > 0;
ok &= file_execute_count > 0;
ok &= file_read_count < max_file_steps;
ok &= file_execute_count < max_file_steps;
ok &= reached_exit || reached_endfile;
ok &= expect_error ? saw_error : !saw_error;
output << "remap_linuxcnc_file_execute=" << (ok ? 1 : 0) << "\n";
unsetenv("INI_FILE_NAME");
return copy_result(output.str());
}
} // extern "C"
bool execute_remap_mdi_command(Interp &interp, std::ostringstream &output, const char *command)
{
const std::string label = command_label(command);
int rc = interp.execute(command);
output << "remap_mdi_" << label << "_execute_0=" << rc << "\n";
int finish_count = 0;
while (rc == INTERP_EXECUTE_FINISH && finish_count < 16) {
++finish_count;
rc = interp.execute();
output << "remap_mdi_" << label << "_execute_" << finish_count << "=" << rc << "\n";
}
output << "remap_mdi_" << label << "_execute_finish_count=" << finish_count << "\n";
return rc == INTERP_OK;
}
char *run_remap_io_mdi_sequence_internal(const char *ini_path)
{
if (ini_path && ini_path[0] != '\0') {
setenv("INI_FILE_NAME", ini_path, 1);
} else {
unsetenv("INI_FILE_NAME");
}
Interp interp;
standalone::reset_canon_events();
initialize_minimal_interp(interp);
interp.init_named_parameters();
std::ostringstream output;
bool ok = parse_remaps_from_ini(interp, output, ini_path);
ok &= output.str().find("remap_parsed_remap_count=7") != std::string::npos;
const struct MdiStep {
const char *command;
bool digital_input;
int digital_value;
bool analog_input;
double analog_value;
} steps[] = {
{"M62 P1\n", false, 0, false, 0.0},
{"G0 X1.0\n", false, 0, false, 0.0},
{"M63 P1\n", false, 0, false, 0.0},
{"G0 X2.0\n", false, 0, false, 0.0},
{"M62 P1\n", false, 0, false, 0.0},
{"G0 X3.0\n", false, 0, false, 0.0},
{"M63 P1\n", false, 0, false, 0.0},
{"G0 X4.0\n", false, 0, false, 0.0},
{"M64 P1\n", false, 0, false, 0.0},
{"M65 P1\n", false, 0, false, 0.0},
{"M64 P1\n", false, 0, false, 0.0},
{"M65 P1\n", false, 0, false, 0.0},
{"M66 P1\n", true, 1, false, 0.0},
{"M66 P1\n", true, 0, false, 0.0},
{"M66 E1 L0\n", false, 0, true, 42.13},
{"M66 E1 L0\n", false, 0, true, -13.42},
{"M67 E1 Q42.13\n", false, 0, false, 0.0},
{"G0 X-1.0\n", false, 0, false, 0.0},
{"M67 E1 Q-13.42\n", false, 0, false, 0.0},
{"G0 X-2.0\n", false, 0, false, 0.0},
{"M67 E1 Q0.0\n", false, 0, false, 0.0},
{"G0 X-3.0\n", false, 0, false, 0.0},
{"M68 E1 Q42.13\n", false, 0, false, 0.0},
{"M68 E1 Q-13.42\n", false, 0, false, 0.0},
{"M68 E1 Q0.0\n", false, 0, false, 0.0},
};
for (const auto &step : steps) {
if (step.digital_input) {
standalone::set_external_digital_input(0, step.digital_value);
}
if (step.analog_input) {
standalone::set_external_analog_input(0, step.analog_value);
}
ok &= execute_remap_mdi_command(interp, output, step.command);
output << "remap_mdi_parameter_5399=" << interp._setup.parameters[5399] << "\n";
output << "remap_mdi_parameter_100=" << interp._setup.parameters[100] << "\n";
}
output << "remap_mdi_canon_event_count=" << standalone::canon_events().size() << "\n";
for (const auto &event : standalone::canon_events()) {
output << "remap_mdi_canon_event=" << event << "\n";
}
ok &= std::fabs(interp._setup.parameters[5399] - -13.42) < 1e-9;
ok &= std::fabs(interp._setup.parameters[100] - -13.42) < 1e-9;
output << "remap_io_ngc_linuxcnc_mdi_sequence=" << (ok ? 1 : 0) << "\n";
unsetenv("INI_FILE_NAME");
return copy_result(output.str());
}
extern "C" {
EMSCRIPTEN_KEEPALIVE
char *lcinterp_run_remap_file(const char *path, const char *ini_path)
{
return run_remap_file_internal(path, ini_path, false, false);
}
EMSCRIPTEN_KEEPALIVE
char *lcinterp_run_remap_file_continue_on_error(const char *path, const char *ini_path)
{
return run_remap_file_internal(path, ini_path, true, true);
}
EMSCRIPTEN_KEEPALIVE
char *lcinterp_run_remap_io_mdi_sequence(const char *ini_path)
{
return run_remap_io_mdi_sequence_internal(ini_path);
}
EMSCRIPTEN_KEEPALIVE EMSCRIPTEN_KEEPALIVE
char *lcinterp_restore_parameters(const char *path) char *lcinterp_restore_parameters(const char *path)
{ {

View File

@@ -0,0 +1,280 @@
#define private public
#include "emc/rs274ngc/rs274ngc_interp.hh"
#undef private
#include <cctype>
#include <cstdio>
#include <cstring>
#include <filesystem>
#include <sstream>
#include <string>
#include <sys/stat.h>
#include "emc/ini/inifile.hh"
#include "canon_event_sink.hh"
#include "linuxcnc_machine_config.hh"
#include "linuxcnc_runtime_state.hh"
#include "tooldata.hh"
namespace standalone {
namespace {
constexpr int kDefaultAxisMask = Interp::AXIS_MASK_X | Interp::AXIS_MASK_Y | Interp::AXIS_MASK_Z;
constexpr int kMaxUserMDirs = 6;
std::string g_user_m_paths[USER_DEFINED_FUNCTION_NUM];
bool is_executable_file(const std::filesystem::path &path)
{
struct stat st {};
return stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode) && (st.st_mode & S_IXUSR);
}
std::filesystem::path resolve_machine_path(const std::filesystem::path &machine_dir, const std::string &entry)
{
std::filesystem::path path(entry);
if (path.is_relative()) {
path = machine_dir / path;
}
return path.lexically_normal();
}
void standalone_user_m_code(int num, double arg1, double arg2)
{
FINISH();
const int code = num + 100;
std::ostringstream event;
event << "USER_M_COMMAND code=M" << code
<< " path=" << g_user_m_paths[num]
<< " p=" << arg1
<< " q=" << arg2;
push_canon_event(event.str());
}
int axis_mask_for_letter(char letter)
{
switch (std::toupper(static_cast<unsigned char>(letter))) {
case 'X':
return Interp::AXIS_MASK_X;
case 'Y':
return Interp::AXIS_MASK_Y;
case 'Z':
return Interp::AXIS_MASK_Z;
case 'A':
return Interp::AXIS_MASK_A;
case 'B':
return Interp::AXIS_MASK_B;
case 'C':
return Interp::AXIS_MASK_C;
case 'U':
return Interp::AXIS_MASK_U;
case 'V':
return Interp::AXIS_MASK_V;
case 'W':
return Interp::AXIS_MASK_W;
default:
return 0;
}
}
} // namespace
int axis_mask_from_coordinates(const char *coordinates)
{
if (!coordinates || coordinates[0] == '\0') {
return kDefaultAxisMask;
}
int axis_mask = 0;
for (const char *cursor = coordinates; *cursor; ++cursor) {
axis_mask |= axis_mask_for_letter(*cursor);
}
return axis_mask == 0 ? kDefaultAxisMask : axis_mask;
}
int axis_mask_from_ini(const char *ini_path)
{
if (!ini_path || ini_path[0] == '\0') {
return kDefaultAxisMask;
}
linuxcnc::IniFile ini(ini_path);
if (!ini) {
return kDefaultAxisMask;
}
if (auto coordinates = ini.findString("COORDINATES", "TRAJ")) {
return axis_mask_from_coordinates(coordinates->c_str());
}
return kDefaultAxisMask;
}
void apply_axis_mask_to_interp(Interp &interp, int axis_mask)
{
if (axis_mask == 0) {
axis_mask = kDefaultAxisMask;
}
set_external_axis_mask(axis_mask);
std::memcpy(interp._readers, Interp::default_readers, sizeof(Interp::default_readers));
if (!(axis_mask & Interp::AXIS_MASK_X)) {
interp._readers[static_cast<int>('x')] = nullptr;
}
if (!(axis_mask & Interp::AXIS_MASK_Y)) {
interp._readers[static_cast<int>('y')] = nullptr;
}
if (!(axis_mask & Interp::AXIS_MASK_Z)) {
interp._readers[static_cast<int>('z')] = nullptr;
}
if (!(axis_mask & Interp::AXIS_MASK_A)) {
interp._readers[static_cast<int>('a')] = nullptr;
}
if (!(axis_mask & Interp::AXIS_MASK_B)) {
interp._readers[static_cast<int>('b')] = nullptr;
}
if (!(axis_mask & Interp::AXIS_MASK_C)) {
interp._readers[static_cast<int>('c')] = nullptr;
}
if (!(axis_mask & Interp::AXIS_MASK_U)) {
interp._readers[static_cast<int>('u')] = nullptr;
}
if (!(axis_mask & Interp::AXIS_MASK_V)) {
interp._readers[static_cast<int>('v')] = nullptr;
}
if (!(axis_mask & Interp::AXIS_MASK_W)) {
interp._readers[static_cast<int>('w')] = nullptr;
}
}
void apply_machine_axis_config(Interp &interp, const linuxcnc::IniFile &ini)
{
interp._setup.orient_offset = ini.findRealV("ORIENT_OFFSET", "RS274NGC", 0.0);
interp._setup.disable_fanuc_style_sub =
ini.findBoolV("DISABLE_FANUC_STYLE_SUB", "RS274NGC", false);
if (auto coordinates = ini.findString("COORDINATES", "TRAJ")) {
apply_axis_mask_to_interp(interp, axis_mask_from_coordinates(coordinates->c_str()));
return;
}
apply_axis_mask_to_interp(interp, kDefaultAxisMask);
}
bool apply_machine_axis_config(Interp &interp, const char *ini_path)
{
if (!ini_path || ini_path[0] == '\0') {
apply_axis_mask_to_interp(interp, kDefaultAxisMask);
return true;
}
linuxcnc::IniFile ini(ini_path);
if (!ini) {
apply_axis_mask_to_interp(interp, kDefaultAxisMask);
return false;
}
apply_machine_axis_config(interp, ini);
return true;
}
int load_tool_table_from_ini(Interp &interp, const linuxcnc::IniFile &ini, const char *machine_dir)
{
if (auto tool_table = ini.findString("TOOL_TABLE", "EMCIO")) {
const std::filesystem::path base_dir = (machine_dir && machine_dir[0] != '\0')
? std::filesystem::path(machine_dir)
: std::filesystem::path();
const std::filesystem::path tool_path = resolve_machine_path(base_dir, *tool_table);
const bool random_toolchanger = ini.findBoolV("RANDOM_TOOLCHANGER", "EMCIO", false);
interp._setup.random_toolchanger = random_toolchanger;
tooldata_init(random_toolchanger);
tooldata_set_db(DB_NOTUSED);
const int rc = tooldata_load(tool_path.c_str());
if (rc != 0) {
return rc;
}
return interp.load_tool_table();
}
return INTERP_OK;
}
int load_tool_table_from_ini(Interp &interp, const char *ini_path)
{
if (!ini_path || ini_path[0] == '\0') {
return INTERP_OK;
}
linuxcnc::IniFile ini(ini_path);
if (!ini) {
return -1;
}
const std::filesystem::path machine_dir = std::filesystem::path(ini_path).parent_path();
return load_tool_table_from_ini(interp, ini, machine_dir.c_str());
}
void reset_user_m_code_registry()
{
for (int index = 0; index < USER_DEFINED_FUNCTION_NUM; ++index) {
USER_DEFINED_FUNCTION[index] = nullptr;
g_user_m_paths[index].clear();
}
}
int register_user_m_codes_from_ini(const linuxcnc::IniFile &ini, const char *machine_dir)
{
std::filesystem::path base_dir = (machine_dir && machine_dir[0] != '\0')
? std::filesystem::path(machine_dir)
: std::filesystem::current_path();
std::string program_prefix = "nc_files";
if (auto prefix = ini.findString("PROGRAM_PREFIX", "DISPLAY")) {
program_prefix = *prefix;
}
std::filesystem::path dirs[kMaxUserMDirs];
int dir_count = 0;
dirs[dir_count++] = resolve_machine_path(base_dir, program_prefix);
if (auto user_m_path = ini.findString("USER_M_PATH", "RS274NGC")) {
std::istringstream entries(*user_m_path);
std::string entry;
while (std::getline(entries, entry, ':') && dir_count < kMaxUserMDirs) {
if (!entry.empty()) {
dirs[dir_count++] = resolve_machine_path(base_dir, entry);
}
}
}
int registered = 0;
for (int num = 0; num < USER_DEFINED_FUNCTION_NUM; ++num) {
for (int dir_index = 0; dir_index < dir_count; ++dir_index) {
char file_name[8] = {0};
std::snprintf(file_name, sizeof(file_name), "M1%02d", num);
std::filesystem::path candidate = dirs[dir_index] / file_name;
if (!is_executable_file(candidate)) {
continue;
}
USER_DEFINED_FUNCTION_ADD(standalone_user_m_code, num);
g_user_m_paths[num] = candidate.string();
++registered;
break;
}
}
return registered;
}
int register_user_m_codes_from_ini(const char *ini_path)
{
if (!ini_path || ini_path[0] == '\0') {
return 0;
}
linuxcnc::IniFile ini(ini_path);
if (!ini) {
return 0;
}
const std::filesystem::path machine_dir = std::filesystem::path(ini_path).parent_path();
return register_user_m_codes_from_ini(ini, machine_dir.c_str());
}
} // namespace standalone

View File

@@ -0,0 +1,22 @@
#pragma once
class Interp;
namespace linuxcnc {
class IniFile;
}
namespace standalone {
int axis_mask_from_coordinates(const char *coordinates);
int axis_mask_from_ini(const char *ini_path);
void apply_axis_mask_to_interp(Interp &interp, int axis_mask);
void apply_machine_axis_config(Interp &interp, const linuxcnc::IniFile &ini);
bool apply_machine_axis_config(Interp &interp, const char *ini_path);
int load_tool_table_from_ini(Interp &interp, const linuxcnc::IniFile &ini, const char *machine_dir);
int load_tool_table_from_ini(Interp &interp, const char *ini_path);
void reset_user_m_code_registry();
int register_user_m_codes_from_ini(const linuxcnc::IniFile &ini, const char *machine_dir);
int register_user_m_codes_from_ini(const char *ini_path);
} // namespace standalone

View File

@@ -0,0 +1,9 @@
#pragma once
namespace standalone {
void reset_external_axis_mask();
void set_external_axis_mask(int axis_mask);
int external_axis_mask();
} // namespace standalone

View File

@@ -39,7 +39,11 @@ vendored LinuxCNC RS274NGC sources and exposes:
- `runProgramWithIni(programText, iniPath)` - `runProgramWithIni(programText, iniPath)`
- `runFile(path)` - `runFile(path)`
- `runFileWithIni(path, iniPath)` - `runFileWithIni(path, iniPath)`
- `runFileWithIniContinueOnError(path, iniPath)`
- `runSimConfigProgram({ iniPath, programPath, files, executionMode })`
- `runFiveAxisRemapFile(path, iniPath)` - `runFiveAxisRemapFile(path, iniPath)`
- `runRemapFile(path, iniPath)`
- `runRemapIoMdiSequence(iniPath)`
- `restoreParameters(path)` - `restoreParameters(path)`
- `saveParameters(path, values)` - `saveParameters(path, values)`
- `loadToolTable(path, options)` - `loadToolTable(path, options)`
@@ -55,3 +59,33 @@ LinuxCNC random-toolchanger branch before calling vendored
sample-machine remap validation. Callers provide INI/remap/demo files in the sample-machine remap validation. Callers provide INI/remap/demo files in the
WASM filesystem; the C ABI runs vendored LinuxCNC `REMAP`, O-word, file WASM filesystem; the C ABI runs vendored LinuxCNC `REMAP`, O-word, file
execution, and HAL adapter paths. execution, and HAL adapter paths.
`runRemapFile()` is the generic remap-test equivalent for vendored LinuxCNC
tests such as `tests/remap/duplicate-o-word`. It reads LinuxCNC INI
`SUBROUTINE_PATH` and `REMAP` entries, then calls vendored LinuxCNC
`Interp::parse_remap()`, `open()`, `read()`, and `execute()`.
`runRemapIoMdiSequence()` is a narrow regression boundary for the NGC-only
branch of vendored LinuxCNC `tests/remap/remap-io/test-ngc.ini`. The C ABI
reads LinuxCNC `REMAP` entries, then feeds the upstream test driver's MDI
command sequence into vendored `Interp::execute()`. JavaScript only copies the
vendored INI/subroutine files into the WASM filesystem and forwards the INI
path; it does not implement M62-M68, M66 input, or remap semantics.
`runFileWithIni()` is the plain interpreter-file equivalent for upstream tests
that need LinuxCNC INI search paths, such as
`tests/interp/sub-call-from-sub`. The C ABI reads LinuxCNC
`SUBROUTINE_PATH` and then calls vendored `Interp::open()`, `read()`, and
`execute()`; the SDK only copies files and forwards paths.
`runSimConfigProgram()` is a convenience host boundary for representative
LinuxCNC `configs/sim` programs. Callers provide `{ path, text, executable }`
file entries, an INI path, and a program path. The SDK writes those text files
into the Emscripten filesystem, applies executable bits for user M-code files,
then forwards to `runFileWithIni()` by default or to `runFiveAxisRemapFile()`
when `executionMode: "fiveAxisRemap"` is explicitly requested.
`runFileWithIniContinueOnError()` uses the same LinuxCNC-backed file execution
path but keeps the runner loop going after LinuxCNC reports an error, matching
upstream `rs274 -n 0` regression tests such as `tests/interp/oword-unwind`.
It does not implement or reinterpret LinuxCNC error semantics.

View File

@@ -58,6 +58,34 @@ export async function createLinuxCncInterpSdk(moduleOptions = {}) {
return mod.FS.readFile(path, { encoding: "utf8" }); return mod.FS.readFile(path, { encoding: "utf8" });
}, },
runSimConfigProgram({
iniPath,
programPath,
files = [],
executionMode = "fileWithIni",
}) {
for (const file of files) {
ensureParentPath(mod, file.path);
mod.FS.writeFile(file.path, file.text, { encoding: "utf8" });
if (file.executable) {
mod.FS.chmod(file.path, 0o755);
}
}
if (executionMode === "fiveAxisRemap") {
return callStringResult(
mod,
"lcinterp_run_fiveaxis_remap_file",
programPath,
iniPath,
);
}
if (executionMode === "fileWithIni") {
return callStringResult(mod, "lcinterp_run_file_with_ini", programPath, iniPath);
}
throw new Error(`unsupported sim config execution mode: ${executionMode}`);
},
runProgram(programText) { runProgram(programText) {
return callStringResult(mod, "lcinterp_run_program", programText); return callStringResult(mod, "lcinterp_run_program", programText);
}, },
@@ -74,10 +102,31 @@ export async function createLinuxCncInterpSdk(moduleOptions = {}) {
return callStringResult(mod, "lcinterp_run_file_with_ini", path, iniPath); return callStringResult(mod, "lcinterp_run_file_with_ini", path, iniPath);
}, },
runFileWithIniContinueOnError(path, iniPath) {
return callStringResult(
mod,
"lcinterp_run_file_with_ini_continue_on_error",
path,
iniPath,
);
},
runFiveAxisRemapFile(path, iniPath) { runFiveAxisRemapFile(path, iniPath) {
return callStringResult(mod, "lcinterp_run_fiveaxis_remap_file", path, iniPath); return callStringResult(mod, "lcinterp_run_fiveaxis_remap_file", path, iniPath);
}, },
runRemapFile(path, iniPath) {
return callStringResult(mod, "lcinterp_run_remap_file", path, iniPath);
},
runRemapFileContinueOnError(path, iniPath) {
return callStringResult(mod, "lcinterp_run_remap_file_continue_on_error", path, iniPath);
},
runRemapIoMdiSequence(iniPath) {
return callStringResult(mod, "lcinterp_run_remap_io_mdi_sequence", iniPath);
},
restoreParameters(path) { restoreParameters(path) {
return callStringResult(mod, "lcinterp_restore_parameters", path); return callStringResult(mod, "lcinterp_restore_parameters", path);
}, },

View File

@@ -67,6 +67,22 @@ const eventCountNode = document.getElementById("event-count");
const wasmBadge = document.getElementById("wasm-badge"); const wasmBadge = document.getElementById("wasm-badge");
const interpBadge = document.getElementById("interp-badge"); const interpBadge = document.getElementById("interp-badge");
const opfsBadge = document.getElementById("opfs-badge"); const opfsBadge = document.getElementById("opfs-badge");
const runProgressNode = document.getElementById("run-progress");
const runProgressLabelNode = document.getElementById("run-progress-label");
const runMotionNode = document.getElementById("run-motion");
const runLineNode = document.getElementById("run-line");
const runStatementNode = document.getElementById("run-statement");
const axisNodes = {
x: document.getElementById("axis-x"),
y: document.getElementById("axis-y"),
z: document.getElementById("axis-z"),
a: document.getElementById("axis-a"),
b: document.getElementById("axis-b"),
c: document.getElementById("axis-c"),
u: document.getElementById("axis-u"),
v: document.getElementById("axis-v"),
w: document.getElementById("axis-w"),
};
const fields = { const fields = {
machine: document.getElementById("field-machine"), machine: document.getElementById("field-machine"),
@@ -88,6 +104,7 @@ let iniSdk = null;
let interpSdk = null; let interpSdk = null;
let loadedSession = null; let loadedSession = null;
let canonicalEventText = ""; let canonicalEventText = "";
let runPlaybackTimer = null;
function setBadge(node, text, className = "badge") { function setBadge(node, text, className = "badge") {
node.className = className; node.className = className;
@@ -119,6 +136,109 @@ function setField(name, value) {
fields[name].textContent = value ?? "-"; fields[name].textContent = value ?? "-";
} }
function nextFrame() {
return new Promise((resolve) => requestAnimationFrame(resolve));
}
function formatAxisValue(value) {
return Number.isFinite(value) ? value.toFixed(3) : "0.000";
}
function clearRunPlayback() {
if (runPlaybackTimer) {
clearInterval(runPlaybackTimer);
runPlaybackTimer = null;
}
}
function setRunMonitor(progress, label, axes = {}, motionText = "idle", lineText = "line -", statement = "-") {
runProgressNode.value = Math.max(0, Math.min(100, progress));
runProgressLabelNode.textContent = label;
runMotionNode.textContent = motionText;
runLineNode.textContent = lineText;
runStatementNode.textContent = statement;
for (const [axis, node] of Object.entries(axisNodes)) {
node.textContent = formatAxisValue(axes[axis] ?? 0);
}
}
function readCanonicalNumber(line, name) {
const match = line.match(new RegExp(`(?:^| )${name}=([-+0-9.eE]+)`));
return match ? Number(match[1]) : null;
}
function programLineMap(programText) {
const lines = new Map();
programText.split(/\r?\n/).forEach((line, index) => {
lines.set(index + 1, line.trim() || "(blank)");
});
return lines;
}
function parseRunMotion(resultText, programText) {
const axes = { x: 0, y: 0, z: 0, a: 0, b: 0, c: 0, u: 0, v: 0, w: 0 };
const snapshots = [];
const sourceLines = programLineMap(programText);
for (const line of resultText.split("\n")) {
const motion = line.match(/^canon_event=(STRAIGHT_TRAVERSE|STRAIGHT_FEED|ARC_FEED)\b/);
if (!motion) {
continue;
}
for (const axis of Object.keys(axes)) {
const value = readCanonicalNumber(line, axis);
if (value !== null && Number.isFinite(value)) {
axes[axis] = value;
}
}
const sourceLine = readCanonicalNumber(line, "line");
snapshots.push({
type: motion[1],
line: sourceLine,
statement: Number.isFinite(sourceLine) ? (sourceLines.get(sourceLine) ?? "-") : "-",
axes: { ...axes },
});
}
return snapshots;
}
function showRunSnapshot(snapshot, index, total) {
const progress = total > 0 ? Math.round(((index + 1) / total) * 100) : 0;
const lineText = Number.isFinite(snapshot.line) ? `line ${snapshot.line}` : "line -";
setRunMonitor(
progress,
`${progress}%`,
snapshot.axes,
`${snapshot.type} ${lineText} (${index + 1}/${total})`,
lineText,
snapshot.statement,
);
}
function playRunMotion(resultText, programText) {
clearRunPlayback();
const snapshots = parseRunMotion(resultText, programText);
if (snapshots.length === 0) {
setRunMonitor(100, "100%", {}, "complete; no axis motion");
return;
}
let index = 0;
showRunSnapshot(snapshots[index], index, snapshots.length);
runPlaybackTimer = setInterval(() => {
index += 1;
if (index >= snapshots.length) {
clearRunPlayback();
showRunSnapshot(snapshots[snapshots.length - 1], snapshots.length - 1, snapshots.length);
return;
}
showRunSnapshot(snapshots[index], index, snapshots.length);
}, 220);
}
function requireIniSdk() { function requireIniSdk() {
if (!iniSdk) { if (!iniSdk) {
throw new Error("INI WASM module is not ready yet."); throw new Error("INI WASM module is not ready yet.");
@@ -287,10 +407,15 @@ document.getElementById("run-gcode").addEventListener("click", async () => {
const interp = requireInterpSdk(); const interp = requireInterpSdk();
const programText = await loadGcodeProgram(GCODE_FILENAME); const programText = await loadGcodeProgram(GCODE_FILENAME);
interp.writeTextFile(GCODE_WASM_FILE, programText); interp.writeTextFile(GCODE_WASM_FILE, programText);
clearRunPlayback();
setField("runStatus", "running");
setRunMonitor(5, "running", {}, "running");
await nextFrame();
const result = interp.runFileWithIni(GCODE_WASM_FILE, loadedSession.ini.wasmPath); const result = interp.runFileWithIni(GCODE_WASM_FILE, loadedSession.ini.wasmPath);
setField("sessionGcode", GCODE_WASM_FILE); setField("sessionGcode", GCODE_WASM_FILE);
setField("runStatus", "ok"); setField("runStatus", "ok");
setCanonicalEvents(result.trim()); setCanonicalEvents(result.trim());
playRunMotion(result.trim(), programText);
setLog( setLog(
[ [
"Ran G-code through LinuxCNC interpreter WASM.", "Ran G-code through LinuxCNC interpreter WASM.",
@@ -302,6 +427,8 @@ document.getElementById("run-gcode").addEventListener("click", async () => {
} catch (error) { } catch (error) {
setField("runStatus", "failed"); setField("runStatus", "failed");
setCanonicalEvents(""); setCanonicalEvents("");
clearRunPlayback();
setRunMonitor(0, "failed", {}, "failed");
setLog(`G-code run failed: ${error.message}`, true); setLog(`G-code run failed: ${error.message}`, true);
} }
}); });

View File

@@ -123,6 +123,53 @@
font: 13px/1.5 "SFMono-Regular", "Consolas", monospace; font: 13px/1.5 "SFMono-Regular", "Consolas", monospace;
white-space: pre-wrap; white-space: pre-wrap;
} }
.run-monitor {
border: 1px solid var(--border);
border-radius: 6px;
background: #fbfdff;
padding: 12px;
display: grid;
gap: 10px;
}
.progress-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 10px;
align-items: center;
}
progress {
width: 100%;
height: 14px;
accent-color: var(--accent-2);
}
.run-readout {
font: 13px/1.4 "SFMono-Regular", "Consolas", monospace;
color: var(--muted);
white-space: nowrap;
}
.axis-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
}
.axis-cell {
border: 1px solid #edf2f6;
border-radius: 6px;
padding: 8px;
display: grid;
gap: 4px;
min-width: 0;
}
.axis-cell span:first-child {
color: var(--muted);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.04em;
}
.axis-cell span:last-child {
font: 14px/1.2 "SFMono-Regular", "Consolas", monospace;
overflow-wrap: anywhere;
}
.events { .events {
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 6px; border-radius: 6px;
@@ -154,6 +201,11 @@
.grid { grid-template-columns: 1fr; } .grid { grid-template-columns: 1fr; }
textarea { min-height: 320px; } textarea { min-height: 320px; }
} }
@media (max-width: 560px) {
.axis-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.progress-row { grid-template-columns: 1fr; }
.run-readout { white-space: normal; }
}
</style> </style>
</head> </head>
<body> <body>
@@ -226,6 +278,28 @@
<dd id="field-fiveaxis-remap">-</dd> <dd id="field-fiveaxis-remap">-</dd>
</dl> </dl>
<h2>Run Monitor</h2>
<div class="run-monitor">
<div class="progress-row">
<progress id="run-progress" max="100" value="0"></progress>
<span id="run-progress-label" class="run-readout">0%</span>
</div>
<div id="run-motion" class="run-readout">idle</div>
<div id="run-line" class="run-readout">line -</div>
<div id="run-statement" class="run-readout">-</div>
<div class="axis-grid" id="axis-grid">
<div class="axis-cell"><span>X</span><span id="axis-x">0.000</span></div>
<div class="axis-cell"><span>Y</span><span id="axis-y">0.000</span></div>
<div class="axis-cell"><span>Z</span><span id="axis-z">0.000</span></div>
<div class="axis-cell"><span>A</span><span id="axis-a">0.000</span></div>
<div class="axis-cell"><span>B</span><span id="axis-b">0.000</span></div>
<div class="axis-cell"><span>C</span><span id="axis-c">0.000</span></div>
<div class="axis-cell"><span>U</span><span id="axis-u">0.000</span></div>
<div class="axis-cell"><span>V</span><span id="axis-v">0.000</span></div>
<div class="axis-cell"><span>W</span><span id="axis-w">0.000</span></div>
</div>
</div>
<div class="log" id="log"></div> <div class="log" id="log"></div>
<h2>Canonical Events</h2> <h2>Canonical Events</h2>

View File

@@ -344,6 +344,24 @@ TOOL_TABLE = browser-tool.tbl
) { ) {
throw new Error(`UI canonical event panel missing LinuxCNC events: ${eventText}`); throw new Error(`UI canonical event panel missing LinuxCNC events: ${eventText}`);
} }
await waitFor(
() => uiDocument.getElementById("run-motion")?.textContent.includes("STRAIGHT_FEED"),
"G-code run motion playback",
);
assertEqual(
uiDocument.getElementById("run-progress-label").textContent,
"100%",
"UI G-code run progress",
);
assertEqual(uiDocument.getElementById("axis-x").textContent, "3.000", "UI G-code X axis");
assertEqual(uiDocument.getElementById("axis-y").textContent, "4.000", "UI G-code Y axis");
assertEqual(uiDocument.getElementById("axis-z").textContent, "0.000", "UI G-code Z axis");
assertEqual(uiDocument.getElementById("run-line").textContent, "line 2", "UI G-code line");
assertEqual(
uiDocument.getElementById("run-statement").textContent,
"G1 X3.0 Y4.0 F120.0",
"UI G-code statement",
);
const eventFilter = uiDocument.getElementById("event-filter"); const eventFilter = uiDocument.getElementById("event-filter");
eventFilter.value = "STRAIGHT_FEED"; eventFilter.value = "STRAIGHT_FEED";
eventFilter.dispatchEvent(new Event("input", { bubbles: true })); eventFilter.dispatchEvent(new Event("input", { bubbles: true }));

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
canon_event=USE_TOOL_LENGTH_OFFSET x=0 y=0 z=6.25 a=0 b=0 c=0 u=0 v=0 w=0
canon_event=PROGRAM_END

View File

@@ -0,0 +1,4 @@
canon_event=SELECT_PLANE plane=1
canon_event=STRAIGHT_TRAVERSE line=1 x=0 y=0 z=0 a=0 b=0 c=0 u=0 v=0 w=0
canon_event=ORIENT_SPINDLE spindle=0 orientation=57.5 mode=2
canon_event=WAIT_SPINDLE_ORIENT_COMPLETE spindle=0 timeout=1.5

View File

@@ -0,0 +1,4 @@
M98 P1
O1
M99
M2

View File

@@ -0,0 +1,2 @@
G43 H2
M2

View File

@@ -0,0 +1,2 @@
G90 G17 G0 X0 Y0 Z0
M19 R45 P2 Q1.5

View File

@@ -0,0 +1,5 @@
[RS274NGC]
DISABLE_FANUC_STYLE_SUB = 1
[TRAJ]
COORDINATES = XYZ

View File

@@ -0,0 +1,6 @@
[TRAJ]
LINEAR_UNITS = mm
[EMCIO]
TOOL_TABLE = ini_tool_table.tbl
RANDOM_TOOLCHANGER = 0

View File

@@ -0,0 +1 @@
T2 P7 Z6.25 D1.5 ;ini-driven tool table entry

View File

@@ -0,0 +1,5 @@
[TRAJ]
COORDINATES = XYZ
[RS274NGC]
ORIENT_OFFSET = 12.5

View File

@@ -71,5 +71,6 @@ export const INTERP_BROWSER_FILE_FIXTURES = INTERP_FILE_FIXTURES;
export const INTERP_INI_FIXTURE = "namedparam_semantics"; export const INTERP_INI_FIXTURE = "namedparam_semantics";
export const INTERP_BASELINE_INI_FIXTURE = "namedparam_ini_semantics"; export const INTERP_BASELINE_INI_FIXTURE = "namedparam_ini_semantics";
export const INTERP_SPINDLE_ORIENT_OFFSET_FIXTURE = "spindle_orient_offset";
export const INTERP_COORDINATE_OFFSETS_FILE_FIXTURE = "coordinate_offsets"; export const INTERP_COORDINATE_OFFSETS_FILE_FIXTURE = "coordinate_offsets";
export const INTERP_POSITION_PARAMS_FILE_FIXTURE = "position_params"; export const INTERP_POSITION_PARAMS_FILE_FIXTURE = "position_params";

View File

@@ -8,6 +8,8 @@ ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
"$ROOT_DIR/tools/build_tp_wasm.sh" "$ROOT_DIR/tools/build_tp_wasm.sh"
SKIP_INI_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_ini_wasm.sh" SKIP_INI_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_ini_wasm.sh"
SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_interp_wasm.sh" SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_interp_wasm.sh"
SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_sim_configs_wasm.sh"
SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_nc_files_wasm.sh"
SKIP_TP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_tp_wasm.sh" SKIP_TP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_tp_wasm.sh"
"$ROOT_DIR/tests/opfs/node/verify_file_service.sh" "$ROOT_DIR/tests/opfs/node/verify_file_service.sh"
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_ini_panel_browser.sh" SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_ini_panel_browser.sh"

View File

@@ -8,12 +8,59 @@ CANON_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/canon"
GCODE_ERROR_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/gcode_errors" GCODE_ERROR_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/gcode_errors"
CANON_ERROR_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/canon_errors" CANON_ERROR_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/canon_errors"
NAMEDPARAM_INI_FIXTURE="$ROOT_DIR/tests/fixtures/ini/namedparams.ini" NAMEDPARAM_INI_FIXTURE="$ROOT_DIR/tests/fixtures/ini/namedparams.ini"
SPINDLE_ORIENT_OFFSET_INI_FIXTURE="$ROOT_DIR/tests/fixtures/ini/spindle_orient_offset.ini"
DISABLE_FANUC_STYLE_SUB_INI_FIXTURE="$ROOT_DIR/tests/fixtures/ini/disable_fanuc_style_sub.ini"
VALIDATION_CACHE_KEY_FILE="$BUILD_DIR/native-probe-validation.input.sha256"
VALIDATION_CACHE_OK_FILE="$BUILD_DIR/native-probe-validation.ok"
"$ROOT_DIR/tools/verify_upstream_baseline.sh" "$ROOT_DIR/tools/verify_upstream_baseline.sh"
"$ROOT_DIR/tools/verify_vendor_sync.sh" "$ROOT_DIR/tools/verify_vendor_sync.sh"
"$ROOT_DIR/tools/verify_no_standalone_cnc_semantics.sh" "$ROOT_DIR/tools/verify_no_standalone_cnc_semantics.sh"
"$ROOT_DIR/tools/verify_native_linuxcnc_fixture_baseline.sh" "$ROOT_DIR/tools/verify_native_linuxcnc_fixture_baseline.sh"
"$ROOT_DIR/tools/build_native_probes.sh" "$ROOT_DIR/tools/build_native_probes.sh"
"$ROOT_DIR/tests/native/verify_sim_configs.sh"
"$ROOT_DIR/tests/native/verify_nc_files.sh"
hash_tree() {
find "$@" -type f -print0 | sort -z | xargs -0 -r sha256sum
}
hash_build_validation_inputs() {
find "$BUILD_DIR" -maxdepth 1 -type f \
\( \
-name '*.exitcode' \
-o -name '*.run.stdout.log' \
-o -name '*.run.stderr.log' \
-o -name 'source-probes.tsv' \
-o -name 'native-probes.ok' \
-o -name 'linuxcnc_interp_minimal_harness' \
\) \
! -name 'native-probe-validation.*' \
-print0 | sort -z | xargs -0 -r sha256sum
}
native_probe_validation_fingerprint() {
{
sha256sum \
"$ROOT_DIR/tests/native/verify_native_probes.sh" \
"$ROOT_DIR/tools/source-manifest.txt"
hash_tree \
"$GCODE_FIXTURE_DIR" \
"$CANON_FIXTURE_DIR" \
"$GCODE_ERROR_FIXTURE_DIR" \
"$CANON_ERROR_FIXTURE_DIR" \
"$ROOT_DIR/tests/fixtures/ini"
hash_build_validation_inputs
} | sha256sum | awk '{ print $1 }'
}
NATIVE_PROBE_VALIDATION_FINGERPRINT="$(native_probe_validation_fingerprint)"
if [[ -f "$VALIDATION_CACHE_KEY_FILE" && -f "$VALIDATION_CACHE_OK_FILE" ]] &&
[[ "$(tr -d '[:space:]' < "$VALIDATION_CACHE_KEY_FILE")" == "$NATIVE_PROBE_VALIDATION_FINGERPRINT" ]] &&
[[ "$(tr -d '[:space:]' < "$VALIDATION_CACHE_OK_FILE")" == "$NATIVE_PROBE_VALIDATION_FINGERPRINT" ]]; then
echo "native probe validation up to date"
exit 0
fi
check_source_probe_coverage() { check_source_probe_coverage() {
local manifest_sources="$BUILD_DIR/source-manifest-sources.sorted" local manifest_sources="$BUILD_DIR/source-manifest-sources.sorted"
@@ -155,6 +202,40 @@ check_exitcode linuxcnc_namedparam_harness
check_exitcode linuxcnc_namedparam_harness.run check_exitcode linuxcnc_namedparam_harness.run
check_exitcode linuxcnc_interp_minimal_harness check_exitcode linuxcnc_interp_minimal_harness
check_exitcode linuxcnc_interp_minimal_harness.run check_exitcode linuxcnc_interp_minimal_harness.run
check_exitcode linuxcnc_interp_minimal_harness.do_while_break.run
check_exitcode linuxcnc_interp_minimal_harness.oword_bug315.run
check_exitcode linuxcnc_interp_minimal_harness.oword_bug315_p2.run
check_exitcode linuxcnc_interp_minimal_harness.interp_exists.run
check_exitcode linuxcnc_interp_minimal_harness.return_value.run
check_exitcode linuxcnc_interp_minimal_harness.subs_follow_main.run
check_exitcode linuxcnc_interp_minimal_harness.fractional_linenumbers.run
check_exitcode linuxcnc_interp_minimal_harness.namedparam_bug424.run
check_exitcode linuxcnc_interp_minimal_harness.g6164.run
check_exitcode linuxcnc_interp_minimal_harness.rotation_abs_pts.run
check_exitcode linuxcnc_interp_minimal_harness.rotation_g28.run
check_exitcode linuxcnc_interp_minimal_harness.rotation_g53.run
check_exitcode linuxcnc_interp_minimal_harness.iniparam.run
check_exitcode linuxcnc_interp_minimal_harness.iniparam_failassign.run
check_exitcode linuxcnc_interp_minimal_harness.sub_call_from_sub.run
check_exitcode linuxcnc_interp_minimal_harness.sequence_number.run
check_exitcode linuxcnc_interp_minimal_harness.nested_sub_error.run
check_exitcode linuxcnc_interp_minimal_harness.nested_sub_in_file_error.run
check_exitcode linuxcnc_interp_minimal_harness.oword_unwind.run
check_exitcode linuxcnc_interp_minimal_harness.abort_hot_comment.run
check_exitcode linuxcnc_interp_minimal_harness.m19.run
check_exitcode linuxcnc_interp_minimal_harness.magic_comments_param_format.run
check_exitcode linuxcnc_interp_minimal_harness.m98m99_01_basics.run
check_exitcode linuxcnc_interp_minimal_harness.m98m99_02_variables.run
check_exitcode linuxcnc_interp_minimal_harness.m98m99_07_nested_subs.run
check_exitcode linuxcnc_interp_minimal_harness.m98m99_08_sub_follows_main.run
check_exitcode linuxcnc_interp_minimal_harness.m98m99_10_m98_p001.run
check_exitcode linuxcnc_interp_minimal_harness.m98m99_13_named_program_named.run
check_exitcode linuxcnc_interp_minimal_harness.m98m99_13_named_program_numbered.run
check_exitcode linuxcnc_interp_minimal_harness.m98m99_14_o_expression_call.run
check_exitcode linuxcnc_interp_minimal_harness.sim_axis_foam.run
check_exitcode linuxcnc_interp_minimal_harness.sim_bridgemill.run
check_exitcode linuxcnc_interp_minimal_harness.sim_geometry_xyzc.run
check_exitcode linuxcnc_interp_minimal_harness.sim_external_offsets_dyn.run
check_exitcode linuxcnc_parameter_file_harness check_exitcode linuxcnc_parameter_file_harness
check_exitcode linuxcnc_parameter_file_harness.run check_exitcode linuxcnc_parameter_file_harness.run
check_exitcode linuxcnc_interp_init_harness check_exitcode linuxcnc_interp_init_harness
@@ -165,6 +246,8 @@ check_exitcode linuxcnc_remap_hal_sync_harness
check_exitcode linuxcnc_remap_hal_sync_harness.run check_exitcode linuxcnc_remap_hal_sync_harness.run
check_exitcode linuxcnc_5axis_remap_execute_harness check_exitcode linuxcnc_5axis_remap_execute_harness
check_exitcode linuxcnc_5axis_remap_execute_harness.run check_exitcode linuxcnc_5axis_remap_execute_harness.run
check_exitcode linuxcnc_duplicate_oword_remap_harness
check_exitcode linuxcnc_duplicate_oword_remap_harness.run
check_exitcode linuxcnc_indexer_harness check_exitcode linuxcnc_indexer_harness
check_exitcode linuxcnc_indexer_harness.run check_exitcode linuxcnc_indexer_harness.run
check_exitcode linuxcnc_rs274_compile_probe check_exitcode linuxcnc_rs274_compile_probe
@@ -214,6 +297,29 @@ check_exitcode linuxcnc_interp_base_source_probe
check_exitcode linuxcnc_gomath_source_probe check_exitcode linuxcnc_gomath_source_probe
check_exitcode linuxcnc_tooldata_common_source_probe check_exitcode linuxcnc_tooldata_common_source_probe
INIPARAM_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.iniparam.run.stdout.log"
grep -Fq "file_open=0" "$INIPARAM_STDOUT"
grep -Fq "file_read_count=9" "$INIPARAM_STDOUT"
grep -Fq "file_execute_count=8" "$INIPARAM_STDOUT"
grep -Fq "file_saw_error=1" "$INIPARAM_STDOUT"
grep -Fq "file_error_text=Named parameter #<_ini[nosuchsection]nosuchname> not defined" "$INIPARAM_STDOUT"
grep -Fq "setup.current_x=10" "$INIPARAM_STDOUT"
grep -Fq "setup.current_y=20" "$INIPARAM_STDOUT"
grep -Fq "setup.current_z=30" "$INIPARAM_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=1 x=10 y=20 z=30" "$INIPARAM_STDOUT"
grep -Fq "canon_event=MESSAGE: position now: 10.000000 20.000000 30.000000" "$INIPARAM_STDOUT"
grep -Fq "canon_event=MESSAGE: not in INI: ######" "$INIPARAM_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$INIPARAM_STDOUT"
INIPARAM_FAILASSIGN_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.iniparam_failassign.run.stdout.log"
grep -Fq "file_open=0" "$INIPARAM_FAILASSIGN_STDOUT"
grep -Fq "file_saw_error=1" "$INIPARAM_FAILASSIGN_STDOUT"
grep -Fq "file_error_text=Cannot assign to read-only parameter #<_ini[vars]toolchange_x>" "$INIPARAM_FAILASSIGN_STDOUT"
if grep -Fq "canon_event=MESSAGE: notreached" "$INIPARAM_FAILASSIGN_STDOUT"; then
echo "iniparam-failassign unexpectedly reached post-assignment message" >&2
exit 1
fi
EMC_STATUS_STDOUT="$BUILD_DIR/linuxcnc_emc_status_probe.run.stdout.log" EMC_STATUS_STDOUT="$BUILD_DIR/linuxcnc_emc_status_probe.run.stdout.log"
grep -Fq "emc_status_default_linear_units=1" "$EMC_STATUS_STDOUT" grep -Fq "emc_status_default_linear_units=1" "$EMC_STATUS_STDOUT"
grep -Fq "emc_status_default_units_visible=1" "$EMC_STATUS_STDOUT" grep -Fq "emc_status_default_units_visible=1" "$EMC_STATUS_STDOUT"
@@ -248,39 +354,56 @@ grep -Fq "xyzbc_trt_switch_mcodes_from_linuxcnc=1" "$FIVEAXIS_REMAP_ASSET_STDOUT
grep -Fq "fiveaxis_remap_assets_from_linuxcnc=1" "$FIVEAXIS_REMAP_ASSET_STDOUT" grep -Fq "fiveaxis_remap_assets_from_linuxcnc=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
REMAP_PARSE_STDOUT="$BUILD_DIR/linuxcnc_remap_parse_harness.run.stdout.log" REMAP_PARSE_STDOUT="$BUILD_DIR/linuxcnc_remap_parse_harness.run.stdout.log"
check_remap_descriptor() {
local machine="$1"
local mcode="$2"
grep -Fq "${machine}_${mcode}_name_lookup=1" "$REMAP_PARSE_STDOUT"
grep -Fq "${machine}_${mcode}_mcode_lookup=1" "$REMAP_PARSE_STDOUT"
grep -Fq "${machine}_${mcode}_ngc=1" "$REMAP_PARSE_STDOUT"
grep -Fq "${machine}_${mcode}_modalgroup10=1" "$REMAP_PARSE_STDOUT"
grep -Fq "${machine}_${mcode}_no_python=1" "$REMAP_PARSE_STDOUT"
grep -Fq "${machine}_${mcode}_linuxcnc_mcode_remappable=1" "$REMAP_PARSE_STDOUT"
}
grep -Fq "xyzac_trt_ini_open=1" "$REMAP_PARSE_STDOUT" grep -Fq "xyzac_trt_ini_open=1" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzac_trt_parse_remap_1=0" "$REMAP_PARSE_STDOUT" grep -Fq "xyzac_trt_parse_remap_1=0" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzac_trt_parse_remap_2=0" "$REMAP_PARSE_STDOUT" grep -Fq "xyzac_trt_parse_remap_2=0" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzac_trt_parse_remap_3=0" "$REMAP_PARSE_STDOUT" grep -Fq "xyzac_trt_parse_remap_3=0" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzac_trt_parsed_remap_count=3" "$REMAP_PARSE_STDOUT" grep -Fq "xyzac_trt_parsed_remap_count=3" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzac_trt_M428_ngc=1" "$REMAP_PARSE_STDOUT" grep -Fq "xyzac_trt_m_remapped_count=3" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzac_trt_M429_ngc=1" "$REMAP_PARSE_STDOUT" check_remap_descriptor "xyzac_trt" "M428"
grep -Fq "xyzac_trt_M430_ngc=1" "$REMAP_PARSE_STDOUT" check_remap_descriptor "xyzac_trt" "M429"
check_remap_descriptor "xyzac_trt" "M430"
grep -Fq "xyzac_trt_linuxcnc_ngc_remaps_parsed=1" "$REMAP_PARSE_STDOUT" grep -Fq "xyzac_trt_linuxcnc_ngc_remaps_parsed=1" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzbc_trt_ini_open=1" "$REMAP_PARSE_STDOUT" grep -Fq "xyzbc_trt_ini_open=1" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzbc_trt_parse_remap_1=0" "$REMAP_PARSE_STDOUT" grep -Fq "xyzbc_trt_parse_remap_1=0" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzbc_trt_parse_remap_2=0" "$REMAP_PARSE_STDOUT" grep -Fq "xyzbc_trt_parse_remap_2=0" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzbc_trt_parse_remap_3=0" "$REMAP_PARSE_STDOUT" grep -Fq "xyzbc_trt_parse_remap_3=0" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzbc_trt_parsed_remap_count=3" "$REMAP_PARSE_STDOUT" grep -Fq "xyzbc_trt_parsed_remap_count=3" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzbc_trt_M428_ngc=1" "$REMAP_PARSE_STDOUT" grep -Fq "xyzbc_trt_m_remapped_count=3" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzbc_trt_M429_ngc=1" "$REMAP_PARSE_STDOUT" check_remap_descriptor "xyzbc_trt" "M428"
grep -Fq "xyzbc_trt_M430_ngc=1" "$REMAP_PARSE_STDOUT" check_remap_descriptor "xyzbc_trt" "M429"
check_remap_descriptor "xyzbc_trt" "M430"
grep -Fq "xyzbc_trt_linuxcnc_ngc_remaps_parsed=1" "$REMAP_PARSE_STDOUT" grep -Fq "xyzbc_trt_linuxcnc_ngc_remaps_parsed=1" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzab_tdr_ini_open=1" "$REMAP_PARSE_STDOUT" grep -Fq "xyzab_tdr_ini_open=1" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzab_tdr_parse_remap_1=0" "$REMAP_PARSE_STDOUT" grep -Fq "xyzab_tdr_parse_remap_1=0" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzab_tdr_parse_remap_2=0" "$REMAP_PARSE_STDOUT" grep -Fq "xyzab_tdr_parse_remap_2=0" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzab_tdr_parsed_remap_count=2" "$REMAP_PARSE_STDOUT" grep -Fq "xyzab_tdr_parsed_remap_count=2" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzab_tdr_M428_ngc=1" "$REMAP_PARSE_STDOUT" grep -Fq "xyzab_tdr_m_remapped_count=2" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzab_tdr_M429_ngc=1" "$REMAP_PARSE_STDOUT" check_remap_descriptor "xyzab_tdr" "M428"
check_remap_descriptor "xyzab_tdr" "M429"
grep -Fq "xyzab_tdr_linuxcnc_ngc_remaps_parsed=1" "$REMAP_PARSE_STDOUT" grep -Fq "xyzab_tdr_linuxcnc_ngc_remaps_parsed=1" "$REMAP_PARSE_STDOUT"
grep -Fq "bridgemill_ini_open=1" "$REMAP_PARSE_STDOUT" grep -Fq "bridgemill_ini_open=1" "$REMAP_PARSE_STDOUT"
grep -Fq "bridgemill_parse_remap_1=0" "$REMAP_PARSE_STDOUT" grep -Fq "bridgemill_parse_remap_1=0" "$REMAP_PARSE_STDOUT"
grep -Fq "bridgemill_parse_remap_2=0" "$REMAP_PARSE_STDOUT" grep -Fq "bridgemill_parse_remap_2=0" "$REMAP_PARSE_STDOUT"
grep -Fq "bridgemill_parse_remap_3=0" "$REMAP_PARSE_STDOUT" grep -Fq "bridgemill_parse_remap_3=0" "$REMAP_PARSE_STDOUT"
grep -Fq "bridgemill_parsed_remap_count=3" "$REMAP_PARSE_STDOUT" grep -Fq "bridgemill_parsed_remap_count=3" "$REMAP_PARSE_STDOUT"
grep -Fq "bridgemill_M428_ngc=1" "$REMAP_PARSE_STDOUT" grep -Fq "bridgemill_m_remapped_count=3" "$REMAP_PARSE_STDOUT"
grep -Fq "bridgemill_M429_ngc=1" "$REMAP_PARSE_STDOUT" check_remap_descriptor "bridgemill" "M428"
grep -Fq "bridgemill_M430_ngc=1" "$REMAP_PARSE_STDOUT" check_remap_descriptor "bridgemill" "M429"
check_remap_descriptor "bridgemill" "M430"
grep -Fq "bridgemill_linuxcnc_ngc_remaps_parsed=1" "$REMAP_PARSE_STDOUT" grep -Fq "bridgemill_linuxcnc_ngc_remaps_parsed=1" "$REMAP_PARSE_STDOUT"
grep -Fq "fiveaxis_linuxcnc_remap_parse_path=1" "$REMAP_PARSE_STDOUT" grep -Fq "fiveaxis_linuxcnc_remap_parse_path=1" "$REMAP_PARSE_STDOUT"
@@ -363,6 +486,14 @@ grep -Fq "xyzac_trt_xyzac_switchkins_test_1_file_final_rc=1" "$FIVEAXIS_REMAP_EX
grep -Fq "xyzac_trt_xyzac_switchkins_test_1_file_reached_exit=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT" grep -Fq "xyzac_trt_xyzac_switchkins_test_1_file_reached_exit=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_1__hal[motion.switchkins-type]: rc=0 found=1 value=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT" grep -Fq "xyzac_trt_xyzac_switchkins_test_1__hal[motion.switchkins-type]: rc=0 found=1 value=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_1_linuxcnc_demo_file_execute=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT" grep -Fq "xyzac_trt_xyzac_switchkins_test_1_linuxcnc_demo_file_execute=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_2_file_open=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_2_file_read_count=120" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_2_file_execute_count=120" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_2_file_finish_count=3" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_2_file_final_rc=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_2_file_reached_exit=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_2__hal[motion.switchkins-type]: rc=0 found=1 value=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_2_linuxcnc_demo_file_execute=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_3_file_open=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT" grep -Fq "xyzac_trt_xyzac_switchkins_test_3_file_open=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_3_file_read_count=86" "$FIVEAXIS_REMAP_EXECUTE_STDOUT" grep -Fq "xyzac_trt_xyzac_switchkins_test_3_file_read_count=86" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_3_file_execute_count=86" "$FIVEAXIS_REMAP_EXECUTE_STDOUT" grep -Fq "xyzac_trt_xyzac_switchkins_test_3_file_execute_count=86" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
@@ -413,6 +544,164 @@ grep -Fq "bridgemill_bridgemill_5axisgui__hal[motion.switchkins-type]: rc=0 foun
grep -Fq "bridgemill_bridgemill_5axisgui_linuxcnc_demo_file_execute=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT" grep -Fq "bridgemill_bridgemill_5axisgui_linuxcnc_demo_file_execute=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "fiveaxis_linuxcnc_remap_execute_path=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT" grep -Fq "fiveaxis_linuxcnc_remap_execute_path=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
DUPLICATE_OWORD_REMAP_STDOUT="$BUILD_DIR/linuxcnc_duplicate_oword_remap_harness.run.stdout.log"
grep -Fq "duplicate_oword_ini_open=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_subroutine_path_count=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_parse_remap_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_parse_remap_2=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_parsed_remap_count=2" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_remaps_ready=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_file_open=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_file_read_count=31" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_file_execute_count=31" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_file_finish_count=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_file_final_rc=3" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_file_reached_endfile=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_canon_event=MESSAGE: executing m207: run remapped m208 before o<problem>" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_canon_event=MESSAGE: rm207 running remapped m208" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_canon_event=MESSAGE: rm208 starting and done" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_canon_event=FINISH" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_linuxcnc_remap_file_execute=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_linuxcnc_regression_path=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_0_ini_open=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_0_parse_remap_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_0_parsed_remap_count=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_0_file_open=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_0_file_saw_error=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_0_canon_event=MESSAGE: M400 call_level= 1.000000 remap_level=1.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_0_canon_event=MESSAGE: P word set: 47.110000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_0_canon_event=MESSAGE: Q word set: 8.150000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_0_canon_event=PROGRAM_END" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_0_linuxcnc_remap_file_execute=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_1_ini_open=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_1_parse_remap_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_1_file_execute_1=5" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_1_file_error_text=user-defined M400: missing: Q" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_1_file_saw_error=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_1_canon_event=PROGRAM_END" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_1_linuxcnc_remap_file_execute=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_2_ini_open=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_2_parse_remap_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_2_file_execute_1=5" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_2_file_error_text=user-defined M400: missing: P,Q" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_2_file_saw_error=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_2_canon_event=PROGRAM_END" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_2_linuxcnc_remap_file_execute=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_body_ngc_ini_open=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_body_ngc_parse_remap_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_body_ngc_file_read_5=5" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_body_ngc_file_error_text=Attempt to divide by zero" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_body_ngc_file_saw_error=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_body_ngc_canon_event=MESSAGE: before M400: call_level= 0.000000 remap_level=0.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_body_ngc_canon_event=MESSAGE: in rm400: call_level= 1.000000 remap_level=1.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_body_ngc_canon_event=MESSAGE: after failed M400: call_level= 0.000000 remap_level=0.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_body_ngc_canon_event=PROGRAM_END" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_body_ngc_linuxcnc_remap_file_execute=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_ini_open=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_subroutine_path_count=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_parse_remap_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_parsed_remap_count=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_remaps_ready=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_file_open=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_file_read_count=4" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_file_execute_count=4" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_file_finish_count=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_file_final_rc=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_file_reached_exit=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_canon_event=MESSAGE: m400 call_level=1.000000 remap_level=1.000000 line=2.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_canon_event=PROGRAM_END" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_linuxcnc_remap_file_execute=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_linuxcnc_regression_path=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_ini_open=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_subroutine_path_count=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_parse_remap_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_parse_remap_2=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_parse_remap_3=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_parse_remap_4=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_parsed_remap_count=4" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_remaps_ready=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_file_open=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_file_read_count=21" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_file_execute_count=21" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_file_finish_count=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_file_final_rc=3" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_file_reached_endfile=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_canon_event=MESSAGE: main call_level=0.000000 remap_level=0.000000 line=2.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_canon_event=MESSAGE: m400 handler pre call_level=1.000000 remap_level=1.000000 line=2.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_canon_event=MESSAGE: m401 handler pre call_level=2.000000 remap_level=2.000000 line=2.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_canon_event=MESSAGE: m402 handler pre call_level=3.000000 remap_level=3.000000 line=2.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_canon_event=MESSAGE: m403 handler pre call_level=4.000000 remap_level=4.000000 line=2.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_canon_event=MESSAGE: m400 handler post call_level=1.000000 remap_level=1.000000 line=4.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_canon_event=FINISH" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_linuxcnc_remap_file_execute=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_linuxcnc_regression_path=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_ini_open=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_subroutine_path_count=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_parse_remap_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_parsed_remap_count=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_remaps_ready=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_file_open=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_file_read_count=19" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_file_execute_count=19" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_file_finish_count=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_file_final_rc=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_file_reached_exit=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_canon_event=MESSAGE: in rg881: n_args=3.000000 [1.000000] [2.000000] [3.000000] [0.000000] [0.000000]" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_canon_event=MESSAGE: in rg881: n_args=4.000000 [1.000000] [2.000000] [3.000000] [4.000000] [0.000000]" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_canon_event=MESSAGE: in rg881: n_args=4.000000 [1.000000] [2.000000] [3.000000] [5.000000] [0.000000]" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_canon_event=MESSAGE: in rg881: n_args=5.000000 [1.000000] [2.000000] [3.000000] [4.000000] [5.000000]" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_canon_event=PROGRAM_END" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_linuxcnc_remap_file_execute=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_linuxcnc_regression_path=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_ini_open=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_subroutine_path_count=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_parse_remap_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_parse_remap_2=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_parse_remap_3=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_parse_remap_4=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_parse_remap_5=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_parse_remap_6=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_parse_remap_7=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_parsed_remap_count=7" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_remaps_ready=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_file_open=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_file_read_count=10084" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_file_execute_count=10084" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_file_final_rc=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_file_reached_exit=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_canon_event=MESSAGE: seq=1.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_canon_event=MESSAGE: seq=5.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_canon_event=MESSAGE: seq=6.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_canon_event=MESSAGE: seq=7.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_canon_event=MESSAGE: seq=8.000000 - reset to 1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_canon_event=MESSAGE: seq=8.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_canon_event=MESSAGE: seq=9.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_canon_event=MESSAGE: seq=10.000000 - reset to 1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_canon_event=PROGRAM_END" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_linuxcnc_remap_file_execute=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_linuxcnc_regression_path=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_ini_open=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_subroutine_path_count=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_parse_remap_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_parse_remap_7=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_parsed_remap_count=7" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_mdi_M66 P1_execute_0=2" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_mdi_M66 P1_execute_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_mdi_M66 E1 L0_execute_0=2" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_mdi_M66 E1 L0_execute_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_parameter_5399=42.13" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_parameter_5399=-13.42" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_canon_event=SET_MOTION_OUTPUT_BIT index=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_canon_event=CLEAR_MOTION_OUTPUT_BIT index=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_canon_event=SET_AUX_OUTPUT_BIT index=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_canon_event=CLEAR_AUX_OUTPUT_BIT index=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_canon_event=WAIT index=0 input_type=1 wait_type=0 timeout=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_canon_event=WAIT index=0 input_type=0 wait_type=0 timeout=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_canon_event=SET_MOTION_OUTPUT_VALUE index=0 value=42.13" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_canon_event=SET_AUX_OUTPUT_VALUE index=0 value=-13.42" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_linuxcnc_mdi_sequence=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "linuxcnc_upstream_remap_regression_path=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
NAMEDPARAM_STDOUT="$BUILD_DIR/linuxcnc_namedparam_harness.run.stdout.log" NAMEDPARAM_STDOUT="$BUILD_DIR/linuxcnc_namedparam_harness.run.stdout.log"
grep -Fq "init_named_parameters=0" "$NAMEDPARAM_STDOUT" grep -Fq "init_named_parameters=0" "$NAMEDPARAM_STDOUT"
grep -Fq "global_named_count=57" "$NAMEDPARAM_STDOUT" grep -Fq "global_named_count=57" "$NAMEDPARAM_STDOUT"
@@ -720,8 +1009,362 @@ check_fixture_output \
"$CANON_FIXTURE_DIR/minimal_linear.events" \ "$CANON_FIXTURE_DIR/minimal_linear.events" \
"$RUN_STDOUT" "$RUN_STDOUT"
DO_WHILE_BREAK_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.do_while_break.run.stdout.log"
grep -Fq "file_open=0" "$DO_WHILE_BREAK_STDOUT"
grep -Fq "file_read_count=30" "$DO_WHILE_BREAK_STDOUT"
grep -Fq "file_execute_count=30" "$DO_WHILE_BREAK_STDOUT"
grep -Fq "canon_event=MESSAGE: must-execute-outer" "$DO_WHILE_BREAK_STDOUT"
grep -Fq "canon_event=MESSAGE: must-execute-nested" "$DO_WHILE_BREAK_STDOUT"
grep -Fq "canon_event=MESSAGE: must-execute-inner" "$DO_WHILE_BREAK_STDOUT"
grep -Fq "canon_event=MESSAGE: post-inner-while" "$DO_WHILE_BREAK_STDOUT"
grep -Fq "canon_event=MESSAGE: post-nested-while" "$DO_WHILE_BREAK_STDOUT"
grep -Fq "canon_event=MESSAGE: post-outer-while" "$DO_WHILE_BREAK_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$DO_WHILE_BREAK_STDOUT"
if grep -Fq "canon_event=MESSAGE: do-not-execute" "$DO_WHILE_BREAK_STDOUT"; then
echo "unexpected do-while-break skipped branch event" >&2
exit 1
fi
OWORD_BUG315_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.oword_bug315.run.stdout.log"
grep -Fq "file_open=0" "$OWORD_BUG315_STDOUT"
grep -Fq "file_read_count=23" "$OWORD_BUG315_STDOUT"
grep -Fq "file_execute_count=23" "$OWORD_BUG315_STDOUT"
grep -Fq "canon_event=MESSAGE: running sub" "$OWORD_BUG315_STDOUT"
grep -Fq "canon_event=MESSAGE: breaking out of sub" "$OWORD_BUG315_STDOUT"
grep -Fq "canon_event=MESSAGE: done" "$OWORD_BUG315_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$OWORD_BUG315_STDOUT"
OWORD_BUG315_P2_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.oword_bug315_p2.run.stdout.log"
grep -Fq "file_open=0" "$OWORD_BUG315_P2_STDOUT"
grep -Fq "file_read_count=26" "$OWORD_BUG315_P2_STDOUT"
grep -Fq "file_execute_count=26" "$OWORD_BUG315_P2_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=7 x=2 y=2 z=2" "$OWORD_BUG315_P2_STDOUT"
grep -Fq "canon_event=MESSAGE: executing this one" "$OWORD_BUG315_P2_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$OWORD_BUG315_P2_STDOUT"
if grep -Fq "canon_event=MESSAGE: not executing this one" "$OWORD_BUG315_P2_STDOUT"; then
echo "unexpected oword-bug315-p2 skipped subroutine event" >&2
exit 1
fi
INTERP_EXISTS_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.interp_exists.run.stdout.log"
grep -Fq "file_open=0" "$INTERP_EXISTS_STDOUT"
grep -Fq "file_read_count=7" "$INTERP_EXISTS_STDOUT"
grep -Fq "file_execute_count=7" "$INTERP_EXISTS_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=2 x=1 y=0 z=1" "$INTERP_EXISTS_STDOUT"
grep -Fq "canon_event=COMMENT: comment" "$INTERP_EXISTS_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=6 x=1 y=0 z=0" "$INTERP_EXISTS_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$INTERP_EXISTS_STDOUT"
SIM_AXIS_FOAM_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.sim_axis_foam.run.stdout.log"
grep -Fq "file_open=0" "$SIM_AXIS_FOAM_STDOUT"
grep -Fq "file_read_count=23" "$SIM_AXIS_FOAM_STDOUT"
if grep -Fq "Bad character 'u' used" "$SIM_AXIS_FOAM_STDOUT"; then
echo "axis_foam INI did not enable U/V axis readers" >&2
exit 1
fi
SIM_BRIDGEMILL_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.sim_bridgemill.run.stdout.log"
grep -Fq "file_open=0" "$SIM_BRIDGEMILL_STDOUT"
if grep -Fq "Bad character 'w' used" "$SIM_BRIDGEMILL_STDOUT"; then
echo "bridgemill INI did not enable W axis reader" >&2
exit 1
fi
SIM_GEOMETRY_XYZC_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.sim_geometry_xyzc.run.stdout.log"
grep -Fq "file_open=0" "$SIM_GEOMETRY_XYZC_STDOUT"
grep -Fq "canon_event=USER_M_COMMAND code=M110" "$SIM_GEOMETRY_XYZC_STDOUT"
if grep -Fq "Unknown m code used: M110" "$SIM_GEOMETRY_XYZC_STDOUT"; then
echo "xyzc INI did not register USER_M_PATH M110" >&2
exit 1
fi
SIM_EXTERNAL_OFFSETS_DYN_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.sim_external_offsets_dyn.run.stdout.log"
grep -Fq "file_open=0" "$SIM_EXTERNAL_OFFSETS_DYN_STDOUT"
grep -Fq "canon_event=USER_M_COMMAND code=M111" "$SIM_EXTERNAL_OFFSETS_DYN_STDOUT"
if grep -Fq "Unknown m code used: M111" "$SIM_EXTERNAL_OFFSETS_DYN_STDOUT"; then
echo "external_offsets INI did not register USER_M_PATH M111" >&2
exit 1
fi
RETURN_VALUE_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.return_value.run.stdout.log"
grep -Fq "file_open=0" "$RETURN_VALUE_STDOUT"
grep -Fq "file_read_count=75" "$RETURN_VALUE_STDOUT"
grep -Fq "file_execute_count=75" "$RETURN_VALUE_STDOUT"
grep -Fq "canon_event=MESSAGE: line 6.000000: _value: - expected 0.000000, got 0.000000" "$RETURN_VALUE_STDOUT"
grep -Fq "canon_event=MESSAGE: line 28.000000: call with arg1=2.000000 expect 246.000000, got 246.000000" "$RETURN_VALUE_STDOUT"
grep -Fq "canon_event=MESSAGE: line 37.000000: call with arg1=-1.000000 expect 0.000000, got 0.000000" "$RETURN_VALUE_STDOUT"
grep -Fq "canon_event=MESSAGE: line 47.000000: call with arg1=0.000000 expect 4712.000000, got 4712.000000" "$RETURN_VALUE_STDOUT"
grep -Fq "canon_event=MESSAGE: line 53.000000: _value=4712.000000 - expected 4712.000000" "$RETURN_VALUE_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$RETURN_VALUE_STDOUT"
if grep -Fq "fail:" "$RETURN_VALUE_STDOUT"; then
echo "unexpected return-value failure message" >&2
exit 1
fi
SUBS_FOLLOW_MAIN_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.subs_follow_main.run.stdout.log"
grep -Fq "file_open=0" "$SUBS_FOLLOW_MAIN_STDOUT"
grep -Fq "file_read_count=9" "$SUBS_FOLLOW_MAIN_STDOUT"
grep -Fq "file_execute_count=9" "$SUBS_FOLLOW_MAIN_STDOUT"
grep -Fq "canon_event=COMMENT: Subs may follow main program " "$SUBS_FOLLOW_MAIN_STDOUT"
grep -Fq "canon_event=PALLET_SHUTTLE" "$SUBS_FOLLOW_MAIN_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$SUBS_FOLLOW_MAIN_STDOUT"
FRACTIONAL_LINENUMBERS_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.fractional_linenumbers.run.stdout.log"
grep -Fq "file_open=0" "$FRACTIONAL_LINENUMBERS_STDOUT"
grep -Fq "file_read_count=4" "$FRACTIONAL_LINENUMBERS_STDOUT"
grep -Fq "file_execute_count=4" "$FRACTIONAL_LINENUMBERS_STDOUT"
grep -Fq "canon_event=SET_SPINDLE_SPEED spindle=0 speed=300" "$FRACTIONAL_LINENUMBERS_STDOUT"
grep -Fq "canon_event=SET_SPINDLE_SPEED spindle=0 speed=600" "$FRACTIONAL_LINENUMBERS_STDOUT"
grep -Fq "canon_event=SET_SPINDLE_SPEED spindle=0 speed=1200" "$FRACTIONAL_LINENUMBERS_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$FRACTIONAL_LINENUMBERS_STDOUT"
NAMEDPARAM_BUG424_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.namedparam_bug424.run.stdout.log"
grep -Fq "file_open=0" "$NAMEDPARAM_BUG424_STDOUT"
grep -Fq "file_read_count=6" "$NAMEDPARAM_BUG424_STDOUT"
grep -Fq "file_execute_count=6" "$NAMEDPARAM_BUG424_STDOUT"
grep -Fq "setup.current_x=2" "$NAMEDPARAM_BUG424_STDOUT"
grep -Fq "setup.current_y=3" "$NAMEDPARAM_BUG424_STDOUT"
grep -Fq "setup.current_z=4" "$NAMEDPARAM_BUG424_STDOUT"
grep -Fq "canon_event=USE_LENGTH_UNITS units=1" "$NAMEDPARAM_BUG424_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=5 x=2 y=3 z=4" "$NAMEDPARAM_BUG424_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$NAMEDPARAM_BUG424_STDOUT"
G6164_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.g6164.run.stdout.log"
grep -Fq "file_open=0" "$G6164_STDOUT"
grep -Fq "file_read_count=6" "$G6164_STDOUT"
grep -Fq "file_execute_count=6" "$G6164_STDOUT"
grep -Fq "canon_event=SET_MOTION_CONTROL_MODE mode=3 tolerance=0" "$G6164_STDOUT"
grep -Fq "canon_event=SET_NAIVECAM_TOLERANCE tolerance=0" "$G6164_STDOUT"
grep -Fq "canon_event=SET_MOTION_CONTROL_MODE mode=3 tolerance=1" "$G6164_STDOUT"
grep -Fq "canon_event=SET_NAIVECAM_TOLERANCE tolerance=1" "$G6164_STDOUT"
grep -Fq "canon_event=SET_NAIVECAM_TOLERANCE tolerance=2" "$G6164_STDOUT"
grep -Fq "canon_event=SET_MOTION_CONTROL_MODE mode=2 tolerance=0" "$G6164_STDOUT"
grep -Fq "canon_event=SET_MOTION_CONTROL_MODE mode=1 tolerance=0" "$G6164_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$G6164_STDOUT"
ROTATION_ABS_PTS_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.rotation_abs_pts.run.stdout.log"
grep -Fq "file_open=0" "$ROTATION_ABS_PTS_STDOUT"
grep -Fq "file_read_count=192" "$ROTATION_ABS_PTS_STDOUT"
grep -Fq "file_execute_count=192" "$ROTATION_ABS_PTS_STDOUT"
grep -Fq "file_saw_error=0" "$ROTATION_ABS_PTS_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$ROTATION_ABS_PTS_STDOUT"
if [[ "$(grep -Fc "canon_event=MESSAGE: 0.000000 0.000000 0.000000" "$ROTATION_ABS_PTS_STDOUT")" != "14" ]]; then
echo "unexpected rotation/abs-pts zero absolute-position message count" >&2
exit 1
fi
if [[ "$(grep -Fc "canon_event=MESSAGE: 1.000000 2.000000 3.000000" "$ROTATION_ABS_PTS_STDOUT")" != "14" ]]; then
echo "unexpected rotation/abs-pts target absolute-position message count" >&2
exit 1
fi
ROTATION_G28_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.rotation_g28.run.stdout.log"
grep -Fq "file_open=0" "$ROTATION_G28_STDOUT"
grep -Fq "file_read_count=13" "$ROTATION_G28_STDOUT"
grep -Fq "file_execute_count=13" "$ROTATION_G28_STDOUT"
grep -Fq "file_saw_error=0" "$ROTATION_G28_STDOUT"
grep -Fq "canon_event=SET_XY_ROTATION rotation=45" "$ROTATION_G28_STDOUT"
grep -Fq "canon_event=COMMENT: G55 G28" "$ROTATION_G28_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=9 x=10 y=10 z=0" "$ROTATION_G28_STDOUT"
grep -Fq "canon_event=COMMENT: G56 G28" "$ROTATION_G28_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=14 x=0 y=14.1421 z=0 a=1" "$ROTATION_G28_STDOUT"
grep -Eq "canon_event=STRAIGHT_TRAVERSE line=14 x=(0|[0-9.e-]+) y=14\\.1421 z=0 a=0" "$ROTATION_G28_STDOUT"
grep -Fq "canon_event=FINISH" "$ROTATION_G28_STDOUT"
ROTATION_G53_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.rotation_g53.run.stdout.log"
grep -Fq "file_open=0" "$ROTATION_G53_STDOUT"
grep -Fq "file_read_count=17" "$ROTATION_G53_STDOUT"
grep -Fq "file_execute_count=17" "$ROTATION_G53_STDOUT"
grep -Fq "file_saw_error=0" "$ROTATION_G53_STDOUT"
grep -Fq "canon_event=SET_XY_ROTATION rotation=45" "$ROTATION_G53_STDOUT"
grep -Fq "canon_event=COMMENT: g53 + g55 to 1,1" "$ROTATION_G53_STDOUT"
grep -Eq "canon_event=STRAIGHT_TRAVERSE line=8 x=1\\.41421 y=([0-9.e-]+) z=0" "$ROTATION_G53_STDOUT"
grep -Fq "canon_event=COMMENT: g53 + g55 + g92 to 1,1" "$ROTATION_G53_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=13 x=0 y=0 z=0" "$ROTATION_G53_STDOUT"
grep -Fq "canon_event=COMMENT: g53 + g56 + g92 to 1,1" "$ROTATION_G53_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=18 x=-0.414214 y=1 z=0 a=1" "$ROTATION_G53_STDOUT"
grep -Fq "canon_event=FINISH" "$ROTATION_G53_STDOUT"
SUB_CALL_FROM_SUB_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.sub_call_from_sub.run.stdout.log"
grep -Fq "file_open=0" "$SUB_CALL_FROM_SUB_STDOUT"
grep -Fq "file_read_count=10" "$SUB_CALL_FROM_SUB_STDOUT"
grep -Fq "file_execute_count=10" "$SUB_CALL_FROM_SUB_STDOUT"
grep -Fq "X caller: 1.000000 2.000000 3.000000" "$SUB_CALL_FROM_SUB_STDOUT"
grep -Fq "X helper: 4.000000 5.000000 6.000000" "$SUB_CALL_FROM_SUB_STDOUT"
grep -Fq "canon_event=COMMENT: Test: calling a sub from within another sub is valid" "$SUB_CALL_FROM_SUB_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$SUB_CALL_FROM_SUB_STDOUT"
SEQUENCE_NUMBER_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.sequence_number.run.stdout.log"
grep -Fq "file_open=0" "$SEQUENCE_NUMBER_STDOUT"
grep -Fq "file_read_count=13" "$SEQUENCE_NUMBER_STDOUT"
grep -Fq "file_execute_count=13" "$SEQUENCE_NUMBER_STDOUT"
grep -Fq "canon_event=MESSAGE: main: line=7.000000 - expect 7" "$SEQUENCE_NUMBER_STDOUT"
grep -Fq "canon_event=MESSAGE: in rm400.ngc line=2.000000 - expect 2" "$SEQUENCE_NUMBER_STDOUT"
grep -Fq "canon_event=MESSAGE: main: line=9.000000 - expect 9" "$SEQUENCE_NUMBER_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$SEQUENCE_NUMBER_STDOUT"
NESTED_SUB_ERROR_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.nested_sub_error.run.stdout.log"
grep -Fq "file_open=0" "$NESTED_SUB_ERROR_STDOUT"
grep -Fq "file_read_4=5" "$NESTED_SUB_ERROR_STDOUT"
grep -Fq "file_read_count=4" "$NESTED_SUB_ERROR_STDOUT"
grep -Fq "file_execute_count=3" "$NESTED_SUB_ERROR_STDOUT"
grep -Fq "canon_event=COMMENT: Test: nested sub definition inside named sub should error" "$NESTED_SUB_ERROR_STDOUT"
if grep -Fq "canon_event=PROGRAM_END" "$NESTED_SUB_ERROR_STDOUT"; then
echo "unexpected nested-sub-error program end" >&2
exit 1
fi
NESTED_SUB_IN_FILE_ERROR_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.nested_sub_in_file_error.run.stdout.log"
grep -Fq "file_open=0" "$NESTED_SUB_IN_FILE_ERROR_STDOUT"
grep -Fq "file_execute_5=5" "$NESTED_SUB_IN_FILE_ERROR_STDOUT"
grep -Fq "file_error_text=Subroutine 'O200' not found -- not in offset table and no file '" "$NESTED_SUB_IN_FILE_ERROR_STDOUT"
grep -Fq "file_read_count=5" "$NESTED_SUB_IN_FILE_ERROR_STDOUT"
grep -Fq "file_execute_count=5" "$NESTED_SUB_IN_FILE_ERROR_STDOUT"
grep -Fq "canon_event=COMMENT: Test: numbered sub after named endsub in same file should error" "$NESTED_SUB_IN_FILE_ERROR_STDOUT"
grep -Fq "canon_event=MESSAGE: sequential main: 7.000000 8.000000 9.000000" "$NESTED_SUB_IN_FILE_ERROR_STDOUT"
if grep -Fq "canon_event=PROGRAM_END" "$NESTED_SUB_IN_FILE_ERROR_STDOUT"; then
echo "unexpected nested-sub-in-file-error program end" >&2
exit 1
fi
OWORD_UNWIND_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.oword_unwind.run.stdout.log"
grep -Fq "file_open=0" "$OWORD_UNWIND_STDOUT"
grep -Fq "file_read_5=5" "$OWORD_UNWIND_STDOUT"
grep -Fq "file_read_10=5" "$OWORD_UNWIND_STDOUT"
grep -Fq "file_read_count=11" "$OWORD_UNWIND_STDOUT"
grep -Fq "file_execute_count=9" "$OWORD_UNWIND_STDOUT"
grep -Fq "canon_event=MESSAGE: pre divide-by-zero" "$OWORD_UNWIND_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$OWORD_UNWIND_STDOUT"
if grep -Fq "canon_event=MESSAGE: post divide-by-zero" "$OWORD_UNWIND_STDOUT"; then
echo "unexpected oword-unwind post-error message" >&2
exit 1
fi
ABORT_HOT_COMMENT_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.abort_hot_comment.run.stdout.log"
grep -Fq "file_open=0" "$ABORT_HOT_COMMENT_STDOUT"
grep -Fq "file_read_count=125" "$ABORT_HOT_COMMENT_STDOUT"
grep -Fq "file_execute_count=125" "$ABORT_HOT_COMMENT_STDOUT"
grep -Fq "file_saw_error=1" "$ABORT_HOT_COMMENT_STDOUT"
grep -Fq "file_error_text= MixedCase param42=20.000000 named=4711.000000 INI=3.140000" "$ABORT_HOT_COMMENT_STDOUT"
if grep -Fq "canon_event=PROGRAM_END" "$ABORT_HOT_COMMENT_STDOUT"; then
echo "abort-hot-comment unexpectedly reached program end" >&2
exit 1
fi
M19_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m19.run.stdout.log"
grep -Fq "file_open=0" "$M19_STDOUT"
grep -Fq "file_read_count=8" "$M19_STDOUT"
grep -Fq "file_execute_count=8" "$M19_STDOUT"
grep -Fq "file_saw_error=0" "$M19_STDOUT"
grep -Fq "canon_event=ORIENT_SPINDLE spindle=0 orientation=87 mode=0" "$M19_STDOUT"
grep -Fq "canon_event=WAIT_SPINDLE_ORIENT_COMPLETE spindle=0 timeout=2" "$M19_STDOUT"
grep -Fq "canon_event=ORIENT_SPINDLE spindle=0 orientation=42 mode=1" "$M19_STDOUT"
grep -Fq "canon_event=ORIENT_SPINDLE spindle=0 orientation=132 mode=0" "$M19_STDOUT"
grep -Fq "canon_event=ORIENT_SPINDLE spindle=0 orientation=132 mode=1" "$M19_STDOUT"
grep -Fq "canon_event=WAIT_SPINDLE_ORIENT_COMPLETE spindle=0 timeout=1" "$M19_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$M19_STDOUT"
MAGIC_COMMENTS_PARAM_FORMAT_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.magic_comments_param_format.run.stdout.log"
grep -Fq "file_open=0" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "file_read_count=13" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "file_execute_count=13" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "file_saw_error=0" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "canon_event=MESSAGE: native value = 1.123457" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "canon_event=MESSAGE: Test round to integer: 1" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "canon_event=MESSAGE: Test round to 4 decimals: 1.1235" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "canon_event=MESSAGE: Test format separate from param: The value is: 1.1235" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "canon_event=MESSAGE: Test round to 7 decimals: 1.1234568" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "canon_event=MESSAGE: native value2 = 2.345679" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "canon_event=MESSAGE: Test2 round all params in line to 4 decimals: The values are: 1.1235, 2.3457" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "canon_event=MESSAGE: Test2 only round last value to integer: The values are: 1.123457, 2" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
M98M99_01_BASICS_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_01_basics.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_01_BASICS_STDOUT"
grep -Fq "file_read_count=22" "$M98M99_01_BASICS_STDOUT"
grep -Fq "file_execute_count=22" "$M98M99_01_BASICS_STDOUT"
grep -Fq "file_saw_error=0" "$M98M99_01_BASICS_STDOUT"
grep -Fq "canon_event=COMMENT: A simple O sub example " "$M98M99_01_BASICS_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=10 x=3 y=0 z=0.25" "$M98M99_01_BASICS_STDOUT"
grep -Fq "canon_event=STRAIGHT_FEED line=11 x=3 y=0 z=-1" "$M98M99_01_BASICS_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$M98M99_01_BASICS_STDOUT"
M98M99_02_VARIABLES_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_02_variables.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_02_VARIABLES_STDOUT"
grep -Fq "file_read_count=54" "$M98M99_02_VARIABLES_STDOUT"
grep -Fq "file_execute_count=54" "$M98M99_02_VARIABLES_STDOUT"
grep -Fq "file_saw_error=0" "$M98M99_02_VARIABLES_STDOUT"
grep -Fq "Q MAIN/FANUC: POST-M98: 1=13.010000; 30=13.300000; 31=13.310000" "$M98M99_02_VARIABLES_STDOUT"
grep -Fq "Q MAIN/RS274NGC: POST-O-CALL: 1=42.010000; 30=42.300000; 31=13.310000" "$M98M99_02_VARIABLES_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$M98M99_02_VARIABLES_STDOUT"
M98M99_07_NESTED_SUBS_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_07_nested_subs.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_07_NESTED_SUBS_STDOUT"
grep -Fq "file_read_count=163" "$M98M99_07_NESTED_SUBS_STDOUT"
grep -Fq "file_execute_count=163" "$M98M99_07_NESTED_SUBS_STDOUT"
grep -Fq "file_saw_error=0" "$M98M99_07_NESTED_SUBS_STDOUT"
grep -Fq "X >>>> LOOP [O2.O1]: 4.500000" "$M98M99_07_NESTED_SUBS_STDOUT"
grep -Fq "X MAIN END: 1=5.000000" "$M98M99_07_NESTED_SUBS_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$M98M99_07_NESTED_SUBS_STDOUT"
M98M99_08_SUB_FOLLOWS_MAIN_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_08_sub_follows_main.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_08_SUB_FOLLOWS_MAIN_STDOUT"
grep -Fq "file_read_count=9" "$M98M99_08_SUB_FOLLOWS_MAIN_STDOUT"
grep -Fq "file_execute_count=9" "$M98M99_08_SUB_FOLLOWS_MAIN_STDOUT"
grep -Fq "file_saw_error=0" "$M98M99_08_SUB_FOLLOWS_MAIN_STDOUT"
grep -Fq "X IN O1" "$M98M99_08_SUB_FOLLOWS_MAIN_STDOUT"
grep -Fq "canon_event=COMMENT: Fanuc-style subs may follow main program " "$M98M99_08_SUB_FOLLOWS_MAIN_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$M98M99_08_SUB_FOLLOWS_MAIN_STDOUT"
M98M99_10_M98_P001_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_10_m98_p001.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_10_M98_P001_STDOUT"
grep -Fq "file_read_count=8" "$M98M99_10_M98_P001_STDOUT"
grep -Fq "file_execute_count=8" "$M98M99_10_M98_P001_STDOUT"
grep -Fq "file_saw_error=0" "$M98M99_10_M98_P001_STDOUT"
grep -Fq "x got here" "$M98M99_10_M98_P001_STDOUT"
grep -Fq "canon_event=COMMENT: main program" "$M98M99_10_M98_P001_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$M98M99_10_M98_P001_STDOUT"
if awk '
/^file_read_1=0$/ { in_file = 1 }
in_file && /ERROR: should not get here/ { found = 1 }
END { exit(found ? 0 : 1) }
' "$M98M99_10_M98_P001_STDOUT"; then
echo "unexpected m98m99 leading-zero O-word fallback branch" >&2
exit 1
fi
M98M99_13_NAMED_PROGRAM_NAMED_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_13_named_program_named.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_13_NAMED_PROGRAM_NAMED_STDOUT"
grep -Fq "file_read_count=4" "$M98M99_13_NAMED_PROGRAM_NAMED_STDOUT"
grep -Fq "file_execute_count=4" "$M98M99_13_NAMED_PROGRAM_NAMED_STDOUT"
grep -Fq "file_saw_error=0" "$M98M99_13_NAMED_PROGRAM_NAMED_STDOUT"
grep -Fq "canon_event=COMMENT: test-named.ngc: Test named programs" "$M98M99_13_NAMED_PROGRAM_NAMED_STDOUT"
grep -Fq "canon_event=COMMENT: ...program body" "$M98M99_13_NAMED_PROGRAM_NAMED_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$M98M99_13_NAMED_PROGRAM_NAMED_STDOUT"
M98M99_13_NAMED_PROGRAM_NUMBERED_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_13_named_program_numbered.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_13_NAMED_PROGRAM_NUMBERED_STDOUT"
grep -Fq "file_read_count=4" "$M98M99_13_NAMED_PROGRAM_NUMBERED_STDOUT"
grep -Fq "file_execute_count=4" "$M98M99_13_NAMED_PROGRAM_NUMBERED_STDOUT"
grep -Fq "file_saw_error=0" "$M98M99_13_NAMED_PROGRAM_NUMBERED_STDOUT"
grep -Fq "canon_event=COMMENT: test-numbered.ngc: Test numbered programs" "$M98M99_13_NAMED_PROGRAM_NUMBERED_STDOUT"
grep -Fq "canon_event=COMMENT: ...program body" "$M98M99_13_NAMED_PROGRAM_NUMBERED_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$M98M99_13_NAMED_PROGRAM_NUMBERED_STDOUT"
M98M99_14_O_EXPRESSION_CALL_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_14_o_expression_call.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_14_O_EXPRESSION_CALL_STDOUT"
grep -Fq "file_read_count=29" "$M98M99_14_O_EXPRESSION_CALL_STDOUT"
grep -Fq "file_execute_count=29" "$M98M99_14_O_EXPRESSION_CALL_STDOUT"
grep -Fq "file_saw_error=0" "$M98M99_14_O_EXPRESSION_CALL_STDOUT"
grep -Fq "In sub 100" "$M98M99_14_O_EXPRESSION_CALL_STDOUT"
grep -Fq "In sub 200" "$M98M99_14_O_EXPRESSION_CALL_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$M98M99_14_O_EXPRESSION_CALL_STDOUT"
for fixture in "$GCODE_FIXTURE_DIR"/*.ngc; do for fixture in "$GCODE_FIXTURE_DIR"/*.ngc; do
name="$(basename "$fixture" .ngc)" name="$(basename "$fixture" .ngc)"
if [[ "$name" == "disable_fanuc_style_sub_m98" ]]; then
continue
fi
expected="$CANON_FIXTURE_DIR/$name.events" expected="$CANON_FIXTURE_DIR/$name.events"
if [[ ! -f "$expected" ]]; then if [[ ! -f "$expected" ]]; then
echo "missing expected canon fixture: $expected" >&2 echo "missing expected canon fixture: $expected" >&2
@@ -737,9 +1380,62 @@ for fixture in "$GCODE_FIXTURE_DIR"/*.ngc; do
if [[ "$name" == "oword_subroutine" || "$name" == "percent_file_finish" ]]; then if [[ "$name" == "oword_subroutine" || "$name" == "percent_file_finish" ]]; then
require_mdi=0 require_mdi=0
fi fi
if [[ "$name" == "namedparam_semantics" ||
"$name" == "namedparam_ini_semantics" ||
"$name" == "ini_tool_table" ||
"$name" == "spindle_orient_offset" ]]; then
continue
fi
check_fixture_output "$fixture" "$expected" "$stdout_file" "$require_mdi" check_fixture_output "$fixture" "$expected" "$stdout_file" "$require_mdi"
done done
for name in namedparam_semantics namedparam_ini_semantics; do
fixture="$GCODE_FIXTURE_DIR/$name.ngc"
expected="$CANON_FIXTURE_DIR/$name.events"
stdout_file="$BUILD_DIR/linuxcnc_interp_minimal_harness.$name.with_ini.stdout.log"
stderr_file="$BUILD_DIR/linuxcnc_interp_minimal_harness.$name.with_ini.stderr.log"
"$BUILD_DIR/linuxcnc_interp_minimal_harness" "$fixture" "$NAMEDPARAM_INI_FIXTURE" \
>"$stdout_file" \
2>"$stderr_file"
check_fixture_output "$fixture" "$expected" "$stdout_file"
done
INI_TOOL_TABLE_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.ini_tool_table.with_ini.stdout.log"
INI_TOOL_TABLE_STDERR="$BUILD_DIR/linuxcnc_interp_minimal_harness.ini_tool_table.with_ini.stderr.log"
"$BUILD_DIR/linuxcnc_interp_minimal_harness" \
"$GCODE_FIXTURE_DIR/ini_tool_table.ngc" \
"$ROOT_DIR/tests/fixtures/ini/ini_tool_table.ini" \
>"$INI_TOOL_TABLE_STDOUT" \
2>"$INI_TOOL_TABLE_STDERR"
check_fixture_output \
"$GCODE_FIXTURE_DIR/ini_tool_table.ngc" \
"$CANON_FIXTURE_DIR/ini_tool_table.events" \
"$INI_TOOL_TABLE_STDOUT"
SPINDLE_ORIENT_OFFSET_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.spindle_orient_offset.with_ini.stdout.log"
SPINDLE_ORIENT_OFFSET_STDERR="$BUILD_DIR/linuxcnc_interp_minimal_harness.spindle_orient_offset.with_ini.stderr.log"
"$BUILD_DIR/linuxcnc_interp_minimal_harness" \
"$GCODE_FIXTURE_DIR/spindle_orient_offset.ngc" \
"$SPINDLE_ORIENT_OFFSET_INI_FIXTURE" \
>"$SPINDLE_ORIENT_OFFSET_STDOUT" \
2>"$SPINDLE_ORIENT_OFFSET_STDERR"
check_fixture_output \
"$GCODE_FIXTURE_DIR/spindle_orient_offset.ngc" \
"$CANON_FIXTURE_DIR/spindle_orient_offset.events" \
"$SPINDLE_ORIENT_OFFSET_STDOUT"
DISABLE_FANUC_STYLE_SUB_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.disable_fanuc_style_sub_m98.with_ini.stdout.log"
DISABLE_FANUC_STYLE_SUB_STDERR="$BUILD_DIR/linuxcnc_interp_minimal_harness.disable_fanuc_style_sub_m98.with_ini.stderr.log"
"$BUILD_DIR/linuxcnc_interp_minimal_harness" \
"$GCODE_FIXTURE_DIR/disable_fanuc_style_sub_m98.ngc" \
"$DISABLE_FANUC_STYLE_SUB_INI_FIXTURE" \
>"$DISABLE_FANUC_STYLE_SUB_STDOUT" \
2>"$DISABLE_FANUC_STYLE_SUB_STDERR"
grep -Fq "file_open=0" "$DISABLE_FANUC_STYLE_SUB_STDOUT"
grep -Fq "file_saw_error=1" "$DISABLE_FANUC_STYLE_SUB_STDOUT"
grep -Fq "file_error_text=DISABLE_FANUC_STYLE_SUB set in INI file, but found m98" \
"$DISABLE_FANUC_STYLE_SUB_STDOUT"
for fixture in "$GCODE_ERROR_FIXTURE_DIR"/*.ngc; do for fixture in "$GCODE_ERROR_FIXTURE_DIR"/*.ngc; do
name="$(basename "$fixture" .ngc)" name="$(basename "$fixture" .ngc)"
expected="$CANON_ERROR_FIXTURE_DIR/$name.expected" expected="$CANON_ERROR_FIXTURE_DIR/$name.expected"
@@ -770,4 +1466,6 @@ for fixture in "$GCODE_ERROR_FIXTURE_DIR"/*.ngc; do
done < "$expected" done < "$expected"
done done
printf '%s\n' "$NATIVE_PROBE_VALIDATION_FINGERPRINT" > "$VALIDATION_CACHE_KEY_FILE"
printf '%s\n' "$NATIVE_PROBE_VALIDATION_FINGERPRINT" > "$VALIDATION_CACHE_OK_FILE"
echo "native probe validation complete" echo "native probe validation complete"

View File

@@ -0,0 +1,313 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
WORKSPACE_ROOT="$(cd "$ROOT_DIR/.." && pwd)"
LINUXCNC_DIR="${LINUXCNC_DIR:-$WORKSPACE_ROOT/linuxcnc}"
NC_DIR="$LINUXCNC_DIR/nc_files"
BUILD_DIR="${NC_FILES_BUILD_DIR:-$ROOT_DIR/build/native/nc-files}"
RS274="${RS274:-$LINUXCNC_DIR/bin/rs274}"
TIMEOUT_SECONDS="${NC_FILES_TIMEOUT_SECONDS:-30}"
ONLY_FILTER=""
SCAN_ALL=0
while [[ $# -gt 0 ]]; do
case "$1" in
--all)
SCAN_ALL=1
shift
;;
--only)
[[ $# -ge 2 ]] || { echo "missing value for --only" >&2; exit 2; }
ONLY_FILTER="$2"
shift 2
;;
--help|-h)
cat <<EOF
Usage: $0 [--all] [--only PATH_SUBSTRING]
Runs LinuxCNC nc_files programs through LinuxCNC's rs274 standalone entry point.
The default suite covers the basic/example programs and common macro libraries.
Use --all to scan every .ngc under linuxcnc/nc_files.
Results are written under:
$BUILD_DIR
EOF
exit 0
;;
*)
echo "unknown argument: $1" >&2
exit 2
;;
esac
done
if [[ ! -x "$RS274" ]]; then
echo "missing executable rs274: $RS274" >&2
exit 1
fi
if [[ ! -d "$NC_DIR" ]]; then
echo "missing LinuxCNC nc_files directory: $NC_DIR" >&2
exit 1
fi
export LD_LIBRARY_PATH="$LINUXCNC_DIR/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
export PYTHONPATH="$LINUXCNC_DIR/lib/python${PYTHONPATH:+:$PYTHONPATH}"
mkdir -p "$BUILD_DIR"/{logs,out,wrapped,params}
SUMMARY_FILE="$BUILD_DIR/summary.tsv"
INI_FILE="$BUILD_DIR/test-rs274.ini"
TOOL_TABLE="$BUILD_DIR/test-tools.tbl"
PARAMETER_TEMPLATE="$BUILD_DIR/test.var.template"
printf 'path\tclass\tstatus\texpected_failure\texit_code\tseconds\toutput_lines\tstderr_summary\n' > "$SUMMARY_FILE"
relpath() {
local path="$1"
printf '%s\n' "${path#"$NC_DIR"/}"
}
safe_name() {
local rel="$1"
rel="${rel//\//_}"
rel="${rel// /_}"
rel="${rel//#/_}"
rel="${rel//=/}"
printf '%s\n' "$rel"
}
write_fixture_config() {
{
printf '[RS274NGC]\n'
printf 'PARAMETER_FILE = test.var\n'
printf 'SUBROUTINE_PATH = %s\n' "$(
find "$NC_DIR" -type d | sort | paste -sd ':' -
)"
printf 'OWORD_NARGS = 1\n'
printf 'FEATURES = 30\n'
printf '\n[DISPLAY]\n'
printf 'PROGRAM_PREFIX = %s\n' "$NC_DIR"
printf '\n[TRAJ]\n'
printf 'COORDINATES = X Y Z A B C U V W\n'
printf '\n[EMCIO]\n'
printf 'TOOL_TABLE = %s\n' "$TOOL_TABLE"
} > "$INI_FILE"
: > "$TOOL_TABLE"
local tool
for tool in $(seq 1 200); do
printf 'T%s P%s D0.125000 Z+0.000000 ; generated nc_files test tool\n' "$tool" "$tool" >> "$TOOL_TABLE"
done
{
printf '5161 0.0\n'
printf '5162 0.0\n'
printf '5163 0.0\n'
printf '5220 1\n'
printf '5221 0.0\n'
printf '5222 0.0\n'
printf '5223 0.0\n'
printf '5399 0.0\n'
} > "$PARAMETER_TEMPLATE"
}
has_program_end() {
local ngc="$1"
awk '
BEGIN { found = 0 }
/^[[:space:]]*%[[:space:]]*$/ { found = 1 }
{
line = tolower($0)
gsub(/\([^)]*\)/, "", line)
if (line ~ /(^|[[:space:]])m0*(2|30)([[:space:]]|$)/) {
found = 1
}
}
END { exit(found ? 0 : 1) }
' "$ngc"
}
is_default_basic_path() {
local rel="$1"
case "$rel" in
*/*)
case "$rel" in
gladevcp_lib/*.ngc|macros/lathe/*.ngc|macros/mill/*.ngc|ngcgui_lib/*.ngc)
return 0
;;
esac
return 1
;;
*.ngc)
return 0
;;
esac
return 1
}
classify_program() {
local rel="$1"
local ngc="$2"
case "$rel" in
macros/*|ngcgui_lib/*|gladevcp_lib/*|remap-subroutines/*|remap_lib/*|probe/*)
printf 'macro_load\n'
return 0
;;
esac
if has_program_end "$ngc"; then
printf 'main\n'
else
printf 'macro_load\n'
fi
}
prepare_input() {
local class="$1"
local ngc="$2"
local wrapped="$3"
if [[ "$class" == "main" ]]; then
printf '%s\n' "$ngc"
return 0
fi
cp "$ngc" "$wrapped"
printf '\nM2\n' >> "$wrapped"
printf '%s\n' "$wrapped"
}
known_expected_failure() {
local rel="$1"
local combined="$2"
case "$rel" in
cone.ngc)
[[ "$combined" == *"Bad character 'w' used"* ]] && { printf 'five-axis-W-machine-context\n'; return 0; }
;;
g76.ngc|lathe-g76.ngc)
[[ "$combined" == *"Length of cutter compensation entry move is not greater than the tool radius"* ]] && { printf 'lathe-cutter-comp-tool-context\n'; return 0; }
;;
lathe_g70_71_demo.ngc)
[[ "$combined" == *"Arc move in concave corner cannot be reached by the tool without gouging"* ]] && { printf 'lathe-profile-tool-context\n'; return 0; }
;;
nestedcall.ngc)
[[ "$combined" == *"Unexpected character after O-word"* ]] && { printf 'upstream-oword-callsub-syntax-edge\n'; return 0; }
;;
nurbs/G5/Curva_Stella\ #1=1_G5.ngc)
[[ "$combined" == *"Unknown g code used"* && "$combined" == *"G2.2"* ]] && { printf 'nurbs-g22-entrypoint-edge\n'; return 0; }
;;
probe*.ngc|probe/*|smartprobe.ngc|tool-length-probe.ngc|touchoff.ngc)
[[ "$combined" == *"probe"* || "$combined" == *"G38"* ]] && { printf 'probe-runtime-context\n'; return 0; }
;;
plasmac/*|plasmatest.ngc)
[[ "$combined" == *"Unknown m code"* || "$combined" == *"Named parameter"* ]] && { printf 'plasmac-runtime-context\n'; return 0; }
;;
esac
return 1
}
summarize_text() {
local file="$1"
if [[ ! -f "$file" ]]; then
return 0
fi
tr '\n\t' ' ' < "$file" | sed -E 's/[[:space:]]+/ /g; s/^ //; s/ $//' | cut -c 1-260
}
write_fixture_config
total=0
pass=0
fail=0
timeout_count=0
expected_fail=0
unexpected_fail=0
while IFS= read -r ngc; do
rel="$(relpath "$ngc")"
if [[ "$SCAN_ALL" -eq 0 ]] && ! is_default_basic_path "$rel"; then
continue
fi
if [[ -n "$ONLY_FILTER" && "$rel" != *"$ONLY_FILTER"* ]]; then
continue
fi
total=$((total + 1))
class="$(classify_program "$rel" "$ngc")"
safe="$(safe_name "$rel")"
wrapped="$BUILD_DIR/wrapped/$safe"
input="$(prepare_input "$class" "$ngc" "$wrapped")"
out_file="$BUILD_DIR/out/$safe.out"
stdout_file="$BUILD_DIR/logs/$safe.stdout"
stderr_file="$BUILD_DIR/logs/$safe.stderr"
param_file="$BUILD_DIR/params/$safe.var"
cp "$PARAMETER_TEMPLATE" "$param_file"
start_time="$(date +%s)"
set +e
(
cd "$WORKSPACE_ROOT"
INI_FILE_NAME="$INI_FILE" timeout "$TIMEOUT_SECONDS" "$RS274" \
-g \
-i "$INI_FILE" \
-t "$TOOL_TABLE" \
-v "$param_file" \
"$input" \
"$out_file"
) >"$stdout_file" 2>"$stderr_file"
rc=$?
set -e
end_time="$(date +%s)"
seconds=$((end_time - start_time))
status="FAIL"
if [[ "$rc" -eq 0 ]]; then
status="PASS"
pass=$((pass + 1))
elif [[ "$rc" -eq 124 || "$rc" -eq 137 ]]; then
status="TIMEOUT"
timeout_count=$((timeout_count + 1))
fail=$((fail + 1))
else
fail=$((fail + 1))
fi
combined="$(cat "$stdout_file" "$stderr_file" 2>/dev/null || true)"
expected=""
if [[ "$status" != "PASS" ]]; then
expected="$(known_expected_failure "$rel" "$combined" || true)"
if [[ -n "$expected" ]]; then
expected_fail=$((expected_fail + 1))
else
unexpected_fail=$((unexpected_fail + 1))
fi
fi
output_lines=0
[[ -f "$out_file" ]] && output_lines="$(wc -l < "$out_file" | tr -d '[:space:]')"
stderr_summary="$(summarize_text "$stderr_file")"
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
"$rel" \
"$class" \
"$status" \
"$expected" \
"$rc" \
"$seconds" \
"$output_lines" \
"$stderr_summary" >> "$SUMMARY_FILE"
done < <(find "$NC_DIR" -type f -name '*.ngc' | sort)
{
printf 'nc_files harness summary\n'
printf 'summary: %s\n' "$SUMMARY_FILE"
printf 'suite: %s\n' "$([[ "$SCAN_ALL" -eq 1 ]] && printf all || printf basic)"
printf 'total: %s\n' "$total"
printf 'pass: %s\n' "$pass"
printf 'fail: %s\n' "$fail"
printf 'timeout: %s\n' "$timeout_count"
printf 'expected_fail: %s\n' "$expected_fail"
printf 'unexpected_fail: %s\n' "$unexpected_fail"
} | tee "$BUILD_DIR/summary.txt"
if [[ "$unexpected_fail" -ne 0 ]]; then
echo "unexpected nc_files failures:" >&2
awk -F '\t' 'NR > 1 && $3 != "PASS" && $4 == "" { print $1 "\t" $3 "\t" $8 }' "$SUMMARY_FILE" >&2
exit 1
fi

View File

@@ -0,0 +1,387 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
WORKSPACE_ROOT="$(cd "$ROOT_DIR/.." && pwd)"
LINUXCNC_DIR="${LINUXCNC_DIR:-$WORKSPACE_ROOT/linuxcnc}"
SIM_DIR="$LINUXCNC_DIR/configs/sim"
BUILD_DIR="${SIM_CONFIG_BUILD_DIR:-$ROOT_DIR/build/native/sim-configs}"
RS274="${RS274:-$LINUXCNC_DIR/bin/rs274}"
TIMEOUT_SECONDS="${SIM_CONFIG_TIMEOUT_SECONDS:-30}"
ONLY_FILTER=""
while [[ $# -gt 0 ]]; do
case "$1" in
--only)
[[ $# -ge 2 ]] || { echo "missing value for --only" >&2; exit 2; }
ONLY_FILTER="$2"
shift 2
;;
--help|-h)
cat <<EOF
Usage: $0 [--only PATH_SUBSTRING]
Runs LinuxCNC configs/sim .ngc programs through LinuxCNC's rs274 standalone
entry point and writes logs plus summary.tsv under:
$BUILD_DIR
EOF
exit 0
;;
*)
echo "unknown argument: $1" >&2
exit 2
;;
esac
done
if [[ ! -x "$RS274" ]]; then
echo "missing executable rs274: $RS274" >&2
exit 1
fi
if [[ ! -d "$SIM_DIR" ]]; then
echo "missing LinuxCNC sim config directory: $SIM_DIR" >&2
exit 1
fi
"$ROOT_DIR/tools/build_native_probes.sh"
export LD_LIBRARY_PATH="$LINUXCNC_DIR/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
export PYTHONPATH="$LINUXCNC_DIR/lib/python${PYTHONPATH:+:$PYTHONPATH}"
mkdir -p "$BUILD_DIR"/{logs,out,wrapped,params}
: > "$BUILD_DIR/skipped.tsv"
SUMMARY_FILE="$BUILD_DIR/summary.tsv"
printf 'path\tclass\tstatus\texpected_failure\texit_code\tseconds\tini\ttbl\toutput_lines\tstderr_summary\n' > "$SUMMARY_FILE"
relpath() {
local path="$1"
printf '%s\n' "${path#"$SIM_DIR"/}"
}
safe_name() {
local rel="$1"
rel="${rel//\//_}"
rel="${rel// /_}"
printf '%s\n' "$rel"
}
find_nearest_ini() {
local dir="$1"
local candidate
while [[ "$dir" == "$SIM_DIR"* ]]; do
candidate="$(find "$dir" -maxdepth 1 -type f -name '*.ini' | sort | sed -n '1p')"
if [[ -n "$candidate" ]]; then
printf '%s\n' "$candidate"
return 0
fi
[[ "$dir" == "$SIM_DIR" ]] && break
dir="$(dirname "$dir")"
done
return 1
}
find_sim_ini() {
local rel="$1"
local ngc_dir="$2"
case "$rel" in
axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/*)
printf '%s\n' "$SIM_DIR/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini"
return 0
;;
esac
find_nearest_ini "$ngc_dir"
}
ini_value() {
local ini="$1"
local section="$2"
local key="$3"
awk -v section="$section" -v key="$key" '
BEGIN { in_section = 0 }
/^[[:space:]]*[#;]/ { next }
/^[[:space:]]*\[/ {
in_section = (toupper($0) ~ "\\[" toupper(section) "\\]")
next
}
in_section {
line = $0
sub(/[[:space:]]*[#;].*$/, "", line)
split(line, parts, "=")
name = parts[1]
gsub(/^[[:space:]]+|[[:space:]]+$/, "", name)
if (toupper(name) == toupper(key)) {
value = substr(line, index(line, "=") + 1)
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
print value
exit
}
}
' "$ini"
}
find_upward_file_by_name() {
local start_dir="$1"
local basename="$2"
local dir="$start_dir"
while [[ "$dir" == "$SIM_DIR"* ]]; do
if [[ -f "$dir/$basename" ]]; then
printf '%s\n' "$dir/$basename"
return 0
fi
[[ "$dir" == "$SIM_DIR" ]] && break
dir="$(dirname "$dir")"
done
return 1
}
find_nearest_tbl() {
local ngc_dir="$1"
local ini="$2"
local tool_table
tool_table="$(ini_value "$ini" EMCIO TOOL_TABLE || true)"
if [[ -n "$tool_table" ]]; then
if [[ "$tool_table" = /* && -f "$tool_table" ]]; then
printf '%s\n' "$tool_table"
return 0
fi
if [[ -f "$(dirname "$ini")/$tool_table" ]]; then
printf '%s\n' "$(dirname "$ini")/$tool_table"
return 0
fi
if find_upward_file_by_name "$ngc_dir" "$(basename "$tool_table")"; then
return 0
fi
fi
local dir="$ngc_dir"
local candidate
while [[ "$dir" == "$SIM_DIR"* ]]; do
candidate="$(find "$dir" -maxdepth 1 -type f -name '*.tbl' | sort | sed -n '1p')"
if [[ -n "$candidate" ]]; then
printf '%s\n' "$candidate"
return 0
fi
[[ "$dir" == "$SIM_DIR" ]] && break
dir="$(dirname "$dir")"
done
return 1
}
copy_parameter_file() {
local ini="$1"
local dst="$2"
local parameter_file
parameter_file="$(ini_value "$ini" RS274NGC PARAMETER_FILE || true)"
rm -f "$dst" "$dst.bak"
if [[ -n "$parameter_file" ]]; then
if [[ "$parameter_file" = /* && -f "$parameter_file" ]]; then
cp "$parameter_file" "$dst"
return 0
fi
if [[ -f "$(dirname "$ini")/$parameter_file" ]]; then
cp "$(dirname "$ini")/$parameter_file" "$dst"
return 0
fi
fi
return 0
}
has_program_end() {
local ngc="$1"
awk '
BEGIN { found = 0 }
/^[[:space:]]*%[[:space:]]*$/ { found = 1 }
{
line = tolower($0)
gsub(/\([^)]*\)/, "", line)
if (line ~ /(^|[[:space:]])m0*(2|30)([[:space:]]|$)/) {
found = 1
}
}
END { exit(found ? 0 : 1) }
' "$ngc"
}
classify_program() {
local rel="$1"
local ngc="$2"
if [[ "$rel" == */remap_subs/* || "$rel" == */nc_subroutines/* ]]; then
printf 'remap_subroutine\n'
elif [[ "$rel" == */macros/* || "$rel" == */lathe_configs/* ]]; then
printf 'macro_load\n'
elif has_program_end "$ngc"; then
printf 'main\n'
else
printf 'macro_load\n'
fi
}
prepare_input() {
local class="$1"
local ngc="$2"
local wrapped="$3"
if [[ "$class" == "main" ]]; then
printf '%s\n' "$ngc"
return 0
fi
cp "$ngc" "$wrapped"
printf '\nM2\n' >> "$wrapped"
printf '%s\n' "$wrapped"
}
known_expected_failure() {
local rel="$1"
local combined="$2"
case "$rel" in
gmoccapy/*)
[[ "$combined" == *"python/toplevel.py"* ]] && { printf 'python-remap-runtime-edge\n'; return 0; }
;;
axis/laser/*)
[[ "$combined" == *"python/toplevel.py"* ]] && { printf 'python-remap-runtime-edge\n'; return 0; }
;;
axis/external_offsets/dyn_demo.ngc|axis/external_offsets/eoffsets.ngc|axis/external_offsets/jwp_z.ngc|axis/external_offsets/opa_demo.ngc)
[[ "$combined" == *"Unknown m code used: M111"* ]] && { printf 'user-m-code-M111\n'; return 0; }
;;
axis/geometry/xyzc.ngc)
[[ "$combined" == *"Unknown m code used: M110"* ]] && { printf 'user-m-code-M110\n'; return 0; }
;;
axis/foam/foam.ngc)
[[ "$combined" == *"Bad character 'u' used"* ]] && { printf 'ini-axis-mask-UV\n'; return 0; }
;;
axis/vismach/5axis/bridgemill/5axisgui.ngc)
[[ "$combined" == *"Bad character 'w' used"* ]] && { printf 'ini-axis-mask-W\n'; return 0; }
;;
axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_g533.ngc)
[[ "$combined" == *"Cannot use axis values without a g code that uses them"* ]] && { printf 'upstream-demo-missing-motion-gcode\n'; return 0; }
;;
axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/*)
[[ "$combined" == *"python/toplevel.py"* ]] && { printf 'python-remap-runtime-edge\n'; return 0; }
;;
esac
return 1
}
summarize_text() {
local file="$1"
if [[ ! -f "$file" ]]; then
return 0
fi
tr '\n\t' ' ' < "$file" | sed -E 's/[[:space:]]+/ /g; s/^ //; s/ $//' | cut -c 1-260
}
total=0
pass=0
fail=0
timeout_count=0
skip=0
expected_fail=0
unexpected_fail=0
while IFS= read -r ngc; do
rel="$(relpath "$ngc")"
if [[ -n "$ONLY_FILTER" && "$rel" != *"$ONLY_FILTER"* ]]; then
continue
fi
if [[ "$rel" == */python/*.ngc ]]; then
printf '%s\tunsupported_runtime_edge\tpython-ngc-helper\n' "$rel" >> "$BUILD_DIR/skipped.tsv"
skip=$((skip + 1))
continue
fi
ini="$(find_sim_ini "$rel" "$(dirname "$ngc")" || true)"
if [[ -z "$ini" ]]; then
printf '%s\tunsupported_runtime_edge\tmissing-ini\n' "$rel" >> "$BUILD_DIR/skipped.tsv"
skip=$((skip + 1))
continue
fi
tbl="$(find_nearest_tbl "$(dirname "$ngc")" "$ini" || true)"
if [[ -z "$tbl" ]]; then
printf '%s\tunsupported_runtime_edge\tmissing-tool-table\n' "$rel" >> "$BUILD_DIR/skipped.tsv"
skip=$((skip + 1))
continue
fi
total=$((total + 1))
class="$(classify_program "$rel" "$ngc")"
safe="$(safe_name "$rel")"
wrapped="$BUILD_DIR/wrapped/$safe"
input="$(prepare_input "$class" "$ngc" "$wrapped")"
out_file="$BUILD_DIR/out/$safe.out"
stdout_file="$BUILD_DIR/logs/$safe.stdout"
stderr_file="$BUILD_DIR/logs/$safe.stderr"
param_file="$BUILD_DIR/params/$safe.var"
copy_parameter_file "$ini" "$param_file"
start_time="$(date +%s)"
set +e
(
cd "$(dirname "$ini")"
INI_FILE_NAME="$ini" timeout "$TIMEOUT_SECONDS" "$RS274" \
-g \
-i "$(basename "$ini")" \
-t "$tbl" \
-v "$param_file" \
"$input" \
"$out_file"
) >"$stdout_file" 2>"$stderr_file"
rc=$?
set -e
end_time="$(date +%s)"
seconds=$((end_time - start_time))
status="FAIL"
if [[ "$rc" -eq 0 ]]; then
status="PASS"
pass=$((pass + 1))
elif [[ "$rc" -eq 124 || "$rc" -eq 137 ]]; then
status="TIMEOUT"
timeout_count=$((timeout_count + 1))
else
fail=$((fail + 1))
fi
combined="$(cat "$stdout_file" "$stderr_file" 2>/dev/null || true)"
expected=""
if [[ "$status" != "PASS" ]]; then
expected="$(known_expected_failure "$rel" "$combined" || true)"
if [[ -n "$expected" ]]; then
expected_fail=$((expected_fail + 1))
else
unexpected_fail=$((unexpected_fail + 1))
fi
fi
output_lines=0
[[ -f "$out_file" ]] && output_lines="$(wc -l < "$out_file" | tr -d '[:space:]')"
stderr_summary="$(summarize_text "$stderr_file")"
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
"$rel" \
"$class" \
"$status" \
"$expected" \
"$rc" \
"$seconds" \
"$(relpath "$ini")" \
"$(relpath "$tbl")" \
"$output_lines" \
"$stderr_summary" >> "$SUMMARY_FILE"
done < <(find "$SIM_DIR" -type f -name '*.ngc' | sort)
{
printf 'sim config harness summary\n'
printf 'summary: %s\n' "$SUMMARY_FILE"
printf 'total: %s\n' "$total"
printf 'pass: %s\n' "$pass"
printf 'fail: %s\n' "$fail"
printf 'timeout: %s\n' "$timeout_count"
printf 'expected_fail: %s\n' "$expected_fail"
printf 'unexpected_fail: %s\n' "$unexpected_fail"
printf 'skipped: %s\n' "$skip"
} | tee "$BUILD_DIR/summary.txt"
if [[ "$unexpected_fail" -ne 0 ]]; then
echo "unexpected sim-config failures:" >&2
awk -F '\t' 'NR > 1 && $3 != "PASS" && $4 == "" { print $1 "\t" $3 "\t" $10 }' "$SUMMARY_FILE" >&2
exit 1
fi

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,108 @@
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { dirname, resolve } from "node:path";
import assert from "node:assert/strict";
import { createLinuxCncInterpSdk } from "../../../runtime/sdk/src/index.js";
function verifyExpectedOutput(fixtureName, output, expectedText) {
const expectedLines = expectedText.split("\n").filter(Boolean);
for (const expectedLine of expectedLines) {
if (expectedLine.startsWith("absent=")) {
const forbidden = expectedLine.slice("absent=".length);
assert.equal(
output.includes(forbidden),
false,
`${fixtureName}: unexpected ${forbidden}`,
);
continue;
}
assert.equal(
output.includes(expectedLine),
true,
`${fixtureName}: ${expectedLine}`,
);
}
}
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const rootDir = resolve(__dirname, "../../..");
const wasmPath = resolve(rootDir, "build/wasm/core/linuxcnc_interp.wasm");
const ncFilesDir = resolve(rootDir, "vendor/linuxcnc/nc_files");
const interp = await createLinuxCncInterpSdk({
wasmBinary: readFileSync(wasmPath),
print() {},
printErr(message) {
console.error(message);
},
});
function runVendorNcFile(filename) {
const wasmPath = `/work/nc-files/${filename}`;
interp.writeTextFile(wasmPath, readFileSync(resolve(ncFilesDir, filename), "utf8"));
return interp.runFile(wasmPath);
}
verifyExpectedOutput(
"nc_files_arcspiral_wasm",
runVendorNcFile("arcspiral.ngc"),
[
"file_open=0",
"file_read_count=1008",
"file_execute_count=1008",
"file_saw_error=0",
"canon_event=STRAIGHT_TRAVERSE line=3 x=0 y=0 z=1",
"canon_event=STRAIGHT_FEED line=6 x=1.72464 y=-1.01273 z=-0.1",
"canon_event=PROGRAM_END",
"absent=file_error_text=",
].join("\n"),
);
verifyExpectedOutput(
"nc_files_hole_circle_wasm",
runVendorNcFile("hole-circle.ngc"),
[
"file_open=0",
"file_read_count=12",
"file_execute_count=12",
"file_saw_error=0",
"canon_event=STRAIGHT_TRAVERSE line=7 x=1.5 y=0 z=0",
"canon_event=STRAIGHT_FEED line=10 x=0.75 y=1.29904 z=-0.4",
"canon_event=PROGRAM_END",
"absent=file_error_text=",
].join("\n"),
);
verifyExpectedOutput(
"nc_files_factorial_wasm",
runVendorNcFile("factorial.ngc"),
[
"file_open=0",
"file_read_count=11",
"file_execute_count=11",
"file_saw_error=0",
"canon_event=PROGRAM_END",
"absent=file_error_text=",
].join("\n"),
);
verifyExpectedOutput(
"nc_files_m6demo_wasm",
runVendorNcFile("m6demo.ngc"),
[
"file_open=0",
"file_read_count=88",
"file_execute_count=88",
"file_saw_error=0",
"canon_event=SET_G5X_OFFSET index=1",
"canon_event=STOP_SPINDLE_TURNING spindle=0",
"canon_event=PROGRAM_END",
"absent=file_error_text=",
].join("\n"),
);
console.log("nc_files_wasm_node_smoke=ok");

View File

@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
if [[ "${SKIP_INTERP_BUILD:-0}" != "1" ]]; then
"$ROOT_DIR/tools/build_wasm_core.sh"
fi
node "$ROOT_DIR/tests/wasm/node/verify_nc_files_wasm.mjs"

View File

@@ -0,0 +1,161 @@
import { readFileSync, statSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { dirname, resolve } from "node:path";
import assert from "node:assert/strict";
import { createLinuxCncInterpSdk } from "../../../runtime/sdk/src/index.js";
function verifyExpectedOutput(fixtureName, output, expectedText) {
const expectedLines = expectedText.split("\n").filter(Boolean);
for (const expectedLine of expectedLines) {
if (expectedLine.startsWith("absent=")) {
const forbidden = expectedLine.slice("absent=".length);
assert.equal(
output.includes(forbidden),
false,
`${fixtureName}: unexpected ${forbidden}`,
);
continue;
}
assert.equal(
output.includes(expectedLine),
true,
`${fixtureName}: ${expectedLine}`,
);
}
}
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const rootDir = resolve(__dirname, "../../..");
const wasmPath = resolve(rootDir, "build/wasm/core/linuxcnc_interp.wasm");
const vendorRoot = resolve(rootDir, "vendor/linuxcnc");
const interp = await createLinuxCncInterpSdk({
wasmBinary: readFileSync(wasmPath),
print() {},
printErr(message) {
console.error(message);
},
});
function vendorFile(sourceRel, wasmPath) {
const sourcePath = resolve(vendorRoot, sourceRel);
return {
path: wasmPath,
text: readFileSync(sourcePath, "utf8"),
executable: (statSync(sourcePath).mode & 0o111) !== 0,
};
}
function simMachine(machineRel, files) {
const wasmDir = `/work/sim/${machineRel}`;
return {
wasmDir,
files: files.map((file) =>
vendorFile(`configs/sim/${machineRel}/${file}`, `${wasmDir}/${file}`),
),
};
}
const foam = simMachine("axis/foam", [
"axis_foam.ini",
"foam.ngc",
]);
verifyExpectedOutput(
"sim_axis_foam_uv_wasm",
interp.runSimConfigProgram({
files: foam.files,
programPath: `${foam.wasmDir}/foam.ngc`,
iniPath: `${foam.wasmDir}/axis_foam.ini`,
}),
[
"file_open=0",
"file_saw_error=0",
"canon_event=PROGRAM_END",
"absent=Bad character 'u' used",
"absent=Bad character 'v' used",
].join("\n"),
);
const bridgeMill = simMachine("axis/vismach/5axis/bridgemill", [
"5axis.ini",
"5axis.tbl",
"5axisgui.ngc",
"remap_subs/428remap.ngc",
"remap_subs/429remap.ngc",
"remap_subs/430remap.ngc",
]);
verifyExpectedOutput(
"sim_axis_bridgemill_w_wasm",
interp.runSimConfigProgram({
files: bridgeMill.files,
programPath: `${bridgeMill.wasmDir}/5axisgui.ngc`,
iniPath: `${bridgeMill.wasmDir}/5axis.ini`,
executionMode: "fiveAxisRemap",
}),
[
"fiveaxis_ini_open=1",
"fiveaxis_tool_table_load=0",
"fiveaxis_parse_remap_1=0",
"fiveaxis_parse_remap_2=0",
"fiveaxis_parse_remap_3=0",
"fiveaxis_parsed_remap_count=3",
"fiveaxis_remaps_ready=1",
"fiveaxis_file_open=0",
"fiveaxis_file_read_count=2197",
"fiveaxis_file_execute_count=2197",
"fiveaxis_file_finish_count=137",
"fiveaxis_file_reached_exit=1",
"fiveaxis_hal_switchkins: rc=0 found=1 value=0",
"fiveaxis_linuxcnc_remap_file_execute=1",
"absent=Bad character 'w' used",
].join("\n"),
);
const geometry = simMachine("axis/geometry", [
"M110",
"xyzc.ini",
"xyzc.ngc",
]);
verifyExpectedOutput(
"sim_axis_geometry_xyzc_user_m110_wasm",
interp.runSimConfigProgram({
files: geometry.files,
programPath: `${geometry.wasmDir}/xyzc.ngc`,
iniPath: `${geometry.wasmDir}/xyzc.ini`,
}),
[
"file_open=0",
"file_saw_error=0",
"canon_event=USER_M_COMMAND code=M110",
"canon_event=PROGRAM_END",
"absent=Unknown m code used: M110",
"absent=Bad character 'c' used",
].join("\n"),
);
const externalOffsets = simMachine("axis/external_offsets", [
"M111",
"dyn_demo.ngc",
"dynamic_offsets.ini",
]);
verifyExpectedOutput(
"sim_axis_external_offsets_user_m111_wasm",
interp.runSimConfigProgram({
files: externalOffsets.files,
programPath: `${externalOffsets.wasmDir}/dyn_demo.ngc`,
iniPath: `${externalOffsets.wasmDir}/dynamic_offsets.ini`,
}),
[
"file_open=0",
"file_saw_error=0",
"canon_event=USER_M_COMMAND code=M111",
"canon_event=PROGRAM_END",
"absent=Unknown m code used: M111",
].join("\n"),
);
console.log("sim_configs_wasm_node_smoke=ok");

View File

@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
if [[ "${SKIP_INTERP_BUILD:-0}" != "1" ]]; then
"$ROOT_DIR/tools/build_wasm_core.sh"
fi
node "$ROOT_DIR/tests/wasm/node/verify_sim_configs_wasm.mjs"

View File

@@ -9,11 +9,116 @@ SHIM_DIR="$ROOT_DIR/runtime/core/shims"
INCLUDE_DIR="$ROOT_DIR/runtime/core/include" INCLUDE_DIR="$ROOT_DIR/runtime/core/include"
SOURCE_PROBE_MAP="$BUILD_DIR/source-probes.tsv" SOURCE_PROBE_MAP="$BUILD_DIR/source-probes.tsv"
MINIMAL_GCODE_FIXTURE="$ROOT_DIR/tests/fixtures/gcode/minimal_linear.ngc" MINIMAL_GCODE_FIXTURE="$ROOT_DIR/tests/fixtures/gcode/minimal_linear.ngc"
DO_WHILE_BREAK_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/do-while-break/test.ngc"
OWORD_BUG315_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/oword-bug315/test.ngc"
OWORD_BUG315_P2_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/oword-bug315-p2/test.ngc"
EXISTS_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/exists/test.ngc"
RETURN_VALUE_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/return-value/test.ngc"
SUBS_FOLLOW_MAIN_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/subs-follow-main/test.ngc"
FRACTIONAL_LINENUMBERS_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/fractional-linenumbers/test.ngc"
NAMEDPARAM_BUG424_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/namedparam-bug424/test.ngc"
G6164_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/g6164/test.ngc"
ROTATION_ABS_PTS_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/rotation/abs-pts/test.ngc"
ROTATION_G28_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/rotation/g28/g28.ngc"
ROTATION_G53_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/rotation/g53/g53.ngc"
INIPARAM_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/iniparam/test.ngc"
INIPARAM_INI="$ROOT_DIR/vendor/linuxcnc/tests/interp/iniparam/test.ini"
INIPARAM_FAILASSIGN_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/iniparam-failassign/test.ngc"
INIPARAM_FAILASSIGN_INI="$ROOT_DIR/vendor/linuxcnc/tests/interp/iniparam-failassign/test.ini"
SUB_CALL_FROM_SUB_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/sub-call-from-sub/test.ngc"
SUB_CALL_FROM_SUB_INI="$ROOT_DIR/vendor/linuxcnc/tests/interp/sub-call-from-sub/test.ini"
SEQUENCE_NUMBER_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/sequence-number/test.ngc"
SEQUENCE_NUMBER_INI="$ROOT_DIR/vendor/linuxcnc/tests/interp/sequence-number/test.ini"
NESTED_SUB_ERROR_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/nested-sub-error/test.ngc"
NESTED_SUB_ERROR_INI="$ROOT_DIR/vendor/linuxcnc/tests/interp/nested-sub-error/test.ini"
NESTED_SUB_IN_FILE_ERROR_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/nested-sub-in-file-error/test.ngc"
NESTED_SUB_IN_FILE_ERROR_INI="$ROOT_DIR/vendor/linuxcnc/tests/interp/nested-sub-in-file-error/test.ini"
OWORD_UNWIND_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/oword-unwind/test.ngc"
OWORD_UNWIND_INI="$ROOT_DIR/vendor/linuxcnc/tests/interp/oword-unwind/test.ini"
ABORT_HOT_COMMENT_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/abort-hot-comment/test.ngc"
ABORT_HOT_COMMENT_INI="$ROOT_DIR/vendor/linuxcnc/tests/interp/abort-hot-comment/test.ini"
M19_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/m19/test.ngc"
M19_INI="$ROOT_DIR/vendor/linuxcnc/tests/interp/m19/test.ini"
MAGIC_COMMENTS_PARAM_FORMAT_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/magic_comments/param_format_printing/test.ngc"
M98M99_01_BASICS_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/m98m99/01-basics/test.ngc"
M98M99_02_VARIABLES_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/m98m99/02-variables/test.ngc"
M98M99_07_NESTED_SUBS_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/m98m99/07-nested-subs/test.ngc"
M98M99_08_SUB_FOLLOWS_MAIN_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/m98m99/08-sub-follows-main/test.ngc"
M98M99_10_M98_P001_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/m98m99/10-M98-P001/test.ngc"
M98M99_13_NAMED_PROGRAM_NAMED_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/m98m99/13-named-program/test-named.ngc"
M98M99_13_NAMED_PROGRAM_NUMBERED_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/m98m99/13-named-program/test-numbered.ngc"
M98M99_14_O_EXPRESSION_CALL_FIXTURE="$ROOT_DIR/vendor/linuxcnc/tests/interp/m98m99/14-o-expression-call/test.ngc"
SIM_AXIS_FOAM_FIXTURE="$ROOT_DIR/../linuxcnc/configs/sim/axis/foam/foam.ngc"
SIM_AXIS_FOAM_INI="$ROOT_DIR/../linuxcnc/configs/sim/axis/foam/axis_foam.ini"
SIM_BRIDGEMILL_FIXTURE="$ROOT_DIR/../linuxcnc/configs/sim/axis/vismach/5axis/bridgemill/5axisgui.ngc"
SIM_BRIDGEMILL_INI="$ROOT_DIR/../linuxcnc/configs/sim/axis/vismach/5axis/bridgemill/5axis.ini"
SIM_GEOMETRY_XYZC_FIXTURE="$ROOT_DIR/../linuxcnc/configs/sim/axis/geometry/xyzc.ngc"
SIM_GEOMETRY_XYZC_INI="$ROOT_DIR/../linuxcnc/configs/sim/axis/geometry/xyzc.ini"
SIM_EXTERNAL_OFFSETS_DYN_FIXTURE="$ROOT_DIR/../linuxcnc/configs/sim/axis/external_offsets/dyn_demo.ngc"
SIM_EXTERNAL_OFFSETS_DYN_INI="$ROOT_DIR/../linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini"
NAMEDPARAM_INI_FIXTURE="$ROOT_DIR/tests/fixtures/ini/namedparams.ini" NAMEDPARAM_INI_FIXTURE="$ROOT_DIR/tests/fixtures/ini/namedparams.ini"
CXX="${CXX:-g++}" CXX="${CXX:-g++}"
CC="${CC:-gcc}" CC="${CC:-gcc}"
CACHE_KEY_FILE="$BUILD_DIR/native-probes.input.sha256"
CACHE_OK_FILE="$BUILD_DIR/native-probes.ok"
CACHE_OUTPUT_MANIFEST="$BUILD_DIR/native-probes.outputs"
NATIVE_PROBE_RUN_TIMEOUT_SECONDS="${NATIVE_PROBE_RUN_TIMEOUT_SECONDS:-60}"
mkdir -p "$BUILD_DIR" mkdir -p "$BUILD_DIR"
hash_tree() {
find "$@" -type f -print0 | sort -z | xargs -0 -r sha256sum
}
native_input_fingerprint() {
{
printf 'CXX=%s\n' "$CXX"
"$CXX" --version 2>/dev/null | sed -n '1p' || true
printf 'CC=%s\n' "$CC"
"$CC" --version 2>/dev/null | sed -n '1p' || true
sha256sum \
"$ROOT_DIR/tools/build_native_probes.sh" \
"$ROOT_DIR/tools/source-manifest.txt"
hash_tree \
"$VENDOR_DIR" \
"$WRAP_DIR" \
"$SHIM_DIR" \
"$INCLUDE_DIR" \
"$ROOT_DIR/tests/fixtures"
} | sha256sum | awk '{ print $1 }'
}
cached_native_outputs_exist() {
[[ -f "$CACHE_OUTPUT_MANIFEST" ]] || return 1
[[ -s "$SOURCE_PROBE_MAP" ]] || return 1
local file
while IFS= read -r file; do
[[ -z "$file" ]] && continue
[[ -f "$file" ]] || return 1
done < "$CACHE_OUTPUT_MANIFEST"
}
write_native_cache_stamp() {
local key="$1"
printf '%s\n' "$key" > "$CACHE_KEY_FILE"
find "$BUILD_DIR" -maxdepth 1 -type f \
! -name 'native-probes.input.sha256' \
! -name 'native-probes.ok' \
! -name 'native-probes.outputs' \
-print | sort > "$CACHE_OUTPUT_MANIFEST"
printf '%s\n' "$key" > "$CACHE_OK_FILE"
}
NATIVE_INPUT_FINGERPRINT="$(native_input_fingerprint)"
if [[ -f "$CACHE_KEY_FILE" && -f "$CACHE_OK_FILE" ]] &&
[[ "$(tr -d '[:space:]' < "$CACHE_KEY_FILE")" == "$NATIVE_INPUT_FINGERPRINT" ]] &&
[[ "$(tr -d '[:space:]' < "$CACHE_OK_FILE")" == "$NATIVE_INPUT_FINGERPRINT" ]] &&
cached_native_outputs_exist; then
echo "native probes up to date"
exit 0
fi
: > "$SOURCE_PROBE_MAP" : > "$SOURCE_PROBE_MAP"
COMMON_FLAGS=( COMMON_FLAGS=(
@@ -145,6 +250,21 @@ object_needs_rebuild() {
return 1 return 1
} }
shared_object_path() {
local source="$1"
shift
local base key
base="$(basename "$source")"
key="$(
{
printf 'source=%q\n' "$source"
printf 'arg=%q\n' "$@"
} | sha256sum | awk '{ print $1 }'
)"
printf '%s/obj/shared/%s-%s.o\n' "$BUILD_DIR" "${base%.*}" "$key"
}
compile_object() { compile_object() {
local target="$1" local target="$1"
local source="$2" local source="$2"
@@ -255,15 +375,13 @@ compile_target_objects() {
local -n flags="$flags_name" local -n flags="$flags_name"
local -n sources="$sources_name" local -n sources="$sources_name"
local -n objs="$objs_name" local -n objs="$objs_name"
local obj_dir="$BUILD_DIR/obj/$target"
local status=0 local status=0
objs=() objs=()
local source local source
for source in "${sources[@]}"; do for source in "${sources[@]}"; do
local base local obj
base="$(basename "$source")" obj="$(shared_object_path "$source" "${flags[@]}")"
local obj="$obj_dir/${base%.*}.o"
objs+=("$obj") objs+=("$obj")
if ! compile_object "$target" "$source" "$obj" "${flags[@]}"; then if ! compile_object "$target" "$source" "$obj" "${flags[@]}"; then
status=1 status=1
@@ -347,6 +465,65 @@ build_c_object_target() {
return "$status" return "$status"
} }
run_command_needs_update() {
local exitfile="$1"
local stdout_log="$2"
local stderr_log="$3"
local cmdfile="$4"
shift 4
[[ -f "$exitfile" && -f "$stdout_log" && -f "$stderr_log" ]] || return 0
command_matches "$cmdfile" "$@" || return 0
local arg
for arg in "$@"; do
if [[ -f "$arg" && "$arg" -nt "$exitfile" ]]; then
return 0
fi
done
return 1
}
run_logged_command() {
local label="$1"
shift
local exitfile="$BUILD_DIR/$label.run.exitcode"
local stdout_log="$BUILD_DIR/$label.run.stdout.log"
local stderr_log="$BUILD_DIR/$label.run.stderr.log"
local cmdfile="$BUILD_DIR/$label.run.cmd"
if ! run_command_needs_update "$exitfile" "$stdout_log" "$stderr_log" "$cmdfile" "$@"; then
return 0
fi
set +e
if command -v timeout >/dev/null 2>&1; then
timeout "$NATIVE_PROBE_RUN_TIMEOUT_SECONDS" "$@" >"$stdout_log" 2>"$stderr_log"
else
"$@" >"$stdout_log" 2>"$stderr_log"
fi
local run_rc=$?
set -e
if [[ "$run_rc" == "124" ]]; then
printf 'run timed out after %s seconds\n' "$NATIVE_PROBE_RUN_TIMEOUT_SECONDS" >>"$stderr_log"
fi
write_command_file "$cmdfile" "$@"
echo "$run_rc" > "$exitfile"
}
clear_logged_command() {
local label="$1"
rm -f \
"$BUILD_DIR/$label.run.exitcode" \
"$BUILD_DIR/$label.run.stdout.log" \
"$BUILD_DIR/$label.run.stderr.log" \
"$BUILD_DIR/$label.run.cmd"
}
NO_LINK_FLAGS=() NO_LINK_FLAGS=()
INI_PROBE_SOURCES=( INI_PROBE_SOURCES=(
@@ -636,6 +813,7 @@ INTERP_CORE_SOURCES=(
"$WRAP_DIR/linuxcnc_hal_adapter.cpp" "$WRAP_DIR/linuxcnc_hal_adapter.cpp"
"$WRAP_DIR/linuxcnc_emc_status_runtime.cpp" "$WRAP_DIR/linuxcnc_emc_status_runtime.cpp"
"$WRAP_DIR/linuxcnc_interp_edge_stubs.cpp" "$WRAP_DIR/linuxcnc_interp_edge_stubs.cpp"
"$WRAP_DIR/linuxcnc_machine_config.cpp"
"$WRAP_DIR/linuxcnc_runtime_state_stubs.cpp" "$WRAP_DIR/linuxcnc_runtime_state_stubs.cpp"
"$WRAP_DIR/linuxcnc_tool_adapter.cpp" "$WRAP_DIR/linuxcnc_tool_adapter.cpp"
"$WRAP_DIR/linuxcnc_interp_minimal_runtime.cpp" "$WRAP_DIR/linuxcnc_interp_minimal_runtime.cpp"
@@ -690,6 +868,7 @@ REMAP_RUNTIME_CORE_SOURCES=(
"$WRAP_DIR/linuxcnc_hal_adapter.cpp" "$WRAP_DIR/linuxcnc_hal_adapter.cpp"
"$WRAP_DIR/linuxcnc_emc_status_runtime.cpp" "$WRAP_DIR/linuxcnc_emc_status_runtime.cpp"
"$WRAP_DIR/linuxcnc_interp_python_edge_stubs.cpp" "$WRAP_DIR/linuxcnc_interp_python_edge_stubs.cpp"
"$WRAP_DIR/linuxcnc_machine_config.cpp"
"$WRAP_DIR/linuxcnc_runtime_state_stubs.cpp" "$WRAP_DIR/linuxcnc_runtime_state_stubs.cpp"
"$WRAP_DIR/linuxcnc_tool_adapter.cpp" "$WRAP_DIR/linuxcnc_tool_adapter.cpp"
"$WRAP_DIR/linuxcnc_interp_minimal_runtime.cpp" "$WRAP_DIR/linuxcnc_interp_minimal_runtime.cpp"
@@ -711,6 +890,31 @@ FIVEAXIS_REMAP_EXECUTE_SOURCES=(
"$WRAP_DIR/linuxcnc_5axis_remap_execute_harness.cpp" "$WRAP_DIR/linuxcnc_5axis_remap_execute_harness.cpp"
) )
DUPLICATE_OWORD_REMAP_SOURCES=(
"${REMAP_RUNTIME_CORE_SOURCES[@]}"
"$WRAP_DIR/linuxcnc_duplicate_oword_remap_harness.cpp"
)
if [[ "${NATIVE_PROBE_ONLY:-}" == "linuxcnc_duplicate_oword_remap_harness" ]]; then
build_binary_target \
linuxcnc_duplicate_oword_remap_harness \
"$BUILD_DIR/linuxcnc_duplicate_oword_remap_harness" \
REMAP_RUNTIME_FLAGS \
DUPLICATE_OWORD_REMAP_SOURCES \
MINIMAL_LINK_FLAGS
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_duplicate_oword_remap_harness.exitcode")" == "0" ]]; then
run_logged_command \
linuxcnc_duplicate_oword_remap_harness \
"$BUILD_DIR/linuxcnc_duplicate_oword_remap_harness"
else
clear_logged_command linuxcnc_duplicate_oword_remap_harness
fi
echo "native probe target complete: linuxcnc_duplicate_oword_remap_harness"
exit 0
fi
build_binary_target \ build_binary_target \
linuxcnc_ini_probe \ linuxcnc_ini_probe \
"$BUILD_DIR/linuxcnc_ini_probe" \ "$BUILD_DIR/linuxcnc_ini_probe" \
@@ -1190,18 +1394,11 @@ build_binary_target \
-lfmt -lfmt
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_namedparam_harness.exitcode")" == "0" ]]; then if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_namedparam_harness.exitcode")" == "0" ]]; then
set +e run_logged_command \
"$BUILD_DIR/linuxcnc_namedparam_harness" "$NAMEDPARAM_INI_FIXTURE" \ linuxcnc_namedparam_harness \
>"$BUILD_DIR/linuxcnc_namedparam_harness.run.stdout.log" \ "$BUILD_DIR/linuxcnc_namedparam_harness" "$NAMEDPARAM_INI_FIXTURE"
2>"$BUILD_DIR/linuxcnc_namedparam_harness.run.stderr.log"
NAMEDPARAM_RUN_RC=$?
set -e
echo "$NAMEDPARAM_RUN_RC" > "$BUILD_DIR/linuxcnc_namedparam_harness.run.exitcode"
else else
rm -f \ clear_logged_command linuxcnc_namedparam_harness
"$BUILD_DIR/linuxcnc_namedparam_harness.run.exitcode" \
"$BUILD_DIR/linuxcnc_namedparam_harness.run.stdout.log" \
"$BUILD_DIR/linuxcnc_namedparam_harness.run.stderr.log"
fi fi
build_binary_target \ build_binary_target \
@@ -1213,20 +1410,90 @@ build_binary_target \
-lfmt -lfmt
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_interp_minimal_harness.exitcode")" == "0" ]]; then if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_interp_minimal_harness.exitcode")" == "0" ]]; then
set +e run_logged_command \
"$BUILD_DIR/linuxcnc_interp_minimal_harness" "$MINIMAL_GCODE_FIXTURE" \ linuxcnc_interp_minimal_harness \
>"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.stdout.log" \ "$BUILD_DIR/linuxcnc_interp_minimal_harness" "$MINIMAL_GCODE_FIXTURE"
2>"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.stderr.log"
INTERP_MIN_RUN_RC=$?
set -e
echo "$INTERP_MIN_RUN_RC" > "$BUILD_DIR/linuxcnc_interp_minimal_harness.run.exitcode"
else else
rm -f \ clear_logged_command linuxcnc_interp_minimal_harness
"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.exitcode" \
"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.stdout.log" \
"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.stderr.log"
fi fi
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_interp_minimal_harness.exitcode")" == "0" ]]; then
run_logged_command \
linuxcnc_interp_minimal_harness.do_while_break \
"$BUILD_DIR/linuxcnc_interp_minimal_harness" "$DO_WHILE_BREAK_FIXTURE"
else
clear_logged_command linuxcnc_interp_minimal_harness.do_while_break
fi
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_interp_minimal_harness.exitcode")" == "0" ]]; then
run_logged_command \
linuxcnc_interp_minimal_harness.oword_bug315 \
"$BUILD_DIR/linuxcnc_interp_minimal_harness" "$OWORD_BUG315_FIXTURE"
else
clear_logged_command linuxcnc_interp_minimal_harness.oword_bug315
fi
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_interp_minimal_harness.exitcode")" == "0" ]]; then
run_logged_command \
linuxcnc_interp_minimal_harness.oword_bug315_p2 \
"$BUILD_DIR/linuxcnc_interp_minimal_harness" "$OWORD_BUG315_P2_FIXTURE"
else
clear_logged_command linuxcnc_interp_minimal_harness.oword_bug315_p2
fi
run_interp_minimal_fixture() {
local label="$1"
local fixture="$2"
local ini="${3:-}"
local mode="${4:-}"
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_interp_minimal_harness.exitcode")" == "0" ]]; then
if [[ "$mode" == "continue_on_error" ]]; then
run_logged_command \
"linuxcnc_interp_minimal_harness.$label" \
"$BUILD_DIR/linuxcnc_interp_minimal_harness" --continue-on-error "$fixture" "$ini"
else
run_logged_command \
"linuxcnc_interp_minimal_harness.$label" \
"$BUILD_DIR/linuxcnc_interp_minimal_harness" "$fixture" "$ini"
fi
else
clear_logged_command "linuxcnc_interp_minimal_harness.$label"
fi
}
run_interp_minimal_fixture interp_exists "$EXISTS_FIXTURE"
run_interp_minimal_fixture return_value "$RETURN_VALUE_FIXTURE"
run_interp_minimal_fixture subs_follow_main "$SUBS_FOLLOW_MAIN_FIXTURE"
run_interp_minimal_fixture fractional_linenumbers "$FRACTIONAL_LINENUMBERS_FIXTURE"
run_interp_minimal_fixture namedparam_bug424 "$NAMEDPARAM_BUG424_FIXTURE"
run_interp_minimal_fixture g6164 "$G6164_FIXTURE"
run_interp_minimal_fixture rotation_abs_pts "$ROTATION_ABS_PTS_FIXTURE"
run_interp_minimal_fixture rotation_g28 "$ROTATION_G28_FIXTURE"
run_interp_minimal_fixture rotation_g53 "$ROTATION_G53_FIXTURE"
run_interp_minimal_fixture iniparam "$INIPARAM_FIXTURE" "$INIPARAM_INI" continue_on_error
run_interp_minimal_fixture iniparam_failassign "$INIPARAM_FAILASSIGN_FIXTURE" "$INIPARAM_FAILASSIGN_INI"
run_interp_minimal_fixture sub_call_from_sub "$SUB_CALL_FROM_SUB_FIXTURE" "$SUB_CALL_FROM_SUB_INI"
run_interp_minimal_fixture sequence_number "$SEQUENCE_NUMBER_FIXTURE" "$SEQUENCE_NUMBER_INI"
run_interp_minimal_fixture nested_sub_error "$NESTED_SUB_ERROR_FIXTURE" "$NESTED_SUB_ERROR_INI"
run_interp_minimal_fixture nested_sub_in_file_error "$NESTED_SUB_IN_FILE_ERROR_FIXTURE" "$NESTED_SUB_IN_FILE_ERROR_INI"
run_interp_minimal_fixture oword_unwind "$OWORD_UNWIND_FIXTURE" "$OWORD_UNWIND_INI" continue_on_error
run_interp_minimal_fixture abort_hot_comment "$ABORT_HOT_COMMENT_FIXTURE" "$ABORT_HOT_COMMENT_INI" continue_on_error
run_interp_minimal_fixture m19 "$M19_FIXTURE" "$M19_INI"
run_interp_minimal_fixture magic_comments_param_format "$MAGIC_COMMENTS_PARAM_FORMAT_FIXTURE"
run_interp_minimal_fixture m98m99_01_basics "$M98M99_01_BASICS_FIXTURE"
run_interp_minimal_fixture m98m99_02_variables "$M98M99_02_VARIABLES_FIXTURE"
run_interp_minimal_fixture m98m99_07_nested_subs "$M98M99_07_NESTED_SUBS_FIXTURE"
run_interp_minimal_fixture m98m99_08_sub_follows_main "$M98M99_08_SUB_FOLLOWS_MAIN_FIXTURE"
run_interp_minimal_fixture m98m99_10_m98_p001 "$M98M99_10_M98_P001_FIXTURE"
run_interp_minimal_fixture m98m99_13_named_program_named "$M98M99_13_NAMED_PROGRAM_NAMED_FIXTURE"
run_interp_minimal_fixture m98m99_13_named_program_numbered "$M98M99_13_NAMED_PROGRAM_NUMBERED_FIXTURE"
run_interp_minimal_fixture m98m99_14_o_expression_call "$M98M99_14_O_EXPRESSION_CALL_FIXTURE"
run_interp_minimal_fixture sim_axis_foam "$SIM_AXIS_FOAM_FIXTURE" "$SIM_AXIS_FOAM_INI" continue_on_error
run_interp_minimal_fixture sim_bridgemill "$SIM_BRIDGEMILL_FIXTURE" "$SIM_BRIDGEMILL_INI" continue_on_error
run_interp_minimal_fixture sim_geometry_xyzc "$SIM_GEOMETRY_XYZC_FIXTURE" "$SIM_GEOMETRY_XYZC_INI" continue_on_error
run_interp_minimal_fixture sim_external_offsets_dyn "$SIM_EXTERNAL_OFFSETS_DYN_FIXTURE" "$SIM_EXTERNAL_OFFSETS_DYN_INI" continue_on_error
build_binary_target \ build_binary_target \
linuxcnc_parameter_file_harness \ linuxcnc_parameter_file_harness \
"$BUILD_DIR/linuxcnc_parameter_file_harness" \ "$BUILD_DIR/linuxcnc_parameter_file_harness" \
@@ -1236,18 +1503,11 @@ build_binary_target \
-lfmt -lfmt
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_parameter_file_harness.exitcode")" == "0" ]]; then if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_parameter_file_harness.exitcode")" == "0" ]]; then
set +e run_logged_command \
"$BUILD_DIR/linuxcnc_parameter_file_harness" \ linuxcnc_parameter_file_harness \
>"$BUILD_DIR/linuxcnc_parameter_file_harness.run.stdout.log" \ "$BUILD_DIR/linuxcnc_parameter_file_harness"
2>"$BUILD_DIR/linuxcnc_parameter_file_harness.run.stderr.log"
PARAMETER_FILE_RUN_RC=$?
set -e
echo "$PARAMETER_FILE_RUN_RC" > "$BUILD_DIR/linuxcnc_parameter_file_harness.run.exitcode"
else else
rm -f \ clear_logged_command linuxcnc_parameter_file_harness
"$BUILD_DIR/linuxcnc_parameter_file_harness.run.exitcode" \
"$BUILD_DIR/linuxcnc_parameter_file_harness.run.stdout.log" \
"$BUILD_DIR/linuxcnc_parameter_file_harness.run.stderr.log"
fi fi
build_binary_target \ build_binary_target \
@@ -1259,18 +1519,11 @@ build_binary_target \
-lfmt -lfmt
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_interp_init_harness.exitcode")" == "0" ]]; then if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_interp_init_harness.exitcode")" == "0" ]]; then
set +e run_logged_command \
"$BUILD_DIR/linuxcnc_interp_init_harness" \ linuxcnc_interp_init_harness \
>"$BUILD_DIR/linuxcnc_interp_init_harness.run.stdout.log" \ "$BUILD_DIR/linuxcnc_interp_init_harness"
2>"$BUILD_DIR/linuxcnc_interp_init_harness.run.stderr.log"
INTERP_INIT_RUN_RC=$?
set -e
echo "$INTERP_INIT_RUN_RC" > "$BUILD_DIR/linuxcnc_interp_init_harness.run.exitcode"
else else
rm -f \ clear_logged_command linuxcnc_interp_init_harness
"$BUILD_DIR/linuxcnc_interp_init_harness.run.exitcode" \
"$BUILD_DIR/linuxcnc_interp_init_harness.run.stdout.log" \
"$BUILD_DIR/linuxcnc_interp_init_harness.run.stderr.log"
fi fi
build_binary_target \ build_binary_target \
@@ -1282,18 +1535,11 @@ build_binary_target \
-lfmt -lfmt
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_remap_parse_harness.exitcode")" == "0" ]]; then if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_remap_parse_harness.exitcode")" == "0" ]]; then
set +e run_logged_command \
"$BUILD_DIR/linuxcnc_remap_parse_harness" \ linuxcnc_remap_parse_harness \
>"$BUILD_DIR/linuxcnc_remap_parse_harness.run.stdout.log" \ "$BUILD_DIR/linuxcnc_remap_parse_harness"
2>"$BUILD_DIR/linuxcnc_remap_parse_harness.run.stderr.log"
REMAP_PARSE_RUN_RC=$?
set -e
echo "$REMAP_PARSE_RUN_RC" > "$BUILD_DIR/linuxcnc_remap_parse_harness.run.exitcode"
else else
rm -f \ clear_logged_command linuxcnc_remap_parse_harness
"$BUILD_DIR/linuxcnc_remap_parse_harness.run.exitcode" \
"$BUILD_DIR/linuxcnc_remap_parse_harness.run.stdout.log" \
"$BUILD_DIR/linuxcnc_remap_parse_harness.run.stderr.log"
fi fi
build_binary_target \ build_binary_target \
@@ -1304,18 +1550,11 @@ build_binary_target \
MINIMAL_LINK_FLAGS MINIMAL_LINK_FLAGS
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_remap_hal_sync_harness.exitcode")" == "0" ]]; then if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_remap_hal_sync_harness.exitcode")" == "0" ]]; then
set +e run_logged_command \
"$BUILD_DIR/linuxcnc_remap_hal_sync_harness" \ linuxcnc_remap_hal_sync_harness \
>"$BUILD_DIR/linuxcnc_remap_hal_sync_harness.run.stdout.log" \ "$BUILD_DIR/linuxcnc_remap_hal_sync_harness"
2>"$BUILD_DIR/linuxcnc_remap_hal_sync_harness.run.stderr.log"
REMAP_HAL_SYNC_RUN_RC=$?
set -e
echo "$REMAP_HAL_SYNC_RUN_RC" > "$BUILD_DIR/linuxcnc_remap_hal_sync_harness.run.exitcode"
else else
rm -f \ clear_logged_command linuxcnc_remap_hal_sync_harness
"$BUILD_DIR/linuxcnc_remap_hal_sync_harness.run.exitcode" \
"$BUILD_DIR/linuxcnc_remap_hal_sync_harness.run.stdout.log" \
"$BUILD_DIR/linuxcnc_remap_hal_sync_harness.run.stderr.log"
fi fi
build_binary_target \ build_binary_target \
@@ -1326,18 +1565,26 @@ build_binary_target \
MINIMAL_LINK_FLAGS MINIMAL_LINK_FLAGS
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_5axis_remap_execute_harness.exitcode")" == "0" ]]; then if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_5axis_remap_execute_harness.exitcode")" == "0" ]]; then
set +e run_logged_command \
"$BUILD_DIR/linuxcnc_5axis_remap_execute_harness" \ linuxcnc_5axis_remap_execute_harness \
>"$BUILD_DIR/linuxcnc_5axis_remap_execute_harness.run.stdout.log" \ "$BUILD_DIR/linuxcnc_5axis_remap_execute_harness"
2>"$BUILD_DIR/linuxcnc_5axis_remap_execute_harness.run.stderr.log"
FIVEAXIS_REMAP_EXECUTE_RUN_RC=$?
set -e
echo "$FIVEAXIS_REMAP_EXECUTE_RUN_RC" > "$BUILD_DIR/linuxcnc_5axis_remap_execute_harness.run.exitcode"
else else
rm -f \ clear_logged_command linuxcnc_5axis_remap_execute_harness
"$BUILD_DIR/linuxcnc_5axis_remap_execute_harness.run.exitcode" \ fi
"$BUILD_DIR/linuxcnc_5axis_remap_execute_harness.run.stdout.log" \
"$BUILD_DIR/linuxcnc_5axis_remap_execute_harness.run.stderr.log" build_binary_target \
linuxcnc_duplicate_oword_remap_harness \
"$BUILD_DIR/linuxcnc_duplicate_oword_remap_harness" \
REMAP_RUNTIME_FLAGS \
DUPLICATE_OWORD_REMAP_SOURCES \
MINIMAL_LINK_FLAGS
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_duplicate_oword_remap_harness.exitcode")" == "0" ]]; then
run_logged_command \
linuxcnc_duplicate_oword_remap_harness \
"$BUILD_DIR/linuxcnc_duplicate_oword_remap_harness"
else
clear_logged_command linuxcnc_duplicate_oword_remap_harness
fi fi
build_binary_target \ build_binary_target \
@@ -1349,18 +1596,11 @@ build_binary_target \
-lfmt -lfmt
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_indexer_harness.exitcode")" == "0" ]]; then if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_indexer_harness.exitcode")" == "0" ]]; then
set +e run_logged_command \
"$BUILD_DIR/linuxcnc_indexer_harness" \ linuxcnc_indexer_harness \
>"$BUILD_DIR/linuxcnc_indexer_harness.run.stdout.log" \ "$BUILD_DIR/linuxcnc_indexer_harness"
2>"$BUILD_DIR/linuxcnc_indexer_harness.run.stderr.log"
INDEXER_RUN_RC=$?
set -e
echo "$INDEXER_RUN_RC" > "$BUILD_DIR/linuxcnc_indexer_harness.run.exitcode"
else else
rm -f \ clear_logged_command linuxcnc_indexer_harness
"$BUILD_DIR/linuxcnc_indexer_harness.run.exitcode" \
"$BUILD_DIR/linuxcnc_indexer_harness.run.stdout.log" \
"$BUILD_DIR/linuxcnc_indexer_harness.run.stderr.log"
fi fi
build_object_target \ build_object_target \
@@ -1639,4 +1879,5 @@ build_object_target \
"$VENDOR_DIR/src/emc/tooldata/tooldata_common.cc" \ "$VENDOR_DIR/src/emc/tooldata/tooldata_common.cc" \
MINIMAL_FLAGS MINIMAL_FLAGS
write_native_cache_stamp "$NATIVE_INPUT_FINGERPRINT"
echo "native probes complete" echo "native probes complete"

View File

@@ -65,6 +65,7 @@ INTERP_CORE_SOURCES=(
"$WRAP_DIR/linuxcnc_hal_adapter.cpp" "$WRAP_DIR/linuxcnc_hal_adapter.cpp"
"$WRAP_DIR/linuxcnc_emc_status_runtime.cpp" "$WRAP_DIR/linuxcnc_emc_status_runtime.cpp"
"$WRAP_DIR/linuxcnc_interp_python_edge_stubs.cpp" "$WRAP_DIR/linuxcnc_interp_python_edge_stubs.cpp"
"$WRAP_DIR/linuxcnc_machine_config.cpp"
"$WRAP_DIR/linuxcnc_runtime_state_stubs.cpp" "$WRAP_DIR/linuxcnc_runtime_state_stubs.cpp"
"$WRAP_DIR/linuxcnc_tool_adapter.cpp" "$WRAP_DIR/linuxcnc_tool_adapter.cpp"
"$WRAP_DIR/linuxcnc_interp_minimal_runtime.cpp" "$WRAP_DIR/linuxcnc_interp_minimal_runtime.cpp"
@@ -97,5 +98,5 @@ link_wasm_module \
-s STACK_SIZE=2MB \ -s STACK_SIZE=2MB \
-s NO_EXIT_RUNTIME=1 \ -s NO_EXIT_RUNTIME=1 \
-s FORCE_FILESYSTEM=1 \ -s FORCE_FILESYSTEM=1 \
-s EXPORTED_FUNCTIONS='["_malloc","_free","_lcinterp_run_program","_lcinterp_run_program_with_ini","_lcinterp_run_file","_lcinterp_run_file_with_ini","_lcinterp_run_fiveaxis_remap_file","_lcinterp_restore_parameters","_lcinterp_save_parameters","_lcinterp_load_tool_table","_lcinterp_load_tool_table_random","_lcinterp_save_tool_table","_lcinterp_probe_init_and_synch","_lcinterp_probe_indexer","_lcinterp_probe_named_parameters","_lcinterp_free_string"]' \ -s EXPORTED_FUNCTIONS='["_malloc","_free","_lcinterp_run_program","_lcinterp_run_program_with_ini","_lcinterp_run_file","_lcinterp_run_file_with_ini","_lcinterp_run_file_with_ini_continue_on_error","_lcinterp_run_fiveaxis_remap_file","_lcinterp_run_remap_file","_lcinterp_run_remap_file_continue_on_error","_lcinterp_run_remap_io_mdi_sequence","_lcinterp_restore_parameters","_lcinterp_save_parameters","_lcinterp_load_tool_table","_lcinterp_load_tool_table_random","_lcinterp_save_tool_table","_lcinterp_probe_init_and_synch","_lcinterp_probe_indexer","_lcinterp_probe_named_parameters","_lcinterp_free_string"]' \
-s EXPORTED_RUNTIME_METHODS='["FS","UTF8ToString","stringToUTF8","lengthBytesUTF8"]' -s EXPORTED_RUNTIME_METHODS='["FS","UTF8ToString","stringToUTF8","lengthBytesUTF8"]'

View File

@@ -17,7 +17,13 @@ copy_file() {
fi fi
mkdir -p "$(dirname "$dst")" mkdir -p "$(dirname "$dst")"
cp "$src" "$dst" if [[ -f "$dst" ]] && cmp -s "$src" "$dst"; then
touch -r "$src" "$dst"
echo "unchanged $src_rel"
return
fi
cp -p "$src" "$dst"
echo "copied $src_rel" echo "copied $src_rel"
} }

View File

@@ -150,6 +150,210 @@ src/emc/rs274ngc/interp_remap.cc
src/emc/rs274ngc/interp_convert.cc src/emc/rs274ngc/interp_convert.cc
src/emc/rs274ngc/interp_cycles.cc src/emc/rs274ngc/interp_cycles.cc
src/emc/rs274ngc/interp_g7x.cc src/emc/rs274ngc/interp_g7x.cc
tests/remap/duplicate-o-word/README
tests/remap/duplicate-o-word/expected
tests/remap/duplicate-o-word/rm207.ngc
tests/remap/duplicate-o-word/rm208.ngc
tests/remap/duplicate-o-word/test.ini
tests/remap/duplicate-o-word/test.ngc
tests/remap/duplicate-o-word/test.sh
tests/remap/fail/args.0/README
tests/remap/fail/args.0/expected
tests/remap/fail/args.0/rm400.ngc
tests/remap/fail/args.0/test.ini
tests/remap/fail/args.0/test.ngc
tests/remap/fail/args.0/test.sh
tests/remap/fail/args.1/README
tests/remap/fail/args.1/expected
tests/remap/fail/args.1/rm400.ngc
tests/remap/fail/args.1/test.ini
tests/remap/fail/args.1/test.ngc
tests/remap/fail/args.1/test.sh
tests/remap/fail/args.2/README
tests/remap/fail/args.2/expected
tests/remap/fail/args.2/rm400.ngc
tests/remap/fail/args.2/test.ini
tests/remap/fail/args.2/test.ngc
tests/remap/fail/args.2/test.sh
tests/remap/fail/body-ngc/README
tests/remap/fail/body-ngc/expected
tests/remap/fail/body-ngc/rm400.ngc
tests/remap/fail/body-ngc/test.ini
tests/remap/fail/body-ngc/test.ngc
tests/remap/fail/body-ngc/test.sh
tests/remap/m30-interaction/README
tests/remap/m30-interaction/expected
tests/remap/m30-interaction/rm400.ngc
tests/remap/m30-interaction/test.ini
tests/remap/m30-interaction/test.ngc
tests/remap/m30-interaction/test.sh
tests/remap/nested-remaps-oword/README
tests/remap/nested-remaps-oword/expected
tests/remap/nested-remaps-oword/rm400.ngc
tests/remap/nested-remaps-oword/rm401.ngc
tests/remap/nested-remaps-oword/rm402.ngc
tests/remap/nested-remaps-oword/rm403.ngc
tests/remap/nested-remaps-oword/test.ini
tests/remap/nested-remaps-oword/test.ngc
tests/remap/nested-remaps-oword/test.sh
tests/remap/nested-remaps-oword/testsub.ngc
tests/remap/posargs.0/README
tests/remap/posargs.0/expected
tests/remap/posargs.0/rg881.ngc
tests/remap/posargs.0/test.ini
tests/remap/posargs.0/test.ngc
tests/remap/posargs.0/test.sh
tests/remap/sequencing/README
tests/remap/sequencing/expected
tests/remap/sequencing/permute.py
tests/remap/sequencing/rg881.ngc
tests/remap/sequencing/rm405.ngc
tests/remap/sequencing/rm406.ngc
tests/remap/sequencing/rm407.ngc
tests/remap/sequencing/rm408.ngc
tests/remap/sequencing/rm409.ngc
tests/remap/sequencing/rm410.ngc
tests/remap/sequencing/test.ini
tests/remap/sequencing/test.ngc
tests/remap/sequencing/test.sh
tests/remap/remap-io/README
tests/remap/remap-io/expected
tests/remap/remap-io/io_input_m66.ngc
tests/remap/remap-io/io_output_m62.ngc
tests/remap/remap-io/io_output_m63.ngc
tests/remap/remap-io/io_output_m64.ngc
tests/remap/remap-io/io_output_m65.ngc
tests/remap/remap-io/io_output_m67.ngc
tests/remap/remap-io/io_output_m68.ngc
tests/remap/remap-io/test-ngc.ini
tests/remap/remap-io/test.sh
tests/interp/m98m99/01-basics/expected
tests/interp/m98m99/01-basics/test.ngc
tests/interp/m98m99/01-basics/test.sh
tests/interp/m98m99/02-variables/expected
tests/interp/m98m99/02-variables/test.ngc
tests/interp/m98m99/02-variables/test.sh
tests/interp/m98m99/07-nested-subs/expected
tests/interp/m98m99/07-nested-subs/test.ngc
tests/interp/m98m99/07-nested-subs/test.sh
tests/interp/m98m99/08-sub-follows-main/expected
tests/interp/m98m99/08-sub-follows-main/test.ngc
tests/interp/m98m99/08-sub-follows-main/test.sh
tests/interp/m98m99/10-M98-P001/expected
tests/interp/m98m99/10-M98-P001/test.ngc
tests/interp/m98m99/10-M98-P001/test.sh
tests/interp/m98m99/13-named-program/expected
tests/interp/m98m99/13-named-program/test-named.ngc
tests/interp/m98m99/13-named-program/test-numbered.ngc
tests/interp/m98m99/13-named-program/test.sh
tests/interp/m98m99/14-o-expression-call/expected
tests/interp/m98m99/14-o-expression-call/test.ngc
tests/interp/m98m99/14-o-expression-call/test.sh
tests/interp/do-while-break/README
tests/interp/do-while-break/bug.ngc
tests/interp/do-while-break/expected
tests/interp/do-while-break/test.ngc
tests/interp/do-while-break/test.sh
tests/interp/oword-bug315/README
tests/interp/oword-bug315/expected
tests/interp/oword-bug315/test.ngc
tests/interp/oword-bug315/test.sh
tests/interp/oword-bug315-p2/README
tests/interp/oword-bug315-p2/expected
tests/interp/oword-bug315-p2/test.ngc
tests/interp/oword-bug315-p2/test.sh
tests/interp/exists/README
tests/interp/exists/expected
tests/interp/exists/test.ngc
tests/interp/exists/test.sh
tests/interp/return-value/expected
tests/interp/return-value/test.ngc
tests/interp/return-value/test.sh
tests/interp/subs-follow-main/expected
tests/interp/subs-follow-main/test.ngc
tests/interp/subs-follow-main/test.sh
tests/interp/fractional-linenumbers/README
tests/interp/fractional-linenumbers/expected
tests/interp/fractional-linenumbers/test.ngc
tests/interp/fractional-linenumbers/test.sh
tests/interp/namedparam-bug424/README
tests/interp/namedparam-bug424/expected
tests/interp/namedparam-bug424/test.ngc
tests/interp/namedparam-bug424/test.sh
tests/interp/g6164/expected
tests/interp/g6164/test.ngc
tests/interp/g6164/test.sh
tests/interp/rotation/abs-pts/expected
tests/interp/rotation/abs-pts/test.ngc
tests/interp/rotation/abs-pts/test.sh
tests/interp/rotation/abs-pts/test.tbl
tests/interp/rotation/g28/expected
tests/interp/rotation/g28/g28.ngc
tests/interp/rotation/g28/test.sh
tests/interp/rotation/g53/expected
tests/interp/rotation/g53/g53.ngc
tests/interp/rotation/g53/test.sh
tests/interp/iniparam/README
tests/interp/iniparam/expected
tests/interp/iniparam/test.ini
tests/interp/iniparam/test.ngc
tests/interp/iniparam/test.sh
tests/interp/iniparam-failassign/README
tests/interp/iniparam-failassign/expected
tests/interp/iniparam-failassign/test.ini
tests/interp/iniparam-failassign/test.ngc
tests/interp/iniparam-failassign/test.sh
tests/interp/m19/README
tests/interp/m19/expected
tests/interp/m19/test.ini
tests/interp/m19/test.ngc
tests/interp/m19/test.sh
tests/interp/magic_comments/param_format_printing/expected
tests/interp/magic_comments/param_format_printing/test.ngc
tests/interp/magic_comments/param_format_printing/test.sh
tests/interp/sub-call-from-sub/expected
tests/interp/sub-call-from-sub/subs/caller.ngc
tests/interp/sub-call-from-sub/subs/helper.ngc
tests/interp/sub-call-from-sub/test.ini
tests/interp/sub-call-from-sub/test.ngc
tests/interp/sub-call-from-sub/test.sh
tests/interp/sequence-number/README
tests/interp/sequence-number/expected
tests/interp/sequence-number/rm400.ngc
tests/interp/sequence-number/test.ini
tests/interp/sequence-number/test.ngc
tests/interp/sequence-number/test.sh
tests/interp/nested-sub-error/expected
tests/interp/nested-sub-error/subs/nested.ngc
tests/interp/nested-sub-error/test.ini
tests/interp/nested-sub-error/test.ngc
tests/interp/nested-sub-error/test.sh
tests/interp/nested-sub-in-file-error/expected
tests/interp/nested-sub-in-file-error/subs/sequential.ngc
tests/interp/nested-sub-in-file-error/test.ini
tests/interp/nested-sub-in-file-error/test.ngc
tests/interp/nested-sub-in-file-error/test.sh
tests/interp/oword-unwind/README
tests/interp/oword-unwind/expected
tests/interp/oword-unwind/fail.ngc
tests/interp/oword-unwind/test.ini
tests/interp/oword-unwind/test.ngc
tests/interp/oword-unwind/test.sh
tests/interp/abort-hot-comment/README
tests/interp/abort-hot-comment/expected
tests/interp/abort-hot-comment/test.ini
tests/interp/abort-hot-comment/test.ngc
tests/interp/abort-hot-comment/test.sh
configs/sim/axis/sim.tbl
configs/sim/axis/external_offsets/M111
configs/sim/axis/external_offsets/dyn_demo.ngc
configs/sim/axis/external_offsets/dynamic_offsets.ini
configs/sim/axis/external_offsets/eoffset.tbl
configs/sim/axis/foam/axis_foam.ini
configs/sim/axis/foam/foam.ngc
configs/sim/axis/geometry/M110
configs/sim/axis/geometry/xyzc.ini
configs/sim/axis/geometry/xyzc.ngc
configs/sim/axis/vismach/5axis/bridgemill/5axis.ini configs/sim/axis/vismach/5axis/bridgemill/5axis.ini
configs/sim/axis/vismach/5axis/bridgemill/5axis.tbl configs/sim/axis/vismach/5axis/bridgemill/5axis.tbl
configs/sim/axis/vismach/5axis/bridgemill/5axis.xml configs/sim/axis/vismach/5axis/bridgemill/5axis.xml
@@ -199,3 +403,7 @@ configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini
configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.tbl configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.tbl
configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.txt configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.txt
configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.xml configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.xml
nc_files/arcspiral.ngc
nc_files/factorial.ngc
nc_files/hole-circle.ngc
nc_files/m6demo.ngc

View File

@@ -7,6 +7,9 @@ GCODE_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/gcode"
CANON_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/canon" CANON_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/canon"
GCODE_ERROR_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/gcode_errors" GCODE_ERROR_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/gcode_errors"
CANON_ERROR_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/canon_errors" CANON_ERROR_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/canon_errors"
BUILD_DIR="$ROOT_DIR/build/native"
CACHE_KEY_FILE="$BUILD_DIR/native-linuxcnc-fixture-baseline.input.sha256"
CACHE_OK_FILE="$BUILD_DIR/native-linuxcnc-fixture-baseline.ok"
FIXTURES=( FIXTURES=(
minimal_linear minimal_linear
@@ -56,6 +59,35 @@ if [[ ! -x "$UPSTREAM_RS274" ]]; then
exit 1 exit 1
fi fi
mkdir -p "$BUILD_DIR"
hash_tree() {
find "$@" -type f -print0 | sort -z | xargs -0 -r sha256sum
}
fixture_baseline_fingerprint() {
{
sha256sum \
"$ROOT_DIR/tools/verify_native_linuxcnc_fixture_baseline.sh" \
"$UPSTREAM_RS274"
"$UPSTREAM_RS274" --version 2>/dev/null || true
hash_tree \
"$GCODE_FIXTURE_DIR" \
"$CANON_FIXTURE_DIR" \
"$GCODE_ERROR_FIXTURE_DIR" \
"$CANON_ERROR_FIXTURE_DIR" \
"$ROOT_DIR/tests/fixtures/ini"
} | sha256sum | awk '{ print $1 }'
}
FIXTURE_BASELINE_FINGERPRINT="$(fixture_baseline_fingerprint)"
if [[ -f "$CACHE_KEY_FILE" && -f "$CACHE_OK_FILE" ]] &&
[[ "$(tr -d '[:space:]' < "$CACHE_KEY_FILE")" == "$FIXTURE_BASELINE_FINGERPRINT" ]] &&
[[ "$(tr -d '[:space:]' < "$CACHE_OK_FILE")" == "$FIXTURE_BASELINE_FINGERPRINT" ]]; then
echo "native LinuxCNC fixture baseline up to date"
exit 0
fi
TMP_DIR="$(mktemp -d)" TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT trap 'rm -rf "$TMP_DIR"' EXIT
TOOL_TABLE_BASELINE="$TMP_DIR/tool_table_setup.tbl" TOOL_TABLE_BASELINE="$TMP_DIR/tool_table_setup.tbl"
@@ -967,4 +999,6 @@ for name in "${ERROR_FIXTURES[@]}"; do
done < "$expected_file" done < "$expected_file"
done done
printf '%s\n' "$FIXTURE_BASELINE_FINGERPRINT" > "$CACHE_KEY_FILE"
printf '%s\n' "$FIXTURE_BASELINE_FINGERPRINT" > "$CACHE_OK_FILE"
echo "native LinuxCNC fixture baseline validation complete" echo "native LinuxCNC fixture baseline validation complete"

View File

@@ -5,6 +5,9 @@ ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
UPSTREAM_DIR="$ROOT_DIR/../linuxcnc" UPSTREAM_DIR="$ROOT_DIR/../linuxcnc"
VENDOR_DIR="$ROOT_DIR/vendor/linuxcnc" VENDOR_DIR="$ROOT_DIR/vendor/linuxcnc"
MANIFEST_FILE="$ROOT_DIR/tools/source-manifest.txt" MANIFEST_FILE="$ROOT_DIR/tools/source-manifest.txt"
BUILD_DIR="$ROOT_DIR/build/native"
CACHE_KEY_FILE="$BUILD_DIR/vendor-sync.input.sha256"
CACHE_OK_FILE="$BUILD_DIR/vendor-sync.ok"
if [[ ! -d "$UPSTREAM_DIR" ]]; then if [[ ! -d "$UPSTREAM_DIR" ]]; then
echo "missing upstream directory: $UPSTREAM_DIR" >&2 echo "missing upstream directory: $UPSTREAM_DIR" >&2
@@ -21,6 +24,36 @@ if [[ ! -f "$MANIFEST_FILE" ]]; then
exit 1 exit 1
fi fi
mkdir -p "$BUILD_DIR"
vendor_sync_fingerprint() {
{
git -C "$UPSTREAM_DIR" rev-parse HEAD
sha256sum \
"$ROOT_DIR/tools/verify_vendor_sync.sh" \
"$MANIFEST_FILE"
while IFS= read -r src_rel; do
[[ -z "$src_rel" ]] && continue
[[ "${src_rel:0:1}" == "#" ]] && continue
printf '%s\0' "$UPSTREAM_DIR/$src_rel"
done < "$MANIFEST_FILE" | xargs -0 -r sha256sum 2>/dev/null || true
while IFS= read -r src_rel; do
[[ -z "$src_rel" ]] && continue
[[ "${src_rel:0:1}" == "#" ]] && continue
printf '%s\0' "$VENDOR_DIR/$src_rel"
done < "$MANIFEST_FILE" | xargs -0 -r sha256sum 2>/dev/null || true
find "$VENDOR_DIR" -type f -printf 'vendor-list:%P\n' | sort
} | sha256sum | awk '{ print $1 }'
}
VENDOR_SYNC_FINGERPRINT="$(vendor_sync_fingerprint)"
if [[ -f "$CACHE_KEY_FILE" && -f "$CACHE_OK_FILE" ]] &&
[[ "$(tr -d '[:space:]' < "$CACHE_KEY_FILE")" == "$VENDOR_SYNC_FINGERPRINT" ]] &&
[[ "$(tr -d '[:space:]' < "$CACHE_OK_FILE")" == "$VENDOR_SYNC_FINGERPRINT" ]]; then
echo "vendor sync up to date"
exit 0
fi
TMP_DIR="$(mktemp -d)" TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT trap 'rm -rf "$TMP_DIR"' EXIT
@@ -82,4 +115,6 @@ while IFS= read -r src_rel; do
fi fi
done < "$MANIFEST_SORTED" done < "$MANIFEST_SORTED"
printf '%s\n' "$VENDOR_SYNC_FINGERPRINT" > "$CACHE_KEY_FILE"
printf '%s\n' "$VENDOR_SYNC_FINGERPRINT" > "$CACHE_OK_FILE"
echo "vendor sync validation complete" echo "vendor sync validation complete"

View File

@@ -0,0 +1,5 @@
#!/bin/bash
halcmd setp axisui.notifications-clear-info 1
sleep .1
halcmd setp axisui.notifications-clear-info 0
exit 0

View File

@@ -0,0 +1,33 @@
#<xmax> = 9
#<ymax> = 4
#<zmax> = 3
#<rct> = 100
#<feed> = 100
#<x1> = #<xmax>
#<y1> = 0
#<z1> = [#<zmax> * 0.5]
#<x2> = #<xmax>
#<y2> = #<ymax>
#<z2> = 0
#<x3> = 0
#<y3> = #<ymax>
#<z3> = #<zmax>
m111 ;clear notifications
g61
g0 x0 y0 z0
o<rpt> repeat [#<rct>]
f #<feed>
g0 x 0 y 0 z 0
g1 x #<x1> y #<y1> z #<z1>
g1 x #<x2> y #<y2> z #<z2>
g1 x #<x3> y #<y3> z #<z3>
g1 x 0 y 0 z 0
o<rpt> endrepeat
m111 ;clear notifications
m2

View File

@@ -0,0 +1,83 @@
[APPLICATIONS]
# delay because some items are done in postgui
DELAY = 5
APP = halscope -i dynamic_offsets.halscope
[HAL]
HALUI = halui
HALFILE = LIB:basic_sim.tcl -no_sim_spindle
POSTGUI_HALFILE = dynamic_offsets_panel.hal
[EMC]
MACHINE = Dynamic External Offsets
VERSION = 1.1
[DISPLAY]
PYVCP = dynamic_offsets_panel.xml
DISPLAY = axis
POSITION_OFFSET = RELATIVE
POSITION_FEEDBACK = ACTUAL
MAX_LINEAR_VELOCITY = 2
OPEN_FILE = ./dyn_demo.ngc
[TASK]
TASK = milltask
CYCLE_TIME = 0.001
[RS274NGC]
USER_M_PATH = .
PARAMETER_FILE = sim.var
[EMCMOT]
EMCMOT = motmod
SERVO_PERIOD = 1000000
[TRAJ]
COORDINATES = XYZ
LINEAR_UNITS = inch
ANGULAR_UNITS = degree
[KINS]
JOINTS = 3
KINEMATICS = trivkins coordinates=XYZ
[AXIS_X]
MAX_VELOCITY = 2
MAX_ACCELERATION = 20
MIN_LIMIT = -1
MAX_LIMIT = 10
[AXIS_Y]
MAX_VELOCITY = 2
MAX_ACCELERATION = 20
MIN_LIMIT = -1
MAX_LIMIT = 10
[AXIS_Z]
# Note:give half to external_offsets:
OFFSET_AV_RATIO = 0.5
# Note: modified in pyvcp panel:
MAX_VELOCITY = 2
MAX_ACCELERATION = 50
MIN_LIMIT = -5
MAX_LIMIT = 5
[JOINT_0]
TYPE = LINEAR
HOME_SEQUENCE = 0
MAX_VELOCITY = 2
MAX_ACCELERATION = 20
[JOINT_1]
TYPE = LINEAR
HOME_SEQUENCE = 0
MAX_VELOCITY = 2
MAX_ACCELERATION = 20
[JOINT_2]
TYPE = LINEAR
HOME_SEQUENCE = 0
MAX_VELOCITY = 2
MAX_ACCELERATION = 50

View File

@@ -0,0 +1 @@
T1 P1 D0.125 Z0.5

View File

@@ -0,0 +1,126 @@
[EMC]
VERSION = 1.1
MACHINE = LinuxCNC-HAL-SIM-AXIS
DEBUG = 0
[DISPLAY]
DISPLAY = axis
CYCLE_TIME = 0.100
HELP_FILE = doc/help.txt
POSITION_OFFSET = RELATIVE
POSITION_FEEDBACK = ACTUAL
MAX_FEED_OVERRIDE = 1.2
MAX_SPINDLE_OVERRIDE = 1.0
PROGRAM_PREFIX = ../../nc_files/
OPEN_FILE = ./foam.ngc
INTRO_GRAPHIC = linuxcnc.gif
INTRO_TIME = 5
#EDITOR = geany
INCREMENTS = 1 mm, .01 in, .1mm, 1 mil, .1 mil, 1/8000 in
GEOMETRY = XY;UV
FOAM = 1
[FILTER]
PROGRAM_EXTENSION = .png,.gif,.jpg Grayscale Depth Image
PROGRAM_EXTENSION = .py Python Script
png = image-to-gcode
gif = image-to-gcode
jpg = image-to-gcode
py = python3
[RS274NGC]
PARAMETER_FILE = foam.var
[EMCMOT]
EMCMOT = motmod
COMM_TIMEOUT = 1.0
SERVO_PERIOD = 1000000
[TASK]
TASK = milltask
CYCLE_TIME = 0.001
[HAL]
HALUI = halui
HALFILE = LIB:basic_sim.tcl
[TRAJ]
NO_FORCE_HOMING = 1
COORDINATES = X Y U V
LINEAR_UNITS = inch
ANGULAR_UNITS = degree
DEFAULT_LINEAR_VELOCITY = 1.0
DEFAULT_ANGULAR_VELOCITY = 45.0
MAX_LINEAR_VELOCITY = 1.2
MAX_ANGULAR_VELOCITY = 90.0
[EMCIO]
TOOL_TABLE = sim.tbl
TOOL_CHANGE_POSITION = 0 0 2
[KINS]
KINEMATICS = trivkins coordinates=xyuv
JOINTS = 4
[AXIS_X]
MIN_LIMIT = -10.0
MAX_LIMIT = 10.0
MAX_VELOCITY = 1.2
MAX_ACCELERATION = 20.0
[JOINT_0]
TYPE = LINEAR
HOME = 0.000
MAX_VELOCITY = 1.2
MAX_ACCELERATION = 20.0
MIN_LIMIT = -10.0
MAX_LIMIT = 10.0
HOME_SEARCH_VEL = 0.0
HOME_SEQUENCE = 0
[AXIS_Y]
MIN_LIMIT = -10.0
MAX_LIMIT = 10.0
MAX_VELOCITY = 1.2
MAX_ACCELERATION = 20.0
[JOINT_1]
TYPE = LINEAR
HOME = 0.000
MAX_VELOCITY = 1.2
MAX_ACCELERATION = 20.0
MIN_LIMIT = -10.0
MAX_LIMIT = 10.0
HOME_SEARCH_VEL = 0.0
HOME_SEQUENCE = 0
[AXIS_U]
MIN_LIMIT = -10.0
MAX_LIMIT = 10.0
MAX_VELOCITY = 1.2
MAX_ACCELERATION = 20.0
[JOINT_2]
TYPE = LINEAR
HOME = 0.0
MAX_VELOCITY = 1.2
MAX_ACCELERATION = 20.0
MIN_LIMIT = -10.0
MAX_LIMIT = 10.0
HOME_SEARCH_VEL = 0.0
HOME_SEQUENCE = 0
[AXIS_V]
MIN_LIMIT = -10.0
MAX_LIMIT = 10.0
MAX_VELOCITY = 1.2
MAX_ACCELERATION = 20.0
[JOINT_3]
TYPE = ANGULAR
HOME = 0.0
MAX_VELOCITY = 90.0
MAX_ACCELERATION = 1200.0
HOME_SEARCH_VEL = 0.0
HOME_SEQUENCE = 0

View File

@@ -0,0 +1,24 @@
(test this with axis_9axis sim machine )
G17 G21 G90 G61 G54
(AXIS,XY_Z_POS,5)
(AXIS,UV_Z_POS,30)
(AXIS,GRID,5)
G0 x0 y0 u0 v0
(square uv 45 deg turned offset 10mm to zero )
G0 x5 y5
G0 x10 y10 u22.5 v22.5
G1 x10 y35 u10 v35 F200
G1 x10 y60 u22.5 v47.5
G1 x35 y60 u35 v60
G2 r25 x60 y35 u60 v35
G1 x60 y10 u47.5 v22.5
G1 x35 y10 u35 v10
G1 X10 Y10 u22.5 v22.5
G1 X5 Y5 U5 V5
G0 x0 u0
G0 x0 y0 u0 v0
M30
%

View File

@@ -0,0 +1,7 @@
#!/bin/sh
halcmd setp axisui.notifications-clear-info 1
sleep .200
halcmd setp axisui.notifications-clear-info 0
exit 0

View File

@@ -0,0 +1,72 @@
# minimal sim config to demonstrate '!' in [DISPLAY]GEOMETRY
# showing rotations respecting g5x,g92 offsets
[EMC]
VERSION = 1.1
MACHINE = xyzc (offset rotations)
[DISPLAY]
OPEN_FILE = ./xyzc.ngc
GEOMETRY = !CXYZ
DISPLAY = axis
[TASK]
TASK = milltask
CYCLE_TIME = 0.001
[RS274NGC]
PARAMETER_FILE = xyzc.var
USER_M_PATH = .
[EMCMOT]
EMCMOT = motmod
SERVO_PERIOD = 1000000
[HAL]
HALFILE = LIB:basic_sim.tcl
[KINS]
JOINTS = 4
KINEMATICS = trivkins coordinates=XYZC kinstype=both
[TRAJ]
COORDINATES = X Y Z C
LINEAR_UNITS = inch
ANGULAR_UNITS = degree
MAX_LINEAR_VELOCITY = 10
MAX_LINEAR_ACCELERATION = 10000
MAX_ANGULAR_VELOCITY = 3600
MAX_ANGULAR_ACCELERATION = 36000
[JOINT_0]
TYPE = LINEAR
HOME_SEQUENCE = 0
[JOINT_1]
TYPE = LINEAR
HOME_SEQUENCE = 0
[JOINT_2]
TYPE = LINEAR
HOME_SEQUENCE = 0
[JOINT_3]
TYPE = ANGULAR
HOME_SEQUENCE = 0
MAX_VELOCITY=3600
MAX_ACCELERATION=36000
[AXIS_X]
MAX_VELOCITY=10
MAX_ACCELERATION=1000
[AXIS_Y]
MAX_VELOCITY=10
MAX_ACCELERATION=1000
[AXIS_Z]
MAX_VELOCITY=10
MAX_ACCELERATION=1000
[AXIS_C]
MAX_VELOCITY=3600
MAX_ACCELERATION=36000

View File

@@ -0,0 +1,66 @@
o110 sub
f100
g61
g0 x1.0 y0.0
g1 x1.5 y0.0
g1 x1.5 y0.5
g1 x1.0 y0.5
g1 x1.0 y0.0
o110 endsub
o100 sub
g0 c90
o110 call
g0 c180
o110 call
g0 c270
o110 call
g0x0y0z0c0
o100 endsub
;#########################################################
m110 ; clear messages
g0x0y0z0c0
o110 call
o200 if [#5210 ne 0]
(debug, ! Prog loaded with g92 offsets)
(debug, RELOAD program required)
g92.1
(debug, bye)
m2
o200 endif
o210 if [[#5221 ne 0] or [#5222 ne 0] or [#5223 ne 0] or [#5223 ne 0] or [#5225 ne 0] or [#5226 ne 0] or [#5227 ne 0] or [#5228 ne 0] or [#5229 ne 0] ]
(debug, ! Prog loaded with g5x offsets)
(debug, RELOAD program required)
g92.1
g10l2p0x0y0z0c0
(debug, bye)
m2
o210 endif
g0x0y0z0c0
g10l2p0x0y0z0c0
(debug, First: run with NO offsets, s to continue)
m0
o100 call
o300 if [#<_task> eq 1]
g10l2p0x1
(debug, Next: run with g5x xoffset=#5221, s to continue)
m0
o100 call
g10l2p0x0y0z0c0
g92x2
(debug, Next: run with g92 xoffset=#5211, s to continue)
m0
o100 call
o300 endif
g10l2p0x0y0z0c0
g92.1
g0x0y0z0c0
(debug,fini)
m2

View File

@@ -0,0 +1,5 @@
T1 P1 Z0.511 D0.125 ;1/8 end mill
T2 P2 Z0.1 D0.0625 ;1/16 end mill
T3 P3 Z1.273 D0.201 ;#7 tap drill
T99999 P99999 Z0.1 ;big tool number
T4 P4 Z0 D.005 ;Added 20131029

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
; recursive function example
; factorial
o<factorial> sub
o<factorialif> if [[#1] EQ 0]
o<factorial> return [1]
o<factorialif> else
o<factorial> call [[#1] - 1]
o<factorial> return [#<_value> * #1]
o<factorialif> endif
o<factorial> endsub
m2

View File

@@ -0,0 +1,12 @@
; drill a bolt hole circle for 6 bolts at diameter 3 inches
G20 G17 G90
; position over first hole, establishing our radius of 1.5
; an equivalent command would be G0 @1.5 ^0 Z0
G0 X1.5 Y0 Z0
; drill six holes, incrementing 60 degrees each time
G91 G81 R.1 Z-.5 ^60 L6 F10
M2

View File

@@ -0,0 +1,88 @@
; This demonstrates doing an M6 remapped to a named oword sub
;
; to activate, incantate as follows in the INI file:
;
; [RS274NGC]
; # remap M6 to a named oword subroutine.
; M6_COMMAND=o<m6demo>call
;
; parameter #1: the current tool-in-spindle
; parameter #2: the tool number requested in the last T (prepare) command
; parameter #3: pocket of new tool
;
;
;
O<m6demo> sub
;
(DEBUG, executing M6 O-word sub, tool-in-spindle=#1 prepared=#2 pocket=#3)
;
M66 P2 L0
;(debug, digital-input-02=#5399)
#<tool_change_with_spindle_on> = 0
#<tool_change_quill_up> = 0
#<tool_change_at_g30> = 0
; number of seconds to wait for 'tool-changed' equivalent
#<timeout> = 9999
;
O<m6demo_spindle_off> if [#<tool_change_with_spindle_on> EQ 0]
M5
O<m6demo_spindle_off> endif
O<m6demo_quill_up> if [#<tool_change_quill_up> NE 0]
G0 G53 Z0
O<m6demo_quill_up> endif
O<m6demo_tc_at_g30> if [#<tool_change_at_g30> NE 0]
G30
O<m6demo_tc_at_g30> endif
; set analog output pin #2 to signal the pocket number
; iocontrol.tool-number becomes motion.analog-out-02
M68 E2 Q[#2]
;(DEBUG, set current tool number on motion.analog-out-02: #2)
;
; assert the equivalent of the iocontrol.tool-change pin
; which is now motion.digital-out-01
M64 P1
;(DEBUG, motion.digital-out-01 set high, waiting for motion.digital-in-01)
; wait for the equivalent of the iocontrol.tool-changed pin to go high
; we use motion.digital-in-01
;
M66 P1 L3 Q#<timeout>
;
; if we waited too long, fail change and abort.
;
O<m6demo_timeout> if [#5399 EQ -1]
(DEBUG, timeout waiting for digital-in-01 to become true - failing change )
O<m6demo> return [-1]
O<m6demo_timeout> endif
; retract iocontrol.tool-change equivalent
;(DEBUG, deasserting motion.digital-out-01)
M65 P1
;
; test fail-change line from gladevcp
M66 P2 L0
; (debug, digital-input-02=#5399)
O<m6demo_change_fail> if [#5399 EQ 1]
(DEBUG, returning -1 to fail change)
O<m6demo> return [-1]
O<m6demo_change_fail> else
(debug, change ok, returning +1 to commit change)
; a positive return value commits the tool change
O<m6demo> return [1]
O<m6demo_change_fail> endif
;
; return a positive value commit the change.
; a negative return value will fail the change and
; abort the interpreter with a message like
; "M6 failed (<return value>)"
;
O<m6demo> endsub
m2

View File

@@ -0,0 +1,8 @@
exercise the (ABORT,<message>) hot comment:
numbered, named and INI parameter substitution in message
case preservation
unwind from oword flow

View File

@@ -0,0 +1,10 @@
executing
1 N..... USE_LENGTH_UNITS(CANON_UNITS_MM)
2 N..... SET_G5X_OFFSET(1, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
3 N..... SET_G92_OFFSET(0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
4 N..... SET_XY_ROTATION(0.0000)
5 N..... SET_FEED_REFERENCE(CANON_XYZ)
6 N..... ON_RESET()
MixedCase param42=20.000000 named=4711.000000 INI=3.140000
(abort, MixedCase param42=#42 named=#<named> INI=#<_ini[a]value>)
7 N..... ON_RESET()

View File

@@ -0,0 +1,8 @@
[RS274NGC]
# enable #<_ini[section]name> expansion
INI_VARS = 1
# test ini variable expansion in (ABORT, text) comment
[A]
VALUE= 3.14

View File

@@ -0,0 +1,10 @@
#42 = 0
#<named> = 4711
o100 while [#42 lt 100]
o200 if [#42 eq 20]
(abort, MixedCase param42=#42 named=#<named> INI=#<_ini[a]value>)
o200 endif
#42 = [#42 + 1]
o100 endwhile
M2

View File

@@ -0,0 +1,5 @@
#!/bin/bash
export PYTHONUNBUFFERED=1
rs274 -n 2 -i test.ini -g test.ngc 2>&1
exit 0

View File

@@ -0,0 +1,3 @@
Tests for bug 3420655 (http://sourceforge.net/tracker/?func=detail&atid=106744&aid=3420655&group_id=6744)

View File

@@ -0,0 +1,7 @@
o102 do
(print,@start of do-while-loop)
o102 break
(print,@end of do-while-loop)
o102 while [1]
(print,@endsub)
M2

View File

@@ -0,0 +1,21 @@
N..... USE_LENGTH_UNITS(CANON_UNITS_MM)
N..... SET_G5X_OFFSET(1, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
N..... SET_G92_OFFSET(0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
N..... SET_XY_ROTATION(0.0000)
N..... SET_FEED_REFERENCE(CANON_XYZ)
N..... ON_RESET()
N..... MESSAGE("must-execute-outer")
N..... MESSAGE("must-execute-nested")
N..... MESSAGE("must-execute-inner")
N..... MESSAGE("post-inner-while")
N..... MESSAGE("post-nested-while")
N..... MESSAGE("post-outer-while")
N..... SET_G5X_OFFSET(1, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
N..... SET_XY_ROTATION(0.0000)
N..... SET_FEED_MODE(0, 0)
N..... SET_FEED_RATE(0.0000)
N..... STOP_SPINDLE_TURNING(0)
N..... SET_SPINDLE_MODE(0 0.0000)
N..... PROGRAM_END()
N..... ON_RESET()
N..... ON_RESET()

View File

@@ -0,0 +1,30 @@
#<one_pass> = 0
o100 do
(debug,must-execute-outer)
#<one_pass> = [#<one_pass> + 1]
#<two_pass> = 0
o200 do
(debug,must-execute-nested)
#<two_pass> = [#<two_pass> + 1]
#<three_pass> = 0
o300 do
(debug,must-execute-inner)
#<three_pass> = [#<three_pass> + 1]
o300 break
(debug,do-not-execute-inner)
o300 while [#<three_pass> LT 3]
(debug,post-inner-while)
o200 break
(debug,do-not-execute-nested)
o200 while [#<two_pass> LT 3]
(debug,post-nested-while)
o100 break
(debug,do-not-execute-outer)
o100 while [#<one_pass> LT 3]
(debug,post-outer-while)
M2

View File

@@ -0,0 +1,3 @@
#!/bin/bash
rs274 -g test.ngc | awk '{$1=""; print}'
exit "${PIPESTATUS[0]}"

View File

@@ -0,0 +1 @@
Test that EXISTS[] works as desired

View File

@@ -0,0 +1,18 @@
N..... USE_LENGTH_UNITS(CANON_UNITS_MM)
N..... SET_G5X_OFFSET(1, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
N..... SET_G92_OFFSET(0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
N..... SET_XY_ROTATION(0.0000)
N..... SET_FEED_REFERENCE(CANON_XYZ)
N..... ON_RESET()
N..... STRAIGHT_TRAVERSE(1.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000)
N..... COMMENT("comment")
N..... STRAIGHT_TRAVERSE(1.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
N..... SET_G5X_OFFSET(1, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
N..... SET_XY_ROTATION(0.0000)
N..... SET_FEED_MODE(0, 0)
N..... SET_FEED_RATE(0.0000)
N..... STOP_SPINDLE_TURNING(0)
N..... SET_SPINDLE_MODE(0 0.0000)
N..... PROGRAM_END()
N..... ON_RESET()
N..... ON_RESET()

View File

@@ -0,0 +1,7 @@
#<a>=999
G0 X EXISTS[#1] Y EXISTS[#99999] Z EXISTS[#<a>] A EXISTS[#<b>] ; comment
#1=2
#2=99999
G0 X EXISTS[##1] Y EXISTS[##2] Z EXISTS[###1] (comment)
M2

View File

@@ -0,0 +1,3 @@
#!/bin/bash
rs274 -g test.ngc | awk '{$1=""; print}'
exit "${PIPESTATUS[0]}"

View File

@@ -0,0 +1 @@
Test that line numbers like Ndddd.ddd are accepted too

View File

@@ -0,0 +1,18 @@
N..... USE_LENGTH_UNITS(CANON_UNITS_MM)
N..... SET_G5X_OFFSET(1, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
N..... SET_G92_OFFSET(0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
N..... SET_XY_ROTATION(0.0000)
N..... SET_FEED_REFERENCE(CANON_XYZ)
N..... ON_RESET()
N..... SET_SPINDLE_SPEED(0, 300.0000)
N1234 SET_SPINDLE_SPEED(0, 600.0000)
N4567 SET_SPINDLE_SPEED(0, 1200.0000)
N..... SET_G5X_OFFSET(1, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
N..... SET_XY_ROTATION(0.0000)
N..... SET_FEED_MODE(0, 0)
N..... SET_FEED_RATE(0.0000)
N..... STOP_SPINDLE_TURNING(0)
N..... SET_SPINDLE_MODE(0 0.0000)
N..... PROGRAM_END()
N..... ON_RESET()
N..... ON_RESET()

View File

@@ -0,0 +1,4 @@
S300
N1234 S600
N4567.789 S1200
M2

View File

@@ -0,0 +1,3 @@
#!/bin/bash
rs274 -g test.ngc | awk '{$1=""; print}'
exit "${PIPESTATUS[0]}"

View File

@@ -0,0 +1,23 @@
N..... USE_LENGTH_UNITS(CANON_UNITS_MM)
N..... SET_G5X_OFFSET(1, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
N..... SET_G92_OFFSET(0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
N..... SET_XY_ROTATION(0.0000)
N..... SET_FEED_REFERENCE(CANON_XYZ)
N..... ON_RESET()
N..... SET_MOTION_CONTROL_MODE(CANON_CONTINUOUS, 0.000000)
N..... SET_NAIVECAM_TOLERANCE(0.0000)
N..... SET_MOTION_CONTROL_MODE(CANON_CONTINUOUS, 1.000000)
N..... SET_NAIVECAM_TOLERANCE(1.0000)
N..... SET_MOTION_CONTROL_MODE(CANON_CONTINUOUS, 1.000000)
N..... SET_NAIVECAM_TOLERANCE(2.0000)
N..... SET_MOTION_CONTROL_MODE(CANON_EXACT_PATH)
N..... SET_MOTION_CONTROL_MODE(CANON_EXACT_STOP)
N..... SET_G5X_OFFSET(1, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
N..... SET_XY_ROTATION(0.0000)
N..... SET_FEED_MODE(0, 0)
N..... SET_FEED_RATE(0.0000)
N..... STOP_SPINDLE_TURNING(0)
N..... SET_SPINDLE_MODE(0 0.0000)
N..... PROGRAM_END()
N..... ON_RESET()
N..... ON_RESET()

View File

@@ -0,0 +1,6 @@
G64
G64P1
G64P1Q2
G61
G61.1
M2

View File

@@ -0,0 +1,3 @@
#!/bin/bash
rs274 -g test.ngc | awk '{$1=""; print}'
exit "${PIPESTATUS[0]}"

View File

@@ -0,0 +1,3 @@
assigning to an inifile variable reference must fail:
#<_ini[section]name> = <value>

View File

@@ -0,0 +1,10 @@
executing
1 N..... USE_LENGTH_UNITS(CANON_UNITS_MM)
2 N..... SET_G5X_OFFSET(1, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
3 N..... SET_G92_OFFSET(0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
4 N..... SET_XY_ROTATION(0.0000)
5 N..... SET_FEED_REFERENCE(CANON_XYZ)
6 N..... ON_RESET()
Cannot assign to read-only parameter #<_ini[vars]toolchange_x>
#<_ini[vars]toolchange_x> = 3.1415
7 N..... ON_RESET()

View File

@@ -0,0 +1,9 @@
[EMC]
DEBUG=0
LOG_LEVEL=0
[RS274NGC]
INI_VARS = 1
[VARS]
TOOLCHANGE_X = 10

View File

@@ -0,0 +1,3 @@
#<_ini[vars]toolchange_x> = 3.1415
(debug, notreached: #<_ini[vars]toolchange_x>)
M2

View File

@@ -0,0 +1,8 @@
#!/bin/bash
export PYTHONUNBUFFERED=1
if ! rs274 -i test.ini -g test.ngc 2>&1; then
# expected to fail
exit 0
fi
exit 1

View File

@@ -0,0 +1,5 @@
named parameters can now reference INI variables; they are retrieved on demand and
cached as global read-only parameters.
Syntax: #<_ini[section]name>

View File

@@ -0,0 +1,13 @@
executing
1 N..... USE_LENGTH_UNITS(CANON_UNITS_MM)
2 N..... SET_G5X_OFFSET(1, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
3 N..... SET_G92_OFFSET(0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
4 N..... SET_XY_ROTATION(0.0000)
5 N..... SET_FEED_REFERENCE(CANON_XYZ)
6 N..... ON_RESET()
7 N..... STRAIGHT_TRAVERSE(10.0000, 20.0000, 30.0000, 0.0000, 0.0000, 0.0000)
8 N..... MESSAGE(" position now: 10.000000 20.000000 30.000000")
9 N..... MESSAGE(" not in INI: ######")
Named parameter #<_ini[nosuchsection]nosuchname> not defined
#4711 = #<_iNI[nosuchsection]nosuchname>
10 N..... ON_RESET()

View File

@@ -0,0 +1,12 @@
[EMC]
DEBUG=0
LOG_LEVEL=0
[RS274NGC]
INI_VARS = 1
[VARS]
TOOLCHANGE_X = 10
TOOLCHANGE_Y = 20
TOOLCHANGE_Z = 30

View File

@@ -0,0 +1,9 @@
G53 G0 x#<_ini[vars]toolchange_x> y#<_InI[vars]toolchange_y> z#<_INi[vars]toolchange_z>
(debug, position now: #<_x> #<_y> #<_z>)
(debug, not in INI: #<_iNi[nosuchsection]nosuchname>)
; now fail referencing an undefined INI variable:
#4711 = #<_iNI[nosuchsection]nosuchname>
M2

View File

@@ -0,0 +1,5 @@
#!/bin/bash
export PYTHONUNBUFFERED=1
rs274 -i test.ini -g test.ngc 2>&1
# this is expected to fail on the undefined INI var
exit 0

View File

@@ -0,0 +1,2 @@
exercise M19 and possible parameters

View File

@@ -0,0 +1,20 @@
N..... USE_LENGTH_UNITS(CANON_UNITS_MM)
N..... SET_G5X_OFFSET(1, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
N..... SET_G92_OFFSET(0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
N..... SET_XY_ROTATION(0.0000)
N..... SET_FEED_REFERENCE(CANON_XYZ)
N..... ON_RESET()
N..... ORIENT_SPINDLE(0, 87.0000, 0)
N..... SPINDLE.0.WAIT_ORIENT_COMPLETE(2.0000)
N..... ORIENT_SPINDLE(0, 42.0000, 1)
N..... ORIENT_SPINDLE(0, 132.0000, 0)
N..... ORIENT_SPINDLE(0, 132.0000, 1)
N..... SPINDLE.0.WAIT_ORIENT_COMPLETE(1.0000)
N..... SET_G5X_OFFSET(1, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
N..... SET_XY_ROTATION(0.0000)
N..... SET_FEED_MODE(0, 0)
N..... SET_FEED_RATE(0.0000)
N..... STOP_SPINDLE_TURNING(0)
N..... SET_SPINDLE_MODE(0 0.0000)
N..... PROGRAM_END()
N..... ON_RESET()

View File

@@ -0,0 +1,2 @@
[RS274NGC]
ORIENT_OFFSET = 42.0

View File

@@ -0,0 +1,8 @@
M19
M19 R45 Q2
M19 P1
M19 R90 P0
M19 R90 P1
M19 Q1
M2

View File

@@ -0,0 +1,3 @@
#!/bin/bash
rs274 -n 0 -i test.ini -g test.ngc | awk '{$1=""; print}'
exit "${PIPESTATUS[0]}"

View File

@@ -0,0 +1,49 @@
N..... USE_LENGTH_UNITS(CANON_UNITS_MM)
N..... SET_G5X_OFFSET(1, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
N..... SET_G92_OFFSET(0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
N..... SET_XY_ROTATION(0.0000)
N..... SET_FEED_REFERENCE(CANON_XYZ)
N..... ON_RESET()
N..... COMMENT("A simple O sub example ")
N..... SET_FEED_RATE(40.0000)
N..... STRAIGHT_TRAVERSE(0.0000, 0.0000, 0.2500, 0.0000, 0.0000, 0.0000)
N..... COMMENT("interpreter: distance mode changed to incremental")
N..... STRAIGHT_TRAVERSE(1.0000, 0.0000, 0.2500, 0.0000, 0.0000, 0.0000)
N..... COMMENT("interpreter: distance mode changed to absolute")
N..... COMMENT("interpreter: retract mode set to r_plane")
N..... SET_MOTION_CONTROL_MODE(CANON_EXACT_PATH)
N..... STRAIGHT_TRAVERSE(1.0000, 0.0000, 0.2500, 0.0000, 0.0000, 0.0000)
N..... STRAIGHT_FEED(1.0000, 0.0000, -1.0000, 0.0000, 0.0000, 0.0000)
N..... STRAIGHT_TRAVERSE(1.0000, 0.0000, 0.2500, 0.0000, 0.0000, 0.0000)
N..... SET_MOTION_CONTROL_MODE(CANON_CONTINUOUS, 0.000000)
N..... SET_NAIVECAM_TOLERANCE(0.0000)
N..... COMMENT("interpreter: distance mode changed to incremental")
N..... STRAIGHT_TRAVERSE(2.0000, 0.0000, 0.2500, 0.0000, 0.0000, 0.0000)
N..... COMMENT("interpreter: distance mode changed to absolute")
N..... COMMENT("interpreter: retract mode set to r_plane")
N..... SET_MOTION_CONTROL_MODE(CANON_EXACT_PATH)
N..... STRAIGHT_TRAVERSE(2.0000, 0.0000, 0.2500, 0.0000, 0.0000, 0.0000)
N..... STRAIGHT_FEED(2.0000, 0.0000, -1.0000, 0.0000, 0.0000, 0.0000)
N..... STRAIGHT_TRAVERSE(2.0000, 0.0000, 0.2500, 0.0000, 0.0000, 0.0000)
N..... SET_MOTION_CONTROL_MODE(CANON_CONTINUOUS, 0.000000)
N..... SET_NAIVECAM_TOLERANCE(0.0000)
N..... COMMENT("interpreter: distance mode changed to incremental")
N..... STRAIGHT_TRAVERSE(3.0000, 0.0000, 0.2500, 0.0000, 0.0000, 0.0000)
N..... COMMENT("interpreter: distance mode changed to absolute")
N..... COMMENT("interpreter: retract mode set to r_plane")
N..... SET_MOTION_CONTROL_MODE(CANON_EXACT_PATH)
N..... STRAIGHT_TRAVERSE(3.0000, 0.0000, 0.2500, 0.0000, 0.0000, 0.0000)
N..... STRAIGHT_FEED(3.0000, 0.0000, -1.0000, 0.0000, 0.0000, 0.0000)
N..... STRAIGHT_TRAVERSE(3.0000, 0.0000, 0.2500, 0.0000, 0.0000, 0.0000)
N..... SET_MOTION_CONTROL_MODE(CANON_CONTINUOUS, 0.000000)
N..... SET_NAIVECAM_TOLERANCE(0.0000)
N..... SET_G5X_OFFSET(1, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000)
N..... SET_XY_ROTATION(0.0000)
N..... SET_FEED_MODE(0, 0)
N..... SET_FEED_RATE(0.0000)
N..... STOP_SPINDLE_TURNING(0)
N..... SET_SPINDLE_MODE(0 0.0000)
N..... PALLET_SHUTTLE()
N..... PROGRAM_END()
N..... ON_RESET()
N..... ON_RESET()

View File

@@ -0,0 +1,13 @@
%
( A simple O sub example )
F40
G90 G0 X0 Y0 Z0.25
M98 P1 L3
M30
O1
G91 G0 X1 ; Jog 1" along X (relative)
G90 G99 G81 Z-1.0 R0.25 ; Drill to Z-1.0
M99
%

View File

@@ -0,0 +1,3 @@
#!/bin/bash
rs274 -g test.ngc | awk '{$1=""; print}'
exit "${PIPESTATUS[0]}"

Some files were not shown because too many files have changed in this diff Show More