ENH: validate the config from 3mf and give some hints when there are errors

Change-Id: Ic25e5426e4e85a35a6a2413109f47b653955ec78
This commit is contained in:
lane.wei 2023-02-13 19:15:28 +08:00 committed by Lane.Wei
parent 84eebfc729
commit 9dceb42ba3
7 changed files with 174 additions and 76 deletions

View File

@ -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] <<std::endl;
int debug_argc = 9;
int debug_argc = 5;
char *debug_argv[] = {
"E:\work\projects\bambu_release\bamboo_slicer\build_debug\src\Debug\bambu-studio.exe",
"--slice",
"0",
"--load-settings",
"machine.json;process.json",
"--load-filaments",
"filament.json",
"9",
//"--load-settings",
//"machine.json;process.json",
//"--load-filaments",
//"filament.json",
"--export-3mf=output.3mf",
"boat.stl"
"test_outside.3mf"
};
if (! this->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<std::string, std::string> 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<std::string, std::string>::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<std::string, std::string> 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<std::string, std::string>::iterator it=validity.begin(); it!=validity.end(); ++it)
boost::nowide::cerr << it->first <<": "<< it->second << std::endl;
return false;
}

View File

@ -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<std::string, std::string> 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<std::string, std::string>();
}
}
@ -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<std::string, std::string> validate(const FullPrintConfig &cfg, bool under_cli)
{
std::map<std::string, std::string> 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.

View File

@ -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<std::string, std::string> 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<std::string, std::string> validate(const FullPrintConfig &config, bool under_cli = false);
PRINT_CONFIG_CLASS_DEFINE(
SLAPrintConfig,

View File

@ -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

View File

@ -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<PopNotification> &notification : 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<PopNotification> &notification : m_pop_notifications) {
if (notification->get_type() == NotificationType::BBL3MFInfo) {
notification->reinit();
notification->update(data);
return;
}
}
auto notification = std::make_unique<NotificationManager::PopNotification>(data, m_id_provider, m_evt_handler);
push_notification_data(std::move(notification), 0);
}
void NotificationManager::bbl_close_plateinfo_notification()
{
for (std::unique_ptr<PopNotification> &notification : 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};

View File

@ -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();

View File

@ -3197,6 +3197,21 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
config.apply(static_cast<const ConfigBase &>(FullPrintConfig::defaults()));
// and place the loaded config over the base.
config += std::move(config_loaded);
std::map<std::string, std::string> validity = config.validate();
if (!validity.empty()) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("Param values in 3mf error: ");
for (std::map<std::string, std::string>::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<std::string, std::string>::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);