From 0db3fd9f81a1dac7b47f505e273687c0ccce37cb Mon Sep 17 00:00:00 2001 From: "chunmao.guo" Date: Thu, 17 Aug 2023 19:34:04 +0800 Subject: [PATCH] ENH: [STUDIO-4034] add functions for custom printer/filament Change-Id: I0fc2738392970b19c0ae8263ea7d968cc8f23c2f JIRA: STUDIO-4034 (cherry picked from commit 30e5b3f61f2f5aae308d5e454f71d69939104ebb) --- src/libslic3r/Preset.cpp | 82 ++++++++++++++++++++- src/libslic3r/Preset.hpp | 7 ++ src/libslic3r/PresetBundle.cpp | 125 +++++++++++++++++++++++++++++++-- src/libslic3r/PresetBundle.hpp | 5 ++ 4 files changed, 210 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 2142a478f..121704e9c 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1427,7 +1427,7 @@ void PresetCollection::set_sync_info_and_save(std::string name, std::string sett else preset->sync_info = syncinfo; if (get_preset_base(*preset) == preset) { - for (auto preset2 : m_presets) + for (auto & preset2 : m_presets) if (preset2.inherits() == preset->name) { preset2.base_id = setting_id; preset2.save_info(); @@ -1436,7 +1436,7 @@ void PresetCollection::set_sync_info_and_save(std::string name, std::string sett preset->setting_id = setting_id; if (update_time > 0) preset->updated_time = update_time; - preset->save_info(); + preset->sync_info == "update" ? preset->save(nullptr) : preset->save_info(); break; } } @@ -2065,6 +2065,84 @@ Preset& PresetCollection::load_preset(const std::string &path, const std::string return preset; } +bool PresetCollection::clone_presets(std::vector const &presets, std::vector &failures, std::function modifier) +{ + std::vector new_presets; + for (auto curr_preset : presets) { + new_presets.push_back(*curr_preset); + auto &preset = new_presets.back(); + preset.vendor = nullptr; + preset.renamed_from.clear(); + preset.setting_id.clear(); + preset.inherits().clear(); + preset.is_default = false; + preset.is_system = false; + preset.is_external = false; + preset.is_visible = true; + preset.is_project_embedded = false; + modifier(preset); + if (find_preset(preset.name)) { + failures.push_back(preset.name); + continue; + } + preset.file = this->path_for_preset(preset); + preset.alias.clear(); + if (m_type == Preset::TYPE_PRINT) + preset.config.option("print_settings_id", true)->value = preset.name; + else if (m_type == Preset::TYPE_FILAMENT) + preset.config.option("filament_settings_id", true)->values[0] = preset.name; + else if (m_type == Preset::TYPE_PRINTER) + preset.config.option("printer_settings_id", true)->value = preset.name; + preset.updated_time = (long long) Slic3r::Utils::get_current_time_utc(); + } + if (!failures.empty()) + return false; + lock(); + for (auto preset : new_presets) { + auto it = this->find_preset_internal(preset.name); + assert(it == m_presets.end() || it->name != preset.name); + Preset & new_preset = *m_presets.insert(it, preset); + new_preset.save(nullptr); + } + unlock(); + return true; +} + +bool PresetCollection::clone_presets_for_printer(std::vector const &presets, std::vector &failures, std::string const &printer) +{ + return clone_presets(presets, failures, [printer](Preset &preset) { + preset.name = preset.alias + " @ " + printer; + auto *compatible_printers = dynamic_cast(preset.config.option("compatible_printers")); + compatible_printers->values = std::vector{ printer }; + }); +} + +bool PresetCollection::create_presets_from_template_for_printer(std::vector const &templates, std::vector &failures, std::string const &printer) +{ + return false; +} + +bool PresetCollection::clone_presets_for_filament(std::vector const &presets, + std::vector & failures, + std::string const & filament_name, + std::string const & filament_id) +{ + return clone_presets(presets, failures, [filament_name, filament_id](Preset &preset) { + preset.name = filament_name + " " + preset.name.substr(preset.name.find_last_of('@')); + preset.alias = filament_name; + preset.filament_id = filament_id; + }); +} + +std::map> PresetCollection::get_filament_presets() const +{ + std::map> filament_presets; + for (auto &preset : m_presets) { + if (get_preset_base(preset) == &preset) { filament_presets[preset.filament_id].push_back(&preset); } + } + return filament_presets; +} + //BBS: add project embedded preset logic void PresetCollection::save_current_preset(const std::string &new_name, bool detach, bool save_to_project, Preset* _curr_preset) { diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index 27351b3a7..30b90da66 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -449,6 +449,13 @@ public: Preset& load_preset(const std::string &path, const std::string &name, const DynamicPrintConfig &config, bool select = true, Semver file_version = Semver(), bool is_custom_defined = false); Preset& load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select = true, Semver file_version = Semver(), bool is_custom_defined = false); + bool clone_presets(std::vector const &presets, std::vector &failures, std::function modifier); + bool clone_presets_for_printer(std::vector const &presets, std::vector &failures, std::string const &printer); + bool create_presets_from_template_for_printer(std::vector const &templates, std::vector &failures, std::string const &printer); + bool clone_presets_for_filament(std::vector const &presets, std::vector &failures, std::string const &filament_name, std::string const &filament_id); + + std::map> get_filament_presets() const; + // Returns a loaded preset, returns true if an existing preset was selected AND modified from config. // In that case the successive filament loaded for a multi material printer should not be modified, but // an external preset should be created instead. diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index c8ca28fe5..35d2197a7 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -1100,6 +1100,112 @@ std::pair PresetBundle::load_system_pre return std::make_pair(std::move(substitutions), errors_cummulative); } +std::pair PresetBundle::load_system_models_from_json(ForwardCompatibilitySubstitutionRule compatibility_rule) +{ + BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, compatibility_rule %1%") % compatibility_rule; + if (compatibility_rule == ForwardCompatibilitySubstitutionRule::EnableSystemSilent) + // Loading system presets, don't log substitutions. + compatibility_rule = ForwardCompatibilitySubstitutionRule::EnableSilent; + else if (compatibility_rule == ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem) + // Loading system presets, throw on unknown option value. + compatibility_rule = ForwardCompatibilitySubstitutionRule::Disable; + + // Here the vendor specific read only Config Bundles are stored. + boost::filesystem::path dir = (boost::filesystem::path(resources_dir()) / "profiles").make_preferred(); + PresetsConfigSubstitutions substitutions; + std::string errors_cummulative; + for (auto &dir_entry : boost::filesystem::directory_iterator(dir)) { + std::string vendor_file = dir_entry.path().string(); + if (Slic3r::is_json_file(vendor_file)) { + std::string vendor_name = dir_entry.path().filename().string(); + // Remove the .json suffix. + vendor_name.erase(vendor_name.size() - 5); + try { + // Load the config bundle, flatten it. + append(substitutions, load_vendor_configs_from_json(dir.string(), vendor_name, PresetBundle::LoadVendorOnly, compatibility_rule).first); + } catch (const std::runtime_error &err) { + errors_cummulative += err.what(); + errors_cummulative += "\n"; + } + } + } + + BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" finished, errors_cummulative %1%") % errors_cummulative; + return std::make_pair(std::move(substitutions), errors_cummulative); +} + +std::pair PresetBundle::load_system_filaments_json(ForwardCompatibilitySubstitutionRule compatibility_rule) +{ + BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, compatibility_rule %1%") % compatibility_rule; + if (compatibility_rule == ForwardCompatibilitySubstitutionRule::EnableSystemSilent) + // Loading system presets, don't log substitutions. + compatibility_rule = ForwardCompatibilitySubstitutionRule::EnableSilent; + else if (compatibility_rule == ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem) + // Loading system presets, throw on unknown option value. + compatibility_rule = ForwardCompatibilitySubstitutionRule::Disable; + + // Here the vendor specific read only Config Bundles are stored. + boost::filesystem::path dir = (boost::filesystem::path(resources_dir()) / "profiles").make_preferred(); + PresetsConfigSubstitutions substitutions; + std::string errors_cummulative; + bool first = true; + for (auto &dir_entry : boost::filesystem::directory_iterator(dir)) { + std::string vendor_file = dir_entry.path().string(); + if (Slic3r::is_json_file(vendor_file)) { + std::string vendor_name = dir_entry.path().filename().string(); + // Remove the .json suffix. + vendor_name.erase(vendor_name.size() - 5); + try { + if (first) { + // Reset this PresetBundle and load the first vendor config. + append(substitutions, this->load_vendor_configs_from_json(dir.string(), vendor_name, PresetBundle::LoadSystem | PresetBundle::LoadFilamentOnly, compatibility_rule).first); + first = false; + } else { + // Load the other vendor configs, merge them with this PresetBundle. + // Report duplicate profiles. + PresetBundle other; + append(substitutions, other.load_vendor_configs_from_json(dir.string(), vendor_name, PresetBundle::LoadSystem | PresetBundle::LoadFilamentOnly, compatibility_rule).first); + std::vector duplicates = this->merge_presets(std::move(other)); + if (!duplicates.empty()) { + errors_cummulative += "Found duplicated settings in vendor " + vendor_name + "'s json file lists: "; + for (size_t i = 0; i < duplicates.size(); ++i) { + if (i > 0) errors_cummulative += ", "; + errors_cummulative += duplicates[i]; + } + } + } + } catch (const std::runtime_error &err) { + errors_cummulative += err.what(); + errors_cummulative += "\n"; + } + } + } + + BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" finished, errors_cummulative %1%") % errors_cummulative; + return std::make_pair(std::move(substitutions), errors_cummulative); +} + +VendorProfile PresetBundle::get_custom_vendor_models() const +{ + VendorProfile vendor; + vendor.name = "Custom"; + vendor.id = "Custom"; + for (auto &preset : printers.get_presets()) { + if (preset.is_system) continue; + if (printers.get_preset_base(preset) != &preset) continue; + auto model = preset.config.opt_string("printer_model"); + auto variant = preset.config.opt_string("printer_variant"); + auto iter_model = std::find_if(vendor.models.begin(), vendor.models.end(), [model](VendorProfile::PrinterModel &m) { + return m.name == model; + }); + if (iter_model == vendor.models.end()) { + iter_model = vendor.models.emplace(vendor.models.end(), VendorProfile::PrinterModel{}); + iter_model->name = model; + } + iter_model->variants.push_back(variant); + } + return vendor; +} // Merge one vendor's presets with the other vendor's presets, report duplicates. std::vector PresetBundle::merge_presets(PresetBundle &&other) @@ -2742,7 +2848,6 @@ std::pair PresetBundle::load_vendor_configs_ // Enable substitutions for user config bundle, throw an exception when loading a system profile. ConfigSubstitutionContext substitution_context { compatibility_rule }; PresetsConfigSubstitutions substitutions; - std::string vendor_system_path = data_dir() + "/" + PRESET_SYSTEM_DIR; //BBS: add config related logs BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, path %1%, compatibility_rule %2%")%path.c_str()%compatibility_rule; @@ -2796,7 +2901,7 @@ std::pair PresetBundle::load_vendor_configs_ auto config_version = Semver::parse(version_str); if (! config_version) { throw ConfigurationError((boost::format("vendor %1%'s config version: %2% invalid\nSuggest cleaning the directory %3% firstly") - % vendor_name % version_str %vendor_system_path).str()); + % vendor_name % version_str % path).str()); } else { vendor_profile.config_version = std::move(*config_version); } @@ -2834,10 +2939,16 @@ std::pair PresetBundle::load_vendor_configs_ catch(nlohmann::detail::parse_error &err) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse "< PresetBundle::load_vendor_configs_ catch(nlohmann::detail::parse_error &err) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse "<< subfile <<" got a nlohmann::detail::parse_error, reason = " << err.what(); throw ConfigurationError((boost::format("Failed loading configuration file %1%: %2%\nSuggest cleaning the directory %3% firstly") - %subfile %err.what() %vendor_system_path).str()); + %subfile %err.what() % path).str()); } if (! model.id.empty() && ! model.variants.empty()) @@ -3132,7 +3243,7 @@ std::pair PresetBundle::load_vendor_configs_ //parse error std::string subfile_path = path + "/" + vendor_name + "/" + subfile.second; BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", got error when parse process setting from %1%") % subfile_path; - throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path %vendor_system_path).str()); + throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path % path).str()); } } @@ -3147,7 +3258,7 @@ std::pair PresetBundle::load_vendor_configs_ //parse error std::string subfile_path = path + "/" + vendor_name + "/" + subfile.second; BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", got error when parse filament setting from %1%") % subfile_path; - throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path %vendor_system_path).str()); + throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path % path).str()); } } @@ -3162,7 +3273,7 @@ std::pair PresetBundle::load_vendor_configs_ //parse error std::string subfile_path = path + "/" + vendor_name + "/" + subfile.second; BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", got error when parse printer setting from %1%") % subfile_path; - throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path %vendor_system_path).str()); + throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path % path).str()); } } diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp index d28ccc1e0..61e7626c7 100644 --- a/src/libslic3r/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -159,6 +159,7 @@ public: // Load a system config bundle. LoadSystem, LoadVendorOnly, + LoadFilamentOnly, }; using LoadConfigBundleAttributes = enum_bitmask; // Load the config bundle based on the flags. @@ -209,6 +210,10 @@ public: //BBS: add project embedded preset logic void save_changes_for_preset(const std::string& new_name, Preset::Type type, const std::vector& unselected_options, bool save_to_project = false); + std::pair load_system_models_from_json(ForwardCompatibilitySubstitutionRule compatibility_rule); + std::pair load_system_filaments_json(ForwardCompatibilitySubstitutionRule compatibility_rule); + VendorProfile get_custom_vendor_models() const; + //BBS: add BBL as default static const char *BBL_BUNDLE; static const char *BBL_DEFAULT_PRINTER_MODEL;