结论:覆盖 LinuxCNC interp_read.cc 中负数 F/G/H/L/M/S/T 和重复 F/H/L/S/T/X/Y/Z word reader 错误;未扩展 smoke。 验证:./test-linuxcnc-source-link.sh;./test-native.sh。
15 KiB
LinuxCNC to WASM porting steps
This file defines the incremental path for replacing the temporary smoke parser with the LinuxCNC interpreter.
Step 0: Stable simulator ABI
Status: done.
The browser calls only cnc_sim_* functions from core/include/cnc_sim_api.h. This ABI remains stable while the backend changes.
Test:
g++ -std=c++17 -I core/include core/src/cnc_sim_api.cpp core/tests/cnc_sim_api_smoke.cpp -o ./cnc_sim_api_smoke
./cnc_sim_api_smoke
Step 1: Temporary event parser
Status: in progress.
This parser only exists to test the ABI and UI before Emscripten and LinuxCNC are wired in. It is not a production feature implementation. Do not extend it with independently designed G-code behavior; when a behavior needs to change, first locate the LinuxCNC source implementation and either route to that source-backed backend or port the corresponding source logic. It currently handles:
- multiple G/M words on one line
G0,G1,G2,G3G17,G18,G19G90.1,G91.1arc IJK distance modeG20,G21,G70,G71G7,G8lathe diameter/radius mode for X-axis inputG90,G91G4 P...G38.2,G38.3,G38.4,G38.5F,S,T,M3,M4,M5,M6,M2,M30- spindle modes
G96,G97 - IJK and R arcs
- numbered and named parameter assignment/reference such as
#1 = ...,#<name> = ...,X#1, and nested expressions likeX[[#<name> + 2] * 3] - expression functions
ABS[],SQRT[],EXP[],LN[], degree-basedSIN[]/COS[]/TAN[]/ASIN[]/ACOS[], LinuxCNC-styleATAN[]/[],FIX[]/FUP[]/ROUND[], and source-linkedmod/**/logical/relational operator precedence - numeric and named O-word subprograms such as
O100 callandO<name> call - canned cycles
G73,G74,G81,G82,G83,G84,G85,G86,G87,G88,G89withG80,G98,G99,L - coordinate offset events for
G10 L2,G10 L20,G54-G59.3,G52,G92,G92.1,G92.2, andG92.3
It is not the production interpreter. Any remaining smoke-only behavior is technical debt to replace with LinuxCNC source-backed code.
Step 2: Canon event sink
Status: started.
Create a C++ file that implements the Canon functions declared by LinuxCNC src/emc/nml_intf/canon.hh.
High-priority callbacks:
INIT_CANONUSE_LENGTH_UNITSSELECT_PLANESET_FEED_RATESET_SPINDLE_SPEEDSELECT_TOOLCHANGE_TOOLSTRAIGHT_TRAVERSESTRAIGHT_FEEDARC_FEEDDWELLPROGRAM_ENDFINISH
The sink will translate these callbacks to CncSimEvent.
Current bridge files:
core/src/canon_event_sink.hcore/src/canon_event_sink.cppcore/src/linuxcnc_canon_bridge.hcore/src/linuxcnc_canon_bridge.cpp
The LinuxCNC bridge is optional and not built by default:
cmake -S core -B build/native-linuxcnc \
-DCNC_SIM_ENABLE_LINUXCNC_BRIDGE=ON \
-DCNC_SIM_LINUXCNC_ROOT=/path/to/linuxcnc
This bridge is intentionally thin. The production parser still needs the rs274ngc interpreter linked on top of it.
Bridge smoke test:
./test-linuxcnc-bridge-native.sh
Set LINUXCNC_ROOT=/path/to/linuxcnc if the LinuxCNC source tree is not next to wasm-simulator.
Step 3a: Native librs274 runner
Status: started.
Before porting rs274ngc sources to wasm, use the already-built native LinuxCNC librs274 to validate the bridge and event schema:
./test-linuxcnc-rs274-native.sh
This builds core/tools/linuxcnc_rs274_dump.cpp, links LinuxCNC librs274, and writes:
- run-local output files under a per-run temporary
build_dir/outputs/ - example files:
cnc_sim_linuxcnc_basic_motion.json,cnc_sim_linuxcnc_basic_mill.json
This is a native-only stepping stone. Once stable, the same event sink is used by the wasm build.
T... M6 now works in the native runner after initializing LinuxCNC mmap tooldata with a minimal tool table.
Step 3b: API-level native librs274 backend
Status: started.
The public cnc_sim_api defaults to the LinuxCNC RS274 backend and can still select backends through config JSON:
{"backend":"linuxcnc-rs274"}
API-level native smoke:
./test-linuxcnc-api-native.sh
Step 4a: Source compilation map
Status: started.
Before replacing librs274 with source-level compilation, keep the source manifest and syntax probe green:
./test-linuxcnc-source-syntax.sh
./test-linuxcnc-source-objects.sh
The manifest is:
linuxcnc-rs274-source-files.txtdocs/linuxcnc-rs274-source-map.md
The first compile condition discovered is that LinuxCNC user-space source probes need -DULAPI.
Step 3: Native LinuxCNC interpreter comparison
Build a native adapter that links:
src/emc/rs274ngcsrc/emc/nml_intf- the Canon event sink
Then run the same G-code corpus through both the temporary parser and LinuxCNC-backed parser. Numeric differences are expected around arc canonicalization; they must be recorded and bounded.
Step 4: Remove browser-hostile dependencies
LinuxCNC interpreter sources currently involve Python/Boost.Python remap paths. For the first WASM target:
- disable Python remap
- disable dynamic module loading
- route LinuxCNC parameter-file reads and writes through an OPFS-backed wasm
workspace, preserving
rs274ngc.varand.bakpersistence in browser runs as defined bysrc/emc/rs274ngc/interp_internal.hhand exercised byInterp::restore_parameters/Interp::save_parametersinsrc/emc/rs274ngc/rs274ngc_pre.cc - provide browser-safe tool table and INI/config loading through JSON or OPFS-backed files
Step 5: Emscripten build
Generate:
web/public/cnc_sim.jsweb/public/cnc_sim.wasm
Command:
./build-wasm.sh
Step 6: Controller dialects
Fanuc and Siemens support should remain outside the LinuxCNC core as preprocessors/adapters. They normalize controller-specific constructs into the internal event/interpreter input layer.
Functional parity matrix
This matrix tracks LinuxCNC feature coverage for the web/WASM simulator. A feature is not considered covered until it has a native regression in at least the librs274 runner and the source-link runner.
| Area | Status | Regression |
|---|---|---|
Basic modal motion G0/G1/G2/G3 |
covered, including G18/G19 arc plane mapping and G90.1/G91.1 IJK modes |
tests/gcode/linuxcnc_basic_motion.ngc, tests/gcode/basic_mill.ngc, tests/gcode/linuxcnc_arc_planes.ngc, tests/gcode/linuxcnc_arc_distance_modes.ngc |
Predefined position moves G28/G28.1, G30/G30.1 |
covered for stored position, waypoint, selected-axis return, and all-axis return | tests/gcode/linuxcnc_predefined_positions.ngc |
Machine-coordinate move G53 |
covered for explicit/modal G0, G1, and incremental-mode rejection in smoke parser |
tests/gcode/linuxcnc_machine_coordinates.ngc |
Tool select/change T... M6 |
covered with minimal native tooldata | tests/gcode/basic_mill.ngc |
Tool length offset G43/G43.1/G43.2/G49 |
covered with nonzero tool-table offset, dynamic replacement, additive axis offsets, G43.2 H..., and clear through LinuxCNC native/source backends |
tests/gcode/linuxcnc_tool_length.ngc, tests/gcode/linuxcnc_dynamic_tool_length.ngc |
RTCP controls G43.4/G43.5/G49 |
covered as simulator-owned control lines | tests/gcode/linuxcnc_rtcp_controls.ngc |
Kinematics switch M428/M429/M430 |
covered as simulator-owned control lines | tests/gcode/linuxcnc_rtcp_controls.ngc |
Spindle, coolant, program stops, and current tool number M3/M4/M5, M7/M8/M9, M0/M1/M60/M30, M61 Q... |
covered through LinuxCNC native/source backends | tests/gcode/linuxcnc_spindle_direction.ngc, tests/gcode/linuxcnc_coolant.ngc, tests/gcode/linuxcnc_program_stops.ngc, tests/gcode/linuxcnc_tool_number.ngc |
Spindle speed mode G96/G97 |
covered with Canon SET_SPINDLE_MODE preserved as mode state events, including G96 D... max-RPM value |
tests/gcode/linuxcnc_spindle_modes.ngc |
Spindle orientation M19 R... P... Q... |
covered with Canon ORIENT_SPINDLE and WAIT_SPINDLE_ORIENT_COMPLETE preserved as temporary mode state events |
tests/gcode/linuxcnc_spindle_orient.ngc |
Spindle-synchronized feed and threading G33/G33.1/G76 |
covered through smoke, LinuxCNC native, and source-linked paths with Canon START_SPEED_FEED_SYNCH/STOP_SPEED_FEED_SYNCH preserved, rigid-tap line numbers normalized, and G76 multi-pass threading smoke-covered |
tests/gcode/linuxcnc_threading_sync.ngc, tests/gcode/linuxcnc_threading_cycle.ngc, smoke API regression |
Lathe diameter/radius mode G7/G8 |
covered through LinuxCNC native/source backends for same-block X scaling, G76 I/J/K threading geometry interaction, and #<_lathe_diameter_mode>/#<_lathe_radius_mode> readonly named-parameter state |
tests/gcode/linuxcnc_lathe_diameter_mode.ngc, tests/gcode/linuxcnc_lathe_diameter_g76.ngc |
Digital/analog I/O M62-M68 |
covered with Canon digital output, analog output, and input wait callbacks preserved as temporary state events | tests/gcode/linuxcnc_io_controls.ngc |
Override controls M48-M53 |
covered with Canon feed override, spindle speed override, adaptive feed, and feed hold callbacks preserved as temporary state events | tests/gcode/linuxcnc_override_controls.ngc |
Modal state save/restore M70-M73 |
covered for main-program save/invalidate/restore plus O-word subroutine autorestore in smoke parser, with native/source regressions checking restored distance mode, units, and XY plane motion | tests/gcode/linuxcnc_modal_state.ngc, tests/gcode/linuxcnc_modal_autorestore.ngc |
| LinuxCNC read-only named parameters | common modal/unit/feed/spindle reads are covered through LinuxCNC native/source backends, with additional ccomp/spindle-mode readonly reads source-linked | tests/gcode/linuxcnc_readonly_named_parameters.ngc, tests/gcode/linuxcnc_readonly_named_parameters_extra.ngc, tests/gcode/linuxcnc_lathe_diameter_mode.ngc |
Feed and motion control modes G93/G94/G95, G61/G61.1/G64 |
covered through LinuxCNC native/source backends | tests/gcode/linuxcnc_feed_modes.ngc, tests/gcode/linuxcnc_motion_modes.ngc |
Canned cycle G81/G80 |
covered for drilling expand-to-canon path, including G18/G19 plane-specific depth-axis mapping for representative drilling/boring cycles |
tests/gcode/linuxcnc_canned_cycle.ngc, tests/gcode/linuxcnc_canned_cycle_planes.ngc |
Coordinate offset Canon events G10 L2, G10 L20, G10 P0 active-system targeting, G54-G59.3 selection, G52, G92/G92.1/G92.2/G92.3 |
covered through LinuxCNC native/source backends, including all nine G5X indices | tests/gcode/linuxcnc_coordinate_offsets.ngc, tests/gcode/linuxcnc_coordinate_l20.ngc, tests/gcode/linuxcnc_coordinate_p0.ngc, tests/gcode/linuxcnc_coordinate_select_all.ngc, tests/gcode/linuxcnc_g92_restore.ngc, tests/gcode/linuxcnc_g52_offset.ngc |
| Cutter compensation | covered for tool-table G41/G42 D... and explicit-radius G41.1/G42.1/G40 through LinuxCNC native/source backends |
tests/gcode/linuxcnc_cutter_comp.ngc |
Probe moves G38.2/G38.3/G38.4/G38.5 |
covered as distinct probe events with LinuxCNC probe_type preserved through smoke, native, and source-linked paths |
tests/gcode/linuxcnc_probe_no_error.ngc |
| O-word subroutines, calls, and control flow | covered for numeric and named O... sub/call/return/endsub plus if/elseif/else, while, repeat, do/while, break, and continue through LinuxCNC file mode; smoke parser also covers named O<name> sub/call/return/endsub and loop-control forms |
tests/gcode/smoke_oword_subprogram.ngc, tests/gcode/linuxcnc_named_oword_subprogram.ngc, tests/gcode/linuxcnc_oword_control_flow.ngc |
| Parameter expressions and operators | covered for parameter references, source-linked mod, **, logical, and relational operator precedence, plus LinuxCNC divide-by-zero, negative-power, malformed ATAN[]/[], unary operation, read_operation() binary-operation prefix, unknown operation, and unclosed-expression error paths from interp_read.cc/interp_execute.cc |
tests/gcode/linuxcnc_parameter_expression_assignment.ngc, tests/gcode/linuxcnc_expression_operators.ngc, tests/gcode/linuxcnc_divide_by_zero_error.ngc, tests/gcode/linuxcnc_negative_power_error.ngc, tests/gcode/linuxcnc_atan_missing_slash_error.ngc, tests/gcode/linuxcnc_atan_missing_bracket_error.ngc, tests/gcode/linuxcnc_unary_missing_bracket_error.ngc, tests/gcode/linuxcnc_unary_unknown_word_error.ngc, tests/gcode/linuxcnc_binary_unknown_a_operation_error.ngc, tests/gcode/linuxcnc_binary_unknown_e_operation_error.ngc, tests/gcode/linuxcnc_binary_unknown_g_operation_error.ngc, tests/gcode/linuxcnc_binary_unknown_l_operation_error.ngc, tests/gcode/linuxcnc_binary_unknown_m_operation_error.ngc, tests/gcode/linuxcnc_binary_unknown_n_operation_error.ngc, tests/gcode/linuxcnc_binary_unknown_o_operation_error.ngc, tests/gcode/linuxcnc_binary_unknown_x_operation_error.ngc, tests/gcode/linuxcnc_binary_unknown_operation_error.ngc, tests/gcode/linuxcnc_unclosed_expression_error.ngc |
| Word reader validation | source-linked LinuxCNC reader errors covered for negative F/G/H/L/M/S/T values and repeated F/H/L/S/T/X/Y/Z words from interp_read.cc |
tests/gcode/linuxcnc_negative_f_word_error.ngc, tests/gcode/linuxcnc_negative_g_code_error.ngc, tests/gcode/linuxcnc_negative_h_word_error.ngc, tests/gcode/linuxcnc_negative_l_word_error.ngc, tests/gcode/linuxcnc_negative_m_code_error.ngc, tests/gcode/linuxcnc_negative_s_word_error.ngc, tests/gcode/linuxcnc_negative_t_word_error.ngc, tests/gcode/linuxcnc_multiple_f_words_error.ngc, tests/gcode/linuxcnc_multiple_h_words_error.ngc, tests/gcode/linuxcnc_multiple_l_words_error.ngc, tests/gcode/linuxcnc_multiple_s_words_error.ngc, tests/gcode/linuxcnc_multiple_t_words_error.ngc, tests/gcode/linuxcnc_multiple_x_words_error.ngc, tests/gcode/linuxcnc_multiple_y_words_error.ngc, tests/gcode/linuxcnc_multiple_z_words_error.ngc |
Broader canned cycles G73, G74, G82-G89 |
partially covered: G73, G74, G82, G83, G84, G85, G86, G87, G88, G89; smoke parser maps supported cycles across G17/G18/G19, including LinuxCNC's special non-XY G74/G84 tapping argument order and G87 plane-specific I/J/K mapping; native/source regressions now check non-XY G74/G84/G86/G87/G88 sequences |
tests/gcode/linuxcnc_canned_cycles_extended.ngc, tests/gcode/linuxcnc_canned_cycle_planes.ngc, smoke API regression |
| Full source-level wasm build | covered for the current wasm-safe RS274 source set, with remaining work focused on broadening browser-hostile dependency replacement and feature coverage | ./build-wasm.sh, test-linuxcnc-wasm-runtime-link.sh, test-web-wasm-node-smoke.sh, test-web-wasm-browser-smoke.sh |