diff --git a/src/slic3r/Utils/MKS.cpp b/src/slic3r/Utils/MKS.cpp index 636a0b3b2c..2188a8f682 100644 --- a/src/slic3r/Utils/MKS.cpp +++ b/src/slic3r/Utils/MKS.cpp @@ -31,122 +31,120 @@ namespace pt = boost::property_tree; namespace Slic3r { - MKS::MKS(DynamicPrintConfig* config) : - host(config->opt_string("print_host")), console_port("8080") - {} +MKS::MKS(DynamicPrintConfig* config) : + m_host(config->opt_string("print_host")), m_console_port("8080") +{} - const char* MKS::get_name() const { return "MKS"; } +const char* MKS::get_name() const { return "MKS"; } - bool MKS::test(wxString& msg) const - { - Utils::TCPConsole console(host, console_port); +bool MKS::test(wxString& msg) const +{ + Utils::TCPConsole console(m_host, m_console_port); - console.enqueue_cmd("M105"); - bool ret = console.run_queue(); + console.enqueue_cmd("M105"); + bool ret = console.run_queue(); - if (!ret) { - msg = wxString::FromUTF8(console.error_message().c_str()); + if (!ret) + msg = wxString::FromUTF8(console.error_message().c_str()); + + return ret; +} + +wxString MKS::get_test_ok_msg() const +{ + return _(L("Connection to MKS works correctly.")); +} + +wxString MKS::get_test_failed_msg(wxString& msg) const +{ + return GUI::from_u8((boost::format("%s: %s") + % _utf8(L("Could not connect to MKS")) + % std::string(msg.ToUTF8())).str()); +} + +bool MKS::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const +{ + bool res = true; + + auto upload_cmd = get_upload_url(upload_data.upload_path.string()); + BOOST_LOG_TRIVIAL(info) << boost::format("MKS: Uploading file %1%, filepath: %2%, print: %3%, command: %4%") + % upload_data.source_path + % upload_data.upload_path + % upload_data.start_print + % upload_cmd; + + auto http = Http::post(std::move(upload_cmd)); + http.set_post_body(upload_data.source_path); + + http.on_complete([&](std::string body, unsigned status) { + BOOST_LOG_TRIVIAL(debug) << boost::format("MKS: File uploaded: HTTP %1%: %2%") % status % body; + + int err_code = get_err_code_from_body(body); + if (err_code != 0) { + BOOST_LOG_TRIVIAL(error) << boost::format("MKS: Request completed but error code was received: %1%") % err_code; + error_fn(format_error(body, L("Unknown error occured"), 0)); + res = false; } - - return ret; - } - - wxString MKS::get_test_ok_msg() const - { - return _(L("Connection to MKS works correctly.")); - } - - wxString MKS::get_test_failed_msg(wxString& msg) const - { - return GUI::from_u8((boost::format("%s: %s") - % _utf8(L("Could not connect to MKS")) - % std::string(msg.ToUTF8())).str()); - } - - bool MKS::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const - { - bool res = true; - - auto upload_cmd = get_upload_url(upload_data.upload_path.string()); - BOOST_LOG_TRIVIAL(info) << boost::format("MKS: Uploading file %1%, filepath: %2%, print: %3%, command: %4%") - % upload_data.source_path - % upload_data.upload_path - % upload_data.start_print - % upload_cmd; - - auto http = Http::post(std::move(upload_cmd)); - http.set_post_body(upload_data.source_path); - - http.on_complete([&](std::string body, unsigned status) { - BOOST_LOG_TRIVIAL(debug) << boost::format("MKS: File uploaded: HTTP %1%: %2%") % status % body; - - int err_code = get_err_code_from_body(body); - if (err_code != 0) { - BOOST_LOG_TRIVIAL(error) << boost::format("MKS: Request completed but error code was received: %1%") % err_code; - error_fn(format_error(body, L("Unknown error occured"), 0)); + else if (upload_data.start_print) { + wxString errormsg; + res = start_print(errormsg, upload_data.upload_path.string()); + if (!res) { + error_fn(std::move(errormsg)); + } + } + }) + .on_error([&](std::string body, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(error) << boost::format("MKS: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body; + error_fn(format_error(body, error, status)); + res = false; + }) + .on_progress([&](Http::Progress progress, bool& cancel) { + prorgess_fn(std::move(progress), cancel); + if (cancel) { + // Upload was canceled + BOOST_LOG_TRIVIAL(info) << "MKS: Upload canceled"; res = false; } - else if (upload_data.start_print) { - wxString errormsg; - res = start_print(errormsg, upload_data.upload_path.string()); - if (!res) { - error_fn(std::move(errormsg)); - } - } - }) - .on_error([&](std::string body, std::string error, unsigned status) { - BOOST_LOG_TRIVIAL(error) << boost::format("MKS: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body; - error_fn(format_error(body, error, status)); - res = false; - }) - .on_progress([&](Http::Progress progress, bool& cancel) { - prorgess_fn(std::move(progress), cancel); - if (cancel) { - // Upload was canceled - BOOST_LOG_TRIVIAL(info) << "MKS: Upload canceled"; - res = false; - } - }).perform_sync(); + }).perform_sync(); - return res; - } + return res; +} - std::string MKS::get_upload_url(const std::string& filename) const - { - return (boost::format("http://%1%/upload?X-Filename=%2%") - % host - % Http::url_encode(filename)).str(); - } +std::string MKS::get_upload_url(const std::string& filename) const +{ + return (boost::format("http://%1%/upload?X-Filename=%2%") + % m_host + % Http::url_encode(filename)).str(); +} - bool MKS::start_print(wxString& msg, const std::string& filename) const - { - // For some reason printer firmware does not want to respond on gcode commands immediately after file upload. - // So we just introduce artificial delay to workaround it. - // TODO: Inspect reasons - std::this_thread::sleep_for(std::chrono::milliseconds(1500)); +bool MKS::start_print(wxString& msg, const std::string& filename) const +{ + // For some reason printer firmware does not want to respond on gcode commands immediately after file upload. + // So we just introduce artificial delay to workaround it. + // TODO: Inspect reasons + std::this_thread::sleep_for(std::chrono::milliseconds(1500)); - Utils::TCPConsole console(host, console_port); + Utils::TCPConsole console(m_host, m_console_port); - console.enqueue_cmd(std::string("M23 ") + filename); - console.enqueue_cmd("M24"); + console.enqueue_cmd(std::string("M23 ") + filename); + console.enqueue_cmd("M24"); - bool ret = console.run_queue(); + bool ret = console.run_queue(); - if (!ret) { - msg = wxString::FromUTF8(console.error_message().c_str()); - } + if (!ret) + msg = wxString::FromUTF8(console.error_message().c_str()); - return ret; - } + return ret; +} - int MKS::get_err_code_from_body(const std::string& body) const - { - pt::ptree root; - std::istringstream iss(body); // wrap returned json to istringstream - pt::read_json(iss, root); +int MKS::get_err_code_from_body(const std::string& body) const +{ + pt::ptree root; + std::istringstream iss(body); // wrap returned json to istringstream + pt::read_json(iss, root); - return root.get("err", 0); - } + return root.get("err", 0); +} } // Slic3r diff --git a/src/slic3r/Utils/MKS.hpp b/src/slic3r/Utils/MKS.hpp index feddd17d8d..7564b7f81b 100644 --- a/src/slic3r/Utils/MKS.hpp +++ b/src/slic3r/Utils/MKS.hpp @@ -8,34 +8,34 @@ #include "TCPConsole.hpp" namespace Slic3r { - class DynamicPrintConfig; - class Http; +class DynamicPrintConfig; +class Http; - class MKS : public PrintHost - { - public: - explicit MKS(DynamicPrintConfig* config); - ~MKS() override = default; +class MKS : public PrintHost +{ +public: + explicit MKS(DynamicPrintConfig* config); + ~MKS() override = default; - const char* get_name() const override; + const char* get_name() const override; - bool test(wxString& curl_msg) const override; - wxString get_test_ok_msg() const override; - wxString get_test_failed_msg(wxString& msg) const override; - bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override; - bool has_auto_discovery() const override { return false; } - bool can_test() const override { return true; } - bool can_start_print() const override { return true; } - std::string get_host() const override { return host; } + bool test(wxString& curl_msg) const override; + wxString get_test_ok_msg() const override; + wxString get_test_failed_msg(wxString& msg) const override; + bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override; + bool has_auto_discovery() const override { return false; } + bool can_test() const override { return true; } + bool can_start_print() const override { return true; } + std::string get_host() const override { return m_host; } - private: - std::string host; - std::string console_port; +private: + std::string m_host; + std::string m_console_port; - std::string get_upload_url(const std::string& filename) const; - bool start_print(wxString& msg, const std::string& filename) const; - int get_err_code_from_body(const std::string& body) const; - }; + std::string get_upload_url(const std::string& filename) const; + bool start_print(wxString& msg, const std::string& filename) const; + int get_err_code_from_body(const std::string& body) const; +}; } diff --git a/src/slic3r/Utils/TCPConsole.cpp b/src/slic3r/Utils/TCPConsole.cpp index bb834bb5f3..a4f4bc21e9 100644 --- a/src/slic3r/Utils/TCPConsole.cpp +++ b/src/slic3r/Utils/TCPConsole.cpp @@ -18,212 +18,189 @@ using boost::asio::steady_timer; using boost::asio::ip::tcp; namespace Slic3r { - namespace Utils { +namespace Utils { - TCPConsole::TCPConsole() : resolver_(io_context_), socket_(io_context_) - { - set_defaults(); - } +void TCPConsole::transmit_next_command() +{ + if (m_cmd_queue.empty()) { + m_io_context.stop(); + return; + } - TCPConsole::TCPConsole(const std::string& host_name, const std::string& port_name) : - resolver_(io_context_), socket_(io_context_) - { - set_defaults(); - set_remote(host_name, port_name); - } + std::string cmd = m_cmd_queue.front(); + m_cmd_queue.pop_front(); - void TCPConsole::transmit_next_command() - { - if (cmd_queue_.empty()) { - io_context_.stop(); - return; - } + BOOST_LOG_TRIVIAL(debug) << boost::format("TCPConsole: transmitting '%3%' to %1%:%2%") + % m_host_name + % m_port_name + % cmd; - std::string cmd = cmd_queue_.front(); - cmd_queue_.pop_front(); + m_send_buffer = cmd + m_newline; - BOOST_LOG_TRIVIAL(debug) << boost::format("TCPConsole: transmitting '%3%' to %1%:%2%") - % host_name_ - % port_name_ - % cmd; + set_deadline_in(m_write_timeout); + boost::asio::async_write( + m_socket, + boost::asio::buffer(m_send_buffer), + boost::bind(&TCPConsole::handle_write, this, _1, _2) + ); +} +void TCPConsole::wait_next_line() +{ + set_deadline_in(m_read_timeout); + boost::asio::async_read_until( + m_socket, + m_recv_buffer, + m_newline, + boost::bind(&TCPConsole::handle_read, this, _1, _2) + ); +} - send_buffer_ = cmd + newline_; +// TODO: Use std::optional here +std::string TCPConsole::extract_next_line() +{ + char linebuf[1024]; + std::istream is(&m_recv_buffer); + is.getline(linebuf, sizeof(linebuf)); + return is.good() ? linebuf : std::string{}; +} - set_deadline_in(write_timeout_); - boost::asio::async_write( - socket_, - boost::asio::buffer(send_buffer_), - boost::bind(&TCPConsole::handle_write, this, _1, _2) - ); - } +void TCPConsole::handle_read( + const boost::system::error_code& ec, + std::size_t bytes_transferred) +{ + m_error_code = ec; - void TCPConsole::wait_next_line() - { - set_deadline_in(read_timeout_); - boost::asio::async_read_until( - socket_, - recv_buffer_, - newline_, - boost::bind(&TCPConsole::handle_read, this, _1, _2) - ); - } + if (ec) { + BOOST_LOG_TRIVIAL(error) << boost::format("TCPConsole: Can't read from %1%:%2%: %3%") + % m_host_name + % m_port_name + % ec.message(); - // TODO: Use std::optional here - std::string TCPConsole::extract_next_line() - { - char linebuf[1024]; + m_io_context.stop(); + } + else { + std::string line = extract_next_line(); + boost::trim(line); - std::istream is(&recv_buffer_); - is.getline(linebuf, sizeof(linebuf)); - if (is.good()) { - return linebuf; - } - - return ""; - } - - void TCPConsole::handle_read( - const boost::system::error_code& ec, - std::size_t bytes_transferred) - { - error_code_ = ec; - - if (ec) { - BOOST_LOG_TRIVIAL(error) << boost::format("TCPConsole: Can't read from %1%:%2%: %3%") - % host_name_ - % port_name_ - % ec.message(); - - io_context_.stop(); - } - else { - std::string line = extract_next_line(); - boost::trim(line); - - BOOST_LOG_TRIVIAL(debug) << boost::format("TCPConsole: received '%3%' from %1%:%2%") - % host_name_ - % port_name_ - % line; - - boost::to_lower(line); - - if (line == done_string_) { - transmit_next_command(); - } - else { - wait_next_line(); - } - } - } - - void TCPConsole::handle_write( - const boost::system::error_code& ec, - std::size_t) - { - error_code_ = ec; - if (ec) { - BOOST_LOG_TRIVIAL(error) << boost::format("TCPConsole: Can't write to %1%:%2%: %3%") - % host_name_ - % port_name_ - % ec.message(); - - io_context_.stop(); - } - else { - wait_next_line(); - } - } - - void TCPConsole::handle_connect(const boost::system::error_code& ec) - { - error_code_ = ec; - - if (ec) { - BOOST_LOG_TRIVIAL(error) << boost::format("TCPConsole: Can't connect to %1%:%2%: %3%") - % host_name_ - % port_name_ - % ec.message(); - - io_context_.stop(); - } - else { - is_connected_ = true; - BOOST_LOG_TRIVIAL(info) << boost::format("TCPConsole: connected to %1%:%2%") - % host_name_ - % port_name_; - - - transmit_next_command(); - } - } - - void TCPConsole::set_deadline_in(std::chrono::steady_clock::duration d) - { - deadline_ = std::chrono::steady_clock::now() + d; - } - bool TCPConsole::is_deadline_over() - { - return deadline_ < std::chrono::steady_clock::now(); - } - - bool TCPConsole::run_queue() - { - auto now = std::chrono::steady_clock::now(); - try { - // TODO: Add more resets and initializations after previous run (reset() method?..) - set_deadline_in(connect_timeout_); - is_connected_ = false; - io_context_.restart(); - - auto endpoints = resolver_.resolve(host_name_, port_name_); - - socket_.async_connect(endpoints->endpoint(), - boost::bind(&TCPConsole::handle_connect, this, _1) - ); - - // Loop until we get any reasonable result. Negative result is also result. - // TODO: Rewrite to more graceful way using deadlime_timer - bool timeout = false; - while (!(timeout = is_deadline_over()) && !io_context_.stopped()) { - if (error_code_) { - io_context_.stop(); - } - io_context_.run_for(boost::asio::chrono::milliseconds(100)); - } - - // Override error message if timeout is set - if (timeout) { - error_code_ = make_error_code(boost::asio::error::timed_out); - } - - // Socket is not closed automatically by boost - socket_.close(); - - if (error_code_) { - // We expect that message is logged in handler - return false; - } - - // It's expected to have empty queue after successful exchange - if (!cmd_queue_.empty()) { - BOOST_LOG_TRIVIAL(error) << "TCPConsole: command queue is not empty after end of exchange"; - return false; - } - } - catch (std::exception& e) - { - BOOST_LOG_TRIVIAL(error) << boost::format("TCPConsole: Exception while talking with %1%:%2%: %3%") - % host_name_ - % port_name_ - % e.what(); - - return false; - } - - return true; - } + BOOST_LOG_TRIVIAL(debug) << boost::format("TCPConsole: received '%3%' from %1%:%2%") + % m_host_name + % m_port_name + % line; + boost::to_lower(line); + if (line == m_done_string) + transmit_next_command(); + else + wait_next_line(); } } + +void TCPConsole::handle_write( + const boost::system::error_code& ec, + std::size_t) +{ + m_error_code = ec; + if (ec) { + BOOST_LOG_TRIVIAL(error) << boost::format("TCPConsole: Can't write to %1%:%2%: %3%") + % m_host_name + % m_port_name + % ec.message(); + + m_io_context.stop(); + } + else { + wait_next_line(); + } +} + +void TCPConsole::handle_connect(const boost::system::error_code& ec) +{ + m_error_code = ec; + + if (ec) { + BOOST_LOG_TRIVIAL(error) << boost::format("TCPConsole: Can't connect to %1%:%2%: %3%") + % m_host_name + % m_port_name + % ec.message(); + + m_io_context.stop(); + } + else { + m_is_connected = true; + BOOST_LOG_TRIVIAL(info) << boost::format("TCPConsole: connected to %1%:%2%") + % m_host_name + % m_port_name; + + transmit_next_command(); + } +} + +void TCPConsole::set_deadline_in(std::chrono::steady_clock::duration d) +{ + m_deadline = std::chrono::steady_clock::now() + d; +} +bool TCPConsole::is_deadline_over() const +{ + return m_deadline < std::chrono::steady_clock::now(); +} + +bool TCPConsole::run_queue() +{ + auto now = std::chrono::steady_clock::now(); + try { + // TODO: Add more resets and initializations after previous run (reset() method?..) + set_deadline_in(m_connect_timeout); + m_is_connected = false; + m_io_context.restart(); + + auto endpoints = m_resolver.resolve(m_host_name, m_port_name); + + m_socket.async_connect(endpoints->endpoint(), + boost::bind(&TCPConsole::handle_connect, this, _1) + ); + + // Loop until we get any reasonable result. Negative result is also result. + // TODO: Rewrite to more graceful way using deadlime_timer + bool timeout = false; + while (!(timeout = is_deadline_over()) && !m_io_context.stopped()) { + if (m_error_code) { + m_io_context.stop(); + } + m_io_context.run_for(boost::asio::chrono::milliseconds(100)); + } + + // Override error message if timeout is set + if (timeout) + m_error_code = make_error_code(boost::asio::error::timed_out); + + // Socket is not closed automatically by boost + m_socket.close(); + + if (m_error_code) { + // We expect that message is logged in handler + return false; + } + + // It's expected to have empty queue after successful exchange + if (!m_cmd_queue.empty()) { + BOOST_LOG_TRIVIAL(error) << "TCPConsole: command queue is not empty after end of exchange"; + return false; + } + } + catch (std::exception& e) + { + BOOST_LOG_TRIVIAL(error) << boost::format("TCPConsole: Exception while talking with %1%:%2%: %3%") + % m_host_name + % m_port_name + % e.what(); + + return false; + } + + return true; +} + +} // namespace Utils +} // namespace Slic3r diff --git a/src/slic3r/Utils/TCPConsole.hpp b/src/slic3r/Utils/TCPConsole.hpp index 20472750be..7c0e1d2901 100644 --- a/src/slic3r/Utils/TCPConsole.hpp +++ b/src/slic3r/Utils/TCPConsole.hpp @@ -2,91 +2,90 @@ #define slic3r_Utils_TCPConsole_hpp_ #include -#include +#include #include #include #include #include namespace Slic3r { - namespace Utils { +namespace Utils { - using boost::asio::ip::tcp; +using boost::asio::ip::tcp; - class TCPConsole - { - public: - TCPConsole(); - TCPConsole(const std::string& host_name, const std::string& port_name); - ~TCPConsole() {} +class TCPConsole +{ +public: + TCPConsole() : m_resolver(m_io_context), m_socket(m_io_context) { set_defaults(); } + TCPConsole(const std::string& host_name, const std::string& port_name) : m_resolver(m_io_context), m_socket(m_io_context) + { set_defaults(); set_remote(host_name, port_name); } + ~TCPConsole() = default; - void set_defaults() - { - newline_ = "\n"; - done_string_ = "ok"; - connect_timeout_ = std::chrono::milliseconds(5000); - write_timeout_ = std::chrono::milliseconds(10000); - read_timeout_ = std::chrono::milliseconds(10000); - } + void set_defaults() + { + m_newline = "\n"; + m_done_string = "ok"; + m_connect_timeout = std::chrono::milliseconds(5000); + m_write_timeout = std::chrono::milliseconds(10000); + m_read_timeout = std::chrono::milliseconds(10000); + } - void set_line_delimiter(const std::string& newline) { - newline_ = newline; - } - void set_command_done_string(const std::string& done_string) { - done_string_ = done_string; - } + void set_line_delimiter(const std::string& newline) { + m_newline = newline; + } + void set_command_done_string(const std::string& done_string) { + m_done_string = done_string; + } - void set_remote(const std::string& host_name, const std::string& port_name) - { - host_name_ = host_name; - port_name_ = port_name; - } + void set_remote(const std::string& host_name, const std::string& port_name) + { + m_host_name = host_name; + m_port_name = port_name; + } - bool enqueue_cmd(const std::string& cmd) { - // TODO: Add multithread protection to queue - cmd_queue_.push_back(cmd); - return true; - } + bool enqueue_cmd(const std::string& cmd) { + // TODO: Add multithread protection to queue + m_cmd_queue.push_back(cmd); + return true; + } - bool run_queue(); - std::string error_message() { - return error_code_.message(); - } + bool run_queue(); + std::string error_message() const { return m_error_code.message(); } - private: - void handle_connect(const boost::system::error_code& ec); - void handle_read(const boost::system::error_code& ec, std::size_t bytes_transferred); - void handle_write(const boost::system::error_code& ec, std::size_t bytes_transferred); +private: + void handle_connect(const boost::system::error_code& ec); + void handle_read(const boost::system::error_code& ec, std::size_t bytes_transferred); + void handle_write(const boost::system::error_code& ec, std::size_t bytes_transferred); - void transmit_next_command(); - void wait_next_line(); - std::string extract_next_line(); + void transmit_next_command(); + void wait_next_line(); + std::string extract_next_line(); - void set_deadline_in(std::chrono::steady_clock::duration); - bool is_deadline_over(); + void set_deadline_in(std::chrono::steady_clock::duration); + bool is_deadline_over() const; - std::string host_name_; - std::string port_name_; - std::string newline_; - std::string done_string_; - std::chrono::steady_clock::duration connect_timeout_; - std::chrono::steady_clock::duration write_timeout_; - std::chrono::steady_clock::duration read_timeout_; + std::string m_host_name; + std::string m_port_name; + std::string m_newline; + std::string m_done_string; + 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::list cmd_queue_; + std::deque m_cmd_queue; - boost::asio::io_context io_context_; - tcp::resolver resolver_; - tcp::socket socket_; - boost::asio::streambuf recv_buffer_; - std::string send_buffer_; + boost::asio::io_context m_io_context; + tcp::resolver m_resolver; + tcp::socket m_socket; + boost::asio::streambuf m_recv_buffer; + std::string m_send_buffer; - bool is_connected_; - boost::system::error_code error_code_; - std::chrono::steady_clock::time_point deadline_; - }; + bool m_is_connected; + boost::system::error_code m_error_code; + std::chrono::steady_clock::time_point m_deadline; +}; - } // Utils +} // Utils } // Slic3r #endif