继续把 interp_execute.cc、interp_queue.cc、interp_find.cc 等 LinuxCNC 源文件纳入直接编译/链接路径,逐步删除临时 convert_g() wrapper 行为

结论:已将核心 LinuxCNC interpreter 源接入 standalone native 编译链接路径,移除 minimal runtime 中的手写 convert_g 行为,并通过 native probe 验证。
This commit is contained in:
2026-06-06 23:49:01 +08:00
commit 5025b0c1a1
95 changed files with 35453 additions and 0 deletions

View File

@@ -0,0 +1,691 @@
# 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_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.
## 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
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 expanding the standalone interpreter core from the verified minimal
traverse path:
1. replace the temporary minimal `convert_g()` implementation with calls into
vendored LinuxCNC interpreter conversion code.
2. identify and shim the native runtime symbols blocking direct compilation of
`interp_convert.cc`, `interp_execute.cc`, and related interpreter files.
3. keep fixture coverage as regression protection while deleting temporary
hand-written semantics.
4. keep all source changes inside `wasm-port/` and leave `../linuxcnc/`
read-only.