Ported config load / stored to C++, thanks @alexrj

Implemented import of config values from the G-code.
This commit is contained in:
bubnikv 2017-06-14 17:51:14 +02:00
parent c61e098066
commit 93dce7a2d3
7 changed files with 192 additions and 35 deletions

View File

@ -101,8 +101,14 @@ sub load {
my $class = shift; my $class = shift;
my ($file) = @_; my ($file) = @_;
my $ini = __PACKAGE__->read_ini($file); if ($file =~ /\.gcode$/i || $file =~ /\.g$/i) {
return $class->load_ini_hash($ini->{_}); my $config = $class->new;
$config->_load_from_gcode($file);
return $config;
} else {
my $ini = __PACKAGE__->read_ini($file);
return $class->load_ini_hash($ini->{_});
}
} }
# Deserialize a perl hash into the underlying C++ Slic3r::DynamicConfig class, # Deserialize a perl hash into the underlying C++ Slic3r::DynamicConfig class,

View File

@ -543,7 +543,7 @@ sub load_config_file {
return unless $self->check_unsaved_changes; return unless $self->check_unsaved_changes;
my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || ''; my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || '';
my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini", my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini",
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST); 'INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g', wxFD_OPEN | wxFD_FILE_MUST_EXIST);
return unless $dlg->ShowModal == wxID_OK; return unless $dlg->ShowModal == wxID_OK;
$file = Slic3r::decode_path($dlg->GetPaths); $file = Slic3r::decode_path($dlg->GetPaths);
$dlg->Destroy; $dlg->Destroy;

View File

