From a1464a737ed3e7f6ecf0c6060c978efc6f70ff9b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Nov 2018 18:02:15 +0100 Subject: [PATCH] Validate CLI config options against min/max --- src/GUI/Plater.cpp | 4 ++-- src/slic3r.cpp | 16 +++++++++++++++- xs/src/libslic3r/ConfigBase.cpp | 33 +++++++++++++++++++++++++++++++-- xs/src/libslic3r/ConfigBase.hpp | 21 ++++++++++++++++++++- xs/src/libslic3r/Print.cpp | 16 +++++++--------- xs/src/libslic3r/Print.hpp | 14 +++++++------- 6 files changed, 82 insertions(+), 22 deletions(-) diff --git a/src/GUI/Plater.cpp b/src/GUI/Plater.cpp index 149ba0c16..10383c2e5 100644 --- a/src/GUI/Plater.cpp +++ b/src/GUI/Plater.cpp @@ -718,12 +718,12 @@ void Plater::remove(int obj_idx, bool dont_push) { Slic3r::Log::info(LogChannel, "Assigned obj_ref"); try { this->model->delete_object(obj_ref->identifier); - } catch (InvalidObjectException& ex) { + } catch (out_of_range &ex) { Slic3r::Log::error(LogChannel, LOG_WSTRING("Failed to delete object " << obj_ref->identifier << " from Model.")); } try { this->print->delete_object(obj_ref->identifier); - } catch (InvalidObjectException& ex) { + } catch (out_of_range &ex) { Slic3r::Log::error(LogChannel, LOG_WSTRING("Failed to delete object " << obj_ref->identifier << " from Print.")); } diff --git a/src/slic3r.cpp b/src/slic3r.cpp index 7533445e5..9cde583f3 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -86,6 +86,14 @@ int CLI::run(int argc, char **argv) { // create a static (full) print config to be used in our logic this->full_print_config.apply(this->print_config); + // validate config + try { + this->full_print_config.validate(); + } catch (InvalidOptionException &e) { + boost::nowide::cerr << e.what() << std::endl; + return 1; + } + // read input file(s) if any for (auto const &file : input_files) { Model model; @@ -271,6 +279,7 @@ int CLI::run(int argc, char **argv) { print.status_cb = [](int ln, const std::string& msg) { boost::nowide::cout << msg << std::endl; }; + print.apply_config(this->print_config); print.center = !this->config.has("center") && !this->config.has("align_xy") && !this->config.getBool("dont_arrange"); @@ -282,7 +291,12 @@ int CLI::run(int argc, char **argv) { std::chrono::time_point t0{ clock_::now() }; const std::string outfile = this->output_filepath(model, IO::Gcode); - print.export_gcode(outfile); + try { + print.export_gcode(outfile); + } catch (InvalidPrintException &e) { + boost::nowide::cerr << e.what() << std::endl; + return 1; + } boost::nowide::cout << "G-code exported to " << outfile << std::endl; // output some statistics diff --git a/xs/src/libslic3r/ConfigBase.cpp b/xs/src/libslic3r/ConfigBase.cpp index b63f8aab6..da248a3fb 100644 --- a/xs/src/libslic3r/ConfigBase.cpp +++ b/xs/src/libslic3r/ConfigBase.cpp @@ -265,7 +265,7 @@ ConfigBase::apply_only(const ConfigBase &other, const t_config_option_keys &opt_ ConfigOption* my_opt = this->option(opt_key, true); if (opt_key.size() == 0) continue; if (my_opt == NULL) { - if (ignore_nonexistent == false) throw UnknownOptionException(); + if (ignore_nonexistent == false) throw UnknownOptionException(opt_key); continue; } if (default_nonexistent && !other.has(opt_key)) { @@ -328,7 +328,7 @@ ConfigBase::set_deserialize(t_config_option_key opt_key, std::string str, bool a if (optdef != NULL) break; } if (optdef == NULL) - throw UnknownOptionException(); + throw UnknownOptionException(opt_key); } if (!optdef->shortcut.empty()) { @@ -472,6 +472,35 @@ ConfigBase::save(const std::string &file) const c.close(); } +void +ConfigBase::validate() const +{ + for (auto &opt_key : this->keys()) { + // get option definition + const ConfigOptionDef* def = this->def->get(opt_key); + assert(def != nullptr); + + if (def->type == coInt) { + auto &value = this->opt(opt_key)->value; + if (value < def->min || value > def->max) + throw InvalidOptionException(opt_key); + } else if (def->type == coFloat) { + auto &value = this->opt(opt_key)->value; + if (value < def->min || value > def->max) + throw InvalidOptionException(opt_key); + } else if (def->type == coInts) { + for (auto &value : this->opt(opt_key)->values) + if (value < def->min || value > def->max) + throw InvalidOptionException(opt_key); + } else if (def->type == coFloats) { + for (auto &value : this->opt(opt_key)->values) + if (value < def->min || value > def->max) + throw InvalidOptionException(opt_key); + } + // TODO: validate coFloatOrPercent (semantics of min/max are ambiguous for it) + } +} + DynamicConfig& DynamicConfig::operator= (DynamicConfig other) { this->swap(other); diff --git a/xs/src/libslic3r/ConfigBase.hpp b/xs/src/libslic3r/ConfigBase.hpp index e8e9caeeb..a784640bf 100644 --- a/xs/src/libslic3r/ConfigBase.hpp +++ b/xs/src/libslic3r/ConfigBase.hpp @@ -732,6 +732,7 @@ class ConfigBase void setenv_(); void load(const std::string &file); void save(const std::string &file) const; + void validate() const; }; /// Configuration store with dynamic number of configuration values. @@ -775,7 +776,25 @@ class StaticConfig : public virtual ConfigBase }; /// Specialization of std::exception to indicate that an unknown config option has been encountered. -class UnknownOptionException : public std::exception {}; +class ConfigOptionException : public std::exception { + public: + t_config_option_key opt_key; + ConfigOptionException(t_config_option_key _opt_key) + : opt_key(_opt_key) {}; +}; +class UnknownOptionException : public ConfigOptionException { + using ConfigOptionException::ConfigOptionException; +}; +class InvalidOptionException : public ConfigOptionException { + using ConfigOptionException::ConfigOptionException; + + public: + virtual const char* what() const noexcept { + std::string s("Invalid value for option: "); + s += this->opt_key; + return s.c_str(); + } +}; } diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index b40f02e53..c6769c2cc 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -80,7 +80,7 @@ Print::delete_object(size_t idx) { PrintObjectPtrs::iterator i = this->objects.begin() + idx; if (i >= this->objects.end()) - throw InvalidObjectException(); + throw std::out_of_range("Object not found"); // before deleting object, invalidate all of its steps in order to // invalidate all of the dependent ones in Print @@ -858,7 +858,7 @@ bool Print::has_skirt() const || this->has_infinite_skirt(); } -std::string +void Print::validate() const { if (this->config.complete_objects) { @@ -896,7 +896,7 @@ Print::validate() const Polygon p = convex_hull; p.translate(*copy); if (!intersection(a, p).empty()) - return "Some objects are too close; your extruder will collide with them."; + throw InvalidPrintException{"Some objects are too close; your extruder will collide with them."}; a = union_(a, p); } @@ -915,7 +915,7 @@ Print::validate() const // it will be printed as last one so its height doesn't matter object_height.pop_back(); if (!object_height.empty() && object_height.back() > scale_(this->config.extruder_clearance_height.value)) - return "Some objects are too tall and cannot be printed without extruder collisions."; + throw InvalidPrintException{"Some objects are too tall and cannot be printed without extruder collisions."}; } } // end if (this->config.complete_objects) @@ -923,15 +923,13 @@ Print::validate() const size_t total_copies_count = 0; FOREACH_OBJECT(this, i_object) total_copies_count += (*i_object)->copies().size(); if (total_copies_count > 1 && !this->config.complete_objects.getBool()) - return "The Spiral Vase option can only be used when printing a single object."; + throw InvalidPrintException{"The Spiral Vase option can only be used when printing a single object."}; if (this->regions.size() > 1) - return "The Spiral Vase option can only be used when printing single material objects."; + throw InvalidPrintException{"The Spiral Vase option can only be used when printing single material objects."}; } if (this->extruders().empty()) - return "The supplied settings will cause an empty print."; - - return std::string(); + throw InvalidPrintException{"The supplied settings will cause an empty print."}; } // the bounding box of objects placed in copies position diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index accb67d35..98ce0816e 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include "BoundingBox.hpp" #include "Flow.hpp" @@ -19,13 +21,11 @@ #include "LayerHeightSpline.hpp" #include "SupportMaterial.hpp" -#include - -#include - namespace Slic3r { -class InvalidObjectException : public std::exception {}; +class InvalidPrintException : public std::runtime_error { + using std::runtime_error::runtime_error; +}; class Print; class PrintObject; @@ -292,8 +292,8 @@ class Print bool apply_config(DynamicPrintConfig config); bool has_infinite_skirt() const; bool has_skirt() const; - // Returns an empty string if valid, otherwise returns an error message. - std::string validate() const; + // Throws exceptions if print is not valid. + void validate() const; BoundingBox bounding_box() const; BoundingBox total_bounding_box() const; double skirt_first_layer_height() const;