mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 12:11:15 -06:00 
			
		
		
		
	Merge remote-tracking branch 'origin/master' into feature_arrange_with_libnest2d
This commit is contained in:
		
						commit
						9036a11c7f
					
				
					 28 changed files with 692 additions and 147 deletions
				
			
		|  | @ -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/PrintHost.cpp | ||||
|     ${LIBDIR}/slic3r/Utils/PrintHost.hpp | ||||
|     ${LIBDIR}/slic3r/Utils/Bonjour.cpp | ||||
|     ${LIBDIR}/slic3r/Utils/Bonjour.hpp | ||||
|     ${LIBDIR}/slic3r/Utils/PresetUpdater.cpp | ||||
|  | @ -367,7 +373,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 | ||||
| ) | ||||
| 
 | ||||
|  | @ -411,7 +417,7 @@ set(XS_XSP_FILES | |||
|     ${XSP_DIR}/Surface.xsp | ||||
|     ${XSP_DIR}/SurfaceCollection.xsp | ||||
|     ${XSP_DIR}/TriangleMesh.xsp | ||||
|     ${XSP_DIR}/Utils_OctoPrint.xsp | ||||
|     ${XSP_DIR}/Utils_PrintHost.xsp | ||||
|     ${XSP_DIR}/Utils_PresetUpdater.xsp | ||||
|     ${XSP_DIR}/AppController.xsp | ||||
|     ${XSP_DIR}/XS.xsp | ||||
|  | @ -432,7 +438,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. | ||||
|  | @ -496,6 +503,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") | ||||
|  |  | |||
|  | @ -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."); | ||||
|  | @ -1140,25 +1137,37 @@ 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->cli = "octoprint-apikey=s"; | ||||
|     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<PrintHostType>::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<PrintHostType>(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 = "printhost-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->cli = "printhost-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->cli = "octoprint-host=s"; | ||||
|     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 = "print-host=s"; | ||||
|     def->default_value = new ConfigOptionString(""); | ||||
| 
 | ||||
|     def = this->add("only_retract_when_crossing_perimeters", coBool); | ||||
|  | @ -2110,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;
 | ||||
|  | @ -2124,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:
 | ||||
|  | @ -2133,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" | ||||
|     }; | ||||
| 
 | ||||
|  | @ -2145,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; | ||||
|     } | ||||
|  |  | |||
|  | @ -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<GCodeFlavor>::get_enum_ | |||
|     return keys_map; | ||||
| } | ||||
| 
 | ||||
| template<> inline t_config_enum_values& ConfigOptionEnum<PrintHostType>::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<InfillPattern>::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<PrintHostType> 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); | ||||
|     } | ||||
|  |  | |||
|  | @ -64,9 +64,9 @@ 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"); | ||||
| 
 | ||||
| SV* ConfigBase__as_hash(ConfigBase* THIS) | ||||
| { | ||||
|  |  | |||
|  | @ -586,6 +586,8 @@ boost::any& Choice::get_value() | |||
| 			m_value = static_cast<SupportMaterialPattern>(ret_enum); | ||||
| 		else if (m_opt_id.compare("seam_position") == 0) | ||||
| 			m_value = static_cast<SeamPosition>(ret_enum); | ||||
| 		else if (m_opt_id.compare("host_type") == 0) | ||||
| 			m_value = static_cast<PrintHostType>(ret_enum); | ||||
| 	}	 | ||||
| 
 | ||||
| 	return m_value; | ||||
|  |  | |||
|  | @ -604,6 +604,8 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt | |||
| 				config.set_key_value(opt_key, new ConfigOptionEnum<SupportMaterialPattern>(boost::any_cast<SupportMaterialPattern>(value))); | ||||
| 			else if (opt_key.compare("seam_position") == 0) | ||||
| 				config.set_key_value(opt_key, new ConfigOptionEnum<SeamPosition>(boost::any_cast<SeamPosition>(value))); | ||||
| 			else if (opt_key.compare("host_type") == 0) | ||||
| 				config.set_key_value(opt_key, new ConfigOptionEnum<PrintHostType>(boost::any_cast<PrintHostType>(value))); | ||||
| 			} | ||||
| 			break; | ||||
| 		case coPoints:{ | ||||
|  |  | |||
|  | @ -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<int>(config.option<ConfigOptionEnum<SupportMaterialPattern>>(opt_key)->value); | ||||
| 		} | ||||
| 		else if (opt_key.compare("seam_position") == 0) | ||||
| 		else if (opt_key.compare("seam_position") == 0){ | ||||
| 			ret = static_cast<int>(config.option<ConfigOptionEnum<SeamPosition>>(opt_key)->value); | ||||
| 		} | ||||
| 		else if (opt_key.compare("host_type") == 0){ | ||||
| 			ret = static_cast<int>(config.option<ConfigOptionEnum<PrintHostType>>(opt_key)->value); | ||||
| 		} | ||||
| 	} | ||||
| 		break; | ||||
| 	case coPoints: | ||||
|  |  | |||
|  | @ -329,8 +329,8 @@ const std::vector<std::string>& Preset::printer_options() | |||
|     static std::vector<std::string> 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", | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ | |||
| #include "../../libslic3r/Utils.hpp" | ||||
| 
 | ||||
