From f3f3a1dfa5f82087d4838a88146bb29bde23af69 Mon Sep 17 00:00:00 2001 From: wangdequan Date: Sat, 23 May 2026 17:59:24 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E7=A4=BA=E8=AF=8D=EF=BC=9A=20?= =?UTF-8?q?=E5=AF=B9=E6=A0=87=20LinuxCNC=EF=BC=8C=E5=88=86=E9=98=B6?= =?UTF-8?q?=E6=AE=B5=E5=AE=8C=E5=96=84=20Web=20=E7=89=88=E6=95=B0=E6=8E=A7?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E4=BB=BF=E7=9C=9F=E7=9A=84=20WASM=20?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F=EF=BC=8C=E7=BB=A7=E7=BB=AD=E8=A1=A5=E9=BD=90?= =?UTF-8?q?=20G/M=20=E4=BB=A3=E7=A0=81=E8=AF=AD=E6=B3=95=E5=B9=B6=E4=B8=A5?= =?UTF-8?q?=E6=A0=BC=E6=B5=8B=E8=AF=95=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 总结: 按 LinuxCNC read_g/read_m 的模态组规则,补充同一行同模态组 G/M 码冲突检查;新增 G0 G1 与 M3 M4 拒绝用例。已通过 ./test-native.sh 和 ./test-linuxcnc-source-link.sh。 --- core/src/smoke_gcode_parser.cpp | 184 +++++++++++++++++++++++++++++++ core/tests/cnc_sim_api_smoke.cpp | 19 ++++ 2 files changed, 203 insertions(+) diff --git a/core/src/smoke_gcode_parser.cpp b/core/src/smoke_gcode_parser.cpp index 12e31f2..422dd79 100644 --- a/core/src/smoke_gcode_parser.cpp +++ b/core/src/smoke_gcode_parser.cpp @@ -495,6 +495,187 @@ bool has_m_code(const std::vector &word_list, int code) { return false; } +int linuxcnc_g_code_number(double value) { + const double scaled = value * 10.0; + int code = static_cast(std::floor(scaled)); + const double fraction = scaled - static_cast(code); + if (fraction > 0.999) { + code = static_cast(std::ceil(scaled)); + } + return code; +} + +int supported_linuxcnc_g_modal_group(double value) { + const int code = linuxcnc_g_code_number(value); + switch (code) { + case 0: + case 10: + case 20: + case 30: + case 300: + case 500: + case 510: + case 520: + case 610: + case 620: + case 700: + case 710: + case 720: + case 730: + case 740: + case 760: + case 800: + case 810: + case 820: + case 830: + case 840: + case 850: + case 860: + case 870: + case 880: + case 890: + return 1; + case 170: + case 180: + case 190: + return 2; + case 900: + case 910: + return 3; + case 901: + case 911: + return 4; + case 930: + case 940: + case 950: + return 5; + case 200: + case 210: + case 7000: + case 7100: + return 6; + case 400: + case 410: + case 411: + case 420: + case 421: + return 7; + case 430: + case 431: + case 432: + case 434: + case 435: + case 490: + return 8; + case 980: + case 990: + return 10; + case 540: + case 550: + case 560: + case 570: + case 580: + case 590: + case 591: + case 592: + case 593: + return 12; + case 611: + case 640: + return 13; + case 960: + case 970: + return 14; + case 70: + case 80: + return 15; + case 922: + case 923: + return 16; + default: + return -1; + } +} + +int supported_linuxcnc_m_modal_group(int value) { + if (value >= 100 && value <= 199) { + return 10; + } + switch (value) { + case 0: + case 1: + case 2: + case 30: + case 60: + case 99: + return 4; + case 62: + case 63: + case 64: + case 65: + case 66: + return 5; + case 6: + case 61: + return 6; + case 3: + case 4: + case 5: + case 19: + case 70: + case 71: + case 72: + case 73: + return 7; + case 7: + case 8: + case 9: + return 8; + case 48: + case 49: + case 50: + case 51: + case 52: + case 53: + return 9; + default: + return -1; + } +} + +bool check_linuxcnc_modal_group_conflicts(const std::vector &word_list, std::string *error) { + std::array g_modes{}; + std::array m_modes{}; + for (const Word &word : word_list) { + if (word.letter == 'G') { + const int mode = supported_linuxcnc_g_modal_group(word.value); + if (mode < 0) { + continue; + } + if (g_modes[static_cast(mode)]) { + if (error) { + *error = "Two G codes used from same modal group"; + } + return false; + } + g_modes[static_cast(mode)] = true; + } else if (word.letter == 'M') { + const int mode = supported_linuxcnc_m_modal_group(rounded_value(word.value)); + if (mode < 0) { + continue; + } + if (m_modes[static_cast(mode)]) { + if (error) { + *error = "Two M codes used from same modal group"; + } + return false; + } + m_modes[static_cast(mode)] = true; + } + } + return true; +} + std::string lower_compact(const std::string &value) { std::string out; out.reserve(value.size()); @@ -2927,6 +3108,9 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string if (!parse_word_list_strict(line, &word_list, error)) { return -1; } + if (!check_linuxcnc_modal_group_conflicts(word_list, error)) { + return -1; + } refresh_readonly_named_parameters(line_number); const std::string o_identifier = first_o_identifier(keyword_line, word_list); const bool has_o_word = !o_identifier.empty(); diff --git a/core/tests/cnc_sim_api_smoke.cpp b/core/tests/cnc_sim_api_smoke.cpp index 194b236..d78e9b8 100644 --- a/core/tests/cnc_sim_api_smoke.cpp +++ b/core/tests/cnc_sim_api_smoke.cpp @@ -3572,6 +3572,25 @@ int main() { sizeof(duplicate_axis_word_program) - 1) != 0, "expected LinuxCNC to reject multiple X words on one line"); + const char same_modal_group_g_program[] = + "G21 G90\n" + "F100\n" + "G0 G1 X1\n"; + events.clear(); + ok &= expect(cnc_sim_parse_program(sim, + same_modal_group_g_program, + sizeof(same_modal_group_g_program) - 1) != 0, + "expected LinuxCNC to reject two G codes from the same modal group"); + + const char same_modal_group_m_program[] = + "G21 G90\n" + "M3 M4\n"; + events.clear(); + ok &= expect(cnc_sim_parse_program(sim, + same_modal_group_m_program, + sizeof(same_modal_group_m_program) - 1) != 0, + "expected LinuxCNC to reject two M codes from the same modal group"); + const char scientific_notation_word_program[] = "G21 G90\n" "F100\n"