From 4917f3b811ddf3f1bf31ac14180af5e7e1940ef7 Mon Sep 17 00:00:00 2001 From: Sergey Kovalev Date: Sun, 27 Dec 2020 21:51:06 +0700 Subject: [PATCH 01/12] Initial implementation, mostly proof of concept --- src/libslic3r/PrintConfig.cpp | 2 + src/libslic3r/PrintConfig.hpp | 2 +- src/slic3r/CMakeLists.txt | 2 + src/slic3r/Utils/MKS.cpp | 171 +++++++++++++++++++++++++++++++++ src/slic3r/Utils/MKS.hpp | 44 +++++++++ src/slic3r/Utils/PrintHost.cpp | 2 + 6 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 src/slic3r/Utils/MKS.cpp create mode 100644 src/slic3r/Utils/MKS.hpp diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index c63780449..85cae5c1b 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1854,12 +1854,14 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("flashair"); def->enum_values.push_back("astrobox"); def->enum_values.push_back("repetier"); + def->enum_values.push_back("mks"); def->enum_labels.push_back("PrusaLink"); def->enum_labels.push_back("OctoPrint"); def->enum_labels.push_back("Duet"); def->enum_labels.push_back("FlashAir"); def->enum_labels.push_back("AstroBox"); def->enum_labels.push_back("Repetier"); + def->enum_labels.push_back("MKS"); def->mode = comAdvanced; def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionEnum(htOctoPrint)); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 68dbd68d3..657f34ad1 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -44,7 +44,7 @@ enum class MachineLimitsUsage { }; enum PrintHostType { - htPrusaLink, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier + htPrusaLink, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS }; enum AuthorizationType { diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index f0c2338e9..75887d17c 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -237,6 +237,8 @@ set(SLIC3R_GUI_SOURCES Utils/UndoRedo.hpp Utils/HexFile.cpp Utils/HexFile.hpp + Utils/MKS.cpp + Utils/MKS.hpp ) if (APPLE) diff --git a/src/slic3r/Utils/MKS.cpp b/src/slic3r/Utils/MKS.cpp new file mode 100644 index 000000000..4f7c9a90b --- /dev/null +++ b/src/slic3r/Utils/MKS.cpp @@ -0,0 +1,171 @@ +#include "MKS.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "libslic3r/PrintConfig.hpp" +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/I18N.hpp" +#include "slic3r/GUI/MsgDialog.hpp" +#include "Http.hpp" + +namespace fs = boost::filesystem; +namespace pt = boost::property_tree; + +namespace Slic3r { + +MKS::MKS(DynamicPrintConfig *config) : + host(config->opt_string("print_host")), console_port(8080) +{} + +const char* MKS::get_name() const { return "MKS"; } + +bool MKS::test(wxString &msg) const +{ + return run_simple_gcode("M105", msg); +} + +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; + } 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(); + + if (res) { + std::this_thread::sleep_for(std::chrono::milliseconds(1500)); + + wxString msg; + res &= run_simple_gcode(std::string("M23 ") + upload_data.upload_path.string(), msg); + if (res) { + res &= run_simple_gcode(std::string("M24"), msg); + } + } + + 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(); +} + +bool MKS::start_print(wxString &msg, const std::string &filename) const +{ + BOOST_LOG_TRIVIAL(warning) << boost::format("MKS: start_print is not implemented yet, called stub"); + return true; +} + +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); +} + +bool MKS::run_simple_gcode(const std::string &cmd, wxString &msg) const +{ + using boost::asio::ip::tcp; + + try + { + boost::asio::io_context io_context; + tcp::socket s(io_context); + + tcp::resolver resolver(io_context); + boost::asio::connect(s, resolver.resolve(host, std::to_string(console_port))); + boost::asio::write(s, boost::asio::buffer(cmd + "\r\n")); + + msg = "request:" + cmd + "\r\n"; + + boost::asio::streambuf input_buffer; + size_t reply_length = boost::asio::read_until(s, input_buffer, '\n'); + + std::string response((std::istreambuf_iterator(&input_buffer)), std::istreambuf_iterator()); + if (response.length() == 0) { + msg += "Empty response"; + return false; + } + + msg += "response:" + response; + return true; + } + catch (std::exception& e) + { + msg = std::string("exception:") + e.what(); + return false; + } +} + +} diff --git a/src/slic3r/Utils/MKS.hpp b/src/slic3r/Utils/MKS.hpp new file mode 100644 index 000000000..4fed921d5 --- /dev/null +++ b/src/slic3r/Utils/MKS.hpp @@ -0,0 +1,44 @@ +#ifndef slic3r_MKS_hpp_ +#define slic3r_MKS_hpp_ + +#include +#include + +#include "PrintHost.hpp" + +namespace Slic3r { + +class DynamicPrintConfig; +class Http; + +class MKS : public PrintHost +{ +public: + explicit MKS(DynamicPrintConfig *config); + ~MKS() override = default; + + 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; } + +private: + std::string host; + int console_port; + + std::string get_upload_url(const std::string &filename) const; + std::string timestamp_str() const; + bool start_print(wxString &msg, const std::string &filename) const; + int get_err_code_from_body(const std::string &body) const; + bool run_simple_gcode(const std::string& cmd, wxString& msg) const; +}; + +} + +#endif diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index 53200a4c9..86f6101b6 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -18,6 +18,7 @@ #include "FlashAir.hpp" #include "AstroBox.hpp" #include "Repetier.hpp" +#include "MKS.hpp" #include "../GUI/PrintHostDialogs.hpp" namespace fs = boost::filesystem; @@ -51,6 +52,7 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config) case htAstroBox: return new AstroBox(config); case htRepetier: return new Repetier(config); case htPrusaLink: return new PrusaLink(config); + case htMKS: return new MKS(config); default: return nullptr; } } else { From 11eafebd8ffd0d1453d90cab0b93681023202fb2 Mon Sep 17 00:00:00 2001 From: Sergey Kovalev Date: Sun, 27 Dec 2020 23:16:19 +0700 Subject: [PATCH 02/12] Fix printing start condition, a little bit more comments --- src/slic3r/Utils/MKS.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/slic3r/Utils/MKS.cpp b/src/slic3r/Utils/MKS.cpp index 4f7c9a90b..aa7eee48e 100644 --- a/src/slic3r/Utils/MKS.cpp +++ b/src/slic3r/Utils/MKS.cpp @@ -99,7 +99,9 @@ bool MKS::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn er }) .perform_sync(); - if (res) { + if (res && upload_data.start_print) { + // 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. std::this_thread::sleep_for(std::chrono::milliseconds(1500)); wxString msg; From 869b8c8c66e0fee0464c98b7008e0f6a2dbc41d8 Mon Sep 17 00:00:00 2001 From: Sergey Kovalev Date: Tue, 29 Dec 2020 00:44:39 +0700 Subject: [PATCH 03/12] Add better implementation of tcp console --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/Utils/MKS.cpp | 74 +++++-------- src/slic3r/Utils/MKS.hpp | 6 +- src/slic3r/Utils/TCPConsole.cpp | 191 ++++++++++++++++++++++++++++++++ src/slic3r/Utils/TCPConsole.hpp | 81 ++++++++++++++ 5 files changed, 303 insertions(+), 51 deletions(-) create mode 100644 src/slic3r/Utils/TCPConsole.cpp create mode 100644 src/slic3r/Utils/TCPConsole.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 75887d17c..94b8bae6f 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -237,6 +237,8 @@ set(SLIC3R_GUI_SOURCES Utils/UndoRedo.hpp Utils/HexFile.cpp Utils/HexFile.hpp + Utils/TCPConsole.cpp + Utils/TCPConsole.hpp Utils/MKS.cpp Utils/MKS.hpp ) diff --git a/src/slic3r/Utils/MKS.cpp b/src/slic3r/Utils/MKS.cpp index aa7eee48e..c25ae9d0d 100644 --- a/src/slic3r/Utils/MKS.cpp +++ b/src/slic3r/Utils/MKS.cpp @@ -32,14 +32,21 @@ namespace pt = boost::property_tree; namespace Slic3r { MKS::MKS(DynamicPrintConfig *config) : - host(config->opt_string("print_host")), console_port(8080) + host(config->opt_string("print_host")), console(config->opt_string("print_host"), "8080") {} const char* MKS::get_name() const { return "MKS"; } bool MKS::test(wxString &msg) const { - return run_simple_gcode("M105", msg); + console.enqueue_cmd("M105"); + bool ret = console.run_queue(); + + if (!ret) { + msg = console.error_message(); + } + + return ret; } wxString MKS::get_test_ok_msg () const @@ -100,15 +107,7 @@ bool MKS::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn er .perform_sync(); if (res && upload_data.start_print) { - // 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. - std::this_thread::sleep_for(std::chrono::milliseconds(1500)); - - wxString msg; - res &= run_simple_gcode(std::string("M23 ") + upload_data.upload_path.string(), msg); - if (res) { - res &= run_simple_gcode(std::string("M24"), msg); - } + start_print(upload_data.upload_path); } return res; @@ -123,8 +122,21 @@ std::string MKS::get_upload_url(const std::string &filename) const bool MKS::start_print(wxString &msg, const std::string &filename) const { - BOOST_LOG_TRIVIAL(warning) << boost::format("MKS: start_print is not implemented yet, called stub"); - return true; + // 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)); + + console.enqueue_cmd("M23 " + upload_data.upload_path.string()); + console.enqueue_cmd("M24"); + + bool ret = console.run_queue(); + + if (!ret) { + msg = console.error_message(); + } + + return ret; } int MKS::get_err_code_from_body(const std::string& body) const @@ -136,38 +148,4 @@ int MKS::get_err_code_from_body(const std::string& body) const return root.get("err", 0); } -bool MKS::run_simple_gcode(const std::string &cmd, wxString &msg) const -{ - using boost::asio::ip::tcp; - - try - { - boost::asio::io_context io_context; - tcp::socket s(io_context); - - tcp::resolver resolver(io_context); - boost::asio::connect(s, resolver.resolve(host, std::to_string(console_port))); - boost::asio::write(s, boost::asio::buffer(cmd + "\r\n")); - - msg = "request:" + cmd + "\r\n"; - - boost::asio::streambuf input_buffer; - size_t reply_length = boost::asio::read_until(s, input_buffer, '\n'); - - std::string response((std::istreambuf_iterator(&input_buffer)), std::istreambuf_iterator()); - if (response.length() == 0) { - msg += "Empty response"; - return false; - } - - msg += "response:" + response; - return true; - } - catch (std::exception& e) - { - msg = std::string("exception:") + e.what(); - return false; - } -} - -} +} // Slic3r diff --git a/src/slic3r/Utils/MKS.hpp b/src/slic3r/Utils/MKS.hpp index 4fed921d5..0414e3335 100644 --- a/src/slic3r/Utils/MKS.hpp +++ b/src/slic3r/Utils/MKS.hpp @@ -5,6 +5,7 @@ #include #include "PrintHost.hpp" +#include "TCPConsole.hpp" namespace Slic3r { @@ -27,16 +28,15 @@ public: bool can_test() const override { return true; } bool can_start_print() const override { return true; } std::string get_host() const override { return host; } - + private: std::string host; - int console_port; + Utils::TCPConsole console; std::string get_upload_url(const std::string &filename) const; std::string timestamp_str() const; bool start_print(wxString &msg, const std::string &filename) const; int get_err_code_from_body(const std::string &body) const; - bool run_simple_gcode(const std::string& cmd, wxString& msg) const; }; } diff --git a/src/slic3r/Utils/TCPConsole.cpp b/src/slic3r/Utils/TCPConsole.cpp new file mode 100644 index 000000000..4e1ca1f7c --- /dev/null +++ b/src/slic3r/Utils/TCPConsole.cpp @@ -0,0 +1,191 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +using boost::asio::steady_timer; +using boost::asio::ip::tcp; + +namespace Slic3r { +namespace Utils { + +void TCPConsole::transmit_next_command() +{ + if (cmd_queue_.empty()) { + io_context_.stop(); + return; + } + + std::string cmd = cmd_queue_.front(); + cmd_queue_.pop_front(); + + BOOST_LOG_TRIVIAL(debug) << boost::format("TCPConsole: transmitting '%3%' to %1%:%2%") + % host_name_ + % port_name_ + % cmd; + + auto data = boost::asio::buffer(cmd + newline_); + + boost::asio::async_write( + socket_, + data, + boost::bind(&TCPConsole::handle_write, this, _1, _2) + ); +} + +void TCPConsole::wait_next_line() +{ + boost::asio::async_read_until( + socket_, + recv_buffer_, + newline_, + boost::bind(&TCPConsole::handle_read, this, _1, _2) + ); +} + +// TODO: Use std::optional here +std::string TCPConsole::extract_next_line() +{ + char linebuf[1024]; + + 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 { + BOOST_LOG_TRIVIAL(info) << boost::format("TCPConsole: connected to %1%:%2%") + % host_name_ + % port_name_; + + + transmit_next_command(); + } +} + +bool TCPConsole::run_queue() +{ + try { + // TODO: Add more resets and initializations after previous run + + auto endpoints = resolver_.resolve(host_name_, port_name_); + + socket_.async_connect(endpoints->endpoint(), + boost::bind(&TCPConsole::handle_connect, this, _1) + ); + + // TODO: Add error and timeout processing + io_context_.restart(); + while (!io_context_.stopped()) { + BOOST_LOG_TRIVIAL(debug) << ".\n"; + if (error_code_) { + io_context_.stop(); + } + io_context_.run_for(boost::asio::chrono::milliseconds(100)); + } + + // 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; +} + + +} +} diff --git a/src/slic3r/Utils/TCPConsole.hpp b/src/slic3r/Utils/TCPConsole.hpp new file mode 100644 index 000000000..6ff65ad8a --- /dev/null +++ b/src/slic3r/Utils/TCPConsole.hpp @@ -0,0 +1,81 @@ +#ifndef slic3r_Utils_TCPConsole_hpp_ +#define slic3r_Utils_TCPConsole_hpp_ + +#include +#include +#include +#include +#include + +namespace Slic3r { +namespace Utils { + +const char * default_newline = "\n"; +const char * default_done_string = "ok"; + +using boost::asio::ip::tcp; + +class TCPConsole +{ +public: + TCPConsole(): resolver_(io_context_), socket_(io_context_), newline_(default_newline), done_string_(default_done_string) {} + + TCPConsole(const std::string &host_name, const std::string &port_name): + resolver_(io_context_), socket_(io_context_), newline_(default_newline), done_string_(default_done_string) + { + set_remote(host_name, port_name); + } + ~TCPConsole(){} + + 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_remote(const std::string &host_name, const std::string &port_name) + { + host_name_ = host_name; + 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 run_queue(); + std::string error_message() { + return 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); + + void transmit_next_command(); + void wait_next_line(); + std::string extract_next_line(); + + std::string host_name_; + std::string port_name_; + std::string newline_; + std::string done_string_; + + std::list cmd_queue_; + + boost::asio::io_context io_context_; + tcp::resolver resolver_; + tcp::socket socket_; + boost::asio::streambuf recv_buffer_; + + boost::system::error_code error_code_; +}; + +} // Utils +} // Slic3r + +#endif From f1ae74f9d3969a8c0235c5b7943a4e2a7660a468 Mon Sep 17 00:00:00 2001 From: Sergey Kovalev Date: Tue, 29 Dec 2020 01:01:39 +0700 Subject: [PATCH 04/12] Small adaptation and formatting --- src/slic3r/Utils/MKS.cpp | 170 ++++++++-------- src/slic3r/Utils/MKS.hpp | 48 ++--- src/slic3r/Utils/TCPConsole.cpp | 330 ++++++++++++++++---------------- src/slic3r/Utils/TCPConsole.hpp | 106 +++++----- 4 files changed, 331 insertions(+), 323 deletions(-) diff --git a/src/slic3r/Utils/MKS.cpp b/src/slic3r/Utils/MKS.cpp index c25ae9d0d..1a60c5a3e 100644 --- a/src/slic3r/Utils/MKS.cpp +++ b/src/slic3r/Utils/MKS.cpp @@ -31,51 +31,51 @@ namespace pt = boost::property_tree; namespace Slic3r { -MKS::MKS(DynamicPrintConfig *config) : - host(config->opt_string("print_host")), console(config->opt_string("print_host"), "8080") -{} + MKS::MKS(DynamicPrintConfig* config) : + host(config->opt_string("print_host")), console(config->opt_string("print_host"), "8080") + {} -const char* MKS::get_name() const { return "MKS"; } + const char* MKS::get_name() const { return "MKS"; } -bool MKS::test(wxString &msg) const -{ - console.enqueue_cmd("M105"); - bool ret = console.run_queue(); + bool MKS::test(wxString& msg) const + { + console.enqueue_cmd("M105"); + bool ret = console.run_queue(); - if (!ret) { - msg = console.error_message(); - } + if (!ret) { + msg = console.error_message(); + } - return ret; -} + return ret; + } -wxString MKS::get_test_ok_msg () const -{ - return _(L("Connection to MKS works correctly.")); -} + 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()); -} + 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; + 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 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); + auto http = Http::post(std::move(upload_cmd)); + http.set_post_body(upload_data.source_path); - http.on_complete([&](std::string body, unsigned status) { + 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); @@ -83,69 +83,73 @@ bool MKS::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn er 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; - } else if (upload_data.start_print) { + } + else if (upload_data.start_print) { wxString errormsg; res = start_print(errormsg, upload_data.upload_path.string()); - if (! res) { + 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"; + }) + .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; - } - }) - .perform_sync(); + }) + .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(); - if (res && upload_data.start_print) { - start_print(upload_data.upload_path); + if (res && upload_data.start_print) { + wxString msg; + if (!start_print(msg, upload_data.upload_path.string())) { + error_fn(wxString("Can't start printing: ") + msg); + } + } + + 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%") - % 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)); + console.enqueue_cmd(std::string("M23 ") + filename); + console.enqueue_cmd("M24"); - console.enqueue_cmd("M23 " + upload_data.upload_path.string()); - console.enqueue_cmd("M24"); + bool ret = console.run_queue(); - bool ret = console.run_queue(); + if (!ret) { + msg = console.error_message(); + } - if (!ret) { - msg = console.error_message(); - } + 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 0414e3335..67c655293 100644 --- a/src/slic3r/Utils/MKS.hpp +++ b/src/slic3r/Utils/MKS.hpp @@ -9,35 +9,35 @@ 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 host; } -private: - std::string host; - Utils::TCPConsole console; + private: + std::string host; + Utils::TCPConsole console; - std::string get_upload_url(const std::string &filename) const; - std::string timestamp_str() 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; + std::string timestamp_str() 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 4e1ca1f7c..bea991945 100644 --- a/src/slic3r/Utils/TCPConsole.cpp +++ b/src/slic3r/Utils/TCPConsole.cpp @@ -18,174 +18,178 @@ using boost::asio::steady_timer; using boost::asio::ip::tcp; namespace Slic3r { -namespace Utils { + namespace Utils { -void TCPConsole::transmit_next_command() -{ - if (cmd_queue_.empty()) { - io_context_.stop(); - return; - } + void TCPConsole::transmit_next_command() + { + if (cmd_queue_.empty()) { + io_context_.stop(); + return; + } - std::string cmd = cmd_queue_.front(); - cmd_queue_.pop_front(); + std::string cmd = cmd_queue_.front(); + cmd_queue_.pop_front(); - BOOST_LOG_TRIVIAL(debug) << boost::format("TCPConsole: transmitting '%3%' to %1%:%2%") - % host_name_ - % port_name_ - % cmd; + BOOST_LOG_TRIVIAL(debug) << boost::format("TCPConsole: transmitting '%3%' to %1%:%2%") + % host_name_ + % port_name_ + % cmd; - auto data = boost::asio::buffer(cmd + newline_); + auto data = boost::asio::buffer(cmd + newline_); - boost::asio::async_write( - socket_, - data, - boost::bind(&TCPConsole::handle_write, this, _1, _2) - ); -} - -void TCPConsole::wait_next_line() -{ - boost::asio::async_read_until( - socket_, - recv_buffer_, - newline_, - boost::bind(&TCPConsole::handle_read, this, _1, _2) - ); -} - -// TODO: Use std::optional here -std::string TCPConsole::extract_next_line() -{ - char linebuf[1024]; - - 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 { - BOOST_LOG_TRIVIAL(info) << boost::format("TCPConsole: connected to %1%:%2%") - % host_name_ - % port_name_; - - - transmit_next_command(); - } -} - -bool TCPConsole::run_queue() -{ - try { - // TODO: Add more resets and initializations after previous run - - auto endpoints = resolver_.resolve(host_name_, port_name_); - - socket_.async_connect(endpoints->endpoint(), - boost::bind(&TCPConsole::handle_connect, this, _1) - ); - - // TODO: Add error and timeout processing - io_context_.restart(); - while (!io_context_.stopped()) { - BOOST_LOG_TRIVIAL(debug) << ".\n"; - if (error_code_) { - io_context_.stop(); + boost::asio::async_write( + socket_, + data, + boost::bind(&TCPConsole::handle_write, this, _1, _2) + ); } - io_context_.run_for(boost::asio::chrono::milliseconds(100)); + + void TCPConsole::wait_next_line() + { + boost::asio::async_read_until( + socket_, + recv_buffer_, + newline_, + boost::bind(&TCPConsole::handle_read, this, _1, _2) + ); + } + + // TODO: Use std::optional here + std::string TCPConsole::extract_next_line() + { + char linebuf[1024]; + + 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 { + BOOST_LOG_TRIVIAL(info) << boost::format("TCPConsole: connected to %1%:%2%") + % host_name_ + % port_name_; + + + transmit_next_command(); + } + } + + bool TCPConsole::run_queue() + { + try { + // TODO: Add more resets and initializations after previous run + + auto endpoints = resolver_.resolve(host_name_, port_name_); + + socket_.async_connect(endpoints->endpoint(), + boost::bind(&TCPConsole::handle_connect, this, _1) + ); + + // TODO: Add error and timeout processing + io_context_.restart(); + while (!io_context_.stopped()) { + BOOST_LOG_TRIVIAL(debug) << ".\n"; + if (error_code_) { + io_context_.stop(); + } + io_context_.run_for(boost::asio::chrono::milliseconds(100)); + } + + // 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; + } + + } - - // 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; -} - - -} } diff --git a/src/slic3r/Utils/TCPConsole.hpp b/src/slic3r/Utils/TCPConsole.hpp index 6ff65ad8a..aa3b7da6f 100644 --- a/src/slic3r/Utils/TCPConsole.hpp +++ b/src/slic3r/Utils/TCPConsole.hpp @@ -8,74 +8,74 @@ #include namespace Slic3r { -namespace Utils { + namespace Utils { -const char * default_newline = "\n"; -const char * default_done_string = "ok"; + const char* default_newline = "\n"; + const char* default_done_string = "ok"; -using boost::asio::ip::tcp; + using boost::asio::ip::tcp; -class TCPConsole -{ -public: - TCPConsole(): resolver_(io_context_), socket_(io_context_), newline_(default_newline), done_string_(default_done_string) {} + class TCPConsole + { + public: + TCPConsole() : resolver_(io_context_), socket_(io_context_), newline_(default_newline), done_string_(default_done_string) {} - TCPConsole(const std::string &host_name, const std::string &port_name): - resolver_(io_context_), socket_(io_context_), newline_(default_newline), done_string_(default_done_string) - { - set_remote(host_name, port_name); - } - ~TCPConsole(){} + TCPConsole(const std::string& host_name, const std::string& port_name) : + resolver_(io_context_), socket_(io_context_), newline_(default_newline), done_string_(default_done_string) + { + set_remote(host_name, port_name); + } + ~TCPConsole() {} - 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) { + newline_ = newline; + } + void set_command_done_string(const std::string& done_string) { + 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) + { + host_name_ = host_name; + 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 + 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() { + return 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(); - std::string host_name_; - std::string port_name_; - std::string newline_; - std::string done_string_; + std::string host_name_; + std::string port_name_; + std::string newline_; + std::string done_string_; - std::list cmd_queue_; + std::list cmd_queue_; - boost::asio::io_context io_context_; - tcp::resolver resolver_; - tcp::socket socket_; - boost::asio::streambuf recv_buffer_; + boost::asio::io_context io_context_; + tcp::resolver resolver_; + tcp::socket socket_; + boost::asio::streambuf recv_buffer_; - boost::system::error_code error_code_; -}; + boost::system::error_code error_code_; + }; -} // Utils + } // Utils } // Slic3r #endif From bb928f6ef7cdc3321834f51765c16166f92ee41b Mon Sep 17 00:00:00 2001 From: Sergey Kovalev Date: Tue, 29 Dec 2020 01:12:04 +0700 Subject: [PATCH 05/12] Multiple fixes for building --- src/slic3r/Utils/MKS.cpp | 6 +++++- src/slic3r/Utils/MKS.hpp | 3 +-- src/slic3r/Utils/TCPConsole.cpp | 14 +++++++++++++- src/slic3r/Utils/TCPConsole.hpp | 12 ++---------- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/slic3r/Utils/MKS.cpp b/src/slic3r/Utils/MKS.cpp index 1a60c5a3e..97cdbfffa 100644 --- a/src/slic3r/Utils/MKS.cpp +++ b/src/slic3r/Utils/MKS.cpp @@ -32,13 +32,15 @@ namespace pt = boost::property_tree; namespace Slic3r { MKS::MKS(DynamicPrintConfig* config) : - host(config->opt_string("print_host")), console(config->opt_string("print_host"), "8080") + host(config->opt_string("print_host")), console_port("8080") {} const char* MKS::get_name() const { return "MKS"; } bool MKS::test(wxString& msg) const { + Utils::TCPConsole console(host, console_port); + console.enqueue_cmd("M105"); bool ret = console.run_queue(); @@ -131,6 +133,8 @@ namespace Slic3r { // TODO: Inspect reasons std::this_thread::sleep_for(std::chrono::milliseconds(1500)); + Utils::TCPConsole console(host, console_port); + console.enqueue_cmd(std::string("M23 ") + filename); console.enqueue_cmd("M24"); diff --git a/src/slic3r/Utils/MKS.hpp b/src/slic3r/Utils/MKS.hpp index 67c655293..6e0aad58b 100644 --- a/src/slic3r/Utils/MKS.hpp +++ b/src/slic3r/Utils/MKS.hpp @@ -8,7 +8,6 @@ #include "TCPConsole.hpp" namespace Slic3r { - class DynamicPrintConfig; class Http; @@ -31,7 +30,7 @@ namespace Slic3r { private: std::string host; - Utils::TCPConsole console; + std::string console_port; std::string get_upload_url(const std::string& filename) const; std::string timestamp_str() const; diff --git a/src/slic3r/Utils/TCPConsole.cpp b/src/slic3r/Utils/TCPConsole.cpp index bea991945..17a37ef7c 100644 --- a/src/slic3r/Utils/TCPConsole.cpp +++ b/src/slic3r/Utils/TCPConsole.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include "TCPConsole.hpp" using boost::asio::steady_timer; using boost::asio::ip::tcp; @@ -20,6 +20,18 @@ using boost::asio::ip::tcp; namespace Slic3r { namespace Utils { + const char* default_newline = "\n"; + const char* default_done_string = "ok"; + + TCPConsole::TCPConsole() : resolver_(io_context_), socket_(io_context_), newline_(default_newline), done_string_(default_done_string) {} + + TCPConsole::TCPConsole(const std::string& host_name, const std::string& port_name) : + resolver_(io_context_), socket_(io_context_), newline_(default_newline), done_string_(default_done_string) + { + set_remote(host_name, port_name); + } + + void TCPConsole::transmit_next_command() { if (cmd_queue_.empty()) { diff --git a/src/slic3r/Utils/TCPConsole.hpp b/src/slic3r/Utils/TCPConsole.hpp index aa3b7da6f..82d850299 100644 --- a/src/slic3r/Utils/TCPConsole.hpp +++ b/src/slic3r/Utils/TCPConsole.hpp @@ -10,21 +10,13 @@ namespace Slic3r { namespace Utils { - const char* default_newline = "\n"; - const char* default_done_string = "ok"; - using boost::asio::ip::tcp; class TCPConsole { public: - TCPConsole() : resolver_(io_context_), socket_(io_context_), newline_(default_newline), done_string_(default_done_string) {} - - TCPConsole(const std::string& host_name, const std::string& port_name) : - resolver_(io_context_), socket_(io_context_), newline_(default_newline), done_string_(default_done_string) - { - set_remote(host_name, port_name); - } + TCPConsole(); + TCPConsole(const std::string& host_name, const std::string& port_name); ~TCPConsole() {} void set_line_delimiter(const std::string& newline) { From fbe90b55346dac8af06bc1194597a0724d2580f1 Mon Sep 17 00:00:00 2001 From: Sergey Kovalev Date: Tue, 29 Dec 2020 04:50:52 +0700 Subject: [PATCH 06/12] Add timeout/errors management, multiple fixes --- src/slic3r/Utils/MKS.cpp | 31 ++++++++------------ src/slic3r/Utils/MKS.hpp | 1 - src/slic3r/Utils/TCPConsole.cpp | 52 +++++++++++++++++++++++---------- src/slic3r/Utils/TCPConsole.hpp | 19 ++++++++++++ 4 files changed, 68 insertions(+), 35 deletions(-) diff --git a/src/slic3r/Utils/MKS.cpp b/src/slic3r/Utils/MKS.cpp index 97cdbfffa..636a0b3b2 100644 --- a/src/slic3r/Utils/MKS.cpp +++ b/src/slic3r/Utils/MKS.cpp @@ -45,7 +45,7 @@ namespace Slic3r { bool ret = console.run_queue(); if (!ret) { - msg = console.error_message(); + msg = wxString::FromUTF8(console.error_message().c_str()); } return ret; @@ -98,25 +98,18 @@ namespace Slic3r { 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(); + }) + .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(); - if (res && upload_data.start_print) { - wxString msg; - if (!start_print(msg, upload_data.upload_path.string())) { - error_fn(wxString("Can't start printing: ") + msg); - } - } - return res; + return res; } std::string MKS::get_upload_url(const std::string& filename) const @@ -141,7 +134,7 @@ namespace Slic3r { bool ret = console.run_queue(); if (!ret) { - msg = console.error_message(); + msg = wxString::FromUTF8(console.error_message().c_str()); } return ret; diff --git a/src/slic3r/Utils/MKS.hpp b/src/slic3r/Utils/MKS.hpp index 6e0aad58b..feddd17d8 100644 --- a/src/slic3r/Utils/MKS.hpp +++ b/src/slic3r/Utils/MKS.hpp @@ -33,7 +33,6 @@ namespace Slic3r { std::string console_port; std::string get_upload_url(const std::string& filename) const; - std::string timestamp_str() 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 17a37ef7c..edbfd8cde 100644 --- a/src/slic3r/Utils/TCPConsole.cpp +++ b/src/slic3r/Utils/TCPConsole.cpp @@ -20,17 +20,17 @@ using boost::asio::ip::tcp; namespace Slic3r { namespace Utils { - const char* default_newline = "\n"; - const char* default_done_string = "ok"; - - TCPConsole::TCPConsole() : resolver_(io_context_), socket_(io_context_), newline_(default_newline), done_string_(default_done_string) {} - - TCPConsole::TCPConsole(const std::string& host_name, const std::string& port_name) : - resolver_(io_context_), socket_(io_context_), newline_(default_newline), done_string_(default_done_string) + TCPConsole::TCPConsole() : resolver_(io_context_), socket_(io_context_) { - set_remote(host_name, port_name); + set_defaults(); } + 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); + } void TCPConsole::transmit_next_command() { @@ -47,17 +47,20 @@ namespace Slic3r { % port_name_ % cmd; - auto data = boost::asio::buffer(cmd + newline_); + send_buffer_ = cmd + newline_; + + set_deadline_in(write_timeout_); boost::asio::async_write( socket_, - data, + boost::asio::buffer(send_buffer_), boost::bind(&TCPConsole::handle_write, this, _1, _2) ); } void TCPConsole::wait_next_line() { + set_deadline_in(read_timeout_); boost::asio::async_read_until( socket_, recv_buffer_, @@ -145,6 +148,7 @@ namespace Slic3r { io_context_.stop(); } else { + is_connected_ = true; BOOST_LOG_TRIVIAL(info) << boost::format("TCPConsole: connected to %1%:%2%") % host_name_ % port_name_; @@ -154,10 +158,23 @@ namespace Slic3r { } } + void TCPConsole::set_deadline_in(boost::chrono::steady_clock::duration d) + { + deadline_ = boost::chrono::steady_clock::now() + d; + } + bool TCPConsole::is_deadline_over() + { + return deadline_ < boost::chrono::steady_clock::now(); + } + bool TCPConsole::run_queue() { + auto now = boost::chrono::steady_clock::now(); try { - // TODO: Add more resets and initializations after previous run + // 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_); @@ -165,16 +182,21 @@ namespace Slic3r { boost::bind(&TCPConsole::handle_connect, this, _1) ); - // TODO: Add error and timeout processing - io_context_.restart(); - while (!io_context_.stopped()) { - BOOST_LOG_TRIVIAL(debug) << ".\n"; + // 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(); diff --git a/src/slic3r/Utils/TCPConsole.hpp b/src/slic3r/Utils/TCPConsole.hpp index 82d850299..507325db6 100644 --- a/src/slic3r/Utils/TCPConsole.hpp +++ b/src/slic3r/Utils/TCPConsole.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -19,6 +20,15 @@ namespace Slic3r { TCPConsole(const std::string& host_name, const std::string& port_name); ~TCPConsole() {} + void set_defaults() + { + newline_ = "\n"; + done_string_ = "ok"; + connect_timeout_ = boost::chrono::milliseconds(5000); + write_timeout_ = boost::chrono::milliseconds(10000); + read_timeout_ = boost::chrono::milliseconds(10000); + } + void set_line_delimiter(const std::string& newline) { newline_ = newline; } @@ -52,10 +62,16 @@ namespace Slic3r { void wait_next_line(); std::string extract_next_line(); + void set_deadline_in(boost::chrono::steady_clock::duration); + bool is_deadline_over(); + std::string host_name_; std::string port_name_; std::string newline_; std::string done_string_; + boost::chrono::steady_clock::duration connect_timeout_; + boost::chrono::steady_clock::duration write_timeout_; + boost::chrono::steady_clock::duration read_timeout_; std::list cmd_queue_; @@ -63,8 +79,11 @@ namespace Slic3r { tcp::resolver resolver_; tcp::socket socket_; boost::asio::streambuf recv_buffer_; + std::string send_buffer_; + bool is_connected_; boost::system::error_code error_code_; + boost::chrono::steady_clock::time_point deadline_; }; } // Utils From a0f0f329c858b2ef7ddce1abcf88e5fe31521c2c Mon Sep 17 00:00:00 2001 From: Sergey Kovalev Date: Wed, 24 Nov 2021 00:18:55 +0700 Subject: [PATCH 07/12] Fix chrono --- src/slic3r/Utils/TCPConsole.cpp | 8 ++++---- src/slic3r/Utils/TCPConsole.hpp | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/slic3r/Utils/TCPConsole.cpp b/src/slic3r/Utils/TCPConsole.cpp index edbfd8cde..bb834bb5f 100644 --- a/src/slic3r/Utils/TCPConsole.cpp +++ b/src/slic3r/Utils/TCPConsole.cpp @@ -158,18 +158,18 @@ namespace Slic3r { } } - void TCPConsole::set_deadline_in(boost::chrono::steady_clock::duration d) + void TCPConsole::set_deadline_in(std::chrono::steady_clock::duration d) { - deadline_ = boost::chrono::steady_clock::now() + d; + deadline_ = std::chrono::steady_clock::now() + d; } bool TCPConsole::is_deadline_over() { - return deadline_ < boost::chrono::steady_clock::now(); + return deadline_ < std::chrono::steady_clock::now(); } bool TCPConsole::run_queue() { - auto now = boost::chrono::steady_clock::now(); + auto now = std::chrono::steady_clock::now(); try { // TODO: Add more resets and initializations after previous run (reset() method?..) set_deadline_in(connect_timeout_); diff --git a/src/slic3r/Utils/TCPConsole.hpp b/src/slic3r/Utils/TCPConsole.hpp index 507325db6..20472750b 100644 --- a/src/slic3r/Utils/TCPConsole.hpp +++ b/src/slic3r/Utils/TCPConsole.hpp @@ -24,9 +24,9 @@ namespace Slic3r { { newline_ = "\n"; done_string_ = "ok"; - connect_timeout_ = boost::chrono::milliseconds(5000); - write_timeout_ = boost::chrono::milliseconds(10000); - read_timeout_ = boost::chrono::milliseconds(10000); + connect_timeout_ = std::chrono::milliseconds(5000); + write_timeout_ = std::chrono::milliseconds(10000); + read_timeout_ = std::chrono::milliseconds(10000); } void set_line_delimiter(const std::string& newline) { @@ -62,16 +62,16 @@ namespace Slic3r { void wait_next_line(); std::string extract_next_line(); - void set_deadline_in(boost::chrono::steady_clock::duration); + void set_deadline_in(std::chrono::steady_clock::duration); bool is_deadline_over(); std::string host_name_; std::string port_name_; std::string newline_; std::string done_string_; - boost::chrono::steady_clock::duration connect_timeout_; - boost::chrono::steady_clock::duration write_timeout_; - boost::chrono::steady_clock::duration read_timeout_; + std::chrono::steady_clock::duration connect_timeout_; + std::chrono::steady_clock::duration write_timeout_; + std::chrono::steady_clock::duration read_timeout_; std::list cmd_queue_; @@ -83,7 +83,7 @@ namespace Slic3r { bool is_connected_; boost::system::error_code error_code_; - boost::chrono::steady_clock::time_point deadline_; + std::chrono::steady_clock::time_point deadline_; }; } // Utils From 5fe529ffbc8ff6034f16f7520423c58c56517bb3 Mon Sep 17 00:00:00 2001 From: Sergey Kovalev Date: Wed, 24 Nov 2021 00:49:53 +0700 Subject: [PATCH 08/12] Add missing mapping --- src/libslic3r/PrintConfig.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 85cae5c1b..e37edec84 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -72,7 +72,8 @@ static t_config_enum_values s_keys_map_PrintHostType { { "duet", htDuet }, { "flashair", htFlashAir }, { "astrobox", htAstroBox }, - { "repetier", htRepetier } + { "repetier", htRepetier }, + { "mks", htMKS } }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PrintHostType) From e7cc12b2c9181951fa442117096e3281298e50b7 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 29 Nov 2021 15:46:25 +0100 Subject: [PATCH 09/12] Fix of output file dialog extension template for OSX: When opening a "Save" file dialog with a default output file name, OSX file dialog strips the provided extension and replaces it with the default extension. This causes issues with a custom file output template, where a non-default file extension is specified. This commit changes the function to generate file dialog templates so that if a default extension is provided, it is emitted as first into the extension template. Fixes Post Processing passes wrong name #7190 --- src/slic3r/GUI/GUI_App.cpp | 104 +++++++++++++++++++++++++++---------- src/slic3r/GUI/GUI_App.hpp | 4 +- src/slic3r/GUI/Plater.cpp | 2 +- 3 files changed, 79 insertions(+), 31 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 80c3709cf..4253f26c2 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -97,6 +98,8 @@ #include #endif +using namespace std::literals; + namespace Slic3r { namespace GUI { @@ -471,39 +474,86 @@ static bool run_updater_win() } #endif //_WIN32 +struct FileWildcards { + std::string_view title; + std::vector file_extensions; +}; + +static const FileWildcards file_wildcards_by_type[FT_SIZE] = { + /* FT_STL */ { "STL files"sv, { ".stl"sv } }, + /* FT_OBJ */ { "OBJ files"sv, { ".obj"sv } }, + /* FT_AMF */ { "AMF files"sv, { ".amf"sv, ".zip.amf"sv, ".xml"sv } }, + /* FT_3MF */ { "3MF files"sv, { ".3mf"sv } }, + /* FT_GCODE */ { "G-code files"sv, { ".gcode"sv, ".gco"sv, ".g"sv, ".ngc"sv } }, + /* FT_MODEL */ { "Known files"sv, { ".stl"sv, ".obj"sv, ".3mf"sv, ".amf"sv, ".zip.amf"sv, ".xml"sv } }, + /* FT_PROJECT */ { "Project files"sv, { ".3mf"sv, ".amf"sv, ".zip.amf"sv } }, + /* FT_GALLERY */ { "Known files"sv, { ".stl"sv, ".obj"sv } }, + + /* FT_INI */ { "INI files"sv, { ".ini"sv } }, + /* FT_SVG */ { "SVG files"sv, { ".svg"sv } }, + + /* FT_TEX */ { "Texture"sv, { ".png"sv, ".svg"sv } }, + + /* FT_SL1 */ { "Masked SLA files"sv, { ".sl1"sv, ".sl1s"sv } }, +}; + +// This function produces a Win32 file dialog file template mask to be consumed by wxWidgets on all platforms. +// The function accepts a custom extension parameter. If the parameter is provided, the custom extension +// will be added as a fist to the list. This is important for a "file save" dialog on OSX, which strips +// an extension from the provided initial file name and substitutes it with the default extension (the first one in the template). wxString file_wildcards(FileType file_type, const std::string &custom_extension) { - static const std::string defaults[FT_SIZE] = { - /* FT_STL */ "STL files (*.stl)|*.stl;*.STL", - /* FT_OBJ */ "OBJ files (*.obj)|*.obj;*.OBJ", - /* FT_AMF */ "AMF files (*.amf)|*.zip.amf;*.amf;*.AMF;*.xml;*.XML", - /* FT_3MF */ "3MF files (*.3mf)|*.3mf;*.3MF;", - /* FT_GCODE */ "G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC", - /* FT_MODEL */ "Known files (*.stl, *.obj, *.amf, *.xml, *.3mf, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.3mf;*.3MF", - /* FT_PROJECT */ "Project files (*.3mf, *.amf)|*.3mf;*.3MF;*.amf;*.AMF", - /* FT_GALLERY */ "Known files (*.stl, *.obj)|*.stl;*.STL;*.obj;*.OBJ", + const FileWildcards data = file_wildcards_by_type[file_type]; + std::string title; + std::string mask; + std::string custom_ext_lower; - /* FT_INI */ "INI files (*.ini)|*.ini;*.INI", - /* FT_SVG */ "SVG files (*.svg)|*.svg;*.SVG", - - /* FT_TEX */ "Texture (*.png, *.svg)|*.png;*.PNG;*.svg;*.SVG", - - /* FT_SL1 */ "Masked SLA files (*.sl1, *.sl1s)|*.sl1;*.SL1;*.sl1s;*.SL1S", - // Workaround for OSX file picker, for some reason it always saves with the 1st extension. - /* FT_SL1S */ "Masked SLA files (*.sl1s, *.sl1)|*.sl1s;*.SL1S;*.sl1;*.SL1", - }; - - std::string out = defaults[file_type]; if (! custom_extension.empty()) { - // Find the custom extension in the template. - if (out.find(std::string("*") + custom_extension + ",") == std::string::npos && out.find(std::string("*") + custom_extension + ")") == std::string::npos) { - // The custom extension was not found in the template. - // Append the custom extension to the wildcards, so that the file dialog would not add the default extension to it. - boost::replace_first(out, ")|", std::string(", *") + custom_extension + ")|"); - out += std::string(";*") + custom_extension; + // Generate an extension into the title mask and into the list of extensions. + custom_ext_lower = custom_extension; + boost::to_lower(custom_ext_lower); + std::string custom_ext_upper = custom_extension; + boost::to_upper(custom_ext_upper); + if (custom_ext_lower == custom_extension) { + // Add a lower case version. + title = std::string("*") + custom_ext_lower; + mask = title; + // Add an upper case version. + mask += ";*"; + mask += custom_ext_upper; + } else if (custom_ext_upper == custom_extension) { + // Add an upper case version. + title = std::string("*") + custom_ext_upper; + mask = title; + // Add a lower case version. + mask += ";*"; + mask += custom_ext_lower; + } else { + // Add the mixed case version only. + title = std::string("*") + custom_extension; + mask = title; } } - return from_u8(out); + + for (const std::string_view ext : data.file_extensions) + // Only add an extension if it was not added first as the custom extension. + if (ext != custom_ext_lower) { + if (title.empty()) { + title = "*"; + title += ext; + mask = title; + } else { + title += ", *"; + title += ext; + mask += ";*"; + mask += ext; + } + mask += ";*"; + std::string ext_upper{ ext }; + boost::to_upper(ext_upper); + mask += ext_upper; + } + return GUI::format("%s (%s)|%s", data.title, title, mask); } static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); } diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 95ac8c025..9e8e913f6 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -66,13 +66,11 @@ enum FileType FT_TEX, FT_SL1, - // Workaround for OSX file picker, for some reason it always saves with the 1st extension. - FT_SL1S, FT_SIZE, }; -extern wxString file_wildcards(FileType file_type, const std::string &custom_extension = std::string()); +extern wxString file_wildcards(FileType file_type, const std::string &custom_extension = std::string{}); enum ConfigMenuIDs { ConfigMenuWizard, diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 4e60f09e2..273bc5c55 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5721,7 +5721,7 @@ void Plater::export_gcode(bool prefer_removable) wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _L("Save G-code file as:") : _L("Save SL1 / SL1S file as:"), start_dir, from_path(default_output_file.filename()), - GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : boost::iequals(ext, ".sl1s") ? FT_SL1S : FT_SL1, ext), + GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : FT_SL1, ext), wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); if (dlg.ShowModal() == wxID_OK) { From 6883919a7bbabf68830c19d5cc50f05367a2c604 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 29 Nov 2021 16:33:44 +0100 Subject: [PATCH 10/12] Fixed handling of "clip_multipart_objects". This was a regression of 2.4.0-alpha1 wrt. 2.3.3, where handling of multiple volumes was refactored. As a result, handling multi-part objects with clip_multipart_objects disabled was broken. With clip_multipart_objects disabled, the multi-part objects no more clip one another and it is sole responsibility of the user to handle overlaps. Fixes "add part" error - PrusaSlicer 2.4.0 b2 #7366 --- src/libslic3r/PrintObjectSlice.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 84b212938..4b91714e5 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -379,11 +379,7 @@ static std::vector> slices_to_regions( int j = i; bool merged = false; ExPolygons &expolygons = temp_slices[i].expolygons; - for (++ j; - j < int(temp_slices.size()) && - temp_slices[i].region_id == temp_slices[j].region_id && - (clip_multipart_objects || temp_slices[i].volume_id == temp_slices[j].volume_id); - ++ j) + for (++ j; j < int(temp_slices.size()) && temp_slices[i].region_id == temp_slices[j].region_id; ++ j) if (ExPolygons &expolygons2 = temp_slices[j].expolygons; ! expolygons2.empty()) { if (expolygons.empty()) { expolygons = std::move(expolygons2); @@ -392,7 +388,10 @@ static std::vector> slices_to_regions( merged = true; } } - if (merged) + // Don't unite the regions if ! clip_multipart_objects. In that case it is user's responsibility + // to handle region overlaps. Indeed, one may intentionally let the regions overlap to produce crossing perimeters + // for example. + if (merged && clip_multipart_objects) expolygons = closing_ex(expolygons, float(scale_(EPSILON))); slices_by_region[temp_slices[i].region_id][z_idx] = std::move(expolygons); i = j; From 56d5a340ce4b1b88d10c9e5571832516eeb025aa Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 29 Nov 2021 18:12:35 +0100 Subject: [PATCH 11/12] Follow up to the MKS merge: Reformatted for code conformity. Changed the xxx_ member variables to m_xxx Replaced std::list with std::deque --- src/slic3r/Utils/MKS.cpp | 196 +++++++++-------- src/slic3r/Utils/MKS.hpp | 46 ++-- src/slic3r/Utils/TCPConsole.cpp | 365 +++++++++++++++----------------- src/slic3r/Utils/TCPConsole.hpp | 127 ++++++----- 4 files changed, 354 insertions(+), 380 deletions(-) diff --git a/src/slic3r/Utils/MKS.cpp b/src/slic3r/Utils/MKS.cpp index 636a0b3b2..2188a8f68 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 feddd17d8..7564b7f81 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 bb834bb5f..a4f4bc21e 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 20472750b..7c0e1d290 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 From f5cf3cb81d5fa306c96e71e9a3b0f5e21c53ebf7 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 29 Nov 2021 21:00:43 +0100 Subject: [PATCH 12/12] MSW specific: Hide "Use system menu for application" option in Preferences for system older then Windows 10 (related to the fix of #7355 - PrusaSlicer 2.4.0-beta-2+win64 will not start on Windows 7 64-bit ) + Updated URL_HASH SHA256 in wxWidgets.cmake in respect to the 51c824019e98fc97b0bcdd4d9f4ed4cb523a7cac in wxWidgets-v3.1.4-patched --- deps/wxWidgets/wxWidgets.cmake | 2 +- src/slic3r/GUI/Preferences.cpp | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake index c993d8948..73d841014 100644 --- a/deps/wxWidgets/wxWidgets.cmake +++ b/deps/wxWidgets/wxWidgets.cmake @@ -13,7 +13,7 @@ prusaslicer_add_cmake_project(wxWidgets # GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" # GIT_TAG tm_cross_compile #${_wx_git_tag} URL https://github.com/prusa3d/wxWidgets/archive/refs/heads/v3.1.4-patched.zip - URL_HASH SHA256=21ed12eb5c215b00999f0374af652be0a6f785df10d18d0dfec8d81ed4abaea3 + URL_HASH SHA256=ed36a2159c781cce07b06378664e683ebd8cb2f51914aba9acd3bfca3d63d7d3 DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG CMAKE_ARGS -DwxBUILD_PRECOMP=ON diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 4359d600b..d6422bbad 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -481,14 +481,18 @@ void PreferencesDialog::build(size_t selected_tab) option = Option(def, "dark_color_mode"); m_optgroup_dark_mode->append_single_option_line(option); - def.label = L("Use system menu for application"); - def.type = coBool; - def.tooltip = L("If enabled, application will use the standart Windows system menu,\n" - "but on some combination od display scales it can look ugly. " - "If disabled, old UI will be used."); - def.set_default_value(new ConfigOptionBool{ app_config->get("sys_menu_enabled") == "1" }); - option = Option(def, "sys_menu_enabled"); - m_optgroup_dark_mode->append_single_option_line(option); + if (wxPlatformInfo::Get().GetOSMajorVersion() >= 10) // Use system menu just for Window newer then Windows 10 + // Use menu with ownerdrawn items by default on systems older then Windows 10 + { + def.label = L("Use system menu for application"); + def.type = coBool; + def.tooltip = L("If enabled, application will use the standart Windows system menu,\n" + "but on some combination od display scales it can look ugly. " + "If disabled, old UI will be used."); + def.set_default_value(new ConfigOptionBool{ app_config->get("sys_menu_enabled") == "1" }); + option = Option(def, "sys_menu_enabled"); + m_optgroup_dark_mode->append_single_option_line(option); + } activate_options_tab(m_optgroup_dark_mode); }