| #include "slic3r/Utils/Http.hpp" | ||||
| #include "slic3r/Utils/OctoPrint.hpp" | ||||
| #include "slic3r/Utils/PrintHost.hpp" | ||||
| #include "slic3r/Utils/Serial.hpp" | ||||
| #include "BonjourDialog.hpp" | ||||
| #include "WipeTowerDialog.hpp" | ||||
|  | @ -1521,10 +1521,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 +1534,50 @@ 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.")) | ||||
| 					); | ||||
| 				std::unique_ptr<PrintHost> 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); | ||||
| 					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)); | ||||
| 				} | ||||
| 			}); | ||||
| 
 | ||||
| 			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 +1587,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 +1607,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,8 +1902,12 @@ void TabPrinter::update(){ | |||
| 			m_serial_test_btn->Disable(); | ||||
| 	} | ||||
| 
 | ||||
| 	m_octoprint_host_test_btn->Enable(!m_config->opt_string("octoprint_host").empty()); | ||||
| 	 | ||||
| 	{ | ||||
| 		std::unique_ptr<PrintHost> 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); | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
							
								
								
									
										279
									
								
								xs/src/slic3r/Utils/Duet.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										279
									
								
								xs/src/slic3r/Utils/Duet.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,279 @@ | |||
| #include "Duet.hpp" | ||||
| #include "PrintHostSendDialog.hpp" | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <ctime> | ||||
| #include <boost/filesystem/path.hpp> | ||||
| #include <boost/format.hpp> | ||||
| #include <boost/log/trivial.hpp> | ||||
| #include <boost/property_tree/ptree.hpp> | ||||
| #include <boost/property_tree/json_parser.hpp> | ||||
| 
 | ||||
| #include <wx/frame.h> | ||||
| #include <wx/event.h> | ||||
| #include <wx/progdlg.h> | ||||
| #include <wx/sizer.h> | ||||
| #include <wx/stattext.h> | ||||
| #include <wx/textctrl.h> | ||||
| #include <wx/checkbox.h> | ||||
| 
 | ||||
| #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")) | ||||
| {} | ||||
| 
 | ||||
| Duet::~Duet() {} | ||||
| 
 | ||||
