From d7f0cc71e6ff41fe60d8f95749d022e9149a74c7 Mon Sep 17 00:00:00 2001 From: Boris Pruessmann Date: Sat, 13 Jun 2020 10:57:24 +0200 Subject: [PATCH] Add support for RepetierServer --- src/libslic3r/GCode.cpp | 4 +- src/libslic3r/PrintConfig.cpp | 15 +++ src/libslic3r/PrintConfig.hpp | 7 +- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/Plater.cpp | 2 +- src/slic3r/GUI/Preset.cpp | 21 ++-- src/slic3r/GUI/PresetBundle.cpp | 2 + src/slic3r/GUI/Tab.cpp | 19 ++++ src/slic3r/Utils/PrintHost.cpp | 2 + src/slic3r/Utils/Repetier.cpp | 174 ++++++++++++++++++++++++++++++++ src/slic3r/Utils/Repetier.hpp | 49 +++++++++ 11 files changed, 288 insertions(+), 9 deletions(-) create mode 100644 src/slic3r/Utils/Repetier.cpp create mode 100644 src/slic3r/Utils/Repetier.hpp diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 50a643fe8..4e79d3cc4 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2744,7 +2744,9 @@ void GCode::append_full_config(const Print &print, std::string &str) "compatible_prints", "print_host", "printhost_apikey", - "printhost_cafile" + "printhost_cafile", + "repetier_group", + "repetier_slug" }; assert(std::is_sorted(banned_keys.begin(), banned_keys.end())); auto is_banned = [banned_keys](const std::string &key) { diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 0b148b1da..da06a1dd8 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -148,7 +148,20 @@ void PrintConfigDef::init_common_params() "the API Key or the password required for authentication."); def->mode = comAdvanced; def->set_default_value(new ConfigOptionString("")); + + def = this->add("repetier_slug", coString); + def->label = L("Repetier Printer"); + def->tooltip = L("Name of the Repetier printer"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionString("")); + def = this->add("repetier_group", coString); + def->label = L("Repetier Group"); + def->tooltip = L("Name of the Repetier group that the G-code file will be sent to."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionString("")); + + def = this->add("printhost_cafile", coString); def->label = L("HTTPS CA File"); def->category = OptionCategory::general; @@ -2277,10 +2290,12 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("duet"); def->enum_values.push_back("flashair"); def->enum_values.push_back("astrobox"); + def->enum_values.push_back("repetier"); 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->mode = comAdvanced; def->set_default_value(new ConfigOptionEnum(htOctoPrint)); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 5c0fff9de..e66f3ee58 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -64,7 +64,7 @@ enum GCodeFlavor : uint8_t { }; enum PrintHostType { - htOctoPrint, htDuet, htFlashAir, htAstroBox + htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier }; enum InfillPattern { @@ -174,6 +174,7 @@ template<> inline const t_config_enum_values& ConfigOptionEnum::g keys_map["duet"] = htDuet; keys_map["flashair"] = htFlashAir; keys_map["astrobox"] = htAstroBox; + keys_map["repetier"] = htRepetier; } return keys_map; } @@ -1276,6 +1277,8 @@ public: ConfigOptionString print_host; ConfigOptionString printhost_apikey; ConfigOptionString printhost_cafile; + ConfigOptionString repetier_slug; + ConfigOptionString repetier_group; ConfigOptionString serial_port; ConfigOptionInt serial_speed; @@ -1286,6 +1289,8 @@ protected: OPT_PTR(print_host); OPT_PTR(printhost_apikey); OPT_PTR(printhost_cafile); + OPT_PTR(repetier_slug); + OPT_PTR(repetier_group); OPT_PTR(serial_port); OPT_PTR(serial_speed); } diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index bd9136981..b2d7d36a1 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -180,6 +180,8 @@ set(SLIC3R_GUI_SOURCES Utils/FlashAir.hpp Utils/AstroBox.cpp Utils/AstroBox.hpp + Utils/Repetier.cpp + Utils/Repetier.hpp Utils/PrintHost.cpp Utils/PrintHost.hpp Utils/Bonjour.cpp diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e7fabed3a..f8882bb1d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2066,7 +2066,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) "complete_objects_one_skirt", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance", "skirt_height", "brim_width", "variable_layer_height", "serial_port", "serial_speed", "host_type", "print_host", - "printhost_apikey", "printhost_cafile", "nozzle_diameter", "single_extruder_multi_material", + "printhost_apikey", "printhost_cafile", "repetier_slug", "repetier_group", "nozzle_diameter", "single_extruder_multi_material", "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim", "extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology", // These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor. diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index d9070106f..83288454c 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -613,7 +613,7 @@ const std::vector& Preset::printer_options() "bed_shape", "bed_custom_texture", "bed_custom_model", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", "min_length", - "host_type", "print_host", "printhost_apikey", "printhost_cafile", + "host_type", "print_host", "printhost_apikey", "printhost_cafile", "repetier_slug", "repetier_group", "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", "feature_gcode", "between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction", @@ -751,7 +751,7 @@ const std::vector& Preset::sla_printer_options() "gamma_correction", "min_exposure_time", "max_exposure_time", "min_initial_exposure_time", "max_initial_exposure_time", - "print_host", "printhost_apikey", "printhost_cafile", + "print_host", "printhost_apikey", "printhost_cafile", "repetier_slug", "repetier_group", "printer_notes", "inherits", "thumbnails", @@ -891,15 +891,21 @@ static ProfileHostParams profile_host_params_same_or_anonymized(const DynamicPri auto opt_print_host_old = cfg_old.option("print_host"); auto opt_printhost_apikey_old = cfg_old.option("printhost_apikey"); auto opt_printhost_cafile_old = cfg_old.option("printhost_cafile"); + auto opt_repetier_slug_old = cfg_old.option("repetier_slug"); + auto opt_repetier_group_old = cfg_old.option("repetier_group"); auto opt_print_host_new = cfg_new.option("print_host"); auto opt_printhost_apikey_new = cfg_new.option("printhost_apikey"); auto opt_printhost_cafile_new = cfg_new.option("printhost_cafile"); + auto opt_repetier_slug_new = cfg_new.option("repetier_slug"); + auto opt_repetier_group_new = cfg_new.option("repetier_group"); // If the new print host data is undefined, use the old data. bool new_print_host_undefined = (opt_print_host_new == nullptr || opt_print_host_new ->empty()) && (opt_printhost_apikey_new == nullptr || opt_printhost_apikey_new ->empty()) && - (opt_printhost_cafile_new == nullptr || opt_printhost_cafile_new ->empty()); + (opt_printhost_cafile_new == nullptr || opt_printhost_cafile_new ->empty()) && + (opt_repetier_slug_new == nullptr || opt_repetier_slug_new ->empty()) && + (opt_repetier_group_new == nullptr || opt_repetier_group_new ->empty()); if (new_print_host_undefined) return ProfileHostParams::Anonymized; @@ -907,8 +913,9 @@ static ProfileHostParams profile_host_params_same_or_anonymized(const DynamicPri return ((l == nullptr || l->empty()) && (r == nullptr || r->empty())) || (l != nullptr && r != nullptr && l->value == r->value); }; - return (opt_same(opt_print_host_old, opt_print_host_new) && opt_same(opt_printhost_apikey_old, opt_printhost_apikey_new) && - opt_same(opt_printhost_cafile_old, opt_printhost_cafile_new)) ? ProfileHostParams::Same : ProfileHostParams::Different; + return (opt_same(opt_print_host_old, opt_print_host_new) && opt_same(opt_printhost_apikey_old, opt_printhost_apikey_new) && + opt_same(opt_printhost_cafile_old, opt_printhost_cafile_new) && opt_same(opt_repetier_slug_old, opt_repetier_slug_new) && + opt_same(opt_repetier_group_old, opt_repetier_group_new)) ? ProfileHostParams::Same : ProfileHostParams::Different; } static bool profile_print_params_same(const DynamicPrintConfig &cfg_old, const DynamicPrintConfig &cfg_new) @@ -920,7 +927,7 @@ static bool profile_print_params_same(const DynamicPrintConfig &cfg_old, const D "compatible_printers", "compatible_printers_condition", "inherits", "print_settings_id", "filament_settings_id", "sla_print_settings_id", "sla_material_settings_id", "printer_settings_id", "printer_model", "printer_variant", "default_print_profile", "default_filament_profile", "default_sla_print_profile", "default_sla_material_profile", - "print_host", "printhost_apikey", "printhost_cafile" }) + "print_host", "printhost_apikey", "repetier_slug", "repetier_group", "printhost_cafile" }) diff.erase(std::remove(diff.begin(), diff.end(), key), diff.end()); // Preset with the same name as stored inside the config exists. return diff.empty() && profile_host_params_same_or_anonymized(cfg_old, cfg_new) != ProfileHostParams::Different; @@ -970,6 +977,8 @@ Preset& PresetCollection::load_external_preset( opt_update("print_host"); opt_update("printhost_apikey"); opt_update("printhost_cafile"); + opt_update("repetier_slug"); + opt_update("repetier_group"); } } // Update the "inherits" field. diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index 8ca0ef83c..9f7ab713a 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -526,6 +526,8 @@ DynamicPrintConfig PresetBundle::full_config_secure() const DynamicPrintConfig config = this->full_config(); config.erase("print_host"); config.erase("printhost_apikey"); + config.erase("repetier_slug"); + config.erase("repetier_group"); config.erase("printhost_cafile"); return config; } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 1d3dc0efb..4504c8934 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2002,6 +2002,12 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup) option = optgroup->get_option("printhost_apikey"); option.opt.width = Field::def_width_wider(); optgroup->append_single_option_line(option); + option = optgroup->get_option("repetier_slug"); + option.opt.width = Field::def_width_wider(); + optgroup->append_single_option_line(option); + option = optgroup->get_option("repetier_group"); + option.opt.width = Field::def_width_wider(); + optgroup->append_single_option_line(option); const auto ca_file_hint = _utf8(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate.")); @@ -2604,6 +2610,19 @@ void TabPrinter::update_fff() field->toggle(have_advanced_wipe_volume); } } + + bool is_repetier = m_config->option>("host_type")->value == htRepetier; + { + Field *rs = get_field("repetier_slug"); + Field *rg = get_field("repetier_group"); + if (is_repetier) { + rs->enable(); + rg->enable(); + } else { + rs->disable(); + rg->disable(); + } + } //z step checks { diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index 0a49b7815..4598db241 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -16,6 +16,7 @@ #include "Duet.hpp" #include "FlashAir.hpp" #include "AstroBox.hpp" +#include "Repetier.hpp" #include "../GUI/PrintHostDialogs.hpp" namespace fs = boost::filesystem; @@ -47,6 +48,7 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config) case htDuet: return new Duet(config); case htFlashAir: return new FlashAir(config); case htAstroBox: return new AstroBox(config); + case htRepetier: return new Repetier(config); default: return nullptr; } } else { diff --git a/src/slic3r/Utils/Repetier.cpp b/src/slic3r/Utils/Repetier.cpp new file mode 100644 index 000000000..7e9560933 --- /dev/null +++ b/src/slic3r/Utils/Repetier.cpp @@ -0,0 +1,174 @@ +#include "Repetier.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "libslic3r/PrintConfig.hpp" +#include "slic3r/GUI/I18N.hpp" +#include "slic3r/GUI/GUI.hpp" +#include "Http.hpp" + + +namespace fs = boost::filesystem; +namespace pt = boost::property_tree; + + +namespace Slic3r { + +Repetier::Repetier(DynamicPrintConfig *config) : + host(config->opt_string("print_host")), + apikey(config->opt_string("printhost_apikey")), + cafile(config->opt_string("printhost_cafile")), + slug(config->opt_string("repetier_slug")), + group(config->opt_string("repetier_group")) +{} + +const char* Repetier::get_name() const { return "Repetier"; } + +bool Repetier::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("printer/info"); + + 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); + + const auto text = ptree.get_optional("name"); + res = validate_version_text(text); + if (! res) { + msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "Repetier")).str()); + } + } + catch (const std::exception &) { + res = false; + msg = "Could not parse server response"; + } + }) + .perform_sync(); + + return res; +} + +wxString Repetier::get_test_ok_msg () const +{ + return _(L("Connection to Repetier works correctly.")); +} + +wxString Repetier::get_test_failed_msg (wxString &msg) const +{ + return GUI::from_u8((boost::format("%s: %s\n\n%s") + % _utf8(L("Could not connect to Repetier")) + % std::string(msg.ToUTF8()) + % _utf8(L("Note: Repetier version at least 0.90.0 is required."))).str()); +} + +bool Repetier::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((boost::format("printer/model/%1%") % slug).str()); + + 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); + + if (! group.empty()) { + http.form_add("group", group); + } + + http.form_add("a", "upload") + .form_add_file("filename", 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) << "Repetier: Upload canceled"; + res = false; + } + }) + .perform_sync(); + + return res; +} + +bool Repetier::validate_version_text(const boost::optional &version_text) const +{ + return version_text ? boost::starts_with(*version_text, "Repetier") : true; +} + +void Repetier::set_auth(Http &http) const +{ + http.header("X-Api-Key", apikey); + + if (! cafile.empty()) { + http.ca_file(cafile); + } +} + +std::string Repetier::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(); + } +} + +} diff --git a/src/slic3r/Utils/Repetier.hpp b/src/slic3r/Utils/Repetier.hpp new file mode 100644 index 000000000..512c74468 --- /dev/null +++ b/src/slic3r/Utils/Repetier.hpp @@ -0,0 +1,49 @@ +#ifndef slic3r_Repetier_hpp_ +#define slic3r_Repetier_hpp_ + +#include +#include +#include + +#include "PrintHost.hpp" + + +namespace Slic3r { + +class DynamicPrintConfig; +class Http; + +class Repetier : public PrintHost +{ +public: + Repetier(DynamicPrintConfig *config); + ~Repetier() override = default; + + const char* get_name() const; + + 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 false; } + std::string get_host() const override { return host; } + +protected: + virtual bool validate_version_text(const boost::optional &version_text) const; + +private: + std::string host; + std::string apikey; + std::string cafile; + std::string slug; + std::string group; + + void set_auth(Http &http) const; + std::string make_url(const std::string &path) const; +}; + +} + +#endif