38 KiB
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
linuxcnc/is upstream and read-only for the port effort.- Any source adaptation needed for WASM happens on copied or generated files under
wasm-port/. - No direct edits inside
linuxcnc/src,linuxcnc/lib,linuxcnc/tests, orlinuxcnc/webare part of the port workflow. - LinuxCNC GUI code is reference-only. The migrated UI is rebuilt in HTML + JavaScript.
- 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../linuxcncand 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:
- extract it into
vendor/linuxcnc/; - patch the vendored copy only;
- record the patch in
patches/; - 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:
tools/extracts upstream files intovendor/patches/are applied to vendored copiesruntime/core/compiles vendored files plus wrappersruntime/sdk/consumes the generated WASM moduleruntime/ui/consumes the SDKruntime/opfs/provides browser persistence servicestests/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:
- Record the exact upstream commit from
linuxcnc/.git. - Record local build assumptions:
- compiler version
- emscripten version
- python version
- node version
- Record the first supported LinuxCNC fixture set:
- one 3-axis machine
- one non-trivial kinematics case
- one 5-axis machine
- 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:
- 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/*
- G-code interpreter:
- For each file, classify it:
- copy unchanged
- copy plus shim
- copy plus light patch
- not included in phase 1
- For each file, record dependencies:
- HAL
- RTAPI
- NML
- native file IO
- Python
- GUI
- 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:
- Write extraction scripts in
wasm-port/tools/. - Copy selected upstream files into
wasm-port/vendor/linuxcnc/. - Preserve relative structure where useful, for example:
vendor/linuxcnc/src/emc/rs274ngc/...vendor/linuxcnc/src/emc/ini/...
- Store every modification as:
- a patch in
wasm-port/patches/, or - a thin wrapper outside the vendored file
- a patch in
- 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
../linuxcncdirectly.
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:
- Create
wasm-port/runtime/core/as the portable core layer. - Add wrapper translation units instead of editing vendored files where possible.
- Start with these subsystems in order:
- INI parser
- numeric parameter table
- named parameter logic
- interpreter core
- planner
- kinematics
- Build a native CLI harness first, before any WASM build.
- 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.shbuilds the INI parser probe, interpreter state probe, named-parameter harness, RS274 compile probe, and the minimal interpreter harness from the standalonewasm-port/workspace.tests/native/verify_native_probes.shvalidates that all native probe exit codes are zero and that the minimal interpreter harness emits aSTRAIGHT_TRAVERSEcanonical event forG0 X1.0 Y2.0, plusSET_FEED_RATEandSTRAIGHT_FEEDcanonical events forG1 X3.0 Y4.0 F120.0.- The same validation is now fixture-driven through
tests/fixtures/gcode/minimal_linear.ngcandtests/fixtures/canon/minimal_linear.events. tests/fixtures/gcode/modal_incremental.ngcandtests/fixtures/canon/modal_incremental.eventsaddG90/G91distance-mode switching plus modalG1carry-forward coverage. The temporary wrapper behavior is based on LinuxCNCinterp_convert.cc::convert_distance_mode()andinterp_convert.cc::convert_straight().tests/fixtures/gcode/position_params.ngcandtests/fixtures/canon/position_params.eventscover Z-axis motion plus current-position parameter visibility for#5420,#5421, and#5422. The parameter source basis is LinuxCNCinterp_parameter_def.hhandinterp_namedparams.cc.tests/fixtures/gcode/canned_cycles.ngcandtests/fixtures/canon/canned_cycles.eventscover LinuxCNC canned-cycle conversion through vendoredinterp_cycles.cc, includingG81,G82,G83,G80cancellation, dwell, peck drilling, and incrementalLrepeats.runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cppnow 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, andSELECT_PLANE.tests/fixtures/gcode/coordinate_offsets.ngcandtests/fixtures/canon/coordinate_offsets.eventspinG55, activeG10 L2,G92,G92.1, andG54behavior frominterp_convert.cc::convert_coordinate_system(),interp_convert.cc::convert_setup(), andinterp_convert.cc::convert_axis_offsets().tests/fixtures/gcode/length_units.ngcandtests/fixtures/canon/length_units.eventspinG20/G21unit switching and theUSE_LENGTH_UNITScanonical boundary emitted by vendoredinterp_convert.cc::convert_length_units().tests/fixtures/gcode/plane_selection.ngcandtests/fixtures/canon/plane_selection.eventspinG17/G18/G19plane switching and theSELECT_PLANEcanonical boundary emitted by vendoredinterp_convert.cc::convert_set_plane().tests/fixtures/gcode_errors/cutter_comp_plane_change.ngcandtests/fixtures/canon_errors/cutter_comp_plane_change.expectedpin the LinuxCNC error path for attempting a plane change afterG41.1enables cutter radius compensation. The source basis is vendoredinterp_convert.cc::convert_cutter_compensation_on()andinterp_convert.cc::convert_set_plane().runtime/core/linuxcnc_wrap/linuxcnc_runtime_state_stubs.cppnow initializessetup::cutter_comp_firstmoveto the same initial value as vendoredrs274ngc_pre.cc, allowing the first cutter-compensated straight move to follow LinuxCNCinterp_convert.cc::convert_straight_comp1()instead of the subsequent-move path.tests/fixtures/gcode/cutter_comp_motion.ngcandtests/fixtures/canon/cutter_comp_motion.eventspin positiveG41.1/G40compensated straight-feed output through vendoredinterp_convert.ccandinterp_queue.cc.tests/fixtures/gcode/g53_machine_coordinates.ngcandtests/fixtures/canon/g53_machine_coordinates.eventspinG53machine coordinate straight-traverse behavior through vendoredinterp_find.cc::find_ends()andinterp_convert.cc::convert_straight().tests/fixtures/gcode_errors/g53_incremental.ngcandtests/fixtures/canon_errors/g53_incremental.expectedpin the LinuxCNC rejection path forG53while incremental distance mode is active.runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cppnow 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, andSET_NAIVECAM_TOLERANCE.tests/fixtures/gcode/feed_control_modes.ngcandtests/fixtures/canon/feed_control_modes.eventspinG93,G94,G95,G61,G61.1, andG64 P/Qbehavior frominterp_convert.cc::convert_feed_mode(),interp_convert.cc::convert_control_mode(), andinterp_queue.cc.runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cppnow records probe canonical boundary calls emitted by vendoredinterp_convert.cc::convert_probe():TURN_PROBE_ON,STRAIGHT_PROBE, andTURN_PROBE_OFF.tests/fixtures/gcode/probe_semantics.ngcandtests/fixtures/canon/probe_semantics.eventspinG38.2,G38.3,G38.4, andG38.5probe type handling.runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cppnow records speed/feed synchronization and rigid-tap canonical boundary calls emitted by vendoredinterp_convert.cc::convert_straight():START_SPEED_FEED_SYNCH,STOP_SPEED_FEED_SYNCH, andRIGID_TAP.tests/fixtures/gcode/threading_sync.ngcandtests/fixtures/canon/threading_sync.eventspinG33spindle-synchronized straight feed andG33.1rigid tap behavior.runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cppnow records NURBS G5 canonical boundary calls emitted by vendoredinterp_convert.cc::convert_nurbs():NURBS_G5_FEED.tests/fixtures/gcode/nurbs_g5_semantics.ngcandtests/fixtures/canon/nurbs_g5_semantics.eventspinG5.2control-point collection andG5.3NURBS feed emission.runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cppnow records NURBS G6 canonical boundary calls emitted by vendoredinterp_convert.cc::convert_nurbs():NURBS_G6_FEED.tests/fixtures/gcode/nurbs_g6_semantics.ngcandtests/fixtures/canon/nurbs_g6_semantics.eventspinG6.2order, interpolation mode, control-point, weight, and K segment handling.runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cppnow records spindle-orient canonical boundary calls emitted by vendored LinuxCNC conversion and queue code:ORIENT_SPINDLEandWAIT_SPINDLE_ORIENT_COMPLETE.tests/fixtures/gcode/spindle_orient.ngcandtests/fixtures/canon/spindle_orient.eventspinM19 R/P/Qbehavior frominterp_convert.cc::convert_m()andinterp_queue.cc.runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cppnow records tool-table canonical boundary calls emitted by vendoredinterp_convert.cc::convert_setup_tool():SET_TOOL_TABLE_ENTRY.tests/fixtures/gcode/tool_table_setup.ngcandtests/fixtures/canon/tool_table_setup.eventspinG10 L1tool offset, diameter, front/back angle, and orientation behavior.runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cppnow records the tool-data reload boundary call emitted by vendoredinterp_convert.cc::convert_modal_0():RELOAD_TOOLDATA.tests/fixtures/gcode/tool_reload.ngcandtests/fixtures/canon/tool_reload.eventspinG10 L0dispatch without adding standalone tool-table reload semantics.runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cppnow records interpreter state-tag boundary calls emitted by vendored LinuxCNCinterp_write.cc::write_state_tag()andinterp_convert.cc::update_tag():UPDATE_TAG.tests/fixtures/gcode/state_tag_motion.ngcandtests/fixtures/canon/state_tag_motion.eventspin straight-motion state tag emission without deriving modal state in standalone code.runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cppnow records rotary indexer lock/unlock boundary calls emitted by vendoredinterp_convert.cc::issue_straight_index():UNLOCK_ROTARYandLOCK_ROTARY. A dedicatedlinuxcnc_indexer_harnesspins the LinuxCNC single-axisG0 A...indexer path, including the surrounding motion-control boundary calls, without adding standalone rotary-indexing semantics.runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cppnow records the canonical flush boundary emitted by vendored LinuxCNC file-reading code:FINISH.tests/fixtures/gcode/percent_file_finish.ngcandtests/fixtures/canon/percent_file_finish.eventspin%-delimited file handling fromrs274ngc_pre.cc::open()andinterp_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.cppnow records the reset/queue-drop boundary emitted by vendoredrs274ngc_pre.cc::reset():ON_RESET.tests/fixtures/gcode/file_open_reset.ngcandtests/fixtures/canon/file_open_reset.eventspin the LinuxCNC file-open reset path without adding standalone queue-management semantics.runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cppnow records the interpreter initialization boundary emitted by vendoredrs274ngc_pre.cc::init():INIT_CANON. A dedicatedlinuxcnc_interp_init_harnesscalls vendoredInterp::init()and pins the LinuxCNC initialization sequence:INIT_CANON,USE_LENGTH_UNITS,SET_G5X_OFFSET,SET_G92_OFFSET,SET_XY_ROTATION, andSET_FEED_REFERENCE, without changing the minimal G-code fixture harness startup path.runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cppnow records comment logging canonical boundary calls emitted by vendoredinterp_convert.cc::convert_comment():LOGOPEN,LOG,LOGAPPEND, andLOGCLOSE.tests/fixtures/gcode/comment_logging.ngcandtests/fixtures/canon/comment_logging.eventspin LinuxCNC comment logging dispatch without adding standalone comment semantics.tools/verify_native_linuxcnc_fixture_baseline.shnow 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 afterM2. Native output is filtered to the event classes each standalone fixture explicitly expects, while upstreamrs274output gaps such asWAIT, 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.ngcandtests/fixtures/canon_errors/g1_zero_feed.expectedpin the negativeG1zero-feed case. The source basis is LinuxCNCinterp_convert.cc::convert_straight()andrs274ngc_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, andinterp_execute.cc. tools/build_native_probes.shnow includeslinuxcnc_interp_convert_source_probe, which directly compiles vendoredinterp_convert.cc. The requiredemcStatusshim is limited to the native status boundary used bytag_arc()for machine units; it is not a project-authored CNC behavior implementation.tools/build_native_probes.shnow also includes direct source compile probes for vendoredinterp_read.cc,interp_check.cc, andinterp_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
rs274ngcinterpreter 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, andrs274ngc_pre.cc.rs274ngc_pre.ccis compiled with the existing standaloneUNIT_TEST/LINUXCNC_STANDALONE_USE_RS274_PRE_STATEboundary that isolates Python runtime integration from the browser simulation core. tools/build_native_probes.shnow includes direct source compile probes for the vendored LinuxCNC trajectory-planner and posemath files used bylinuxcnc_tp_api_probe:tp.c,tc.c,tcq.c,spherical_arc.c,blendmath.c,sp_scurve.c,ruckig_wrapper.c, the selectedcruckig/*.csources,emcpose.c,posemath.cc,_posemath.c, andsincos.c. These probes use the sameTP_FLAGSas the standalone TP harness, including the existing-fpermissiveboundary required by upstream enum conversions intp.c.tools/verify_vendor_sync.shnow 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 undervendor/linuxcnc/and the matching file under../linuxcnc/.tools/source-manifest.txtwas also de-duplicated so the manifest is a single authoritative extraction list.- Direct source compile probes now also cover vendored
interp_base.ccandgomath.c.interp_base.ccuses a standaloneEMC2_HOMEcompile-time path boundary for LinuxCNC's dynamic interpreter lookup, andgomath.cis compiled as C withgccso its LinuxCNC C linkage is preserved. The standalonertapi.hshim was made C/C++ compatible for this C-source boundary without changing vendored LinuxCNC files. inifile.ccnow has its own direct source compile probe in addition to the existing INI parser harness. With this probe, every.cand.ccfile listed intools/source-manifest.txthas a native source-level compile check under the standalone build boundary.tools/build_native_probes.shnow emitsbuild/native/source-probes.tsvwhile compiling vendored*_source_probetargets. The native verification script compares that map against every.cand.ccentry intools/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.shnow guards against reintroducing a standaloneInterp::convert_g()definition outsidevendor/linuxcnc/. This keeps G-code group conversion on the vendored LinuxCNCinterp_convert.ccpath instead of allowing the wrapper layer to grow another project-authored conversion implementation.docs/scope-and-baseline.mdrecords the current upstream LinuxCNC commit, local tool versions, and fixture baseline.tools/verify_upstream_baseline.shnow runs before vendor sync validation so extraction drift is checked against the intended upstream HEAD, not an accidental checkout change.docs/source-reuse-map.mdrecords 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.mdrecords the current validation chain, source coverage requirements, native harnesses, fixture coverage, and validation boundaries.docs/drift-report.mdrecords 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:
- Vendor:
../linuxcnc/src/emc/ini/inifile.cc../linuxcnc/src/emc/ini/inifile.hh../linuxcnc/src/emc/ini/inifile.h
- Add shim headers under
wasm-port/runtime/core/shims/for small dependencies only. - Replace file loading through:
- wrapper-level adapter, preferred
- minimal vendored patch only if unavoidable
- Preserve:
#INCLUDE- relative includes
- recursion checks
- line continuation
- duplicate section merge behavior
- typed query behavior
- Add tests for INI semantics under
tests/native/.
Involved LinuxCNC source:
src/emc/ini/inifile.ccsrc/emc/ini/inifile.hhsrc/emc/ini/inifile.h
Phase 5: Port Parameter Tables and Variable Files
Purpose: Preserve LinuxCNC numeric parameter behavior exactly enough for simulation.
Steps:
- 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
- Extract:
setup.parameters[]required_parameters[]readonly_parameters[]- parameter file load/save logic
- Replace native file writes with a standalone file service abstraction.
- Keep LinuxCNC’s parameter text format in phase 1.
- 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.hhsrc/emc/rs274ngc/interp_array.ccsrc/emc/rs274ngc/interp_internal.hhsrc/emc/rs274ngc/rs274ngc_pre.cc
Phase 6: Port Named Parameters and Interpreter State
Purpose: Preserve LinuxCNC variable semantics and controller-visible state.
Steps:
- 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
- Preserve:
context.named_paramsPA_READONLYPA_GLOBALPA_USE_LOOKUPPA_FROM_INI- built-in named parameters from
init_named_parameters()
- Preserve lookup order:
- local
- global
_ini[...]_hal[...]- optional Python providers later
- Export state outward rather than redesigning it in JS.
- Add regression cases for:
- local/global scoping
- built-in state variables
_ini[...]_hal[...]- read-only errors
Involved LinuxCNC source:
src/emc/rs274ngc/interp_namedparams.ccsrc/emc/rs274ngc/interp_internal.hhsrc/emc/rs274ngc/interp_fwd.hhsrc/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:
- Do not vendor the whole HAL runtime as a target runtime dependency.
- 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
- Define a standalone HAL adapter interface:
- lookup by name
- typed numeric value
- connection/existence status
- Make
_hal[...]reads resolve through this adapter. - Seed the adapter from simulation config and runtime state.
- Add tests for:
- existing names
- missing names
- disconnected signals
- type conversion
Involved LinuxCNC source:
src/hal/hal.hsrc/hal/halmodule.ccsrc/emc/rs274ngc/interp_namedparams.ccsrc/emc/ini/inihal.cc
Phase 8: Port the Interpreter Core
Purpose: Build the standalone G-code execution engine without touching upstream files.
Steps:
- Vendor selected files from
../linuxcnc/src/emc/rs274ngc/. - Keep original source as intact as possible in
vendor/. - Add wrappers or minimal patches only in the standalone area.
- Replace these external edges:
- file open/read
- world sync
- HAL reads
- logging
- runtime callbacks
- Preserve:
- modal state
- subroutines
- offsets
- tool handling
- parameter interactions
- error semantics
- Add a canonical event sink interface in standalone code.
Involved LinuxCNC source:
src/emc/rs274ngc/interp_execute.ccsrc/emc/rs274ngc/interp_read.ccsrc/emc/rs274ngc/interp_check.ccsrc/emc/rs274ngc/interp_convert.ccsrc/emc/rs274ngc/interp_cycles.ccsrc/emc/rs274ngc/interp_find.ccsrc/emc/rs274ngc/interp_write.ccsrc/emc/rs274ngc/interp_o_word.ccsrc/emc/rs274ngc/rs274ngc_pre.ccsrc/emc/rs274ngc/rs274ngc_interp.hh
Phase 9: Port Planner and Kinematics
Purpose: Preserve LinuxCNC motion planning and 5-axis simulation behavior.
Steps:
- Vendor selected planner files from
../linuxcnc/src/emc/tp/. - Vendor selected kinematics files from:
../linuxcnc/src/emc/kinematics/- selected
.compsources mirrored into standalone code where needed
- Replace loadable-module assumptions with a registry in the standalone runtime.
- Keep original math and state logic intact as far as practical.
- Add tests for:
- planner outputs
- forward/inverse kinematics
- 5-axis world/joint transforms
- LinuxCNC five-axis
M428/M429/M430remap assets and switchkins machine configuration links
Current five-axis switchkins status:
- Vendored LinuxCNC
5axiskins,xyzac-trt-kins, andxyzbc-trt-kinssources are compiled and probed natively. - Vendored LinuxCNC sample machine assets now cover bridge-mill, table-dual-rotary, and table-rotary-tilting INI/HAL/tool/demo/remap files.
M428,M429, andM430remain LinuxCNCREMAPentries backed by vendored LinuxCNC NGC subroutines; do not replace them with standalone M-code handlers.linuxcnc_remap_parse_harnessnow links vendoredinterp_remap.ccand validates the LinuxCNCInterp::parse_remap()path forxyzac-trt,xyzbc-trt,xyzab-tdr, and bridge-mill NGC remap descriptors, matching each vendored machine's LinuxCNC-definedM428/M429/M430set.linuxcnc_remap_hal_sync_harnessvalidates vendored LinuxCNCM68andM66synchronization through the standalone HAL adapter boundary.linuxcnc_5axis_remap_execute_harnessexecutes thexyzac-trtandxyzbc-trtswitchkinsM429 -> M428 -> M430 -> M429paths plus thexyzab-tdrtwo-remapM429 -> M428 -> M429path and the bridge-millM429 -> M428 -> M430 -> M429path where M428 selects default bridge-mill kinematics, loads vendored machine tool tables through LinuxCNCtooldata_load(), and runs vendoredxyzac_switchkins.ngc,xyzbc_switchkins.ngc,xyzac_switchkins_test_1.ngc,xyzac_switchkins_test_2.ngc,xyzac_switchkins_test_3.ngc,boat-xyzac.ngc,boat-xyzbc.ngc,impeller-7bl-xyzac.ngc, andxyzab-tdr-demo.ngc, plus bridge-mill5axisgui.ngcthrough LinuxCNC remap/file execution paths.- The WASM interpreter core now links vendored
interp_remap.ccplus the Python-only runtime edge stub, and the Node WASM smoke runs vendoredxyzac_switchkins.ngc,xyzbc_switchkins.ngc,xyzac_switchkins_test_1.ngc,xyzac_switchkins_test_2.ngc,xyzac_switchkins_test_3.ngc,boat-xyzac.ngc,boat-xyzbc.ngc,impeller-7bl-xyzac.ngc,xyzab-tdr-demo.ngc, and5axisgui.ngcthrough the SDK C ABI without JavaScript M-code or kinematics semantics. - Browser smoke and the INI panel UI now expose the same validated 5-axis remap execution path by copying vendored LinuxCNC sample-machine files into the interpreter WASM filesystem and calling the SDK C ABI.
- LinuxCNC upstream
tests/remap/duplicate-o-wordandtests/remap/m30-interactionare now vendored unchanged and validated through the generic remap file execution path. Native, Node WASM, and browser interpreter smokes load the vendored INI, program, and remap subroutines, then call vendoredInterp::parse_remap(),open(),read(), andexecute(); standalone code only supplies the INI/file path boundary.
Next work:
- Continue broadening source-backed remap coverage through LinuxCNC upstream tests that can run through NGC REMAP/O-word/file execution without Python semantics.
- Evaluate additional vendored LinuxCNC five-axis sample machines and demos after the TRT boat/impeller, TDR demo, and bridge-mill demo paths are stable in native, Node WASM, browser, and UI remap paths.
Involved LinuxCNC source:
src/emc/tp/tp.csrc/emc/tp/tc.csrc/emc/tp/tcq.csrc/emc/tp/blendmath.csrc/emc/tp/sp_scurve.csrc/emc/tp/ruckig_wrapper.csrc/emc/kinematics/*.csrc/hal/components/xyzab_tdr_kins.compsrc/hal/components/xyzacb_trsrn.compsrc/hal/components/xyzbca_trsrn.compconfigs/sim/axis/vismach/5axis/bridgemill/*configs/sim/axis/vismach/5axis/table-dual-rotary/*configs/sim/axis/vismach/5axis/table-rotary-tilting/*
Phase 10: Build the Standalone WASM Program
Purpose: Keep the migrated program completely outside native LinuxCNC management.
Steps:
- Build the standalone native core first.
- Add a standalone WASM export layer under:
wasm-port/runtime/core/wasm-port/runtime/sdk/
- Use
vendor/sources plus standalone wrappers as build input. - Do not compile from
../linuxcncdirectly in the final WASM product build. - Emit:
wasmmodule- 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:
- Build frontend files under:
wasm-port/runtime/ui/wasm-port/runtime/opfs/
- Implement:
- HTML shell
- JavaScript control panel
- OPFS persistence
- file import/export
- machine/controller state views
- Keep LinuxCNC GUI code reference-only.
- 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:
- Use native LinuxCNC tests and fixtures as semantic baselines.
- Compare:
- LinuxCNC native behavior
- standalone native extracted core
- standalone WASM behavior
- Maintain a drift report.
- Re-run extraction if upstream source changes.
- Keep patches small and traceable.
Outputs:
- drift report
- compatibility regression suite
Management Rules For Daily Development
Use these rules continuously:
- Never edit files under
linuxcnc/as part of the port. - Any needed modification to upstream logic must be applied to vendored copies only.
- Any upstream sync must be script-driven and repeatable.
- Every shim must be documented.
- 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.mdsource-reuse-map.mdstate-porting-strategy.mdwasm-build-strategy.mdfrontend-architecture.mdopfs-file-model.mdcompatibility-validation.mddrift-report.md
Immediate Next Step
Continue from the current verified extracted-core baseline without adding project-authored CNC semantics:
- 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. - 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. - 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.
- Move browser-facing work through SDK and OPFS adapters only after the core behavior is validated against native LinuxCNC or vendored-source harnesses.
- 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 indocs/source-reuse-map.md.