| 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.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); | ||||
| 			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) { | ||||
| 					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::has_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 | ||||
| { | ||||
| 	enum { BUFFER_SIZE = 32 }; | ||||
| 
 | ||||
| 	auto t = std::time(nullptr); | ||||
| 	auto tm = *std::localtime(&t); | ||||
| 
 | ||||
| 	char buffer[BUFFER_SIZE]; | ||||
| 	std::strftime(buffer, BUFFER_SIZE, "time=%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) | ||||
| { | ||||
| 	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<int>("err", 0); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										47
									
								
								xs/src/slic3r/Utils/Duet.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								xs/src/slic3r/Utils/Duet.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,47 @@ | |||
| #ifndef slic3r_Duet_hpp_ | ||||
| #define slic3r_Duet_hpp_ | ||||
| 
 | ||||
| #include <string> | ||||
| #include <wx/string.h> | ||||
| 
 | ||||
| #include "PrintHost.hpp" | ||||
| 
 | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| 
 | ||||
| class DynamicPrintConfig; | ||||
| class Http; | ||||
| 
 | ||||
| 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 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; | ||||
| 	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 | ||||
|  | @ -4,6 +4,7 @@ | |||
| #include <functional> | ||||
| #include <thread> | ||||
| #include <deque> | ||||
| #include <sstream> | ||||
| #include <boost/filesystem/fstream.hpp> | ||||
| #include <boost/format.hpp> | ||||
| 
 | ||||
|  | @ -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<fs::ifstream> 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 set_post_body(const fs::path &path); | ||||
| 
 | ||||
| 	std::string curl_error(CURLcode curlcode); | ||||
| 	std::string body_size_error(); | ||||
|  | @ -187,6 +190,13 @@ void Http::priv::form_add_file(const char *name, const fs::path &path, const cha | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| void Http::priv::set_post_body(const fs::path &path) | ||||
| { | ||||
| 	std::ifstream file(path.string()); | ||||
| 	std::string file_content { std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>() }; | ||||
| 	postfields = file_content; | ||||
| } | ||||
| 
 | ||||
| std::string Http::priv::curl_error(CURLcode curlcode) | ||||
| { | ||||
| 	return (boost::format("%1% (%2%)") | ||||
|  | @ -229,6 +239,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 +353,12 @@ Http& Http::form_add_file(const std::string &name, const fs::path &path, const s | |||
| 	return *this; | ||||
| } | ||||
| 
 | ||||
| Http& Http::set_post_body(const fs::path &path) | ||||
| { | ||||
| 	if (p) { p->set_post_body(path);} | ||||
| 	return *this; | ||||
| } | ||||
| 
 | ||||
| Http& Http::on_complete(CompleteFn fn) | ||||
| { | ||||
| 	if (p) { p->completefn = std::move(fn); } | ||||
|  |  | |||
|  | @ -73,6 +73,11 @@ 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); | ||||
| 
 | ||||
| 	// 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); | ||||
| 	// Callback called on an error occuring at any stage of the requests: Url parsing, DNS lookup,
 | ||||
|  |  | |||
|  | @ -1,21 +1,11 @@ | |||
| #include "OctoPrint.hpp" | ||||
| #include "PrintHostSendDialog.hpp" | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <boost/filesystem/path.hpp> | ||||
| #include <boost/format.hpp> | ||||
| #include <boost/log/trivial.hpp> | ||||
| 
 | ||||
| #include <wx/frame.h> | ||||
| #include <wx/event.h> | ||||
| #include <wx/progdlg.h> | ||||
| #include <wx/sizer.h> | ||||
| #include <wx/stattext.h> | ||||
| #include <wx/textctrl.h> | ||||
| #include <wx/checkbox.h> | ||||
| 
 | ||||
| #include "libslic3r/PrintConfig.hpp" | ||||
| #include "slic3r/GUI/GUI.hpp" | ||||
| #include "slic3r/GUI/MsgDialog.hpp" | ||||
| #include "Http.hpp" | ||||
| 
 | ||||
| namespace fs = boost::filesystem; | ||||
|  | @ -23,49 +13,14 @@ 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")) | ||||
| {} | ||||
| 
 | ||||
| OctoPrint::~OctoPrint() {} | ||||
| 
 | ||||
| bool OctoPrint::test(wxString &msg) const | ||||
| { | ||||
| 	// Since the request is performed synchronously here,
 | ||||
|  | @ -91,6 +46,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 +64,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 +127,16 @@ bool OctoPrint::send_gcode(const std::string &filename) const | |||
| 	return res; | ||||
| } | ||||
| 
 | ||||
| bool OctoPrint::has_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); | ||||
|  |  | |||
|  | @ -4,6 +4,8 @@ | |||
| #include <string> | ||||
| #include <wx/string.h> | ||||
| 
 | ||||
| #include "PrintHost.hpp" | ||||
| 
 | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
|  | @ -11,14 +13,19 @@ namespace Slic3r { | |||
| class DynamicPrintConfig; | ||||
| class Http; | ||||
| 
 | ||||
| class OctoPrint | ||||
| 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 has_auto_discovery() const; | ||||
| 	bool can_test() const; | ||||
| private: | ||||
| 	std::string host; | ||||
| 	std::string apikey; | ||||
|  |  | |||
							
								
								
									
										23
									
								
								xs/src/slic3r/Utils/PrintHost.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								xs/src/slic3r/Utils/PrintHost.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | |||
| #include "OctoPrint.hpp" | ||||
| #include "Duet.hpp" | ||||
| 
 | ||||
| #include "libslic3r/PrintConfig.hpp" | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| 
 | ||||
| PrintHost::~PrintHost() {} | ||||
| 
 | ||||
| PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config) | ||||
| { | ||||
| 	PrintHostType kind = config->option<ConfigOptionEnum<PrintHostType>>("host_type")->value; | ||||
| 	if (kind == htOctoPrint) { | ||||
| 		return new OctoPrint(config); | ||||
| 	} else if (kind == htDuet) { | ||||
| 		return new Duet(config); | ||||
| 	} | ||||
| 	return nullptr; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										35
									
								
								xs/src/slic3r/Utils/PrintHost.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								xs/src/slic3r/Utils/PrintHost.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | |||
| #ifndef slic3r_PrintHost_hpp_ | ||||
| #define slic3r_PrintHost_hpp_ | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include <wx/string.h> | ||||
| 
 | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| 
 | ||||
| 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 has_auto_discovery() const = 0; | ||||
| 	virtual bool can_test() const = 0; | ||||
| 
 | ||||
| 	static PrintHost* get_print_host(DynamicPrintConfig *config); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										52
									
								
								xs/src/slic3r/Utils/PrintHostSendDialog.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								xs/src/slic3r/Utils/PrintHostSendDialog.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,52 @@ | |||
| #include "PrintHostSendDialog.hpp" | ||||
| 
 | ||||
| #include <wx/frame.h> | ||||
| #include <wx/event.h> | ||||
| #include <wx/progdlg.h> | ||||
| #include <wx/sizer.h> | ||||
| #include <wx/stattext.h> | ||||
| #include <wx/textctrl.h> | ||||
| #include <wx/checkbox.h> | ||||
| 
 | ||||
| #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()); | ||||
| 
 | ||||
| 	box_print->Enable(can_start_print); | ||||
| 
 | ||||
| 	Fit(); | ||||
| } | ||||
| 
 | ||||
| fs::path PrintHostSendDialog::filename() const  | ||||
| { | ||||
| 	return fs::path(txt_filename->GetValue().wx_str()); | ||||
| } | ||||
| 
 | ||||
| bool PrintHostSendDialog::print() const  | ||||
| {  | ||||
| 	return box_print->GetValue(); } | ||||
| } | ||||
							
								
								
									
										38
									
								
								xs/src/slic3r/Utils/PrintHostSendDialog.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								xs/src/slic3r/Utils/PrintHostSendDialog.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,38 @@ | |||
| #ifndef slic3r_PrintHostSendDialog_hpp_ | ||||
| #define slic3r_PrintHostSendDialog_hpp_ | ||||
| 
 | ||||
| #include <string> | ||||
| 
 | ||||
| #include <boost/filesystem/path.hpp> | ||||
| 
 | ||||
| #include <wx/string.h> | ||||
| #include <wx/frame.h> | ||||
| #include <wx/event.h> | ||||
| #include <wx/progdlg.h> | ||||
| #include <wx/sizer.h> | ||||
| #include <wx/stattext.h> | ||||
| #include <wx/textctrl.h> | ||||
| #include <wx/checkbox.h> | ||||
| 
 | ||||
| #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 | ||||
|  | @ -1,13 +0,0 @@ | |||
| %module{Slic3r::XS}; | ||||
| 
 | ||||
| %{ | ||||
| #include <xsinit.h> | ||||
| #include "slic3r/Utils/OctoPrint.hpp" | ||||
| %} | ||||
| 
 | ||||
| %name{Slic3r::OctoPrint} class OctoPrint { | ||||
|     OctoPrint(DynamicPrintConfig *config); | ||||
|     ~OctoPrint(); | ||||
| 
 | ||||
|     bool send_gcode(std::string filename) const; | ||||
| }; | ||||
							
								
								
									
										12
									
								
								xs/xsp/Utils_PrintHost.xsp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								xs/xsp/Utils_PrintHost.xsp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | |||
| %module{Slic3r::XS}; | ||||
| 
 | ||||
| %{ | ||||
| #include <xsinit.h> | ||||
| #include "slic3r/Utils/PrintHost.hpp" | ||||
| %} | ||||
| 
 | ||||
| %name{Slic3r::PrintHost} class PrintHost { | ||||
| 	bool send_gcode(std::string filename) const; | ||||
| 
 | ||||
| 	static PrintHost* get_print_host(DynamicPrintConfig *config); | ||||
| }; | ||||
|  | @ -239,9 +239,7 @@ Ref<TabIface> 				O_OBJECT_SLIC3R_T | |||
| PresetUpdater*              O_OBJECT_SLIC3R | ||||
| Ref<PresetUpdater>          O_OBJECT_SLIC3R_T | ||||
| 
 | ||||
| OctoPrint*                  O_OBJECT_SLIC3R | ||||
| Ref<OctoPrint>              O_OBJECT_SLIC3R_T | ||||
| Clone<OctoPrint>            O_OBJECT_SLIC3R_T | ||||
| PrintHost*                  O_OBJECT_SLIC3R | ||||
| 
 | ||||
| Axis                  T_UV | ||||
| ExtrusionLoopRole     T_UV | ||||
|  |  | |||
|  | @ -270,3 +270,4 @@ | |||
| }; | ||||
| %typemap{AppController*}; | ||||
| %typemap{PrintController*}; | ||||
| %typemap{PrintHost*}; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 tamasmeszaros
						tamasmeszaros