mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-07 15:51:08 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f140a1208 | ||
|
|
cb7d2c698d | ||
|
|
a38e6c61f2 | ||
|
|
f9ddb23804 | ||
|
|
ff8aebe76f | ||
|
|
a56d1bf33e | ||
|
|
662a8e340d |
@@ -311,7 +311,7 @@ function CreatePrinterBlock(OneModel)
|
||||
|
||||
return '<div class="PrinterBlock" onClick="ChooseModel(\''+vendor+'\',\''+OneModel['model']+'\')">'+
|
||||
' <div class="PImg">'+
|
||||
' <img class="ModelThumbnail" src="' + OneModel['cover'] + '" />'+
|
||||
' <img class="ModelThumbnail" src="' + OneModel['cover'] + '" onerror="this.onerror=null;this.src=\'../img/printer-dummy.png\';"/>'+
|
||||
' </div>'+
|
||||
' <div class="PrinterInfoMark">?</div>'+
|
||||
' <div class="PrinterInfo">'+
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -4,5 +4,10 @@
|
||||
<dict>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
<!-- The Bambu network plug-in's code protector rewrites one of its own signed code pages
|
||||
after loading. Under the hardened runtime macOS kills the process when that page is
|
||||
paged back in; this lets it run, as Bambu Studio's signature does. -->
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -3618,6 +3618,9 @@ int CLI::run(int argc, char **argv)
|
||||
{
|
||||
if (opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "model_id" || opt_key == "dev_model_name" || opt_key == "filament_settings_id")
|
||||
continue;
|
||||
// rebuilt from every filament after this loop
|
||||
if (filament_dev_options.find(opt_key) != filament_dev_options.end())
|
||||
continue;
|
||||
ConfigOption *opt = m_print_config.option(opt_key, true);
|
||||
if (opt == nullptr) {
|
||||
// opt_key does not exist in this ConfigBase and it cannot be created, because it is not defined by this->def().
|
||||
@@ -3681,6 +3684,14 @@ int CLI::run(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
// The stored values cannot be told apart per filament, so they are kept as they are unless every slot has a config.
|
||||
std::vector<const DynamicPrintConfig *> filament_configs(filament_count, nullptr);
|
||||
for (size_t index = 0; index < load_filaments_config.size(); index++)
|
||||
if (load_filaments_index[index] >= 1 && load_filaments_index[index] <= filament_count)
|
||||
filament_configs[load_filaments_index[index] - 1] = &load_filaments_config[index];
|
||||
if (std::find(filament_configs.begin(), filament_configs.end(), nullptr) == filament_configs.end())
|
||||
set_filament_dev_options(m_print_config, filament_configs);
|
||||
|
||||
if (m_print_config.option<ConfigOptionStrings>("filament_extruder_variant")) {
|
||||
std::vector<int>& filament_self_indice = m_print_config.option<ConfigOptionInts>("filament_self_index", true)->values;
|
||||
int index_size = m_print_config.option<ConfigOptionStrings>("filament_extruder_variant")->size();
|
||||
|
||||
@@ -5,5 +5,10 @@
|
||||
<!-- for dynamic loading of libraries without signature validation. Used for 3dconnection drivers.-->
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
<!-- The Bambu network plug-in's code protector rewrites one of its own signed code pages
|
||||
after loading. Under the hardened runtime macOS kills the process when that page is
|
||||
paged back in; this lets it run, as Bambu Studio's signature does. -->
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -10822,6 +10822,22 @@ void normalize_filament_values_to_variants(DynamicPrintConfig &config)
|
||||
}
|
||||
}
|
||||
|
||||
void set_filament_dev_options(DynamicPrintConfig &config, const std::vector<const DynamicPrintConfig *> &filament_configs)
|
||||
{
|
||||
for (const std::string &key : filament_dev_options) {
|
||||
if (std::none_of(filament_configs.begin(), filament_configs.end(), [&key](const DynamicPrintConfig *filament) { return filament->has(key); }))
|
||||
continue;
|
||||
const ConfigOption *default_value = print_config_def.get(key)->default_value.get();
|
||||
auto *dst = static_cast<ConfigOptionVectorBase *>(config.option(key, true));
|
||||
dst->clear();
|
||||
for (const DynamicPrintConfig *filament : filament_configs) {
|
||||
const auto *src = static_cast<const ConfigOptionVectorBase *>(filament->has(key) ? filament->option(key) : default_value);
|
||||
if (!src->empty())
|
||||
dst->append(src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//used for object/region config
|
||||
//use the smallest of multiple to single
|
||||
@@ -11520,12 +11536,18 @@ void DynamicPrintConfig::update_non_diff_values_to_base_config(DynamicPrintConfi
|
||||
int cur_variant_count = cur_extruder_variants.size();
|
||||
int target_variant_count = target_extruder_variants.size();
|
||||
|
||||
// A base variant this config does not list (the base gained it after the config was saved, or the
|
||||
// config lists none) takes this config's first variant of the same extruder, as a user preset's
|
||||
// values do in update_diff_values_to_child_config. Left unmatched, the base's value would silently
|
||||
// replace the user's.
|
||||
variant_index.resize(target_variant_count, -1);
|
||||
if (cur_variant_count == 0) {
|
||||
// Defensive: target_variant_count may be 0 if the preset doesn't carry extruder_variant_name.
|
||||
// In that case keep variant_index empty and let the downstream size checks produce a useful error.
|
||||
if (!variant_index.empty())
|
||||
variant_index[0] = 0;
|
||||
// This config's one value belongs to the extruder of the base's first variant.
|
||||
variant_index = map_variant_indices(target_extruder_variants, target_extruder_ids, {},
|
||||
target_extruder_ids.empty() ? std::vector<int>() : std::vector<int>{target_extruder_ids[0]});
|
||||
}
|
||||
else if ((cur_extruder_ids.size() > 0) && cur_variant_count != cur_extruder_ids.size()){
|
||||
//should not happen
|
||||
@@ -11538,18 +11560,7 @@ void DynamicPrintConfig::update_non_diff_values_to_base_config(DynamicPrintConfi
|
||||
%extruder_variant_name %target_variant_count %extruder_id_name %target_extruder_ids.size();
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < target_variant_count; i++)
|
||||
{
|
||||
for (int j = 0; j < cur_variant_count; j++)
|
||||
{
|
||||
if ((target_extruder_variants[i] == cur_extruder_variants[j])
|
||||
&&(target_extruder_ids.empty() || (target_extruder_ids[i] == cur_extruder_ids[j])))
|
||||
{
|
||||
variant_index[i] = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
variant_index = map_variant_indices(target_extruder_variants, target_extruder_ids, cur_extruder_variants, cur_extruder_ids);
|
||||
}
|
||||
|
||||
for (auto& opt : keys) {
|
||||
@@ -11574,6 +11585,13 @@ void DynamicPrintConfig::update_non_diff_values_to_base_config(DynamicPrintConfi
|
||||
if (cur_variant_count > target_variant_count)
|
||||
continue;
|
||||
|
||||
// The variant lists are the base's layout itself, which every other value is
|
||||
// carried onto: a variant this config lacks keeps its own name and id.
|
||||
if (opt == extruder_id_name || opt == extruder_variant_name) {
|
||||
opt_src->set(opt_target);
|
||||
continue;
|
||||
}
|
||||
|
||||
int stride = 1;
|
||||
if (key_set2.find(opt) != key_set2.end())
|
||||
stride = 2;
|
||||
|
||||
@@ -928,6 +928,11 @@ void normalize_filament_values_to_variants(DynamicPrintConfig &config);
|
||||
|
||||
extern std::set<std::string> filament_dev_options;
|
||||
|
||||
// Orca: a filament_dev_options option holds several values per filament, and how many is up to the
|
||||
// filament preset, so one filament's values cannot be replaced in place. This rebuilds each option from
|
||||
// filament_configs, one config per filament in slot order, as the filaments' values one after another.
|
||||
void set_filament_dev_options(DynamicPrintConfig &config, const std::vector<const DynamicPrintConfig *> &filament_configs);
|
||||
|
||||
extern void update_static_print_config_from_dynamic(ConfigBase& config, const DynamicPrintConfig& dest_config, std::vector<int> variant_index, std::set<std::string>& key_set1, int stride = 1);
|
||||
extern void compute_filament_override_value(const std::string& opt_key, const ConfigOption *opt_old_machine, const ConfigOption *opt_new_machine, const ConfigOption *opt_new_filament, const DynamicPrintConfig& new_full_config,
|
||||
t_config_option_keys& diff_keys, DynamicPrintConfig& filament_overrides, std::vector<int>& f_map_indices);
|
||||
|
||||
@@ -596,7 +596,8 @@ PrintParams_Legacy BBLNetworkPlugin::as_legacy(PrintParams& param)
|
||||
}
|
||||
|
||||
// Every PrintParams field except the four the 02.08.01 series added
|
||||
// (task_timelapse_use_internal, extruder_cali_manual_mode, svc_context, slicer_uid).
|
||||
// (task_timelapse_use_internal, extruder_cali_manual_mode, svc_context, slicer_uid) and the
|
||||
// queue_plate_id 02.08.02 appended.
|
||||
PrintParams_0203 BBLNetworkPlugin::as_0203(PrintParams& param)
|
||||
{
|
||||
PrintParams_0203 p;
|
||||
|
||||
@@ -336,6 +336,7 @@ struct PrintParams {
|
||||
bool try_emmc_print;
|
||||
std::string svc_context;
|
||||
std::string slicer_uid;
|
||||
std::string queue_plate_id;
|
||||
};
|
||||
|
||||
struct TaskQueryParams
|
||||
@@ -412,7 +413,7 @@ enum class NetworkAbi {
|
||||
Unsupported, // no generation in this build can call it - never dispatch through it
|
||||
Legacy, // 01.10.01: PrintParams_Legacy; send_message/send_message_to_printer take no flag
|
||||
V0203, // 02.03.00: PrintParams_0203; bind takes no dev_model
|
||||
Current, // 02.08.01: the layouts and signatures this build declares directly
|
||||
Current, // 02.08.04: the layouts and signatures this build declares directly
|
||||
};
|
||||
|
||||
struct NetworkLibraryVersion {
|
||||
@@ -425,10 +426,12 @@ struct NetworkLibraryVersion {
|
||||
};
|
||||
|
||||
// Every row names the generation that can call it, so a series can never be offered without a
|
||||
// host-side ABI for it. Series with no generation - 02.01.01, 02.00.02 and older - must stay out;
|
||||
// is_supported_network_version() is the gate that keeps them from loading.
|
||||
// host-side ABI for it. Series with no generation - 02.08.01 (whose PrintParams lacks the
|
||||
// queue_plate_id that 02.08.02 appended, and whose malformed bind table macOS 27 refuses to
|
||||
// load), 02.01.01, 02.00.02 and older - must stay out; is_supported_network_version() is the
|
||||
// gate that keeps them from loading.
|
||||
static const NetworkLibraryVersion AVAILABLE_NETWORK_VERSIONS[] = {
|
||||
{"02.08.01", "02.08.01", nullptr, true, nullptr, NetworkAbi::Current},
|
||||
{"02.08.04", "02.08.04", nullptr, true, nullptr, NetworkAbi::Current},
|
||||
{"02.03.00", "02.03.00", nullptr, false,
|
||||
"An older plug-in series. Features that need newer plug-in support, such as print-failure "
|
||||
"snapshots in the device error dialog, are unavailable.", NetworkAbi::V0203},
|
||||
|
||||
@@ -25,11 +25,19 @@ extern const char* const INSTALL_STATE_FILE;
|
||||
// Plugin config and orca.host.ui payloads both cross the boundary as plain JSON-compatible
|
||||
// values, so both go through these.
|
||||
|
||||
inline pybind11::object json_to_py(const nlohmann::json& j)
|
||||
// Maximum nesting depth for JSON <-> Python conversion. A self-referential or pathologically
|
||||
// deep value would otherwise recurse until the native C stack overflows, an uncatchable crash;
|
||||
// past this bound we raise instead. 200 is far beyond any legitimate plugin config or UI payload.
|
||||
inline constexpr int kMaxJsonConversionDepth = 200;
|
||||
|
||||
inline pybind11::object json_to_py(const nlohmann::json& j, int depth = 0)
|
||||
{
|
||||
namespace py = pybind11;
|
||||
using json = nlohmann::json;
|
||||
|
||||
if (depth > kMaxJsonConversionDepth)
|
||||
throw py::value_error("Plugin JSON value nested too deeply");
|
||||
|
||||
switch (j.type()) {
|
||||
case json::value_t::null: return py::none();
|
||||
case json::value_t::boolean: return py::bool_(j.get<bool>());
|
||||
@@ -40,24 +48,27 @@ inline pybind11::object json_to_py(const nlohmann::json& j)
|
||||
case json::value_t::array: {
|
||||
py::list lst;
|
||||
for (const auto& e : j)
|
||||
lst.append(json_to_py(e));
|
||||
lst.append(json_to_py(e, depth + 1));
|
||||
return lst;
|
||||
}
|
||||
case json::value_t::object: {
|
||||
py::dict d;
|
||||
for (auto it = j.begin(); it != j.end(); ++it)
|
||||
d[py::str(it.key())] = json_to_py(it.value());
|
||||
d[py::str(it.key())] = json_to_py(it.value(), depth + 1);
|
||||
return d;
|
||||
}
|
||||
default: return py::none();
|
||||
}
|
||||
}
|
||||
|
||||
inline nlohmann::json py_to_json(const pybind11::handle& o)
|
||||
inline nlohmann::json py_to_json(const pybind11::handle& o, int depth = 0)
|
||||
{
|
||||
namespace py = pybind11;
|
||||
using json = nlohmann::json;
|
||||
|
||||
if (depth > kMaxJsonConversionDepth)
|
||||
throw py::value_error("Plugin value nested too deeply (possible cycle)");
|
||||
|
||||
if (o.is_none())
|
||||
return json(nullptr);
|
||||
if (py::isinstance<py::bool_>(o)) // bool before int (bool subclasses int in Python)
|
||||
@@ -73,13 +84,13 @@ inline nlohmann::json py_to_json(const pybind11::handle& o)
|
||||
if (py::isinstance<py::dict>(o)) {
|
||||
json obj = json::object();
|
||||
for (auto item : py::reinterpret_borrow<py::dict>(o))
|
||||
obj[py::str(item.first).cast<std::string>()] = py_to_json(item.second);
|
||||
obj[py::str(item.first).cast<std::string>()] = py_to_json(item.second, depth + 1);
|
||||
return obj;
|
||||
}
|
||||
if (py::isinstance<py::list>(o) || py::isinstance<py::tuple>(o)) {
|
||||
json arr = json::array();
|
||||
for (auto e : o)
|
||||
arr.push_back(py_to_json(e));
|
||||
arr.push_back(py_to_json(e, depth + 1));
|
||||
return arr;
|
||||
}
|
||||
return py::str(o).cast<std::string>(); // fallback: str()
|
||||
|
||||
@@ -538,6 +538,110 @@ SCENARIO("update_diff_values_to_child_config keeps a child's values on variants
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("update_non_diff_values_to_base_config keeps a project's changed values on variants it does not list",
|
||||
"[Config][Variant]") {
|
||||
std::set<std::string> no_keys;
|
||||
auto variants = [](std::initializer_list<std::string> names) { return new Slic3r::ConfigOptionStrings(names); };
|
||||
|
||||
GIVEN("A filament base with three variants") {
|
||||
Slic3r::DynamicPrintConfig base;
|
||||
base.set_key_value("filament_extruder_variant",
|
||||
variants({"Direct Drive Standard", "Bowden Standard", "Direct Drive High Flow"}));
|
||||
base.set_deserialize_strict("nozzle_temperature", "220,220,220");
|
||||
|
||||
WHEN("the project was saved when the base had only its first variant") {
|
||||
Slic3r::DynamicPrintConfig project;
|
||||
project.set_key_value("filament_extruder_variant", variants({"Direct Drive Standard"}));
|
||||
project.set_deserialize_strict("nozzle_temperature", "199");
|
||||
|
||||
AND_WHEN("the project lists the value as changed") {
|
||||
project.update_non_diff_values_to_base_config(base, project.keys(), {"nozzle_temperature"}, "", "filament_extruder_variant",
|
||||
Slic3r::filament_options_with_variant, no_keys);
|
||||
THEN("the project's value applies to every variant") {
|
||||
REQUIRE(project.opt_serialize("nozzle_temperature") == "199,199,199");
|
||||
}
|
||||
}
|
||||
AND_WHEN("the project does not list the value as changed") {
|
||||
project.update_non_diff_values_to_base_config(base, project.keys(), {}, "", "filament_extruder_variant",
|
||||
Slic3r::filament_options_with_variant, no_keys);
|
||||
THEN("the base's values replace it") {
|
||||
REQUIRE(project.opt_serialize("nozzle_temperature") == "220,220,220");
|
||||
}
|
||||
}
|
||||
}
|
||||
WHEN("the project lists every variant, in another order") {
|
||||
Slic3r::DynamicPrintConfig project;
|
||||
project.set_key_value("filament_extruder_variant",
|
||||
variants({"Bowden Standard", "Direct Drive High Flow", "Direct Drive Standard"}));
|
||||
project.set_deserialize_strict("nozzle_temperature", "190,205,199");
|
||||
project.update_non_diff_values_to_base_config(base, project.keys(), {"nozzle_temperature"}, "", "filament_extruder_variant",
|
||||
Slic3r::filament_options_with_variant, no_keys);
|
||||
THEN("each variant keeps its own value") {
|
||||
REQUIRE(project.opt_serialize("nozzle_temperature") == "199,190,205");
|
||||
}
|
||||
}
|
||||
WHEN("the project lists no variants") {
|
||||
Slic3r::DynamicPrintConfig project;
|
||||
project.set_deserialize_strict("nozzle_temperature", "199");
|
||||
project.update_non_diff_values_to_base_config(base, project.keys(), {"nozzle_temperature"}, "", "filament_extruder_variant",
|
||||
Slic3r::filament_options_with_variant, no_keys);
|
||||
THEN("the project's value applies to every variant") {
|
||||
REQUIRE(project.opt_serialize("nozzle_temperature") == "199,199,199");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A two-extruder printer base with two variants per extruder") {
|
||||
Slic3r::DynamicPrintConfig base;
|
||||
base.set_key_value("printer_extruder_variant",
|
||||
variants({"Direct Drive Standard", "Direct Drive High Flow", "Direct Drive Standard", "Direct Drive High Flow"}));
|
||||
base.set_key_value("printer_extruder_id", new Slic3r::ConfigOptionInts({1, 1, 2, 2}));
|
||||
base.set_deserialize_strict("retraction_length", "0.8,0.8,0.8,0.8");
|
||||
|
||||
WHEN("the project lists only the Standard variant of each extruder") {
|
||||
Slic3r::DynamicPrintConfig project;
|
||||
project.set_key_value("printer_extruder_variant", variants({"Direct Drive Standard", "Direct Drive Standard"}));
|
||||
project.set_key_value("printer_extruder_id", new Slic3r::ConfigOptionInts({1, 2}));
|
||||
project.set_deserialize_strict("retraction_length", "1.1,2.2");
|
||||
project.update_non_diff_values_to_base_config(base, project.keys(), {"retraction_length"}, "printer_extruder_id", "printer_extruder_variant",
|
||||
Slic3r::printer_options_with_variant_1,
|
||||
Slic3r::printer_options_with_variant_2);
|
||||
THEN("each extruder's High Flow variant takes that extruder's value") {
|
||||
REQUIRE(project.opt_serialize("retraction_length") == "1.1,1.1,2.2,2.2");
|
||||
}
|
||||
}
|
||||
WHEN("the project lists only the Standard variant of each extruder, and the variant lists as changed") {
|
||||
Slic3r::DynamicPrintConfig project;
|
||||
project.set_key_value("printer_extruder_variant", variants({"Direct Drive Standard", "Direct Drive Standard"}));
|
||||
project.set_key_value("printer_extruder_id", new Slic3r::ConfigOptionInts({1, 2}));
|
||||
project.set_deserialize_strict("machine_max_speed_x", "300,100,400,150");
|
||||
base.set_deserialize_strict("machine_max_speed_x", "500,200,500,200,500,200,500,200");
|
||||
project.update_non_diff_values_to_base_config(base, project.keys(),
|
||||
{"machine_max_speed_x", "printer_extruder_id", "printer_extruder_variant"},
|
||||
"printer_extruder_id", "printer_extruder_variant",
|
||||
Slic3r::printer_options_with_variant_1,
|
||||
Slic3r::printer_options_with_variant_2);
|
||||
THEN("the variant lists are the base's") {
|
||||
REQUIRE(project.opt_serialize("printer_extruder_variant") == base.opt_serialize("printer_extruder_variant"));
|
||||
REQUIRE(project.opt_serialize("printer_extruder_id") == "1,1,2,2");
|
||||
}
|
||||
THEN("each extruder's High Flow variant takes that extruder's pair of limits") {
|
||||
REQUIRE(project.opt_serialize("machine_max_speed_x") == "300,100,300,100,400,150,400,150");
|
||||
}
|
||||
}
|
||||
WHEN("the project lists no variants") {
|
||||
Slic3r::DynamicPrintConfig project;
|
||||
project.set_deserialize_strict("retraction_length", "1.1");
|
||||
project.update_non_diff_values_to_base_config(base, project.keys(), {"retraction_length"}, "printer_extruder_id", "printer_extruder_variant",
|
||||
Slic3r::printer_options_with_variant_1,
|
||||
Slic3r::printer_options_with_variant_2);
|
||||
THEN("only the first extruder's variants take the project's value") {
|
||||
REQUIRE(project.opt_serialize("retraction_length") == "1.1,1.1,0.8,0.8");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SCENARIO("DynamicPrintConfig JSON serialization", "[Config]") {
|
||||
// WHEN("DynamicPrintConfig is serialized and deserialized") {
|
||||
// auto now = std::chrono::high_resolution_clock::now();
|
||||
|
||||
@@ -799,3 +799,30 @@ TEST_CASE("A per-variant filament option read with a single value gives it to ev
|
||||
config.load_from_ini_string("pressure_advance = 0.021", ForwardCompatibilitySubstitutionRule::Disable);
|
||||
REQUIRE(config.option<ConfigOptionFloats>("pressure_advance")->values == std::vector<double>({0.021, 0.021, 0.021}));
|
||||
}
|
||||
|
||||
// The device drying options hold several values per filament, as many as each filament preset gives.
|
||||
TEST_CASE("The device drying options are rebuilt as each filament's values in slot order", "[Config]")
|
||||
{
|
||||
DynamicPrintConfig two_values, one_value, no_value;
|
||||
two_values.option<ConfigOptionStrings>("filament_dev_ams_drying_ams_limitations", true)->values = {"1", "0"};
|
||||
two_values.option<ConfigOptionFloats>("filament_dev_ams_drying_temperature", true)->values = {45., 45., 55., 55.};
|
||||
one_value.option<ConfigOptionStrings>("filament_dev_ams_drying_ams_limitations", true)->values = {"1"};
|
||||
one_value.option<ConfigOptionFloats>("filament_dev_ams_drying_temperature", true)->values = {65., 65., 75., 75.};
|
||||
|
||||
// values a project stored for three other filaments
|
||||
DynamicPrintConfig config;
|
||||
config.option<ConfigOptionStrings>("filament_dev_ams_drying_ams_limitations", true)->values = {"0", "0", "0"};
|
||||
config.option<ConfigOptionFloats>("filament_dev_chamber_drying_time", true)->values = {12., 8., 12.};
|
||||
|
||||
set_filament_dev_options(config, {&two_values, &one_value, &two_values});
|
||||
REQUIRE(config.option<ConfigOptionStrings>("filament_dev_ams_drying_ams_limitations")->values ==
|
||||
std::vector<std::string>({"1", "0", "1", "1", "0"}));
|
||||
REQUIRE(config.option<ConfigOptionFloats>("filament_dev_ams_drying_temperature")->values ==
|
||||
std::vector<double>({45., 45., 55., 55., 65., 65., 75., 75., 45., 45., 55., 55.}));
|
||||
// an option no filament defines keeps the stored values
|
||||
REQUIRE(config.option<ConfigOptionFloats>("filament_dev_chamber_drying_time")->values == std::vector<double>({12., 8., 12.}));
|
||||
|
||||
// a filament without the option takes the option's default
|
||||
set_filament_dev_options(config, {&two_values, &no_value});
|
||||
REQUIRE(config.option<ConfigOptionStrings>("filament_dev_ams_drying_ams_limitations")->values == std::vector<std::string>({"1", "0", ""}));
|
||||
}
|
||||
|
||||
@@ -6228,8 +6228,9 @@ TEST_CASE("A per-variant project value maps onto its base preset's variant layou
|
||||
base_finder(&base, calls));
|
||||
CHECK(config.option<ConfigOptionStrings>("print_extruder_variant")->values ==
|
||||
std::vector<std::string>{"Direct Drive Standard", "Direct Drive High Flow"});
|
||||
// The listed key keeps the project's Standard value and takes High Flow from the base.
|
||||
check_double_vector(config.option<ConfigOptionFloats>("outer_wall_speed")->values, {100., 300.});
|
||||
// The listed key keeps the project's Standard value, and High Flow, which the project does not
|
||||
// list, takes it too, as a user preset's value does.
|
||||
check_double_vector(config.option<ConfigOptionFloats>("outer_wall_speed")->values, {100., 100.});
|
||||
check_double_vector(config.option<ConfigOptionFloats>("inner_wall_speed")->values, {250., 350.});
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ add_executable(${_TEST_NAME}_tests
|
||||
test_plugin_sort.cpp
|
||||
test_plugin_cloud_metadata.cpp
|
||||
test_plugin_audit.cpp
|
||||
test_plugin_json_depth.cpp
|
||||
test_shortcuts.cpp
|
||||
test_file_url.cpp
|
||||
test_user_manager.cpp
|
||||
|
||||
@@ -78,31 +78,34 @@ TEST_CASE("Series and managed classification", "[NetworkVersions]")
|
||||
|
||||
TEST_CASE_METHOD(PluginFolderFixture, "Managed builds fold into the series; customs are surfaced", "[NetworkVersions]")
|
||||
{
|
||||
add_plugin("02.08.01.55"); // managed, same series -> folded into the 02.08.01 row
|
||||
add_plugin("02.08.04.60"); // managed, same series -> folded into the 02.08.04 row
|
||||
add_plugin("02.09.00.10"); // managed, unknown series -> not listed
|
||||
add_plugin("02.08.01.55"); // managed, series this build no longer has an ABI for -> not listed
|
||||
add_plugin("02.03.00.62"); // managed, older whitelisted series -> folded into 02.03.00
|
||||
add_plugin("02.01.01.52"); // managed, series with no ABI in this build -> not listed
|
||||
add_plugin("02.08.01_custom"); // custom, whitelisted series -> listed under it
|
||||
add_plugin("02.08.01.52-dev"); // custom (dash-suffixed), whitelisted series -> listed
|
||||
add_plugin("02.08.04_custom"); // custom, whitelisted series -> listed under it
|
||||
add_plugin("02.08.04.52-dev"); // custom (dash-suffixed), whitelisted series -> listed
|
||||
|
||||
auto versions = get_all_available_versions();
|
||||
|
||||
// The specific managed build never gets its own row - the series represents it.
|
||||
REQUIRE(count_version(versions, "02.08.01.55") == 0);
|
||||
REQUIRE(count_version(versions, "02.08.01") == 1);
|
||||
REQUIRE(count_version(versions, "02.08.04.60") == 0);
|
||||
REQUIRE(count_version(versions, "02.08.04") == 1);
|
||||
REQUIRE(count_version(versions, "02.09.00.10") == 0);
|
||||
REQUIRE(count_version(versions, "02.08.01.55") == 0);
|
||||
REQUIRE(count_version(versions, "02.08.01") == 0);
|
||||
REQUIRE(count_version(versions, "02.03.00.62") == 0);
|
||||
REQUIRE(count_version(versions, "02.03.00") == 1);
|
||||
REQUIRE(count_version(versions, "02.01.01.52") == 0);
|
||||
// Custom-named builds are distinct files kept under their own name.
|
||||
REQUIRE(count_version(versions, "02.08.01_custom") == 1);
|
||||
REQUIRE(count_version(versions, "02.08.01.52-dev") == 1);
|
||||
REQUIRE(count_version(versions, "02.08.04_custom") == 1);
|
||||
REQUIRE(count_version(versions, "02.08.04.52-dev") == 1);
|
||||
|
||||
// Newest series first, its customs nested under it (suffix sort: "" < ".52-dev" < "_custom"),
|
||||
// then older series, legacy last.
|
||||
REQUIRE(versions[0].version == "02.08.01");
|
||||
REQUIRE(versions[1].version == "02.08.01.52-dev");
|
||||
REQUIRE(versions[2].version == "02.08.01_custom");
|
||||
REQUIRE(versions[0].version == "02.08.04");
|
||||
REQUIRE(versions[1].version == "02.08.04.52-dev");
|
||||
REQUIRE(versions[2].version == "02.08.04_custom");
|
||||
REQUIRE(versions[3].version == "02.03.00");
|
||||
REQUIRE(versions.back().version == BAMBU_NETWORK_AGENT_VERSION_LEGACY);
|
||||
|
||||
@@ -111,9 +114,9 @@ TEST_CASE_METHOD(PluginFolderFixture, "Managed builds fold into the series; cust
|
||||
REQUIRE_FALSE(versions[3].is_latest);
|
||||
|
||||
// Customs sort/render nested under their series (non-empty suffix, base = the series).
|
||||
REQUIRE(versions[1].base_version == "02.08.01");
|
||||
REQUIRE(versions[1].base_version == "02.08.04");
|
||||
REQUIRE_FALSE(versions[1].suffix.empty());
|
||||
REQUIRE(versions[2].base_version == "02.08.01");
|
||||
REQUIRE(versions[2].base_version == "02.08.04");
|
||||
REQUIRE_FALSE(versions[2].suffix.empty());
|
||||
|
||||
// "(Latest)" is the series row, never a nested custom build.
|
||||
@@ -123,20 +126,20 @@ TEST_CASE_METHOD(PluginFolderFixture, "Managed builds fold into the series; cust
|
||||
REQUIRE_FALSE(versions[2].is_latest);
|
||||
|
||||
// The stored default that drives download and update-check decisions is now the series.
|
||||
REQUIRE(std::string(get_latest_network_version()) == "02.08.01");
|
||||
REQUIRE(std::string(get_latest_network_version()) == "02.08.04");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(PluginFolderFixture, "Only the loaded series is marked installed", "[NetworkVersions]")
|
||||
{
|
||||
add_plugin("02.08.01.55");
|
||||
add_plugin("02.08.01_custom");
|
||||
add_plugin("02.08.04.60");
|
||||
add_plugin("02.08.04_custom");
|
||||
|
||||
// The loaded plug-in reports its full build (02.08.01.55); the series row is what gets marked.
|
||||
// The loaded plug-in reports its full build (02.08.04.60); the series row is what gets marked.
|
||||
{
|
||||
auto versions = get_all_available_versions("02.08.01.55");
|
||||
auto versions = get_all_available_versions("02.08.04.60");
|
||||
int marked = 0;
|
||||
for (const auto& info : versions)
|
||||
if (info.is_loaded) { ++marked; REQUIRE(info.version == "02.08.01"); }
|
||||
if (info.is_loaded) { ++marked; REQUIRE(info.version == "02.08.04"); }
|
||||
REQUIRE(marked == 1);
|
||||
}
|
||||
|
||||
@@ -152,10 +155,10 @@ TEST_CASE_METHOD(PluginFolderFixture, "Only the loaded series is marked installe
|
||||
|
||||
// A loaded custom build matches its own row, never the bare series.
|
||||
{
|
||||
auto versions = get_all_available_versions("02.08.01_custom");
|
||||
auto versions = get_all_available_versions("02.08.04_custom");
|
||||
int marked = 0;
|
||||
for (const auto& info : versions)
|
||||
if (info.is_loaded) { ++marked; REQUIRE(info.version == "02.08.01_custom"); }
|
||||
if (info.is_loaded) { ++marked; REQUIRE(info.version == "02.08.04_custom"); }
|
||||
REQUIRE(marked == 1);
|
||||
}
|
||||
|
||||
@@ -167,11 +170,11 @@ TEST_CASE_METHOD(PluginFolderFixture, "Only the loaded series is marked installe
|
||||
TEST_CASE("Only whitelisted series pass the load gate", "[NetworkVersions]")
|
||||
{
|
||||
// Each whitelisted series, its builds, and custom-named builds of that series.
|
||||
REQUIRE(is_supported_network_version("02.08.01"));
|
||||
REQUIRE(is_supported_network_version("02.08.01.52"));
|
||||
REQUIRE(is_supported_network_version("02.08.01.55"));
|
||||
REQUIRE(is_supported_network_version("02.08.01_custom"));
|
||||
REQUIRE(is_supported_network_version("02.08.01.52-dev"));
|
||||
REQUIRE(is_supported_network_version("02.08.04"));
|
||||
REQUIRE(is_supported_network_version("02.08.04.52"));
|
||||
REQUIRE(is_supported_network_version("02.08.04.60"));
|
||||
REQUIRE(is_supported_network_version("02.08.04_custom"));
|
||||
REQUIRE(is_supported_network_version("02.08.04.52-dev"));
|
||||
REQUIRE(is_supported_network_version("02.03.00"));
|
||||
REQUIRE(is_supported_network_version("02.03.00.62"));
|
||||
REQUIRE(is_supported_network_version("02.03.00.70"));
|
||||
@@ -179,6 +182,9 @@ TEST_CASE("Only whitelisted series pass the load gate", "[NetworkVersions]")
|
||||
REQUIRE(is_supported_network_version(BAMBU_NETWORK_AGENT_VERSION_LEGACY));
|
||||
|
||||
// Series whitelisted by previous Orca releases that no generation here can call.
|
||||
REQUIRE_FALSE(is_supported_network_version("02.08.01"));
|
||||
REQUIRE_FALSE(is_supported_network_version("02.08.01.55"));
|
||||
REQUIRE_FALSE(is_supported_network_version("02.08.01_custom"));
|
||||
REQUIRE_FALSE(is_supported_network_version("02.01.01.52"));
|
||||
REQUIRE_FALSE(is_supported_network_version("02.00.02.50"));
|
||||
|
||||
@@ -198,9 +204,9 @@ TEST_CASE("Each version resolves to the ABI generation that can call it", "[Netw
|
||||
{
|
||||
// The generation is keyed on the series, so every build of a series - including the
|
||||
// custom-named ones - resolves to the same one.
|
||||
CHECK(network_plugin_abi("02.08.01") == NetworkAbi::Current);
|
||||
CHECK(network_plugin_abi("02.08.01.55") == NetworkAbi::Current);
|
||||
CHECK(network_plugin_abi("02.08.01.52-dev") == NetworkAbi::Current);
|
||||
CHECK(network_plugin_abi("02.08.04") == NetworkAbi::Current);
|
||||
CHECK(network_plugin_abi("02.08.04.60") == NetworkAbi::Current);
|
||||
CHECK(network_plugin_abi("02.08.04.52-dev") == NetworkAbi::Current);
|
||||
CHECK(network_plugin_abi("02.03.00") == NetworkAbi::V0203);
|
||||
CHECK(network_plugin_abi("02.03.00.62") == NetworkAbi::V0203);
|
||||
CHECK(network_plugin_abi("02.03.00_custom") == NetworkAbi::V0203);
|
||||
@@ -208,6 +214,7 @@ TEST_CASE("Each version resolves to the ABI generation that can call it", "[Netw
|
||||
|
||||
// Anything the load gate rejects must dispatch through nothing at all, rather than
|
||||
// defaulting to a layout it does not share.
|
||||
CHECK(network_plugin_abi("02.08.01.55") == NetworkAbi::Unsupported);
|
||||
CHECK(network_plugin_abi("02.01.01.52") == NetworkAbi::Unsupported);
|
||||
CHECK(network_plugin_abi("02.00.02.50") == NetworkAbi::Unsupported);
|
||||
CHECK(network_plugin_abi("02.09.00.10") == NetworkAbi::Unsupported);
|
||||
@@ -234,7 +241,7 @@ TEST_CASE_METHOD(PluginFolderFixture, "Legacy series never adopts discovered bui
|
||||
// With nothing else on disk, the series holds "(Latest)" even though its library is
|
||||
// not installed.
|
||||
for (const auto& info : versions) {
|
||||
if (info.version == "02.08.01") {
|
||||
if (info.version == "02.08.04") {
|
||||
REQUIRE(info.is_latest);
|
||||
REQUIRE_FALSE(info.is_loaded);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#include <catch2/catch_all.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <slic3r/plugin/PluginFsUtils.hpp>
|
||||
#include <slic3r/plugin/PluginManager.hpp>
|
||||
#include <slic3r/plugin/PythonInterpreter.hpp>
|
||||
|
||||
#include "plugin_test_utils.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <utility>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <pybind11/embed.h>
|
||||
#include <pybind11/gil.h>
|
||||
#include <pybind11/pytypes.h>
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
namespace {
|
||||
// Brings the embedded interpreter up for one test and tears it down before boost::log does,
|
||||
// mirroring the ScopedPluginManager idiom in the other plugin tests.
|
||||
struct ScopedPluginManager
|
||||
{
|
||||
ScopedDataDir python_data_dir{"plugin-json-depth"};
|
||||
bool initialized = PluginManager::instance().initialize();
|
||||
~ScopedPluginManager()
|
||||
{
|
||||
PluginManager::instance().shutdown();
|
||||
PythonInterpreter::instance().shutdown();
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("py_to_json raises instead of overflowing on pathologically deep input", "[PluginHost][Python]")
|
||||
{
|
||||
ScopedPluginManager manager;
|
||||
REQUIRE(manager.initialized);
|
||||
namespace py = pybind11;
|
||||
py::gil_scoped_acquire gil;
|
||||
|
||||
// [[[ ... 0 ... ]]] nested 300 deep: past the 200 conversion-depth cap, but shallow enough
|
||||
// that the pre-fix code returns without crashing, so a regression fails cleanly rather than
|
||||
// taking the process down. Built in C++ so the test does not depend on Python builtins.
|
||||
py::object deep = py::int_(0);
|
||||
for (int i = 0; i < 300; ++i) {
|
||||
py::list wrapper;
|
||||
wrapper.append(deep);
|
||||
deep = std::move(wrapper);
|
||||
}
|
||||
CHECK_THROWS_AS(py_to_json(deep), std::exception);
|
||||
}
|
||||
|
||||
TEST_CASE("py_to_json still converts reasonably nested input", "[PluginHost][Python]")
|
||||
{
|
||||
ScopedPluginManager manager;
|
||||
REQUIRE(manager.initialized);
|
||||
namespace py = pybind11;
|
||||
py::gil_scoped_acquire gil;
|
||||
|
||||
py::dict d;
|
||||
d["a"] = py::int_(1);
|
||||
py::list inner;
|
||||
inner.append(py::str("x"));
|
||||
inner.append(py::int_(2));
|
||||
d["b"] = inner;
|
||||
|
||||
const nlohmann::json j = py_to_json(d);
|
||||
CHECK(j.at("a").get<std::int64_t>() == 1);
|
||||
CHECK(j.at("b").at(0).get<std::string>() == "x");
|
||||
CHECK(j.at("b").at(1).get<std::int64_t>() == 2);
|
||||
}
|
||||
Reference in New Issue
Block a user