From 2efee60a22b691f275fbe588e70a7a710d286211 Mon Sep 17 00:00:00 2001 From: Asim Siddiqui Date: Sun, 23 Mar 2025 02:38:29 +1100 Subject: [PATCH] Added TCP Queue Delay + Fixed Flashforge Serial comms (#8905) * Added TCP Queue Delay + Updated Flashforge Serial comms - Added TCP Queue Delay parameter to delay TCP messages in queue - Updated Flashforge Serial comms - Upload/Print to older Flashforge devices (AD3/AD4 etc) now working alongside FF Klipper devices * Set buffer to 4096 & add 3 sec delay for file save command * include thread in TCPConsole --- src/slic3r/Utils/Flashforge.cpp | 138 +++++++++++++++++++++++--------- src/slic3r/Utils/Flashforge.hpp | 18 +++-- src/slic3r/Utils/TCPConsole.cpp | 9 ++- src/slic3r/Utils/TCPConsole.hpp | 6 ++ 4 files changed, 126 insertions(+), 45 deletions(-) diff --git a/src/slic3r/Utils/Flashforge.cpp b/src/slic3r/Utils/Flashforge.cpp index 6161623e15..ed40fec5a1 100644 --- a/src/slic3r/Utils/Flashforge.cpp +++ b/src/slic3r/Utils/Flashforge.cpp @@ -33,54 +33,100 @@ namespace pt = boost::property_tree; namespace Slic3r { -Flashforge::Flashforge(DynamicPrintConfig* config) : m_host(config->opt_string("print_host")), m_console_port("8899") -{ -} +Flashforge::Flashforge(DynamicPrintConfig* config) + : m_host(config->opt_string("print_host")) + , m_console_port("8899") + , m_gcFlavor(config->option>("gcode_flavor")->value) + , m_bufferSize(4096) // 4K buffer size +{} const char* Flashforge::get_name() const { return "Flashforge"; } bool Flashforge::test(wxString& msg) const { - BOOST_LOG_TRIVIAL(debug) << boost::format("[Flashforge] testing connection"); + BOOST_LOG_TRIVIAL(debug) << boost::format("[Flashforge Serial] testing connection"); // Utils::TCPConsole console(m_host, m_console_port); Utils::TCPConsole client(m_host, m_console_port); client.enqueue_cmd(controlCommand); bool res = client.run_queue(); if (!res) { msg = wxString::FromUTF8(client.error_message().c_str()); - BOOST_LOG_TRIVIAL(info) << boost::format("[Flashforge] testing connection failed"); + BOOST_LOG_TRIVIAL(info) << boost::format("[Flashforge Serial] testing connection failed"); } else { - BOOST_LOG_TRIVIAL(info) << boost::format("[Flashforge] testing connection success"); + BOOST_LOG_TRIVIAL(info) << boost::format("[Flashforge Serial] testing connection success"); } return res; } -wxString Flashforge::get_test_ok_msg() const { return _(L("Connection to Flashforge works correctly.")); } +wxString Flashforge::get_test_ok_msg() const { return _(L("Serial connection to Flashforge works correctly.")); } wxString Flashforge::get_test_failed_msg(wxString& msg) const { - return GUI::from_u8((boost::format("%s: %s") % _utf8(L("Could not connect to Flashforge")) % std::string(msg.ToUTF8())).str()); + return GUI::from_u8((boost::format("%s: %s") % _utf8(L("Could not connect to Flashforge via serial")) % std::string(msg.ToUTF8())).str()); +} + + +bool Flashforge::connect(wxString& msg) const +{ + + Utils::TCPConsole client(m_host, m_console_port); + + client.enqueue_cmd(controlCommand); + client.enqueue_cmd(deviceInfoCommand); + + if (m_gcFlavor == gcfKlipper) + client.enqueue_cmd(connectKlipperCommand); + else { + client.enqueue_cmd(connectLegacyCommand); + + } + + client.enqueue_cmd(statusCommand); + + + bool res = client.run_queue(); + + if (!res) { + msg = wxString::FromUTF8(client.error_message().c_str()); + BOOST_LOG_TRIVIAL(info) << boost::format("[Flashforge Serial] Failed to initiate connection"); + } else + BOOST_LOG_TRIVIAL(info) << boost::format("[Flashforge Serial] Successfully initiated Connection"); + + return res; +} + +bool Flashforge::start_print(wxString& msg, const std::string& filename) const +{ + Utils::TCPConsole client(m_host, m_console_port); + Slic3r::Utils::SerialMessage startPrintCommand = {(boost::format("~M23 0:/user/%1%") % filename).str(), Slic3r::Utils::Command}; + client.enqueue_cmd(startPrintCommand); + bool res = client.run_queue(); + + if (!res) { + msg = wxString::FromUTF8(client.error_message().c_str()); + BOOST_LOG_TRIVIAL(info) << boost::format("[Flashforge Serial] Failed to start print %1%") % filename; + } else + BOOST_LOG_TRIVIAL(info) << boost::format("[Flashforge Serial] Started print %1%") % filename; + + return res; } bool Flashforge::upload(PrintHostUpload upload_data, ProgressFn progress_fn, ErrorFn error_fn, InfoFn info_fn) const { bool res = true; + wxString errormsg; Utils::TCPConsole client(m_host, m_console_port); - //sometimes FF AD5M is very slow in data upload, so timeout is increased to 10 minutes - client.set_write_timeout(std::chrono::minutes(10)); - client.set_read_timeout(std::chrono::minutes(10)); - client.enqueue_cmd(controlCommand); - - client.enqueue_cmd(connect5MCommand); - - client.enqueue_cmd(statusCommand); - wxString errormsg; + try { + + res = connect(errormsg); + std::ifstream newfile; newfile.open(upload_data.source_path.c_str(), std::ios::binary); // open a file to perform read operation using file object + std::string gcodeFile; if (newfile.is_open()) { // checking whether the file is open - BOOST_LOG_TRIVIAL(info) << boost::format("[Flashforge] Reading file..."); + BOOST_LOG_TRIVIAL(info) << boost::format("[Flashforge Serial] Reading file..."); newfile.seekg(0, std::ios::end); std::ifstream::pos_type pos = newfile.tellg(); @@ -88,37 +134,55 @@ bool Flashforge::upload(PrintHostUpload upload_data, ProgressFn progress_fn, Err newfile.seekg(0, std::ios::beg); newfile.read(&result[0], pos); - BOOST_LOG_TRIVIAL(info) << boost::format("[Flashforge] Reading file...done size is %1%") % result.size(); - Slic3r::Utils::SerialMessage fileuploadCommand = - {(boost::format("~M28 %1% 0:/user/%2%") % result.size() % upload_data.upload_path.generic_string()).str(), - Slic3r::Utils::Command}; - client.enqueue_cmd(fileuploadCommand); - Slic3r::Utils::SerialMessage dataCommand = {std::string(result.begin(), result.end()), Slic3r::Utils::Data}; - client.enqueue_cmd(dataCommand); + gcodeFile = std::string(result.begin(), result.end()); // TODO: Find more efficient way of breaking ifstream + + BOOST_LOG_TRIVIAL(info) << boost::format("[Flashforge Serial] Reading file...done size is %1%") % gcodeFile.size(); + newfile.close(); // close the file object. - BOOST_LOG_TRIVIAL(info) << boost::format("[Flashforge] Sent %1% ") % result.size(); } - BOOST_LOG_TRIVIAL(info) << boost::format("[Flashforge] Sending file save command "); - client.enqueue_cmd(saveFileCommand); - if (upload_data.post_action == PrintHostPostUploadAction::StartPrint) { - BOOST_LOG_TRIVIAL(info) << boost::format("[Flashforge] Starting print %1%") % upload_data.upload_path.string(); - Slic3r::Utils::SerialMessage startPrintCommand = {(boost::format("~M23 0:/user/%1%") % upload_data.upload_path.string()).str(), - Slic3r::Utils::Command}; - client.enqueue_cmd(startPrintCommand); + Slic3r::Utils::SerialMessage fileuploadCommand = + {(boost::format("~M28 %1% 0:/user/%2%") % gcodeFile.size() % upload_data.upload_path.generic_string()).str(), + Slic3r::Utils::Command}; + client.enqueue_cmd(fileuploadCommand); + + //client.set_tcp_queue_delay(std::chrono::nanoseconds(10000)); + + for (int bytePos = 0; bytePos < gcodeFile.size(); bytePos += m_bufferSize) { // TODO: Find more efficient way of breaking ifstream + + int bytePosEnd = (gcodeFile.size() - bytePos > m_bufferSize - 1) ? m_bufferSize : gcodeFile.size(); + Slic3r::Utils::SerialMessage dataCommand = {gcodeFile.substr(bytePos, bytePosEnd), Slic3r::Utils::Data}; // Break into smaller byte chunks + + client.enqueue_cmd(dataCommand); + } res = client.run_queue(); + if (res) + BOOST_LOG_TRIVIAL(info) << boost::format("[Flashforge Serial] Sent %1% ") % gcodeFile.size(); + + if (!res) { - BOOST_LOG_TRIVIAL(info) << boost::format("[Flashforge] error %1%") % client.error_message().c_str(); + BOOST_LOG_TRIVIAL(info) << boost::format("[Flashforge Serial] error %1%") % client.error_message().c_str(); errormsg = wxString::FromUTF8(client.error_message().c_str()); - } - if (!res) { error_fn(std::move(errormsg)); + } else { + + client.set_tcp_queue_delay(std::chrono::milliseconds(3000)); + + BOOST_LOG_TRIVIAL(info) << boost::format("[Flashforge Serial] Sending file save command "); + + client.enqueue_cmd(saveFileCommand); + + res = client.run_queue(); + + if (upload_data.post_action == PrintHostPostUploadAction::StartPrint) + res = start_print(errormsg, upload_data.upload_path.string()); } + } catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(info) << boost::format("[Flashforge] error %1%") % e.what(); + BOOST_LOG_TRIVIAL(info) << boost::format("[Flashforge Serial] error %1%") % e.what(); errormsg = wxString::FromUTF8(e.what()); error_fn(std::move(errormsg)); } diff --git a/src/slic3r/Utils/Flashforge.hpp b/src/slic3r/Utils/Flashforge.hpp index 18776ea1e7..82f9092753 100644 --- a/src/slic3r/Utils/Flashforge.hpp +++ b/src/slic3r/Utils/Flashforge.hpp @@ -32,12 +32,20 @@ public: private: std::string m_host; std::string m_console_port; - Slic3r::Utils::SerialMessage controlCommand = {"~M601 S1\r\n",Slic3r::Utils::Command}; - Slic3r::Utils::SerialMessage connect5MCommand = {"~M640\r\n",Slic3r::Utils::Command}; - Slic3r::Utils::SerialMessage connectGuiderCommand = {"~M650\r\n",Slic3r::Utils::Command}; - Slic3r::Utils::SerialMessage statusCommand = {"~M119\r\n",Slic3r::Utils::Command}; - Slic3r::Utils::SerialMessage saveFileCommand = {"~M29\r\n",Slic3r::Utils::Command}; + const int m_bufferSize; + GCodeFlavor m_gcFlavor; + Slic3r::Utils::SerialMessage controlCommand = {"~M601 S1\r\n",Slic3r::Utils::Command}; + Slic3r::Utils::SerialMessage connectKlipperCommand = {"~M640\r\n",Slic3r::Utils::Command}; + Slic3r::Utils::SerialMessage connectLegacyCommand = {"~M650\r\n",Slic3r::Utils::Command}; + Slic3r::Utils::SerialMessage nozzlePosCommand = {"~M114\r\n", Slic3r::Utils::Command}; + Slic3r::Utils::SerialMessage deviceInfoCommand = {"~M115\r\n", Slic3r::Utils::Command}; + Slic3r::Utils::SerialMessage statusCommand = {"~M119\r\n",Slic3r::Utils::Command}; + Slic3r::Utils::SerialMessage tempStatusCommand = {"~M105\r\n", Slic3r::Utils::Command}; + Slic3r::Utils::SerialMessage printStatusCommand = {"~M27\r\n", Slic3r::Utils::Command}; + Slic3r::Utils::SerialMessage saveFileCommand = {"~M29\r\n",Slic3r::Utils::Command}; int get_err_code_from_body(const std::string &body) const; + bool connect(wxString& msg) const; + bool start_print(wxString& msg, const std::string& filename) const; }; } // namespace Slic3r diff --git a/src/slic3r/Utils/TCPConsole.cpp b/src/slic3r/Utils/TCPConsole.cpp index d76d29fe5c..25802e3908 100644 --- a/src/slic3r/Utils/TCPConsole.cpp +++ b/src/slic3r/Utils/TCPConsole.cpp @@ -11,6 +11,7 @@ #include #include +#include #include "TCPConsole.hpp" #include "SerialMessage.hpp" @@ -29,6 +30,8 @@ void TCPConsole::transmit_next_command() return; } + std::this_thread::sleep_for(m_tcp_queue_delay); + SerialMessage cmd = m_cmd_queue.front(); m_cmd_queue.pop_front(); @@ -117,10 +120,10 @@ void TCPConsole::handle_write( m_io_context.stop(); } else { - if(messageType == Command){ - wait_next_line(); + if(messageType == Command) { + wait_next_line(); } - else{ + else { transmit_next_command(); } } diff --git a/src/slic3r/Utils/TCPConsole.hpp b/src/slic3r/Utils/TCPConsole.hpp index ac11f60de5..e94e130a9e 100644 --- a/src/slic3r/Utils/TCPConsole.hpp +++ b/src/slic3r/Utils/TCPConsole.hpp @@ -31,6 +31,7 @@ public: m_connect_timeout = std::chrono::milliseconds(5000); m_write_timeout = std::chrono::milliseconds(10000); m_read_timeout = std::chrono::milliseconds(10000); + m_tcp_queue_delay = std::chrono::milliseconds(0); } void set_write_timeout(std::chrono::steady_clock::duration timeout) { @@ -41,6 +42,10 @@ public: m_read_timeout = timeout; } + void set_tcp_queue_delay(std::chrono::steady_clock::duration delay) { + m_tcp_queue_delay = delay; + } + void set_line_delimiter(const std::string& newline) { m_newline = newline; } @@ -82,6 +87,7 @@ private: std::chrono::steady_clock::duration m_connect_timeout; std::chrono::steady_clock::duration m_write_timeout; std::chrono::steady_clock::duration m_read_timeout; + std::chrono::steady_clock::duration m_tcp_queue_delay; std::deque m_cmd_queue;