Files
wasm-simulator/docs/linuxcnc-porting.md
2026-05-22 17:54:44 +08:00

7.4 KiB

LinuxCNC to WASM porting steps

This file defines the incremental path for replacing the temporary smoke parser with the LinuxCNC interpreter.

Step 0: Stable simulator ABI

Status: done.

The browser calls only cnc_sim_* functions from core/include/cnc_sim_api.h. This ABI remains stable while the backend changes.

Test:

g++ -std=c++17 -I core/include core/src/cnc_sim_api.cpp core/tests/cnc_sim_api_smoke.cpp -o /tmp/cnc_sim_api_smoke
/tmp/cnc_sim_api_smoke

Step 1: Temporary event parser

Status: in progress.

This parser only exists to test the ABI and UI before Emscripten and LinuxCNC are wired in. It currently handles:

  • multiple G/M words on one line
  • G0, G1, G2, G3
  • G17, G18, G19
  • G20, G21, G70, G71
  • G90, G91
  • G4 P...
  • F, S, T, M3, M4, M5, M6, M2, M30
  • IJK and R arcs
  • numbered and named parameter assignment/reference such as #1 = ..., #<name> = ..., X#1, and nested expressions like X[[#<name> + 2] * 3]
  • expression functions ABS[], SQRT[], EXP[], LN[], degree-based SIN[]/COS[]/TAN[]/ASIN[]/ACOS[], LinuxCNC-style ATAN[]/[], and FIX[]/FUP[]/ROUND[]
  • numeric and named O-word subprograms such as O100 call and O<name> call
  • canned cycles G73, G81, G82, G83, G85, G86, G89 with G80, G98, G99, L

It is not the production interpreter.

Step 2: Canon event sink

Status: started.

Create a C++ file that implements the Canon functions declared by LinuxCNC src/emc/nml_intf/canon.hh.

High-priority callbacks:

  • INIT_CANON
  • USE_LENGTH_UNITS
  • SELECT_PLANE
  • SET_FEED_RATE
  • SET_SPINDLE_SPEED
  • SELECT_TOOL
  • CHANGE_TOOL
  • STRAIGHT_TRAVERSE
  • STRAIGHT_FEED
  • ARC_FEED
  • DWELL
  • PROGRAM_END
  • FINISH

The sink will translate these callbacks to CncSimEvent.

Current bridge files:

  • core/src/canon_event_sink.h
  • core/src/canon_event_sink.cpp
  • core/src/linuxcnc_canon_bridge.h
  • core/src/linuxcnc_canon_bridge.cpp

The LinuxCNC bridge is optional and not built by default:

cmake -S core -B build/native-linuxcnc \
  -DCNC_SIM_ENABLE_LINUXCNC_BRIDGE=ON \
  -DCNC_SIM_LINUXCNC_ROOT=/path/to/linuxcnc

This bridge is intentionally thin. The production parser still needs the rs274ngc interpreter linked on top of it.

Bridge smoke test:

./test-linuxcnc-bridge-native.sh

Set LINUXCNC_ROOT=/path/to/linuxcnc if the LinuxCNC source tree is not next to wasm-simulator.

Step 3a: Native librs274 runner

Status: started.

Before porting rs274ngc sources to wasm, use the already-built native LinuxCNC librs274 to validate the bridge and event schema:

./test-linuxcnc-rs274-native.sh

This builds core/tools/linuxcnc_rs274_dump.cpp, links LinuxCNC librs274, and writes:

  • /tmp/cnc_sim_linuxcnc_basic_motion.json
  • /tmp/cnc_sim_linuxcnc_basic_mill.json

This is a native-only stepping stone. Once stable, the same event sink is used by the wasm build.

T... M6 now works in the native runner after initializing LinuxCNC mmap tooldata with a minimal tool table.

Step 3b: API-level native librs274 backend

Status: started.

The public cnc_sim_api can now select backends through config JSON:

{"backend":"smoke"}

or, in a native build compiled with CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND:

{"backend":"linuxcnc-rs274"}

API-level native smoke:

./test-linuxcnc-api-native.sh

Step 4a: Source compilation map

Status: started.

