From 267712eb32e663873578b589d9383074a25feb3a Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Thu, 16 Aug 2018 16:34:59 +0200 Subject: [PATCH 1/8] Build: Plumb perl include path Thanks to @kortschak for contributing to this fix Co-authored-by: Dan Kortschak --- Build.PL | 1 - CMakeLists.txt | 15 +++++++++++---- xs/CMakeLists.txt | 5 +++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Build.PL b/Build.PL index 8f882fc4b..16e37986a 100644 --- a/Build.PL +++ b/Build.PL @@ -38,7 +38,6 @@ if ($gui) { %prereqs = qw( Class::Accessor 0 Wx 0.9918 - Socket 2.016 ); %recommends = qw( Wx::GLCanvas 0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 89e6369e0..5d9194d06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,4 @@ -# Boost 1.63 requires CMake 3.7 or newer -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.2) project(Slic3r) @@ -36,10 +35,18 @@ else() set(ENV_PATH_SEPARATOR ":") endif() set(ENV{PATH} "${PROJECT_SOURCE_DIR}/local-lib/bin${ENV_PATH_SEPARATOR}$ENV{PATH}") -set(ENV{PERL5LIB} "${PROJECT_SOURCE_DIR}/local-lib/lib/perl${ENV_PATH_SEPARATOR}$ENV{PERL5LIB}") +set(PERL_INCLUDE "${PROJECT_SOURCE_DIR}/local-lib/lib/perl5${ENV_PATH_SEPARATOR}$ENV{PERL5LIB}") message("PATH: $ENV{PATH}") -message("PERL5LIB: $ENV{PERL5LIB}") +message("PERL_INCLUDE: ${PERL_INCLUDE}") find_package(Perl REQUIRED) +if (WIN32) + # On Windows passing the PERL5LIB variable causes various problems (such as with MAX_PATH and others), + # basically I've found no good way to do it on Windows. + set(PERL5LIB_ENV_CMD "") +else() + set(PERL5LIB_ENV_CMD ${CMAKE_COMMAND} -E env PERL5LIB=${PERL_INCLUDE}) +endif() + # CMAKE_PREFIX_PATH is used to point CMake to the remaining dependencies (Boost, TBB, ...) # We pick it from environment if it is not defined in another way diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index d41b4c13a..6c2a61685 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -367,7 +367,7 @@ set(MyTypemap ${CMAKE_CURRENT_BINARY_DIR}/typemap) add_custom_command( OUTPUT ${MyTypemap} DEPENDS ${CMAKE_CURRENT_LIST_DIR}/xsp/my.map - COMMAND ${PERL_EXECUTABLE} -MExtUtils::Typemaps -MExtUtils::Typemaps::Basic -e "$typemap = ExtUtils::Typemaps->new(file => \"${CMAKE_CURRENT_LIST_DIR}/xsp/my.map\"); $typemap->merge(typemap => ExtUtils::Typemaps::Basic->new); $typemap->write(file => \"${MyTypemap}\")" + COMMAND ${PERL5LIB_ENV_CMD} ${PERL_EXECUTABLE} -MExtUtils::Typemaps -MExtUtils::Typemaps::Basic -e "$typemap = ExtUtils::Typemaps->new(file => \"${CMAKE_CURRENT_LIST_DIR}/xsp/my.map\"); $typemap->merge(typemap => ExtUtils::Typemaps::Basic->new); $typemap->write(file => \"${MyTypemap}\")" VERBATIM ) @@ -432,7 +432,8 @@ set(XS_MAIN_CPP ${CMAKE_CURRENT_BINARY_DIR}/XS.cpp) add_custom_command( OUTPUT ${XS_MAIN_CPP} DEPENDS ${MyTypemap} ${XS_XSP_FILES} ${CMAKE_CURRENT_LIST_DIR}/xsp/typemap.xspt - COMMAND COMMAND xsubpp -typemap typemap -output ${XS_MAIN_CPP} -hiertype ${XS_MAIN_XS} + COMMAND ${PERL5LIB_ENV_CMD} xsubpp -typemap typemap -output ${XS_MAIN_CPP} -hiertype ${XS_MAIN_XS} + VERBATIM ) # Define the Perl XS shared library. From 7be24414f3ad83f7c45c40f244699fd41d487f72 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Fri, 17 Aug 2018 12:38:33 +0200 Subject: [PATCH 2/8] Build: Option to force generation of PDB file on MSVC Release build --- CMakeLists.txt | 1 + xs/CMakeLists.txt | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d9194d06..8e3d8ad4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ option(SLIC3R_GUI "Compile Slic3r with GUI components (OpenGL, wxWidgets)" option(SLIC3R_PRUSACONTROL "Compile Slic3r with the PrusaControl prject file format (requires wxWidgets base library)" 1) option(SLIC3R_PROFILE "Compile Slic3r with an invasive Shiny profiler" 0) option(SLIC3R_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1) +option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1) if (MSVC AND SLIC3R_MSVC_COMPILE_PARALLEL) add_compile_options(/MP) diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 6c2a61685..d4306c525 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -497,6 +497,14 @@ if (WIN32) target_compile_definitions(XS PRIVATE -DNOGDI -DNOMINMAX -DHAS_BOOL) endif () +# SLIC3R_MSVC_PDB +if (MSVC AND SLIC3R_MSVC_PDB AND ${CMAKE_BUILD_TYPE} STREQUAL "Release") + set_target_properties(XS PROPERTIES + COMPILE_FLAGS "/Zi" + LINK_FLAGS "/DEBUG /OPT:REF /OPT:ICF" + ) +endif() + ## Configuration flags if (SLIC3R_GUI) message("Slic3r will be built with GUI support") From 3433e8e3743f8aebea940ae92e3f26fec03c7a22 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 17 Aug 2018 15:42:46 +0200 Subject: [PATCH 3/8] Fixed a few tooltips, changed default value for minimal purge on wipe tower --- xs/src/libslic3r/PrintConfig.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index d8f2d85a6..a78e73fb5 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -518,7 +518,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "filament-minimal-purge-on-wipe-tower=f@"; def->sidetext = L("mm³"); def->min = 0; - def->default_value = new ConfigOptionFloats { 5.f }; + def->default_value = new ConfigOptionFloats { 15.f }; def = this->add("filament_cooling_final_speed", coFloats); def->label = L("Speed of the last cooling move"); @@ -572,10 +572,7 @@ PrintConfigDef::PrintConfigDef() def = this->add("filament_type", coStrings); def->label = L("Filament type"); - def->tooltip = L("If you want to process the output G-code through custom scripts, just list their " - "absolute paths here. Separate multiple scripts with a semicolon. Scripts will be passed " - "the absolute path to the G-code file as the first argument, and they can access " - "the Slic3r config settings by reading environment variables."); + def->tooltip = L("The filament material type for use in custom G-codes."); def->cli = "filament_type=s@"; def->gui_type = "f_enum_open"; def->gui_flags = "show_value"; @@ -921,7 +918,7 @@ PrintConfigDef::PrintConfigDef() def = this->add("remaining_times", coBool); def->label = L("Supports remaining times"); - def->tooltip = L("Emit M73 P[percent printed] R[remaining time in seconds] at 1 minute" + def->tooltip = L("Emit M73 P[percent printed] R[remaining time in minutes] at 1 minute" " intervals into the G-code to let the firmware show accurate remaining time." " As of now only the Prusa i3 MK3 firmware recognizes M73." " Also the i3 MK3 firmware supports M73 Qxx Sxx for the silent mode."); From dd1fd66a47f64ca8bfba736728787d2781a983e1 Mon Sep 17 00:00:00 2001 From: Martin Loidl Date: Sun, 8 Jul 2018 14:32:48 +0200 Subject: [PATCH 4/8] Added possibility for upload to Duet Further changes: - Added new configuration option Host Type - Added abstract base class for future printer hosts - Moved location of upload dialog (also made it a little bit more configureable) - added possibility to send file via postfield instead a new frame --- lib/Slic3r.pm | 2 + lib/Slic3r/GUI/Plater.pm | 15 +- resources/profiles/PrusaResearch.ini | 4 +- xs/CMakeLists.txt | 9 +- xs/src/libslic3r/PrintConfig.cpp | 32 ++- xs/src/libslic3r/PrintConfig.hpp | 27 +- xs/src/perlglue.cpp | 3 +- xs/src/slic3r/GUI/Field.cpp | 2 + xs/src/slic3r/GUI/GUI.cpp | 2 + xs/src/slic3r/GUI/OptionsGroup.cpp | 6 +- xs/src/slic3r/GUI/Preset.cpp | 4 +- xs/src/slic3r/GUI/Tab.cpp | 63 +++-- xs/src/slic3r/GUI/Tab.hpp | 3 +- xs/src/slic3r/Utils/Duet.cpp | 281 ++++++++++++++++++++ xs/src/slic3r/Utils/Duet.hpp | 46 ++++ xs/src/slic3r/Utils/Http.cpp | 24 ++ xs/src/slic3r/Utils/Http.hpp | 3 + xs/src/slic3r/Utils/OctoPrint.cpp | 78 ++---- xs/src/slic3r/Utils/OctoPrint.hpp | 8 +- xs/src/slic3r/Utils/PrintHost.hpp | 31 +++ xs/src/slic3r/Utils/PrintHostFactory.cpp | 21 ++ xs/src/slic3r/Utils/PrintHostFactory.hpp | 24 ++ xs/src/slic3r/Utils/PrintHostSendDialog.cpp | 54 ++++ xs/src/slic3r/Utils/PrintHostSendDialog.hpp | 40 +++ xs/xsp/Utils_OctoPrint.xsp | 13 - xs/xsp/Utils_PrintHost.xsp | 10 + xs/xsp/Utils_PrintHostFactory.xsp | 13 + xs/xsp/my.map | 8 +- xs/xsp/typemap.xspt | 1 + 29 files changed, 699 insertions(+), 128 deletions(-) create mode 100644 xs/src/slic3r/Utils/Duet.cpp create mode 100644 xs/src/slic3r/Utils/Duet.hpp create mode 100644 xs/src/slic3r/Utils/PrintHost.hpp create mode 100644 xs/src/slic3r/Utils/PrintHostFactory.cpp create mode 100644 xs/src/slic3r/Utils/PrintHostFactory.hpp create mode 100644 xs/src/slic3r/Utils/PrintHostSendDialog.cpp create mode 100644 xs/src/slic3r/Utils/PrintHostSendDialog.hpp delete mode 100644 xs/xsp/Utils_OctoPrint.xsp create mode 100644 xs/xsp/Utils_PrintHost.xsp create mode 100644 xs/xsp/Utils_PrintHostFactory.xsp diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 0c6c81bb5..5eaa0e522 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -167,6 +167,8 @@ sub thread_cleanup { *Slic3r::GUI::PresetHints::DESTROY = sub {}; *Slic3r::GUI::TabIface::DESTROY = sub {}; *Slic3r::OctoPrint::DESTROY = sub {}; + *Slic3r::Duet::DESTROY = sub {}; + *Slic3r::PrintHostFactory::DESTROY = sub {}; *Slic3r::PresetUpdater::DESTROY = sub {}; return undef; # this prevents a "Scalars leaked" warning } diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index a0eef72fe..89f803228 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -53,7 +53,7 @@ sub new { my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); $self->{config} = Slic3r::Config::new_from_defaults_keys([qw( bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height - serial_port serial_speed octoprint_host octoprint_apikey octoprint_cafile + serial_port serial_speed host_type print_host printhost_apikey printhost_cafile nozzle_diameter single_extruder_multi_material wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_rotation_angle extruder_colour filament_colour max_print_height printer_model )]); @@ -1569,7 +1569,7 @@ sub on_export_completed { $message = L("File added to print queue"); $do_print = 1; } elsif ($self->{send_gcode_file}) { - $message = L("Sending G-code file to the OctoPrint server..."); + $message = L("Sending G-code file to the Printer Host ..."); $send_gcode = 1; } else { $message = L("G-code file exported to ") . $self->{export_gcode_output_file}; @@ -1585,9 +1585,10 @@ sub on_export_completed { # Send $self->{send_gcode_file} to OctoPrint. if ($send_gcode) { - my $op = Slic3r::OctoPrint->new($self->{config}); - if ($op->send_gcode($self->{send_gcode_file})) { - $self->statusbar->SetStatusText(L("OctoPrint upload finished.")); + my $host = Slic3r::PrintHostFactory::get_print_host($self->{config}); + + if ($host->send_gcode($self->{send_gcode_file})) { + $self->statusbar->SetStatusText(L("Upload to host finished.")); } else { $self->statusbar->SetStatusText(""); } @@ -1914,8 +1915,8 @@ sub on_config_change { } elsif ($opt_key eq 'serial_port') { $self->{btn_print}->Show($config->get('serial_port')); $self->Layout; - } elsif ($opt_key eq 'octoprint_host') { - $self->{btn_send_gcode}->Show($config->get('octoprint_host')); + } elsif ($opt_key eq 'print_host') { + $self->{btn_send_gcode}->Show($config->get('print_host')); $self->Layout; } elsif ($opt_key eq 'variable_layer_height') { if ($config->get('variable_layer_height') != 1) { diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 32ec800e7..7e358c77e 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -1007,8 +1007,8 @@ max_layer_height = 0.25 min_layer_height = 0.07 max_print_height = 200 nozzle_diameter = 0.4 -octoprint_apikey = -octoprint_host = +printhost_apikey = +print_host = printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2\n printer_settings_id = retract_before_travel = 1 diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index d4306c525..998d44cb0 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -251,8 +251,14 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/Utils/Http.hpp ${LIBDIR}/slic3r/Utils/FixModelByWin10.cpp ${LIBDIR}/slic3r/Utils/FixModelByWin10.hpp + ${LIBDIR}/slic3r/Utils/PrintHostSendDialog.cpp + ${LIBDIR}/slic3r/Utils/PrintHostSendDialog.hpp ${LIBDIR}/slic3r/Utils/OctoPrint.cpp ${LIBDIR}/slic3r/Utils/OctoPrint.hpp + ${LIBDIR}/slic3r/Utils/Duet.cpp + ${LIBDIR}/slic3r/Utils/Duet.hpp + ${LIBDIR}/slic3r/Utils/PrintHostFactory.cpp + ${LIBDIR}/slic3r/Utils/PrintHostFactory.hpp ${LIBDIR}/slic3r/Utils/Bonjour.cpp ${LIBDIR}/slic3r/Utils/Bonjour.hpp ${LIBDIR}/slic3r/Utils/PresetUpdater.cpp @@ -411,7 +417,8 @@ set(XS_XSP_FILES ${XSP_DIR}/Surface.xsp ${XSP_DIR}/SurfaceCollection.xsp ${XSP_DIR}/TriangleMesh.xsp - ${XSP_DIR}/Utils_OctoPrint.xsp + ${XSP_DIR}/Utils_PrintHostFactory.xsp + ${XSP_DIR}/Utils_PrintHost.xsp ${XSP_DIR}/Utils_PresetUpdater.xsp ${XSP_DIR}/AppController.xsp ${XSP_DIR}/XS.xsp diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index a78e73fb5..794c27608 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1137,24 +1137,36 @@ PrintConfigDef::PrintConfigDef() def->cli = "nozzle-diameter=f@"; def->default_value = new ConfigOptionFloats { 0.5 }; - def = this->add("octoprint_apikey", coString); - def->label = L("API Key"); - def->tooltip = L("Slic3r can upload G-code files to OctoPrint. This field should contain " - "the API Key required for authentication."); + def = this->add("host_type", coEnum); + def->label = L("Host Type"); + def->tooltip = L("Slic3r can upload G-code files to a printer host. This field must contain " + "the kind of the host."); + def->cli = "host-type=s"; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("octoprint"); + def->enum_values.push_back("duet"); + def->enum_labels.push_back("OctoPrint"); + def->enum_labels.push_back("Duet"); + def->default_value = new ConfigOptionEnum(htOctoPrint); + + def = this->add("printhost_apikey", coString); + def->label = L("API Key / Password"); + def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain " + "the API Key or the password required for authentication."); def->cli = "octoprint-apikey=s"; def->default_value = new ConfigOptionString(""); - def = this->add("octoprint_cafile", coString); + def = this->add("printhost_cafile", coString); def->label = "HTTPS CA file"; def->tooltip = "Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. " "If left blank, the default OS CA certificate repository is used."; def->cli = "octoprint-cafile=s"; def->default_value = new ConfigOptionString(""); - def = this->add("octoprint_host", coString); + def = this->add("print_host", coString); def->label = L("Hostname, IP or URL"); - def->tooltip = L("Slic3r can upload G-code files to OctoPrint. This field should contain " - "the hostname, IP address or URL of the OctoPrint instance."); + def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain " + "the hostname, IP address or URL of the printer host instance."); def->cli = "octoprint-host=s"; def->default_value = new ConfigOptionString(""); @@ -2107,10 +2119,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va std::ostringstream oss; oss << "0x0," << p.value.x << "x0," << p.value.x << "x" << p.value.y << ",0x" << p.value.y; value = oss.str(); -// Maybe one day we will rename octoprint_host to print_host as it has been done in the upstream Slic3r. -// Commenting this out fixes github issue #869 for now. -// } else if (opt_key == "octoprint_host" && !value.empty()) { -// opt_key = "print_host"; } else if ((opt_key == "perimeter_acceleration" && value == "25") || (opt_key == "infill_acceleration" && value == "50")) { /* For historical reasons, the world's full of configs having these very low values; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index b18603d87..438e90681 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -27,6 +27,10 @@ enum GCodeFlavor { gcfSmoothie, gcfNoExtrusion, }; +enum PrintHostType { + htOctoPrint, htDuet, +}; + enum InfillPattern { ipRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, @@ -61,6 +65,15 @@ template<> inline t_config_enum_values& ConfigOptionEnum::get_enum_ return keys_map; } +template<> inline t_config_enum_values& ConfigOptionEnum::get_enum_values() { + static t_config_enum_values keys_map; + if (keys_map.empty()) { + keys_map["octoprint"] = htOctoPrint; + keys_map["duet"] = htDuet; + } + return keys_map; +} + template<> inline t_config_enum_values& ConfigOptionEnum::get_enum_values() { static t_config_enum_values keys_map; if (keys_map.empty()) { @@ -789,18 +802,20 @@ class HostConfig : public StaticPrintConfig { STATIC_PRINT_CONFIG_CACHE(HostConfig) public: - ConfigOptionString octoprint_host; - ConfigOptionString octoprint_apikey; - ConfigOptionString octoprint_cafile; + ConfigOptionEnum host_type; + ConfigOptionString print_host; + ConfigOptionString printhost_apikey; + ConfigOptionString printhost_cafile; ConfigOptionString serial_port; ConfigOptionInt serial_speed; protected: void initialize(StaticCacheBase &cache, const char *base_ptr) { - OPT_PTR(octoprint_host); - OPT_PTR(octoprint_apikey); - OPT_PTR(octoprint_cafile); + OPT_PTR(host_type); + OPT_PTR(print_host); + OPT_PTR(printhost_apikey); + OPT_PTR(printhost_cafile); OPT_PTR(serial_port); OPT_PTR(serial_speed); } diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index c8aadc8c3..997938a91 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -64,9 +64,10 @@ REGISTER_CLASS(PresetCollection, "GUI::PresetCollection"); REGISTER_CLASS(PresetBundle, "GUI::PresetBundle"); REGISTER_CLASS(TabIface, "GUI::Tab"); REGISTER_CLASS(PresetUpdater, "PresetUpdater"); -REGISTER_CLASS(OctoPrint, "OctoPrint"); REGISTER_CLASS(AppController, "AppController"); REGISTER_CLASS(PrintController, "PrintController"); +REGISTER_CLASS(PrintHost, "PrintHost"); +REGISTER_CLASS(PrintHostFactory, "PrintHostFactory"); SV* ConfigBase__as_hash(ConfigBase* THIS) { diff --git a/xs/src/slic3r/GUI/Field.cpp b/xs/src/slic3r/GUI/Field.cpp index 85fa790a5..757a18f11 100644 --- a/xs/src/slic3r/GUI/Field.cpp +++ b/xs/src/slic3r/GUI/Field.cpp @@ -586,6 +586,8 @@ boost::any& Choice::get_value() m_value = static_cast(ret_enum); else if (m_opt_id.compare("seam_position") == 0) m_value = static_cast(ret_enum); + else if (m_opt_id.compare("host_type") == 0) + m_value = static_cast(ret_enum); } return m_value; diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 8cd7ed776..8555f0b92 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -604,6 +604,8 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); else if (opt_key.compare("seam_position") == 0) config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); + else if (opt_key.compare("host_type") == 0) + config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); } break; case coPoints:{ diff --git a/xs/src/slic3r/GUI/OptionsGroup.cpp b/xs/src/slic3r/GUI/OptionsGroup.cpp index d5cc29e19..a2d6559a9 100644 --- a/xs/src/slic3r/GUI/OptionsGroup.cpp +++ b/xs/src/slic3r/GUI/OptionsGroup.cpp @@ -459,8 +459,12 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config else if (opt_key.compare("support_material_pattern") == 0){ ret = static_cast(config.option>(opt_key)->value); } - else if (opt_key.compare("seam_position") == 0) + else if (opt_key.compare("seam_position") == 0){ ret = static_cast(config.option>(opt_key)->value); + } + else if (opt_key.compare("host_type") == 0){ + ret = static_cast(config.option>(opt_key)->value); + } } break; case coPoints: diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index 9f51f7b97..8335e48b5 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -329,8 +329,8 @@ const std::vector& Preset::printer_options() static std::vector s_opts; if (s_opts.empty()) { s_opts = { - "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", - "octoprint_host", "octoprint_apikey", "octoprint_cafile", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", + "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", "host_type", + "print_host", "printhost_apikey", "printhost_cafile", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", "between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction", "cooling_tube_length", "parking_pos_retraction", "extra_loading_move", "max_print_height", "default_print_profile", "inherits", diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp index 7c4322c5a..13b0ece5f 100644 --- a/xs/src/slic3r/GUI/Tab.cpp +++ b/xs/src/slic3r/GUI/Tab.cpp @@ -5,7 +5,8 @@ #include "../../libslic3r/Utils.hpp" #include "slic3r/Utils/Http.hpp" -#include "slic3r/Utils/OctoPrint.hpp" +#include "slic3r/Utils/PrintHostFactory.hpp" +#include "slic3r/Utils/PrintHost.hpp" #include "slic3r/Utils/Serial.hpp" #include "BonjourDialog.hpp" #include "WipeTowerDialog.hpp" @@ -1521,10 +1522,12 @@ void TabPrinter::build() optgroup->append_line(line); } - optgroup = page->new_optgroup(_(L("OctoPrint upload"))); + optgroup = page->new_optgroup(_(L("Printer Host upload"))); - auto octoprint_host_browse = [this, optgroup] (wxWindow* parent) { - auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); + optgroup->append_single_option_line("host_type"); + + auto printhost_browse = [this, optgroup] (wxWindow* parent) { + auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(btn); @@ -1532,47 +1535,52 @@ void TabPrinter::build() btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent e) { BonjourDialog dialog(parent); if (dialog.show_and_lookup()) { - optgroup->set_value("octoprint_host", std::move(dialog.get_selected()), true); + optgroup->set_value("print_host", std::move(dialog.get_selected()), true); } }); return sizer; }; - auto octoprint_host_test = [this](wxWindow* parent) { - auto btn = m_octoprint_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")), + auto print_host_test = [this](wxWindow* parent) { + auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")), wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG)); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(btn); btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) { - OctoPrint octoprint(m_config); - wxString msg; - if (octoprint.test(msg)) { - show_info(this, _(L("Connection to OctoPrint works correctly.")), _(L("Success!"))); - } else { - const auto text = wxString::Format("%s: %s\n\n%s", - _(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required.")) - ); + PrintHost *host = PrintHostFactory::get_print_host(m_config); + if (host == NULL) { + const auto text = wxString::Format("%s", + _(L("Could not get a valid Printer Host reference"))); show_error(this, text); + return; } + wxString msg; + if (host->test(msg)) { + show_info(this, host->get_test_ok_msg(), _(L("Success!"))); + } else { + show_error(this, host->get_test_failed_msg(msg)); + } + + delete (host); }); return sizer; }; - Line host_line = optgroup->create_single_option_line("octoprint_host"); - host_line.append_widget(octoprint_host_browse); - host_line.append_widget(octoprint_host_test); + Line host_line = optgroup->create_single_option_line("print_host"); + host_line.append_widget(printhost_browse); + host_line.append_widget(print_host_test); optgroup->append_line(host_line); - optgroup->append_single_option_line("octoprint_apikey"); + optgroup->append_single_option_line("printhost_apikey"); if (Http::ca_file_supported()) { - Line cafile_line = optgroup->create_single_option_line("octoprint_cafile"); + Line cafile_line = optgroup->create_single_option_line("printhost_cafile"); - auto octoprint_cafile_browse = [this, optgroup] (wxWindow* parent) { + auto printhost_cafile_browse = [this, optgroup] (wxWindow* parent) { auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); auto sizer = new wxBoxSizer(wxHORIZONTAL); @@ -1582,17 +1590,17 @@ void TabPrinter::build() static const auto filemasks = _(L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*")); wxFileDialog openFileDialog(this, _(L("Open CA certificate file")), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (openFileDialog.ShowModal() != wxID_CANCEL) { - optgroup->set_value("octoprint_cafile", std::move(openFileDialog.GetPath()), true); + optgroup->set_value("printhost_cafile", std::move(openFileDialog.GetPath()), true); } }); return sizer; }; - cafile_line.append_widget(octoprint_cafile_browse); + cafile_line.append_widget(printhost_cafile_browse); optgroup->append_line(cafile_line); - auto octoprint_cafile_hint = [this, optgroup] (wxWindow* parent) { + auto printhost_cafile_hint = [this, optgroup] (wxWindow* parent) { auto txt = new wxStaticText(parent, wxID_ANY, _(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate."))); auto sizer = new wxBoxSizer(wxHORIZONTAL); @@ -1602,7 +1610,7 @@ void TabPrinter::build() Line cafile_hint { "", "" }; cafile_hint.full_width = 1; - cafile_hint.widget = std::move(octoprint_cafile_hint); + cafile_hint.widget = std::move(printhost_cafile_hint); optgroup->append_line(cafile_hint); } @@ -1897,7 +1905,10 @@ void TabPrinter::update(){ m_serial_test_btn->Disable(); } - m_octoprint_host_test_btn->Enable(!m_config->opt_string("octoprint_host").empty()); + PrintHost *host = PrintHostFactory::get_print_host(m_config); + m_print_host_test_btn->Enable(!m_config->opt_string("print_host").empty() && host->can_test()); + m_printhost_browse_btn->Enable(host->have_auto_discovery()); + delete (host); bool have_multiple_extruders = m_extruders_count > 1; get_field("toolchange_gcode")->toggle(have_multiple_extruders); diff --git a/xs/src/slic3r/GUI/Tab.hpp b/xs/src/slic3r/GUI/Tab.hpp index 8b4eae7de..230fe659e 100644 --- a/xs/src/slic3r/GUI/Tab.hpp +++ b/xs/src/slic3r/GUI/Tab.hpp @@ -321,7 +321,8 @@ class TabPrinter : public Tab bool m_rebuild_kinematics_page = false; public: wxButton* m_serial_test_btn; - wxButton* m_octoprint_host_test_btn; + wxButton* m_print_host_test_btn; + wxButton* m_printhost_browse_btn; size_t m_extruders_count; size_t m_extruders_count_old = 0; diff --git a/xs/src/slic3r/Utils/Duet.cpp b/xs/src/slic3r/Utils/Duet.cpp new file mode 100644 index 000000000..aabc8eb40 --- /dev/null +++ b/xs/src/slic3r/Utils/Duet.cpp @@ -0,0 +1,281 @@ +#include "Duet.hpp" +#include "PrintHostSendDialog.hpp" + +#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/MsgDialog.hpp" +#include "Http.hpp" + +namespace fs = boost::filesystem; +namespace pt = boost::property_tree; + +namespace Slic3r { + +Duet::Duet(DynamicPrintConfig *config) : + host(config->opt_string("print_host")), + password(config->opt_string("printhost_apikey")) +{} + +bool Duet::test(wxString &msg) const +{ + bool connected = connect(msg); + if (connected) { + disconnect(); + } + + return connected; +} + +wxString Duet::get_test_ok_msg () const +{ + return wxString::Format("%s", _(L("Connection to Duet works correctly."))); +} + +wxString Duet::get_test_failed_msg (wxString &msg) const +{ + return wxString::Format("%s: %s", + _(L("Could not connect to Duet")), msg); +} + +bool Duet::send_gcode(const std::string &filename) const +{ + enum { PROGRESS_RANGE = 1000 }; + + const auto errortitle = _(L("Error while uploading to the Duet")); + fs::path filepath(filename); + + PrintHostSendDialog send_dialog(filepath.filename(), true); + if (send_dialog.ShowModal() != wxID_OK) { return false; } + + const bool print = send_dialog.print(); + const auto upload_filepath = send_dialog.filename(); + const auto upload_filename = upload_filepath.filename(); + const auto upload_parent_path = upload_filepath.parent_path(); + + wxProgressDialog progress_dialog( + _(L("Duet upload")), + _(L("Sending G-code file to Duet...")), + PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); + progress_dialog.Pulse(); + + wxString connect_msg; + if (!connect(connect_msg)) { + auto errormsg = wxString::Format("%s: %s", errortitle, connect_msg); + GUI::show_error(&progress_dialog, std::move(errormsg)); + return false; + } + + bool res = true; + + + auto upload_cmd = get_upload_url(upload_filepath.string()); + BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filename: %2%, path: %3%, print: %4%, command: %5%") + % filepath.string() + % upload_filename.string() + % upload_parent_path.string() + % print + % upload_cmd; + + auto http = Http::post(std::move(upload_cmd)); + http.postfield_add_file(filename) + .on_complete([&](std::string body, unsigned status) { + BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body; + progress_dialog.Update(PROGRESS_RANGE); + + int err_code = get_err_code_from_body(body); + switch (err_code) { + case 0: + break; + default: + auto msg = format_error(body, L("Unknown error occured"), 0); + GUI::show_error(&progress_dialog, std::move(msg)); + res = false; + break; + } + + if (err_code == 0 && print) { + wxString errormsg; + res = start_print(errormsg, upload_filepath.string()); + if (!res) { + GUI::show_error(&progress_dialog, std::move(errormsg)); + } + } + }) + .on_error([&](std::string body, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body; + auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status)); + GUI::show_error(&progress_dialog, std::move(errormsg)); + res = false; + }) + .on_progress([&](Http::Progress progress, bool &cancel) { + if (cancel) { + // Upload was canceled + res = false; + } else if (progress.ultotal > 0) { + int value = PROGRESS_RANGE * progress.ulnow / progress.ultotal; + cancel = !progress_dialog.Update(std::min(value, PROGRESS_RANGE - 1)); // Cap the value to prevent premature dialog closing + } else { + cancel = !progress_dialog.Pulse(); + } + }) + .perform_sync(); + + disconnect(); + + return res; +} + +bool Duet::have_auto_discovery() const +{ + return false; +} + +bool Duet::can_test() const +{ + return true; +} + +bool Duet::connect(wxString &msg) const +{ + bool res = false; + auto url = get_connect_url(); + + auto http = Http::get(std::move(url)); + http.on_error([&](std::string body, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error connecting: %1%, HTTP %2%, body: `%3%`") % error % status % body; + msg = format_error(body, error, status); + }) + .on_complete([&](std::string body, unsigned) { + BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: Got: %1%") % body; + + int err_code = get_err_code_from_body(body); + switch (err_code) { + case 0: + res = true; + break; + case 1: + msg = format_error(body, L("Wrong password"), 0); + break; + case 2: + msg = format_error(body, L("Could not get resources to create a new connection"), 0); + break; + default: + msg = format_error(body, L("Unknown error occured"), 0); + break; + } + + }) + .perform_sync(); + + return res; +} + +void Duet::disconnect() const +{ + auto url = (boost::format("%1%rr_disconnect") + % get_base_url()).str(); + + auto http = Http::get(std::move(url)); + http.on_error([&](std::string body, std::string error, unsigned status) { + // we don't care about it, if disconnect is not working Duet will disconnect automatically after some time + BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error disconnecting: %1%, HTTP %2%, body: `%3%`") % error % status % body; + }) + .perform_sync(); + +} + +std::string Duet::get_upload_url(const std::string &filename) const +{ + return (boost::format("%1%rr_upload?name=0:/gcodes/%2%&%3%") + % get_base_url() + % filename + % timestamp_str()).str(); +} + +std::string Duet::get_connect_url() const +{ + return (boost::format("%1%rr_connect?password=%2%&%3%") + % get_base_url() + % (password.empty() ? "reprap" : password) + % timestamp_str()).str(); +} + +std::string Duet::get_base_url() const +{ + if (host.find("http://") == 0 || host.find("https://") == 0) { + if (host.back() == '/') { + return host; + } else { + return (boost::format("%1%/") % host).str(); + } + } else { + return (boost::format("http://%1%/") % host).str(); + } +} + +std::string Duet::timestamp_str() const +{ + auto t = std::time(nullptr); + auto tm = *std::localtime(&t); + std::stringstream ss; + ss << "time=" << std::put_time(&tm, "%Y-%d-%mT%H:%M:%S"); + + return ss.str(); +} + +wxString Duet::format_error(const std::string &body, const std::string &error, unsigned status) +{ + if (status != 0) { + auto wxbody = wxString::FromUTF8(body.data()); + return wxString::Format("HTTP %u: %s", status, wxbody); + } else { + return wxString::FromUTF8(error.data()); + } +} + +bool Duet::start_print(wxString &msg, const std::string &filename) const { + bool res = false; + auto url = (boost::format("%1%rr_gcode?gcode=M32%%20\"%2%\"") + % get_base_url() + % filename).str(); + + auto http = Http::get(std::move(url)); + http.on_error([&](std::string body, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error starting print: %1%, HTTP %2%, body: `%3%`") % error % status % body; + msg = format_error(body, error, status); + }) + .on_complete([&](std::string body, unsigned) { + BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: Got: %1%") % body; + res = true; + }) + .perform_sync(); + + return res; +} + +int Duet::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); +} + + +} diff --git a/xs/src/slic3r/Utils/Duet.hpp b/xs/src/slic3r/Utils/Duet.hpp new file mode 100644 index 000000000..83ba0cbbb --- /dev/null +++ b/xs/src/slic3r/Utils/Duet.hpp @@ -0,0 +1,46 @@ +#ifndef slic3r_Duet_hpp_ +#define slic3r_Duet_hpp_ + +#include +#include + +#include "PrintHost.hpp" + + +namespace Slic3r { + + +class DynamicPrintConfig; +class Http; + +class Duet : public PrintHost +{ +public: + Duet(DynamicPrintConfig *config); + + bool test(wxString &curl_msg) const; + wxString get_test_ok_msg () const; + wxString get_test_failed_msg (wxString &msg) const; + // Send gcode file to duet, filename is expected to be in UTF-8 + bool send_gcode(const std::string &filename) const; + bool have_auto_discovery() const; + bool can_test() const; +private: + std::string host; + std::string password; + + std::string get_upload_url(const std::string &filename) const; + std::string get_connect_url() const; + std::string get_base_url() const; + std::string timestamp_str() const; + bool connect(wxString &msg) const; + void disconnect() const; + bool start_print(wxString &msg, const std::string &filename) const; + int get_err_code_from_body(const std::string &body) const; + static wxString format_error(const std::string &body, const std::string &error, unsigned status); +}; + + +} + +#endif diff --git a/xs/src/slic3r/Utils/Http.cpp b/xs/src/slic3r/Utils/Http.cpp index 37eb59a00..f5407b9fb 100644 --- a/xs/src/slic3r/Utils/Http.cpp +++ b/xs/src/slic3r/Utils/Http.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -42,6 +43,7 @@ struct Http::priv // Used for storing file streams added as multipart form parts // Using a deque here because unlike vector it doesn't ivalidate pointers on insertion std::deque form_files; + std::string postfields; size_t limit; bool cancel; @@ -60,6 +62,7 @@ struct Http::priv static size_t form_file_read_cb(char *buffer, size_t size, size_t nitems, void *userp); void form_add_file(const char *name, const fs::path &path, const char* filename); + void postfield_add_file(const fs::path &path); std::string curl_error(CURLcode curlcode); std::string body_size_error(); @@ -187,6 +190,16 @@ void Http::priv::form_add_file(const char *name, const fs::path &path, const cha } } +void Http::priv::postfield_add_file(const fs::path &path) +{ + std::ifstream f (path.string()); + std::string file_content { std::istreambuf_iterator(f), std::istreambuf_iterator() }; + if (!postfields.empty()) { + postfields += "&"; + } + postfields += file_content; +} + std::string Http::priv::curl_error(CURLcode curlcode) { return (boost::format("%1% (%2%)") @@ -229,6 +242,11 @@ void Http::priv::http_perform() ::curl_easy_setopt(curl, CURLOPT_HTTPPOST, form); } + if (!postfields.empty()) { + ::curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postfields.c_str()); + ::curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, postfields.size()); + } + CURLcode res = ::curl_easy_perform(curl); if (res != CURLE_OK) { @@ -338,6 +356,12 @@ Http& Http::form_add_file(const std::string &name, const fs::path &path, const s return *this; } +Http& Http::postfield_add_file(const fs::path &path) +{ + if (p) { p->postfield_add_file(path);} + return *this; +} + Http& Http::on_complete(CompleteFn fn) { if (p) { p->completefn = std::move(fn); } diff --git a/xs/src/slic3r/Utils/Http.hpp b/xs/src/slic3r/Utils/Http.hpp index ce4e438ca..cf5712d96 100644 --- a/xs/src/slic3r/Utils/Http.hpp +++ b/xs/src/slic3r/Utils/Http.hpp @@ -73,6 +73,9 @@ public: // Same as above except also override the file's filename with a custom one Http& form_add_file(const std::string &name, const boost::filesystem::path &path, const std::string &filename); + // Add the file as POSTFIELD to the request, this can be used for hosts which do not support multipart requests + Http& postfield_add_file(const boost::filesystem::path &path); + // Callback called on HTTP request complete Http& on_complete(CompleteFn fn); // Callback called on an error occuring at any stage of the requests: Url parsing, DNS lookup, diff --git a/xs/src/slic3r/Utils/OctoPrint.cpp b/xs/src/slic3r/Utils/OctoPrint.cpp index 97b4123d4..c62f9b55c 100644 --- a/xs/src/slic3r/Utils/OctoPrint.cpp +++ b/xs/src/slic3r/Utils/OctoPrint.cpp @@ -1,21 +1,11 @@ #include "OctoPrint.hpp" +#include "PrintHostSendDialog.hpp" #include -#include #include #include -#include -#include -#include -#include -#include -#include -#include - #include "libslic3r/PrintConfig.hpp" -#include "slic3r/GUI/GUI.hpp" -#include "slic3r/GUI/MsgDialog.hpp" #include "Http.hpp" namespace fs = boost::filesystem; @@ -23,47 +13,10 @@ namespace fs = boost::filesystem; namespace Slic3r { - -struct SendDialog : public GUI::MsgDialog -{ - wxTextCtrl *txt_filename; - wxCheckBox *box_print; - - SendDialog(const fs::path &path) : - MsgDialog(nullptr, _(L("Send G-Code to printer")), _(L("Upload to OctoPrint with the following filename:")), wxID_NONE), - txt_filename(new wxTextCtrl(this, wxID_ANY, path.filename().wstring())), - box_print(new wxCheckBox(this, wxID_ANY, _(L("Start printing after upload")))) - { - auto *label_dir_hint = new wxStaticText(this, wxID_ANY, _(L("Use forward slashes ( / ) as a directory separator if needed."))); - label_dir_hint->Wrap(CONTENT_WIDTH); - - content_sizer->Add(txt_filename, 0, wxEXPAND); - content_sizer->Add(label_dir_hint); - content_sizer->AddSpacer(VERT_SPACING); - content_sizer->Add(box_print, 0, wxBOTTOM, 2*VERT_SPACING); - - btn_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL)); - - txt_filename->SetFocus(); - wxString stem(path.stem().wstring()); - txt_filename->SetSelection(0, stem.Length()); - - Fit(); - } - - fs::path filename() const { - return fs::path(txt_filename->GetValue().wx_str()); - } - - bool print() const { return box_print->GetValue(); } -}; - - - OctoPrint::OctoPrint(DynamicPrintConfig *config) : - host(config->opt_string("octoprint_host")), - apikey(config->opt_string("octoprint_apikey")), - cafile(config->opt_string("octoprint_cafile")) + host(config->opt_string("print_host")), + apikey(config->opt_string("printhost_apikey")), + cafile(config->opt_string("printhost_cafile")) {} bool OctoPrint::test(wxString &msg) const @@ -91,6 +44,17 @@ bool OctoPrint::test(wxString &msg) const return res; } +wxString OctoPrint::get_test_ok_msg () const +{ + return wxString::Format("%s", _(L("Connection to OctoPrint works correctly."))); +} + +wxString OctoPrint::get_test_failed_msg (wxString &msg) const +{ + return wxString::Format("%s: %s\n\n%s", + _(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required."))); +} + bool OctoPrint::send_gcode(const std::string &filename) const { enum { PROGRESS_RANGE = 1000 }; @@ -98,7 +62,7 @@ bool OctoPrint::send_gcode(const std::string &filename) const const auto errortitle = _(L("Error while uploading to the OctoPrint server")); fs::path filepath(filename); - SendDialog send_dialog(filepath.filename()); + PrintHostSendDialog send_dialog(filepath.filename(), true); if (send_dialog.ShowModal() != wxID_OK) { return false; } const bool print = send_dialog.print(); @@ -161,6 +125,16 @@ bool OctoPrint::send_gcode(const std::string &filename) const return res; } +bool OctoPrint::have_auto_discovery() const +{ + return true; +} + +bool OctoPrint::can_test() const +{ + return true; +} + void OctoPrint::set_auth(Http &http) const { http.header("X-Api-Key", apikey); diff --git a/xs/src/slic3r/Utils/OctoPrint.hpp b/xs/src/slic3r/Utils/OctoPrint.hpp index 1e2098ae3..aea2ba58f 100644 --- a/xs/src/slic3r/Utils/OctoPrint.hpp +++ b/xs/src/slic3r/Utils/OctoPrint.hpp @@ -4,6 +4,8 @@ #include #include +#include "PrintHost.hpp" + namespace Slic3r { @@ -11,14 +13,18 @@ namespace Slic3r { class DynamicPrintConfig; class Http; -class OctoPrint +class OctoPrint : public PrintHost { public: OctoPrint(DynamicPrintConfig *config); bool test(wxString &curl_msg) const; + wxString get_test_ok_msg () const; + wxString get_test_failed_msg (wxString &msg) const; // Send gcode file to octoprint, filename is expected to be in UTF-8 bool send_gcode(const std::string &filename) const; + bool have_auto_discovery() const; + bool can_test() const; private: std::string host; std::string apikey; diff --git a/xs/src/slic3r/Utils/PrintHost.hpp b/xs/src/slic3r/Utils/PrintHost.hpp new file mode 100644 index 000000000..204740635 --- /dev/null +++ b/xs/src/slic3r/Utils/PrintHost.hpp @@ -0,0 +1,31 @@ +#ifndef slic3r_PrintHost_hpp_ +#define slic3r_PrintHost_hpp_ + +#include +#include + + +namespace Slic3r { + + +class DynamicPrintConfig; + +class PrintHost +{ +public: + + virtual bool test(wxString &curl_msg) const = 0; + virtual wxString get_test_ok_msg () const = 0; + virtual wxString get_test_failed_msg (wxString &msg) const = 0; + // Send gcode file to print host, filename is expected to be in UTF-8 + virtual bool send_gcode(const std::string &filename) const = 0; + virtual bool have_auto_discovery() const = 0; + virtual bool can_test() const = 0; +}; + + + + +} + +#endif diff --git a/xs/src/slic3r/Utils/PrintHostFactory.cpp b/xs/src/slic3r/Utils/PrintHostFactory.cpp new file mode 100644 index 000000000..173c5d743 --- /dev/null +++ b/xs/src/slic3r/Utils/PrintHostFactory.cpp @@ -0,0 +1,21 @@ +#include "PrintHostFactory.hpp" +#include "OctoPrint.hpp" +#include "Duet.hpp" + +#include "libslic3r/PrintConfig.hpp" + +namespace Slic3r { + + +PrintHost * PrintHostFactory::get_print_host(DynamicPrintConfig *config) +{ + PrintHostType kind = config->option>("host_type")->value; + if (kind == htOctoPrint) { + return new OctoPrint(config); + } else if (kind == htDuet) { + return new Duet(config); + } + return NULL; +} + +} diff --git a/xs/src/slic3r/Utils/PrintHostFactory.hpp b/xs/src/slic3r/Utils/PrintHostFactory.hpp new file mode 100644 index 000000000..4c9ff2bf2 --- /dev/null +++ b/xs/src/slic3r/Utils/PrintHostFactory.hpp @@ -0,0 +1,24 @@ +#ifndef slic3r_PrintHostFactory_hpp_ +#define slic3r_PrintHostFactory_hpp_ + +#include +#include + + +namespace Slic3r { + +class DynamicPrintConfig; +class PrintHost; + +class PrintHostFactory +{ +public: + PrintHostFactory() {}; + ~PrintHostFactory() {}; + static PrintHost * get_print_host(DynamicPrintConfig *config); +}; + + +} + +#endif diff --git a/xs/src/slic3r/Utils/PrintHostSendDialog.cpp b/xs/src/slic3r/Utils/PrintHostSendDialog.cpp new file mode 100644 index 000000000..b1dd86961 --- /dev/null +++ b/xs/src/slic3r/Utils/PrintHostSendDialog.cpp @@ -0,0 +1,54 @@ +#include "PrintHostSendDialog.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/MsgDialog.hpp" + + +namespace fs = boost::filesystem; + +namespace Slic3r { + +PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_print) : + MsgDialog(nullptr, _(L("Send G-Code to printer host")), _(L("Upload to Printer Host with the following filename:")), wxID_NONE), + txt_filename(new wxTextCtrl(this, wxID_ANY, path.filename().wstring())), + box_print(new wxCheckBox(this, wxID_ANY, _(L("Start printing after upload")))), + can_start_print(can_start_print) +{ + auto *label_dir_hint = new wxStaticText(this, wxID_ANY, _(L("Use forward slashes ( / ) as a directory separator if needed."))); + label_dir_hint->Wrap(CONTENT_WIDTH); + + content_sizer->Add(txt_filename, 0, wxEXPAND); + content_sizer->Add(label_dir_hint); + content_sizer->AddSpacer(VERT_SPACING); + content_sizer->Add(box_print, 0, wxBOTTOM, 2*VERT_SPACING); + + btn_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL)); + + txt_filename->SetFocus(); + wxString stem(path.stem().wstring()); + txt_filename->SetSelection(0, stem.Length()); + + if (!can_start_print) { + box_print->Disable(); + } + + Fit(); +} + +fs::path PrintHostSendDialog::filename() const +{ + return fs::path(txt_filename->GetValue().wx_str()); +} + +bool PrintHostSendDialog::print() const +{ + return box_print->GetValue(); } +} diff --git a/xs/src/slic3r/Utils/PrintHostSendDialog.hpp b/xs/src/slic3r/Utils/PrintHostSendDialog.hpp new file mode 100644 index 000000000..7d2040d97 --- /dev/null +++ b/xs/src/slic3r/Utils/PrintHostSendDialog.hpp @@ -0,0 +1,40 @@ +#ifndef slic3r_PrintHostSendDialog_hpp_ +#define slic3r_PrintHostSendDialog_hpp_ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/MsgDialog.hpp" + + +namespace Slic3r { + +class PrintHostSendDialog : public GUI::MsgDialog +{ + +private: + wxTextCtrl *txt_filename; + wxCheckBox *box_print; + bool can_start_print; + +public: + + PrintHostSendDialog(const boost::filesystem::path &path, bool can_start_print); + boost::filesystem::path filename() const; + bool print() const; +}; + +} + +#endif diff --git a/xs/xsp/Utils_OctoPrint.xsp b/xs/xsp/Utils_OctoPrint.xsp deleted file mode 100644 index 28610cb01..000000000 --- a/xs/xsp/Utils_OctoPrint.xsp +++ /dev/null @@ -1,13 +0,0 @@ -%module{Slic3r::XS}; - -%{ -#include -#include "slic3r/Utils/OctoPrint.hpp" -%} - -%name{Slic3r::OctoPrint} class OctoPrint { - OctoPrint(DynamicPrintConfig *config); - ~OctoPrint(); - - bool send_gcode(std::string filename) const; -}; diff --git a/xs/xsp/Utils_PrintHost.xsp b/xs/xsp/Utils_PrintHost.xsp new file mode 100644 index 000000000..0c3fea137 --- /dev/null +++ b/xs/xsp/Utils_PrintHost.xsp @@ -0,0 +1,10 @@ +%module{Slic3r::XS}; + +%{ +#include +#include "slic3r/Utils/PrintHost.hpp" +%} + +%name{Slic3r::PrintHost} class PrintHost { + bool send_gcode(std::string filename) const; +}; diff --git a/xs/xsp/Utils_PrintHostFactory.xsp b/xs/xsp/Utils_PrintHostFactory.xsp new file mode 100644 index 000000000..2b083c957 --- /dev/null +++ b/xs/xsp/Utils_PrintHostFactory.xsp @@ -0,0 +1,13 @@ +%module{Slic3r::XS}; + +%{ +#include +#include "slic3r/Utils/PrintHostFactory.hpp" +%} + +%name{Slic3r::PrintHostFactory} class PrintHostFactory { + PrintHostFactory(); + ~PrintHostFactory(); + + static PrintHost * get_print_host(DynamicPrintConfig *config); +}; diff --git a/xs/xsp/my.map b/xs/xsp/my.map index 4a14f483f..aefe7b345 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -239,9 +239,11 @@ Ref O_OBJECT_SLIC3R_T PresetUpdater* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T -OctoPrint* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T -Clone O_OBJECT_SLIC3R_T +PrintHostFactory* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +Clone O_OBJECT_SLIC3R_T + +PrintHost* O_OBJECT_SLIC3R Axis T_UV ExtrusionLoopRole T_UV diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index b576b1373..cee75fe26 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -270,3 +270,4 @@ }; %typemap{AppController*}; %typemap{PrintController*}; +%typemap{PrintHost*}; From d4b73701d939f6832d5b86818a318d409a25b508 Mon Sep 17 00:00:00 2001 From: Martin Loidl Date: Fri, 13 Jul 2018 16:10:55 +0200 Subject: [PATCH 5/8] some code formatting --- lib/Slic3r.pm | 2 +- xs/CMakeLists.txt | 12 ++++++------ xs/src/slic3r/Utils/Duet.cpp | 9 ++++----- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 5eaa0e522..7aacd1fd9 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -168,7 +168,7 @@ sub thread_cleanup { *Slic3r::GUI::TabIface::DESTROY = sub {}; *Slic3r::OctoPrint::DESTROY = sub {}; *Slic3r::Duet::DESTROY = sub {}; - *Slic3r::PrintHostFactory::DESTROY = sub {}; + *Slic3r::PrintHostFactory::DESTROY = sub {}; *Slic3r::PresetUpdater::DESTROY = sub {}; return undef; # this prevents a "Scalars leaked" warning } diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 998d44cb0..3558b6d3c 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -255,10 +255,10 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/Utils/PrintHostSendDialog.hpp ${LIBDIR}/slic3r/Utils/OctoPrint.cpp ${LIBDIR}/slic3r/Utils/OctoPrint.hpp - ${LIBDIR}/slic3r/Utils/Duet.cpp - ${LIBDIR}/slic3r/Utils/Duet.hpp - ${LIBDIR}/slic3r/Utils/PrintHostFactory.cpp - ${LIBDIR}/slic3r/Utils/PrintHostFactory.hpp + ${LIBDIR}/slic3r/Utils/Duet.cpp + ${LIBDIR}/slic3r/Utils/Duet.hpp + ${LIBDIR}/slic3r/Utils/PrintHostFactory.cpp + ${LIBDIR}/slic3r/Utils/PrintHostFactory.hpp ${LIBDIR}/slic3r/Utils/Bonjour.cpp ${LIBDIR}/slic3r/Utils/Bonjour.hpp ${LIBDIR}/slic3r/Utils/PresetUpdater.cpp @@ -417,8 +417,8 @@ set(XS_XSP_FILES ${XSP_DIR}/Surface.xsp ${XSP_DIR}/SurfaceCollection.xsp ${XSP_DIR}/TriangleMesh.xsp - ${XSP_DIR}/Utils_PrintHostFactory.xsp - ${XSP_DIR}/Utils_PrintHost.xsp + ${XSP_DIR}/Utils_PrintHostFactory.xsp + ${XSP_DIR}/Utils_PrintHost.xsp ${XSP_DIR}/Utils_PresetUpdater.xsp ${XSP_DIR}/AppController.xsp ${XSP_DIR}/XS.xsp diff --git a/xs/src/slic3r/Utils/Duet.cpp b/xs/src/slic3r/Utils/Duet.cpp index aabc8eb40..86573ff30 100644 --- a/xs/src/slic3r/Utils/Duet.cpp +++ b/xs/src/slic3r/Utils/Duet.cpp @@ -82,7 +82,6 @@ bool Duet::send_gcode(const std::string &filename) const bool res = true; - auto upload_cmd = get_upload_url(upload_filepath.string()); BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filename: %2%, path: %3%, print: %4%, command: %5%") % filepath.string() @@ -196,7 +195,6 @@ void Duet::disconnect() const BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error disconnecting: %1%, HTTP %2%, body: `%3%`") % error % status % body; }) .perform_sync(); - } std::string Duet::get_upload_url(const std::string &filename) const @@ -231,8 +229,8 @@ std::string Duet::get_base_url() const std::string Duet::timestamp_str() const { auto t = std::time(nullptr); - auto tm = *std::localtime(&t); - std::stringstream ss; + auto tm = *std::localtime(&t); + std::stringstream ss; ss << "time=" << std::put_time(&tm, "%Y-%d-%mT%H:%M:%S"); return ss.str(); @@ -248,7 +246,8 @@ wxString Duet::format_error(const std::string &body, const std::string &error, u } } -bool Duet::start_print(wxString &msg, const std::string &filename) const { +bool Duet::start_print(wxString &msg, const std::string &filename) const +{ bool res = false; auto url = (boost::format("%1%rr_gcode?gcode=M32%%20\"%2%\"") % get_base_url() From ee9f7eaef69cbb84e3f5b54e6b624e317d16f7a3 Mon Sep 17 00:00:00 2001 From: Martin Loidl Date: Mon, 20 Aug 2018 20:19:22 +0200 Subject: [PATCH 6/8] Host upload backwards compatibility * Added legacy code to preserve backwards compatibility * renamed some cli option names to better match option names --- resources/profiles/PrusaResearch.ini | 4 ++-- xs/src/libslic3r/PrintConfig.cpp | 12 +++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 7e358c77e..32ec800e7 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -1007,8 +1007,8 @@ max_layer_height = 0.25 min_layer_height = 0.07 max_print_height = 200 nozzle_diameter = 0.4 -printhost_apikey = -print_host = +octoprint_apikey = +octoprint_host = printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2\n printer_settings_id = retract_before_travel = 1 diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 794c27608..943db2a30 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1153,21 +1153,21 @@ PrintConfigDef::PrintConfigDef() def->label = L("API Key / Password"); def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain " "the API Key or the password required for authentication."); - def->cli = "octoprint-apikey=s"; + def->cli = "printhost-apikey=s"; def->default_value = new ConfigOptionString(""); def = this->add("printhost_cafile", coString); def->label = "HTTPS CA file"; def->tooltip = "Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. " "If left blank, the default OS CA certificate repository is used."; - def->cli = "octoprint-cafile=s"; + def->cli = "printhost-cafile=s"; def->default_value = new ConfigOptionString(""); def = this->add("print_host", coString); def->label = L("Hostname, IP or URL"); def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain " "the hostname, IP address or URL of the printer host instance."); - def->cli = "octoprint-host=s"; + def->cli = "print-host=s"; def->default_value = new ConfigOptionString(""); def = this->add("only_retract_when_crossing_perimeters", coBool); @@ -2129,6 +2129,12 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va } else if (opt_key == "support_material_pattern" && value == "pillars") { // Slic3r PE does not support the pillars. They never worked well. value = "rectilinear"; + } else if (opt_key == "octoprint_host") { + opt_key = "print_host"; + } else if (opt_key == "octoprint_cafile") { + opt_key = "printhost_cafile"; + } else if (opt_key == "octoprint_apikey") { + opt_key = "printhost_apikey"; } // Ignore the following obsolete configuration keys: From 0c984c75841f2f691af5a73c070ba9d2378bb634 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Tue, 21 Aug 2018 11:10:32 +0200 Subject: [PATCH 7/8] Print host bugfixes / refactoring --- lib/Slic3r.pm | 1 - lib/Slic3r/GUI/Plater.pm | 2 +- xs/CMakeLists.txt | 5 ++- xs/src/libslic3r/PrintConfig.cpp | 4 --- xs/src/perlglue.cpp | 1 - xs/src/slic3r/GUI/Tab.cpp | 18 +++++----- xs/src/slic3r/Utils/Duet.cpp | 35 +++++++++---------- xs/src/slic3r/Utils/Duet.hpp | 5 +-- xs/src/slic3r/Utils/Http.cpp | 17 ++++----- xs/src/slic3r/Utils/Http.hpp | 6 ++-- xs/src/slic3r/Utils/OctoPrint.cpp | 4 ++- xs/src/slic3r/Utils/OctoPrint.hpp | 3 +- .../{PrintHostFactory.cpp => PrintHost.cpp} | 8 +++-- xs/src/slic3r/Utils/PrintHost.hpp | 6 +++- xs/src/slic3r/Utils/PrintHostFactory.hpp | 24 ------------- xs/src/slic3r/Utils/PrintHostSendDialog.cpp | 4 +-- xs/src/slic3r/Utils/PrintHostSendDialog.hpp | 2 -- xs/xsp/Utils_PrintHost.xsp | 2 ++ xs/xsp/Utils_PrintHostFactory.xsp | 13 ------- xs/xsp/my.map | 6 +--- 20 files changed, 61 insertions(+), 105 deletions(-) rename xs/src/slic3r/Utils/{PrintHostFactory.cpp => PrintHost.cpp} (73%) delete mode 100644 xs/src/slic3r/Utils/PrintHostFactory.hpp delete mode 100644 xs/xsp/Utils_PrintHostFactory.xsp diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 7aacd1fd9..46627311f 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -168,7 +168,6 @@ sub thread_cleanup { *Slic3r::GUI::TabIface::DESTROY = sub {}; *Slic3r::OctoPrint::DESTROY = sub {}; *Slic3r::Duet::DESTROY = sub {}; - *Slic3r::PrintHostFactory::DESTROY = sub {}; *Slic3r::PresetUpdater::DESTROY = sub {}; return undef; # this prevents a "Scalars leaked" warning } diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 89f803228..dbdf0be27 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1585,7 +1585,7 @@ sub on_export_completed { # Send $self->{send_gcode_file} to OctoPrint. if ($send_gcode) { - my $host = Slic3r::PrintHostFactory::get_print_host($self->{config}); + my $host = Slic3r::PrintHost::get_print_host($self->{config}); if ($host->send_gcode($self->{send_gcode_file})) { $self->statusbar->SetStatusText(L("Upload to host finished.")); diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 3558b6d3c..be7b57b72 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -257,8 +257,8 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/Utils/OctoPrint.hpp ${LIBDIR}/slic3r/Utils/Duet.cpp ${LIBDIR}/slic3r/Utils/Duet.hpp - ${LIBDIR}/slic3r/Utils/PrintHostFactory.cpp - ${LIBDIR}/slic3r/Utils/PrintHostFactory.hpp + ${LIBDIR}/slic3r/Utils/PrintHost.cpp + ${LIBDIR}/slic3r/Utils/PrintHost.hpp ${LIBDIR}/slic3r/Utils/Bonjour.cpp ${LIBDIR}/slic3r/Utils/Bonjour.hpp ${LIBDIR}/slic3r/Utils/PresetUpdater.cpp @@ -417,7 +417,6 @@ set(XS_XSP_FILES ${XSP_DIR}/Surface.xsp ${XSP_DIR}/SurfaceCollection.xsp ${XSP_DIR}/TriangleMesh.xsp - ${XSP_DIR}/Utils_PrintHostFactory.xsp ${XSP_DIR}/Utils_PrintHost.xsp ${XSP_DIR}/Utils_PresetUpdater.xsp ${XSP_DIR}/AppController.xsp diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 943db2a30..bf5f734ac 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -2144,9 +2144,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va "standby_temperature", "scale", "rotate", "duplicate", "duplicate_grid", "start_perimeters_at_concave_points", "start_perimeters_at_non_overhang", "randomize_start", "seal_position", "vibration_limit", "bed_size", - // Maybe one day we will rename octoprint_host to print_host as it has been done in the upstream Slic3r. - // Commenting this out fixes github issue #869 for now. - // "octoprint_host", "print_center", "g0", "threads", "pressure_advance", "wipe_tower_per_color_wipe" }; @@ -2156,7 +2153,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va } if (! print_config_def.has(opt_key)) { - //printf("Unknown option %s\n", opt_key.c_str()); opt_key = ""; return; } diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index 997938a91..d6bd0e94c 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -67,7 +67,6 @@ REGISTER_CLASS(PresetUpdater, "PresetUpdater"); REGISTER_CLASS(AppController, "AppController"); REGISTER_CLASS(PrintController, "PrintController"); REGISTER_CLASS(PrintHost, "PrintHost"); -REGISTER_CLASS(PrintHostFactory, "PrintHostFactory"); SV* ConfigBase__as_hash(ConfigBase* THIS) { diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp index 13b0ece5f..bde4fdc34 100644 --- a/xs/src/slic3r/GUI/Tab.cpp +++ b/xs/src/slic3r/GUI/Tab.cpp @@ -5,7 +5,6 @@ #include "../../libslic3r/Utils.hpp" #include "slic3r/Utils/Http.hpp" -#include "slic3r/Utils/PrintHostFactory.hpp" #include "slic3r/Utils/PrintHost.hpp" #include "slic3r/Utils/Serial.hpp" #include "BonjourDialog.hpp" @@ -1550,8 +1549,8 @@ void TabPrinter::build() sizer->Add(btn); btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) { - PrintHost *host = PrintHostFactory::get_print_host(m_config); - if (host == NULL) { + std::unique_ptr host(PrintHost::get_print_host(m_config)); + if (! host) { const auto text = wxString::Format("%s", _(L("Could not get a valid Printer Host reference"))); show_error(this, text); @@ -1563,8 +1562,6 @@ void TabPrinter::build() } else { show_error(this, host->get_test_failed_msg(msg)); } - - delete (host); }); return sizer; @@ -1905,11 +1902,12 @@ void TabPrinter::update(){ m_serial_test_btn->Disable(); } - PrintHost *host = PrintHostFactory::get_print_host(m_config); - m_print_host_test_btn->Enable(!m_config->opt_string("print_host").empty() && host->can_test()); - m_printhost_browse_btn->Enable(host->have_auto_discovery()); - delete (host); - + { + std::unique_ptr host(PrintHost::get_print_host(m_config)); + m_print_host_test_btn->Enable(!m_config->opt_string("print_host").empty() && host->can_test()); + m_printhost_browse_btn->Enable(host->has_auto_discovery()); + } + bool have_multiple_extruders = m_extruders_count > 1; get_field("toolchange_gcode")->toggle(have_multiple_extruders); get_field("single_extruder_multi_material")->toggle(have_multiple_extruders); diff --git a/xs/src/slic3r/Utils/Duet.cpp b/xs/src/slic3r/Utils/Duet.cpp index 86573ff30..517f02486 100644 --- a/xs/src/slic3r/Utils/Duet.cpp +++ b/xs/src/slic3r/Utils/Duet.cpp @@ -2,6 +2,7 @@ #include "PrintHostSendDialog.hpp" #include +#include #include #include #include @@ -31,6 +32,8 @@ Duet::Duet(DynamicPrintConfig *config) : password(config->opt_string("printhost_apikey")) {} +Duet::~Duet() {} + bool Duet::test(wxString &msg) const { bool connected = connect(msg); @@ -48,8 +51,7 @@ wxString Duet::get_test_ok_msg () const wxString Duet::get_test_failed_msg (wxString &msg) const { - return wxString::Format("%s: %s", - _(L("Could not connect to Duet")), msg); + return wxString::Format("%s: %s", _(L("Could not connect to Duet")), msg); } bool Duet::send_gcode(const std::string &filename) const @@ -91,23 +93,17 @@ bool Duet::send_gcode(const std::string &filename) const % upload_cmd; auto http = Http::post(std::move(upload_cmd)); - http.postfield_add_file(filename) + http.set_post_body(filename) .on_complete([&](std::string body, unsigned status) { BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body; progress_dialog.Update(PROGRESS_RANGE); int err_code = get_err_code_from_body(body); - switch (err_code) { - case 0: - break; - default: - auto msg = format_error(body, L("Unknown error occured"), 0); - GUI::show_error(&progress_dialog, std::move(msg)); - res = false; - break; - } - - if (err_code == 0 && print) { + if (err_code != 0) { + auto msg = format_error(body, L("Unknown error occured"), 0); + GUI::show_error(&progress_dialog, std::move(msg)); + res = false; + } else if (print) { wxString errormsg; res = start_print(errormsg, upload_filepath.string()); if (!res) { @@ -139,7 +135,7 @@ bool Duet::send_gcode(const std::string &filename) const return res; } -bool Duet::have_auto_discovery() const +bool Duet::has_auto_discovery() const { return false; } @@ -228,12 +224,15 @@ std::string Duet::get_base_url() const std::string Duet::timestamp_str() const { + enum { BUFFER_SIZE = 32 }; + auto t = std::time(nullptr); auto tm = *std::localtime(&t); - std::stringstream ss; - ss << "time=" << std::put_time(&tm, "%Y-%d-%mT%H:%M:%S"); - return ss.str(); + char buffer[BUFFER_SIZE]; + std::strftime(buffer, BUFFER_SIZE, "%Y-%d-%mT%H:%M:%S", &tm); + + return std::string(buffer); } wxString Duet::format_error(const std::string &body, const std::string &error, unsigned status) diff --git a/xs/src/slic3r/Utils/Duet.hpp b/xs/src/slic3r/Utils/Duet.hpp index 83ba0cbbb..bc210d7a4 100644 --- a/xs/src/slic3r/Utils/Duet.hpp +++ b/xs/src/slic3r/Utils/Duet.hpp @@ -17,18 +17,19 @@ class Duet : public PrintHost { public: Duet(DynamicPrintConfig *config); + virtual ~Duet(); bool test(wxString &curl_msg) const; wxString get_test_ok_msg () const; wxString get_test_failed_msg (wxString &msg) const; // Send gcode file to duet, filename is expected to be in UTF-8 bool send_gcode(const std::string &filename) const; - bool have_auto_discovery() const; + bool has_auto_discovery() const; bool can_test() const; private: std::string host; std::string password; - + std::string get_upload_url(const std::string &filename) const; std::string get_connect_url() const; std::string get_base_url() const; diff --git a/xs/src/slic3r/Utils/Http.cpp b/xs/src/slic3r/Utils/Http.cpp index f5407b9fb..a92e399a0 100644 --- a/xs/src/slic3r/Utils/Http.cpp +++ b/xs/src/slic3r/Utils/Http.cpp @@ -62,7 +62,7 @@ struct Http::priv static size_t form_file_read_cb(char *buffer, size_t size, size_t nitems, void *userp); void form_add_file(const char *name, const fs::path &path, const char* filename); - void postfield_add_file(const fs::path &path); + void set_post_body(const fs::path &path); std::string curl_error(CURLcode curlcode); std::string body_size_error(); @@ -190,14 +190,11 @@ void Http::priv::form_add_file(const char *name, const fs::path &path, const cha } } -void Http::priv::postfield_add_file(const fs::path &path) +void Http::priv::set_post_body(const fs::path &path) { - std::ifstream f (path.string()); - std::string file_content { std::istreambuf_iterator(f), std::istreambuf_iterator() }; - if (!postfields.empty()) { - postfields += "&"; - } - postfields += file_content; + std::ifstream file(path.string()); + std::string file_content { std::istreambuf_iterator(file), std::istreambuf_iterator() }; + postfields = file_content; } std::string Http::priv::curl_error(CURLcode curlcode) @@ -356,9 +353,9 @@ Http& Http::form_add_file(const std::string &name, const fs::path &path, const s return *this; } -Http& Http::postfield_add_file(const fs::path &path) +Http& Http::set_post_body(const fs::path &path) { - if (p) { p->postfield_add_file(path);} + if (p) { p->set_post_body(path);} return *this; } diff --git a/xs/src/slic3r/Utils/Http.hpp b/xs/src/slic3r/Utils/Http.hpp index cf5712d96..f1302b0ed 100644 --- a/xs/src/slic3r/Utils/Http.hpp +++ b/xs/src/slic3r/Utils/Http.hpp @@ -73,8 +73,10 @@ public: // Same as above except also override the file's filename with a custom one Http& form_add_file(const std::string &name, const boost::filesystem::path &path, const std::string &filename); - // Add the file as POSTFIELD to the request, this can be used for hosts which do not support multipart requests - Http& postfield_add_file(const boost::filesystem::path &path); + // Set the file contents as a POST request body. + // The data is used verbatim, it is not additionally encoded in any way. + // This can be used for hosts which do not support multipart requests. + Http& set_post_body(const boost::filesystem::path &path); // Callback called on HTTP request complete Http& on_complete(CompleteFn fn); diff --git a/xs/src/slic3r/Utils/OctoPrint.cpp b/xs/src/slic3r/Utils/OctoPrint.cpp index c62f9b55c..db86d7697 100644 --- a/xs/src/slic3r/Utils/OctoPrint.cpp +++ b/xs/src/slic3r/Utils/OctoPrint.cpp @@ -19,6 +19,8 @@ OctoPrint::OctoPrint(DynamicPrintConfig *config) : cafile(config->opt_string("printhost_cafile")) {} +OctoPrint::~OctoPrint() {} + bool OctoPrint::test(wxString &msg) const { // Since the request is performed synchronously here, @@ -125,7 +127,7 @@ bool OctoPrint::send_gcode(const std::string &filename) const return res; } -bool OctoPrint::have_auto_discovery() const +bool OctoPrint::has_auto_discovery() const { return true; } diff --git a/xs/src/slic3r/Utils/OctoPrint.hpp b/xs/src/slic3r/Utils/OctoPrint.hpp index aea2ba58f..f6c4d58c8 100644 --- a/xs/src/slic3r/Utils/OctoPrint.hpp +++ b/xs/src/slic3r/Utils/OctoPrint.hpp @@ -17,13 +17,14 @@ class OctoPrint : public PrintHost { public: OctoPrint(DynamicPrintConfig *config); + virtual ~OctoPrint(); bool test(wxString &curl_msg) const; wxString get_test_ok_msg () const; wxString get_test_failed_msg (wxString &msg) const; // Send gcode file to octoprint, filename is expected to be in UTF-8 bool send_gcode(const std::string &filename) const; - bool have_auto_discovery() const; + bool has_auto_discovery() const; bool can_test() const; private: std::string host; diff --git a/xs/src/slic3r/Utils/PrintHostFactory.cpp b/xs/src/slic3r/Utils/PrintHost.cpp similarity index 73% rename from xs/src/slic3r/Utils/PrintHostFactory.cpp rename to xs/src/slic3r/Utils/PrintHost.cpp index 173c5d743..dd72bae40 100644 --- a/xs/src/slic3r/Utils/PrintHostFactory.cpp +++ b/xs/src/slic3r/Utils/PrintHost.cpp @@ -1,4 +1,3 @@ -#include "PrintHostFactory.hpp" #include "OctoPrint.hpp" #include "Duet.hpp" @@ -7,7 +6,9 @@ namespace Slic3r { -PrintHost * PrintHostFactory::get_print_host(DynamicPrintConfig *config) +PrintHost::~PrintHost() {} + +PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config) { PrintHostType kind = config->option>("host_type")->value; if (kind == htOctoPrint) { @@ -15,7 +16,8 @@ PrintHost * PrintHostFactory::get_print_host(DynamicPrintConfig *config) } else if (kind == htDuet) { return new Duet(config); } - return NULL; + return nullptr; } + } diff --git a/xs/src/slic3r/Utils/PrintHost.hpp b/xs/src/slic3r/Utils/PrintHost.hpp index 204740635..bc828ea46 100644 --- a/xs/src/slic3r/Utils/PrintHost.hpp +++ b/xs/src/slic3r/Utils/PrintHost.hpp @@ -1,6 +1,7 @@ #ifndef slic3r_PrintHost_hpp_ #define slic3r_PrintHost_hpp_ +#include #include #include @@ -13,14 +14,17 @@ class DynamicPrintConfig; class PrintHost { public: + virtual ~PrintHost(); virtual bool test(wxString &curl_msg) const = 0; virtual wxString get_test_ok_msg () const = 0; virtual wxString get_test_failed_msg (wxString &msg) const = 0; // Send gcode file to print host, filename is expected to be in UTF-8 virtual bool send_gcode(const std::string &filename) const = 0; - virtual bool have_auto_discovery() const = 0; + virtual bool has_auto_discovery() const = 0; virtual bool can_test() const = 0; + + static PrintHost* get_print_host(DynamicPrintConfig *config); }; diff --git a/xs/src/slic3r/Utils/PrintHostFactory.hpp b/xs/src/slic3r/Utils/PrintHostFactory.hpp deleted file mode 100644 index 4c9ff2bf2..000000000 --- a/xs/src/slic3r/Utils/PrintHostFactory.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef slic3r_PrintHostFactory_hpp_ -#define slic3r_PrintHostFactory_hpp_ - -#include -#include - - -namespace Slic3r { - -class DynamicPrintConfig; -class PrintHost; - -class PrintHostFactory -{ -public: - PrintHostFactory() {}; - ~PrintHostFactory() {}; - static PrintHost * get_print_host(DynamicPrintConfig *config); -}; - - -} - -#endif diff --git a/xs/src/slic3r/Utils/PrintHostSendDialog.cpp b/xs/src/slic3r/Utils/PrintHostSendDialog.cpp index b1dd86961..c5d441f87 100644 --- a/xs/src/slic3r/Utils/PrintHostSendDialog.cpp +++ b/xs/src/slic3r/Utils/PrintHostSendDialog.cpp @@ -36,9 +36,7 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_pr wxString stem(path.stem().wstring()); txt_filename->SetSelection(0, stem.Length()); - if (!can_start_print) { - box_print->Disable(); - } + box_print->Enable(can_start_print); Fit(); } diff --git a/xs/src/slic3r/Utils/PrintHostSendDialog.hpp b/xs/src/slic3r/Utils/PrintHostSendDialog.hpp index 7d2040d97..dc4a8d6f7 100644 --- a/xs/src/slic3r/Utils/PrintHostSendDialog.hpp +++ b/xs/src/slic3r/Utils/PrintHostSendDialog.hpp @@ -22,14 +22,12 @@ namespace Slic3r { class PrintHostSendDialog : public GUI::MsgDialog { - private: wxTextCtrl *txt_filename; wxCheckBox *box_print; bool can_start_print; public: - PrintHostSendDialog(const boost::filesystem::path &path, bool can_start_print); boost::filesystem::path filename() const; bool print() const; diff --git a/xs/xsp/Utils_PrintHost.xsp b/xs/xsp/Utils_PrintHost.xsp index 0c3fea137..59c09c431 100644 --- a/xs/xsp/Utils_PrintHost.xsp +++ b/xs/xsp/Utils_PrintHost.xsp @@ -7,4 +7,6 @@ %name{Slic3r::PrintHost} class PrintHost { bool send_gcode(std::string filename) const; + + static PrintHost* get_print_host(DynamicPrintConfig *config); }; diff --git a/xs/xsp/Utils_PrintHostFactory.xsp b/xs/xsp/Utils_PrintHostFactory.xsp deleted file mode 100644 index 2b083c957..000000000 --- a/xs/xsp/Utils_PrintHostFactory.xsp +++ /dev/null @@ -1,13 +0,0 @@ -%module{Slic3r::XS}; - -%{ -#include -#include "slic3r/Utils/PrintHostFactory.hpp" -%} - -%name{Slic3r::PrintHostFactory} class PrintHostFactory { - PrintHostFactory(); - ~PrintHostFactory(); - - static PrintHost * get_print_host(DynamicPrintConfig *config); -}; diff --git a/xs/xsp/my.map b/xs/xsp/my.map index aefe7b345..ba20ee236 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -239,11 +239,7 @@ Ref O_OBJECT_SLIC3R_T PresetUpdater* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T -PrintHostFactory* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T -Clone O_OBJECT_SLIC3R_T - -PrintHost* O_OBJECT_SLIC3R +PrintHost* O_OBJECT_SLIC3R Axis T_UV ExtrusionLoopRole T_UV From 43f8f10445d46c582904eca8749116b115e0f39a Mon Sep 17 00:00:00 2001 From: Martin Loidl Date: Tue, 21 Aug 2018 22:56:29 +0200 Subject: [PATCH 8/8] fixed timestamp for duet upload * Added missing time= --- xs/src/slic3r/Utils/Duet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/slic3r/Utils/Duet.cpp b/xs/src/slic3r/Utils/Duet.cpp index 517f02486..865d2b418 100644 --- a/xs/src/slic3r/Utils/Duet.cpp +++ b/xs/src/slic3r/Utils/Duet.cpp @@ -230,7 +230,7 @@ std::string Duet::timestamp_str() const auto tm = *std::localtime(&t); char buffer[BUFFER_SIZE]; - std::strftime(buffer, BUFFER_SIZE, "%Y-%d-%mT%H:%M:%S", &tm); + std::strftime(buffer, BUFFER_SIZE, "time=%Y-%d-%mT%H:%M:%S", &tm); return std::string(buffer); }