Initial wasm simulator checkpoint
This commit is contained in:
80
docs/architecture.md
Normal file
80
docs/architecture.md
Normal 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
194
docs/linuxcnc-porting.md
Normal 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 |
|
||||
125
docs/linuxcnc-rs274-source-map.md
Normal file
125
docs/linuxcnc-rs274-source-map.md
Normal 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.
|
||||
Reference in New Issue
Block a user