Files
cnc_wams/wasm-port/docs/porting-steps-standalone.md
wangdequan acb207c23e 按建议继续完成后续工作
结论:upstream rs274 side-by-side 基线已扩展到 program-end cleanup canonical 调用;standalone modal-state assertions 仍由 harness 覆盖,完整 native 验证已通过。
2026-06-07 23:04:53 +08:00

34 KiB
Raw Blame History

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:

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

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.

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. keep convert_g() execution on vendored LinuxCNC interpreter conversion code and reject any new standalone Interp::convert_g() implementation.
  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.