diff --git a/deps/+LibBGCode/LibBGCode.cmake b/deps/+LibBGCode/LibBGCode.cmake index 484c0748fa..8f2ead2585 100644 --- a/deps/+LibBGCode/LibBGCode.cmake +++ b/deps/+LibBGCode/LibBGCode.cmake @@ -1,8 +1,8 @@ set(LibBGCode_SOURCE_DIR "" CACHE PATH "Optionally specify local LibBGCode source directory") set(_source_dir_line - URL https://github.com/prusa3d/libbgcode/archive/8ae75bd0eea622f0e34cae311b3bd065b55eae9b.zip - URL_HASH SHA256=141a8cea3baea6066527389fda734057181414c4fa296d34501ef8f69ea412e9) + URL https://github.com/prusa3d/libbgcode/archive/d20e3a3a6d1ee3d8026bf20c5a9ce2ad88f4b433.zip + URL_HASH SHA256=bfcba51262bdda6f0bf06b9508f4386c50145d3e97bba6c76ee1f1520dae3b93) if (LibBGCode_SOURCE_DIR) set(_source_dir_line "SOURCE_DIR;${LibBGCode_SOURCE_DIR};BUILD_ALWAYS;ON") diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index c310d419fd..d1323c8b31 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -957,8 +957,8 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail // printer data - this section contains duplicates from the slicer metadata // that we just created. Find and copy the entries that we want to duplicate. const auto& slicer_metadata = binary_data.slicer_metadata.raw_data; - const std::vector keys_to_duplicate = { "printer_model", "filament_type", "nozzle_diameter", "bed_temperature", - "brim_width", "fill_density", "layer_height", "temperature", "ironing", "support_material", "extruder_colour" }; + const std::vector keys_to_duplicate = { "printer_model", "filament_type", "filament_abrasive", "nozzle_diameter", "nozzle_high_flow", "bed_temperature", + "brim_width", "fill_density", "layer_height", "temperature", "ironing", "support_material", "extruder_colour"}; assert(std::is_sorted(slicer_metadata.begin(), slicer_metadata.end(), [](const auto& a, const auto& b) { return a.first < b.first; })); for (const std::string& key : keys_to_duplicate) { diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 96a5572435..fe943d8cb8 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -165,6 +165,13 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem res.repo_id = ""; } + const auto repo_prefix = vendor_section.find("repo_prefix"); + if (repo_prefix != vendor_section.not_found()) { + res.repo_prefix = repo_prefix->second.data(); + } else { + res.repo_prefix = ""; + } + if (! load_all) { return res; } @@ -486,7 +493,7 @@ static std::vector s_Preset_print_options { }; static std::vector s_Preset_filament_options { - "filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed", "filament_infill_max_speed", "filament_infill_max_crossing_speed", + "filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_abrasive", "filament_notes", "filament_max_volumetric_speed", "filament_infill_max_speed", "filament_infill_max_crossing_speed", "extrusion_multiplier", "filament_density", "filament_cost", "filament_spool_weight", "filament_loading_speed", "filament_loading_speed_start", "filament_load_time", "filament_unloading_speed", "filament_unloading_speed_start", "filament_unload_time", "filament_toolchange_delay", "filament_cooling_moves", "filament_stamping_loading_speed", "filament_stamping_distance", "filament_cooling_initial_speed", "filament_purge_multiplier", "filament_cooling_final_speed", "filament_ramming_parameters", "filament_minimal_purge_on_wipe_tower", @@ -525,7 +532,8 @@ static std::vector s_Preset_printer_options { "cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "multimaterial_purging", "max_print_height", "default_print_profile", "inherits", "remaining_times", "silent_mode", - "machine_limits_usage", "thumbnails", "thumbnails_format" + "machine_limits_usage", "thumbnails", "thumbnails_format", + "nozzle_high_flow" }; static std::vector s_Preset_sla_print_options { diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index a2985ac4ec..5b430442f4 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -40,6 +40,7 @@ public: std::string config_update_url; std::string changelog_url; std::string repo_id; + std::string repo_prefix; bool templates_profile { false }; struct PrinterVariant { diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index cd358951b7..1bd340d165 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -114,6 +114,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n "fan_always_on", "fan_below_layer_time", "full_fan_speed_layer", + "filament_abrasive", "filament_colour", "filament_diameter", "filament_density", @@ -126,6 +127,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n "first_layer_speed_over_raft", "gcode_comments", "gcode_label_objects", + "nozzle_high_flow", "infill_acceleration", "layer_gcode", "min_fan_speed", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index a22656ae46..fbe9fbaa51 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1370,6 +1370,12 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionBools { false }); + def = this->add("filament_abrasive", coBools); + def->label = L("Abrasive material"); + def->tooltip = L("This flag means that the material is abrasive and requires a hardened nozzle. The value is used by the printer to check it."); + def->mode = comExpert; + def->set_default_value(new ConfigOptionBools { false }); + def = this->add("filament_cost", coFloats); def->label = L("Cost"); def->tooltip = L("Enter your filament cost per kg here. This is only for statistical information."); @@ -2550,6 +2556,12 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->set_default_value(new ConfigOptionBools{false}); + def = this->add("nozzle_high_flow", coBools); + def->label = L("High flow nozzle"); + def->tooltip = L("High flow nozzles allow higher print speeds."); + def->mode = comExpert; + def->set_default_value(new ConfigOptionBools{false}); + def = this->add("retract_lift", coFloats); def->label = L("Lift height"); def->tooltip = L("Lift height applied before travel."); @@ -3715,7 +3727,7 @@ void PrintConfigDef::init_extruder_option_keys() "retract_before_wipe", "retract_restart_extra", "retract_before_travel", "wipe", "travel_slope", "travel_max_lift", "travel_ramping_lift", "travel_lift_before_obstacle", "retract_layer_change", "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour", - "default_filament_profile" + "default_filament_profile", "nozzle_high_flow" }; m_extruder_retract_keys = { diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index e3769e4313..fb89debbd6 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -782,6 +782,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloats, filament_density)) ((ConfigOptionStrings, filament_type)) ((ConfigOptionBools, filament_soluble)) + ((ConfigOptionBools, filament_abrasive)) ((ConfigOptionFloats, filament_cost)) ((ConfigOptionFloats, filament_spool_weight)) ((ConfigOptionFloats, filament_max_volumetric_speed)) @@ -825,6 +826,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloats, travel_max_lift)) ((ConfigOptionFloats, travel_slope)) ((ConfigOptionBools, travel_lift_before_obstacle)) + ((ConfigOptionBools, nozzle_high_flow)) ((ConfigOptionPercents, retract_before_wipe)) ((ConfigOptionFloats, retract_length)) ((ConfigOptionFloats, retract_length_toolchange)) diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index d2b2459a5a..e09515f08c 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -23,6 +23,8 @@ set(SLIC3R_GUI_SOURCES GUI/UserAccountCommunication.hpp GUI/UserAccountSession.cpp GUI/UserAccountSession.hpp + GUI/UserAccountUtils.cpp + GUI/UserAccountUtils.hpp GUI/UserAccount.cpp GUI/UserAccount.hpp GUI/WebViewDialog.cpp diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 805d012a43..1ef8b24ffc 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -101,6 +101,7 @@ #include "PhysicalPrinterDialog.hpp" #include "WifiConfigDialog.hpp" #include "UserAccount.hpp" +#include "UserAccountUtils.hpp" #include "WebViewDialog.hpp" #include "LoginDialog.hpp" // IWYU pragma: keep #include "PresetArchiveDatabase.hpp" @@ -3823,14 +3824,102 @@ bool GUI_App::select_printer_preset(const Preset* preset) return is_installed; } +namespace { +const Preset* find_preset_by_nozzle_and_options( + const PrinterPresetCollection& collection + , const std::string& model_id + , std::map>& options) +{ + // find all matching presets when repo prefix is ommited + std::vector results; + for (const Preset &preset : collection) { + // trim repo prefix + std::string printer_model = preset.config.opt_string("printer_model"); + std::string vendor_repo_prefix; + if (preset.vendor) { + vendor_repo_prefix = preset.vendor->repo_prefix; + } else if (std::string inherits = preset.inherits(); !inherits.empty()) { + const Preset *parent = wxGetApp().preset_bundle->printers.find_preset(inherits); + if (parent && parent->vendor) { + vendor_repo_prefix = parent->vendor->repo_prefix; + } + } + if (printer_model.find(vendor_repo_prefix) == 0) { + printer_model = printer_model.substr(vendor_repo_prefix.size() + ); + boost::trim_left(printer_model); + } + + if (!preset.is_system || printer_model != model_id) + continue; + // options (including nozzle_diameter) + bool failed = false; + for (const auto& opt : options) { + assert(preset.config.has(opt.first)); + // We compare only first value now, but options contains data for all (some might be empty tho) + std::string opt_val; + if (preset.config.option(opt.first)->is_scalar()) { + opt_val = preset.config.option(opt.first)->serialize(); + } else { + switch (preset.config.option(opt.first)->type()) { + case coInts: opt_val = std::to_string(static_cast(preset.config.option(opt.first))->values[0]); break; + case coFloats: + opt_val = into_u8(double_to_string(static_cast(preset.config.option(opt.first))->values[0])); + if (size_t pos = opt_val.find(",") != std::string::npos) + opt_val.replace(pos, 1, 1, '.'); + break; + case coStrings: opt_val = static_cast(preset.config.option(opt.first))->values[0]; break; + case coBools: opt_val = static_cast(preset.config.option(opt.first))->values[0] ? "1" : "0"; break; + default: + assert(true); + continue; + } + } + + if (opt_val != opt.second[0]) + { + failed = true; + break; + } + } + if (!failed) { + results.push_back(&preset); + } + } + // find visible without prefix + for (const Preset *preset : results) { + if (preset->is_visible && preset->config.opt_string("printer_model") == model_id) { + return preset; + } + } + // find one visible + for (const Preset *preset : results) { + if (preset->is_visible) { + return preset; + } + } + // find one without prefix + for (const Preset* preset : results) { + if (preset->config.opt_string("printer_model") == model_id) { + return preset; + } + } + if (results.size() != 0) { + return results.front(); + } + return nullptr; +} +} + bool GUI_App::select_printer_from_connect(const std::string& msg) { - // parse message - std::string model_name = plater()->get_user_account()->get_keyword_from_json(msg, "printer_model"); - std::string uuid = plater()->get_user_account()->get_keyword_from_json(msg, "uuid"); + // parse message "binary_gcode" + boost::property_tree::ptree ptree; + std::string model_name = UserAccountUtils::get_keyword_from_json(ptree, msg, "printer_model"); + std::string uuid = UserAccountUtils::get_keyword_from_json(ptree, msg, "uuid"); if (model_name.empty()) { std::vector compatible_printers; - plater()->get_user_account()->fill_supported_printer_models_from_json(msg, compatible_printers); + UserAccountUtils::fill_supported_printer_models_from_json(ptree, compatible_printers); if (!compatible_printers.empty()) { model_name = compatible_printers.front(); } @@ -3839,10 +3928,23 @@ bool GUI_App::select_printer_from_connect(const std::string& msg) BOOST_LOG_TRIVIAL(error) << "Failed to select printer from Connect. Printer_model is empty."; return false; } - std::string nozzle = plater()->get_user_account()->get_nozzle_from_json(msg); - BOOST_LOG_TRIVIAL(info) << "Select printer from Connect. Model: " << model_name << "nozzle: " << nozzle; + std::map> config_options_to_match; + UserAccountUtils::fill_config_options_from_json(ptree, config_options_to_match); + // prevent not having nozzle diameter + if (config_options_to_match.find("nozzle_diameter") == config_options_to_match.end()) { + std::string diameter = UserAccountUtils::get_keyword_from_json(ptree, msg, "nozzle_diameter"); + if (!diameter.empty()) + config_options_to_match["nozzle_diameter"] = {diameter}; + } + // log + BOOST_LOG_TRIVIAL(info) << "Select printer from Connect. Model: " << model_name; + for(const auto& pair :config_options_to_match) { + std::string out; + for(const std::string& val :pair.second) { out += val + ",";} + BOOST_LOG_TRIVIAL(info) << pair.first << ": " << out; + } // select printer - const Preset* printer_preset = preset_bundle->printers.find_system_preset_by_model_and_variant(model_name, nozzle); + const Preset* printer_preset = find_preset_by_nozzle_and_options(preset_bundle->printers, model_name, config_options_to_match); bool is_installed = printer_preset && select_printer_preset(printer_preset); // notification std::string out = printer_preset ? @@ -3871,11 +3973,14 @@ bool GUI_App::select_filament_preset(const Preset* preset, size_t extruder_index assert(preset->is_visible); return preset_bundle->extruders_filaments[extruder_index].select_filament(preset->name); } -void GUI_App::search_and_select_filaments(const std::string& material, size_t extruder_index, std::string& out_message) +void GUI_App::search_and_select_filaments(const std::string& material, bool avoid_abrasive, size_t extruder_index, std::string& out_message) { const Preset* preset = preset_bundle->extruders_filaments[extruder_index].get_selected_preset(); // selected is ok - if (!preset->is_default && preset->config.has("filament_type") && preset->config.option("filament_type")->serialize() == material) { + if (!preset->is_default && preset->config.has("filament_type") + && (!avoid_abrasive || preset->config.option("filament_abrasive")->values[0] == false) + && preset->config.option("filament_type")->serialize() == material) + { return; } // find installed compatible filament that is Prusa with suitable type and select it @@ -3885,6 +3990,7 @@ void GUI_App::search_and_select_filaments(const std::string& material, size_t ex && filament.preset->is_visible && (!filament.preset->vendor || !filament.preset->vendor->templates_profile) && filament.preset->config.has("filament_type") + && (!avoid_abrasive || filament.preset->config.option("filament_abrasive")->values[0] == false) && filament.preset->config.option("filament_type")->serialize() == material && filament.preset->name.compare(0, 9, "Prusament") == 0 && select_filament_preset(filament.preset, extruder_index) @@ -3903,6 +4009,7 @@ void GUI_App::search_and_select_filaments(const std::string& material, size_t ex && filament.preset->is_visible && (!filament.preset->vendor || !filament.preset->vendor->templates_profile) && filament.preset->config.has("filament_type") + && (!avoid_abrasive || filament.preset->config.option("filament_abrasive")->values[0] == false) && filament.preset->config.option("filament_type")->serialize() == material && select_filament_preset(filament.preset, extruder_index) ) @@ -3920,6 +4027,7 @@ void GUI_App::search_and_select_filaments(const std::string& material, size_t ex && !filament.preset->is_default && (!filament.preset->vendor || !filament.preset->vendor->templates_profile) && filament.preset->config.has("filament_type") + && (!avoid_abrasive || filament.preset->config.option("filament_abrasive")->values[0] == false) && filament.preset->config.option("filament_type")->serialize() == material && filament.preset->name.compare(0, 9, "Prusament") == 0 && select_filament_preset(filament.preset, extruder_index)) @@ -3935,7 +4043,8 @@ void GUI_App::select_filament_from_connect(const std::string& msg) { // parse message std::vector materials; - plater()->get_user_account()->fill_material_from_json(msg, materials); + std::vector avoid_abrasive; + UserAccountUtils::fill_material_from_json(msg, materials, avoid_abrasive); if (materials.empty()) { BOOST_LOG_TRIVIAL(error) << "Failed to select filament from Connect. No material data."; return; @@ -3944,11 +4053,14 @@ void GUI_App::select_filament_from_connect(const std::string& msg) size_t extruder_count = preset_bundle->extruders_filaments.size(); if (extruder_count != materials.size()) { BOOST_LOG_TRIVIAL(error) << format("Failed to select filament from Connect. Selected printer has %1% extruders while data from Connect contains %2% materials.", extruder_count, materials.size()); + plater()->get_notification_manager()->close_notification_of_type(NotificationType::SelectFilamentFromConnect); + // TRN: Notification text. + plater()->get_notification_manager()->push_notification(NotificationType::SelectFilamentFromConnect, NotificationManager::NotificationLevel::ImportantNotificationLevel, _u8L("Failed to select filament from Connect.")); return; } std::string notification_text; for (size_t i = 0; i < extruder_count; i++) { - search_and_select_filaments(materials[i], i, notification_text); + search_and_select_filaments(materials[i], avoid_abrasive.size() > i ? avoid_abrasive[i] : false, i, notification_text); } // When all filaments are selected/intalled, @@ -3969,7 +4081,8 @@ void GUI_App::handle_connect_request_printer_select(const std::string& msg) // Here comes code from ConnectWebViewPanel // It only contains uuid of a printer to be selected // Lets queue it and wait on result. The result is send via event to plater, where it is send to handle_connect_request_printer_select_inner - std::string uuid = plater()->get_user_account()->get_keyword_from_json(msg, "uuid"); + boost::property_tree::ptree ptree; + std::string uuid = UserAccountUtils::get_keyword_from_json(ptree, msg, "uuid"); plater()->get_user_account()->enqueue_printer_data_action(uuid); } void GUI_App::handle_connect_request_printer_select_inner(const std::string & msg) @@ -3977,7 +4090,6 @@ void GUI_App::handle_connect_request_printer_select_inner(const std::string & ms BOOST_LOG_TRIVIAL(debug) << "Handling web request: " << msg; // return to plater this->mainframe->select_tab(size_t(0)); - if (!select_printer_from_connect(msg)) { // If printer was not selected, do not select filament. return; diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 6d7008f0dd..e915576943 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -416,7 +416,7 @@ public: // return true if preset vas invisible and we have to installed it to make it selectable bool select_printer_preset(const Preset* printer_preset); bool select_filament_preset(const Preset* filament_preset, size_t extruder_index); - void search_and_select_filaments(const std::string& material, size_t extruder_index, std::string& out_message); + void search_and_select_filaments(const std::string& material, bool avoid_abrasive, size_t extruder_index, std::string& out_message); void handle_script_message(std::string msg) {} void request_model_download(std::string import_json) {} void download_project(std::string project_id) {} diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp index 4cae18c133..10b05d62c2 100644 --- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp +++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp @@ -728,9 +728,10 @@ void PhysicalPrinterDialog::update_host_type(bool printer_change) for (PresetForPrinter* prstft : m_presets) { std::string preset_name = prstft->get_preset_name(); if (Preset* preset = wxGetApp().preset_bundle->printers.find_preset(preset_name)) { - std::string model_id = preset->config.opt_string("printer_model"); + std::string model_id = preset->config.opt_string("printer_model"); if (preset->vendor) { - if (preset->vendor->name == "Prusa Research") { + // No need to remove prefix from printer_model, family is not prefixed + if (preset->vendor->name.find("Prusa Research") != std::string::npos) { const std::vector& models = preset->vendor->models; auto it = std::find_if(models.begin(), models.end(), [model_id](const VendorProfile::PrinterModel& model) { return model.id == model_id; }); @@ -754,11 +755,16 @@ void PhysicalPrinterDialog::update_host_type(bool printer_change) break; } std::string model_id = preset->config.opt_string("printer_model"); - if (preset->vendor && preset->vendor->name != "Prusa Research") { - connect.supported = false; - break; + // remove prefix from printer_model + if (preset->vendor) { + std::string vendor_repo_prefix; + vendor_repo_prefix = preset->vendor->repo_prefix; + if (model_id.find(vendor_repo_prefix) == 0) { + model_id = model_id.substr(vendor_repo_prefix.size()); + boost::trim_left(model_id); + } } - if (preset->vendor && preset->vendor->name != "Prusa Research") { + if (preset->vendor && preset->vendor->name.find("Prusa Research") == std::string::npos) { connect.supported = false; break; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 162c8b09bf..9c70d60074 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -123,6 +123,7 @@ #include "Gizmos/GLGizmoCut.hpp" #include "FileArchiveDialog.hpp" #include "UserAccount.hpp" +#include "UserAccountUtils.hpp" #include "DesktopIntegrationDialog.hpp" #include "WebViewDialog.hpp" #include "PresetArchiveDatabase.hpp" @@ -2059,6 +2060,22 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool if (full_config.has("binary_gcode")) // needed for SLA full_config.set("binary_gcode", bool(full_config.opt_bool("binary_gcode") & wxGetApp().app_config->get_bool("use_binary_gcode_when_supported"))); + const Preset &selected_printer = wxGetApp().preset_bundle->printers.get_selected_preset(); + std::string printer_model_serialized = full_config.option("printer_model")->serialize(); + std::string vendor_repo_prefix; + if (selected_printer.vendor) { + vendor_repo_prefix = selected_printer.vendor->repo_prefix; + } else if (std::string inherits = selected_printer.inherits(); !inherits.empty()) { + const Preset *parent = wxGetApp().preset_bundle->printers.find_preset(inherits); + if (parent && parent->vendor) { + vendor_repo_prefix = parent->vendor->repo_prefix; + } + } + if (printer_model_serialized.find(vendor_repo_prefix) == 0) { + printer_model_serialized = printer_model_serialized.substr(vendor_repo_prefix.size()); + boost::trim_left(printer_model_serialized); + full_config.set("printer_model", printer_model_serialized); + } // If the update_background_process() was not called by the timer, kill the timer, // so the update_restart_background_process() will not be called again in vain. background_process_timer.Stop(); @@ -3648,7 +3665,9 @@ bool Plater::priv::can_show_upload_to_connect() const vendor_id = parent->vendor->id; } } - return vendor_id.compare(0, 5, "Prusa") == 0; + // Upload to Connect should show only for prusa printers + // Some vendors might have prefixed name due to repository id. + return vendor_id.find("Prusa") != std::string::npos; } void Plater::priv::show_action_buttons(const bool ready_to_slice_) const @@ -6022,10 +6041,11 @@ void Plater::connect_gcode() */ const Preset* selected_printer_preset = &wxGetApp().preset_bundle->printers.get_selected_preset(); - const std::string filename = p->user_account->get_keyword_from_json(dialog_msg, "filename"); - const std::string team_id = p->user_account->get_keyword_from_json(dialog_msg, "team_id"); + boost::property_tree::ptree ptree; + const std::string filename = UserAccountUtils::get_keyword_from_json(ptree, dialog_msg, "filename"); + const std::string team_id = UserAccountUtils::get_keyword_from_json(ptree, dialog_msg, "team_id"); - std::string data_subtree = p->user_account->get_print_data_from_json(dialog_msg, "data"); + std::string data_subtree = UserAccountUtils::get_print_data_from_json(dialog_msg, "data"); if (filename.empty() || team_id.empty() || data_subtree.empty()) { std::string msg = _u8L("Failed to read response from Prusa Connect server. Upload is cancelled."); BOOST_LOG_TRIVIAL(error) << msg; diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 6b3aea7f6d..733a2c6907 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -970,16 +970,47 @@ static std::string get_connect_state_suffix_for_printer(const Preset& printer_pr !printer_state_map.empty()) { for (const auto& [printer_model_nozzle_pair, states] : printer_state_map) { - if (printer_model_nozzle_pair.first == printer_preset.config.opt_string("printer_model") - && printer_model_nozzle_pair.second == printer_preset.config.opt_string("printer_variant")) - { - PrinterStatesCount states_cnt = get_printe_states_count(states); + std::string printer_model = printer_preset.config.opt_string("printer_model"); + std::string vendor_repo_prefix; + if (printer_preset.vendor) { + vendor_repo_prefix = printer_preset.vendor->repo_prefix; + } else if (std::string inherits = printer_preset.inherits(); !inherits.empty()) { + const Preset *parent = wxGetApp().preset_bundle->printers.find_preset(inherits); + if (parent && parent->vendor) { + vendor_repo_prefix = parent->vendor->repo_prefix; + } + } + if (printer_model.find(vendor_repo_prefix) == 0) { + printer_model = printer_model.substr(vendor_repo_prefix.size()); + boost::trim_left(printer_model); + } - if (states_cnt.available_cnt > 0) - return "_available"; - if (states_cnt.busy_cnt > 0) - return "_busy"; - return "_offline"; + if (printer_preset.config.has("nozzle_diameter")) { + double nozzle_diameter = static_cast(printer_preset.config.option("nozzle_diameter"))->values[0]; + wxString nozzle_diameter_serialized = double_to_string(nozzle_diameter); + nozzle_diameter_serialized.Replace(L",", L"."); + + if (printer_model_nozzle_pair.first == printer_model + && printer_model_nozzle_pair.second == GUI::into_u8(nozzle_diameter_serialized)) + { + PrinterStatesCount states_cnt = get_printe_states_count(states); + + if (states_cnt.available_cnt > 0) + return "_available"; + if (states_cnt.busy_cnt > 0) + return "_busy"; + return "_offline"; + } + } else { + if (printer_model_nozzle_pair.first == printer_model) { + PrinterStatesCount states_cnt = get_printe_states_count(states); + + if (states_cnt.available_cnt > 0) + return "_available"; + if (states_cnt.busy_cnt > 0) + return "_busy"; + return "_offline"; + } } } } @@ -1002,21 +1033,56 @@ static bool fill_data_to_connect_info_line( const Preset& printer_preset, !printer_state_map.empty()) { for (const auto& [printer_model_nozzle_pair, states] : printer_state_map) { - if (printer_model_nozzle_pair.first == printer_preset.config.opt_string("printer_model") - && printer_model_nozzle_pair.second == printer_preset.config.opt_string("printer_variant")) - { - PrinterStatesCount states_cnt = get_printe_states_count(states); + // get printer_model without repo prefix + std::string printer_model = printer_preset.config.opt_string("printer_model"); + std::string vendor_repo_prefix; + if (printer_preset.vendor) { + vendor_repo_prefix = printer_preset.vendor->repo_prefix; + } else if (std::string inherits = printer_preset.inherits(); !inherits.empty()) { + const Preset *parent = wxGetApp().preset_bundle->printers.find_preset(inherits); + if (parent && parent->vendor) { + vendor_repo_prefix = parent->vendor->repo_prefix; + } + } + if (printer_model.find(vendor_repo_prefix) == 0) { + printer_model = printer_model.substr(vendor_repo_prefix.size()); + boost::trim_left(printer_model); + } + if (printer_preset.config.has("nozzle_diameter")) { + double nozzle_diameter = static_cast(printer_preset.config.option("nozzle_diameter"))->values[0]; + wxString nozzle_diameter_serialized = double_to_string(nozzle_diameter); + nozzle_diameter_serialized.Replace(L",", L"."); + + if (printer_model_nozzle_pair.first == printer_model + && printer_model_nozzle_pair.second == GUI::into_u8(nozzle_diameter_serialized)) + { + PrinterStatesCount states_cnt = get_printe_states_count(states); #ifdef _WIN32 - connect_available_info->SetLabelMarkup(format_wxstr("%1% %2%", format("%1%", states_cnt.available_cnt), _L("available"))); - connect_offline_info ->SetLabelMarkup(format_wxstr("%1% %2%", format("%1%", states_cnt.offline_cnt), _L("offline"))); - connect_printing_info ->SetLabelMarkup(format_wxstr("%1% %2%", format("%1%", states_cnt.busy_cnt), _L("printing"))); + connect_available_info->SetLabelMarkup(format_wxstr("%1% %2%", format("%1%", states_cnt.available_cnt), _L("available"))); + connect_offline_info ->SetLabelMarkup(format_wxstr("%1% %2%", format("%1%", states_cnt.offline_cnt), _L("offline"))); + connect_printing_info ->SetLabelMarkup(format_wxstr("%1% %2%", format("%1%", states_cnt.busy_cnt), _L("printing"))); #else - connect_available_info->SetLabel(format_wxstr("%1% ", states_cnt.available_cnt)); - connect_offline_info ->SetLabel(format_wxstr("%1% ", states_cnt.offline_cnt)); - connect_printing_info ->SetLabel(format_wxstr("%1% ", states_cnt.busy_cnt)); + connect_available_info->SetLabel(format_wxstr("%1% ", states_cnt.available_cnt)); + connect_offline_info ->SetLabel(format_wxstr("%1% ", states_cnt.offline_cnt)); + connect_printing_info ->SetLabel(format_wxstr("%1% ", states_cnt.busy_cnt)); #endif - return true; + return true; + } + } else { + if (printer_model_nozzle_pair.first == printer_model) { + PrinterStatesCount states_cnt = get_printe_states_count(states); +#ifdef _WIN32 + connect_available_info->SetLabelMarkup(format_wxstr("%1% %2%", format("%1%", states_cnt.available_cnt), _L("available"))); + connect_offline_info ->SetLabelMarkup(format_wxstr("%1% %2%", format("%1%", states_cnt.offline_cnt), _L("offline"))); + connect_printing_info ->SetLabelMarkup(format_wxstr("%1% %2%", format("%1%", states_cnt.busy_cnt), _L("printing"))); +#else + connect_available_info->SetLabel(format_wxstr("%1% ", states_cnt.available_cnt)); + connect_offline_info ->SetLabel(format_wxstr("%1% ", states_cnt.offline_cnt)); + connect_printing_info ->SetLabel(format_wxstr("%1% ", states_cnt.busy_cnt)); +#endif + return true; + } } } } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index c68c7128fc..23bf15da15 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2227,6 +2227,7 @@ void TabFilament::build() option.opt.width = Field::def_width(); optgroup->append_single_option_line(option); optgroup->append_single_option_line("filament_soluble"); + optgroup->append_single_option_line("filament_abrasive"); optgroup = page->new_optgroup(L("Print speed override")); optgroup->append_single_option_line("filament_max_volumetric_speed", "max-volumetric-speed_127176"); @@ -2653,22 +2654,26 @@ void TabPrinter::build_fff() if (boost::any_cast(value) && m_extruders_count > 1) { SuppressBackgroundProcessingUpdate sbpu; std::vector nozzle_diameters = static_cast(m_config->option("nozzle_diameter"))->values; - const double frst_diam = nozzle_diameters[0]; + std::vector high_flow_nozzles = static_cast(m_config->option("nozzle_high_flow"))->values; + assert(nozzle_diameters.size() == high_flow_nozzles.size()); - for (auto cur_diam : nozzle_diameters) { + for (size_t i = 1; i < nozzle_diameters.size(); ++i) { // if value is differs from first nozzle diameter value - if (fabs(cur_diam - frst_diam) > EPSILON) { + if (fabs(nozzle_diameters[i] - nozzle_diameters[0]) > EPSILON || high_flow_nozzles[i] != high_flow_nozzles[0]) { const wxString msg_text = _(L("Single Extruder Multi Material is selected, \n" - "and all extruders must have the same diameter.\n" - "Do you want to change the diameter for all extruders to first extruder nozzle diameter value?")); - MessageDialog dialog(parent(), msg_text, _(L("Nozzle diameter")), wxICON_WARNING | wxYES_NO); + "and all extruders must have the same diameter and 'High flow' state.\n" + "Do you want to change these values for all extruders to first extruder values?")); + MessageDialog dialog(parent(), msg_text, _(L("Nozzle settings mismatch")), wxICON_WARNING | wxYES_NO); DynamicPrintConfig new_conf = *m_config; if (dialog.ShowModal() == wxID_YES) { - for (size_t i = 1; i < nozzle_diameters.size(); i++) - nozzle_diameters[i] = frst_diam; + for (size_t i = 1; i < nozzle_diameters.size(); i++) { + nozzle_diameters[i] = nozzle_diameters[0]; + high_flow_nozzles[i] = high_flow_nozzles[0]; + } new_conf.set_key_value("nozzle_diameter", new ConfigOptionFloats(nozzle_diameters)); + new_conf.set_key_value("nozzle_high_flow", new ConfigOptionBools(high_flow_nozzles)); } else new_conf.set_key_value("single_extruder_multi_material", new ConfigOptionBool(false)); @@ -3132,7 +3137,7 @@ const std::vector extruder_options = { "retract_length", "retract_lift", "retract_lift_above", "retract_lift_below", "retract_speed", "deretract_speed", "retract_restart_extra", "retract_before_travel", "retract_layer_change", "wipe", "retract_before_wipe", "travel_ramping_lift", - "travel_slope", "travel_max_lift", "travel_lift_before_obstacle", + "travel_slope", "travel_max_lift", "travel_lift_before_obstacle", "nozzle_high_flow", "retract_length_toolchange", "retract_restart_extra_toolchange", }; @@ -3150,7 +3155,8 @@ void TabPrinter::build_extruder_pages(size_t n_before_extruders) optgroup->on_change = [this, extruder_idx](const t_config_option_key&opt_key, boost::any value) { const bool is_single_extruder_MM = m_config->opt_bool("single_extruder_multi_material"); - const bool is_nozzle_diameter_changed = opt_key.find_first_of("nozzle_diameter") != std::string::npos; + const bool is_nozzle_diameter_changed = opt_key.find("nozzle_diameter") != std::string::npos; + const bool is_high_flow_changed = opt_key.find("nozzle_high_flow") != std::string::npos; if (is_single_extruder_MM && m_extruders_count > 1 && is_nozzle_diameter_changed) { @@ -3163,7 +3169,6 @@ void TabPrinter::build_extruder_pages(size_t n_before_extruders) { const wxString msg_text = _L("This is a single extruder multimaterial printer, diameters of all extruders " "will be set to the new value. Do you want to proceed?"); - //wxMessageDialog dialog(parent(), msg_text, _(L("Nozzle diameter")), wxICON_WARNING | wxYES_NO); MessageDialog dialog(parent(), msg_text, _L("Nozzle diameter"), wxICON_WARNING | wxYES_NO); DynamicPrintConfig new_conf = *m_config; @@ -3182,7 +3187,36 @@ void TabPrinter::build_extruder_pages(size_t n_before_extruders) } } - if (is_nozzle_diameter_changed) { + if (is_single_extruder_MM && m_extruders_count > 1 && is_high_flow_changed) + { + SuppressBackgroundProcessingUpdate sbpu; + const unsigned char new_hf = boost::any_cast(value); + std::vector nozzle_high_flow = static_cast(m_config->option("nozzle_high_flow"))->values; + + // if value was changed + if (nozzle_high_flow[extruder_idx == 0 ? 1 : 0] != new_hf) + { + const wxString msg_text = _L("This is a single extruder multimaterial printer, 'high_flow' state of all extruders " + "will be set to the new value. Do you want to proceed?"); + MessageDialog dialog(parent(), msg_text, _L("High flow"), wxICON_WARNING | wxYES_NO); + + DynamicPrintConfig new_conf = *m_config; + if (dialog.ShowModal() == wxID_YES) { + for (size_t i = 0; i < nozzle_high_flow.size(); i++) { + if (i==extruder_idx) + continue; + nozzle_high_flow[i] = new_hf; + } + } + else + nozzle_high_flow[extruder_idx] = nozzle_high_flow[extruder_idx == 0 ? 1 : 0]; + + new_conf.set_key_value("nozzle_high_flow", new ConfigOptionBools(nozzle_high_flow)); + load_config(new_conf); + } + } + + if (is_nozzle_diameter_changed || is_high_flow_changed) { if (extruder_idx == 0) // Mark the print & filament enabled if they are compatible with the currently selected preset. // If saving the preset changes compatibility with other presets, keep the now incompatible dependent presets selected, however with a "red flag" icon showing that they are no more compatible. @@ -3195,6 +3229,8 @@ void TabPrinter::build_extruder_pages(size_t n_before_extruders) update(); }; + optgroup->append_single_option_line("nozzle_high_flow", "", extruder_idx); + optgroup = page->new_optgroup(L("Preview")); auto reset_to_filament_color = [this, extruder_idx](wxWindow*parent) { diff --git a/src/slic3r/GUI/UserAccount.cpp b/src/slic3r/GUI/UserAccount.cpp index 265c89cc3c..b7bc79a59f 100644 --- a/src/slic3r/GUI/UserAccount.cpp +++ b/src/slic3r/GUI/UserAccount.cpp @@ -1,5 +1,6 @@ #include "UserAccount.hpp" +#include "UserAccountUtils.hpp" #include "format.hpp" #include "GUI.hpp" @@ -160,64 +161,7 @@ void UserAccount::on_communication_fail() } } -namespace { - std::string parse_tree_for_param(const pt::ptree& tree, const std::string& param) - { - for (const auto& section : tree) { - if (section.first == param) { - return section.second.data(); - } - if (std::string res = parse_tree_for_param(section.second, param); !res.empty()) { - return res; - } - } - return {}; - } - void parse_tree_for_param_vector(const pt::ptree& tree, const std::string& param, std::vector& results) - { - for (const auto& section : tree) { - if (section.first == param) { - results.emplace_back(section.second.data()); - } else { - parse_tree_for_param_vector(section.second, param, results); - } - } - } - - pt::ptree parse_tree_for_subtree(const pt::ptree& tree, const std::string& param) - { - for (const auto& section : tree) { - if (section.first == param) { - return section.second; - } - else { - if (pt::ptree res = parse_tree_for_subtree(section.second, param); !res.empty()) - return res; - } - - } - return pt::ptree(); - } - - void fill_supported_printer_models_from_json_inner(const pt::ptree& ptree, std::vector& result) { - std::string printer_model = parse_tree_for_param(ptree, "printer_model"); - if (!printer_model.empty()) { - result.emplace_back(printer_model); - } - pt::ptree out = parse_tree_for_subtree(ptree, "supported_printer_models"); - if (out.empty()) { - BOOST_LOG_TRIVIAL(error) << "Failed to find supported_printer_models in printer detail."; - return; - } - for (const auto& sub : out) { - if (printer_model != sub.second.data()) { - result.emplace_back(sub.second.data()); - } - - } - } -} bool UserAccount::on_connect_printers_success(const std::string& data, AppConfig* app_config, bool& out_printers_changed) { @@ -327,8 +271,8 @@ bool UserAccount::on_connect_uiid_map_success(const std::string& data, AppConfig return on_connect_printers_success(data, app_config, out_printers_changed); } -std::string UserAccount::get_current_printer_uuid_from_connect(const std::string& selected_printer_id) const -{ +std::string UserAccount::get_current_printer_uuid_from_connect(const std::string &selected_printer_id +) const { if (m_current_printer_data_json_from_connect.empty() || m_current_printer_uuid_from_connect.empty()) { return {}; } @@ -343,12 +287,12 @@ std::string UserAccount::get_current_printer_uuid_from_connect(const std::string return {}; } - std::string data_uuid = parse_tree_for_param(ptree, "uuid"); + std::string data_uuid = UserAccountUtils::get_keyword_from_json(ptree, "", "uuid"); assert(data_uuid == m_current_printer_uuid_from_connect); //std::string model_name = parse_tree_for_param(ptree, "printer_model"); std::vector compatible_printers; - fill_supported_printer_models_from_json_inner(ptree, compatible_printers); + UserAccountUtils::fill_supported_printer_models_from_json(ptree, compatible_printers); if (compatible_printers.empty()) { return {}; } @@ -356,173 +300,4 @@ std::string UserAccount::get_current_printer_uuid_from_connect(const std::string return std::find(compatible_printers.begin(), compatible_printers.end(), selected_printer_id) == compatible_printers.end() ? "" : m_current_printer_uuid_from_connect; } - -std::string UserAccount::get_nozzle_from_json(const std::string& message) const -{ - std::string out; - try { - std::stringstream ss(message); - pt::ptree ptree; - pt::read_json(ss, ptree); - - out = parse_tree_for_param(ptree, "nozzle_diameter"); - //assert(!out.empty()); - } - catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "Could not parse prusaconnect message. " << e.what(); - } - - // Get rid of trailing zeros. - // This is because somtimes we get "nozzle_diameter":0.40000000000000002 - // This will return wrong result for f.e. 0.05. But we dont have such profiles right now. - if (size_t fist_dot = out.find('.'); fist_dot != std::string::npos) { - if (size_t first_zero = out.find('0', fist_dot); first_zero != std::string::npos) { - return out.substr(0, first_zero); - } - } - return out; -} - -std::string UserAccount::get_keyword_from_json(const std::string& json, const std::string& keyword) const -{ - std::string out; - try { - std::stringstream ss(json); - pt::ptree ptree; - pt::read_json(ss, ptree); - - out = parse_tree_for_param(ptree, keyword); - //assert(!out.empty()); - } - catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "Could not parse prusaconnect message. " << e.what(); - } - return out; -} - -std::string UserAccount::get_print_data_from_json(const std::string &json, const std::string &keyword) const -{ - // copy subtree string f.e. - // { "": {"param1": "something", "filename":"abcd.gcode", "param3":true}, "something_else" : 0 } - // into: {"param1": "something", "filename":"%1%", "param3":true, "size":%2%} - // yes there will be 2 placeholders for later format - - // this will fail if not flat subtree - size_t start_of_keyword = json.find("\""+keyword+"\""); - if (start_of_keyword == std::string::npos) - return {}; - size_t start_of_sub = json.find('{', start_of_keyword); - if (start_of_sub == std::string::npos) - return {}; - size_t end_of_sub = json.find('}', start_of_sub); - if (end_of_sub == std::string::npos) - return {}; - size_t start_of_filename = json.find("\"filename\"", start_of_sub); - if (start_of_filename == std::string::npos) - return {}; - size_t filename_doubledot = json.find(':', start_of_filename); - if (filename_doubledot == std::string::npos) - return {}; - size_t start_of_filename_data = json.find('\"', filename_doubledot); - if (start_of_filename_data == std::string::npos) - return {}; - size_t end_of_filename_data = json.find('\"', start_of_filename_data + 1); - if (end_of_filename_data == std::string::npos) - return {}; - size_t size = json.size(); - std::string result = json.substr(start_of_sub, start_of_filename_data - start_of_sub + 1); - result += "%1%"; - result += json.substr(end_of_filename_data, end_of_sub - end_of_filename_data); - result += ",\"size\":%2%}"; - return result; -} - -void UserAccount::fill_supported_printer_models_from_json(const std::string& json, std::vector& result) const -{ - try { - std::stringstream ss(json); - pt::ptree ptree; - pt::read_json(ss, ptree); - - fill_supported_printer_models_from_json_inner(ptree, result); - } - catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "Could not parse prusaconnect message. " << e.what(); - } -} - -void UserAccount::fill_material_from_json(const std::string& json, std::vector& result) const -{ - - /* option 1: - "slot": { - "active": 2, - "slots": { - "1": { - "material": "PLA", - "temp": 170, - "fan_hotend": 7689, - "fan_print": 0 - }, - "2": { - "material": "PLA", - "temp": 225, - "fan_hotend": 7798, - "fan_print": 6503 - }, - "3": { - "material": "PLA", - "temp": 36, - "fan_hotend": 6636, - "fan_print": 0 - }, - "4": { - "material": "PLA", - "temp": 35, - "fan_hotend": 0, - "fan_print": 0 - }, - "5": { - "material": "PETG", - "temp": 136, - "fan_hotend": 8132, - "fan_print": 0 - } - } - } - */ - /* option 2 - "filament": { - "material": "PLA", - "bed_temperature": 60, - "nozzle_temperature": 210 - } - */ - // try finding "slot" subtree a use it to - // if not found, find "filament" subtree - try { - std::stringstream ss(json); - pt::ptree ptree; - pt::read_json(ss, ptree); - // find "slot" subtree - pt::ptree slot_subtree = parse_tree_for_subtree(ptree, "slot"); - if (slot_subtree.empty()) { - // if not found, find "filament" subtree - pt::ptree filament_subtree = parse_tree_for_subtree(ptree, "filament"); - if (!filament_subtree.empty()) { - std::string material = parse_tree_for_param(filament_subtree, "material"); - if (!material.empty()) { - result.emplace_back(std::move(material)); - } - } - return; - } - // search "slot" subtree for all "material"s - parse_tree_for_param_vector(slot_subtree, "material", result); - } - catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "Could not parse prusaconnect message. " << e.what(); - } -} - }} // namespace slic3r::GUI \ No newline at end of file diff --git a/src/slic3r/GUI/UserAccount.hpp b/src/slic3r/GUI/UserAccount.hpp index d241f778be..e38efdd02c 100644 --- a/src/slic3r/GUI/UserAccount.hpp +++ b/src/slic3r/GUI/UserAccount.hpp @@ -67,13 +67,6 @@ public: const ConnectPrinterStateMap& get_printer_state_map() const { return m_printer_map; } boost::filesystem::path get_avatar_path(bool logged) const; - // standalone utility methods - std::string get_nozzle_from_json(const std::string& message) const; - std::string get_keyword_from_json(const std::string& json, const std::string& keyword) const; - std::string get_print_data_from_json(const std::string &json, const std::string &keyword) const; - void fill_supported_printer_models_from_json(const std::string& json, std::vector& result) const; - void fill_material_from_json(const std::string& json, std::vector& result) const; - const std::map& get_printer_state_table() const { return printer_state_table; } void set_current_printer_uuid_from_connect(const std::string& uuid) { m_current_printer_uuid_from_connect = uuid; } diff --git a/src/slic3r/GUI/UserAccountUtils.cpp b/src/slic3r/GUI/UserAccountUtils.cpp new file mode 100644 index 0000000000..7cde12db6e --- /dev/null +++ b/src/slic3r/GUI/UserAccountUtils.cpp @@ -0,0 +1,334 @@ +#include "UserAccountUtils.hpp" + +#include "format.hpp" + +#include +#include +#include + +namespace pt = boost::property_tree; + +namespace Slic3r { namespace GUI { namespace UserAccountUtils { + +namespace { +std::string parse_tree_for_param(const pt::ptree& tree, const std::string& param) { + for (const auto §ion : tree) { + if (section.first == param) { + return section.second.data(); + } + if (std::string res = parse_tree_for_param(section.second, param); !res.empty()) { + return res; + } + } + return {}; +} + +void parse_tree_for_param_vector( +const pt::ptree &tree, const std::string& param, std::vector& results) { + for (const auto §ion : tree) { + if (section.first == param) { + results.emplace_back(section.second.data()); + } else { + parse_tree_for_param_vector(section.second, param, results); + } + } +} + +pt::ptree parse_tree_for_subtree(const pt::ptree& tree, const std::string& param) { + for (const auto §ion : tree) { + if (section.first == param) { + return section.second; + } else { + if (pt::ptree res = parse_tree_for_subtree(section.second, param); !res.empty()) + return res; + } + } + return pt::ptree(); +} + +void json_to_ptree(boost::property_tree::ptree& ptree, const std::string& json) { + try { + std::stringstream ss(json); + pt::read_json(ss, ptree); + } catch (const std::exception &e) { + BOOST_LOG_TRIVIAL(error) << "Failed to parse json to ptree. " << e.what(); + BOOST_LOG_TRIVIAL(error) << "json: " << json; + } +} + +} // namespace + +std::string get_nozzle_from_json(boost::property_tree::ptree& ptree) { + assert(!ptree.empty()); + + std::string out = parse_tree_for_param(ptree, "nozzle_diameter"); + // Get rid of trailing zeros. + // This is because somtimes we get "nozzle_diameter":0.40000000000000002 + // This will return wrong result for f.e. 0.05. But we dont have such profiles right now. + if (size_t fist_dot = out.find('.'); fist_dot != std::string::npos) { + if (size_t first_zero = out.find('0', fist_dot); first_zero != std::string::npos) { + return out.substr(0, first_zero); + } + } + return out; +} + +std::string get_keyword_from_json(boost::property_tree::ptree& ptree, const std::string& json, const std::string& keyword ) +{ + if (ptree.empty()) { + json_to_ptree(ptree, json); + } + assert(!ptree.empty()); + return parse_tree_for_param(ptree, keyword); +} + +void fill_supported_printer_models_from_json(boost::property_tree::ptree& ptree, std::vector& result) +{ + assert(!ptree.empty()); + std::string printer_model = parse_tree_for_param(ptree, "printer_model"); + if (!printer_model.empty()) { + result.emplace_back(printer_model); + } + pt::ptree out = parse_tree_for_subtree(ptree, "supported_printer_models"); + if (out.empty()) { + BOOST_LOG_TRIVIAL(error) << "Failed to find supported_printer_models in printer detail."; + return; + } + for (const auto &sub : out) { + if (printer_model != sub.second.data()) { + result.emplace_back(sub.second.data()); + } + } +} + +namespace { +std::string json_var_to_opt_string(const std::string& json_var) +{ + if (json_var == "true") + return "1"; + if (json_var == "false") + return "0"; + return json_var; +} + +void fill_config_options_from_json_inner(boost::property_tree::ptree& ptree, std::map>& result, const std::map& parameters) +{ + pt::ptree slots = parse_tree_for_subtree(parse_tree_for_subtree(ptree, "slot"), "slots"); + for (const auto &subtree : slots) { + size_t slot_id; + try { + slot_id = boost::lexical_cast(subtree.first); + } catch (const boost::bad_lexical_cast&) { + continue; + } + for (const auto &item : subtree.second) { + if (parameters.find(item.first) == parameters.end()) { + continue; + } + std::string config_name = parameters.at(item.first); + // resolve value + std::string val; + if (item.second.size() > 0) { + for (const auto &subitem : item.second) { + if (!val.empty()) { + val += ","; + } + val += json_var_to_opt_string(subitem.second.data()); + } + } else { + val = json_var_to_opt_string(item.second.data()); + } + // insert value + while (result[config_name].size() < slot_id) + result[config_name].emplace_back(); + result[config_name][slot_id - 1] = val; + } + } +} +} + +void fill_config_options_from_json(boost::property_tree::ptree& ptree, std::map>& result) +{ + assert(!ptree.empty()); + /* + "slot": { + "active": 3, + "slots": { + "1": { + "material": "PETG", + "temp": 32.0, + "fan_hotend": 0.0, + "fan_print": 0.0, + "nozzle_diameter": 3.2, // float + "high_flow": true, // boolean + "high_temperature": false, // boolean + "hardened": true, // boolean + }, + "3": { + "material": "ASA", + "temp": 35.0, + "fan_hotend": 0.0, + "fan_print": 0.0, + "nozzle_diameter": 3.2, // float + "high_flow": true, // boolean + "high_temperature": false, // boolean + "hardened": true, // boolean + }, + } + } + */ + const std::map parameters = { + // first name from connect, second config option + {"nozzle_diameter","nozzle_diameter"}, + {"high_flow","nozzle_high_flow"}, + //{"",""} + }; + fill_config_options_from_json_inner(ptree, result, parameters); +} + +void fill_material_from_json(const std::string& json, std::vector& material_result, std::vector& avoid_abrasive_result) +{ + pt::ptree ptree; + json_to_ptree(ptree, json); + assert(!ptree.empty()); + + /* option 1: + "slot": { + "active": 2, + "slots": { + "1": { + "material": "PLA", + "temp": 170, + "fan_hotend": 7689, + "fan_print": 0 + }, + "2": { + "material": "PLA", + "temp": 225, + "fan_hotend": 7798, + "fan_print": 6503 + }, + "3": { + "material": "PLA", + "temp": 36, + "fan_hotend": 6636, + "fan_print": 0 + }, + "4": { + "material": "PLA", + "temp": 35, + "fan_hotend": 0, + "fan_print": 0 + }, + "5": { + "material": "PETG", + "temp": 136, + "fan_hotend": 8132, + "fan_print": 0 + } + } + } + */ + /* option 2 + "filament": { + "material": "PLA", + "bed_temperature": 60, + "nozzle_temperature": 210 + } + */ + // try finding "slot" subtree a use it to + // if not found, find "filament" subtree + + // find "slot" subtree + pt::ptree slot_subtree = parse_tree_for_subtree(ptree, "slot"); + if (slot_subtree.empty()) { + // if not found, find "filament" subtree + pt::ptree filament_subtree = parse_tree_for_subtree(ptree, "filament"); + if (!filament_subtree.empty()) { + std::string material = parse_tree_for_param(filament_subtree, "material"); + if (!material.empty()) { + material_result.emplace_back(std::move(material)); + avoid_abrasive_result.emplace_back(true); + } + } + return; + } + // search "slot" subtree for all "material"s + // this parses "slots" with respect to numbers of slots and adds empty string to missing numbers + // if only filled should be used. Use: parse_tree_for_param_vector(slot_subtree, "material", result); + /* + pt::ptree slots = parse_tree_for_subtree(slot_subtree, "slots"); + assert(!slots.empty()); + for (const auto &subtree : slots) { + size_t slot_id; + try { + slot_id = boost::lexical_cast(subtree.first); + } catch (const boost::bad_lexical_cast&) { + continue; + } + std::string val = parse_tree_for_param(subtree.second, "material"); + // add empty for missing id + while (result.size() < slot_id) + result.emplace_back(); + result[slot_id - 1] = val; + } + */ + const std::map parameters = { + // first name from connect, second config option + {"material","material"}, + {"hardened","hardened"}, + //{"",""} + }; + std::map> result_map; + fill_config_options_from_json_inner(ptree, result_map, parameters); + if (result_map.find("material") != result_map.end()) { + for (const std::string& val : result_map["material"]) { + material_result.emplace_back(val); + } + } + if (result_map.find("hardened") != result_map.end()) { + for (const std::string& val : result_map["hardened"]) { + avoid_abrasive_result.emplace_back(val == "0" ? 1 : 0); + } + } +} + +std::string get_print_data_from_json(const std::string& json, const std::string& keyword) { + // copy subtree string f.e. + // { "": {"param1": "something", "filename":"abcd.gcode", "param3":true}, + // "something_else" : 0 } into: {"param1": "something", "filename":"%1%", "param3":true, + // "size":%2%} yes there will be 2 placeholders for later format + + // this will fail if not flat subtree + size_t start_of_keyword = json.find("\"" + keyword + "\""); + if (start_of_keyword == std::string::npos) + return {}; + size_t start_of_sub = json.find('{', start_of_keyword); + if (start_of_sub == std::string::npos) + return {}; + size_t start_of_filename = json.find("\"filename\"", start_of_sub); + if (start_of_filename == std::string::npos) + return {}; + size_t filename_doubledot = json.find(':', start_of_filename); + if (filename_doubledot == std::string::npos) + return {}; + size_t start_of_filename_data = json.find('\"', filename_doubledot); + if (start_of_filename_data == std::string::npos) + return {}; + size_t end_of_filename_data = json.find('\"', start_of_filename_data + 1); + if (end_of_filename_data == std::string::npos) + return {}; + size_t end_of_sub = json.find('}', end_of_filename_data); + if (end_of_sub == std::string::npos) + return {}; + size_t size = json.size(); + std::string result = json.substr(start_of_sub, start_of_filename_data - start_of_sub + 1); + result += "%1%"; + result += json.substr(end_of_filename_data, end_of_sub - end_of_filename_data); + result += ",\"size\":%2%}"; + return result; +} + +}}} // Slic3r::GUI::UserAccountUtils + + diff --git a/src/slic3r/GUI/UserAccountUtils.hpp b/src/slic3r/GUI/UserAccountUtils.hpp new file mode 100644 index 0000000000..d470a93900 --- /dev/null +++ b/src/slic3r/GUI/UserAccountUtils.hpp @@ -0,0 +1,27 @@ +#ifndef slic3r_UserAccountUtils_hpp_ +#define slic3r_UserAccountUtils_hpp_ + +#include +#include + +#include + +namespace Slic3r { namespace GUI { +namespace UserAccountUtils { + +// If ptree parameter is empty, json parameter needs to contain data and ptree is filled. +// If ptree is non-epty, json parameter is not used. +std::string get_keyword_from_json(boost::property_tree::ptree& ptree, const std::string& json, const std::string& keyword); +// Only ptree is passed since these functions are called on places that already has the ptree from get_keyword_from_json call +std::string get_nozzle_from_json(boost::property_tree::ptree &ptree); +void fill_supported_printer_models_from_json(boost::property_tree::ptree& ptree, std::vector& result); +void fill_config_options_from_json(boost::property_tree::ptree& ptree, std::map>& result); + +// Since fill_material_from_json is called only from one place where ptree doesnt need to be shared, it is not always read from json. +void fill_material_from_json(const std::string& json, std::vector& material_result, std::vector& avoid_abrasive_result); + +std::string get_print_data_from_json(const std::string &json, const std::string &keyword); + +}}} // Slic3r::GUI::UserAccountUtils + +#endif // slic3r_UserAccountUtils_hpp_ \ No newline at end of file diff --git a/src/slic3r/GUI/WebViewDialog.cpp b/src/slic3r/GUI/WebViewDialog.cpp index cc2fe608b2..9d5ec17a1f 100644 --- a/src/slic3r/GUI/WebViewDialog.cpp +++ b/src/slic3r/GUI/WebViewDialog.cpp @@ -1274,32 +1274,52 @@ void PrinterPickWebViewDialog::on_connect_action_webapp_ready(const std::string& } } -void PrinterPickWebViewDialog::request_compatible_printers_FFF() -{ - //PrinterParams: { - //material: Material; - //nozzleDiameter: number; - //printerType: string; - //filename: string; - //} - const Preset& selected_printer = wxGetApp().preset_bundle->printers.get_selected_preset(); - const Preset& selected_filament = wxGetApp().preset_bundle->filaments.get_selected_preset(); - double nozzle_diameter = static_cast(selected_printer.config.option("nozzle_diameter"))->values[0]; +void PrinterPickWebViewDialog::request_compatible_printers_FFF() { + // PrinterParams: { + // material: Material; + // nozzleDiameter: number; + // printerType: string; + // filename: string; + // } + const Preset &selected_printer = wxGetApp().preset_bundle->printers.get_selected_preset(); + const DynamicPrintConfig full_config = wxGetApp().preset_bundle->full_config(); + double nozzle_diameter = static_cast(full_config.option("nozzle_diameter"))->values[0]; wxString nozzle_diameter_serialized = double_to_string(nozzle_diameter); nozzle_diameter_serialized.Replace(L",", L"."); // Sending only first filament type for now. This should change to array of values - const std::string filament_type_serialized = selected_filament.config.option("filament_type")->serialize(); - const std::string printer_model_serialized = selected_printer.config.option("printer_model")->serialize(); + const std::string filament_type_serialized = full_config.option("filament_type")->serialize(); + const std::string nozzle_high_flow_serialized = static_cast(full_config.option("nozzle_high_flow"))->values[0] ? "1" : "0"; + const std::string filament_abrasive_serialized = static_cast(full_config.option("filament_abrasive"))->values[0] ? "1" : "0"; + + std::string printer_model_serialized = full_config.option("printer_model")->serialize(); + std::string vendor_repo_prefix; + if (selected_printer.vendor) { + vendor_repo_prefix = selected_printer.vendor->repo_prefix; + } else if (std::string inherits = selected_printer.inherits(); !inherits.empty()) { + const Preset *parent = wxGetApp().preset_bundle->printers.find_preset(inherits); + if (parent && parent->vendor) { + vendor_repo_prefix = parent->vendor->repo_prefix; + } + } + if (printer_model_serialized.find(vendor_repo_prefix) == 0) { + printer_model_serialized = printer_model_serialized.substr(vendor_repo_prefix.size()); + boost::trim_left(printer_model_serialized); + } + const std::string uuid = wxGetApp().plater()->get_user_account()->get_current_printer_uuid_from_connect(printer_model_serialized); const std::string filename = wxGetApp().plater()->get_upload_filename(); - const std::string request = GUI::format( + //filament_abrasive + std::string request = GUI::format( "{" "\"printerUuid\": \"%4%\", " "\"printerModel\": \"%3%\", " - "\"nozzleDiameter\": %2%, " + "\"nozzle_diameter\": %2%, " "\"material\": \"%1%\", " - "\"filename\": \"%5%\" " - "}", filament_type_serialized, nozzle_diameter_serialized, printer_model_serialized, uuid, filename); + "\"filename\": \"%5%\", " + "\"filament_abrasive\": \"%6%\"," + "\"nozzle_high_flow\": \"%7%\"" + "}" + , filament_type_serialized, nozzle_diameter_serialized, printer_model_serialized, uuid, filename, nozzle_high_flow_serialized, filament_abrasive_serialized); wxString script = GUI::format_wxstr("window._prusaConnect_v1.requestCompatiblePrinter(%1%)", request); run_script(script); @@ -1307,7 +1327,21 @@ void PrinterPickWebViewDialog::request_compatible_printers_FFF() void PrinterPickWebViewDialog::request_compatible_printers_SLA() { const Preset& selected_printer = wxGetApp().preset_bundle->printers.get_selected_preset(); - const std::string printer_model_serialized = selected_printer.config.option("printer_model")->serialize(); + std::string printer_model_serialized = selected_printer.config.option("printer_model")->serialize(); + + std::string vendor_repo_prefix; + if (selected_printer.vendor) { + vendor_repo_prefix = selected_printer.vendor->repo_prefix; + } else if (std::string inherits = selected_printer.inherits(); !inherits.empty()) { + const Preset *parent = wxGetApp().preset_bundle->printers.find_preset(inherits); + if (parent && parent->vendor) { + vendor_repo_prefix = parent->vendor->repo_prefix; + } + } + if (printer_model_serialized.find(vendor_repo_prefix) == 0) { + printer_model_serialized = printer_model_serialized.substr(vendor_repo_prefix.size()); + boost::trim_left(printer_model_serialized); + } const Preset& selected_material = wxGetApp().preset_bundle->sla_materials.get_selected_preset(); const std::string material_type_serialized = selected_material.config.option("material_type")->serialize(); const std::string uuid = wxGetApp().plater()->get_user_account()->get_current_printer_uuid_from_connect(printer_model_serialized);