mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-29 13:52:07 +00:00
fix: preset resolution
This commit is contained in:
@@ -615,11 +615,9 @@ set(SLIC3R_GUI_SOURCES
|
|||||||
plugin/CloudPluginService.hpp
|
plugin/CloudPluginService.hpp
|
||||||
plugin/PluginFsUtils.cpp
|
plugin/PluginFsUtils.cpp
|
||||||
plugin/PluginFsUtils.hpp
|
plugin/PluginFsUtils.hpp
|
||||||
plugin/CapabilityConfigDocument.cpp
|
|
||||||
plugin/CapabilityConfigDocument.hpp
|
plugin/CapabilityConfigDocument.hpp
|
||||||
plugin/PluginConfig.cpp
|
plugin/PluginConfig.cpp
|
||||||
plugin/PluginConfig.hpp
|
plugin/PluginConfig.hpp
|
||||||
plugin/PresetPluginConfig.cpp
|
|
||||||
plugin/PresetPluginConfig.hpp
|
plugin/PresetPluginConfig.hpp
|
||||||
plugin/PythonJsonUtils.hpp
|
plugin/PythonJsonUtils.hpp
|
||||||
plugin/PluginCatalog.cpp
|
plugin/PluginCatalog.cpp
|
||||||
|
|||||||
@@ -1,122 +0,0 @@
|
|||||||
#include "CapabilityConfigDocument.hpp"
|
|
||||||
|
|
||||||
namespace Slic3r {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
constexpr const char* KEY_PLUGIN = "plugin_key";
|
|
||||||
constexpr const char* KEY_CAPABILITY = "capability";
|
|
||||||
constexpr const char* KEY_VERSION = "plugin_version";
|
|
||||||
constexpr const char* KEY_CAP_CONFIG = "cap_config";
|
|
||||||
|
|
||||||
std::string string_field(const nlohmann::json& entry, const char* key)
|
|
||||||
{
|
|
||||||
const auto it = entry.find(key);
|
|
||||||
return it != entry.end() && it->is_string() ? it->get<std::string>() : std::string();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_recognized_entry(const nlohmann::json& entry, CapabilityConfigId& id)
|
|
||||||
{
|
|
||||||
if (!entry.is_object())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
id.plugin_key = string_field(entry, KEY_PLUGIN);
|
|
||||||
id.capability = string_field(entry, KEY_CAPABILITY);
|
|
||||||
return !id.plugin_key.empty() && !id.capability.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
CapabilityConfigEntry decode_entry(const CapabilityConfigId& id, const nlohmann::json& entry)
|
|
||||||
{
|
|
||||||
CapabilityConfigEntry result;
|
|
||||||
result.id = id;
|
|
||||||
result.plugin_version = string_field(entry, KEY_VERSION);
|
|
||||||
const auto cap_it = entry.find(KEY_CAP_CONFIG);
|
|
||||||
result.cap_config = cap_it != entry.end() ? *cap_it : nlohmann::json::object();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
CapabilityConfigDocument CapabilityConfigDocument::from_entries(const nlohmann::json& entries)
|
|
||||||
{
|
|
||||||
CapabilityConfigDocument document;
|
|
||||||
if (!entries.is_array())
|
|
||||||
return document;
|
|
||||||
|
|
||||||
for (const nlohmann::json& entry : entries) {
|
|
||||||
CapabilityConfigId id;
|
|
||||||
if (is_recognized_entry(entry, id))
|
|
||||||
document.m_entries[id] = entry;
|
|
||||||
else
|
|
||||||
document.m_opaque_entries.push_back(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
return document;
|
|
||||||
}
|
|
||||||
|
|
||||||
CapabilityConfigDocument CapabilityConfigDocument::from_root_json(const nlohmann::json& root)
|
|
||||||
{
|
|
||||||
const auto entries = root.find(KeyEntries);
|
|
||||||
return entries != root.end() ? from_entries(*entries) : CapabilityConfigDocument();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<CapabilityConfigEntry> CapabilityConfigDocument::find(const CapabilityConfigId& id) const
|
|
||||||
{
|
|
||||||
const auto it = m_entries.find(id);
|
|
||||||
if (it == m_entries.end())
|
|
||||||
return std::nullopt;
|
|
||||||
return decode_entry(it->first, it->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CapabilityConfigDocument::contains(const CapabilityConfigId& id) const
|
|
||||||
{
|
|
||||||
return m_entries.find(id) != m_entries.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CapabilityConfigDocument::upsert(CapabilityConfigEntry entry)
|
|
||||||
{
|
|
||||||
if (entry.id.plugin_key.empty() || entry.id.capability.empty())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
nlohmann::json serialized = nlohmann::json::object();
|
|
||||||
const auto existing = m_entries.find(entry.id);
|
|
||||||
if (existing != m_entries.end() && existing->second.is_object())
|
|
||||||
serialized = existing->second;
|
|
||||||
|
|
||||||
serialized[KEY_PLUGIN] = entry.id.plugin_key;
|
|
||||||
serialized[KEY_CAPABILITY] = entry.id.capability;
|
|
||||||
serialized[KEY_VERSION] = entry.plugin_version;
|
|
||||||
serialized[KEY_CAP_CONFIG] = entry.cap_config;
|
|
||||||
|
|
||||||
m_entries[entry.id] = std::move(serialized);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CapabilityConfigDocument::erase(const CapabilityConfigId& id)
|
|
||||||
{
|
|
||||||
return m_entries.erase(id) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CapabilityConfigDocument::empty() const
|
|
||||||
{
|
|
||||||
return m_entries.empty() && m_opaque_entries.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
nlohmann::json CapabilityConfigDocument::serialize_entries() const
|
|
||||||
{
|
|
||||||
nlohmann::json result = nlohmann::json::array();
|
|
||||||
for (const auto& item : m_entries)
|
|
||||||
result.push_back(item.second);
|
|
||||||
for (const nlohmann::json& entry : m_opaque_entries)
|
|
||||||
result.push_back(entry);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
nlohmann::json CapabilityConfigDocument::root_json() const
|
|
||||||
{
|
|
||||||
nlohmann::json root = nlohmann::json::object();
|
|
||||||
root[KeyEntries] = serialize_entries();
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Slic3r
|
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
#include "PluginConfig.hpp"
|
#include "PluginConfig.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
#include <boost/nowide/fstream.hpp>
|
#include <boost/nowide/fstream.hpp>
|
||||||
|
|
||||||
|
#include <libslic3r/PresetBundle.hpp>
|
||||||
|
#include <libslic3r/PrintConfig.hpp>
|
||||||
#include <slic3r/GUI/GUI.hpp>
|
#include <slic3r/GUI/GUI.hpp>
|
||||||
|
#include <slic3r/GUI/GUI_App.hpp>
|
||||||
#include <slic3r/GUI/I18N.hpp>
|
#include <slic3r/GUI/I18N.hpp>
|
||||||
#include <slic3r/GUI/format.hpp>
|
#include <slic3r/GUI/format.hpp>
|
||||||
#include <slic3r/plugin/PluginLoader.hpp>
|
#include <slic3r/plugin/PluginLoader.hpp>
|
||||||
@@ -14,12 +18,44 @@
|
|||||||
#include <slic3r/plugin/PythonPluginInterface.hpp>
|
#include <slic3r/plugin/PythonPluginInterface.hpp>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <wx/app.h>
|
||||||
#include <wx/utils.h>
|
#include <wx/utils.h>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
constexpr const char* KEY_PLUGIN = "plugin_key";
|
||||||
|
constexpr const char* KEY_CAPABILITY = "capability";
|
||||||
|
constexpr const char* KEY_VERSION = "plugin_version";
|
||||||
|
constexpr const char* KEY_CAP_CONFIG = "cap_config";
|
||||||
|
|
||||||
|
std::string string_field(const nlohmann::json& entry, const char* key)
|
||||||
|
{
|
||||||
|
const auto it = entry.find(key);
|
||||||
|
return it != entry.end() && it->is_string() ? it->get<std::string>() : std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_recognized_entry(const nlohmann::json& entry, CapabilityConfigId& id)
|
||||||
|
{
|
||||||
|
if (!entry.is_object())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
id.plugin_key = string_field(entry, KEY_PLUGIN);
|
||||||
|
id.capability = string_field(entry, KEY_CAPABILITY);
|
||||||
|
return !id.plugin_key.empty() && !id.capability.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
CapabilityConfigEntry decode_entry(const CapabilityConfigId& id, const nlohmann::json& entry)
|
||||||
|
{
|
||||||
|
CapabilityConfigEntry result;
|
||||||
|
result.id = id;
|
||||||
|
result.plugin_version = string_field(entry, KEY_VERSION);
|
||||||
|
const auto cap_it = entry.find(KEY_CAP_CONFIG);
|
||||||
|
result.cap_config = cap_it != entry.end() ? *cap_it : nlohmann::json::object();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// PluginDescriptor::version is overwritten with the latest cloud version on a cloud merge, so it can
|
// PluginDescriptor::version is overwritten with the latest cloud version on a cloud merge, so it can
|
||||||
// name a version that is not the one on disk; installed_version is what actually loaded.
|
// name a version that is not the one on disk; installed_version is what actually loaded.
|
||||||
std::string running_plugin_version(const std::string& plugin_key)
|
std::string running_plugin_version(const std::string& plugin_key)
|
||||||
@@ -30,6 +66,19 @@ std::string running_plugin_version(const std::string& plugin_key)
|
|||||||
return descriptor.installed_version.empty() ? descriptor.version : descriptor.installed_version;
|
return descriptor.installed_version.empty() ? descriptor.version : descriptor.installed_version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CapabilityConfigId make_id(const PluginCapabilityIdentifier& id)
|
||||||
|
{
|
||||||
|
return CapabilityConfigId{id.plugin_key, id.name};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Null wherever the plugin host runs without the GUI app (the unit tests). wxGetApp() dereferences
|
||||||
|
// the app unconditionally, so ask wxWidgets instead.
|
||||||
|
const PresetBundle* active_preset_bundle()
|
||||||
|
{
|
||||||
|
const auto* app = dynamic_cast<const GUI::GUI_App*>(wxApp::GetInstance());
|
||||||
|
return app == nullptr ? nullptr : app->preset_bundle;
|
||||||
|
}
|
||||||
|
|
||||||
// PluginLoader stamps both halves onto the instance when it materializes the capability, so the
|
// PluginLoader stamps both halves onto the instance when it materializes the capability, so the
|
||||||
// caller never supplies them and cannot name another capability's entry. Empty means the instance
|
// caller never supplies them and cannot name another capability's entry. Empty means the instance
|
||||||
// was never materialized: refuse rather than read or clobber a wrong entry.
|
// was never materialized: refuse rather than read or clobber a wrong entry.
|
||||||
@@ -42,11 +91,8 @@ std::pair<std::string, std::string> capability_identity(const PluginCapabilityIn
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The identity above plus the type, which decides which preset may override the capability (see
|
// The capability type identifies the preset that owns its override. If a plugin raises while
|
||||||
// preset_type_for_capability). Taken from the instance, not the loader's registry: a capability
|
// reporting its type, resolve against the base config instead of failing the capability config API.
|
||||||
// calling get_config() from on_load() is not registered yet and must still see its preset's config.
|
|
||||||
// A raising get_type() costs it only the preset layer — Unknown names no preset, so the base config
|
|
||||||
// answers.
|
|
||||||
PluginCapabilityIdentifier capability_full_identity(const PluginCapabilityInterface& capability, const char* api_name)
|
PluginCapabilityIdentifier capability_full_identity(const PluginCapabilityInterface& capability, const char* api_name)
|
||||||
{
|
{
|
||||||
const auto [plugin_key, capability_name] = capability_identity(capability, api_name);
|
const auto [plugin_key, capability_name] = capability_identity(capability, api_name);
|
||||||
@@ -63,6 +109,88 @@ PluginCapabilityIdentifier capability_full_identity(const PluginCapabilityInterf
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
CapabilityConfigDocument CapabilityConfigDocument::from_entries(const nlohmann::json& entries)
|
||||||
|
{
|
||||||
|
CapabilityConfigDocument document;
|
||||||
|
if (!entries.is_array())
|
||||||
|
return document;
|
||||||
|
|
||||||
|
for (const nlohmann::json& entry : entries) {
|
||||||
|
CapabilityConfigId id;
|
||||||
|
if (is_recognized_entry(entry, id))
|
||||||
|
document.m_entries[id] = entry;
|
||||||
|
else
|
||||||
|
document.m_opaque_entries.push_back(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
|
CapabilityConfigDocument CapabilityConfigDocument::from_root_json(const nlohmann::json& root)
|
||||||
|
{
|
||||||
|
const auto entries = root.find(KeyEntries);
|
||||||
|
return entries != root.end() ? from_entries(*entries) : CapabilityConfigDocument();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<CapabilityConfigEntry> CapabilityConfigDocument::find(const CapabilityConfigId& id) const
|
||||||
|
{
|
||||||
|
const auto it = m_entries.find(id);
|
||||||
|
if (it == m_entries.end())
|
||||||
|
return std::nullopt;
|
||||||
|
return decode_entry(it->first, it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CapabilityConfigDocument::contains(const CapabilityConfigId& id) const
|
||||||
|
{
|
||||||
|
return m_entries.find(id) != m_entries.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CapabilityConfigDocument::upsert(CapabilityConfigEntry entry)
|
||||||
|
{
|
||||||
|
if (entry.id.plugin_key.empty() || entry.id.capability.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
nlohmann::json serialized = nlohmann::json::object();
|
||||||
|
const auto existing = m_entries.find(entry.id);
|
||||||
|
if (existing != m_entries.end() && existing->second.is_object())
|
||||||
|
serialized = existing->second;
|
||||||
|
|
||||||
|
serialized[KEY_PLUGIN] = entry.id.plugin_key;
|
||||||
|
serialized[KEY_CAPABILITY] = entry.id.capability;
|
||||||
|
serialized[KEY_VERSION] = entry.plugin_version;
|
||||||
|
serialized[KEY_CAP_CONFIG] = entry.cap_config;
|
||||||
|
|
||||||
|
m_entries[entry.id] = std::move(serialized);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CapabilityConfigDocument::erase(const CapabilityConfigId& id)
|
||||||
|
{
|
||||||
|
return m_entries.erase(id) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CapabilityConfigDocument::empty() const
|
||||||
|
{
|
||||||
|
return m_entries.empty() && m_opaque_entries.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json CapabilityConfigDocument::serialize_entries() const
|
||||||
|
{
|
||||||
|
nlohmann::json result = nlohmann::json::array();
|
||||||
|
for (const auto& item : m_entries)
|
||||||
|
result.push_back(item.second);
|
||||||
|
for (const nlohmann::json& entry : m_opaque_entries)
|
||||||
|
result.push_back(entry);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json CapabilityConfigDocument::root_json() const
|
||||||
|
{
|
||||||
|
nlohmann::json root = nlohmann::json::object();
|
||||||
|
root[KeyEntries] = serialize_entries();
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
void PluginConfig::load()
|
void PluginConfig::load()
|
||||||
{
|
{
|
||||||
const std::string path = plugin_config_file();
|
const std::string path = plugin_config_file();
|
||||||
@@ -194,6 +322,139 @@ bool PluginConfig::dirty() const
|
|||||||
return m_dirty;
|
return m_dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string plugin_overrides_of(const Preset& preset)
|
||||||
|
{
|
||||||
|
const auto* opt = dynamic_cast<const ConfigOptionString*>(preset.config.option(PLUGIN_OVERRIDES_OPTION_KEY));
|
||||||
|
return opt == nullptr ? std::string() : opt->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parse_plugin_overrides(const std::string& raw, CapabilityConfigDocument& document, std::string& error)
|
||||||
|
{
|
||||||
|
document = CapabilityConfigDocument();
|
||||||
|
error.clear();
|
||||||
|
|
||||||
|
if (raw.empty())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const nlohmann::json parsed = nlohmann::json::parse(raw, nullptr, /* allow_exceptions */ false);
|
||||||
|
if (parsed.is_discarded()) {
|
||||||
|
error = "The preset stores invalid plugin capability configuration JSON.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!parsed.is_array()) {
|
||||||
|
error = "The preset's plugin capability configuration is not an array and cannot be edited.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
document = CapabilityConfigDocument::from_entries(parsed);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string serialize_plugin_overrides(const CapabilityConfigDocument& document)
|
||||||
|
{
|
||||||
|
return document.empty() ? std::string() : document.serialize_entries().dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
EffectiveCapabilityConfig PresetPluginConfigService::get_effective_config(const CapabilityConfigDocument& overrides,
|
||||||
|
const PluginCapabilityIdentifier& id) const
|
||||||
|
{
|
||||||
|
EffectiveCapabilityConfig result;
|
||||||
|
result.id = make_id(id);
|
||||||
|
result.running_plugin_version = running_plugin_version(id.plugin_key);
|
||||||
|
|
||||||
|
const BaseConfig base = PluginManager::instance().get_config().get_config(id.plugin_key, id.name);
|
||||||
|
result.has_base_config = !base.empty();
|
||||||
|
|
||||||
|
if (const auto entry = overrides.find(result.id)) {
|
||||||
|
result.has_preset_override = true;
|
||||||
|
result.config = entry->cap_config;
|
||||||
|
result.stored_plugin_version = entry->plugin_version;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.has_base_config) {
|
||||||
|
result.config = base.config;
|
||||||
|
result.stored_plugin_version = base.plugin_version;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MutationResult PresetPluginConfigService::set_preset_override(CapabilityConfigDocument& overrides,
|
||||||
|
const PluginCapabilityIdentifier& id,
|
||||||
|
const nlohmann::json& value) const
|
||||||
|
{
|
||||||
|
MutationResult result;
|
||||||
|
const CapabilityConfigId config_id = make_id(id);
|
||||||
|
const std::string version = running_plugin_version(id.plugin_key);
|
||||||
|
|
||||||
|
// A no-op is a successful unchanged result: re-saving the displayed value must not mark the
|
||||||
|
// preset dirty.
|
||||||
|
const auto existing = overrides.find(config_id);
|
||||||
|
if (existing && existing->cap_config == value && existing->plugin_version == version) {
|
||||||
|
result.ok = true;
|
||||||
|
result.effective = get_effective_config(overrides, id);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
overrides.upsert({config_id, version, value});
|
||||||
|
|
||||||
|
result.ok = true;
|
||||||
|
result.changed = true;
|
||||||
|
result.effective = get_effective_config(overrides, id);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MutationResult PresetPluginConfigService::remove_preset_override(CapabilityConfigDocument& overrides,
|
||||||
|
const PluginCapabilityIdentifier& id) const
|
||||||
|
{
|
||||||
|
MutationResult result;
|
||||||
|
result.ok = true;
|
||||||
|
result.changed = overrides.erase(make_id(id));
|
||||||
|
result.effective = get_effective_config(overrides, id);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
EffectiveCapabilityConfig active_capability_config(const PluginCapabilityIdentifier& id)
|
||||||
|
{
|
||||||
|
const PresetPluginConfigService service;
|
||||||
|
|
||||||
|
CapabilityConfigDocument overrides;
|
||||||
|
|
||||||
|
const PresetBundle* bundle = active_preset_bundle();
|
||||||
|
const Preset* preset = nullptr;
|
||||||
|
|
||||||
|
if (bundle != nullptr) {
|
||||||
|
const std::string type_key = plugin_capability_type_to_string(id.type);
|
||||||
|
for (const auto& [key, def] : print_config_def.options) {
|
||||||
|
if (def.plugin_type != type_key)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto& print_options = Preset::print_options();
|
||||||
|
if (std::find(print_options.begin(), print_options.end(), key) != print_options.end()) {
|
||||||
|
preset = &bundle->prints.get_edited_preset();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& printer_options = Preset::printer_options();
|
||||||
|
if (std::find(printer_options.begin(), printer_options.end(), key) != printer_options.end()) {
|
||||||
|
preset = &bundle->printers.get_edited_preset();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preset != nullptr) {
|
||||||
|
std::string error;
|
||||||
|
if (!parse_plugin_overrides(plugin_overrides_of(*preset), overrides, error)) {
|
||||||
|
// Text we cannot read is not an override: log it and resolve against the base config.
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Preset \"" << preset->name << "\": " << error;
|
||||||
|
overrides = CapabilityConfigDocument();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return service.get_effective_config(overrides, id);
|
||||||
|
}
|
||||||
|
|
||||||
nlohmann::json capability_get_config(const PluginCapabilityInterface& capability)
|
nlohmann::json capability_get_config(const PluginCapabilityInterface& capability)
|
||||||
{
|
{
|
||||||
// Shares its resolution with the dialogs, so a capability reads back exactly the config its UI
|
// Shares its resolution with the dialogs, so a capability reads back exactly the config its UI
|
||||||
|
|||||||
@@ -180,36 +180,6 @@ std::vector<PluginCapabilityIdentifier> capabilities_in_use(Preset::Type type, c
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Preset::Type preset_type_for_capability(PluginCapabilityType type)
|
|
||||||
{
|
|
||||||
if (type == PluginCapabilityType::Unknown)
|
|
||||||
return Preset::TYPE_INVALID;
|
|
||||||
|
|
||||||
// The three typed option lists are disjoint, so the first that holds the key names its owner.
|
|
||||||
const auto owner_of_option = [](const std::string& key) {
|
|
||||||
const auto holds = [&key](const std::vector<std::string>& keys) {
|
|
||||||
return std::find(keys.begin(), keys.end(), key) != keys.end();
|
|
||||||
};
|
|
||||||
if (holds(Preset::print_options()))
|
|
||||||
return Preset::TYPE_PRINT;
|
|
||||||
if (holds(Preset::filament_options()))
|
|
||||||
return Preset::TYPE_FILAMENT;
|
|
||||||
if (holds(Preset::printer_options()))
|
|
||||||
return Preset::TYPE_PRINTER;
|
|
||||||
return Preset::TYPE_INVALID;
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::string type_key = plugin_capability_type_to_string(type);
|
|
||||||
for (const auto& [key, def] : print_config_def.options) {
|
|
||||||
if (def.plugin_type != type_key)
|
|
||||||
continue;
|
|
||||||
const Preset::Type owner = owner_of_option(key);
|
|
||||||
if (owner != Preset::TYPE_INVALID)
|
|
||||||
return owner;
|
|
||||||
}
|
|
||||||
return Preset::TYPE_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string resolve_cloud_base_url()
|
static std::string resolve_cloud_base_url()
|
||||||
{
|
{
|
||||||
std::string cloud_base_url = "https://cloud.orcaslicer.com";
|
std::string cloud_base_url = "https://cloud.orcaslicer.com";
|
||||||
|
|||||||
@@ -80,13 +80,6 @@ std::string resolve_recovery_url(const PluginCapabilityRef& ref);
|
|||||||
std::vector<PluginCapabilityRef> referenced_capabilities(Preset::Type type, const Preset& preset);
|
std::vector<PluginCapabilityRef> referenced_capabilities(Preset::Type type, const Preset& preset);
|
||||||
std::vector<PluginCapabilityIdentifier> capabilities_in_use(Preset::Type type, const Preset& preset);
|
std::vector<PluginCapabilityIdentifier> capabilities_in_use(Preset::Type type, const Preset& preset);
|
||||||
|
|
||||||
// The preset type whose presets can reference capabilities of `type` and therefore carry their
|
|
||||||
// overrides. Derived from the ConfigDef rather than hardcoded: a plugin-backed option names the
|
|
||||||
// capability type it accepts (ConfigOptionDef::plugin_type) and belongs to exactly one preset type,
|
|
||||||
// so declaring the option is all it takes to map a new capability type. TYPE_INVALID when no option
|
|
||||||
// accepts the type — nothing can reference it, so no preset owns it.
|
|
||||||
Preset::Type preset_type_for_capability(PluginCapabilityType type);
|
|
||||||
|
|
||||||
// The referenced capabilities of the active preset(s) of `type` that are loaded right now: the set
|
// The referenced capabilities of the active preset(s) of `type` that are loaded right now: the set
|
||||||
// that can actually be configured. Missing and broken refs are absent, having no instance to ask for
|
// that can actually be configured. Missing and broken refs are absent, having no instance to ask for
|
||||||
// a config UI or defaults. A loaded-but-disabled capability IS listed — it still has stored config
|
// a config UI or defaults. A loaded-but-disabled capability IS listed — it still has stored config
|
||||||
|
|||||||
@@ -1,170 +0,0 @@
|
|||||||
#include "PresetPluginConfig.hpp"
|
|
||||||
|
|
||||||
#include "PluginManager.hpp"
|
|
||||||
#include "PluginResolver.hpp"
|
|
||||||
|
|
||||||
#include <slic3r/GUI/GUI_App.hpp>
|
|
||||||
|
|
||||||
#include <boost/log/trivial.hpp>
|
|
||||||
#include <libslic3r/PresetBundle.hpp>
|
|
||||||
#include <wx/app.h>
|
|
||||||
|
|
||||||
namespace Slic3r {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
CapabilityConfigId make_id(const PluginCapabilityIdentifier& id)
|
|
||||||
{
|
|
||||||
return CapabilityConfigId{id.plugin_key, id.name};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string running_plugin_version(const std::string& plugin_key)
|
|
||||||
{
|
|
||||||
PluginDescriptor descriptor;
|
|
||||||
if (!PluginManager::instance().get_catalog().try_get_valid_plugin_descriptor(plugin_key, descriptor))
|
|
||||||
return {};
|
|
||||||
return descriptor.installed_version.empty() ? descriptor.version : descriptor.installed_version;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Null wherever the plugin host runs without the GUI app (the unit tests). wxGetApp() dereferences
|
|
||||||
// the app unconditionally, so ask wxWidgets instead.
|
|
||||||
const PresetBundle* active_preset_bundle()
|
|
||||||
{
|
|
||||||
const auto* app = dynamic_cast<const GUI::GUI_App*>(wxApp::GetInstance());
|
|
||||||
return app == nullptr ? nullptr : app->preset_bundle;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The active preset that can carry an override for a capability of `type`. Edited, not selected: an
|
|
||||||
// override typed into the tab configures the next slice the same way every other unsaved setting does.
|
|
||||||
const Preset* active_preset_for(Preset::Type type)
|
|
||||||
{
|
|
||||||
const PresetBundle* bundle = active_preset_bundle();
|
|
||||||
if (bundle == nullptr)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case Preset::TYPE_PRINT: return &bundle->prints.get_edited_preset();
|
|
||||||
case Preset::TYPE_PRINTER: return &bundle->printers.get_edited_preset();
|
|
||||||
|
|
||||||
|
|
||||||
// Deliberately unimplemented. We currently don't support any filament-based
|
|
||||||
// plugin capabilities. We will defer this to when we have filament-based plugins
|
|
||||||
// capabilities to have a clearer understanding on how to implement this.
|
|
||||||
case Preset::TYPE_FILAMENT: return nullptr;
|
|
||||||
|
|
||||||
default: return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
std::string plugin_overrides_of(const Preset& preset)
|
|
||||||
{
|
|
||||||
const auto* opt = dynamic_cast<const ConfigOptionString*>(preset.config.option(PLUGIN_OVERRIDES_OPTION_KEY));
|
|
||||||
return opt == nullptr ? std::string() : opt->value;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool parse_plugin_overrides(const std::string& raw, CapabilityConfigDocument& document, std::string& error)
|
|
||||||
{
|
|
||||||
document = CapabilityConfigDocument();
|
|
||||||
error.clear();
|
|
||||||
|
|
||||||
if (raw.empty())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
const nlohmann::json parsed = nlohmann::json::parse(raw, nullptr, /* allow_exceptions */ false);
|
|
||||||
if (parsed.is_discarded()) {
|
|
||||||
error = "The preset stores invalid plugin capability configuration JSON.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!parsed.is_array()) {
|
|
||||||
error = "The preset's plugin capability configuration is not an array and cannot be edited.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
document = CapabilityConfigDocument::from_entries(parsed);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string serialize_plugin_overrides(const CapabilityConfigDocument& document)
|
|
||||||
{
|
|
||||||
return document.empty() ? std::string() : document.serialize_entries().dump();
|
|
||||||
}
|
|
||||||
|
|
||||||
EffectiveCapabilityConfig PresetPluginConfigService::get_effective_config(const CapabilityConfigDocument& overrides,
|
|
||||||
const PluginCapabilityIdentifier& id) const
|
|
||||||
{
|
|
||||||
EffectiveCapabilityConfig result;
|
|
||||||
result.id = make_id(id);
|
|
||||||
result.running_plugin_version = running_plugin_version(id.plugin_key);
|
|
||||||
|
|
||||||
const BaseConfig base = PluginManager::instance().get_config().get_config(id.plugin_key, id.name);
|
|
||||||
result.has_base_config = !base.empty();
|
|
||||||
|
|
||||||
if (const auto entry = overrides.find(result.id)) {
|
|
||||||
result.has_preset_override = true;
|
|
||||||
result.config = entry->cap_config;
|
|
||||||
result.stored_plugin_version = entry->plugin_version;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.has_base_config) {
|
|
||||||
result.config = base.config;
|
|
||||||
result.stored_plugin_version = base.plugin_version;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
MutationResult PresetPluginConfigService::set_preset_override(CapabilityConfigDocument& overrides,
|
|
||||||
const PluginCapabilityIdentifier& id,
|
|
||||||
const nlohmann::json& value) const
|
|
||||||
{
|
|
||||||
MutationResult result;
|
|
||||||
const CapabilityConfigId config_id = make_id(id);
|
|
||||||
const std::string version = running_plugin_version(id.plugin_key);
|
|
||||||
|
|
||||||
// A no-op is a successful unchanged result: re-saving the displayed value must not mark the
|
|
||||||
// preset dirty.
|
|
||||||
const auto existing = overrides.find(config_id);
|
|
||||||
if (existing && existing->cap_config == value && existing->plugin_version == version) {
|
|
||||||
result.ok = true;
|
|
||||||
result.effective = get_effective_config(overrides, id);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
overrides.upsert({config_id, version, value});
|
|
||||||
|
|
||||||
result.ok = true;
|
|
||||||
result.changed = true;
|
|
||||||
result.effective = get_effective_config(overrides, id);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
MutationResult PresetPluginConfigService::remove_preset_override(CapabilityConfigDocument& overrides,
|
|
||||||
const PluginCapabilityIdentifier& id) const
|
|
||||||
{
|
|
||||||
MutationResult result;
|
|
||||||
result.ok = true;
|
|
||||||
result.changed = overrides.erase(make_id(id));
|
|
||||||
result.effective = get_effective_config(overrides, id);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
EffectiveCapabilityConfig active_capability_config(const PluginCapabilityIdentifier& id)
|
|
||||||
{
|
|
||||||
const PresetPluginConfigService service;
|
|
||||||
|
|
||||||
CapabilityConfigDocument overrides;
|
|
||||||
if (const Preset* preset = active_preset_for(preset_type_for_capability(id.type))) {
|
|
||||||
std::string error;
|
|
||||||
if (!parse_plugin_overrides(plugin_overrides_of(*preset), overrides, error)) {
|
|
||||||
// Text we cannot read is not an override: log it and resolve against the base config.
|
|
||||||
BOOST_LOG_TRIVIAL(error) << "Preset \"" << preset->name << "\": " << error;
|
|
||||||
overrides = CapabilityConfigDocument();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return service.get_effective_config(overrides, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Slic3r
|
|
||||||
@@ -63,10 +63,8 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// The same resolution against the preset that is active right now, rather than a document the caller
|
// The same resolution against the preset that is active right now, rather than a document the caller
|
||||||
// holds: this is what a running capability reads through the Python config API. Only one preset type
|
// holds: this is what a running capability reads through the Python config API. It falls back to the
|
||||||
// can reference a given capability type (preset_type_for_capability), so there is exactly one preset
|
// base config when no active preset bundle is available.
|
||||||
// to consult. Falls back to the base config for filament capabilities (see active_preset_for) and in
|
|
||||||
// a host with no preset bundle (the plugin unit tests).
|
|
||||||
EffectiveCapabilityConfig active_capability_config(const PluginCapabilityIdentifier& id);
|
EffectiveCapabilityConfig active_capability_config(const PluginCapabilityIdentifier& id);
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|||||||
@@ -72,18 +72,3 @@ TEST_CASE("referenced_capabilities skips malformed manifest entries", "[PluginRe
|
|||||||
|
|
||||||
CHECK(capability_names(referenced_capabilities(Preset::TYPE_PRINT, preset)) == std::vector<std::string>{"CapA"});
|
CHECK(capability_names(referenced_capabilities(Preset::TYPE_PRINT, preset)) == std::vector<std::string>{"CapA"});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("preset_type_for_capability names the preset type whose options reference the capability", "[PluginResolver]")
|
|
||||||
{
|
|
||||||
// Read out of the ConfigDef: declaring a plugin_type on an option is what puts its capability
|
|
||||||
// type on this map.
|
|
||||||
CHECK(preset_type_for_capability(PluginCapabilityType::SlicingPipeline) == Preset::TYPE_PRINT);
|
|
||||||
CHECK(preset_type_for_capability(PluginCapabilityType::PrinterConnection) == Preset::TYPE_PRINTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("preset_type_for_capability leaves capability types no option accepts unowned", "[PluginResolver]")
|
|
||||||
{
|
|
||||||
// No option accepts them, so no preset can override them: they read config.json alone.
|
|
||||||
CHECK(preset_type_for_capability(PluginCapabilityType::Automation) == Preset::TYPE_INVALID);
|
|
||||||
CHECK(preset_type_for_capability(PluginCapabilityType::Unknown) == Preset::TYPE_INVALID);
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user