Files
cnc/wasm-port/docs/porting-steps-standalone.md
cnc fb8d8b2d9d 转向直接移植LinuxCNC源程序
结论:明确禁止继续扩展自写 CNC 主体语义,最小 runtime 仅保留为迁移探针;新增 interp_convert.cc 源码直连编译探针,并以最小 emcStatus 状态边界 shim 暴露 native runtime 阻塞点。检查:git diff --check 通过;wasm-port/tests/native/verify_native_probes.sh 通过,linuxcnc_interp_convert_source_probe exitcode=0。
2026-06-06 23:13:44 +08:00

692 lines
21 KiB
Markdown
Raw Permalink 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_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.