Before replacing librs274 with source-level compilation, keep the source manifest and syntax probe green:

./test-linuxcnc-source-syntax.sh
./test-linuxcnc-source-objects.sh

The manifest is:

  • linuxcnc-rs274-source-files.txt
  • docs/linuxcnc-rs274-source-map.md

The first compile condition discovered is that LinuxCNC user-space source probes need -DULAPI.

Step 3: Native LinuxCNC interpreter comparison

Build a native adapter that links:

  • src/emc/rs274ngc
  • src/emc/nml_intf
  • the Canon event sink

Then run the same G-code corpus through both the temporary parser and LinuxCNC-backed parser. Numeric differences are expected around arc canonicalization; they must be recorded and bounded.

Step 4: Remove browser-hostile dependencies

LinuxCNC interpreter sources currently involve Python/Boost.Python remap paths. For the first WASM target:

  • disable Python remap
  • disable dynamic module loading
  • replace file-backed parameter persistence with in-memory buffers
  • provide browser-safe tool table and INI/config loading through JSON

Step 5: Emscripten build

Generate:

  • web/public/cnc_sim.js
  • web/public/cnc_sim.wasm

Command:

./build-wasm.sh

Step 6: Controller dialects

Fanuc and Siemens support should remain outside the LinuxCNC core as preprocessors/adapters. They normalize controller-specific constructs into the internal event/interpreter input layer.

Functional parity matrix

This matrix tracks LinuxCNC feature coverage for the web/WASM simulator. A feature is not considered covered until it has a native regression in at least the librs274 runner and the source-link runner.

Area Status Regression
Basic modal motion G0/G1/G2/G3 covered tests/gcode/linuxcnc_basic_motion.ngc, tests/gcode/basic_mill.ngc
Tool select/change T... M6 covered with minimal native tooldata tests/gcode/basic_mill.ngc
Tool length offset G43/G49 covered with nonzero tool-table offset through LinuxCNC native/source backends tests/gcode/linuxcnc_tool_length.ngc
RTCP controls G43.4/G43.5/G49 covered as simulator-owned control lines tests/gcode/linuxcnc_rtcp_controls.ngc
Kinematics switch M428/M429/M430 covered as simulator-owned control lines tests/gcode/linuxcnc_rtcp_controls.ngc
Spindle, coolant, program stops, and current tool number M3/M4/M5, M7/M8/M9, M0/M1/M60/M30, M61 Q... covered through LinuxCNC native/source backends tests/gcode/linuxcnc_spindle_direction.ngc, tests/gcode/linuxcnc_coolant.ngc, tests/gcode/linuxcnc_program_stops.ngc, tests/gcode/linuxcnc_tool_number.ngc
Feed and motion control modes G93/G94/G95, G61/G61.1/G64 covered through LinuxCNC native/source backends tests/gcode/linuxcnc_feed_modes.ngc, tests/gcode/linuxcnc_motion_modes.ngc
Canned cycle G81/G80 covered for drilling expand-to-canon path tests/gcode/linuxcnc_canned_cycle.ngc
Coordinate offset Canon events G10 L2, G10 L20, G92/G92.1 covered through LinuxCNC native/source backends tests/gcode/linuxcnc_coordinate_offsets.ngc, tests/gcode/linuxcnc_coordinate_l20.ngc
Cutter compensation covered for tool-table G41/G42 D... and explicit-radius G41.1/G42.1/G40 through LinuxCNC native/source backends tests/gcode/linuxcnc_cutter_comp.ngc
Probe move G38.3 covered as a distinct probe event through LinuxCNC native/source backends tests/gcode/linuxcnc_probe_no_error.ngc
O-word subroutines and calls covered for numeric O... sub/call/return/endsub through LinuxCNC file mode; smoke parser also covers named O<name> sub/call/return/endsub tests/gcode/smoke_oword_subprogram.ngc
Broader canned cycles G73, G82-G89 partially covered: G73, G82, G83, G85, G86, G89 tests/gcode/linuxcnc_canned_cycles_extended.ngc, smoke API regression
Full source-level wasm build pending replace Python/HAL/INI/tooldata support dependencies