diff --git a/resources/localization/list.txt b/resources/localization/list.txt index 7f0b67252..93ee2f441 100644 --- a/resources/localization/list.txt +++ b/resources/localization/list.txt @@ -50,6 +50,7 @@ src/slic3r/GUI/ExtruderSequenceDialog.cpp src/slic3r/Utils/Duet.cpp src/slic3r/Utils/OctoPrint.cpp src/slic3r/Utils/FlashAir.cpp +src/slic3r/Utils/AstroBox.cpp src/slic3r/Utils/PresetUpdater.cpp src/slic3r/Utils/FixModelByWin10.cpp src/libslic3r/SLA/SLAPad.cpp diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 82815131e..0835693b2 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1333,9 +1333,11 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("octoprint"); def->enum_values.push_back("duet"); def->enum_values.push_back("flashair"); + def->enum_values.push_back("astrobox"); def->enum_labels.push_back("OctoPrint"); def->enum_labels.push_back("Duet"); def->enum_labels.push_back("FlashAir"); + def->enum_values.push_back("AstroBox"); def->mode = comAdvanced; def->set_default_value(new ConfigOptionEnum(htOctoPrint)); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 5f5a861da..6d70f5408 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -30,7 +30,7 @@ enum GCodeFlavor : unsigned char { }; enum PrintHostType { - htOctoPrint, htDuet, htFlashAir + htOctoPrint, htDuet, htFlashAir, htAstroBox }; enum InfillPattern { @@ -103,6 +103,7 @@ template<> inline const t_config_enum_values& ConfigOptionEnum::g keys_map["octoprint"] = htOctoPrint; keys_map["duet"] = htDuet; keys_map["flashair"] = htFlashAir; + keys_map["astrobox"] = htAstroBox; } return keys_map; } diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 5b55b96b4..9fd846272 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -152,6 +152,8 @@ set(SLIC3R_GUI_SOURCES Utils/Duet.hpp Utils/FlashAir.cpp Utils/FlashAir.hpp + Utils/AstroBox.cpp + Utils/AstroBox.hpp Utils/PrintHost.cpp Utils/PrintHost.hpp Utils/Bonjour.cpp diff --git a/src/slic3r/Utils/AstroBox.cpp b/src/slic3r/Utils/AstroBox.cpp new file mode 100644 index 000000000..ded03ed4c --- /dev/null +++ b/src/slic3r/Utils/AstroBox.cpp @@ -0,0 +1,215 @@ +#include "AstroBox.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "libslic3r/PrintConfig.hpp" +#include "slic3r/GUI/I18N.hpp" +#include "Http.hpp" + + +namespace fs = boost::filesystem; +namespace pt = boost::property_tree; + + +namespace Slic3r { + +AstroBox::AstroBox(DynamicPrintConfig *config) : + host(config->opt_string("print_host")), + apikey(config->opt_string("printhost_apikey")), + cafile(config->opt_string("printhost_cafile")) +{} + +AstroBox::~AstroBox() {} + +const char* AstroBox::get_name() const { return "AstroBox"; } + +bool AstroBox::test(wxString &msg) const +{ + // Since the request is performed synchronously here, + // it is ok to refer to `msg` from within the closure + + const char *name = get_name(); + + bool res = true; + auto url = make_url("api/version"); + + BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url; + + 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; + + try { + std::stringstream ss(body); + pt::ptree ptree; + pt::read_json(ss, ptree); + + if (! ptree.get_optional("api")) { + res = false; + return; + } + + const auto text = ptree.get_optional("text"); + res = validate_version_text(text); + if (! res) { + msg = wxString::Format(_(L("Mismatched type of print host: %s")), text ? *text : "AstroBox"); + } + } + catch (const std::exception &) { + res = false; + msg = "Could not parse server response"; + } + }) + .perform_sync(); + + return res; +} + +wxString AstroBox::get_test_ok_msg () const +{ + return _(L("Connection to AstroBox works correctly.")); +} + +wxString AstroBox::get_test_failed_msg (wxString &msg) const +{ + return wxString::Format("%s: %s\n\n%s", + _(L("Could not connect to AstroBox")), msg, _(L("Note: AstroBox version at least 1.1.0 is required."))); +} + +bool AstroBox::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_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("api/files/local"); + + BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%") + % name + % upload_data.source_path + % url + % upload_filename.string() + % upload_parent_path.string() + % upload_data.start_print; + + auto http = Http::post(std::move(url)); + set_auth(http); + http.form_add("print", upload_data.start_print ? "true" : "false") + .form_add("path", upload_parent_path.string()) // XXX: slashes on windows ??? + .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; + }) + .on_error([&](std::string body, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % 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) << "AstroBox: Upload canceled"; + res = false; + } + }) + .perform_sync(); + + return res; +} + +bool AstroBox::has_auto_discovery() const +{ + return true; +} + +bool AstroBox::can_test() const +{ + return true; +} + +bool AstroBox::can_start_print() const +{ + return true; +} + +bool AstroBox::validate_version_text(const boost::optional &version_text) const +{ + return version_text ? boost::starts_with(*version_text, "AstroBox") : true; +} + +void AstroBox::set_auth(Http &http) const +{ + http.header("X-Api-Key", apikey); + + if (! cafile.empty()) { + http.ca_file(cafile); + } +} + +std::string AstroBox::make_url(const std::string &path) const +{ + if (host.find("http://") == 0 || host.find("https://") == 0) { + if (host.back() == '/') { + return (boost::format("%1%%2%") % host % path).str(); + } else { + return (boost::format("%1%/%2%") % host % path).str(); + } + } else { + return (boost::format("http://%1%/%2%") % host % path).str(); + } +} + + +// SL1Host + +SL1Host::~SL1Host() {} + +const char* SL1Host::get_name() const { return "SL1Host"; } + +wxString SL1Host::get_test_ok_msg () const +{ + return _(L("Connection to Prusa SL1 works correctly.")); +} + +wxString SL1Host::get_test_failed_msg (wxString &msg) const +{ + return wxString::Format("%s: %s", _(L("Could not connect to Prusa SLA")), msg); +} + +bool SL1Host::can_start_print() const +{ + return false; +} + +bool SL1Host::validate_version_text(const boost::optional &version_text) const +{ + return version_text ? boost::starts_with(*version_text, "Prusa SLA") : false; +} + + +} diff --git a/src/slic3r/Utils/AstroBox.hpp b/src/slic3r/Utils/AstroBox.hpp new file mode 100644 index 000000000..1474132da --- /dev/null +++ b/src/slic3r/Utils/AstroBox.hpp @@ -0,0 +1,65 @@ +#ifndef slic3r_AstroBox_hpp_ +#define slic3r_AstroBox_hpp_ + +#include +#include +#include + +#include "PrintHost.hpp" + + +namespace Slic3r { + + +class DynamicPrintConfig; +class Http; + +class AstroBox : public PrintHost +{ +public: + AstroBox(DynamicPrintConfig *config); + virtual ~AstroBox(); + + virtual const char* get_name() const; + + virtual bool test(wxString &curl_msg) const; + virtual wxString get_test_ok_msg () const; + virtual wxString get_test_failed_msg (wxString &msg) const; + virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const; + virtual bool has_auto_discovery() const; + virtual bool can_test() const; + virtual bool can_start_print() const; + virtual std::string get_host() const { return host; } + +protected: + virtual bool validate_version_text(const boost::optional &version_text) const; + +private: + std::string host; + std::string apikey; + std::string cafile; + + void set_auth(Http &http) const; + std::string make_url(const std::string &path) const; +}; + + +class SL1Host: public AstroBox +{ +public: + SL1Host(DynamicPrintConfig *config) : AstroBox(config) {} + virtual ~SL1Host(); + + virtual const char* get_name() const; + + virtual wxString get_test_ok_msg () const; + virtual wxString get_test_failed_msg (wxString &msg) const; + virtual bool can_start_print() const ; +protected: + virtual bool validate_version_text(const boost::optional &version_text) const; +}; + + +} + +#endif diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index 59a929ecc..0a49b7815 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -15,6 +15,7 @@ #include "OctoPrint.hpp" #include "Duet.hpp" #include "FlashAir.hpp" +#include "AstroBox.hpp" #include "../GUI/PrintHostDialogs.hpp" namespace fs = boost::filesystem; @@ -45,6 +46,7 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config) case htOctoPrint: return new OctoPrint(config); case htDuet: return new Duet(config); case htFlashAir: return new FlashAir(config); + case htAstroBox: return new AstroBox(config); default: return nullptr; } } else {