Improved error reporting of the PlaceholderParser.
The PlaceholderParser is currently used by the GCode.cpp and by Printer.cpp to generate a new name for the exported G-code or SVG file. The PlaceholderParser::process() will throw a runtime_error with a comprehensive error message. The G-code export will include these error messages into the G-code text with !!!!!! separators, and the GUI will inform the user, that the G-code export failed.
This commit is contained in:
parent
1244fd09eb
commit
8746f84fa2
11 changed files with 190 additions and 141 deletions
|
@ -422,6 +422,7 @@ sub quick_slice {
|
||||||
if ($params{reslice}) {
|
if ($params{reslice}) {
|
||||||
$output_file = $qs_last_output_file if defined $qs_last_output_file;
|
$output_file = $qs_last_output_file if defined $qs_last_output_file;
|
||||||
} elsif ($params{save_as}) {
|
} elsif ($params{save_as}) {
|
||||||
|
# The following line may die if the output_filename_format template substitution fails.
|
||||||
$output_file = $sprint->output_filepath;
|
$output_file = $sprint->output_filepath;
|
||||||
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/ if $params{export_svg};
|
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/ if $params{export_svg};
|
||||||
my $dlg = Wx::FileDialog->new($self, 'Save ' . ($params{export_svg} ? 'SVG' : 'G-code') . ' file as:',
|
my $dlg = Wx::FileDialog->new($self, 'Save ' . ($params{export_svg} ? 'SVG' : 'G-code') . ' file as:',
|
||||||
|
|
|
@ -1291,9 +1291,11 @@ sub export_gcode {
|
||||||
|
|
||||||
# select output file
|
# select output file
|
||||||
if ($output_file) {
|
if ($output_file) {
|
||||||
$self->{export_gcode_output_file} = $self->{print}->output_filepath($output_file);
|
$self->{export_gcode_output_file} = eval { $self->{print}->output_filepath($output_file) };
|
||||||
|
Slic3r::GUI::catch_error($self) and return;
|
||||||
} else {
|
} else {
|
||||||
my $default_output_file = $self->{print}->output_filepath($main::opt{output} // '');
|
my $default_output_file = eval { $self->{print}->output_filepath($main::opt{output} // '') };
|
||||||
|
Slic3r::GUI::catch_error($self) and return;
|
||||||
my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:',
|
my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:',
|
||||||
wxTheApp->{app_config}->get_last_output_dir(dirname($default_output_file)),
|
wxTheApp->{app_config}->get_last_output_dir(dirname($default_output_file)),
|
||||||
basename($default_output_file), &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
basename($default_output_file), &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||||
|
@ -1544,7 +1546,8 @@ sub export_amf {
|
||||||
sub _get_export_file {
|
sub _get_export_file {
|
||||||
my ($self, $format) = @_;
|
my ($self, $format) = @_;
|
||||||
my $suffix = $format eq 'STL' ? '.stl' : '.amf.xml';
|
my $suffix = $format eq 'STL' ? '.stl' : '.amf.xml';
|
||||||
my $output_file = $self->{print}->output_filepath($main::opt{output} // '');
|
my $output_file = eval { $self->{print}->output_filepath($main::opt{output} // '') };
|
||||||
|
Slic3r::GUI::catch_error($self) and return undef;
|
||||||
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/$suffix/;
|
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/$suffix/;
|
||||||
my $dlg = Wx::FileDialog->new($self, "Save $format file as:", dirname($output_file),
|
my $dlg = Wx::FileDialog->new($self, "Save $format file as:", dirname($output_file),
|
||||||
basename($output_file), &Slic3r::GUI::MODEL_WILDCARD, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
basename($output_file), &Slic3r::GUI::MODEL_WILDCARD, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||||
|
|
|
@ -65,6 +65,9 @@ sub process {
|
||||||
}
|
}
|
||||||
|
|
||||||
# G-code export process, running at a background thread.
|
# G-code export process, running at a background thread.
|
||||||
|
# The export_gcode may die for various reasons (fails to process output_filename_format,
|
||||||
|
# write error into the G-code, cannot execute post-processing scripts).
|
||||||
|
# It is up to the caller to show an error message.
|
||||||
sub export_gcode {
|
sub export_gcode {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my %params = @_;
|
my %params = @_;
|
||||||
|
@ -73,11 +76,12 @@ sub export_gcode {
|
||||||
$self->process;
|
$self->process;
|
||||||
|
|
||||||
# output everything to a G-code file
|
# output everything to a G-code file
|
||||||
|
# The following call may die if the output_filename_format template substitution fails.
|
||||||
my $output_file = $self->output_filepath($params{output_file} // '');
|
my $output_file = $self->output_filepath($params{output_file} // '');
|
||||||
$self->status_cb->(90, "Exporting G-code" . ($output_file ? " to $output_file" : ""));
|
$self->status_cb->(90, "Exporting G-code" . ($output_file ? " to $output_file" : ""));
|
||||||
|
|
||||||
die "G-code export to " . $output_file . " failed\n"
|
# The following line may die for multiple reasons.
|
||||||
if ! Slic3r::GCode->new->do_export($self, $output_file);
|
Slic3r::GCode->new->do_export($self, $output_file);
|
||||||
|
|
||||||
# run post-processing scripts
|
# run post-processing scripts
|
||||||
if (@{$self->config->post_process}) {
|
if (@{$self->config->post_process}) {
|
||||||
|
@ -99,6 +103,7 @@ sub export_gcode {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Export SVG slices for the offline SLA printing.
|
# Export SVG slices for the offline SLA printing.
|
||||||
|
# The export_svg is expected to be executed inside an eval block.
|
||||||
sub export_svg {
|
sub export_svg {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my %params = @_;
|
my %params = @_;
|
||||||
|
@ -107,6 +112,7 @@ sub export_svg {
|
||||||
|
|
||||||
my $fh = $params{output_fh};
|
my $fh = $params{output_fh};
|
||||||
if (!$fh) {
|
if (!$fh) {
|
||||||
|
# The following line may die if the output_filename_format template substitution fails.
|
||||||
my $output_file = $self->output_filepath($params{output_file});
|
my $output_file = $self->output_filepath($params{output_file});
|
||||||
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/;
|
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/;
|
||||||
Slic3r::open(\$fh, ">", $output_file) or die "Failed to open $output_file for writing\n";
|
Slic3r::open(\$fh, ">", $output_file) or die "Failed to open $output_file for writing\n";
|
||||||
|
|
|
@ -219,6 +219,8 @@ if (@ARGV) { # slicing from command line
|
||||||
$sprint->export_svg;
|
$sprint->export_svg;
|
||||||
} else {
|
} else {
|
||||||
my $t0 = [gettimeofday];
|
my $t0 = [gettimeofday];
|
||||||
|
# The following call may die if the output_filename_format template substitution fails,
|
||||||
|
# if the file cannot be written into, or if the post processing scripts cannot be executed.
|
||||||
$sprint->export_gcode;
|
$sprint->export_gcode;
|
||||||
|
|
||||||
# output some statistics
|
# output some statistics
|
||||||
|
|
|
@ -187,7 +187,7 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
|
||||||
if (! start_filament_gcode.empty()) {
|
if (! start_filament_gcode.empty()) {
|
||||||
// Process the start_filament_gcode for the active filament only.
|
// Process the start_filament_gcode for the active filament only.
|
||||||
gcodegen.placeholder_parser().set("current_extruder", new_extruder_id);
|
gcodegen.placeholder_parser().set("current_extruder", new_extruder_id);
|
||||||
gcode += gcodegen.placeholder_parser().process(start_filament_gcode, new_extruder_id);
|
gcode += gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id);
|
||||||
check_add_eol(gcode);
|
check_add_eol(gcode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -362,7 +362,7 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
|
||||||
return layers_to_print;
|
return layers_to_print;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GCode::do_export(Print *print, const char *path)
|
void GCode::do_export(Print *print, const char *path)
|
||||||
{
|
{
|
||||||
// Remove the old g-code if it exists.
|
// Remove the old g-code if it exists.
|
||||||
boost::nowide::remove(path);
|
boost::nowide::remove(path);
|
||||||
|
@ -372,23 +372,34 @@ bool GCode::do_export(Print *print, const char *path)
|
||||||
|
|
||||||
FILE *file = boost::nowide::fopen(path_tmp.c_str(), "wb");
|
FILE *file = boost::nowide::fopen(path_tmp.c_str(), "wb");
|
||||||
if (file == nullptr)
|
if (file == nullptr)
|
||||||
return false;
|
throw std::runtime_error(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n");
|
||||||
|
|
||||||
bool result = this->_do_export(*print, file);
|
this->m_placeholder_parser_failed_templates.clear();
|
||||||
|
this->_do_export(*print, file);
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
if (ferror(file)) {
|
||||||
if (result && boost::nowide::rename(path_tmp.c_str(), path) != 0) {
|
|
||||||
boost::nowide::cerr << "Failed to remove the output G-code file from " << path_tmp << " to " << path
|
|
||||||
<< ". Is " << path_tmp << " locked?" << std::endl;
|
|
||||||
result = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! result)
|
|
||||||
boost::nowide::remove(path_tmp.c_str());
|
boost::nowide::remove(path_tmp.c_str());
|
||||||
return result;
|
throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n");
|
||||||
|
}
|
||||||
|
if (! this->m_placeholder_parser_failed_templates.empty()) {
|
||||||
|
// G-code export proceeded, but some of the PlaceholderParser substitutions failed.
|
||||||
|
std::string msg = std::string("G-code export to ") + path + " failed due to invalid custom G-code sections:\n\n";
|
||||||
|
for (const std::string &name : this->m_placeholder_parser_failed_templates)
|
||||||
|
msg += std::string("\t") + name + "\n";
|
||||||
|
msg += "\nPlease inspect the file ";
|
||||||
|
msg += path_tmp + " for error messages enclosed between\n";
|
||||||
|
msg += " !!!!! Failed to process the custom G-code template ...\n";
|
||||||
|
msg += "and\n";
|
||||||
|
msg += " !!!!! End of an error report for the custom G-code template ...\n";
|
||||||
|
throw std::runtime_error(msg);
|
||||||
|
}
|
||||||
|
if (boost::nowide::rename(path_tmp.c_str(), path) != 0)
|
||||||
|
throw std::runtime_error(
|
||||||
|
std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' +
|
||||||
|
"Is " + path_tmp + " locked?" + '\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GCode::_do_export(Print &print, FILE *file)
|
void GCode::_do_export(Print &print, FILE *file)
|
||||||
{
|
{
|
||||||
// How many times will be change_layer() called?
|
// How many times will be change_layer() called?
|
||||||
// change_layer() in turn increments the progress bar status.
|
// change_layer() in turn increments the progress bar status.
|
||||||
|
@ -557,7 +568,7 @@ bool GCode::_do_export(Print &print, FILE *file)
|
||||||
m_placeholder_parser.set("current_object_idx", 0);
|
m_placeholder_parser.set("current_object_idx", 0);
|
||||||
// For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided.
|
// For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided.
|
||||||
m_placeholder_parser.set("has_wipe_tower", has_wipe_tower);
|
m_placeholder_parser.set("has_wipe_tower", has_wipe_tower);
|
||||||
std::string start_gcode = m_placeholder_parser.process(print.config.start_gcode.value, initial_extruder_id);
|
std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config.start_gcode.value, initial_extruder_id);
|
||||||
|
|
||||||
// Set bed temperature if the start G-code does not contain any bed temp control G-codes.
|
// Set bed temperature if the start G-code does not contain any bed temp control G-codes.
|
||||||
this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true);
|
this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true);
|
||||||
|
@ -571,11 +582,11 @@ bool GCode::_do_export(Print &print, FILE *file)
|
||||||
// Wipe tower will control the extruder switching, it will call the start_filament_gcode.
|
// Wipe tower will control the extruder switching, it will call the start_filament_gcode.
|
||||||
} else {
|
} else {
|
||||||
// Only initialize the initial extruder.
|
// Only initialize the initial extruder.
|
||||||
writeln(file, m_placeholder_parser.process(print.config.start_filament_gcode.values[initial_extruder_id], initial_extruder_id));
|
writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config.start_filament_gcode.values[initial_extruder_id], initial_extruder_id));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (const std::string &start_gcode : print.config.start_filament_gcode.values)
|
for (const std::string &start_gcode : print.config.start_filament_gcode.values)
|
||||||
writeln(file, m_placeholder_parser.process(start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front())));
|
writeln(file, this->placeholder_parser_process("start_gcode", start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front())));
|
||||||
}
|
}
|
||||||
this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true);
|
this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true);
|
||||||
|
|
||||||
|
@ -668,7 +679,7 @@ bool GCode::_do_export(Print &print, FILE *file)
|
||||||
// another one, set first layer temperatures. This happens before the Z move
|
// another one, set first layer temperatures. This happens before the Z move
|
||||||
// is triggered, so machine has more time to reach such temperatures.
|
// is triggered, so machine has more time to reach such temperatures.
|
||||||
m_placeholder_parser.set("current_object_idx", int(finished_objects));
|
m_placeholder_parser.set("current_object_idx", int(finished_objects));
|
||||||
std::string between_objects_gcode = m_placeholder_parser.process(print.config.between_objects_gcode.value, initial_extruder_id);
|
std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config.between_objects_gcode.value, initial_extruder_id);
|
||||||
// Set first layer bed and extruder temperatures, don't wait for it to reach the temperature.
|
// Set first layer bed and extruder temperatures, don't wait for it to reach the temperature.
|
||||||
this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false);
|
this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false);
|
||||||
this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false);
|
this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false);
|
||||||
|
@ -747,12 +758,12 @@ bool GCode::_do_export(Print &print, FILE *file)
|
||||||
// Process filament-specific gcode in extruder order.
|
// Process filament-specific gcode in extruder order.
|
||||||
if (print.config.single_extruder_multi_material) {
|
if (print.config.single_extruder_multi_material) {
|
||||||
// Process the end_filament_gcode for the active filament only.
|
// Process the end_filament_gcode for the active filament only.
|
||||||
writeln(file, m_placeholder_parser.process(print.config.end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id()));
|
writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config.end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id()));
|
||||||
} else {
|
} else {
|
||||||
for (const std::string &end_gcode : print.config.end_filament_gcode.values)
|
for (const std::string &end_gcode : print.config.end_filament_gcode.values)
|
||||||
writeln(file, m_placeholder_parser.process(end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front())));
|
writeln(file, this->placeholder_parser_process("end_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front())));
|
||||||
}
|
}
|
||||||
writeln(file, m_placeholder_parser.process(print.config.end_gcode, m_writer.extruder()->id()));
|
writeln(file, this->placeholder_parser_process("end_gcode", print.config.end_gcode, m_writer.extruder()->id()));
|
||||||
write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
|
write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
|
||||||
write(file, m_writer.postamble());
|
write(file, m_writer.postamble());
|
||||||
|
|
||||||
|
@ -793,8 +804,21 @@ bool GCode::_do_export(Print &print, FILE *file)
|
||||||
fprintf(file, "; %s = %s\n", key.c_str(), cfg->serialize(key).c_str());
|
fprintf(file, "; %s = %s\n", key.c_str(), cfg->serialize(key).c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return m_placeholder_parser.process(templ, current_extruder_id, config_override);
|
||||||
|
} catch (std::runtime_error &err) {
|
||||||
|
// Collect the names of failed template substitutions for error reporting.
|
||||||
|
this->m_placeholder_parser_failed_templates.insert(name);
|
||||||
|
// Insert the macro error message into the G-code.
|
||||||
|
return
|
||||||
|
std::string("!!!!! Failed to process the custom G-code template ") + name + "\n" +
|
||||||
|
err.what() +
|
||||||
|
"!!!!! End of an error report for the custom G-code template " + name + "\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the custom G-code, try to find mcode_set_temp_dont_wait and mcode_set_temp_and_wait inside the custom G-code.
|
// Parse the custom G-code, try to find mcode_set_temp_dont_wait and mcode_set_temp_and_wait inside the custom G-code.
|
||||||
|
@ -997,7 +1021,7 @@ void GCode::process_layer(
|
||||||
DynamicConfig config;
|
DynamicConfig config;
|
||||||
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1));
|
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1));
|
||||||
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
|
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
|
||||||
gcode += m_placeholder_parser.process(
|
gcode += this->placeholder_parser_process("before_layer_gcode",
|
||||||
print.config.before_layer_gcode.value, m_writer.extruder()->id(), &config)
|
print.config.before_layer_gcode.value, m_writer.extruder()->id(), &config)
|
||||||
+ "\n";
|
+ "\n";
|
||||||
}
|
}
|
||||||
|
@ -1007,7 +1031,7 @@ void GCode::process_layer(
|
||||||
DynamicConfig config;
|
DynamicConfig config;
|
||||||
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
|
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
|
||||||
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
|
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
|
||||||
gcode += m_placeholder_parser.process(
|
gcode += this->placeholder_parser_process("layer_gcode",
|
||||||
print.config.layer_gcode.value, m_writer.extruder()->id(), &config)
|
print.config.layer_gcode.value, m_writer.extruder()->id(), &config)
|
||||||
+ "\n";
|
+ "\n";
|
||||||
}
|
}
|
||||||
|
@ -2206,7 +2230,7 @@ std::string GCode::set_extruder(unsigned int extruder_id)
|
||||||
unsigned int old_extruder_id = m_writer.extruder()->id();
|
unsigned int old_extruder_id = m_writer.extruder()->id();
|
||||||
const std::string &end_filament_gcode = m_config.end_filament_gcode.get_at(old_extruder_id);
|
const std::string &end_filament_gcode = m_config.end_filament_gcode.get_at(old_extruder_id);
|
||||||
if (m_config.single_extruder_multi_material && ! end_filament_gcode.empty()) {
|
if (m_config.single_extruder_multi_material && ! end_filament_gcode.empty()) {
|
||||||
gcode += m_placeholder_parser.process(end_filament_gcode, old_extruder_id);
|
gcode += placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id);
|
||||||
check_add_eol(gcode);
|
check_add_eol(gcode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2218,7 +2242,7 @@ std::string GCode::set_extruder(unsigned int extruder_id)
|
||||||
DynamicConfig config;
|
DynamicConfig config;
|
||||||
config.set_key_value("previous_extruder", new ConfigOptionInt((int)m_writer.extruder()->id()));
|
config.set_key_value("previous_extruder", new ConfigOptionInt((int)m_writer.extruder()->id()));
|
||||||
config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id));
|
config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id));
|
||||||
gcode += m_placeholder_parser.process(m_config.toolchange_gcode.value, extruder_id, &config);
|
gcode += placeholder_parser_process("toolchange_gcode", m_config.toolchange_gcode.value, extruder_id, &config);
|
||||||
check_add_eol(gcode);
|
check_add_eol(gcode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2232,7 +2256,7 @@ std::string GCode::set_extruder(unsigned int extruder_id)
|
||||||
const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id);
|
const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id);
|
||||||
if (m_config.single_extruder_multi_material && ! start_filament_gcode.empty()) {
|
if (m_config.single_extruder_multi_material && ! start_filament_gcode.empty()) {
|
||||||
// Process the start_filament_gcode for the active filament only.
|
// Process the start_filament_gcode for the active filament only.
|
||||||
gcode += m_placeholder_parser.process(start_filament_gcode, extruder_id);
|
gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id);
|
||||||
check_add_eol(gcode);
|
check_add_eol(gcode);
|
||||||
}
|
}
|
||||||
// Set the new extruder to the operating temperature.
|
// Set the new extruder to the operating temperature.
|
||||||
|
|
|
@ -130,7 +130,8 @@ public:
|
||||||
{}
|
{}
|
||||||
~GCode() {}
|
~GCode() {}
|
||||||
|
|
||||||
bool do_export(Print *print, const char *path);
|
// throws std::runtime_exception
|
||||||
|
void do_export(Print *print, const char *path);
|
||||||
|
|
||||||
// Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
|
// Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
|
||||||
const Pointf& origin() const { return m_origin; }
|
const Pointf& origin() const { return m_origin; }
|
||||||
|
@ -143,6 +144,9 @@ public:
|
||||||
const Layer* layer() const { return m_layer; }
|
const Layer* layer() const { return m_layer; }
|
||||||
GCodeWriter& writer() { return m_writer; }
|
GCodeWriter& writer() { return m_writer; }
|
||||||
PlaceholderParser& placeholder_parser() { return m_placeholder_parser; }
|
PlaceholderParser& placeholder_parser() { return m_placeholder_parser; }
|
||||||
|
// Process a template through the placeholder parser, collect error messages to be reported
|
||||||
|
// inside the generated string and after the G-code export finishes.
|
||||||
|
std::string placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr);
|
||||||
bool enable_cooling_markers() const { return m_enable_cooling_markers; }
|
bool enable_cooling_markers() const { return m_enable_cooling_markers; }
|
||||||
|
|
||||||
// For Perl bindings, to be used exclusively by unit tests.
|
// For Perl bindings, to be used exclusively by unit tests.
|
||||||
|
@ -151,7 +155,7 @@ public:
|
||||||
void apply_print_config(const PrintConfig &print_config);
|
void apply_print_config(const PrintConfig &print_config);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool _do_export(Print &print, FILE *file);
|
void _do_export(Print &print, FILE *file);
|
||||||
|
|
||||||
// Object and support extrusions of the same PrintObject at the same print_z.
|
// Object and support extrusions of the same PrintObject at the same print_z.
|
||||||
struct LayerToPrint
|
struct LayerToPrint
|
||||||
|
@ -223,6 +227,8 @@ protected:
|
||||||
FullPrintConfig m_config;
|
FullPrintConfig m_config;
|
||||||
GCodeWriter m_writer;
|
GCodeWriter m_writer;
|
||||||
PlaceholderParser m_placeholder_parser;
|
PlaceholderParser m_placeholder_parser;
|
||||||
|
// Collection of templates, on which the placeholder substitution failed.
|
||||||
|
std::set<std::string> m_placeholder_parser_failed_templates;
|
||||||
OozePrevention m_ooze_prevention;
|
OozePrevention m_ooze_prevention;
|
||||||
Wipe m_wipe;
|
Wipe m_wipe;
|
||||||
AvoidCrossingPerimeters m_avoid_crossing_perimeters;
|
AvoidCrossingPerimeters m_avoid_crossing_perimeters;
|
||||||
|
|
|
@ -368,7 +368,7 @@ namespace client
|
||||||
value = lhs.to_string() == rhs.to_string();
|
value = lhs.to_string() == rhs.to_string();
|
||||||
} else {
|
} else {
|
||||||
boost::throw_exception(qi::expectation_failure<Iterator>(
|
boost::throw_exception(qi::expectation_failure<Iterator>(
|
||||||
lhs.it_range.begin(), rhs.it_range.end(), spirit::info("Cannot compare the types.")));
|
lhs.it_range.begin(), rhs.it_range.end(), spirit::info("*Cannot compare the types.")));
|
||||||
}
|
}
|
||||||
lhs.type = TYPE_BOOL;
|
lhs.type = TYPE_BOOL;
|
||||||
lhs.data.b = (op == '=') ? value : !value;
|
lhs.data.b = (op == '=') ? value : !value;
|
||||||
|
@ -387,7 +387,7 @@ namespace client
|
||||||
void throw_exception(const char *message) const
|
void throw_exception(const char *message) const
|
||||||
{
|
{
|
||||||
boost::throw_exception(qi::expectation_failure<Iterator>(
|
boost::throw_exception(qi::expectation_failure<Iterator>(
|
||||||
this->it_range.begin(), this->it_range.end(), spirit::info(message)));
|
this->it_range.begin(), this->it_range.end(), spirit::info(std::string("*") + message)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void throw_if_not_numeric(const char *message) const
|
void throw_if_not_numeric(const char *message) const
|
||||||
|
@ -417,6 +417,7 @@ namespace client
|
||||||
const PlaceholderParser *pp = nullptr;
|
const PlaceholderParser *pp = nullptr;
|
||||||
const DynamicConfig *config_override = nullptr;
|
const DynamicConfig *config_override = nullptr;
|
||||||
const size_t current_extruder_id = 0;
|
const size_t current_extruder_id = 0;
|
||||||
|
std::string error_message;
|
||||||
|
|
||||||
const ConfigOption* resolve_symbol(const std::string &opt_key) const
|
const ConfigOption* resolve_symbol(const std::string &opt_key) const
|
||||||
{
|
{
|
||||||
|
@ -444,26 +445,22 @@ namespace client
|
||||||
opt = ctx->resolve_symbol(opt_key_str.substr(0, idx));
|
opt = ctx->resolve_symbol(opt_key_str.substr(0, idx));
|
||||||
if (opt != nullptr) {
|
if (opt != nullptr) {
|
||||||
if (! opt->is_vector())
|
if (! opt->is_vector())
|
||||||
boost::throw_exception(qi::expectation_failure<Iterator>(
|
ctx->throw_exception("Trying to index a scalar variable", opt_key);
|
||||||
opt_key.begin(), opt_key.end(), spirit::info("Trying to index a scalar variable")));
|
|
||||||
char *endptr = nullptr;
|
char *endptr = nullptr;
|
||||||
idx = strtol(opt_key_str.c_str() + idx + 1, &endptr, 10);
|
idx = strtol(opt_key_str.c_str() + idx + 1, &endptr, 10);
|
||||||
if (endptr == nullptr || *endptr != 0)
|
if (endptr == nullptr || *endptr != 0)
|
||||||
boost::throw_exception(qi::expectation_failure<Iterator>(
|
ctx->throw_exception("Invalid vector index", boost::iterator_range<Iterator>(opt_key.begin() + idx + 1, opt_key.end()));
|
||||||
opt_key.begin() + idx + 1, opt_key.end(), spirit::info("Invalid vector index")));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (opt == nullptr)
|
if (opt == nullptr)
|
||||||
boost::throw_exception(qi::expectation_failure<Iterator>(
|
ctx->throw_exception("Variable does not exist", boost::iterator_range<Iterator>(opt_key.begin(), opt_key.end()));
|
||||||
opt_key.begin(), opt_key.end(), spirit::info("Variable does not exist")));
|
|
||||||
if (opt->is_scalar())
|
if (opt->is_scalar())
|
||||||
output = opt->serialize();
|
output = opt->serialize();
|
||||||
else {
|
else {
|
||||||
const ConfigOptionVectorBase *vec = static_cast<const ConfigOptionVectorBase*>(opt);
|
const ConfigOptionVectorBase *vec = static_cast<const ConfigOptionVectorBase*>(opt);
|
||||||
if (vec->empty())
|
if (vec->empty())
|
||||||
boost::throw_exception(qi::expectation_failure<Iterator>(
|
ctx->throw_exception("Indexing an empty vector variable", opt_key);
|
||||||
opt_key.begin(), opt_key.end(), spirit::info("Indexing an empty vector variable")));
|
|
||||||
output = vec->vserialize()[(idx >= vec->size()) ? 0 : idx];
|
output = vec->vserialize()[(idx >= vec->size()) ? 0 : idx];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -484,23 +481,18 @@ namespace client
|
||||||
opt = ctx->resolve_symbol(opt_key_str);
|
opt = ctx->resolve_symbol(opt_key_str);
|
||||||
}
|
}
|
||||||
if (! opt->is_vector())
|
if (! opt->is_vector())
|
||||||
boost::throw_exception(qi::expectation_failure<Iterator>(
|
ctx->throw_exception("Trying to index a scalar variable", opt_key);
|
||||||
opt_key.begin(), opt_key.end(), spirit::info("Trying to index a scalar variable")));
|
|
||||||
const ConfigOptionVectorBase *vec = static_cast<const ConfigOptionVectorBase*>(opt);
|
const ConfigOptionVectorBase *vec = static_cast<const ConfigOptionVectorBase*>(opt);
|
||||||
if (vec->empty())
|
if (vec->empty())
|
||||||
boost::throw_exception(qi::expectation_failure<Iterator>(
|
ctx->throw_exception("Indexing an empty vector variable", boost::iterator_range<Iterator>(opt_key.begin(), opt_key.end()));
|
||||||
opt_key.begin(), opt_key.end(), spirit::info("Indexing an empty vector variable")));
|
|
||||||
const ConfigOption *opt_index = ctx->resolve_symbol(std::string(opt_vector_index.begin(), opt_vector_index.end()));
|
const ConfigOption *opt_index = ctx->resolve_symbol(std::string(opt_vector_index.begin(), opt_vector_index.end()));
|
||||||
if (opt_index == nullptr)
|
if (opt_index == nullptr)
|
||||||
boost::throw_exception(qi::expectation_failure<Iterator>(
|
ctx->throw_exception("Variable does not exist", opt_key);
|
||||||
opt_key.begin(), opt_key.end(), spirit::info("Variable does not exist")));
|
|
||||||
if (opt_index->type() != coInt)
|
if (opt_index->type() != coInt)
|
||||||
boost::throw_exception(qi::expectation_failure<Iterator>(
|
ctx->throw_exception("Indexing variable has to be integer", opt_key);
|
||||||
opt_key.begin(), opt_key.end(), spirit::info("Indexing variable has to be integer")));
|
|
||||||
int idx = opt_index->getInt();
|
int idx = opt_index->getInt();
|
||||||
if (idx < 0)
|
if (idx < 0)
|
||||||
boost::throw_exception(qi::expectation_failure<Iterator>(
|
ctx->throw_exception("Negative vector index", opt_key);
|
||||||
opt_key.begin(), opt_key.end(), spirit::info("Negative vector index")));
|
|
||||||
output = vec->vserialize()[(idx >= (int)vec->size()) ? 0 : idx];
|
output = vec->vserialize()[(idx >= (int)vec->size()) ? 0 : idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,8 +504,7 @@ namespace client
|
||||||
{
|
{
|
||||||
const ConfigOption *opt = ctx->resolve_symbol(std::string(opt_key.begin(), opt_key.end()));
|
const ConfigOption *opt = ctx->resolve_symbol(std::string(opt_key.begin(), opt_key.end()));
|
||||||
if (opt == nullptr)
|
if (opt == nullptr)
|
||||||
boost::throw_exception(qi::expectation_failure<Iterator>(
|
ctx->throw_exception("Not a variable name", opt_key);
|
||||||
opt_key.begin(), opt_key.end(), spirit::info("Not a variable name")));
|
|
||||||
output.opt = opt;
|
output.opt = opt;
|
||||||
output.it_range = opt_key;
|
output.it_range = opt_key;
|
||||||
}
|
}
|
||||||
|
@ -525,8 +516,7 @@ namespace client
|
||||||
expr<Iterator> &output)
|
expr<Iterator> &output)
|
||||||
{
|
{
|
||||||
if (opt.opt->is_vector())
|
if (opt.opt->is_vector())
|
||||||
boost::throw_exception(qi::expectation_failure<Iterator>(
|
ctx->throw_exception("Referencing a scalar variable in a vector context", opt.it_range);
|
||||||
opt.it_range.begin(), opt.it_range.end(), spirit::info("Referencing a scalar variable in a vector context")));
|
|
||||||
switch (opt.opt->type()) {
|
switch (opt.opt->type()) {
|
||||||
case coFloat: output.set_d(opt.opt->getFloat()); break;
|
case coFloat: output.set_d(opt.opt->getFloat()); break;
|
||||||
case coInt: output.set_i(opt.opt->getInt()); break;
|
case coInt: output.set_i(opt.opt->getInt()); break;
|
||||||
|
@ -535,11 +525,9 @@ namespace client
|
||||||
case coPoint: output.set_s(opt.opt->serialize()); break;
|
case coPoint: output.set_s(opt.opt->serialize()); break;
|
||||||
case coBool: output.set_b(opt.opt->getBool()); break;
|
case coBool: output.set_b(opt.opt->getBool()); break;
|
||||||
case coFloatOrPercent:
|
case coFloatOrPercent:
|
||||||
boost::throw_exception(qi::expectation_failure<Iterator>(
|
ctx->throw_exception("FloatOrPercent variables are not supported", opt.it_range);
|
||||||
opt.it_range.begin(), opt.it_range.end(), spirit::info("FloatOrPercent variables are not supported")));
|
|
||||||
default:
|
default:
|
||||||
boost::throw_exception(qi::expectation_failure<Iterator>(
|
ctx->throw_exception("Unknown scalar variable type", opt.it_range);
|
||||||
opt.it_range.begin(), opt.it_range.end(), spirit::info("Unknown scalar variable type")));
|
|
||||||
}
|
}
|
||||||
output.it_range = opt.it_range;
|
output.it_range = opt.it_range;
|
||||||
}
|
}
|
||||||
|
@ -553,12 +541,10 @@ namespace client
|
||||||
expr<Iterator> &output)
|
expr<Iterator> &output)
|
||||||
{
|
{
|
||||||
if (opt.opt->is_scalar())
|
if (opt.opt->is_scalar())
|
||||||
boost::throw_exception(qi::expectation_failure<Iterator>(
|
ctx->throw_exception("Referencing a vector variable in a scalar context", opt.it_range);
|
||||||
opt.it_range.begin(), opt.it_range.end(), spirit::info("Referencing a vector variable in a scalar context")));
|
|
||||||
const ConfigOptionVectorBase *vec = static_cast<const ConfigOptionVectorBase*>(opt.opt);
|
const ConfigOptionVectorBase *vec = static_cast<const ConfigOptionVectorBase*>(opt.opt);
|
||||||
if (vec->empty())
|
if (vec->empty())
|
||||||
boost::throw_exception(qi::expectation_failure<Iterator>(
|
ctx->throw_exception("Indexing an empty vector variable", opt.it_range);
|
||||||
opt.it_range.begin(), opt.it_range.end(), spirit::info("Indexing an empty vector variable")));
|
|
||||||
size_t idx = (index < 0) ? 0 : (index >= int(vec->size())) ? 0 : size_t(index);
|
size_t idx = (index < 0) ? 0 : (index >= int(vec->size())) ? 0 : size_t(index);
|
||||||
switch (opt.opt->type()) {
|
switch (opt.opt->type()) {
|
||||||
case coFloats: output.set_d(static_cast<const ConfigOptionFloats *>(opt.opt)->values[idx]); break;
|
case coFloats: output.set_d(static_cast<const ConfigOptionFloats *>(opt.opt)->values[idx]); break;
|
||||||
|
@ -568,8 +554,7 @@ namespace client
|
||||||
case coPoints: output.set_s(static_cast<const ConfigOptionPoints *>(opt.opt)->values[idx].dump_perl()); break;
|
case coPoints: output.set_s(static_cast<const ConfigOptionPoints *>(opt.opt)->values[idx].dump_perl()); break;
|
||||||
case coBools: output.set_b(static_cast<const ConfigOptionBools *>(opt.opt)->values[idx] != 0); break;
|
case coBools: output.set_b(static_cast<const ConfigOptionBools *>(opt.opt)->values[idx] != 0); break;
|
||||||
default:
|
default:
|
||||||
boost::throw_exception(qi::expectation_failure<Iterator>(
|
ctx->throw_exception("Unknown vector variable type", opt.it_range);
|
||||||
opt.it_range.begin(), opt.it_range.end(), spirit::info("Unknown vector variable type")));
|
|
||||||
}
|
}
|
||||||
output.it_range = boost::iterator_range<Iterator>(opt.it_range.begin(), it_end);
|
output.it_range = boost::iterator_range<Iterator>(opt.it_range.begin(), it_end);
|
||||||
}
|
}
|
||||||
|
@ -579,10 +564,54 @@ namespace client
|
||||||
template <typename Iterator>
|
template <typename Iterator>
|
||||||
static void evaluate_index(expr<Iterator> &expr_index, int &output)
|
static void evaluate_index(expr<Iterator> &expr_index, int &output)
|
||||||
{
|
{
|
||||||
if (expr_index.type != expr<Iterator>::TYPE_INT)
|
if (expr_index.type != expr<Iterator>::TYPE_INT)
|
||||||
expr_index.throw_exception("Non-integer index is not allowed to address a vector variable.");
|
expr_index.throw_exception("Non-integer index is not allowed to address a vector variable.");
|
||||||
output = expr_index.i();
|
output = expr_index.i();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Iterator>
|
||||||
|
static void throw_exception(const std::string &msg, const boost::iterator_range<Iterator> &it_range)
|
||||||
|
{
|
||||||
|
// An asterix is added to the start of the string to differentiate the boost::spirit::info::tag content
|
||||||
|
// between the grammer terminal / non-terminal symbol name and a free-form error message.
|
||||||
|
boost::throw_exception(qi::expectation_failure<Iterator>(it_range.begin(), it_range.end(), spirit::info(std::string("*") + msg)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Iterator>
|
||||||
|
static void process_error_message(const MyContext *context, const boost::spirit::info &info, const Iterator &it_begin, const Iterator &it_end)
|
||||||
|
{
|
||||||
|
struct expectation_printer
|
||||||
|
{
|
||||||
|
expectation_printer(std::string &msg) : result(msg) {}
|
||||||
|
std::string &result;
|
||||||
|
void element(std::string const& tag, std::string const& value, int depth)
|
||||||
|
{
|
||||||
|
// Indent to depth.
|
||||||
|
for (int i = 0; i < depth * 4; ++ i)
|
||||||
|
result += ' ';
|
||||||
|
if (tag.empty() || tag.front() != '*') {
|
||||||
|
if (depth == 0)
|
||||||
|
this->result += "Expecting ";
|
||||||
|
this->result += "tag: ";
|
||||||
|
this->result += tag;
|
||||||
|
} else
|
||||||
|
this->result += tag.substr(1);
|
||||||
|
if (! value.empty()) {
|
||||||
|
this->result += ", value: ";
|
||||||
|
this->result += value;
|
||||||
|
}
|
||||||
|
this->result += '\n';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string &msg = const_cast<MyContext*>(context)->error_message;
|
||||||
|
msg += "Error! ";
|
||||||
|
expectation_printer ep(msg);
|
||||||
|
spirit::basic_info_walker<expectation_printer> walker(ep, info.tag, 0);
|
||||||
|
boost::apply_visitor(walker, info.value);
|
||||||
|
msg += " got: \"";
|
||||||
|
msg += std::string(it_begin, it_end) + "\"\n";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// For debugging the boost::spirit parsers. Print out the string enclosed in it_range.
|
// For debugging the boost::spirit parsers. Print out the string enclosed in it_range.
|
||||||
|
@ -651,7 +680,8 @@ namespace client
|
||||||
first = it;
|
first = it;
|
||||||
return true;
|
return true;
|
||||||
err:
|
err:
|
||||||
boost::throw_exception(qi::expectation_failure<Iterator>(first, last, spirit::info("Invalid utf8 sequence")));
|
MyContext::throw_exception("Invalid utf8 sequence", boost::iterator_range<Iterator>(first, last));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is called during error handling to create a human readable string for the error context.
|
// This function is called during error handling to create a human readable string for the error context.
|
||||||
|
@ -692,6 +722,8 @@ namespace client
|
||||||
qi::_val_type _val;
|
qi::_val_type _val;
|
||||||
qi::_1_type _1;
|
qi::_1_type _1;
|
||||||
qi::_2_type _2;
|
qi::_2_type _2;
|
||||||
|
qi::_3_type _3;
|
||||||
|
qi::_4_type _4;
|
||||||
qi::_a_type _a;
|
qi::_a_type _a;
|
||||||
qi::_b_type _b;
|
qi::_b_type _b;
|
||||||
qi::_r1_type _r1;
|
qi::_r1_type _r1;
|
||||||
|
@ -701,6 +733,7 @@ namespace client
|
||||||
// Without it, some of the errors would not trigger the error handler.
|
// Without it, some of the errors would not trigger the error handler.
|
||||||
start = eps > text_block(_r1);
|
start = eps > text_block(_r1);
|
||||||
start.name("start");
|
start.name("start");
|
||||||
|
qi::on_error<qi::fail>(start, px::bind(&MyContext::process_error_message<Iterator>, _r1, _4, _3, _2));
|
||||||
|
|
||||||
text_block = *(
|
text_block = *(
|
||||||
text [_val+=_1]
|
text [_val+=_1]
|
||||||
|
@ -831,16 +864,6 @@ namespace client
|
||||||
variable_reference = identifier
|
variable_reference = identifier
|
||||||
[ px::bind(&MyContext::resolve_variable<Iterator>, _r1, _1, _val) ];
|
[ px::bind(&MyContext::resolve_variable<Iterator>, _r1, _1, _val) ];
|
||||||
variable_reference.name("variable reference");
|
variable_reference.name("variable reference");
|
||||||
/*
|
|
||||||
qi::on_error<qi::fail>(start,
|
|
||||||
phx::ref(std::cout)
|
|
||||||
<< "Error! Expecting "
|
|
||||||
<< qi::_4
|
|
||||||
<< " here: '"
|
|
||||||
<< px::construct<std::string>(qi::_3, qi::_2)
|
|
||||||
<< "'\n"
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
|
|
||||||
keywords.add
|
keywords.add
|
||||||
("and")
|
("and")
|
||||||
|
@ -907,69 +930,30 @@ namespace client
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
struct printer
|
|
||||||
{
|
|
||||||
typedef spirit::utf8_string string;
|
|
||||||
|
|
||||||
void element(string const& tag, string const& value, int depth) const
|
|
||||||
{
|
|
||||||
for (int i = 0; i < (depth*4); ++i) // indent to depth
|
|
||||||
std::cout << ' ';
|
|
||||||
std::cout << "tag: " << tag;
|
|
||||||
if (value != "")
|
|
||||||
std::cout << ", value: " << value;
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void print_info(spirit::info const& what)
|
|
||||||
{
|
|
||||||
using spirit::basic_info_walker;
|
|
||||||
printer pr;
|
|
||||||
basic_info_walker<printer> walker(pr, what.tag, 0);
|
|
||||||
boost::apply_visitor(walker, what.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string PlaceholderParser::process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) const
|
std::string PlaceholderParser::process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) const
|
||||||
{
|
{
|
||||||
typedef std::string::const_iterator iterator_type;
|
typedef std::string::const_iterator iterator_type;
|
||||||
typedef client::calculator<iterator_type> calculator;
|
typedef client::calculator<iterator_type> calculator;
|
||||||
|
|
||||||
spirit::ascii::space_type space; // Our skipper
|
// Our whitespace skipper.
|
||||||
calculator calc; // Our grammar
|
spirit::ascii::space_type space;
|
||||||
|
// Our grammar.
|
||||||
|
calculator calc;
|
||||||
|
// Iterators over the source template.
|
||||||
std::string::const_iterator iter = templ.begin();
|
std::string::const_iterator iter = templ.begin();
|
||||||
std::string::const_iterator end = templ.end();
|
std::string::const_iterator end = templ.end();
|
||||||
//std::string result;
|
// Accumulator for the processed template.
|
||||||
std::string result;
|
std::string output;
|
||||||
bool r = false;
|
client::MyContext context;
|
||||||
try {
|
context.pp = this;
|
||||||
client::MyContext context;
|
context.config_override = config_override;
|
||||||
context.pp = this;
|
bool res = phrase_parse(iter, end, calc(&context), space, output);
|
||||||
context.config_override = config_override;
|
if (! context.error_message.empty()) {
|
||||||
r = phrase_parse(iter, end, calc(&context), space, result);
|
if (context.error_message.back() != '\n' && context.error_message.back() != '\r')
|
||||||
} catch (qi::expectation_failure<iterator_type> const& x) {
|
context.error_message += '\n';
|
||||||
std::cout << "expected: "; print_info(x.what_);
|
throw std::runtime_error(context.error_message);
|
||||||
std::cout << "got: \"" << std::string(x.first, x.last) << '"' << std::endl;
|
|
||||||
}
|
}
|
||||||
|
return output;
|
||||||
if (r && iter == end)
|
|
||||||
{
|
|
||||||
// std::cout << "-------------------------\n";
|
|
||||||
// std::cout << "Parsing succeeded\n";
|
|
||||||
// std::cout << "result = " << result << std::endl;
|
|
||||||
// std::cout << "-------------------------\n";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::string rest(iter, end);
|
|
||||||
std::cout << "-------------------------\n";
|
|
||||||
std::cout << "Parsing failed\n";
|
|
||||||
std::cout << "stopped at: \" " << rest << "\"\n";
|
|
||||||
std::cout << "source: \n" << templ;
|
|
||||||
std::cout << "-------------------------\n";
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1075,7 +1075,11 @@ void Print::_make_wipe_tower()
|
||||||
std::string Print::output_filename()
|
std::string Print::output_filename()
|
||||||
{
|
{
|
||||||
this->placeholder_parser.update_timestamp();
|
this->placeholder_parser.update_timestamp();
|
||||||
return this->placeholder_parser.process(this->config.output_filename_format.value, 0);
|
try {
|
||||||
|
return this->placeholder_parser.process(this->config.output_filename_format.value, 0);
|
||||||
|
} catch (std::runtime_error &err) {
|
||||||
|
throw std::runtime_error(std::string("Failed processing of the output_filename_format template.\n") + err.what());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Print::output_filepath(const std::string &path)
|
std::string Print::output_filepath(const std::string &path)
|
||||||
|
|
|
@ -17,7 +17,14 @@
|
||||||
%name{Slic3r::GCode} class GCode {
|
%name{Slic3r::GCode} class GCode {
|
||||||
GCode();
|
GCode();
|
||||||
~GCode();
|
~GCode();
|
||||||
std::string do_export(Print *print, const char *path);
|
void do_export(Print *print, const char *path)
|
||||||
|
%code%{
|
||||||
|
try {
|
||||||
|
THIS->do_export(print, path);
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
croak(e.what());
|
||||||
|
}
|
||||||
|
%};
|
||||||
|
|
||||||
Ref<Pointf> origin()
|
Ref<Pointf> origin()
|
||||||
%code{% RETVAL = &(THIS->origin()); %};
|
%code{% RETVAL = &(THIS->origin()); %};
|
||||||
|
|
|
@ -14,5 +14,11 @@
|
||||||
%code%{ THIS->apply_config(*config); %};
|
%code%{ THIS->apply_config(*config); %};
|
||||||
void set(std::string key, int value);
|
void set(std::string key, int value);
|
||||||
std::string process(std::string str) const
|
std::string process(std::string str) const
|
||||||
%code%{ RETVAL = THIS->process(str, 0); %};
|
%code%{
|
||||||
|
try {
|
||||||
|
RETVAL = THIS->process(str, 0);
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
croak(e.what());
|
||||||
|
}
|
||||||
|
%};
|
||||||
};
|
};
|
||||||
|
|
|
@ -206,8 +206,14 @@ _constant()
|
||||||
double max_allowed_layer_height() const;
|
double max_allowed_layer_height() const;
|
||||||
bool has_support_material() const;
|
bool has_support_material() const;
|
||||||
void auto_assign_extruders(ModelObject* model_object);
|
void auto_assign_extruders(ModelObject* model_object);
|
||||||
std::string output_filename();
|
std::string output_filepath(std::string path = "")
|
||||||
std::string output_filepath(std::string path = "");
|
%code%{
|
||||||
|
try {
|
||||||
|
RETVAL = THIS->output_filepath(path);
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
croak(e.what());
|
||||||
|
}
|
||||||
|
%};
|
||||||
|
|
||||||
void add_model_object(ModelObject* model_object, int idx = -1);
|
void add_model_object(ModelObject* model_object, int idx = -1);
|
||||||
bool apply_config(DynamicPrintConfig* config)
|
bool apply_config(DynamicPrintConfig* config)
|
||||||
|
|
Loading…
Reference in a new issue