Files
cnc_wams/wasm-port/docs/porting-steps-standalone.md
2026-06-09 06:13:28 +08:00

967 lines
38 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# LinuxCNC WASM Porting Steps
## Goal
Build a separate WASM-based CNC simulation program that:
- uses LinuxCNC source as the semantic source of truth,
- does not modify the original `linuxcnc/` source tree,
- is managed independently from native LinuxCNC,
- provides HTML + JavaScript frontend plus OPFS persistence,
- matches LinuxCNC software behavior as closely as practical except for realtime hardware driving.
## Non-Negotiable Constraints
1. `linuxcnc/` is upstream and read-only for the port effort.
2. Any source adaptation needed for WASM happens on copied or generated files under `wasm-port/`.
3. No direct edits inside `linuxcnc/src`, `linuxcnc/lib`, `linuxcnc/tests`, or `linuxcnc/web` are part of the port workflow.
4. LinuxCNC GUI code is reference-only. The migrated UI is rebuilt in HTML + JavaScript.
5. LinuxCNC compute logic should be reused before any reimplementation is considered.
## Workspace Structure
Create and keep these directories under `wasm-port/`:
- `docs/`
Planning, architecture, and validation documents.
- `vendor/linuxcnc/`
Copied source files selected for the port.
- `patches/`
Patch files against the vendored copies.
- `tools/`
Scripts that extract files from `../linuxcnc`, apply patches, and verify drift.
- `runtime/core/`
WASM-targeted C/C++ code and wrappers.
- `runtime/sdk/`
JavaScript/TypeScript wrapper over the WASM module.
- `runtime/ui/`
HTML + JavaScript frontend.
- `runtime/opfs/`
Browser file adapter.
- `tests/native/`
Native extracted-core regression tests.
- `tests/browser/`
Browser/WASM regression and smoke tests.
## Ported Program Directory Structure
The migrated program itself should be managed as a standalone product under
`wasm-port/`, with LinuxCNC acting as an upstream source provider.
The recommended final structure is:
```text
wasm-port/
├── docs/
├── vendor/
│ └── linuxcnc/
│ └── src/
│ ├── emc/
│ │ ├── ini/
│ │ ├── kinematics/
│ │ ├── rs274ngc/
│ │ └── tp/
│ ├── hal/
│ │ └── components/
│ └── libnml/
│ └── posemath/
├── patches/
├── tools/
├── runtime/
│ ├── core/
│ │ ├── include/
│ │ ├── shims/
│ │ ├── adapters/
│ │ ├── canon/
│ │ ├── session/
│ │ ├── simulation/
│ │ ├── linuxcnc_wrap/
│ │ └── c_api/
│ ├── sdk/
│ │ └── src/
│ ├── ui/
│ │ ├── public/
│ │ └── src/
│ │ ├── panels/
│ │ ├── preview/
│ │ ├── state/
│ │ ├── machine/
│ │ └── files/
│ └── opfs/
├── tests/
│ ├── native/
│ ├── wasm/
│ └── fixtures/
├── build/
│ ├── native/
│ └── wasm/
└── dist/
├── sdk/
└── web/
```
### Directory Responsibilities
- `vendor/linuxcnc/`
Holds extracted upstream LinuxCNC source files. These are copied or
generated from `../linuxcnc` and are the only place where source-level
port patches are applied.
- `patches/`
Stores every patch applied to vendored LinuxCNC files. Patches are tracked
here instead of being mixed into the original source tree.
- `tools/`
Holds extraction, sync, verification, manifest generation, and
compatibility-check scripts.
- `runtime/core/`
The standalone simulation runtime. This is where vendored LinuxCNC source
is wrapped, adapted, and exposed to the rest of the ported program.
- `runtime/core/include/`
Public internal headers for the standalone runtime.
- `runtime/core/shims/`
Small compatibility headers and implementation stubs required to compile
vendored LinuxCNC code outside the native runtime.
- `runtime/core/adapters/`
Host boundary adapters such as file IO, logging, simulation HAL, and
runtime-state providers.
- `runtime/core/canon/`
Canonical motion event collection and serialization.
- `runtime/core/session/`
Per-simulation session ownership for interpreter state, planner state,
machine state, and controller state.
- `runtime/core/simulation/`
Higher-level simulation orchestration built on top of reused LinuxCNC
compute modules.
- `runtime/core/linuxcnc_wrap/`
Thin wrappers around vendored LinuxCNC entry points. Prefer wrappers here
over editing vendored source directly.
- `runtime/core/c_api/`
Stable C ABI exported to the WASM layer.
- `runtime/sdk/`
JavaScript/TypeScript SDK that calls the WASM module and hides memory
management and ABI details from the frontend.
- `runtime/ui/`
The actual CNC simulation web application built with HTML + JavaScript.
- `runtime/ui/src/panels/`
Operator panels, machine configuration panels, parameter editors, and
controller-status panels.
- `runtime/ui/src/preview/`
2D/3D path preview, 5-axis visualization, joint/world overlays, and
playback views.
- `runtime/ui/src/state/`
Browser-side state management for session lifecycle and UI coordination.
- `runtime/ui/src/machine/`
Machine-model-specific UI logic.
- `runtime/ui/src/files/`
File import/export and project/session handling UI logic.
- `runtime/opfs/`
OPFS-backed file services, snapshot persistence, and path mapping.
- `tests/native/`
Native extracted-core regression tests run before WASM build validation.
- `tests/wasm/`
WASM tests for Node and browser environments.
- `tests/fixtures/`
Shared INI, G-code, parameter, tool-table, and machine fixtures.
- `build/`
Intermediate build output. This is disposable.
- `dist/`
Deliverable artifacts such as generated web bundles and SDK packages.
### Source Ownership Rule
The ported program's own source code is expected to live under:
- `runtime/core/`
- `runtime/sdk/`
- `runtime/ui/`
- `runtime/opfs/`
- `tests/`
- `tools/`
- `docs/`
Vendored LinuxCNC code must live under:
- `vendor/linuxcnc/`
The original upstream tree must remain outside the ported program's source
ownership boundary:
- `../linuxcnc/`
### Patch Placement Rule
If a LinuxCNC source file needs adaptation:
1. extract it into `vendor/linuxcnc/`;
2. patch the vendored copy only;
3. record the patch in `patches/`;
4. document why the patch exists in `docs/` or in the patch header.
Do not place LinuxCNC source patches under `runtime/`.
### Build Graph Rule
The standalone build should flow like this:
1. `tools/` extracts upstream files into `vendor/`
2. `patches/` are applied to vendored copies
3. `runtime/core/` compiles vendored files plus wrappers
4. `runtime/sdk/` consumes the generated WASM module
5. `runtime/ui/` consumes the SDK
6. `runtime/opfs/` provides browser persistence services
7. `tests/` validate native and browser behavior
This ensures the ported program remains independently buildable while still
tracking LinuxCNC as the semantic source of truth.
## Phase 0: Freeze Upstream Reference
Purpose:
Make the LinuxCNC source baseline explicit before extraction starts.
Steps:
1. Record the exact upstream commit from `linuxcnc/.git`.
2. Record local build assumptions:
- compiler version
- emscripten version
- python version
- node version
3. Record the first supported LinuxCNC fixture set:
- one 3-axis machine
- one non-trivial kinematics case
- one 5-axis machine
4. Record the first G-code feature set:
- linear motion
- arc motion
- canned cycles
- offsets and coordinate systems
- subroutines
- numeric and named variables
- representative 5-axis programs
Outputs:
- upstream revision note
- supported feature baseline
- supported machine baseline
## Phase 1: Build the Source Reuse Map
Purpose:
Identify exactly which LinuxCNC source files are needed.
Steps:
1. Map each required capability to LinuxCNC files:
- G-code interpreter:
`../linuxcnc/src/emc/rs274ngc/*`
- parameter tables and named variables:
`../linuxcnc/src/emc/rs274ngc/interp_*`
- INI parsing:
`../linuxcnc/src/emc/ini/inifile.*`
- planner:
`../linuxcnc/src/emc/tp/*`
- kinematics:
`../linuxcnc/src/emc/kinematics/*`
and selected files from `../linuxcnc/src/hal/components/*.comp`
- posemath:
`../linuxcnc/src/libnml/posemath/*`
2. For each file, classify it:
- copy unchanged
- copy plus shim
- copy plus light patch
- not included in phase 1
3. For each file, record dependencies:
- HAL
- RTAPI
- NML
- native file IO
- Python
- GUI
4. Save the mapping as a table under `wasm-port/docs/`.
Outputs:
- file-level reuse matrix
- dependency matrix
## Phase 2: Build the Extraction Pipeline
Purpose:
Keep LinuxCNC source untouched while making port-specific copies available.
Steps:
1. Write extraction scripts in `wasm-port/tools/`.
2. Copy selected upstream files into `wasm-port/vendor/linuxcnc/`.
3. Preserve relative structure where useful, for example:
- `vendor/linuxcnc/src/emc/rs274ngc/...`
- `vendor/linuxcnc/src/emc/ini/...`
4. Store every modification as:
- a patch in `wasm-port/patches/`, or
- a thin wrapper outside the vendored file
5. Add a verification script that compares upstream file hashes against the vendored source list.
Rules:
- if upstream changes, re-run extraction;
- reapply patches only in the standalone workspace;
- never patch `../linuxcnc` directly.
Outputs:
- reproducible extraction pipeline
- vendored source tree
## Phase 3: Build the Native Extracted Core
Purpose:
Prove the reusable LinuxCNC compute code works outside the full runtime.
Steps:
1. Create `wasm-port/runtime/core/` as the portable core layer.
2. Add wrapper translation units instead of editing vendored files where possible.
3. Start with these subsystems in order:
- INI parser
- numeric parameter table
- named parameter logic
- interpreter core
- planner
- kinematics
4. Build a native CLI harness first, before any WASM build.
5. Replace host edges with abstractions:
- file provider
- HAL adapter
- runtime state provider
- logging adapter
Outputs:
- portable native core
- native harness executable
Current verified progress:
- `tools/build_native_probes.sh` builds the INI parser probe, interpreter
state probe, named-parameter harness, RS274 compile probe, and the minimal
interpreter harness from the standalone `wasm-port/` workspace.
- `tests/native/verify_native_probes.sh` validates that all native probe
exit codes are zero and that the minimal interpreter harness emits a
`STRAIGHT_TRAVERSE` canonical event for `G0 X1.0 Y2.0`, plus
`SET_FEED_RATE` and `STRAIGHT_FEED` canonical events for
`G1 X3.0 Y4.0 F120.0`.
- The same validation is now fixture-driven through
`tests/fixtures/gcode/minimal_linear.ngc` and
`tests/fixtures/canon/minimal_linear.events`.
- `tests/fixtures/gcode/modal_incremental.ngc` and
`tests/fixtures/canon/modal_incremental.events` add `G90`/`G91`
distance-mode switching plus modal `G1` carry-forward coverage. The
temporary wrapper behavior is based on LinuxCNC
`interp_convert.cc::convert_distance_mode()` and
`interp_convert.cc::convert_straight()`.
- `tests/fixtures/gcode/position_params.ngc` and
`tests/fixtures/canon/position_params.events` cover Z-axis motion plus
current-position parameter visibility for `#5420`, `#5421`, and `#5422`.
The parameter source basis is LinuxCNC
`interp_parameter_def.hh` and `interp_namedparams.cc`.
- `tests/fixtures/gcode/canned_cycles.ngc` and
`tests/fixtures/canon/canned_cycles.events` cover LinuxCNC canned-cycle
conversion through vendored `interp_cycles.cc`, including `G81`, `G82`,
`G83`, `G80` cancellation, dwell, peck drilling, and incremental `L`
repeats.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
coordinate-system canonical boundary calls emitted by vendored LinuxCNC
conversion code: `SET_G5X_OFFSET`, `SET_G92_OFFSET`, `SET_XY_ROTATION`,
`CANON_UPDATE_END_POINT`, `USE_LENGTH_UNITS`, and `SELECT_PLANE`.
`tests/fixtures/gcode/coordinate_offsets.ngc` and
`tests/fixtures/canon/coordinate_offsets.events` pin `G55`, active
`G10 L2`, `G92`, `G92.1`, and `G54` behavior from
`interp_convert.cc::convert_coordinate_system()`,
`interp_convert.cc::convert_setup()`, and
`interp_convert.cc::convert_axis_offsets()`.
- `tests/fixtures/gcode/length_units.ngc` and
`tests/fixtures/canon/length_units.events` pin `G20`/`G21` unit switching
and the `USE_LENGTH_UNITS` canonical boundary emitted by vendored
`interp_convert.cc::convert_length_units()`.
- `tests/fixtures/gcode/plane_selection.ngc` and
`tests/fixtures/canon/plane_selection.events` pin `G17`/`G18`/`G19` plane
switching and the `SELECT_PLANE` canonical boundary emitted by vendored
`interp_convert.cc::convert_set_plane()`.
- `tests/fixtures/gcode_errors/cutter_comp_plane_change.ngc` and
`tests/fixtures/canon_errors/cutter_comp_plane_change.expected` pin the
LinuxCNC error path for attempting a plane change after `G41.1` enables
cutter radius compensation. The source basis is vendored
`interp_convert.cc::convert_cutter_compensation_on()` and
`interp_convert.cc::convert_set_plane()`.
- `runtime/core/linuxcnc_wrap/linuxcnc_runtime_state_stubs.cpp` now initializes
`setup::cutter_comp_firstmove` to the same initial value as vendored
`rs274ngc_pre.cc`, allowing the first cutter-compensated straight move to
follow LinuxCNC `interp_convert.cc::convert_straight_comp1()` instead of the
subsequent-move path. `tests/fixtures/gcode/cutter_comp_motion.ngc` and
`tests/fixtures/canon/cutter_comp_motion.events` pin positive `G41.1`/`G40`
compensated straight-feed output through vendored `interp_convert.cc` and
`interp_queue.cc`.
- `tests/fixtures/gcode/g53_machine_coordinates.ngc` and
`tests/fixtures/canon/g53_machine_coordinates.events` pin `G53` machine
coordinate straight-traverse behavior through vendored
`interp_find.cc::find_ends()` and `interp_convert.cc::convert_straight()`.
`tests/fixtures/gcode_errors/g53_incremental.ngc` and
`tests/fixtures/canon_errors/g53_incremental.expected` pin the LinuxCNC
rejection path for `G53` while incremental distance mode is active.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
feed/control canonical boundary calls emitted by vendored LinuxCNC
conversion and queue code: `SET_TRAVERSE_RATE`, `SET_FEED_REFERENCE`,
`SET_FEED_MODE`, `SET_MOTION_CONTROL_MODE`, and
`SET_NAIVECAM_TOLERANCE`. `tests/fixtures/gcode/feed_control_modes.ngc`
and `tests/fixtures/canon/feed_control_modes.events` pin `G93`, `G94`,
`G95`, `G61`, `G61.1`, and `G64 P/Q` behavior from
`interp_convert.cc::convert_feed_mode()`,
`interp_convert.cc::convert_control_mode()`, and `interp_queue.cc`.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
probe canonical boundary calls emitted by vendored
`interp_convert.cc::convert_probe()`: `TURN_PROBE_ON`,
`STRAIGHT_PROBE`, and `TURN_PROBE_OFF`.
`tests/fixtures/gcode/probe_semantics.ngc` and
`tests/fixtures/canon/probe_semantics.events` pin `G38.2`, `G38.3`,
`G38.4`, and `G38.5` probe type handling.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
speed/feed synchronization and rigid-tap canonical boundary calls emitted by
vendored `interp_convert.cc::convert_straight()`: `START_SPEED_FEED_SYNCH`,
`STOP_SPEED_FEED_SYNCH`, and `RIGID_TAP`.
`tests/fixtures/gcode/threading_sync.ngc` and
`tests/fixtures/canon/threading_sync.events` pin `G33` spindle-synchronized
straight feed and `G33.1` rigid tap behavior.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
NURBS G5 canonical boundary calls emitted by vendored
`interp_convert.cc::convert_nurbs()`: `NURBS_G5_FEED`.
`tests/fixtures/gcode/nurbs_g5_semantics.ngc` and
`tests/fixtures/canon/nurbs_g5_semantics.events` pin `G5.2` control-point
collection and `G5.3` NURBS feed emission.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
NURBS G6 canonical boundary calls emitted by vendored
`interp_convert.cc::convert_nurbs()`: `NURBS_G6_FEED`.
`tests/fixtures/gcode/nurbs_g6_semantics.ngc` and
`tests/fixtures/canon/nurbs_g6_semantics.events` pin `G6.2` order,
interpolation mode, control-point, weight, and K segment handling.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
spindle-orient canonical boundary calls emitted by vendored LinuxCNC
conversion and queue code: `ORIENT_SPINDLE` and
`WAIT_SPINDLE_ORIENT_COMPLETE`. `tests/fixtures/gcode/spindle_orient.ngc`
and `tests/fixtures/canon/spindle_orient.events` pin `M19 R/P/Q` behavior
from `interp_convert.cc::convert_m()` and `interp_queue.cc`.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
tool-table canonical boundary calls emitted by vendored
`interp_convert.cc::convert_setup_tool()`: `SET_TOOL_TABLE_ENTRY`.
`tests/fixtures/gcode/tool_table_setup.ngc` and
`tests/fixtures/canon/tool_table_setup.events` pin `G10 L1` tool offset,
diameter, front/back angle, and orientation behavior.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
the tool-data reload boundary call emitted by vendored
`interp_convert.cc::convert_modal_0()`: `RELOAD_TOOLDATA`.
`tests/fixtures/gcode/tool_reload.ngc` and
`tests/fixtures/canon/tool_reload.events` pin `G10 L0` dispatch without
adding standalone tool-table reload semantics.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
interpreter state-tag boundary calls emitted by vendored LinuxCNC
`interp_write.cc::write_state_tag()` and
`interp_convert.cc::update_tag()`: `UPDATE_TAG`.
`tests/fixtures/gcode/state_tag_motion.ngc` and
`tests/fixtures/canon/state_tag_motion.events` pin straight-motion state tag
emission without deriving modal state in standalone code.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
rotary indexer lock/unlock boundary calls emitted by vendored
`interp_convert.cc::issue_straight_index()`: `UNLOCK_ROTARY` and
`LOCK_ROTARY`. A dedicated `linuxcnc_indexer_harness` pins the LinuxCNC
single-axis `G0 A...` indexer path, including the surrounding motion-control
boundary calls, without adding standalone rotary-indexing semantics.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
the canonical flush boundary emitted by vendored LinuxCNC file-reading
code: `FINISH`. `tests/fixtures/gcode/percent_file_finish.ngc` and
`tests/fixtures/canon/percent_file_finish.events` pin `%`-delimited file
handling from `rs274ngc_pre.cc::open()` and `interp_read.cc::read_text()`.
The native verification script treats this fixture as file-mode-only
because `%` is a LinuxCNC program-file delimiter, not a valid MDI command.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
the reset/queue-drop boundary emitted by vendored
`rs274ngc_pre.cc::reset()`: `ON_RESET`.
`tests/fixtures/gcode/file_open_reset.ngc` and
`tests/fixtures/canon/file_open_reset.events` pin the LinuxCNC file-open
reset path without adding standalone queue-management semantics.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
the interpreter initialization boundary emitted by vendored
`rs274ngc_pre.cc::init()`: `INIT_CANON`. A dedicated
`linuxcnc_interp_init_harness` calls vendored `Interp::init()` and pins the
LinuxCNC initialization sequence: `INIT_CANON`, `USE_LENGTH_UNITS`,
`SET_G5X_OFFSET`, `SET_G92_OFFSET`, `SET_XY_ROTATION`, and
`SET_FEED_REFERENCE`, without changing the minimal G-code fixture harness
startup path.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
comment logging canonical boundary calls emitted by vendored
`interp_convert.cc::convert_comment()`: `LOGOPEN`, `LOG`, `LOGAPPEND`,
and `LOGCLOSE`. `tests/fixtures/gcode/comment_logging.ngc` and
`tests/fixtures/canon/comment_logging.events` pin LinuxCNC comment logging
dispatch without adding standalone comment semantics.
- `tools/verify_native_linuxcnc_fixture_baseline.sh` now runs an expanded
side-by-side baseline against upstream `../linuxcnc/bin/rs274`. It normalizes
native LinuxCNC canonical output for simple motion, offsets, feed-control,
comment/logging, numbered-parameter, spindle-orient, file-finish, and
tool-reload fixtures. It now also includes file-open reset and O-word
subroutine fixture coverage, threading/rigid tap coverage, NURBS dispatch
boundary coverage, comparable canonical runtime edge calls, and the
program-end cleanup calls emitted after `M2`. Native output is filtered to
the event classes each standalone fixture explicitly expects, while upstream
`rs274` output gaps such as `WAIT`, hidden NURBS control-point detail, and
standalone modal-state assertions remain covered by the standalone harness
rather than being treated as side-by-side evidence. The comparison does not
introduce a project-authored CNC semantics oracle.
- `tests/fixtures/gcode_errors/g1_zero_feed.ngc` and
`tests/fixtures/canon_errors/g1_zero_feed.expected` pin the negative
`G1` zero-feed case. The source basis is LinuxCNC
`interp_convert.cc::convert_straight()` and
`rs274ngc_return.hh::NCE_CANNOT_DO_G1_WITH_ZERO_FEED_RATE`.
- This proves the current extracted interpreter slice can parse and execute a
small fixture set through a standalone canonical event sink.
- Important direction change: the minimal wrapper behavior is a migration
probe, not the program body. Do not expand it into a separate CNC
implementation. The next work must retire hand-written wrapper semantics and
move execution through vendored LinuxCNC interpreter source such as
`interp_convert.cc`, `interp_read.cc`, `interp_check.cc`, and
`interp_execute.cc`.
- `tools/build_native_probes.sh` now includes
`linuxcnc_interp_convert_source_probe`, which directly compiles vendored
`interp_convert.cc`. The required `emcStatus` shim is limited to the native
status boundary used by `tag_arc()` for machine units; it is not a
project-authored CNC behavior implementation.
- `tools/build_native_probes.sh` now also includes direct source compile
probes for vendored `interp_read.cc`, `interp_check.cc`, and
`interp_execute.cc`. These probes keep the next interpreter-core migration
work focused on LinuxCNC source entry points and expose missing standalone
runtime shims before wrapper behavior is expanded.
- The direct source compile probes now cover the remaining vendored
`rs274ngc` interpreter core files used by the standalone harness:
`modal_state.cc`, `interp_array.cc`, `interp_internal.cc`,
`interp_arc.cc`, `interp_inverse.cc`, `interp_cycles.cc`,
`interp_g7x.cc`, `interp_queue.cc`, `interp_find.cc`,
`interp_namedparams.cc`, `interp_write.cc`, `interp_o_word.cc`, and
`rs274ngc_pre.cc`. `rs274ngc_pre.cc` is compiled with the existing
standalone `UNIT_TEST`/`LINUXCNC_STANDALONE_USE_RS274_PRE_STATE` boundary
that isolates Python runtime integration from the browser simulation core.
- `tools/build_native_probes.sh` now includes direct source compile probes
for the vendored LinuxCNC trajectory-planner and posemath files used by
`linuxcnc_tp_api_probe`: `tp.c`, `tc.c`, `tcq.c`, `spherical_arc.c`,
`blendmath.c`, `sp_scurve.c`, `ruckig_wrapper.c`, the selected
`cruckig/*.c` sources, `emcpose.c`, `posemath.cc`, `_posemath.c`, and
`sincos.c`. These probes use the same `TP_FLAGS` as the standalone TP
harness, including the existing `-fpermissive` boundary required by
upstream enum conversions in `tp.c`.
- `tools/verify_vendor_sync.sh` now validates the LinuxCNC source extraction
boundary before native probes run. It rejects duplicate manifest entries,
missing or extra vendored files, and byte-level drift between each manifest
file under `vendor/linuxcnc/` and the matching file under `../linuxcnc/`.
`tools/source-manifest.txt` was also de-duplicated so the manifest is a
single authoritative extraction list.
- Direct source compile probes now also cover vendored `interp_base.cc` and
`gomath.c`. `interp_base.cc` uses a standalone `EMC2_HOME` compile-time
path boundary for LinuxCNC's dynamic interpreter lookup, and `gomath.c` is
compiled as C with `gcc` so its LinuxCNC C linkage is preserved. The
standalone `rtapi.h` shim was made C/C++ compatible for this C-source
boundary without changing vendored LinuxCNC files.
- `inifile.cc` now has its own direct source compile probe in addition to the
existing INI parser harness. With this probe, every `.c` and `.cc` file
listed in `tools/source-manifest.txt` has a native source-level compile
check under the standalone build boundary.
- `tools/build_native_probes.sh` now emits `build/native/source-probes.tsv`
while compiling vendored `*_source_probe` targets. The native verification
script compares that map against every `.c` and `.cc` entry in
`tools/source-manifest.txt`, so future LinuxCNC source additions fail
validation unless they also get an explicit standalone source compile probe.
- `tools/verify_no_standalone_cnc_semantics.sh` now guards against
reintroducing a standalone `Interp::convert_g()` definition outside
`vendor/linuxcnc/`. This keeps G-code group conversion on the vendored
LinuxCNC `interp_convert.cc` path instead of allowing the wrapper layer to
grow another project-authored conversion implementation.
- `docs/scope-and-baseline.md` records the current upstream LinuxCNC commit,
local tool versions, and fixture baseline. `tools/verify_upstream_baseline.sh`
now runs before vendor sync validation so extraction drift is checked against
the intended upstream HEAD, not an accidental checkout change.
- `docs/source-reuse-map.md` records the current vendored LinuxCNC source
reuse matrix and dependency matrix, tying each extracted group to its
standalone runtime boundary and validation path.
- `docs/compatibility-validation.md` records the current validation chain,
source coverage requirements, native harnesses, fixture coverage, and
validation boundaries.
- `docs/drift-report.md` records current no-drift enforcement, allowed
standalone runtime boundaries, known gaps, and the current drift conclusion.
## Phase 4: Port INI Parsing Without Editing Upstream
Purpose:
Move LinuxCNC config parsing into the standalone runtime.
Steps:
1. Vendor:
- `../linuxcnc/src/emc/ini/inifile.cc`
- `../linuxcnc/src/emc/ini/inifile.hh`
- `../linuxcnc/src/emc/ini/inifile.h`
2. Add shim headers under `wasm-port/runtime/core/shims/` for small dependencies only.
3. Replace file loading through:
- wrapper-level adapter, preferred
- minimal vendored patch only if unavoidable
4. Preserve:
- `#INCLUDE`
- relative includes
- recursion checks
- line continuation
- duplicate section merge behavior
- typed query behavior
5. Add tests for INI semantics under `tests/native/`.
Involved LinuxCNC source:
- `src/emc/ini/inifile.cc`
- `src/emc/ini/inifile.hh`
- `src/emc/ini/inifile.h`
## Phase 5: Port Parameter Tables and Variable Files
Purpose:
Preserve LinuxCNC numeric parameter behavior exactly enough for simulation.
Steps:
1. Vendor:
- `../linuxcnc/src/emc/rs274ngc/interp_parameter_def.hh`
- `../linuxcnc/src/emc/rs274ngc/interp_array.cc`
- `../linuxcnc/src/emc/rs274ngc/interp_internal.hh`
- `../linuxcnc/src/emc/rs274ngc/rs274ngc_pre.cc`
2. Extract:
- `setup.parameters[]`
- `required_parameters[]`
- `readonly_parameters[]`
- parameter file load/save logic
3. Replace native file writes with a standalone file service abstraction.
4. Keep LinuxCNCs parameter text format in phase 1.
5. Add regression tests for:
- missing file
- out-of-order parameters
- zero-fill behavior
- required parameter persistence
- read-only protection
Involved LinuxCNC source:
- `src/emc/rs274ngc/interp_parameter_def.hh`
- `src/emc/rs274ngc/interp_array.cc`
- `src/emc/rs274ngc/interp_internal.hh`
- `src/emc/rs274ngc/rs274ngc_pre.cc`
## Phase 6: Port Named Parameters and Interpreter State
Purpose:
Preserve LinuxCNC variable semantics and controller-visible state.
Steps:
1. Vendor:
- `../linuxcnc/src/emc/rs274ngc/interp_namedparams.cc`
- `../linuxcnc/src/emc/rs274ngc/interp_internal.hh`
- `../linuxcnc/src/emc/rs274ngc/interp_fwd.hh`
- selected interpreter files needed by setup/state handling
2. Preserve:
- `context.named_params`
- `PA_READONLY`
- `PA_GLOBAL`
- `PA_USE_LOOKUP`
- `PA_FROM_INI`
- built-in named parameters from `init_named_parameters()`
3. Preserve lookup order:
- local
- global
- `_ini[...]`
- `_hal[...]`
- optional Python providers later
4. Export state outward rather than redesigning it in JS.
5. Add regression cases for:
- local/global scoping
- built-in state variables
- `_ini[...]`
- `_hal[...]`
- read-only errors
Involved LinuxCNC source:
- `src/emc/rs274ngc/interp_namedparams.cc`
- `src/emc/rs274ngc/interp_internal.hh`
- `src/emc/rs274ngc/interp_fwd.hh`
- `src/emc/rs274ngc/interpmodule.cc`
## Phase 7: Replace HAL Runtime With a Simulation HAL Adapter
Purpose:
Keep LinuxCNC interpreter-visible HAL behavior without migrating HAL itself.
Steps:
1. Do not vendor the whole HAL runtime as a target runtime dependency.
2. Use LinuxCNC HAL source as reference only for interface semantics:
- `../linuxcnc/src/hal/hal.h`
- `../linuxcnc/src/hal/halmodule.cc`
- `../linuxcnc/src/emc/ini/inihal.cc`
3. Define a standalone HAL adapter interface:
- lookup by name
- typed numeric value
- connection/existence status
4. Make `_hal[...]` reads resolve through this adapter.
5. Seed the adapter from simulation config and runtime state.
6. Add tests for:
- existing names
- missing names
- disconnected signals
- type conversion
Involved LinuxCNC source:
- `src/hal/hal.h`
- `src/hal/halmodule.cc`
- `src/emc/rs274ngc/interp_namedparams.cc`
- `src/emc/ini/inihal.cc`
## Phase 8: Port the Interpreter Core
Purpose:
Build the standalone G-code execution engine without touching upstream files.
Steps:
1. Vendor selected files from `../linuxcnc/src/emc/rs274ngc/`.
2. Keep original source as intact as possible in `vendor/`.
3. Add wrappers or minimal patches only in the standalone area.
4. Replace these external edges:
- file open/read
- world sync
- HAL reads
- logging
- runtime callbacks
5. Preserve:
- modal state
- subroutines
- offsets
- tool handling
- parameter interactions
- error semantics
6. Add a canonical event sink interface in standalone code.
Involved LinuxCNC source:
- `src/emc/rs274ngc/interp_execute.cc`
- `src/emc/rs274ngc/interp_read.cc`
- `src/emc/rs274ngc/interp_check.cc`
- `src/emc/rs274ngc/interp_convert.cc`
- `src/emc/rs274ngc/interp_cycles.cc`
- `src/emc/rs274ngc/interp_find.cc`
- `src/emc/rs274ngc/interp_write.cc`
- `src/emc/rs274ngc/interp_o_word.cc`
- `src/emc/rs274ngc/rs274ngc_pre.cc`
- `src/emc/rs274ngc/rs274ngc_interp.hh`
## Phase 9: Port Planner and Kinematics
Purpose:
Preserve LinuxCNC motion planning and 5-axis simulation behavior.
Steps:
1. Vendor selected planner files from `../linuxcnc/src/emc/tp/`.
2. Vendor selected kinematics files from:
- `../linuxcnc/src/emc/kinematics/`
- selected `.comp` sources mirrored into standalone code where needed
3. Replace loadable-module assumptions with a registry in the standalone runtime.
4. Keep original math and state logic intact as far as practical.
5. Add tests for:
- planner outputs
- forward/inverse kinematics
- 5-axis world/joint transforms
- LinuxCNC five-axis `M428`/`M429`/`M430` remap assets and switchkins
machine configuration links
Current five-axis switchkins status:
1. Vendored LinuxCNC `5axiskins`, `xyzac-trt-kins`, and `xyzbc-trt-kins`
sources are compiled and probed natively.
2. Vendored LinuxCNC sample machine assets now cover bridge-mill,
table-dual-rotary, and table-rotary-tilting INI/HAL/tool/demo/remap
files.
3. `M428`, `M429`, and `M430` remain LinuxCNC `REMAP` entries backed by
vendored LinuxCNC NGC subroutines; do not replace them with standalone
M-code handlers.
4. `linuxcnc_remap_parse_harness` now links vendored `interp_remap.cc` and
validates the LinuxCNC `Interp::parse_remap()` path for `xyzac-trt`,
`xyzbc-trt`, `xyzab-tdr`, and bridge-mill NGC remap descriptors, matching
each vendored machine's LinuxCNC-defined `M428`/`M429`/`M430` set.
5. `linuxcnc_remap_hal_sync_harness` validates vendored LinuxCNC `M68` and
`M66` synchronization through the standalone HAL adapter boundary.
6. `linuxcnc_5axis_remap_execute_harness` executes the `xyzac-trt` and
`xyzbc-trt` switchkins `M429 -> M428 -> M430 -> M429` paths plus the
`xyzab-tdr` two-remap `M429 -> M428 -> M429` path and the bridge-mill
`M429 -> M428 -> M430 -> M429` path where M428 selects default
bridge-mill kinematics, loads vendored machine tool tables through
LinuxCNC `tooldata_load()`, and 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`, and `xyzab-tdr-demo.ngc`, plus bridge-mill
`5axisgui.ngc` through LinuxCNC remap/file execution paths.
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
`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 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
remap execution path by copying vendored LinuxCNC sample-machine files into
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:
1. Continue broadening source-backed remap coverage through LinuxCNC upstream
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
after the TRT boat/impeller, TDR demo, and bridge-mill demo paths are
stable in native, Node WASM, browser, and UI remap paths.
Involved LinuxCNC source:
- `src/emc/tp/tp.c`
- `src/emc/tp/tc.c`
- `src/emc/tp/tcq.c`
- `src/emc/tp/blendmath.c`
- `src/emc/tp/sp_scurve.c`
- `src/emc/tp/ruckig_wrapper.c`
- `src/emc/kinematics/*.c`
- `src/hal/components/xyzab_tdr_kins.comp`
- `src/hal/components/xyzacb_trsrn.comp`
- `src/hal/components/xyzbca_trsrn.comp`
- `configs/sim/axis/vismach/5axis/bridgemill/*`
- `configs/sim/axis/vismach/5axis/table-dual-rotary/*`
- `configs/sim/axis/vismach/5axis/table-rotary-tilting/*`
## Phase 10: Build the Standalone WASM Program
Purpose:
Keep the migrated program completely outside native LinuxCNC management.
Steps:
1. Build the standalone native core first.
2. Add a standalone WASM export layer under:
- `wasm-port/runtime/core/`
- `wasm-port/runtime/sdk/`
3. Use `vendor/` sources plus standalone wrappers as build input.
4. Do not compile from `../linuxcnc` directly in the final WASM product build.
5. Emit:
- `wasm` module
- JS loader
- standalone SDK
Outputs:
- independently managed WASM simulation core
## Phase 11: Build the Frontend and OPFS Layer
Purpose:
Keep the standalone port program separate from LinuxCNC GUI code and storage.
Steps:
1. Build frontend files under:
- `wasm-port/runtime/ui/`
- `wasm-port/runtime/opfs/`
2. Implement:
- HTML shell
- JavaScript control panel
- OPFS persistence
- file import/export
- machine/controller state views
3. Keep LinuxCNC GUI code reference-only.
4. Ensure the frontend depends only on the standalone SDK, not on native LinuxCNC binaries.
Outputs:
- standalone browser program
## Phase 12: Validate Drift Against Upstream
Purpose:
Prove the standalone port still tracks LinuxCNC behavior.
Steps:
1. Use native LinuxCNC tests and fixtures as semantic baselines.
2. Compare:
- LinuxCNC native behavior
- standalone native extracted core
- standalone WASM behavior
3. Maintain a drift report.
4. Re-run extraction if upstream source changes.
5. Keep patches small and traceable.
Outputs:
- drift report
- compatibility regression suite
## Management Rules For Daily Development
Use these rules continuously:
1. Never edit files under `linuxcnc/` as part of the port.
2. Any needed modification to upstream logic must be applied to vendored copies only.
3. Any upstream sync must be script-driven and repeatable.
4. Every shim must be documented.
5. Every local patch against vendored source must be stored as a patch file or clearly isolated wrapper.
## Recommended Document Set
Keep these documents under `wasm-port/docs/`:
- `scope-and-baseline.md`
- `source-reuse-map.md`
- `state-porting-strategy.md`
- `wasm-build-strategy.md`
- `frontend-architecture.md`
- `opfs-file-model.md`
- `compatibility-validation.md`
- `drift-report.md`
## Immediate Next Step
Continue from the current verified extracted-core baseline without adding
project-authored CNC semantics:
1. Keep all `Interp::...` interpreter member behavior on vendored LinuxCNC
source. Standalone code may only provide documented runtime-edge stubs such
as the current Python/remap boundary.
2. Expand WASM interpreter coverage by routing more existing native fixture
paths through `runtime/core/linuxcnc_wrap/linuxcnc_interp_wasm.cpp`, using
the same vendored interpreter source set as the native harness.
3. Promote remaining standalone-only fixture expectations to native LinuxCNC
baselines where possible, especially adapter-heavy paths such as INI/HAL
named parameters, tool-change host state, and richer machine session state.
4. Move browser-facing work through SDK and OPFS adapters only after the core
behavior is validated against native LinuxCNC or vendored-source harnesses.
5. Before adding any CNC feature, update `tools/source-manifest.txt`, extract
the LinuxCNC source file, add a source probe or harness, and document the
reuse boundary in `docs/source-reuse-map.md`.