Validate CLI config options against min/max

This commit is contained in:
Alessandro Ranellucci 2018-11-17 18:02:15 +01:00 committed by Joseph Lenox
parent bb29661a90
commit a1464a737e
6 changed files with 82 additions and 22 deletions

View File

@ -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."));
}

View File

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

View File

@ -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<ConfigOptionInt>(opt_key)->value;
if (value < def->min || value > def->max)
throw InvalidOptionException(opt_key);
} else if (def->type == coFloat) {
auto &value = this->opt<ConfigOptionFloat>(opt_key)->value;
if (value < def->min || value > def->max)
throw InvalidOptionException(opt_key);
} else if (def->type == coInts) {
for (auto &value : this->opt<ConfigOptionInts>(opt_key)->values)
if (value < def->min || value > def->max)
throw InvalidOptionException(opt_key);
} else if (def->type == coFloats) {
for (auto &value : this->opt<ConfigOptionFloats>(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);

View File

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

View File

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

View File

@ -6,6 +6,8 @@
#include <string>
#include <vector>
#include <memory>
#include <exception>
#include <stdexcept>
#include <boost/thread.hpp>
#include "BoundingBox.hpp"
#include "Flow.hpp"
@ -19,13 +21,11 @@
#include "LayerHeightSpline.hpp"
#include "SupportMaterial.hpp"
#include <exception>
#include <exception>
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;