diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 3380c396230..c122b547388 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -103,6 +103,7 @@ static t_config_enum_values s_keys_map_PrintHostType { { "astrobox", htAstroBox }, { "repetier", htRepetier }, { "mks", htMKS }, + { "esp3d", htESP3D }, { "obico", htObico }, { "flashforge", htFlashforge }, { "simplyprint", htSimplyPrint }, @@ -3148,6 +3149,7 @@ def = this->add("filament_loading_speed", coFloats); def->enum_values.push_back("astrobox"); def->enum_values.push_back("repetier"); def->enum_values.push_back("mks"); + def->enum_values.push_back("esp3d"); def->enum_values.push_back("obico"); def->enum_values.push_back("flashforge"); def->enum_values.push_back("simplyprint"); @@ -3159,6 +3161,7 @@ def = this->add("filament_loading_speed", coFloats); def->enum_labels.push_back("AstroBox"); def->enum_labels.push_back("Repetier"); def->enum_labels.push_back("MKS"); + def->enum_labels.push_back("ESP3D"); 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 836d326e7d5..64a360e74dc 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -59,7 +59,7 @@ enum class FuzzySkinType { }; enum PrintHostType { - htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htObico, htFlashforge, htSimplyPrint + htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htESP3D, htObico, htFlashforge, htSimplyPrint }; enum AuthorizationType { diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index d03a0c20dc7..d2242c9fff0 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -542,6 +542,8 @@ set(SLIC3R_GUI_SOURCES Utils/SerialMessage.hpp Utils/MKS.cpp Utils/MKS.hpp + Utils/ESP3D.cpp + Utils/ESP3D.hpp Utils/WxFontUtils.cpp Utils/WxFontUtils.hpp Utils/Duet.cpp diff --git a/src/slic3r/Utils/ESP3D.cpp b/src/slic3r/Utils/ESP3D.cpp new file mode 100644 index 00000000000..57b917e3a19 --- /dev/null +++ b/src/slic3r/Utils/ESP3D.cpp @@ -0,0 +1,183 @@ +#include "ESP3D.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" +#include "SerialMessage.hpp" +#include "SerialMessageType.hpp" + +namespace fs = boost::filesystem; +namespace pt = boost::property_tree; + +namespace Slic3r { + +ESP3D::ESP3D(DynamicPrintConfig* config) : m_host(config->opt_string("print_host")), m_console_port("8888") {} + +const char* ESP3D::get_name() const { return "ESP3D"; } + +bool ESP3D::test(wxString& msg) const +{ + bool ret = false; + std::string url_test = format_command("/command", "plain", "M105"); + auto http = Http::get(url_test); + http.on_complete([&](std::string body, unsigned status) { + // check for OK + ret = true; + msg = get_test_ok_msg(); + }) + .on_error([&](std::string body, std::string error, unsigned status) { + ret = false; + msg = format_error(body , error, status); + }) + .perform_sync(); + return ret; +} + +wxString ESP3D::get_test_ok_msg() const { return _(L("Connection to ESP3D works correctly.")); } + +wxString ESP3D::get_test_failed_msg(wxString& msg) const +{ + return GUI::from_u8((boost::format("%s: %s") % _utf8(L("Could not connect to ESP3D")) % std::string(msg.ToUTF8())).str()); +} + +bool ESP3D::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const +{ + std::string short_name = get_short_name(upload_data.upload_path.string()); + bool res = false; + + auto http = Http::post(std::move((boost::format("http://%1%/upload_serial") % m_host).str())); + 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 + if (upload_data.post_action == PrintHostPostUploadAction::StartPrint) { + wxString errormsg; + res = start_print(errormsg, short_name); + if (!res) { + error_fn(std::move(errormsg)); + } + } + }) + .on_error([&](std::string body, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(error) << boost::format("ESP3D: 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) { + // workaround: + // progress bar disappears before .on_complete + // ESP3D can be super slow, the user could close slicer before upload completes & M24 is sent because no progress bar + // M24 can only be sent after .on_complete + Http::Progress prog = std::move(progress); + prog.ulnow -= 1; + prorgess_fn(std::move(prog), cancel); + if (cancel) { + // Upload was canceled + BOOST_LOG_TRIVIAL(info) << "ESP3D: Upload canceled"; + res = false; + } + }) + .perform_sync(); + + return res; +} + +bool ESP3D::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. + // ESP3D also locks the serial during SD transfer, this is safer + std::this_thread::sleep_for(std::chrono::milliseconds(1500)); + + bool ret = false; + auto select_file = (boost::format("%1% %2%") % "M23" % filename).str(); + auto select = format_command("/command", "plain", Http::url_encode(select_file)); + auto http_sel = Http::get(select); + http_sel + .on_complete([&](std::string body, unsigned status) { + ret = true; + }) + .on_error([&](std::string body, std::string error, unsigned status) { + // error sending M23 + ret = false; + msg = (wxString::FromUTF8(error)); + }) + .perform_sync(); + + if (!ret) + return ret; + + auto start = format_command("/command", "plain", "M24"); + auto http_start = Http::get(start); + http_start + .on_complete([&](std::string body, unsigned status) { + // print kicked off succesfully + ret = true; + }) + .on_error([&](std::string body, std::string error, unsigned status) { + // error sending M24 + ret = false; + msg = (wxString::FromUTF8(error)); + }) + .perform_sync(); + + return ret; +} + +int ESP3D::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); +} + +// ESP3D only accepts 8.3 filenames else it crashes marlin and other undefined behaviour +std::string ESP3D::get_short_name(const std::string& filename) const +{ + std::string shortname = ""; + boost::filesystem::path p(filename); + std::string stem = p.stem().string(); + std::string extension = p.extension().string(); + if (!extension.empty() && extension[0] == '.') { + extension = extension.substr(1); + } + stem = stem.substr(0, 8); + extension = extension.substr(0, 3); + if (!extension.empty()) { + shortname = stem + "." + extension; + } else { + shortname = stem; + } + return shortname; +} + +std::string ESP3D::format_command(const std::string& path, const std::string& arg, const std::string& val) const +{ + return (boost::format("http://%1%%2%?%3%=%4%") % m_host % path % arg % val).str(); +} + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/Utils/ESP3D.hpp b/src/slic3r/Utils/ESP3D.hpp new file mode 100644 index 00000000000..7ac3c66f481 --- /dev/null +++ b/src/slic3r/Utils/ESP3D.hpp @@ -0,0 +1,43 @@ +#ifndef slic3r_ESP3D_hpp_ +#define slic3r_ESP3D_hpp_ + +#include +#include + +#include "PrintHost.hpp" +#include "TCPConsole.hpp" + +namespace Slic3r { +class DynamicPrintConfig; +class Http; + +class ESP3D : public PrintHost +{ +public: + explicit ESP3D(DynamicPrintConfig* config); + ~ESP3D() 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, InfoFn info_fn) const override; + bool has_auto_discovery() const override { return false; } + bool can_test() const override { return true; } + PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; } + std::string get_host() const override { return m_host; } + +private: + std::string m_host; + std::string m_console_port; + + bool start_print(wxString& msg, const std::string& filename) const; + int get_err_code_from_body(const std::string& body) const; + std::string get_short_name(const std::string& filename) const; + std::string format_command(const std::string& path, const std::string& arg, const std::string& val) const; +}; + +} // namespace Slic3r + +#endif diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index cf77d4ff5c9..b6dff850520 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -19,6 +19,7 @@ #include "AstroBox.hpp" #include "Repetier.hpp" #include "MKS.hpp" +#include "ESP3D.hpp" #include "../GUI/PrintHostDialogs.hpp" #include "Obico.hpp" #include "Flashforge.hpp" @@ -57,6 +58,7 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config) case htPrusaLink: return new PrusaLink(config); case htPrusaConnect: return new PrusaConnect(config); case htMKS: return new MKS(config); + case htESP3D: return new ESP3D(config); case htObico: return new Obico(config); case htFlashforge: return new Flashforge(config); case htSimplyPrint: return new SimplyPrint(config);