From 9dceb42ba343b1e937219478955774e4f90ccf10 Mon Sep 17 00:00:00 2001 From: "lane.wei" Date: Mon, 13 Feb 2023 19:15:28 +0800 Subject: [PATCH] ENH: validate the config from 3mf and give some hints when there are errors Change-Id: Ic25e5426e4e85a35a6a2413109f47b653955ec78 --- src/BambuStudio.cpp | 28 +++-- src/libslic3r/PrintConfig.cpp | 163 ++++++++++++++++--------- src/libslic3r/PrintConfig.hpp | 5 +- src/libslic3r/Utils.hpp | 1 + src/slic3r/GUI/NotificationManager.cpp | 30 ++++- src/slic3r/GUI/NotificationManager.hpp | 6 + src/slic3r/GUI/Plater.cpp | 17 +++ 7 files changed, 174 insertions(+), 76 deletions(-) diff --git a/src/BambuStudio.cpp b/src/BambuStudio.cpp index 5e61e50ac..167d77e3e 100644 --- a/src/BambuStudio.cpp +++ b/src/BambuStudio.cpp @@ -360,17 +360,17 @@ int CLI::run(int argc, char **argv) /*BOOST_LOG_TRIVIAL(info) << "begin to setup params, argc=" << argc << std::endl; for (int index=0; index < argc; index++) BOOST_LOG_TRIVIAL(info) << "index="<< index <<", arg is "<< argv[index] <setup(debug_argc, debug_argv))*/ if (!this->setup(argc, argv)) @@ -1259,10 +1259,12 @@ int CLI::run(int argc, char **argv) m_print_config.apply(sla_print_config, true);*/ } - std::string validity = m_print_config.validate(); + std::map validity = m_print_config.validate(true); if (!validity.empty()) { - boost::nowide::cerr <<"Error: The composite configation is not valid: " << validity << std::endl; - flush_and_exit(CLI_INVALID_PRINTER_TECH); + boost::nowide::cerr << "Param values in 3mf/config error: "<< std::endl; + for (std::map::iterator it=validity.begin(); it!=validity.end(); ++it) + boost::nowide::cerr << it->first <<": "<< it->second << std::endl; + flush_and_exit(CLI_INVALID_VALUES_IN_3MF); } //BBS: partplate list @@ -2405,7 +2407,7 @@ bool CLI::setup(int argc, char **argv) } //FIXME Validating at this stage most likely does not make sense, as the config is not fully initialized yet. - std::string validity = m_config.validate(); + std::map validity = m_config.validate(true); // Initialize with defaults. for (const t_optiondef_map *options : { &cli_actions_config_def.options, &cli_transform_config_def.options, &cli_misc_config_def.options }) @@ -2416,7 +2418,9 @@ bool CLI::setup(int argc, char **argv) //FIXME Validating at this stage most likely does not make sense, as the config is not fully initialized yet. if (!validity.empty()) { - boost::nowide::cerr << "error: " << validity << std::endl; + boost::nowide::cerr << "Params in command line error: "<< std::endl; + for (std::map::iterator it=validity.begin(); it!=validity.end(); ++it) + boost::nowide::cerr << it->first <<": "<< it->second << std::endl; return false; } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 4145c5af6..8d3afc05b 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3174,7 +3174,7 @@ void PrintConfigDef::init_filament_option_keys() "retraction_speed", "wipe", "wipe_distance", - "z_hop", + "z_hop", "z_hop_types" }; assert(std::is_sorted(m_filament_retract_keys.begin(), m_filament_retract_keys.end())); @@ -4181,7 +4181,8 @@ void DynamicPrintConfig::set_num_filaments(unsigned int num_filaments) } } -std::string DynamicPrintConfig::validate() +//BBS: pass map to recording all invalid valies +std::map DynamicPrintConfig::validate(bool under_cli) { // Full print config is initialized from the defaults. const ConfigOption *opt = this->option("printer_technology", false); @@ -4192,11 +4193,11 @@ std::string DynamicPrintConfig::validate() FullPrintConfig fpc; fpc.apply(*this, true); // Verify this print options through the FullPrintConfig. - return Slic3r::validate(fpc); + return Slic3r::validate(fpc, under_cli); } default: //FIXME no validation on SLA data? - return std::string(); + return std::map(); } } @@ -4267,80 +4268,104 @@ bool DynamicPrintConfig::is_custom_defined() return false; } +//BBS: pass map to recording all invalid valies //FIXME localize this function. -std::string validate(const FullPrintConfig &cfg) +std::map validate(const FullPrintConfig &cfg, bool under_cli) { + std::map error_message; // --layer-height - if (cfg.get_abs_value("layer_height") <= 0) - return "Invalid value for --layer-height"; - if (fabs(fmod(cfg.get_abs_value("layer_height"), SCALING_FACTOR)) > 1e-4) - return "--layer-height must be a multiple of print resolution"; + if (cfg.get_abs_value("layer_height") <= 0) { + error_message.emplace("layer_height", L("invalid value ") + std::to_string(cfg.get_abs_value("layer_height"))); + } + else if (fabs(fmod(cfg.get_abs_value("layer_height"), SCALING_FACTOR)) > 1e-4) { + error_message.emplace("layer_height", L("invalid value ") + std::to_string(cfg.get_abs_value("layer_height"))); + } // --first-layer-height - if (cfg.initial_layer_print_height.value <= 0) - return "Invalid value for --first-layer-height"; + if (cfg.initial_layer_print_height.value <= 0) { + error_message.emplace("initial_layer_print_height", L("invalid value ") + std::to_string(cfg.initial_layer_print_height.value)); + } // --filament-diameter for (double fd : cfg.filament_diameter.values) - if (fd < 1) - return "Invalid value for --filament-diameter"; + if (fd < 1) { + error_message.emplace("filament_diameter", L("invalid value ") + cfg.filament_diameter.serialize()); + break; + } // --nozzle-diameter for (double nd : cfg.nozzle_diameter.values) - if (nd < 0.005) - return "Invalid value for --nozzle-diameter"; + if (nd < 0.005) { + error_message.emplace("nozzle_diameter", L("invalid value ") + cfg.nozzle_diameter.serialize()); + break; + } // --perimeters - if (cfg.wall_loops.value < 0) - return "Invalid value for --wall_loops"; + if (cfg.wall_loops.value < 0) { + error_message.emplace("wall_loops", L("invalid value ") + std::to_string(cfg.wall_loops.value)); + } // --solid-layers - if (cfg.top_shell_layers < 0) - return "Invalid value for --top-solid-layers"; - if (cfg.bottom_shell_layers < 0) - return "Invalid value for --bottom-solid-layers"; + if (cfg.top_shell_layers < 0) { + error_message.emplace("top_shell_layers", L("invalid value ") + std::to_string(cfg.top_shell_layers)); + } + if (cfg.bottom_shell_layers < 0) { + error_message.emplace("bottom_shell_layers", L("invalid value ") + std::to_string(cfg.bottom_shell_layers)); + } // --gcode-flavor - if (! print_config_def.get("gcode_flavor")->has_enum_value(cfg.gcode_flavor.serialize())) - return "Invalid value for --gcode-flavor"; + if (! print_config_def.get("gcode_flavor")->has_enum_value(cfg.gcode_flavor.serialize())) { + error_message.emplace("gcode_flavor", L("invalid value ") + cfg.gcode_flavor.serialize()); + } // --fill-pattern - if (! print_config_def.get("sparse_infill_pattern")->has_enum_value(cfg.sparse_infill_pattern.serialize())) - return "Invalid value for --fill-pattern"; + if (! print_config_def.get("sparse_infill_pattern")->has_enum_value(cfg.sparse_infill_pattern.serialize())) { + error_message.emplace("sparse_infill_pattern", L("invalid value ") + cfg.sparse_infill_pattern.serialize()); + } // --top-fill-pattern - if (! print_config_def.get("top_surface_pattern")->has_enum_value(cfg.top_surface_pattern.serialize())) - return "Invalid value for --top-fill-pattern"; + if (! print_config_def.get("top_surface_pattern")->has_enum_value(cfg.top_surface_pattern.serialize())) { + error_message.emplace("top_surface_pattern", L("invalid value ") + cfg.top_surface_pattern.serialize()); + } // --bottom-fill-pattern - if (! print_config_def.get("bottom_surface_pattern")->has_enum_value(cfg.bottom_surface_pattern.serialize())) - return "Invalid value for --bottom-fill-pattern"; + if (! print_config_def.get("bottom_surface_pattern")->has_enum_value(cfg.bottom_surface_pattern.serialize())) { + error_message.emplace("bottom_surface_pattern", L("invalid value ") + cfg.bottom_surface_pattern.serialize()); + } // --fill-density if (fabs(cfg.sparse_infill_density.value - 100.) < EPSILON && - ! print_config_def.get("top_surface_pattern")->has_enum_value(cfg.sparse_infill_pattern.serialize())) - return "The selected fill pattern is not supposed to work at 100% density"; + ! print_config_def.get("top_surface_pattern")->has_enum_value(cfg.sparse_infill_pattern.serialize())) { + error_message.emplace("sparse_infill_pattern", cfg.sparse_infill_pattern.serialize() + L(" doesn't work at 100%% density ")); + } // --skirt-height - if (cfg.skirt_height < 0) - return "Invalid value for --skirt-height"; + if (cfg.skirt_height < 0) { + error_message.emplace("skirt_height", L("invalid value ") + std::to_string(cfg.skirt_height)); + } // --bridge-flow-ratio - if (cfg.bridge_flow <= 0) - return "Invalid value for --bridge-flow-ratio"; + if (cfg.bridge_flow <= 0) { + error_message.emplace("bridge_flow", L("invalid value ") + std::to_string(cfg.bridge_flow)); + } // extruder clearance - if (cfg.extruder_clearance_radius <= 0) - return "Invalid value for --extruder-clearance-radius"; - if (cfg.extruder_clearance_height_to_rod <= 0) - return "Invalid value for --extruder-clearance-height-to-rod"; - if (cfg.extruder_clearance_height_to_lid <= 0) - return "Invalid value for --extruder-clearance-height-to-lid"; + if (cfg.extruder_clearance_radius <= 0) { + error_message.emplace("extruder_clearance_radius", L("invalid value ") + std::to_string(cfg.extruder_clearance_radius)); + } + if (cfg.extruder_clearance_height_to_rod <= 0) { + error_message.emplace("extruder_clearance_height_to_rod", L("invalid value ") + std::to_string(cfg.extruder_clearance_height_to_rod)); + } + if (cfg.extruder_clearance_height_to_lid <= 0) { + error_message.emplace("extruder_clearance_height_to_lid", L("invalid value ") + std::to_string(cfg.extruder_clearance_height_to_lid)); + } // --extrusion-multiplier for (double em : cfg.filament_flow_ratio.values) - if (em <= 0) - return "Invalid value for --filament-flow-ratio"; + if (em <= 0) { + error_message.emplace("filament_flow_ratio", L("invalid value ") + cfg.filament_flow_ratio.serialize()); + break; + } // The following test was commented out after 482841b, see also https://github.com/prusa3d/PrusaSlicer/pull/6743. // The backend should now handle this case correctly. I.e., zero default_acceleration behaves as if all others @@ -4354,19 +4379,34 @@ std::string validate(const FullPrintConfig &cfg) // return "Invalid zero value for --default-acceleration when using other acceleration settings"; // --spiral-vase - if (cfg.spiral_mode) { + //for non-cli case, we will popup dialog for spiral mode correction + if (cfg.spiral_mode && under_cli) { // Note that we might want to have more than one perimeter on the bottom // solid layers. - if (cfg.wall_loops > 1) - return "Can't make more than one perimeter when spiral vase mode is enabled"; - else if (cfg.wall_loops < 1) - return "Can't make less than one perimeter when spiral vase mode is enabled"; - if (cfg.sparse_infill_density > 0) - return "Spiral vase mode can only print hollow objects, so you need to set Fill density to 0"; - if (cfg.top_shell_layers > 0) - return "Spiral vase mode is not compatible with top solid layers"; - if (cfg.enable_support || cfg.enforce_support_layers > 0) - return "Spiral vase mode is not compatible with support"; + if (cfg.wall_loops != 1) { + error_message.emplace("wall_loops", L("Invalid value when spiral vase mode is enabled: ") + std::to_string(cfg.wall_loops)); + //return "Can't make more than one perimeter when spiral vase mode is enabled"; + //return "Can't make less than one perimeter when spiral vase mode is enabled"; + } + + if (cfg.sparse_infill_density > 0) { + error_message.emplace("sparse_infill_density", L("Invalid value when spiral vase mode is enabled: ") + std::to_string(cfg.sparse_infill_density)); + //return "Spiral vase mode can only print hollow objects, so you need to set Fill density to 0"; + } + + if (cfg.top_shell_layers > 0) { + error_message.emplace("top_shell_layers", L("Invalid value when spiral vase mode is enabled: ") + std::to_string(cfg.top_shell_layers)); + //return "Spiral vase mode is not compatible with top solid layers"; + } + + if (cfg.enable_support ) { + error_message.emplace("enable_support", L("Invalid value when spiral vase mode is enabled: ") + std::to_string(cfg.enable_support)); + //return "Spiral vase mode is not compatible with support"; + } + if (cfg.enforce_support_layers > 0) { + error_message.emplace("enforce_support_layers", L("Invalid value when spiral vase mode is enabled: ") + std::to_string(cfg.enforce_support_layers)); + //return "Spiral vase mode is not compatible with support"; + } } // extrusion widths @@ -4384,8 +4424,10 @@ std::string validate(const FullPrintConfig &cfg) "initial_layer_line_width" }; for (size_t i = 0; i < sizeof(widths) / sizeof(widths[i]); ++ i) { std::string key(widths[i]); - if (cfg.get_abs_value(key) > 2.5 * max_nozzle_diameter) - return std::string("Too Large line width: ") + key; + if (cfg.get_abs_value(key) > 2.5 * max_nozzle_diameter) { + error_message.emplace(key, L("too large line width ") + std::to_string(cfg.get_abs_value(key))); + //return std::string("Too Large line width: ") + key; + } } } @@ -4428,12 +4470,15 @@ std::string validate(const FullPrintConfig &cfg) break; default:; } - if (out_of_range) - return std::string("Value out of range: " + opt_key); + if (out_of_range) { + if (error_message.find(opt_key) == error_message.end()) + error_message.emplace(opt_key, opt->serialize() + L(" not in range ") +"[" + std::to_string(optdef->min) + "," + std::to_string(optdef->max) + "]"); + //return std::string("Value out of range: " + opt_key); + } } // The configuration is valid. - return ""; + return error_message; } // Declare and initialize static caches of StaticPrintConfig derived classes. diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 8708e1c67..ac02b869d 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -378,8 +378,9 @@ public: // BBS void set_num_filaments(unsigned int num_filaments); + //BBS // Validate the PrintConfig. Returns an empty string on success, otherwise an error message is returned. - std::string validate(); + std::map validate(bool under_cli = false); // Verify whether the opt_key has not been obsoleted or renamed. // Both opt_key and value may be modified by handle_legacy(). @@ -953,7 +954,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE0( ) // Validate the FullPrintConfig. Returns an empty string on success, otherwise an error message is returned. -std::string validate(const FullPrintConfig &config); +std::map validate(const FullPrintConfig &config, bool under_cli = false); PRINT_CONFIG_CLASS_DEFINE( SLAPrintConfig, diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 2bbb075d8..fcb818daa 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -34,6 +34,7 @@ #define CLI_3MF_NOT_SUPPORT_MACHINE_CHANGE -15 #define CLI_3MF_NEW_MACHINE_NOT_SUPPORTED -16 #define CLI_PROCESS_NOT_COMPATIBLE -17 +#define CLI_INVALID_VALUES_IN_3MF -18 #define CLI_NO_SUITABLE_OBJECTS -50 diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 8bd36eb1a..cf1a89cdc 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1565,8 +1565,8 @@ NotificationManager::NotificationManager(wxEvtHandler* evt_handler) : void NotificationManager::on_change_color_mode(bool is_dark) { m_is_dark = is_dark; - for (std::unique_ptr& notification : m_pop_notifications){ - notification->on_change_color_mode(is_dark); + for (std::unique_ptr& notification : m_pop_notifications){ + notification->on_change_color_mode(is_dark); } } @@ -2338,7 +2338,6 @@ size_t NotificationManager::get_notification_count() const return ret; } - void NotificationManager::bbl_show_plateinfo_notification(const std::string &text) { NotificationData data{NotificationType::BBLPlateInfo, NotificationLevel::PrintInfoNotificationLevel, BBL_NOTICE_MAX_INTERVAL, text}; @@ -2355,6 +2354,30 @@ void NotificationManager::bbl_show_plateinfo_notification(const std::string &tex push_notification_data(std::move(notification), 0); } +void NotificationManager::bbl_close_3mf_warn_notification() +{ + for (std::unique_ptr ¬ification : m_pop_notifications) + if (notification->get_type() == NotificationType::BBL3MFInfo) { + notification->close(); + } +} + +void NotificationManager::bbl_show_3mf_warn_notification(const std::string &text) +{ + NotificationData data{NotificationType::BBL3MFInfo, NotificationLevel::ErrorNotificationLevel, BBL_NOTICE_MAX_INTERVAL, text}; + + for (std::unique_ptr ¬ification : m_pop_notifications) { + if (notification->get_type() == NotificationType::BBL3MFInfo) { + notification->reinit(); + notification->update(data); + return; + } + } + + auto notification = std::make_unique(data, m_id_provider, m_evt_handler); + push_notification_data(std::move(notification), 0); +} + void NotificationManager::bbl_close_plateinfo_notification() { for (std::unique_ptr ¬ification : m_pop_notifications) @@ -2363,6 +2386,7 @@ void NotificationManager::bbl_close_plateinfo_notification() } } + void NotificationManager::bbl_show_preview_only_notification(const std::string &text) { NotificationData data{NotificationType::BBLPreviewOnlyMode, NotificationLevel::WarningNotificationLevel, 0, text}; diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 0070d12bb..9016a3df2 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -123,6 +123,8 @@ enum class NotificationType ArrangeOngoing, // BBL: Plate Info ,Design For @YangLeDuo BBLPlateInfo, + // BBL: 3MF warnings + BBL3MFInfo, // BBL: Some Objects Info, Design For @YangLeDuo BBLObjectInfo, // BBL: Objects have empty layer when Slicing @@ -282,6 +284,10 @@ public: void bbl_show_plateinfo_notification(const std::string &text); void bbl_close_plateinfo_notification(); + //BBS-- 3mf warning + void bbl_show_3mf_warn_notification(const std::string &text); + void bbl_close_3mf_warn_notification(); + //BBS--preview only mode void bbl_show_preview_only_notification(const std::string &text); void bbl_close_preview_only_notification(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 445545341..89f57f453 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3197,6 +3197,21 @@ std::vector Plater::priv::load_files(const std::vector& input_ config.apply(static_cast(FullPrintConfig::defaults())); // and place the loaded config over the base. config += std::move(config_loaded); + std::map validity = config.validate(); + if (!validity.empty()) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("Param values in 3mf error: "); + for (std::map::iterator it=validity.begin(); it!=validity.end(); ++it) + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("%1%: %2%")%it->first %it->second; + // + NotificationManager *notify_manager = q->get_notification_manager(); + std::string error_message = L("Invalid values found in the 3mf:"); + error_message += "\n"; + for (std::map::iterator it=validity.begin(); it!=validity.end(); ++it) + error_message += "-" + it->first + ": " + it->second + "\n"; + error_message += "\n"; + error_message += L("Please correct them in the param tabs"); + notify_manager->bbl_show_3mf_warn_notification(error_message); + } } if (!config_substitutions.empty()) show_substitutions_info(config_substitutions.substitutions, filename.string()); @@ -7453,6 +7468,7 @@ int Plater::new_project(bool skip_confirm, bool silent) m_loading_project = false; get_notification_manager()->bbl_close_plateinfo_notification(); get_notification_manager()->bbl_close_preview_only_notification(); + get_notification_manager()->bbl_close_3mf_warn_notification(); if (!silent) wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor); @@ -7536,6 +7552,7 @@ void Plater::load_project(wxString const& filename2, m_exported_file = false; get_notification_manager()->bbl_close_plateinfo_notification(); get_notification_manager()->bbl_close_preview_only_notification(); + get_notification_manager()->bbl_close_3mf_warn_notification(); auto path = into_path(filename);