@ -1,6 +1,22 @@
#include "Config.hpp" #include "Config.hpp"
#include <stdlib.h> // for setenv()
#include <assert.h> #include <assert.h>
#include <ctime>
#include <fstream>
#include <iostream>
#include <exception> // std::runtime_error
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/erase.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/config.hpp>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/nowide/cenv.hpp>
#include <boost/nowide/fstream.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <string.h> #include <string.h>
#if defined(_WIN32) && !defined(setenv) && defined(_putenv_s) #if defined(_WIN32) && !defined(setenv) && defined(_putenv_s)
@ -94,7 +110,6 @@ bool unescape_string_cstyle(const std::string &str, std::string &str_out)
bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &out) bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &out)
{ {
out.clear();
if (str.empty()) if (str.empty())
return true; return true;
@ -195,19 +210,36 @@ std::string ConfigBase::serialize(const t_config_option_key &opt_key) const
return opt->serialize(); return opt->serialize();
} }
bool ConfigBase::set_deserialize(const t_config_option_key &opt_key, std::string str) bool ConfigBase::set_deserialize(t_config_option_key opt_key, const std::string &str, bool append)
{ {
const ConfigOptionDef* optdef = this->def->get(opt_key); const ConfigOptionDef* optdef = this->def->get(opt_key);
if (optdef == NULL) throw "Calling set_deserialize() on unknown option"; if (optdef == nullptr) {
if (!optdef->shortcut.empty()) { // If we didn't find an option, look for any other option having this as an alias.
for (std::vector<t_config_option_key>::const_iterator it = optdef->shortcut.begin(); it != optdef->shortcut.end(); ++it) { for (const auto &opt : this->def->options) {
if (!this->set_deserialize(*it, str)) return false; for (const t_config_option_key &opt_key2 : opt.second.aliases) {
if (opt_key2 == opt_key) {
opt_key = opt_key2;
optdef = &opt.second;
break;
}
}
if (optdef != nullptr)
break;
} }
if (optdef == nullptr)
throw UnknownOptionException();
}
if (! optdef->shortcut.empty()) {
for (const t_config_option_key &shortcut : optdef->shortcut)
if (! this->set_deserialize(shortcut, str))
return false;
return true; return true;
} }
ConfigOption* opt = this->option(opt_key, true);
ConfigOption *opt = this->option(opt_key, true);
assert(opt != nullptr); assert(opt != nullptr);
return opt->deserialize(str); return opt->deserialize(str, append);
} }
// Return an absolute value of a possibly relative config variable. // Return an absolute value of a possibly relative config variable.
@ -259,6 +291,90 @@ void ConfigBase::setenv_()
#endif #endif
} }
void ConfigBase::load(const std::string &file)
{
namespace pt = boost::property_tree;
pt::ptree tree;
boost::nowide::ifstream ifs(file);
pt::read_ini(ifs, tree);
for (const pt::ptree::value_type &v : tree) {
try {
t_config_option_key opt_key = v.first;
std::string value = v.second.get_value<std::string>();
this->set_deserialize(opt_key, value);
} catch (UnknownOptionException & /* e */) {
// ignore
}
}
}
// Load the config keys from the tail of a G-code.
void ConfigBase::load_from_gcode(const std::string &file)
{
// 1) Read a 64k block from the end of the G-code.
boost::nowide::ifstream ifs(file);
ifs.seekg(0, ifs.end);
auto length = std::min<std::fstream::streampos>(65535, ifs.tellg());
ifs.seekg(std::min(length, length), ifs.end);
std::vector<char> data(size_t(length) + 1, 0);
ifs.read(data.data(), length);
ifs.close();
// 2) Walk line by line in reverse until a non-configuration key appears.
char *data_start = data.data();
char *end = data_start + length;
for (;;) {
// Extract next line.
for (-- end; end > data_start && (*end == '\r' || *end == '\n'); -- end);
if (end == data_start)
break;
char *start = end;
*(++ end) = 0;
for (-- start; start > data_start && *start != '\r' && *start != '\n'; -- start);
if (start == data_start)
break;
// Extracted a line from start to end. Extract the key = value pair.
if (end - start < 10 || start[0] != ';' || start[1] != ' ' || (start[2] == ' ' || start[2] == '\t'))
break;
char *key = start + 2;
char *sep = strchr(key, '=');
if (sep == nullptr)
break;
char *value = sep + 2;
if (value >= end)
break;
char *key_end = sep - 1;
if (key_end - key < 3)
break;
*key_end = 0;
try {
this->set_deserialize(key, value);
} catch (UnknownOptionException & /* e */) {
// ignore
}
}
}
void ConfigBase::save(const std::string &file) const
{
using namespace std;
boost::nowide::ofstream c;
c.open(file, ios::out | ios::trunc);
{
time_t now;
time(&now);
char buf[sizeof "0000-00-00 00:00:00"];
strftime(buf, sizeof buf, "%F %T", gmtime(&now));
c << "# generated by Slic3r " << SLIC3R_VERSION << " on " << buf << endl;
}
t_config_option_keys my_keys = this->keys();
for (t_config_option_keys::const_iterator opt_key = my_keys.begin(); opt_key != my_keys.end(); ++opt_key)
c << *opt_key << " = " << this->serialize(*opt_key) << endl;
c.close();
}
ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool create) { ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool create) {
t_options_map::iterator it = options.find(opt_key); t_options_map::iterator it = options.find(opt_key);
if (it == options.end()) { if (it == options.end()) {

View File

@ -29,7 +29,7 @@ class ConfigOption {
public: public:
virtual ~ConfigOption() {}; virtual ~ConfigOption() {};
virtual std::string serialize() const = 0; virtual std::string serialize() const = 0;
virtual bool deserialize(std::string str) = 0; virtual bool deserialize(const std::string &str, bool append = false) = 0;
virtual void set(const ConfigOption &option) = 0; virtual void set(const ConfigOption &option) = 0;
virtual int getInt() const { return 0; }; virtual int getInt() const { return 0; };
virtual double getFloat() const { return 0; }; virtual double getFloat() const { return 0; };
@ -94,7 +94,7 @@ public:
return ss.str(); return ss.str();
}; };
bool deserialize(std::string str) { bool deserialize(const std::string &str, bool append = false) {
std::istringstream iss(str); std::istringstream iss(str);
iss >> this->value; iss >> this->value;
return !iss.fail(); return !iss.fail();
@ -124,8 +124,9 @@ public:
return vv; return vv;
}; };
bool deserialize(std::string str) { bool deserialize(const std::string &str, bool append = false) {
this->values.clear(); if (! append)
this->values.clear();
std::istringstream is(str); std::istringstream is(str);
std::string item_str; std::string item_str;
while (std::getline(is, item_str, ',')) { while (std::getline(is, item_str, ',')) {
@ -153,7 +154,7 @@ public:
return ss.str(); return ss.str();
}; };
bool deserialize(std::string str) { bool deserialize(const std::string &str, bool append = false) {
std::istringstream iss(str); std::istringstream iss(str);
iss >> this->value; iss >> this->value;
return !iss.fail(); return !iss.fail();
@ -183,8 +184,9 @@ public:
return vv; return vv;
}; };
bool deserialize(std::string str) { bool deserialize(const std::string &str, bool append = false) {
this->values.clear(); if (! append)
this->values.clear();
std::istringstream is(str); std::istringstream is(str);
std::string item_str; std::string item_str;
while (std::getline(is, item_str, ',')) { while (std::getline(is, item_str, ',')) {
@ -207,7 +209,7 @@ public:
return escape_string_cstyle(this->value); return escape_string_cstyle(this->value);
} }
bool deserialize(std::string str) { bool deserialize(const std::string &str, bool append = false) {
return unescape_string_cstyle(str, this->value); return unescape_string_cstyle(str, this->value);
}; };
}; };
@ -224,7 +226,9 @@ public:
return this->values; return this->values;
}; };
bool deserialize(std::string str) { bool deserialize(const std::string &str, bool append = false) {
if (! append)
this->values.clear();
return unescape_strings_cstyle(str, this->values); return unescape_strings_cstyle(str, this->values);
}; };
}; };
@ -247,7 +251,7 @@ public:
return s; return s;
}; };
bool deserialize(std::string str) { bool deserialize(const std::string &str, bool append = false) {
// don't try to parse the trailing % since it's optional // don't try to parse the trailing % since it's optional
std::istringstream iss(str); std::istringstream iss(str);
iss >> this->value; iss >> this->value;
@ -280,8 +284,9 @@ public:
return vv; return vv;
}; };
bool deserialize(std::string str) { bool deserialize(const std::string &str, bool append = false) {
this->values.clear(); if (! append)
this->values.clear();
std::istringstream is(str); std::istringstream is(str);
std::string item_str; std::string item_str;
while (std::getline(is, item_str, ',')) { while (std::getline(is, item_str, ',')) {
@ -327,7 +332,7 @@ public:
return s; return s;
}; };
bool deserialize(std::string str) { bool deserialize(const std::string &str, bool append = false) {
this->percent = str.find_first_of("%") != std::string::npos; this->percent = str.find_first_of("%") != std::string::npos;
std::istringstream iss(str); std::istringstream iss(str);
iss >> this->value; iss >> this->value;
@ -349,7 +354,7 @@ public:
return ss.str(); return ss.str();
}; };
bool deserialize(std::string str) { bool deserialize(const std::string &str, bool append = false) {
std::istringstream iss(str); std::istringstream iss(str);
iss >> this->value.x; iss >> this->value.x;
iss.ignore(std::numeric_limits<std::streamsize>::max(), ','); iss.ignore(std::numeric_limits<std::streamsize>::max(), ',');
@ -383,8 +388,9 @@ public:
return vv; return vv;
}; };
bool deserialize(std::string str) { bool deserialize(const std::string &str, bool append = false) {
this->values.clear(); if (! append)
this->values.clear();
std::istringstream is(str); std::istringstream is(str);
std::string point_str; std::string point_str;
while (std::getline(is, point_str, ',')) { while (std::getline(is, point_str, ',')) {
@ -415,7 +421,7 @@ public:
return std::string(this->value ? "1" : "0"); return std::string(this->value ? "1" : "0");
}; };
bool deserialize(std::string str) { bool deserialize(const std::string &str, bool append = false) {
this->value = (str.compare("1") == 0); this->value = (str.compare("1") == 0);
return true; return true;
}; };
@ -443,8 +449,9 @@ public:
return vv; return vv;
}; };
bool deserialize(std::string str) { bool deserialize(const std::string &str, bool append = false) {
this->values.clear(); if (! append)
this->values.clear();
std::istringstream is(str); std::istringstream is(str);
std::string item_str; std::string item_str;
while (std::getline(is, item_str, ',')) { while (std::getline(is, item_str, ',')) {
@ -473,7 +480,7 @@ public:
return ""; return "";
}; };
bool deserialize(std::string str) { bool deserialize(const std::string &str, bool append = false) {
t_config_enum_values enum_keys_map = ConfigOptionEnum<T>::get_enum_values(); t_config_enum_values enum_keys_map = ConfigOptionEnum<T>::get_enum_values();
if (enum_keys_map.count(str) == 0) return false; if (enum_keys_map.count(str) == 0) return false;
this->value = static_cast<T>(enum_keys_map[str]); this->value = static_cast<T>(enum_keys_map[str]);
@ -500,7 +507,7 @@ public:
return ""; return "";
}; };
bool deserialize(std::string str) { bool deserialize(const std::string &str, bool append = false) {
if (this->keys_map->count(str) == 0) return false; if (this->keys_map->count(str) == 0) return false;
this->value = (*const_cast<t_config_enum_values*>(this->keys_map))[str]; this->value = (*const_cast<t_config_enum_values*>(this->keys_map))[str];
return true; return true;
@ -660,11 +667,14 @@ public:
bool equals(const ConfigBase &other) const { return this->diff(other).empty(); } bool equals(const ConfigBase &other) const { return this->diff(other).empty(); }
t_config_option_keys diff(const ConfigBase &other) const; t_config_option_keys diff(const ConfigBase &other) const;
std::string serialize(const t_config_option_key &opt_key) const; std::string serialize(const t_config_option_key &opt_key) const;
bool set_deserialize(const t_config_option_key &opt_key, std::string str); bool set_deserialize(t_config_option_key opt_key, const std::string &str, bool append = false);
double get_abs_value(const t_config_option_key &opt_key) const; double get_abs_value(const t_config_option_key &opt_key) const;
double get_abs_value(const t_config_option_key &opt_key, double ratio_over) const; double get_abs_value(const t_config_option_key &opt_key, double ratio_over) const;
void setenv_(); void setenv_();
void load(const std::string &file);
void load_from_gcode(const std::string &file);
void save(const std::string &file) const;
}; };
// Configuration store with dynamic number of configuration values. // Configuration store with dynamic number of configuration values.
@ -703,6 +713,9 @@ public:
// virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0; // virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0;
}; };
/// Specialization of std::exception to indicate that an unknown config option has been encountered.
class UnknownOptionException : public std::exception {};
} }
#endif #endif

