Initial wasm simulator checkpoint

This commit is contained in:
CNC Local
2026-05-22 04:43:21 +08:00
commit 55d9fff9f0
54 changed files with 5523 additions and 0 deletions

80
docs/architecture.md Normal file
View File

@@ -0,0 +1,80 @@
# WASM CNC simulator architecture
## Native code boundary
The wasm core should expose a small C ABI and hide all LinuxCNC internals. The browser should never call LinuxCNC classes directly. This keeps the UI independent from whether the backend is LinuxCNC RS274, Fanuc preprocessing, Siemens preprocessing, or a future independent interpreter.
Current ABI entry points:
- `cnc_sim_create`
- `cnc_sim_destroy`
- `cnc_sim_reset`
- `cnc_sim_set_dialect`
- `cnc_sim_load_config_json`
- `cnc_sim_parse_program`
- `cnc_sim_last_error`
The event callback emits normalized `CncSimEvent` records. JavaScript can transform those records into JSON, binary buffers, or renderable typed arrays.
## LinuxCNC integration plan
1. Build a native `CanonEventSink` that implements all functions declared in `canon.hh`.
2. Link the sink with `src/emc/rs274ngc` and `src/emc/nml_intf` instead of the task controller.
3. Stub or remove Python remap support for the first browser target.
4. Compile with Emscripten after replacing `dlopen`, Python, HAL and filesystem-only features.
5. Compare event output with native LinuxCNC using the same G-code corpus.
## Dialect expansion
LinuxCNC support should be the baseline. Fanuc and Siemens support should be implemented as dialect adapters, not by forking the simulator core.
Fanuc high-priority items:
- Macro B variables and expression semantics
- `G65`, `G66`, `G67` macro calls
- common fixed cycles
- cutter compensation and work offsets
- lathe cycles where required
Siemens high-priority items:
- named variables and arithmetic expressions
- `CYCLE*` canned cycles
- `TRANS`, `ROT`, `SCALE`, `MIRROR`
- `TRAORI` and `CYCLE800`
- frame and workpiece coordinate transforms
## Commercial simulator parity
Feature parity needs more than G-code parsing:
- exact toolpath display with modal state inspection
- configurable machine kinematics and limits
- holder, fixture and stock collision detection
- material removal simulation
- time estimation with acceleration and lookahead
- diagnostics for unsupported controller-specific words
- reproducible comparison tests for each controller dialect
## Five-axis and RTCP
The first RTCP implementation is a geometry kernel, not the full LinuxCNC motion
controller. It treats programmed XYZ as the tool-center point, applies A/B/C
orientation to a local tool vector, and computes the compensated pivot/spindle
point needed to keep the tool tip fixed.
RTCP is opt-in through config JSON. When enabled, the original motion events
remain programmed tool-tip motion and an additional `rtcp-pivot` event is emitted
for each rapid/feed/arc event. This keeps the ABI useful for both toolpath
display and machine-axis/pivot visualization.
Current files:
- `core/src/rtcp_kinematics.h`
- `core/src/rtcp_kinematics.cpp`
- `core/tests/rtcp_kinematics_smoke.cpp`
The next integration step is to add machine configuration for rotary topology,
pivot offsets, tool length sources, and rotation order, then apply RTCP
compensation while converting LinuxCNC Canon motion events into renderable
machine/tool-tip trajectories.

194
docs/linuxcnc-porting.md Normal file
View File

