From 597a4e97e4d0a8ef4f1da1979b02ed5c9d3516e2 Mon Sep 17 00:00:00 2001 From: bdring Date: Sat, 18 Jan 2025 22:43:46 -0600 Subject: [PATCH] Better m6 macros (#1425) * WIP - Still borken * WIP Testing new ideas. * Update GCode.cpp * Fix alarm race and unhomed glitch * M2 and M30 return from macros * Handle % file delimiters * Update GCode.cpp --------- Co-authored-by: Mitch Bradley --- FluidNC/src/Channel.h | 4 +++- FluidNC/src/GCode.cpp | 27 +++++++++++++++++---------- FluidNC/src/InputFile.cpp | 14 ++++++++++++++ FluidNC/src/InputFile.h | 2 ++ FluidNC/src/Machine/Homing.cpp | 6 ++++-- FluidNC/src/Machine/Macros.cpp | 27 +++++++++++++++++++++++++-- FluidNC/src/Machine/Macros.h | 2 ++ FluidNC/src/Protocol.cpp | 18 +++++++++++------- FluidNC/src/Spindles/Spindle.cpp | 12 +++++------- 9 files changed, 83 insertions(+), 29 deletions(-) diff --git a/FluidNC/src/Channel.h b/FluidNC/src/Channel.h index aa89f440a..1be84ae39 100644 --- a/FluidNC/src/Channel.h +++ b/FluidNC/src/Channel.h @@ -81,7 +81,8 @@ class Channel : public Stream { UTF8 _utf8; - bool _ended = false; + bool _ended = false; + bool _percent = false; protected: bool _active = true; @@ -180,6 +181,7 @@ class Channel : public Stream { void push(const std::string& s) { push(reinterpret_cast(s.c_str()), s.length()); } void end() { _ended = true; } + void percent() { _percent = true; } // Pin extender functions virtual void out(const char* s, const char* tag); diff --git a/FluidNC/src/GCode.cpp b/FluidNC/src/GCode.cpp index 0c1436ea8..d67ca1e45 100644 --- a/FluidNC/src/GCode.cpp +++ b/FluidNC/src/GCode.cpp @@ -192,12 +192,13 @@ void collapseGCode(char* line) { *outPtr = '\0'; return; case '%': - // TODO: Install '%' feature - // Program start-end percent sign NOT SUPPORTED. - // NOTE: This may be installed to distinguish between program running vs manual input, - // where, during a program, the system auto-cycle start will continue to execute - // everything until the next '%' sign. This will help fix resuming issues with certain - // functions that empty the planner buffer to execute its task on-time. + // Per https://linuxcnc.org/docs/html/gcode/overview.html#gcode:file-requirements + // % only applies to "job" channels like files and macros, not to serial channels + // where the sequence of lines is potentially never-ending. A sender that handles + // files on the host system could apply the % semantics. + if (Job::active()) { + Job::channel()->percent(); + } break; case '\r': // In case one sneaks in @@ -1613,6 +1614,7 @@ Error gc_execute_line(char* line) { bool stopped_spindle = false; // was spindle stopped via the change bool new_spindle = false; // was the spindle changed protocol_buffer_synchronize(); // wait for motion in buffer to finish + Spindles::Spindle::switchSpindle( gc_state.selected_tool, Spindles::SpindleFactory::objects(), spindle, stopped_spindle, new_spindle); if (stopped_spindle) { @@ -1621,13 +1623,19 @@ Error gc_execute_line(char* line) { if (new_spindle) { gc_state.spindle_speed = 0.0; } + log_info("Sel:" << gc_state.selected_tool << " Cur:" << gc_state.current_tool); spindle->tool_change(gc_state.selected_tool, false, false); - gc_state.current_tool = gc_state.selected_tool; - report_ovr_counter = 0; // Set to report change immediately + if (spindle->_atc_name == "" && spindle->_m6_macro.get().empty()) { // if neither of these exist we need to set the value here + gc_state.current_tool = gc_state.selected_tool; + } + report_ovr_counter = 0; // Set to report change immediately gc_ovr_changed(); } } - if (gc_block.modal.set_tool_number == SetToolNumber::Enable) { + if (gc_block.modal.set_tool_number == SetToolNumber::Enable) { // M61 + if (gc_block.values.q < 0) { + FAIL(Error::NegativeValue); // https://linuxcnc.org/docs/2.8/html/gcode/m-code.html#mcode:m61 + } gc_state.selected_tool = gc_block.values.q; bool stopped_spindle = false; // was spindle stopped via the change bool new_spindle = false; // was the spindle changed @@ -1894,7 +1902,6 @@ Error gc_execute_line(char* line) { if (Job::active()) { Job::channel()->end(); - break; } // Upon program complete, only a subset of g-codes reset to certain defaults, according to // LinuxCNC's program end descriptions and testing. Only modal groups [G-code 1,2,3,5,7,12] diff --git a/FluidNC/src/InputFile.cpp b/FluidNC/src/InputFile.cpp index 4cbb49ef5..3b9f7f138 100644 --- a/FluidNC/src/InputFile.cpp +++ b/FluidNC/src/InputFile.cpp @@ -24,6 +24,9 @@ Error InputFile::readLine(char* line, int maxlen) { } if (c == '\n') { ++_line_number; + if (len == 0) { + ++_blank_lines; + } break; } line[len++] = c; @@ -62,6 +65,17 @@ Error InputFile::pollLine(char* line) { if (_pending_error != Error::Ok) { return _pending_error; } + if (_percent) { + _percent = false; + // If the first non-blank line in the file is a % line, it denotes start-of-file. + // Otherwise a % line causes the rest of the file to be skipped, per + // https://linuxcnc.org/docs/html/gcode/overview.html#gcode:file-requirements + // The line with % is not blank, so if it is the first non-blank line + // _line_number will be one more than _blank_lines + if (_line_number != (_blank_lines + 1)) { + _ended = true; + } + } if (_ended) { end_message(); return Error::Eof; diff --git a/FluidNC/src/InputFile.h b/FluidNC/src/InputFile.h index c54409816..0e9895c36 100644 --- a/FluidNC/src/InputFile.h +++ b/FluidNC/src/InputFile.h @@ -24,6 +24,8 @@ class InputFile : public FileStream { Error _pending_error = Error::Ok; void end_message(); + size_t _blank_lines = 0; + public: // fsname is the default file system on which the file is located, in case the path does not specify // path is the full path to the file diff --git a/FluidNC/src/Machine/Homing.cpp b/FluidNC/src/Machine/Homing.cpp index 2ce3668f0..ae4e9694e 100644 --- a/FluidNC/src/Machine/Homing.cpp +++ b/FluidNC/src/Machine/Homing.cpp @@ -43,7 +43,7 @@ namespace Machine { uint32_t Homing::_runs; - AxisMask Homing::_unhomed_axes; // Bitmap of axes whose position is unknown + AxisMask Homing::_unhomed_axes = 0; // Bitmap of axes whose position is unknown bool Homing::axis_is_homed(size_t axis) { return bitnum_is_false(_unhomed_axes, axis); @@ -55,7 +55,9 @@ namespace Machine { set_bitnum(_unhomed_axes, axis); } void Homing::set_all_axes_unhomed() { - _unhomed_axes = Machine::Axes::homingMask; + if (config->_start->_mustHome) { + _unhomed_axes = Machine::Axes::homingMask; + } } void Homing::set_all_axes_homed() { _unhomed_axes = 0; diff --git a/FluidNC/src/Machine/Macros.cpp b/FluidNC/src/Machine/Macros.cpp index 70af07142..f127142b5 100644 --- a/FluidNC/src/Machine/Macros.cpp +++ b/FluidNC/src/Machine/Macros.cpp @@ -100,6 +100,10 @@ Error MacroChannel::readLine(char* line, int maxlen) { } line[len] = '\0'; ++_line_number; + if (len == 0) { + ++_blank_lines; + } + return len ? Error::Ok : Error::Eof; } @@ -117,6 +121,11 @@ void MacroChannel::ack(Error status) { MacroChannel::MacroChannel(Macro* macro) : Channel(macro->name(), false), _macro(macro) {} +void MacroChannel::end_message() { + _progress += name(); + _progress += ": Sent"; +} + Error MacroChannel::pollLine(char* line) { // Macros only execute as proper jobs so we should not be polling one with a null line if (!line) { @@ -125,6 +134,21 @@ Error MacroChannel::pollLine(char* line) { if (_pending_error != Error::Ok) { return _pending_error; } + if (_percent) { + _percent = false; + // If the first non-blank line in the macro is a % line, it denotes start-of-file. + // Otherwise a % line causes the rest of the macro to be skipped, per + // https://linuxcnc.org/docs/html/gcode/overview.html#gcode:file-requirements + // The line with % is not blank, so if it is the first non-blank line + // _line_number will be one more than _blank_lines + if (_line_number != _blank_lines + 1) { + _ended = true; + } + } + if (_ended) { + end_message(); + return Error::Eof; + } switch (auto err = readLine(line, Channel::maxLine)) { case Error::Ok: { log_debug("Macro line: " << line); @@ -136,8 +160,7 @@ Error MacroChannel::pollLine(char* line) { } return Error::Ok; case Error::Eof: - _progress = name(); - _progress += ": Sent"; + end_message(); return Error::Eof; default: log_error("Macro readLine failed"); diff --git a/FluidNC/src/Machine/Macros.h b/FluidNC/src/Machine/Macros.h index 4925d44df..b044f6908 100644 --- a/FluidNC/src/Machine/Macros.h +++ b/FluidNC/src/Machine/Macros.h @@ -58,10 +58,12 @@ namespace Machine { private: Error _pending_error = Error::Ok; size_t _position = 0; + size_t _blank_lines = 0; Macro* _macro; Error readLine(char* line, int maxlen); + void end_message(); public: Error pollLine(char* line) override; diff --git a/FluidNC/src/Protocol.cpp b/FluidNC/src/Protocol.cpp index 98548f5d5..d67403c28 100644 --- a/FluidNC/src/Protocol.cpp +++ b/FluidNC/src/Protocol.cpp @@ -153,7 +153,7 @@ void polling_loop(void* unused) { // channels to see if one has a line ready. activeChannel = pollChannels(activeLine); } else { - if (state_is(State::Alarm) || state_is(State::ConfigAlarm)) { + if (state_is(State::Alarm) || state_is(State::ConfigAlarm) || state_is(State::Critical)) { log_debug("Unwinding from Alarm"); Job::abort(); unwind_cause = nullptr; @@ -449,9 +449,8 @@ static void protocol_do_start() { send_alarm(ExecAlarm::Init); return; } - Homing::set_all_axes_homed(); - if (config->_start->_mustHome && Machine::Axes::homingMask) { - Homing::set_all_axes_unhomed(); + Homing::set_all_axes_unhomed(); + if (Homing::unhomed_axes()) { // If there is an axis with homing configured, enter Alarm state on startup send_alarm(ExecAlarm::Unhomed); } else { @@ -465,20 +464,25 @@ static void protocol_do_alarm(void* alarmVoid) { if (spindle->_off_on_alarm) { spindle->stop(); } - alarm_msg(lastAlarm); + // It is important to do set_state() before alarm_msg() because the + // latter can cause a task switch that can introduce a race condition + // whereby polling_loop() does not see the state change. if (lastAlarm == ExecAlarm::HardLimit || lastAlarm == ExecAlarm::HardStop) { - set_state(State::Critical); // Set system alarm state - report_error_message(Message::CriticalEvent); protocol_disable_steppers(); Homing::set_all_axes_unhomed(); + set_state(State::Critical); // Set system alarm state + alarm_msg(lastAlarm); + report_error_message(Message::CriticalEvent); return; } if (lastAlarm == ExecAlarm::SoftLimit) { set_state(State::Critical); // Set system alarm state + alarm_msg(lastAlarm); report_error_message(Message::CriticalEvent); return; } set_state(State::Alarm); + alarm_msg(lastAlarm); } static void protocol_start_holding() { diff --git a/FluidNC/src/Spindles/Spindle.cpp b/FluidNC/src/Spindles/Spindle.cpp index 99cd877a7..1f5aa2018 100644 --- a/FluidNC/src/Spindles/Spindle.cpp +++ b/FluidNC/src/Spindles/Spindle.cpp @@ -42,9 +42,10 @@ namespace Spindles { spindle->stop(); // stop the current spindle stop_spindle = true; // used to stop the next spindle } - if (candidate != spindle) { - spindle = candidate; - new_spindle = true; + if (candidate != spindle) { // we are changing spindles + gc_state.selected_tool = new_tool; + spindle = candidate; + new_spindle = true; log_info("Changed to spindle:" << spindle->name()); } } else { @@ -134,7 +135,6 @@ namespace Spindles { return _atc->tool_change(tool_number, pre_select, set_tool); } if (!_m6_macro.get().empty()) { - log_info(_name << " spindle changed to tool:" << tool_number << " using m6_macro"); if (pre_select) { return true; } @@ -142,14 +142,12 @@ namespace Spindles { if (set_tool) { return true; } - - //if (tool_number != _last_tool) { - log_info(_name << " spindle run macro: " << _m6_macro.get()); _m6_macro.run(nullptr); _last_tool = tool_number; return true; //} } + return true; }