From 1d0acddf12deb555f820e15c517c9a2adbce3b07 Mon Sep 17 00:00:00 2001 From: CNC Local Date: Fri, 22 May 2026 05:27:34 +0800 Subject: [PATCH] Emit coolant state events --- README.zh-CN.md | 2 + core/src/linuxcnc_canon_bridge.cpp | 38 ++++++++++++++----- core/src/smoke_gcode_parser.cpp | 28 ++++++++++++++ .../cnc_sim_api_linuxcnc_rs274_smoke.cpp | 36 ++++++++++++++++++ core/tests/cnc_sim_api_smoke.cpp | 35 +++++++++++++++++ core/tests/linuxcnc_canon_bridge_smoke.cpp | 27 +++++++++++++ test-linuxcnc-rs274-native.sh | 12 ++++++ tests/gcode/linuxcnc_coolant.ngc | 5 +++ 8 files changed, 174 insertions(+), 9 deletions(-) create mode 100644 tests/gcode/linuxcnc_coolant.ngc diff --git a/README.zh-CN.md b/README.zh-CN.md index 3df01f3..322bd9a 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -63,6 +63,7 @@ The first useful target is: - 已有注释事件行号回归语料,LinuxCNC `COMMENT` 事件会保留当前 G-code 行号。 - 已有当前刀号事件行号回归语料,LinuxCNC `M61 Q...` 会输出带行号的 `tool-change` 事件。 - 已有停止类事件回归语料,`M0/M1/M60` 会通过 `program-end.reserved` 输出 `1=程序停`、`2=可选停`、`3=托盘交换`。 +- 已有冷却液状态回归语料,`M7/M8/M9` 会通过 `comment.reserved` 临时输出 `10/11=mist off/on`、`20/21=flood off/on`。 - LinuxCNC Canon bridge 会把当前解释行号补到没有自带 `lineno` 的回调事件上,方便 Web 端诊断和源码高亮。 - 下一步是继续替换源码级链接中残留的 Python、INI/HAL、文件系统和动态加载依赖,并把 RTCP 内核接入 canon 事件流和机型配置。 @@ -86,6 +87,7 @@ The first useful target is: - `M61 Q...` 当前刀号事件行号回归 - LinuxCNC 注释事件行号回归 - `M0` / `M1` / `M60` 停止类事件回归 +- `M7` / `M8` / `M9` 冷却液状态回归 - `G10 L2` / `G10 L20` / `G92.1` 坐标系事件回归 - `G38.3` 探针事件回归 diff --git a/core/src/linuxcnc_canon_bridge.cpp b/core/src/linuxcnc_canon_bridge.cpp index c24085b..1c48b67 100644 --- a/core/src/linuxcnc_canon_bridge.cpp +++ b/core/src/linuxcnc_canon_bridge.cpp @@ -15,6 +15,8 @@ namespace { CanonEventSink *active_sink = nullptr; int active_line = 0; +bool flood_on = false; +bool mist_on = false; std::string parameter_file_name = "rs274ngc.var"; CncSimPose make_pose(double x, double y, double z, @@ -86,12 +88,26 @@ int event_line(int lineno = 0) { return lineno > 0 ? lineno : active_line; } +void emit_state_event(CncSimEventType type, int reserved) { + if (!active_sink) { + return; + } + CncSimEvent event{}; + event.version = 1; + event.type = type; + event.line = event_line(); + event.reserved = reserved; + active_sink->emit_raw(event); +} + } // namespace void cnc_sim_linuxcnc_set_canon_sink(CanonEventSink *sink) { trace_call("cnc_sim_linuxcnc_set_canon_sink"); active_sink = sink; active_line = 0; + flood_on = false; + mist_on = false; } CanonEventSink *cnc_sim_linuxcnc_get_canon_sink() { @@ -107,6 +123,8 @@ void INIT_CANON() { if (active_sink) { active_sink->reset(); } + flood_on = false; + mist_on = false; } void USE_LENGTH_UNITS(CANON_UNITS units) { @@ -410,13 +428,7 @@ void CLAMP_AXIS(CANON_AXIS) { } void COMMENT(const char *) { - if (active_sink) { - CncSimEvent event{}; - event.version = 1; - event.type = CNC_SIM_EVENT_COMMENT; - event.line = event_line(); - active_sink->emit_raw(event); - } + emit_state_event(CNC_SIM_EVENT_COMMENT, 0); } void DISABLE_ADAPTIVE_FEED() { @@ -444,9 +456,13 @@ void ENABLE_FEED_HOLD() { } void FLOOD_OFF() { + flood_on = false; + emit_state_event(CNC_SIM_EVENT_COMMENT, 20); } void FLOOD_ON() { + flood_on = true; + emit_state_event(CNC_SIM_EVENT_COMMENT, 21); } void MESSAGE(char *) { @@ -465,9 +481,13 @@ void LOGCLOSE() { } void MIST_OFF() { + mist_on = false; + emit_state_event(CNC_SIM_EVENT_COMMENT, 10); } void MIST_ON() { + mist_on = true; + emit_state_event(CNC_SIM_EVENT_COMMENT, 11); } void PALLET_SHUTTLE() { @@ -555,7 +575,7 @@ double GET_EXTERNAL_FEED_RATE() { } int GET_EXTERNAL_FLOOD() { - return 0; + return flood_on ? 1 : 0; } CANON_UNITS GET_EXTERNAL_LENGTH_UNIT_TYPE() { @@ -571,7 +591,7 @@ double GET_EXTERNAL_ANGLE_UNITS() { } int GET_EXTERNAL_MIST() { - return 0; + return mist_on ? 1 : 0; } CANON_MOTION_MODE GET_EXTERNAL_MOTION_CONTROL_MODE() { diff --git a/core/src/smoke_gcode_parser.cpp b/core/src/smoke_gcode_parser.cpp index 2132a63..a461216 100644 --- a/core/src/smoke_gcode_parser.cpp +++ b/core/src/smoke_gcode_parser.cpp @@ -230,6 +230,15 @@ bool stop_if_callback_aborted(CanonEventSink &sink, std::string *error) { return true; } +void emit_comment_state(CanonEventSink &sink, int line, int reserved) { + CncSimEvent event{}; + event.version = 1; + event.type = CNC_SIM_EVENT_COMMENT; + event.line = line; + event.reserved = reserved; + sink.emit_raw(event); +} + } // namespace int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string *error) { @@ -366,6 +375,25 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string if (stop_if_callback_aborted(sink_, error)) { return -1; } + } else if (m == 7) { + emit_comment_state(sink_, line_number, 11); + if (stop_if_callback_aborted(sink_, error)) { + return -1; + } + } else if (m == 8) { + emit_comment_state(sink_, line_number, 21); + if (stop_if_callback_aborted(sink_, error)) { + return -1; + } + } else if (m == 9) { + emit_comment_state(sink_, line_number, 10); + if (stop_if_callback_aborted(sink_, error)) { + return -1; + } + emit_comment_state(sink_, line_number, 20); + if (stop_if_callback_aborted(sink_, error)) { + return -1; + } } else if (m == 428) { sink_.switch_kinematics(1, true, line_number); if (stop_if_callback_aborted(sink_, error)) { diff --git a/core/tests/cnc_sim_api_linuxcnc_rs274_smoke.cpp b/core/tests/cnc_sim_api_linuxcnc_rs274_smoke.cpp index 012b65d..42b2143 100644 --- a/core/tests/cnc_sim_api_linuxcnc_rs274_smoke.cpp +++ b/core/tests/cnc_sim_api_linuxcnc_rs274_smoke.cpp @@ -82,6 +82,10 @@ int main() { bool saw_optional_stop = false; bool saw_pallet_stop = false; bool saw_final_end = false; + bool saw_mist_on = false; + bool saw_flood_on = false; + bool saw_mist_off = false; + bool saw_flood_off = 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; @@ -207,6 +211,38 @@ int main() { ok &= expect(saw_pallet_stop, "expected LinuxCNC M60 pallet shuttle event"); ok &= expect(saw_final_end, "expected LinuxCNC M30 final program end event"); + const char coolant_program[] = + "G21 G90 G17\n" + "M7\n" + "M8\n" + "M9\n" + "M30\n"; + events.clear(); + ok &= expect(cnc_sim_parse_program(sim, coolant_program, sizeof(coolant_program) - 1) == 0, + cnc_sim_last_error(sim)); + for (const auto &event : events) { + saw_mist_on = saw_mist_on || + (event.type == CNC_SIM_EVENT_COMMENT && + event.line == 2 && + event.reserved == 11); + saw_flood_on = saw_flood_on || + (event.type == CNC_SIM_EVENT_COMMENT && + event.line == 3 && + event.reserved == 21); + saw_mist_off = saw_mist_off || + (event.type == CNC_SIM_EVENT_COMMENT && + event.line == 4 && + event.reserved == 10); + saw_flood_off = saw_flood_off || + (event.type == CNC_SIM_EVENT_COMMENT && + event.line == 4 && + event.reserved == 20); + } + ok &= expect(saw_mist_on, "expected LinuxCNC M7 mist on state"); + ok &= expect(saw_flood_on, "expected LinuxCNC M8 flood on state"); + ok &= expect(saw_mist_off, "expected LinuxCNC M9 mist off state"); + ok &= expect(saw_flood_off, "expected LinuxCNC M9 flood off state"); + const char comment_program[] = "G21 G90 G17\n" "(operator note)\n" diff --git a/core/tests/cnc_sim_api_smoke.cpp b/core/tests/cnc_sim_api_smoke.cpp index f11ce2c..ba0a800 100644 --- a/core/tests/cnc_sim_api_smoke.cpp +++ b/core/tests/cnc_sim_api_smoke.cpp @@ -73,6 +73,10 @@ int main() { bool saw_program_stop = false; bool saw_optional_stop = false; bool saw_pallet_stop = false; + bool saw_mist_on = false; + bool saw_flood_on = false; + bool saw_mist_off = false; + bool saw_flood_off = false; bool saw_incremental_move = false; bool saw_r_arc_center = false; for (const auto &event : events) { @@ -191,6 +195,37 @@ int main() { ok &= expect(saw_optional_stop, "expected M1 optional stop event"); ok &= expect(saw_pallet_stop, "expected M60 pallet shuttle event"); + const char coolant_program[] = + "G21\n" + "M7\n" + "M8\n" + "M9\n"; + events.clear(); + ok &= expect(cnc_sim_parse_program(sim, coolant_program, sizeof(coolant_program) - 1) == 0, + cnc_sim_last_error(sim)); + for (const auto &event : events) { + saw_mist_on = saw_mist_on || + (event.type == CNC_SIM_EVENT_COMMENT && + event.line == 2 && + event.reserved == 11); + saw_flood_on = saw_flood_on || + (event.type == CNC_SIM_EVENT_COMMENT && + event.line == 3 && + event.reserved == 21); + saw_mist_off = saw_mist_off || + (event.type == CNC_SIM_EVENT_COMMENT && + event.line == 4 && + event.reserved == 10); + saw_flood_off = saw_flood_off || + (event.type == CNC_SIM_EVENT_COMMENT && + event.line == 4 && + event.reserved == 20); + } + ok &= expect(saw_mist_on, "expected M7 mist on state"); + ok &= expect(saw_flood_on, "expected M8 flood on state"); + ok &= expect(saw_mist_off, "expected M9 mist off state"); + ok &= expect(saw_flood_off, "expected M9 flood off state"); + const char linuxcnc_config[] = "{\"backend\":\"linuxcnc-rs274\"}"; config_rc = cnc_sim_load_config_json(sim, linuxcnc_config, sizeof(linuxcnc_config) - 1); rc = cnc_sim_parse_program(sim, program, sizeof(program) - 1); diff --git a/core/tests/linuxcnc_canon_bridge_smoke.cpp b/core/tests/linuxcnc_canon_bridge_smoke.cpp index f64db78..6aef0ee 100644 --- a/core/tests/linuxcnc_canon_bridge_smoke.cpp +++ b/core/tests/linuxcnc_canon_bridge_smoke.cpp @@ -51,6 +51,13 @@ int main() { SET_XY_ROTATION(15.0); cnc_sim_linuxcnc_set_current_line(20); COMMENT("bridge comment"); + cnc_sim_linuxcnc_set_current_line(24); + MIST_ON(); + cnc_sim_linuxcnc_set_current_line(25); + FLOOD_ON(); + cnc_sim_linuxcnc_set_current_line(26); + MIST_OFF(); + FLOOD_OFF(); cnc_sim_linuxcnc_set_current_line(21); PROGRAM_STOP(); cnc_sim_linuxcnc_set_current_line(22); @@ -77,6 +84,10 @@ int main() { bool saw_g92 = false; bool saw_rotation = false; bool saw_comment = false; + bool saw_mist_on = false; + bool saw_flood_on = false; + bool saw_mist_off = false; + bool saw_flood_off = false; bool saw_program_stop = false; bool saw_optional_stop = false; bool saw_pallet_stop = false; @@ -122,6 +133,18 @@ int main() { event.feed == 15.0); saw_comment = saw_comment || (event.type == CNC_SIM_EVENT_COMMENT && event.line == 20); + saw_mist_on = saw_mist_on || (event.type == CNC_SIM_EVENT_COMMENT && + event.line == 24 && + event.reserved == 11); + saw_flood_on = saw_flood_on || (event.type == CNC_SIM_EVENT_COMMENT && + event.line == 25 && + event.reserved == 21); + saw_mist_off = saw_mist_off || (event.type == CNC_SIM_EVENT_COMMENT && + event.line == 26 && + event.reserved == 10); + saw_flood_off = saw_flood_off || (event.type == CNC_SIM_EVENT_COMMENT && + event.line == 26 && + event.reserved == 20); saw_program_stop = saw_program_stop || (event.type == CNC_SIM_EVENT_PROGRAM_END && event.line == 21 && event.reserved == 1); @@ -147,6 +170,10 @@ int main() { ok &= expect(saw_g92, "expected G92 offset event"); ok &= expect(saw_rotation, "expected XY rotation event"); ok &= expect(saw_comment, "expected comment event line"); + ok &= expect(saw_mist_on, "expected mist on state"); + ok &= expect(saw_flood_on, "expected flood on state"); + ok &= expect(saw_mist_off, "expected mist off state"); + ok &= expect(saw_flood_off, "expected flood off state"); ok &= expect(saw_program_stop, "expected program stop event"); ok &= expect(saw_optional_stop, "expected optional stop event"); ok &= expect(saw_pallet_stop, "expected pallet shuttle stop event"); diff --git a/test-linuxcnc-rs274-native.sh b/test-linuxcnc-rs274-native.sh index 7596209..87eb10e 100755 --- a/test-linuxcnc-rs274-native.sh +++ b/test-linuxcnc-rs274-native.sh @@ -40,6 +40,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_spindle_direction.ngc >/tmp/cnc_sim_linuxcnc_spindle_direction.json CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_tool_number.ngc >/tmp/cnc_sim_linuxcnc_tool_number.json CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_program_stops.ngc >/tmp/cnc_sim_linuxcnc_program_stops.json +CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_coolant.ngc >/tmp/cnc_sim_linuxcnc_coolant.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_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 @@ -106,6 +107,16 @@ for expected in [(2, 1), (3, 2), (4, 3), (4, 1), (5, 0)]: if expected not in stop_states: raise SystemExit(f"missing program stop state {expected!r}") +coolant = json.loads(Path("/tmp/cnc_sim_linuxcnc_coolant.json").read_text()) +coolant_states = [ + (event["line"], event["reserved"]) + for event in coolant + if event["type"] == "comment" +] +for expected in [(2, 11), (3, 21), (4, 10), (4, 20)]: + if expected not in coolant_states: + raise SystemExit(f"missing coolant state {expected!r}") + cycle = json.loads(Path("/tmp/cnc_sim_linuxcnc_canned_cycle.json").read_text()) motions = [event for event in cycle if event["type"] in {"rapid", "linear-feed"}] expected_cycle = [ @@ -184,6 +195,7 @@ echo "dumped /tmp/cnc_sim_linuxcnc_rtcp_controls.json" echo "dumped /tmp/cnc_sim_linuxcnc_spindle_direction.json" echo "dumped /tmp/cnc_sim_linuxcnc_tool_number.json" echo "dumped /tmp/cnc_sim_linuxcnc_program_stops.json" +echo "dumped /tmp/cnc_sim_linuxcnc_coolant.json" echo "dumped /tmp/cnc_sim_linuxcnc_canned_cycle.json" echo "dumped /tmp/cnc_sim_linuxcnc_comments.json" echo "dumped /tmp/cnc_sim_linuxcnc_probe_no_error.json" diff --git a/tests/gcode/linuxcnc_coolant.ngc b/tests/gcode/linuxcnc_coolant.ngc new file mode 100644 index 0000000..a1a6156 --- /dev/null +++ b/tests/gcode/linuxcnc_coolant.ngc @@ -0,0 +1,5 @@ +G21 G90 G17 +M7 +M8 +M9 +M30