View File

@ -4,7 +4,7 @@ use strict;
use warnings; use warnings;
use Slic3r::XS; use Slic3r::XS;
use Test::More tests => 146; use Test::More tests => 147;
foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintConfig) { foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintConfig) {
$config->set('layer_height', 0.3); $config->set('layer_height', 0.3);
@ -241,4 +241,13 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo
is_deeply $config->get('retract_layer_change'), [0,0], 'retract_layer_change is disabled with spiral_vase'; is_deeply $config->get('retract_layer_change'), [0,0], 'retract_layer_change is disabled with spiral_vase';
} }
{
use Cwd qw(abs_path);
use File::Basename qw(dirname);
my $class = Slic3r::Config->new;
my $path = abs_path($0);
my $config = $class->_load(dirname($path)."/inc/22_config_bad_config_options.ini");
ok 1, 'did not crash on reading invalid items in config';
}
__END__ __END__

View File

@ -0,0 +1,7 @@
# generated by Slic3r 1.1.7 on Tue Aug 19 21:49:50 2014
avoid_crossing_perimeters = 1
bed_size = 200,180
g0 = 0
perimeter_acceleration = 0
support_material_extruder = 1
support_material_extrusion_width = 0

View File

@ -38,6 +38,9 @@
void normalize(); void normalize();
%name{setenv} void setenv_(); %name{setenv} void setenv_();
double min_object_distance(); double min_object_distance();
%name{_load} void load(std::string file);
%name{_load_from_gcode} void load_from_gcode(std::string file);
%name{_save} void save(std::string file);
}; };
%name{Slic3r::Config::Static} class StaticPrintConfig { %name{Slic3r::Config::Static} class StaticPrintConfig {
@ -84,6 +87,9 @@
%}; %};
%name{setenv} void setenv_(); %name{setenv} void setenv_();
double min_object_distance(); double min_object_distance();
%name{_load} void load(std::string file);
%name{_load_from_gcode} void load_from_gcode(std::string file);
%name{_save} void save(std::string file);
}; };
%package{Slic3r::Config}; %package{Slic3r::Config};