@@ -0,0 +1,194 @@
# 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:
```bash
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
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:
```bash
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:
```bash
./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:
```bash
./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:
```json
{"backend":"smoke"}
```
or, in a native build compiled with `CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND`:
```json
{"backend":"linuxcnc-rs274"}
```
API-level native smoke:
```bash
./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:
```bash
./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:
```bash
./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` |
| 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` |
| Canned cycle `G81/G80` | covered for drilling expand-to-canon path | `tests/gcode/linuxcnc_canned_cycle.ngc` |
| Coordinate offset Canon events | bridge-level covered | `core/tests/linuxcnc_canon_bridge_smoke.cpp` |
| Cutter compensation | pending | add LinuxCNC corpus and tolerance checks |
| O-word subroutines and calls | pending | current line-by-line runner does not execute sub bodies like LinuxCNC task planner |
| Broader canned cycles `G82`-`G89` | pending | add corpus after `G81` baseline |
| Full source-level wasm build | pending | replace Python/HAL/INI/tooldata support dependencies |

View File

@@ -0,0 +1,125 @@
# LinuxCNC rs274 source map
This is the working map for moving from native `librs274` linking to source-level compilation and then to wasm.
## Compile assumptions
Native user-space syntax checks require:
- `-DULAPI`
- LinuxCNC include roots:
- `linuxcnc/src`
- `linuxcnc/src/emc`
- `linuxcnc/src/emc/nml_intf`
- `linuxcnc/src/emc/rs274ngc`
- `linuxcnc/src/emc/motion`
- `linuxcnc/src/emc/pythonplugin`
- `linuxcnc/include`
- Python development headers for current upstream sources, because the interpreter still embeds Python/Boost.Python paths.
## Source groups
### Interpreter core
These are the first group to keep compiling while we peel away native-only dependencies:
- `interp_arc.cc`
- `interp_array.cc`
- `interp_base.cc`
- `interp_check.cc`
- `interp_convert.cc`
- `interp_cycles.cc`
- `interp_execute.cc`
- `interp_find.cc`
- `interp_g7x.cc`
- `interp_inspection.cc`
- `interp_internal.cc`
- `interp_inverse.cc`
- `interp_namedparams.cc`
- `interp_o_word.cc`
- `interp_python.cc`
- `interp_queue.cc`
- `interp_read.cc`
- `interp_remap.cc`
- `interp_setup.cc`
- `interp_write.cc`
- `modal_state.cc`
- `nurbs_additional_functions.cc`
- `rs274ngc_pre.cc`
### Python binding modules
These are not needed for the browser simulator ABI and should not be part of the wasm core:
- `canonmodule.cc`
- `gcodemodule.cc`
- `interpmodule.cc`
- `pyarrays.cc`
- `pyblock.cc`
- `pyemctypes.cc`
- `pyinterp1.cc`
- `pyparamclass.cc`
### Browser-hostile dependencies to replace
- Python/Boost.Python remap and named parameter hooks.
- `dlopen`/`dlsym` interpreter loading in `interp_base.cc`.
- mmap-backed `tooldata_mmap.cc`.
- persistent parameter file writes.
- dynamic INI/HAL queries.
## Current syntax probe
Run:
```bash
./test-linuxcnc-source-syntax.sh
```
The probe checks a representative subset of source files with `-DULAPI`. It is not a full source build yet; it is a guardrail before the full source backend is introduced.
## Current object probe
Run:
```bash
./test-linuxcnc-source-objects.sh
```
This compiles every `core:` entry in `linuxcnc-rs274-source-files.txt` into object files. As of this step, the core interpreter sources compile to `.o` in the native environment with `-DULAPI`.
The next boundary is linking. Expected link risks:
- `PythonPlugin` and Boost.Python symbols from remap/named parameter paths.
- Python module initialization symbols if the source backend reuses the existing builtin module setup.
- `tooldata_*` implementations, currently mmap-backed in native LinuxCNC and unsuitable for wasm.
- dynamic loader code in `interp_base.cc`.
- parameter file persistence in `rs274ngc_pre.cc`.
## Current source-link probe
Run:
```bash
./test-linuxcnc-source-link.sh
```
This compiles every `core:` interpreter source into local objects and links the
native runner without `librs274`. It still uses built LinuxCNC support objects
and libraries for the dependencies that have not been replaced yet:
- Boost.Python binding module initializers required by the current interpreter constructor.
- `libpyplugin` for Python remap and named-parameter hooks.
- `liblinuxcncini` and `liblinuxcnchal` for INI/HAL named parameter paths.
- `liblinuxcnc-uspace-posix` for `rtapi_*` user-space helpers.
- `libtooldata` for the current mmap-backed native tool table.
This is not wasm-ready yet, but it proves the simulator can own and compile the
RS274 interpreter core sources directly. The next source-port boundary is to
replace each support dependency with browser-safe shims instead of pulling in
the LinuxCNC task, motion, HAL, and Python runtime layers.
The native runner used by this probe now recognizes simulator-owned control
lines before handing code to LinuxCNC: `M428`, `M429`, `M430`, `G43.4`,
`G43.5`, and `G49`. This keeps the direct runner aligned with the public
`linuxcnc-rs274` API backend for RTCP and kinematics switch tests.