结论:解释器 WASM、SDK 统一入口、浏览器解释器 smoke 与兼容性文档已闭环,native 和 host/WASM/browser 验证全部通过。
908 lines
35 KiB
Markdown
908 lines
35 KiB
Markdown
# 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 LinuxCNC’s 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
|
||
|
||
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`
|
||
|
||
## 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`.
|