From 735d179f1a8acc97daaf1c8fd0fdcdd97228a86c Mon Sep 17 00:00:00 2001 From: Rob Duplock Date: Fri, 27 Sep 2024 17:06:07 +1000 Subject: [PATCH 1/2] Add support for creality print --- src/libslic3r/PrintConfig.cpp | 3 + src/libslic3r/PrintConfig.hpp | 2 +- src/slic3r/CMakeLists.txt | 2 + src/slic3r/Utils/CrealityPrint.cpp | 228 +++++++++++++++++++++++++++++ src/slic3r/Utils/CrealityPrint.hpp | 48 ++++++ src/slic3r/Utils/ESP3D.cpp | 2 +- src/slic3r/Utils/PrintHost.cpp | 2 + 7 files changed, 285 insertions(+), 2 deletions(-) create mode 100644 src/slic3r/Utils/CrealityPrint.cpp create mode 100644 src/slic3r/Utils/CrealityPrint.hpp diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 33941656dae..11bb9aa3252 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -78,6 +78,7 @@ static t_config_enum_values s_keys_map_PrintHostType { { "prusalink", htPrusaLink }, { "prusaconnect", htPrusaConnect }, { "octoprint", htOctoPrint }, + { "crealityprint", htCrealityPrint }, { "duet", htDuet }, { "flashair", htFlashAir }, { "astrobox", htAstroBox }, @@ -3399,6 +3400,7 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("repetier"); def->enum_values.push_back("mks"); def->enum_values.push_back("esp3d"); + def->enum_values.push_back("crealityprint"); def->enum_values.push_back("obico"); def->enum_values.push_back("flashforge"); def->enum_values.push_back("simplyprint"); @@ -3411,6 +3413,7 @@ void PrintConfigDef::init_fff_params() def->enum_labels.push_back("Repetier"); def->enum_labels.push_back("MKS"); def->enum_labels.push_back("ESP3D"); + def->enum_labels.push_back("CrealityPrint"); def->enum_labels.push_back("Obico"); def->enum_labels.push_back("Flashforge"); def->enum_labels.push_back("SimplyPrint"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 0b418f268e1..e5255d35374 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -42,7 +42,7 @@ enum class FuzzySkinType { }; enum PrintHostType { - htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htESP3D, htObico, htFlashforge, htSimplyPrint + htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htESP3D, htCrealityPrint, htObico, htFlashforge, htSimplyPrint }; enum AuthorizationType { diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 87d93965784..0c54d835ada 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -535,6 +535,8 @@ set(SLIC3R_GUI_SOURCES Utils/MKS.hpp Utils/ESP3D.cpp Utils/ESP3D.hpp + Utils/CrealityPrint.cpp + Utils/CrealityPrint.hpp Utils/WxFontUtils.cpp Utils/WxFontUtils.hpp Utils/Duet.cpp diff --git a/src/slic3r/Utils/CrealityPrint.cpp b/src/slic3r/Utils/CrealityPrint.cpp new file mode 100644 index 00000000000..cc112c9518c --- /dev/null +++ b/src/slic3r/Utils/CrealityPrint.cpp @@ -0,0 +1,228 @@ +#include "CrealityPrint.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/I18N.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/format.hpp" +#include "Http.hpp" +#include "libslic3r/AppConfig.hpp" +#include "Bonjour.hpp" +#include "slic3r/GUI/BonjourDialog.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +using json = nlohmann::json; +using std::to_string; + +namespace beast = boost::beast; +namespace http = beast::http; +namespace websocket = beast::websocket; +namespace net = boost::asio; +using tcp = boost::asio::ip::tcp; + +namespace fs = boost::filesystem; +namespace pt = boost::property_tree; + +namespace Slic3r { + +CrealityPrint::CrealityPrint(DynamicPrintConfig* config) : + m_host(config->opt_string("print_host")), + m_web_ui(config->opt_string("print_host_webui")), + m_cafile(config->opt_string("printhost_cafile")), + m_port(config->opt_string("printhost_port")), + m_apikey(config->opt_string("printhost_apikey")), + m_ssl_revoke_best_effort(config->opt_bool("printhost_ssl_ignore_revoke")) +{} + +const char* CrealityPrint::get_name() const { return "CrealityPrint"; } + +std::string CrealityPrint::get_host() const { + return m_host; +} +void CrealityPrint::set_auth(Http& http) const +{ + http.header("Authorization", "Bearer " + m_apikey); + if (!m_cafile.empty()) { + http.ca_file(m_cafile); + } +} + +wxString CrealityPrint::get_test_ok_msg() const { return _(L("Connected to CrealityPrint successfully!")); } + +wxString CrealityPrint::get_test_failed_msg(wxString& msg) const +{ + return GUI::format_wxstr("%s: %s", _L("Could not connect to CrealityPrint"), msg.Truncate(256)); +} + +bool CrealityPrint::test(wxString& msg) const +{ + bool res = true; + const char* name = get_name(); + auto url = make_url("info"); + + BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url; + // Here we do not have to add custom "Host" header - the url contains host filled by user and libCurl will set the header by itself. + auto http = Http::get(std::move(url)); + set_auth(http); + http.on_error([&](std::string body, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version: %2%, HTTP %3%, body: `%4%`") % name % error % status % + body; + res = false; + msg = format_error(body, error, status); + }) + .on_complete([&, this](std::string body, unsigned) { + BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got version: %2%") % name % body; + }) +#ifdef WIN32 + .ssl_revoke_best_effort(m_ssl_revoke_best_effort) + .on_ip_resolve([&](std::string address) { + // Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail. + // Remember resolved address to be reused at successive REST API call. + msg = GUI::from_u8(address); + }) +#endif // WIN32 + .perform_sync(); + + return res; +} + +PrintHostPostUploadActions CrealityPrint::get_post_upload_actions() const { + return PrintHostPostUploadAction::StartPrint; +} + +bool CrealityPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const +{ + const char* name = get_name(); + const auto upload_filename = upload_data.upload_path.filename(); + const auto upload_parent_path = upload_data.upload_path.parent_path(); + wxString test_msg; + if (!test(test_msg)) { + error_fn(std::move(test_msg)); + return false; + } + + bool res = true; + auto url = make_url("upload/" + upload_filename.string()); + + auto http = Http::post(url); // std::move(url)); + set_auth(http); + http.form_add("path", upload_parent_path.string()) + .form_add_file("file", upload_data.source_path.string(), upload_filename.string()) + + .on_complete([&](std::string body, unsigned status) { + BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body; + + if (upload_data.post_action == PrintHostPostUploadAction::StartPrint) { + start_print(upload_filename.string()); + } + }) + .on_error([&](std::string body, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file to %2%: %3%, HTTP %4%, body: `%5%`") % name % url % 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) << name << ": Upload canceled"; + res = false; + } + }) +#ifdef WIN32 + .ssl_revoke_best_effort(m_ssl_revoke_best_effort) +#endif + .perform_sync(); + return res; +} + +std::string CrealityPrint::make_url(const std::string &path) const +{ + if (m_host.find("http://") == 0 || m_host.find("https://") == 0) { + if (m_host.back() == '/') { + return (boost::format("%1%%2%") % m_host % path).str(); + } else { + return (boost::format("%1%/%2%") % m_host % path).str(); + } + } else { + return (boost::format("http://%1%/%2%") % m_host % path).str(); + } +} + +std::string CrealityPrint::start_print(const std::string &filename) const +{ + try { + std::string host = m_host; + auto const port = "9999"; + + json j2 = { + { "method", "set" }, + { + "params", { + { "opGcodeFile", "printprt:/usr/data/printer_data/gcodes/" + filename } + } + } + }; + + net::io_context ioc; + + tcp::resolver resolver{ioc}; + websocket::stream ws{ioc}; + + auto const results = resolver.resolve(host, port); + + auto ep = net::connect(ws.next_layer(), results); + + host += ':' + std::to_string(ep.port()); + + ws.set_option(websocket::stream_base::decorator( + [](websocket::request_type& req) + { + req.set(http::field::user_agent, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-client-coro"); + })); + + ws.handshake(host, "/"); + + ws.write(net::buffer(to_string(j2))); + + beast::flat_buffer buffer; + + ws.read(buffer); + + ws.close(websocket::close_code::normal); + } catch(std::exception const& e) { + std::cerr << "Error: " << e.what() << std::endl; + return "false"; + } + + return "true"; +} + +} diff --git a/src/slic3r/Utils/CrealityPrint.hpp b/src/slic3r/Utils/CrealityPrint.hpp new file mode 100644 index 00000000000..888b260492c --- /dev/null +++ b/src/slic3r/Utils/CrealityPrint.hpp @@ -0,0 +1,48 @@ +#ifndef slic3r_CrealityPrint_hpp_ +#define slic3r_CrealityPrint_hpp_ + +#include +#include +#include +#include + +#include "PrintHost.hpp" +#include "libslic3r/PrintConfig.hpp" + +namespace Slic3r { + +class DynamicPrintConfig; +class Http; +class CrealityPrint : public PrintHost +{ +public: + CrealityPrint(DynamicPrintConfig* config); + ~CrealityPrint() override = default; + + const char* get_name() const override; + virtual bool can_test() const { return true; }; + std::string get_host() const override; + bool has_auto_discovery() const override { return true; } + + wxString get_test_ok_msg() const override; + wxString get_test_failed_msg(wxString& msg) const override; + virtual bool test(wxString& curl_msg) const override; + PrintHostPostUploadActions get_post_upload_actions() const; + bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const override; + +protected: + virtual void set_auth(Http& http) const; +private: + std::string m_host; + std::string m_port; + std::string m_apikey; + std::string m_cafile; + std::string m_web_ui; + bool m_ssl_revoke_best_effort; + + std::string make_url(const std::string& path) const; + std::string start_print(const std::string& path) const; +}; +} // namespace Slic3r + +#endif \ No newline at end of file diff --git a/src/slic3r/Utils/ESP3D.cpp b/src/slic3r/Utils/ESP3D.cpp index 531e9d08e90..ff33a61a689 100644 --- a/src/slic3r/Utils/ESP3D.cpp +++ b/src/slic3r/Utils/ESP3D.cpp @@ -71,7 +71,7 @@ bool ESP3D::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn http.header("Connection", "keep-alive") .form_add_file("file", upload_data.source_path, short_name) .on_complete([&](std::string body, unsigned status) { - // check for OK + // check for OK if (upload_data.post_action == PrintHostPostUploadAction::StartPrint) { wxString errormsg; res = start_print(errormsg, short_name); diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index f88dceb14a8..256b7641330 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -20,6 +20,7 @@ #include "Repetier.hpp" #include "MKS.hpp" #include "ESP3D.hpp" +#include "CrealityPrint.hpp" #include "../GUI/PrintHostDialogs.hpp" #include "../GUI/MainFrame.hpp" #include "Obico.hpp" @@ -60,6 +61,7 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config) case htPrusaConnect: return new PrusaConnect(config); case htMKS: return new MKS(config); case htESP3D: return new ESP3D(config); + case htCrealityPrint: return new CrealityPrint(config); case htObico: return new Obico(config); case htFlashforge: return new Flashforge(config); case htSimplyPrint: return new SimplyPrint(config); From 6dea7b444e428cc4f76d193facebf1b4f2869756 Mon Sep 17 00:00:00 2001 From: Rob Duplock Date: Fri, 27 Sep 2024 22:06:48 +1000 Subject: [PATCH 2/2] Fixed bug with spaces in filename --- src/slic3r/Utils/CrealityPrint.cpp | 18 ++++++++++++------ src/slic3r/Utils/CrealityPrint.hpp | 3 ++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/slic3r/Utils/CrealityPrint.cpp b/src/slic3r/Utils/CrealityPrint.cpp index cc112c9518c..1df8c72d4b5 100644 --- a/src/slic3r/Utils/CrealityPrint.cpp +++ b/src/slic3r/Utils/CrealityPrint.cpp @@ -58,7 +58,7 @@ CrealityPrint::CrealityPrint(DynamicPrintConfig* config) : m_ssl_revoke_best_effort(config->opt_bool("printhost_ssl_ignore_revoke")) {} -const char* CrealityPrint::get_name() const { return "CrealityPrint"; } +const char* CrealityPrint::get_name() const { return "Creality Print"; } std::string CrealityPrint::get_host() const { return m_host; @@ -126,7 +126,7 @@ bool CrealityPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, } bool res = true; - auto url = make_url("upload/" + upload_filename.string()); + auto url = make_url("upload/" + safe_filename(upload_filename.string())); auto http = Http::post(url); // std::move(url)); set_auth(http); @@ -137,7 +137,7 @@ bool CrealityPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body; if (upload_data.post_action == PrintHostPostUploadAction::StartPrint) { - start_print(upload_filename.string()); + start_print(safe_filename(upload_filename.string())); } }) .on_error([&](std::string body, std::string error, unsigned status) { @@ -174,7 +174,15 @@ std::string CrealityPrint::make_url(const std::string &path) const } } -std::string CrealityPrint::start_print(const std::string &filename) const +std::string CrealityPrint::safe_filename(const std::string &filename) const +{ + std::string safe_filename = filename; + std::replace(safe_filename.begin(), safe_filename.end(), ' ', '_'); + + return safe_filename; +} + +void CrealityPrint::start_print(const std::string &filename) const { try { std::string host = m_host; @@ -219,10 +227,8 @@ std::string CrealityPrint::start_print(const std::string &filename) const ws.close(websocket::close_code::normal); } catch(std::exception const& e) { std::cerr << "Error: " << e.what() << std::endl; - return "false"; } - return "true"; } } diff --git a/src/slic3r/Utils/CrealityPrint.hpp b/src/slic3r/Utils/CrealityPrint.hpp index 888b260492c..14d133277f9 100644 --- a/src/slic3r/Utils/CrealityPrint.hpp +++ b/src/slic3r/Utils/CrealityPrint.hpp @@ -41,7 +41,8 @@ class CrealityPrint : public PrintHost bool m_ssl_revoke_best_effort; std::string make_url(const std::string& path) const; - std::string start_print(const std::string& path) const; + void start_print(const std::string& path) const; + std::string safe_filename(const std::string& filename) const; }; } // namespace Slic3r