Ported config load / stored to C++, thanks @alexrj
Implemented import of config values from the G-code.
This commit is contained in:
parent
c61e098066
commit
93dce7a2d3
@ -101,8 +101,14 @@ sub load {
|
||||
my $class = shift;
|
||||
my ($file) = @_;
|
||||
|
||||
if ($file =~ /\.gcode$/i || $file =~ /\.g$/i) {
|
||||
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,
|
||||
|
@ -543,7 +543,7 @@ sub load_config_file {
|
||||
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 $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;
|
||||
$file = Slic3r::decode_path($dlg->GetPaths);
|
||||
$dlg->Destroy;
|
||||
|
@ -1,6 +1,22 @@
|
||||
#include "Config.hpp"
|
||||
#include <stdlib.h> // for setenv()
|
||||
#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>
|
||||
|
||||
#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)
|
||||
{
|
||||
out.clear();
|
||||
if (str.empty())
|
||||
return true;
|
||||
|
||||
@ -195,19 +210,36 @@ std::string ConfigBase::serialize(const t_config_option_key &opt_key) const
|
||||
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);
|
||||
if (optdef == NULL) throw "Calling set_deserialize() on unknown option";
|
||||
if (!optdef->shortcut.empty()) {
|
||||
for (std::vector<t_config_option_key>::const_iterator it = optdef->shortcut.begin(); it != optdef->shortcut.end(); ++it) {
|
||||
if (!this->set_deserialize(*it, str)) return false;
|
||||
if (optdef == nullptr) {
|
||||
// If we didn't find an option, look for any other option having this as an alias.
|
||||
for (const auto &opt : this->def->options) {
|
||||
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;
|
||||
}
|
||||
ConfigOption* opt = this->option(opt_key, true);
|
||||
|
||||
ConfigOption *opt = this->option(opt_key, true);
|
||||
assert(opt != nullptr);
|
||||
return opt->deserialize(str);
|
||||
return opt->deserialize(str, append);
|
||||
}
|
||||
|
||||
// Return an absolute value of a possibly relative config variable.
|
||||
@ -259,6 +291,90 @@ void ConfigBase::setenv_()
|
||||
#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) {
|
||||
t_options_map::iterator it = options.find(opt_key);
|
||||
if (it == options.end()) {
|
||||
|
@ -29,7 +29,7 @@ class ConfigOption {
|
||||
public:
|
||||
virtual ~ConfigOption() {};
|
||||
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 int getInt() const { return 0; };
|
||||
virtual double getFloat() const { return 0; };
|
||||
@ -94,7 +94,7 @@ public:
|
||||
return ss.str();
|
||||
};
|
||||
|
||||
bool deserialize(std::string str) {
|
||||
bool deserialize(const std::string &str, bool append = false) {
|
||||
std::istringstream iss(str);
|
||||
iss >> this->value;
|
||||
return !iss.fail();
|
||||
@ -124,7 +124,8 @@ public:
|
||||
return vv;
|
||||
};
|
||||
|
||||
bool deserialize(std::string str) {
|
||||
bool deserialize(const std::string &str, bool append = false) {
|
||||
if (! append)
|
||||
this->values.clear();
|
||||
std::istringstream is(str);
|
||||
std::string item_str;
|
||||
@ -153,7 +154,7 @@ public:
|
||||
return ss.str();
|
||||
};
|
||||
|
||||
bool deserialize(std::string str) {
|
||||
bool deserialize(const std::string &str, bool append = false) {
|
||||
std::istringstream iss(str);
|
||||
iss >> this->value;
|
||||
return !iss.fail();
|
||||
@ -183,7 +184,8 @@ public:
|
||||
return vv;
|
||||
};
|
||||
|
||||
bool deserialize(std::string str) {
|
||||
bool deserialize(const std::string &str, bool append = false) {
|
||||
if (! append)
|
||||
this->values.clear();
|
||||
std::istringstream is(str);
|
||||
std::string item_str;
|
||||
@ -207,7 +209,7 @@ public:
|
||||
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);
|
||||
};
|
||||
};
|
||||
@ -224,7 +226,9 @@ public:
|
||||
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);
|
||||
};
|
||||
};
|
||||
@ -247,7 +251,7 @@ public:
|
||||
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
|
||||
std::istringstream iss(str);
|
||||
iss >> this->value;
|
||||
@ -280,7 +284,8 @@ public:
|
||||
return vv;
|
||||
};
|
||||
|
||||
bool deserialize(std::string str) {
|
||||
bool deserialize(const std::string &str, bool append = false) {
|
||||
if (! append)
|
||||
this->values.clear();
|
||||
std::istringstream is(str);
|
||||
std::string item_str;
|
||||
@ -327,7 +332,7 @@ public:
|
||||
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;
|
||||
std::istringstream iss(str);
|
||||
iss >> this->value;
|
||||
@ -349,7 +354,7 @@ public:
|
||||
return ss.str();
|
||||
};
|
||||
|
||||
bool deserialize(std::string str) {
|
||||
bool deserialize(const std::string &str, bool append = false) {
|
||||
std::istringstream iss(str);
|
||||
iss >> this->value.x;
|
||||
iss.ignore(std::numeric_limits<std::streamsize>::max(), ',');
|
||||
@ -383,7 +388,8 @@ public:
|
||||
return vv;
|
||||
};
|
||||
|
||||
bool deserialize(std::string str) {
|
||||
bool deserialize(const std::string &str, bool append = false) {
|
||||
if (! append)
|
||||
this->values.clear();
|
||||
std::istringstream is(str);
|
||||
std::string point_str;
|
||||
@ -415,7 +421,7 @@ public:
|
||||
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);
|
||||
return true;
|
||||
};
|
||||
@ -443,7 +449,8 @@ public:
|
||||
return vv;
|
||||
};
|
||||
|
||||
bool deserialize(std::string str) {
|
||||
bool deserialize(const std::string &str, bool append = false) {
|
||||
if (! append)
|
||||
this->values.clear();
|
||||
std::istringstream is(str);
|
||||
std::string item_str;
|
||||
@ -473,7 +480,7 @@ public:
|
||||
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();
|
||||
if (enum_keys_map.count(str) == 0) return false;
|
||||
this->value = static_cast<T>(enum_keys_map[str]);
|
||||
@ -500,7 +507,7 @@ public:
|
||||
return "";
|
||||
};
|
||||
|
||||
bool deserialize(std::string str) {
|
||||
bool deserialize(const std::string &str, bool append = false) {
|
||||
if (this->keys_map->count(str) == 0) return false;
|
||||
this->value = (*const_cast<t_config_enum_values*>(this->keys_map))[str];
|
||||
return true;
|
||||
@ -660,11 +667,14 @@ public:
|
||||
bool equals(const ConfigBase &other) const { return this->diff(other).empty(); }
|
||||
t_config_option_keys diff(const ConfigBase &other) 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, double ratio_over) const;
|
||||
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.
|
||||
@ -703,6 +713,9 @@ public:
|
||||
// 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
|
||||
|
@ -4,7 +4,7 @@ use strict;
|
||||
use warnings;
|
||||
|
||||
use Slic3r::XS;
|
||||
use Test::More tests => 146;
|
||||
use Test::More tests => 147;
|
||||
|
||||
foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintConfig) {
|
||||
$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';
|
||||
}
|
||||
|
||||
{
|
||||
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__
|
||||
|
7
xs/t/inc/22_config_bad_config_options.ini
Normal file
7
xs/t/inc/22_config_bad_config_options.ini
Normal 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
|
@ -38,6 +38,9 @@
|
||||
void normalize();
|
||||
%name{setenv} void setenv_();
|
||||
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 {
|
||||
@ -84,6 +87,9 @@
|
||||
%};
|
||||
%name{setenv} void setenv_();
|
||||
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};
|
||||
|
Loading…
Reference in New Issue
Block a user