Add LinuxCNC cutter compensation coverage

This commit is contained in:
cnc
2026-05-22 15:21:34 +08:00
parent 0ff0058417
commit 3dae570f88
5 changed files with 131 additions and 1 deletions

View File

@@ -1,5 +1,6 @@
#include "cnc_sim_api.h"
#include <cmath>
#include <iostream>
#include <vector>
@@ -25,6 +26,10 @@ bool expect(bool value, const char *message) {
return true;
}
bool near(double lhs, double rhs) {
return std::fabs(lhs - rhs) < 0.000001;
}
} // namespace
int main() {
@@ -96,6 +101,12 @@ int main() {
bool saw_g64_mode = false;
bool saw_tool_length = false;
bool saw_tool_length_clear = false;
bool saw_cutter_left_first = false;
bool saw_cutter_left_second = false;
bool saw_cutter_left_off_move = false;
bool saw_cutter_right_first = false;
bool saw_cutter_right_second = false;
bool saw_cutter_right_off_move = false;
for (const auto &event : events) {
saw_tool = saw_tool || event.type == CNC_SIM_EVENT_TOOL_CHANGE;
saw_rapid = saw_rapid || event.type == CNC_SIM_EVENT_RAPID;
@@ -341,6 +352,61 @@ int main() {
ok &= expect(saw_tool_length, "expected LinuxCNC G43 H1 tool length event");
ok &= expect(saw_tool_length_clear, "expected LinuxCNC G49 tool length clear event");
const char cutter_comp_program[] =
"G21 G90 G17\n"
"G0 X0 Y0 Z0\n"
"F100\n"
"G41.1 D6\n"
"G1 X20 Y0\n"
"G1 X20 Y20\n"
"G40\n"
"G1 X0 Y0\n"
"G42.1 D6\n"
"G1 X20 Y0\n"
"G1 X20 Y-20\n"
"G40\n"
"G1 X0 Y0\n"
"M30\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim, cutter_comp_program, sizeof(cutter_comp_program) - 1) == 0,
cnc_sim_last_error(sim));
for (const auto &event : events) {
saw_cutter_left_first = saw_cutter_left_first ||
(event.type == CNC_SIM_EVENT_LINEAR_FEED &&
near(event.end.x, 17.0) &&
near(event.end.y, 3.0));
saw_cutter_left_second = saw_cutter_left_second ||
(event.type == CNC_SIM_EVENT_LINEAR_FEED &&
event.line == 7 &&
near(event.end.x, 17.0) &&
near(event.end.y, 20.0));
saw_cutter_left_off_move = saw_cutter_left_off_move ||
(event.type == CNC_SIM_EVENT_LINEAR_FEED &&
event.line == 8 &&
near(event.end.x, 0.0) &&
near(event.end.y, 0.0));
saw_cutter_right_first = saw_cutter_right_first ||
(event.type == CNC_SIM_EVENT_LINEAR_FEED &&
near(event.end.x, 17.0) &&
near(event.end.y, -3.0));
saw_cutter_right_second = saw_cutter_right_second ||
(event.type == CNC_SIM_EVENT_LINEAR_FEED &&
event.line == 12 &&
near(event.end.x, 17.0) &&
near(event.end.y, -20.0));
saw_cutter_right_off_move = saw_cutter_right_off_move ||
(event.type == CNC_SIM_EVENT_LINEAR_FEED &&
event.line == 13 &&
near(event.end.x, 0.0) &&
near(event.end.y, 0.0));
}
ok &= expect(saw_cutter_left_first, "expected LinuxCNC G41.1 compensated lead-in motion");
ok &= expect(saw_cutter_left_second, "expected LinuxCNC G41.1 compensated side motion");
ok &= expect(saw_cutter_left_off_move, "expected LinuxCNC G40 move after left compensation");
ok &= expect(saw_cutter_right_first, "expected LinuxCNC G42.1 compensated lead-in motion");
ok &= expect(saw_cutter_right_second, "expected LinuxCNC G42.1 compensated side motion");
ok &= expect(saw_cutter_right_off_move, "expected LinuxCNC G40 move after right compensation");
const char comment_program[] =
"G21 G90 G17\n"
"(operator note)\n"

View File

@@ -192,7 +192,7 @@ This matrix tracks LinuxCNC feature coverage for the web/WASM simulator. A featu
| Kinematics switch `M428/M429/M430` | covered as simulator-owned control lines | `tests/gcode/linuxcnc_rtcp_controls.ngc` |
| Canned cycle `G81/G80` | covered for drilling expand-to-canon path | `tests/gcode/linuxcnc_canned_cycle.ngc` |
| Coordinate offset Canon events | bridge-level covered | `core/tests/linuxcnc_canon_bridge_smoke.cpp` |
| Cutter compensation | pending | add LinuxCNC corpus and tolerance checks |
| Cutter compensation | covered for explicit-radius `G41.1/G42.1/G40` through LinuxCNC native/source backends; tool-table `G41/G42 D...` remains pending | `tests/gcode/linuxcnc_cutter_comp.ngc` |
| O-word subroutines and calls | covered for numeric `O... sub/call/return/endsub` through LinuxCNC file mode; smoke parser also covers named `O<name>` sub/call/return/endsub | `tests/gcode/smoke_oword_subprogram.ngc` |
| Broader canned cycles `G73`, `G82`-`G89` | partially covered: `G73`, `G82`, `G83`, `G85`, `G86`, `G89` | `tests/gcode/linuxcnc_canned_cycles_extended.ngc`, smoke API regression |
| Full source-level wasm build | pending | replace Python/HAL/INI/tooldata support dependencies |

View File

@@ -46,6 +46,7 @@ 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_tool_length.ngc >/tmp/cnc_sim_linuxcnc_tool_length.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_canned_cycle.ngc >/tmp/cnc_sim_linuxcnc_canned_cycle.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_canned_cycles_extended.ngc >/tmp/cnc_sim_linuxcnc_canned_cycles_extended.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_cutter_comp.ngc >/tmp/cnc_sim_linuxcnc_cutter_comp.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_comments.ngc >/tmp/cnc_sim_linuxcnc_comments.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_probe_no_error.ngc >/tmp/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 >/tmp/cnc_sim_linuxcnc_oword_subprogram.json
@@ -213,6 +214,28 @@ if not any(
):
raise SystemExit("missing G83 final retract to R plane")
cutter_comp = json.loads(Path("/tmp/cnc_sim_linuxcnc_cutter_comp.json").read_text())
cutter_comp_motions = [
event
for event in cutter_comp
if event["type"] in {"rapid", "linear-feed", "arc-feed"}
]
expected_cutter_comp = [
("rapid", 2, 0, 0),
("linear-feed", 6, 17, 3),
("linear-feed", 7, 17, 20),
("linear-feed", 8, 0, 0),
("linear-feed", 11, 17, -3),
("linear-feed", 12, 17, -20),
("linear-feed", 13, 0, 0),
]
actual_cutter_comp = [
(event["type"], event["line"], event["end"]["x"], event["end"]["y"])
for event in cutter_comp_motions
]
if actual_cutter_comp != expected_cutter_comp:
raise SystemExit(f"unexpected cutter compensation path: {actual_cutter_comp!r}")
comments = json.loads(Path("/tmp/cnc_sim_linuxcnc_comments.json").read_text())
if not any(event["type"] == "comment" and event["line"] == 2 for event in comments):
raise SystemExit("missing LinuxCNC comment line event")
@@ -309,6 +332,7 @@ echo "dumped /tmp/cnc_sim_linuxcnc_motion_modes.json"
echo "dumped /tmp/cnc_sim_linuxcnc_tool_length.json"
echo "dumped /tmp/cnc_sim_linuxcnc_canned_cycle.json"
echo "dumped /tmp/cnc_sim_linuxcnc_canned_cycles_extended.json"
echo "dumped /tmp/cnc_sim_linuxcnc_cutter_comp.json"
echo "dumped /tmp/cnc_sim_linuxcnc_comments.json"
echo "dumped /tmp/cnc_sim_linuxcnc_probe_no_error.json"
echo "dumped /tmp/cnc_sim_linuxcnc_oword_subprogram.json"

View File

@@ -106,6 +106,9 @@ CNC_SIM_RS274_VAR="$var_file" \
CNC_SIM_RS274_VAR="$var_file" \
"$build_dir/linuxcnc_rs274_source_dump" tests/gcode/linuxcnc_canned_cycles_extended.ngc \
>/tmp/cnc_sim_linuxcnc_source_canned_cycles_extended.json
CNC_SIM_RS274_VAR="$var_file" \
"$build_dir/linuxcnc_rs274_source_dump" tests/gcode/linuxcnc_cutter_comp.ngc \
>/tmp/cnc_sim_linuxcnc_source_cutter_comp.json
CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" \
"$build_dir/linuxcnc_rs274_source_dump" tests/gcode/smoke_oword_subprogram.ngc \
>/tmp/cnc_sim_linuxcnc_source_oword_subprogram.json
@@ -188,6 +191,28 @@ if not any(
):
raise SystemExit("missing source-linked G83 final retract to R plane")
cutter_comp = json.loads(Path("/tmp/cnc_sim_linuxcnc_source_cutter_comp.json").read_text())
cutter_comp_motions = [
event
for event in cutter_comp
if event["type"] in {"rapid", "linear-feed", "arc-feed"}
]
expected_cutter_comp = [
("rapid", 2, 0, 0),
("linear-feed", 6, 17, 3),
("linear-feed", 7, 17, 20),
("linear-feed", 8, 0, 0),
("linear-feed", 11, 17, -3),
("linear-feed", 12, 17, -20),
("linear-feed", 13, 0, 0),
]
actual_cutter_comp = [
(event["type"], event["line"], event["end"]["x"], event["end"]["y"])
for event in cutter_comp_motions
]
if actual_cutter_comp != expected_cutter_comp:
raise SystemExit(f"unexpected source-linked cutter compensation path: {actual_cutter_comp!r}")
oword = json.loads(Path("/tmp/cnc_sim_linuxcnc_source_oword_subprogram.json").read_text())
if not any(
event["type"] == "linear-feed" and
@@ -219,4 +244,5 @@ echo "dumped /tmp/cnc_sim_linuxcnc_source_basic_mill.json"
echo "dumped /tmp/cnc_sim_linuxcnc_source_rtcp_controls.json"
echo "dumped /tmp/cnc_sim_linuxcnc_source_canned_cycle.json"
echo "dumped /tmp/cnc_sim_linuxcnc_source_canned_cycles_extended.json"
echo "dumped /tmp/cnc_sim_linuxcnc_source_cutter_comp.json"
echo "dumped /tmp/cnc_sim_linuxcnc_source_oword_subprogram.json"

View File

@@ -0,0 +1,14 @@
G21 G90 G17
G0 X0 Y0 Z0
F100
G41.1 D6
G1 X20 Y0
G1 X20 Y20
G40
G1 X0 Y0
G42.1 D6
G1 X20 Y0
G1 X20 Y-20
G40
G1 X0 Y0
M30