diff --git a/docs/linuxcnc-porting.md b/docs/linuxcnc-porting.md index bcb24ec..69ba8b0 100644 --- a/docs/linuxcnc-porting.md +++ b/docs/linuxcnc-porting.md @@ -190,7 +190,7 @@ This matrix tracks LinuxCNC feature coverage for the web/WASM simulator. A featu | 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` | +| Basic modal motion `G0/G1/G2/G3` | covered, including `G91` incremental linear motion, R-format arcs, `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/incremental_and_r_arc.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` | @@ -211,7 +211,7 @@ This matrix tracks LinuxCNC feature coverage for the web/WASM simulator. A featu | 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; source-linked read/execution errors cover malformed O-word commands, missing control-expression brackets, duplicate/undefined/unmatched labels, out-of-context `return`/`endsub`, wrong subroutine close style, too many or empty call parameters, too many nested call levels, Fanuc-style `M98` missing `P`, and midline/comment-prefixed `M98` falling through to normal M-code handling; smoke parser also covers named `O` 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`, `tests/gcode/linuxcnc_oword_unknown_command_error.ngc`, `tests/gcode/linuxcnc_oword_unexpected_trailing_item_error.ngc`, `tests/gcode/linuxcnc_oword_if_missing_bracket_error.ngc`, `tests/gcode/linuxcnc_oword_elseif_missing_bracket_error.ngc`, `tests/gcode/linuxcnc_oword_while_missing_bracket_error.ngc`, `tests/gcode/linuxcnc_oword_repeat_missing_bracket_error.ngc`, `tests/gcode/linuxcnc_oword_too_many_call_parameters_error.ngc`, `tests/gcode/linuxcnc_oword_empty_call_parameter_error.ngc`, `tests/gcode/linuxcnc_oword_too_many_call_levels_error.ngc`, `tests/gcode/linuxcnc_oword_endsub_without_sub_error.ngc`, `tests/gcode/linuxcnc_oword_return_without_sub_error.ngc`, `tests/gcode/linuxcnc_oword_duplicate_if_label_error.ngc`, `tests/gcode/linuxcnc_oword_elseif_undefined_label_error.ngc`, `tests/gcode/linuxcnc_oword_else_undefined_label_error.ngc`, `tests/gcode/linuxcnc_oword_endif_undefined_label_error.ngc`, `tests/gcode/linuxcnc_oword_break_undefined_label_error.ngc`, `tests/gcode/linuxcnc_oword_continue_undefined_label_error.ngc`, `tests/gcode/linuxcnc_oword_endwhile_undefined_label_error.ngc`, `tests/gcode/linuxcnc_oword_elseif_no_matching_if_error.ngc`, `tests/gcode/linuxcnc_oword_break_no_matching_loop_error.ngc`, `tests/gcode/linuxcnc_oword_continue_no_matching_loop_error.ngc`, `tests/gcode/linuxcnc_oword_endrepeat_no_matching_repeat_error.ngc`, `tests/gcode/linuxcnc_oword_endif_no_matching_if_error.ngc`, `tests/gcode/linuxcnc_oword_endwhile_no_matching_while_error.ngc`, `tests/gcode/linuxcnc_oword_fanuc_requires_m99_error.ngc`, `tests/gcode/linuxcnc_oword_sub_requires_endsub_error.ngc`, `tests/gcode/linuxcnc_m98_missing_p_word_error.ngc`, `tests/gcode/linuxcnc_midline_m98_error.ngc`, `tests/gcode/linuxcnc_comment_prefixed_m98_error.ngc` | +| O-word and Fanuc subroutines, calls, and control flow | covered for numeric and named `O... sub/call/return/endsub`, Fanuc-style `M98 P... L...`/`M99`, plus `if`/`elseif`/`else`, `while`, `repeat`, `do`/`while`, `break`, and `continue` through LinuxCNC file mode; source-linked read/execution errors cover malformed O-word commands, missing control-expression brackets, duplicate/undefined/unmatched labels, out-of-context `return`/`endsub`, wrong subroutine close style, too many or empty call parameters, too many nested call levels, Fanuc-style `M98` missing `P`, and midline/comment-prefixed `M98` falling through to normal M-code handling; smoke parser also covers named `O` sub/call/return/endsub and loop-control forms | `tests/gcode/smoke_oword_subprogram.ngc`, `tests/gcode/linuxcnc_named_oword_subprogram.ngc`, `tests/gcode/smoke_subprogram_m98.ngc`, `tests/gcode/linuxcnc_oword_control_flow.ngc`, `tests/gcode/linuxcnc_oword_unknown_command_error.ngc`, `tests/gcode/linuxcnc_oword_unexpected_trailing_item_error.ngc`, `tests/gcode/linuxcnc_oword_if_missing_bracket_error.ngc`, `tests/gcode/linuxcnc_oword_elseif_missing_bracket_error.ngc`, `tests/gcode/linuxcnc_oword_while_missing_bracket_error.ngc`, `tests/gcode/linuxcnc_oword_repeat_missing_bracket_error.ngc`, `tests/gcode/linuxcnc_oword_too_many_call_parameters_error.ngc`, `tests/gcode/linuxcnc_oword_empty_call_parameter_error.ngc`, `tests/gcode/linuxcnc_oword_too_many_call_levels_error.ngc`, `tests/gcode/linuxcnc_oword_endsub_without_sub_error.ngc`, `tests/gcode/linuxcnc_oword_return_without_sub_error.ngc`, `tests/gcode/linuxcnc_oword_duplicate_if_label_error.ngc`, `tests/gcode/linuxcnc_oword_elseif_undefined_label_error.ngc`, `tests/gcode/linuxcnc_oword_else_undefined_label_error.ngc`, `tests/gcode/linuxcnc_oword_endif_undefined_label_error.ngc`, `tests/gcode/linuxcnc_oword_break_undefined_label_error.ngc`, `tests/gcode/linuxcnc_oword_continue_undefined_label_error.ngc`, `tests/gcode/linuxcnc_oword_endwhile_undefined_label_error.ngc`, `tests/gcode/linuxcnc_oword_elseif_no_matching_if_error.ngc`, `tests/gcode/linuxcnc_oword_break_no_matching_loop_error.ngc`, `tests/gcode/linuxcnc_oword_continue_no_matching_loop_error.ngc`, `tests/gcode/linuxcnc_oword_endrepeat_no_matching_repeat_error.ngc`, `tests/gcode/linuxcnc_oword_endif_no_matching_if_error.ngc`, `tests/gcode/linuxcnc_oword_endwhile_no_matching_while_error.ngc`, `tests/gcode/linuxcnc_oword_fanuc_requires_m99_error.ngc`, `tests/gcode/linuxcnc_oword_sub_requires_endsub_error.ngc`, `tests/gcode/linuxcnc_m98_missing_p_word_error.ngc`, `tests/gcode/linuxcnc_midline_m98_error.ngc`, `tests/gcode/linuxcnc_comment_prefixed_m98_error.ngc` | | Parameter expressions and operators | covered for parameter references, source-linked `mod`, `**`, logical, and relational operator precedence, plus LinuxCNC parameter-setting syntax, parameter index validation, named-parameter termination, real-number parsing, divide-by-zero, negative sqrt/power, infinity rejection, malformed and bare `ATAN[]/[]`, unary operation syntax including bare unary items, `read_operation_unary()` unknown-word branches, `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_parameter_missing_equal_error.ngc`, `tests/gcode/linuxcnc_midline_parameter_missing_equal_error.ngc`, `tests/gcode/linuxcnc_parameter_missing_value_error.ngc`, `tests/gcode/linuxcnc_parameter_number_out_of_range_error.ngc`, `tests/gcode/linuxcnc_parameter_reference_out_of_range_error.ngc`, `tests/gcode/linuxcnc_non_integer_parameter_index_error.ngc`, `tests/gcode/linuxcnc_named_parameter_not_terminated_error.ngc`, `tests/gcode/linuxcnc_no_real_value_error.ngc`, `tests/gcode/linuxcnc_bad_number_format_error.ngc`, `tests/gcode/linuxcnc_expression_operators.ngc`, `tests/gcode/linuxcnc_negative_sqrt_error.ngc`, `tests/gcode/linuxcnc_divide_by_zero_error.ngc`, `tests/gcode/linuxcnc_negative_power_error.ngc`, `tests/gcode/linuxcnc_infinite_expression_error.ngc`, `tests/gcode/linuxcnc_atan_missing_slash_error.ngc`, `tests/gcode/linuxcnc_atan_missing_bracket_error.ngc`, `tests/gcode/linuxcnc_top_level_bare_atan_error.ngc`, `tests/gcode/linuxcnc_midline_bare_atan_error.ngc`, `tests/gcode/linuxcnc_unary_missing_bracket_error.ngc`, `tests/gcode/linuxcnc_unary_unknown_word_error.ngc`, `tests/gcode/linuxcnc_unary_unknown_c_word_error.ngc`, `tests/gcode/linuxcnc_unary_unknown_e_word_error.ngc`, `tests/gcode/linuxcnc_unary_unknown_f_word_error.ngc`, `tests/gcode/linuxcnc_unary_unknown_l_word_error.ngc`, `tests/gcode/linuxcnc_unary_unknown_r_word_error.ngc`, `tests/gcode/linuxcnc_unary_unknown_s_word_error.ngc`, `tests/gcode/linuxcnc_unary_unknown_t_word_error.ngc`, `tests/gcode/linuxcnc_unary_unknown_default_word_error.ngc`, `tests/gcode/linuxcnc_top_level_bare_unary_error.ngc`, `tests/gcode/linuxcnc_midline_bare_unary_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 `A/B/C/D/E/F/H/I/J/K/L/P/Q/R/S/T/U/V/W/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_a_words_error.ngc`, `tests/gcode/linuxcnc_multiple_b_words_error.ngc`, `tests/gcode/linuxcnc_multiple_c_words_error.ngc`, `tests/gcode/linuxcnc_multiple_d_words_error.ngc`, `tests/gcode/linuxcnc_multiple_e_words_error.ngc`, `tests/gcode/linuxcnc_multiple_f_words_error.ngc`, `tests/gcode/linuxcnc_multiple_h_words_error.ngc`, `tests/gcode/linuxcnc_multiple_i_words_error.ngc`, `tests/gcode/linuxcnc_multiple_j_words_error.ngc`, `tests/gcode/linuxcnc_multiple_k_words_error.ngc`, `tests/gcode/linuxcnc_multiple_l_words_error.ngc`, `tests/gcode/linuxcnc_multiple_p_words_error.ngc`, `tests/gcode/linuxcnc_multiple_q_words_error.ngc`, `tests/gcode/linuxcnc_multiple_r_words_error.ngc`, `tests/gcode/linuxcnc_multiple_s_words_error.ngc`, `tests/gcode/linuxcnc_multiple_t_words_error.ngc`, `tests/gcode/linuxcnc_multiple_u_words_error.ngc`, `tests/gcode/linuxcnc_multiple_v_words_error.ngc`, `tests/gcode/linuxcnc_multiple_w_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` | | G/M reader validation | source-linked LinuxCNC `read_g()`/`read_m()` errors covered for range checks, unknown code tables, and same-modal-group conflicts from `interp_read.cc` and `interp_array.cc` | `tests/gcode/linuxcnc_g_code_out_of_range_error.ngc`, `tests/gcode/linuxcnc_unknown_g_code_error.ngc`, `tests/gcode/linuxcnc_two_g_codes_same_modal_group_error.ngc`, `tests/gcode/linuxcnc_m_code_greater_199_error.ngc`, `tests/gcode/linuxcnc_unknown_m_code_error.ngc`, `tests/gcode/linuxcnc_two_m_codes_same_modal_group_error.ngc` | diff --git a/test-linuxcnc-rs274-native.sh b/test-linuxcnc-rs274-native.sh index 0e4dc2c..2c4ea5c 100755 --- a/test-linuxcnc-rs274-native.sh +++ b/test-linuxcnc-rs274-native.sh @@ -127,6 +127,10 @@ make --output-sync=target -j"$build_jobs" -f "$rs274_native_makefile" CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_basic_motion.ngc >"$output_dir/cnc_sim_linuxcnc_basic_motion.json" CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/basic_mill.ngc >"$output_dir/cnc_sim_linuxcnc_basic_mill.json" +# Source basis: LinuxCNC src/emc/rs274ngc/interp_convert.cc +# convert_distance_mode() updates G90/G91 state, and convert_arc() routes +# R-format arcs through src/emc/rs274ngc/interp_find.cc arc-center math. +CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/incremental_and_r_arc.ngc >"$output_dir/cnc_sim_linuxcnc_incremental_and_r_arc.json" CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_rtcp_controls.ngc >"$output_dir/cnc_sim_linuxcnc_rtcp_controls.json" CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_spindle_direction.ngc >"$output_dir/cnc_sim_linuxcnc_spindle_direction.json" CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_spindle_modes.ngc >"$output_dir/cnc_sim_linuxcnc_spindle_modes.json" @@ -166,6 +170,10 @@ CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linux CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_probe_no_error.ngc >"$output_dir/cnc_sim_linuxcnc_probe_no_error.json" CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/smoke_oword_subprogram.ngc >"$output_dir/cnc_sim_linuxcnc_oword_subprogram.json" CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_named_oword_subprogram.ngc >"$output_dir/cnc_sim_linuxcnc_named_oword_subprogram.json" +# Source basis: LinuxCNC src/emc/rs274ngc/interp_read.cc read_m() marks +# Fanuc-style M98/M99 subprogram blocks, and interp_o_word.cc execute_call() +# handles L-word repeat counts and return-to-caller position. +CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/smoke_subprogram_m98.ngc >"$output_dir/cnc_sim_linuxcnc_m98_subprogram.json" CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_oword_control_flow.ngc >"$output_dir/cnc_sim_linuxcnc_oword_control_flow.json" CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_named_parameter_exists.ngc >"$output_dir/cnc_sim_linuxcnc_named_parameter_exists.json" CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_indexed_parameters.ngc >"$output_dir/cnc_sim_linuxcnc_indexed_parameters.json" @@ -830,6 +838,37 @@ expected_arc_centers = [(5, 5, 0), (7, 15, 0), (9, 25, 0)] if arc_centers != expected_arc_centers: raise SystemExit(f"unexpected G90.1/G91.1 arc centers: {arc_centers!r}") +incremental_r_arc = load("cnc_sim_linuxcnc_incremental_and_r_arc.json") +if not any( + event["type"] == "linear-feed" and + event["line"] == 4 and + event["start"]["x"] == 0 and + event["end"]["x"] == 10 + for event in incremental_r_arc +): + raise SystemExit("missing LinuxCNC G91 incremental X feed move") +if not any( + event["type"] == "linear-feed" and + event["line"] == 5 and + event["start"]["y"] == 0 and + event["end"]["y"] == 10 + for event in incremental_r_arc +): + raise SystemExit("missing LinuxCNC modal incremental Y feed move") +if not any( + event["type"] == "arc-feed" and + event["line"] == 7 and + event["start"]["x"] == 10 and + event["start"]["y"] == 10 and + event["end"]["x"] == 20 and + event["end"]["y"] == 10 and + event["center"]["x"] == 15 and + event["center"]["y"] == 10 and + event["arcTurns"] == -1 + for event in incremental_r_arc +): + raise SystemExit("missing LinuxCNC absolute R-format clockwise arc") + predefined = load("cnc_sim_linuxcnc_predefined_positions.json") predefined_rapids = [ ( @@ -949,6 +988,23 @@ if any( ): raise SystemExit("LinuxCNC O-word return should skip remaining subprogram body") +m98_subprogram = load("cnc_sim_linuxcnc_m98_subprogram.json") +m98_sub_moves = [ + (event["line"], event["start"]["x"], event["end"]["x"]) + for event in m98_subprogram + if event["type"] == "linear-feed" and event["line"] == 8 +] +if m98_sub_moves != [(8, 0, 5), (8, 5, 10)]: + raise SystemExit(f"unexpected LinuxCNC M98 repeated subprogram moves: {m98_sub_moves!r}") +if not any( + event["type"] == "linear-feed" and + event["line"] == 5 and + event["start"]["x"] == 10 and + event["end"]["x"] == 20 + for event in m98_subprogram +): + raise SystemExit("missing LinuxCNC M98 return-to-caller motion") + named_oword = load("cnc_sim_linuxcnc_named_oword_subprogram.json") if not any( event["type"] == "linear-feed" and @@ -1235,6 +1291,7 @@ PY echo "linuxcnc rs274 native smoke passed" echo "dumped $output_dir/cnc_sim_linuxcnc_basic_motion.json" echo "dumped $output_dir/cnc_sim_linuxcnc_basic_mill.json" +echo "dumped $output_dir/cnc_sim_linuxcnc_incremental_and_r_arc.json" echo "dumped $output_dir/cnc_sim_linuxcnc_rtcp_controls.json" echo "dumped $output_dir/cnc_sim_linuxcnc_spindle_direction.json" echo "dumped $output_dir/cnc_sim_linuxcnc_spindle_modes.json" @@ -1266,6 +1323,7 @@ echo "dumped $output_dir/cnc_sim_linuxcnc_comments.json" echo "dumped $output_dir/cnc_sim_linuxcnc_probe_no_error.json" echo "dumped $output_dir/cnc_sim_linuxcnc_oword_subprogram.json" echo "dumped $output_dir/cnc_sim_linuxcnc_named_oword_subprogram.json" +echo "dumped $output_dir/cnc_sim_linuxcnc_m98_subprogram.json" echo "dumped $output_dir/cnc_sim_linuxcnc_oword_control_flow.json" echo "dumped $output_dir/cnc_sim_linuxcnc_named_parameter_exists.json" echo "dumped $output_dir/cnc_sim_linuxcnc_indexed_parameters.json" diff --git a/test-linuxcnc-source-link.sh b/test-linuxcnc-source-link.sh index 9b52da6..6015898 100755 --- a/test-linuxcnc-source-link.sh +++ b/test-linuxcnc-source-link.sh @@ -178,6 +178,12 @@ CNC_SIM_RS274_VAR="$var_file" \ CNC_SIM_RS274_VAR="$var_file" \ "$build_dir/linuxcnc_rs274_source_dump" tests/gcode/basic_mill.ngc \ >"$output_dir/cnc_sim_linuxcnc_source_basic_mill.json" +# Source basis: LinuxCNC src/emc/rs274ngc/interp_convert.cc +# convert_distance_mode() updates G90/G91 state, and convert_arc() routes +# R-format arcs through src/emc/rs274ngc/interp_find.cc arc-center math. +CNC_SIM_RS274_VAR="$var_file" \ + "$build_dir/linuxcnc_rs274_source_dump" tests/gcode/incremental_and_r_arc.ngc \ + >"$output_dir/cnc_sim_linuxcnc_source_incremental_and_r_arc.json" CNC_SIM_RS274_VAR="$var_file" \ "$build_dir/linuxcnc_rs274_source_dump" tests/gcode/linuxcnc_block_delete.ngc \ >"$output_dir/cnc_sim_linuxcnc_source_block_delete_off.json" @@ -686,6 +692,12 @@ CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" \ CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" \ "$build_dir/linuxcnc_rs274_source_dump" tests/gcode/linuxcnc_named_oword_subprogram.ngc \ >"$output_dir/cnc_sim_linuxcnc_source_named_oword_subprogram.json" +# Source basis: LinuxCNC src/emc/rs274ngc/interp_read.cc read_m() marks +# Fanuc-style M98/M99 subprogram blocks, and interp_o_word.cc execute_call() +# handles L-word repeat counts and return-to-caller position. +CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" \ + "$build_dir/linuxcnc_rs274_source_dump" tests/gcode/smoke_subprogram_m98.ngc \ + >"$output_dir/cnc_sim_linuxcnc_source_m98_subprogram.json" CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" \ "$build_dir/linuxcnc_rs274_source_dump" tests/gcode/linuxcnc_oword_control_flow.ngc \ >"$output_dir/cnc_sim_linuxcnc_source_oword_control_flow.json" @@ -2169,6 +2181,37 @@ expected_arc_centers = [(5, 5, 0), (7, 15, 0), (9, 25, 0)] if arc_centers != expected_arc_centers: raise SystemExit(f"unexpected source-linked G90.1/G91.1 arc centers: {arc_centers!r}") +incremental_r_arc = json.loads((OUTPUT_DIR / "cnc_sim_linuxcnc_source_incremental_and_r_arc.json").read_text()) +if not any( + event["type"] == "linear-feed" and + event["line"] == 4 and + event["start"]["x"] == 0 and + event["end"]["x"] == 10 + for event in incremental_r_arc +): + raise SystemExit("missing source-linked G91 incremental X feed move") +if not any( + event["type"] == "linear-feed" and + event["line"] == 5 and + event["start"]["y"] == 0 and + event["end"]["y"] == 10 + for event in incremental_r_arc +): + raise SystemExit("missing source-linked modal incremental Y feed move") +if not any( + event["type"] == "arc-feed" and + event["line"] == 7 and + event["start"]["x"] == 10 and + event["start"]["y"] == 10 and + event["end"]["x"] == 20 and + event["end"]["y"] == 10 and + event["center"]["x"] == 15 and + event["center"]["y"] == 10 and + event["arcTurns"] == -1 + for event in incremental_r_arc +): + raise SystemExit("missing source-linked absolute R-format clockwise arc") + polar_coordinates = json.loads((OUTPUT_DIR / "cnc_sim_linuxcnc_source_polar_coordinates.json").read_text()) polar_linear_events = [ event @@ -2326,6 +2369,23 @@ if any( ): raise SystemExit("source-linked O-word return should skip remaining subprogram body") +m98_subprogram = json.loads((OUTPUT_DIR / "cnc_sim_linuxcnc_source_m98_subprogram.json").read_text()) +m98_sub_moves = [ + (event["line"], event["start"]["x"], event["end"]["x"]) + for event in m98_subprogram + if event["type"] == "linear-feed" and event["line"] == 8 +] +if m98_sub_moves != [(8, 0, 5), (8, 5, 10)]: + raise SystemExit(f"unexpected source-linked M98 repeated subprogram moves: {m98_sub_moves!r}") +if not any( + event["type"] == "linear-feed" and + event["line"] == 5 and + event["start"]["x"] == 10 and + event["end"]["x"] == 20 + for event in m98_subprogram +): + raise SystemExit("missing source-linked M98 return-to-caller motion") + named_oword = json.loads((OUTPUT_DIR / "cnc_sim_linuxcnc_source_named_oword_subprogram.json").read_text()) if not any( event["type"] == "linear-feed" and @@ -3195,6 +3255,7 @@ PY echo "linuxcnc rs274 source link smoke passed (${#objects[@]} local objects)" echo "dumped $output_dir/cnc_sim_linuxcnc_source_basic_motion.json" echo "dumped $output_dir/cnc_sim_linuxcnc_source_basic_mill.json" +echo "dumped $output_dir/cnc_sim_linuxcnc_source_incremental_and_r_arc.json" echo "dumped $output_dir/cnc_sim_linuxcnc_source_rtcp_controls.json" echo "dumped $output_dir/cnc_sim_linuxcnc_source_spindle_direction.json" echo "dumped $output_dir/cnc_sim_linuxcnc_source_spindle_modes.json" @@ -3227,6 +3288,7 @@ echo "dumped $output_dir/cnc_sim_linuxcnc_source_comments.json" echo "dumped $output_dir/cnc_sim_linuxcnc_source_probe_no_error.json" echo "dumped $output_dir/cnc_sim_linuxcnc_source_oword_subprogram.json" echo "dumped $output_dir/cnc_sim_linuxcnc_source_named_oword_subprogram.json" +echo "dumped $output_dir/cnc_sim_linuxcnc_source_m98_subprogram.json" echo "dumped $output_dir/cnc_sim_linuxcnc_source_oword_control_flow.json" echo "dumped $output_dir/cnc_sim_linuxcnc_source_named_parameter_exists.json" echo "dumped $output_dir/cnc_sim_linuxcnc_source_indexed_parameters.json"