diff options
Diffstat (limited to 'xs/src/libslic3r/GCode.cpp')
-rw-r--r-- | xs/src/libslic3r/GCode.cpp | 384 |
1 files changed, 268 insertions, 116 deletions
diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index cf826ff68..0e85c55a8 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -4,6 +4,7 @@ #include "Geometry.hpp" #include "GCode/PrintExtents.hpp" #include "GCode/WipeTowerPrusaMM.hpp" +#include "Utils.hpp" #include <algorithm> #include <cstdlib> @@ -11,7 +12,6 @@ #include <boost/algorithm/string.hpp> #include <boost/algorithm/string/find.hpp> -#include <boost/date_time/local_time/local_time.hpp> #include <boost/foreach.hpp> #include <boost/nowide/iostream.hpp> @@ -30,6 +30,13 @@ #include <assert.h> namespace Slic3r { + +// Only add a newline in case the current G-code does not end with a newline. +static inline void check_add_eol(std::string &gcode) +{ + if (! gcode.empty() && gcode.back() != '\n') + gcode += '\n'; +} // Plan a travel move while minimizing the number of perimeter crossings. // point is in unscaled coordinates, in the coordinate system of the current active object @@ -157,6 +164,8 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T { std::string gcode; + // Disable linear advance for the wipe tower operations. + gcode += "M900 K0\n"; // Move over the wipe tower. // Retract for a tool change, using the toolchange retract value and setting the priming extra length. gcode += gcodegen.retract(true); @@ -171,8 +180,17 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T // Inform the G-code writer about the changes done behind its back. gcode += tcr.gcode; // Let the m_writer know the current extruder_id, but ignore the generated G-code. - if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id)) + if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id)) { gcodegen.writer().toolchange(new_extruder_id); + // Append the filament start G-code. + const std::string &start_filament_gcode = gcodegen.config().start_filament_gcode.get_at(new_extruder_id); + if (! start_filament_gcode.empty()) { + // Process the start_filament_gcode for the active filament only. + gcodegen.placeholder_parser().set("current_extruder", new_extruder_id); + gcode += gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id); + check_add_eol(gcode); + } + } // A phony move to the end position at the wipe tower. gcodegen.writer().travel_to_xy(Pointf(tcr.end_pos.x, tcr.end_pos.y)); gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, tcr.end_pos)); @@ -199,15 +217,18 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen) std::string gcode; if (&m_priming != nullptr && ! m_priming.extrusions.empty()) { + // Disable linear advance for the wipe tower operations. + gcode += "M900 K0\n"; // Let the tool change be executed by the wipe tower class. // Inform the G-code writer about the changes done behind its back. gcode += m_priming.gcode; // Let the m_writer know the current extruder_id, but ignore the generated G-code. - gcodegen.writer().toolchange(m_priming.extrusions.back().tool); + unsigned int current_extruder_id = m_priming.extrusions.back().tool; + gcodegen.writer().toolchange(current_extruder_id); + gcodegen.placeholder_parser().set("current_extruder", current_extruder_id); // A phony move to the end position at the wipe tower. gcodegen.writer().travel_to_xy(Pointf(m_priming.end_pos.x, m_priming.end_pos.y)); gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, m_priming.end_pos)); - // Prepare a future wipe. gcodegen.m_wipe.path.points.clear(); // Start the wipe at the current position. @@ -220,19 +241,6 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen) return gcode; } -std::string WipeTowerIntegration::prime_single_color_print(const Print & /* print */, unsigned int initial_tool, GCode & /* gcodegen */) -{ - std::string gcode = "\ -G1 Z0.250 F7200.000\n\ -G1 X50.0 E80.0 F1000.0\n\ -G1 X160.0 E20.0 F1000.0\n\ -G1 Z0.200 F7200.000\n\ -G1 X220.0 E13 F1000.0\n\ -G1 X240.0 E0 F1000.0\n\ -G1 E-4 F1000.0\n"; - return gcode; -} - std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, bool finish_layer) { std::string gcode; @@ -313,10 +321,15 @@ inline void write_format(FILE* file, const char* format, ...) write(file, buffer); } +// Write a string into a file. Add a newline, if the string does not end with a newline already. +// Used to export a custom G-code section processed by the PlaceholderParser. inline void writeln(FILE *file, const std::string &what) { if (! what.empty()) { - write_format(file, "%s\n", what.c_str()); + if (what.back() != '\n') + write_format(file, "%s\n", what.c_str()); + else + write(file, what); } } @@ -399,7 +412,7 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec 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. boost::nowide::remove(path); @@ -409,24 +422,37 @@ bool GCode::do_export(Print *print, const char *path) FILE *file = boost::nowide::fopen(path_tmp.c_str(), "wb"); 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); + fflush(file); + if (ferror(file)) { + fclose(file); + boost::nowide::remove(path_tmp.c_str()); + throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n"); + } fclose(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 (! 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 (! result) - boost::nowide::remove(path_tmp.c_str()); - - return result; + 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) { // resets time estimator m_time_estimator.reset(); @@ -515,15 +541,7 @@ bool GCode::_do_export(Print &print, FILE *file) m_enable_extrusion_role_markers = (bool)m_pressure_equalizer; // Write information on the generator. - { - const auto now = boost::posix_time::second_clock::local_time(); - const auto date = now.date(); - fprintf(file, "; generated by Slic3r %s on %04d-%02d-%02d at %02d:%02d:%02d\n\n", - SLIC3R_VERSION, - // Local date in an ANSII format. - int(now.date().year()), int(now.date().month()), int(now.date().day()), - int(now.time_of_day().hours()), int(now.time_of_day().minutes()), int(now.time_of_day().seconds())); - } + fprintf(file, "; %s\n\n", Slic3r::header_slic3r_generated().c_str()); // Write notes (content of the Print Settings tab -> Notes) { std::list<std::string> lines; @@ -541,6 +559,7 @@ bool GCode::_do_export(Print &print, FILE *file) { const PrintObject *first_object = print.objects.front(); const double layer_height = first_object->config.layer_height.value; + const double first_layer_height = first_object->config.first_layer_height.get_abs_value(layer_height); for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) { auto region = print.regions[region_id]; fprintf(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width); @@ -551,7 +570,7 @@ bool GCode::_do_export(Print &print, FILE *file) if (print.has_support_material()) fprintf(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width); if (print.config.first_layer_extrusion_width.value > 0) - fprintf(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, layer_height, false, true, -1., *first_object).width); + fprintf(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width); fprintf(file, "\n"); } } @@ -566,6 +585,7 @@ bool GCode::_do_export(Print &print, FILE *file) unsigned int initial_extruder_id = (unsigned int)-1; unsigned int final_extruder_id = (unsigned int)-1; size_t initial_print_object_id = 0; + bool has_wipe_tower = false; if (print.config.complete_objects.value) { // Find the 1st printing object, find its tool ordering and the initial extruder ID. for (; initial_print_object_id < print.objects.size(); ++initial_print_object_id) { @@ -580,6 +600,7 @@ bool GCode::_do_export(Print &print, FILE *file) ToolOrdering(print, initial_extruder_id) : print.m_tool_ordering; initial_extruder_id = tool_ordering.first_extruder(); + has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower(); } if (initial_extruder_id == (unsigned int)-1) { // Nothing to print! @@ -596,28 +617,35 @@ bool GCode::_do_export(Print &print, FILE *file) if (! print.config.cooling.get_at(initial_extruder_id) || print.config.disable_fan_first_layers.get_at(initial_extruder_id)) write(file, m_writer.set_fan(0, true)); - // Set bed temperature if the start G-code does not contain any bed temp control G-codes. - { - // Always call m_writer.set_bed_temperature() so it will set the internal "current" state of the bed temp as if - // the custom start G-code emited these. - //FIXME Should one parse the custom G-code to initialize the "current" bed temp state at m_writer? - std::string gcode = m_writer.set_bed_temperature(print.config.first_layer_bed_temperature.get_at(initial_extruder_id), true); - if (boost::ifind_first(print.config.start_gcode.value, std::string("M140")).empty() && - boost::ifind_first(print.config.start_gcode.value, std::string("M190")).empty()) - write(file, gcode); - } - - // Set extruder(s) temperature before and after start G-code. - this->_print_first_layer_extruder_temperatures(file, print, initial_extruder_id, false); // Let the start-up script prime the 1st printing tool. m_placeholder_parser.set("initial_tool", initial_extruder_id); m_placeholder_parser.set("initial_extruder", initial_extruder_id); m_placeholder_parser.set("current_extruder", initial_extruder_id); - writeln(file, m_placeholder_parser.process(print.config.start_gcode.value, initial_extruder_id)); + // Useful for sequential prints. + 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. + m_placeholder_parser.set("has_wipe_tower", has_wipe_tower); + 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. + this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true); + // Set extruder(s) temperature before and after start G-code. + this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, false); + // Write the custom start G-code + writeln(file, start_gcode); // Process filament-specific gcode in extruder order. - 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()))); - this->_print_first_layer_extruder_temperatures(file, print, initial_extruder_id, true); + if (print.config.single_extruder_multi_material) { + if (has_wipe_tower) { + // Wipe tower will control the extruder switching, it will call the start_filament_gcode. + } else { + // Only initialize the initial extruder. + writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config.start_filament_gcode.values[initial_extruder_id], initial_extruder_id)); + } + } else { + for (const std::string &start_gcode : print.config.start_filament_gcode.values) + 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); // Set other general things. write(file, this->preamble()); @@ -707,9 +735,12 @@ bool GCode::_do_export(Print &print, FILE *file) // Ff we are printing the bottom layer of an object, and we have already finished // another one, set first layer temperatures. This happens before the Z move // is triggered, so machine has more time to reach such temperatures. - write(file, m_writer.set_bed_temperature(print.config.first_layer_bed_temperature.get_at(initial_extruder_id))); - // Set first layer extruder. - this->_print_first_layer_extruder_temperatures(file, print, initial_extruder_id, false); + m_placeholder_parser.set("current_object_idx", int(finished_objects)); + 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. + 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); + writeln(file, between_objects_gcode); } // Reset the cooling buffer internal state (the current position, feed rate, accelerations). m_cooling_buffer->reset(); @@ -740,33 +771,29 @@ bool GCode::_do_export(Print &print, FILE *file) // All extrusion moves with the same top layer height are extruded uninterrupted. std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print); // Prusa Multi-Material wipe tower. - if (print.has_wipe_tower() && ! layers_to_print.empty()) { - if (tool_ordering.has_wipe_tower()) { - m_wipe_tower.reset(new WipeTowerIntegration(print.config, *print.m_wipe_tower_priming.get(), print.m_wipe_tower_tool_changes, *print.m_wipe_tower_final_purge.get())); - write(file, m_wipe_tower->prime(*this)); - // Verify, whether the print overaps the priming extrusions. - BoundingBoxf bbox_print(get_print_extrusions_extents(print)); - coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON; - for (const PrintObject *print_object : print.objects) - bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz)); - bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz)); - BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print)); - bbox_prime.offset(0.5f); - // Beep for 500ms, tone 800Hz. Yet better, play some Morse. - write(file, this->retract()); - write(file, "M300 S800 P500\n"); - if (bbox_prime.overlap(bbox_print)) { - // Wait for the user to remove the priming extrusions, otherwise they would - // get covered by the print. - write(file, "M1 Remove priming towers and click button.\n"); + if (has_wipe_tower && ! layers_to_print.empty()) { + m_wipe_tower.reset(new WipeTowerIntegration(print.config, *print.m_wipe_tower_priming.get(), print.m_wipe_tower_tool_changes, *print.m_wipe_tower_final_purge.get())); + write(file, m_wipe_tower->prime(*this)); + // Verify, whether the print overaps the priming extrusions. + BoundingBoxf bbox_print(get_print_extrusions_extents(print)); + coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON; + for (const PrintObject *print_object : print.objects) + bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz)); + bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz)); + BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print)); + bbox_prime.offset(0.5f); + // Beep for 500ms, tone 800Hz. Yet better, play some Morse. + write(file, this->retract()); + write(file, "M300 S800 P500\n"); + if (bbox_prime.overlap(bbox_print)) { + // Wait for the user to remove the priming extrusions, otherwise they would + // get covered by the print. + write(file, "M1 Remove priming towers and click button.\n"); + } else { + // Just wait for a bit to let the user check, that the priming succeeded. + //TODO Add a message explaining what the printer is waiting for. This needs a firmware fix. + write(file, "M1 S10\n"); } - else { - // Just wait for a bit to let the user check, that the priming succeeded. - //TODO Add a message explaining what the printer is waiting for. This needs a firmware fix. - write(file, "M1 S10\n"); - } - } else - write(file, WipeTowerIntegration::prime_single_color_print(print, initial_extruder_id, *this)); } // Extrude the layers. for (auto &layer : layers_to_print) { @@ -786,9 +813,14 @@ bool GCode::_do_export(Print &print, FILE *file) write(file, this->retract()); write(file, m_writer.set_fan(false)); // Process filament-specific gcode in extruder order. - 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, m_placeholder_parser.process(print.config.end_gcode, m_writer.extruder()->id())); + if (print.config.single_extruder_multi_material) { + // Process the end_filament_gcode for the active filament only. + 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 { + for (const std::string &end_gcode : print.config.end_filament_gcode.values) + writeln(file, this->placeholder_parser_process("end_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front()))); + } + 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.postamble()); @@ -830,22 +862,117 @@ bool GCode::_do_export(Print &print, FILE *file) for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++ i) { StaticPrintConfig *cfg = configs[i]; for (const std::string &key : cfg->keys()) - fprintf(file, "; %s = %s\n", key.c_str(), cfg->serialize(key).c_str()); + if (key != "compatible_printers") + 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("\n!!!!! 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\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. +// Returns true if one of the temp commands are found, and try to parse the target temperature value into temp_out. +static bool custom_gcode_sets_temperature(const std::string &gcode, const int mcode_set_temp_dont_wait, const int mcode_set_temp_and_wait, int &temp_out) +{ + temp_out = -1; + if (gcode.empty()) + return false; + + const char *ptr = gcode.data(); + bool temp_set_by_gcode = false; + while (*ptr != 0) { + // Skip whitespaces. + for (; *ptr == ' ' || *ptr == '\t'; ++ ptr); + if (*ptr == 'M') { + // Line starts with 'M'. It is a machine command. + ++ ptr; + // Parse the M code value. + char *endptr = nullptr; + int mcode = int(strtol(ptr, &endptr, 10)); + if (endptr != nullptr && endptr != ptr && (mcode == mcode_set_temp_dont_wait || mcode == mcode_set_temp_and_wait)) { + // M104/M109 or M140/M190 found. + ptr = endptr; + // Let the caller know that the custom G-code sets the temperature. + temp_set_by_gcode = true; + // Now try to parse the temperature value. + // While not at the end of the line: + while (strchr(";\r\n\0", *ptr) == nullptr) { + // Skip whitespaces. + for (; *ptr == ' ' || *ptr == '\t'; ++ ptr); + if (*ptr == 'S') { + // Skip whitespaces. + for (++ ptr; *ptr == ' ' || *ptr == '\t'; ++ ptr); + // Parse an int. + endptr = nullptr; + long temp_parsed = strtol(ptr, &endptr, 10); + if (endptr > ptr) { + ptr = endptr; + temp_out = temp_parsed; + } + } else { + // Skip this word. + for (; strchr(" \t;\r\n\0", *ptr) == nullptr; ++ ptr); + } + } + } + } + // Skip the rest of the line. + for (; *ptr != 0 && *ptr != '\r' && *ptr != '\n'; ++ ptr); + // Skip the end of line indicators. + for (; *ptr == '\r' || *ptr == '\n'; ++ ptr); + } + return temp_set_by_gcode; +} + +// Write 1st layer bed temperatures into the G-code. +// Only do that if the start G-code does not already contain any M-code controlling an extruder temperature. +// M140 - Set Extruder Temperature +// M190 - Set Extruder Temperature and Wait +void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) +{ + // Initial bed temperature based on the first extruder. + int temp = print.config.first_layer_bed_temperature.get_at(first_printing_extruder_id); + // Is the bed temperature set by the provided custom G-code? + int temp_by_gcode = -1; + bool temp_set_by_gcode = custom_gcode_sets_temperature(gcode, 140, 190, temp_by_gcode); + if (temp_set_by_gcode && temp_by_gcode >= 0 && temp_by_gcode < 1000) + temp = temp_by_gcode; + // Always call m_writer.set_bed_temperature() so it will set the internal "current" state of the bed temp as if + // the custom start G-code emited these. + std::string set_temp_gcode = m_writer.set_bed_temperature(temp, wait); + if (! temp_set_by_gcode) + write(file, set_temp_gcode); } // Write 1st layer extruder temperatures into the G-code. // Only do that if the start G-code does not already contain any M-code controlling an extruder temperature. -// FIXME this does not work correctly for multi-extruder, single heater configuration as it emits multiple preheat commands for the same heater. // M104 - Set Extruder Temperature // M109 - Set Extruder Temperature and Wait -void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, unsigned int first_printing_extruder_id, bool wait) +void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) { - if (boost::ifind_first(print.config.start_gcode.value, std::string("M104")).empty() && - boost::ifind_first(print.config.start_gcode.value, std::string("M109")).empty()) { + // Is the bed temperature set by the provided custom G-code? + int temp_by_gcode = -1; + if (custom_gcode_sets_temperature(gcode, 104, 109, temp_by_gcode)) { + // Set the extruder temperature at m_writer, but throw away the generated G-code as it will be written with the custom G-code. + int temp = print.config.first_layer_temperature.get_at(first_printing_extruder_id); + if (temp_by_gcode >= 0 && temp_by_gcode < 1000) + temp = temp_by_gcode; + m_writer.set_temperature(temp_by_gcode, wait, first_printing_extruder_id); + } else { + // Custom G-code does not set the extruder temperature. Do it now. if (print.config.single_extruder_multi_material.value) { // Set temperature of the first printing extruder only. int temp = print.config.first_layer_temperature.get_at(first_printing_extruder_id); @@ -953,18 +1080,22 @@ void GCode::process_layer( // Set new layer - this will change Z and force a retraction if retract_layer_change is enabled. if (! print.config.before_layer_gcode.value.empty()) { - PlaceholderParser pp(m_placeholder_parser); - pp.set("layer_num", m_layer_index + 1); - pp.set("layer_z", print_z); - gcode += pp.process(print.config.before_layer_gcode.value, m_writer.extruder()->id()) + "\n"; + DynamicConfig config; + config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1)); + config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); + gcode += this->placeholder_parser_process("before_layer_gcode", + print.config.before_layer_gcode.value, m_writer.extruder()->id(), &config) + + "\n"; } gcode += this->change_layer(print_z); // this will increase m_layer_index m_layer = &layer; if (! print.config.layer_gcode.value.empty()) { - PlaceholderParser pp(m_placeholder_parser); - pp.set("layer_num", m_layer_index); - pp.set("layer_z", print_z); - gcode += pp.process(print.config.layer_gcode.value, m_writer.extruder()->id()) + "\n"; + DynamicConfig config; + config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); + config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); + gcode += this->placeholder_parser_process("layer_gcode", + print.config.layer_gcode.value, m_writer.extruder()->id(), &config) + + "\n"; } if (! first_layer && ! m_second_layer_things_done) { @@ -2141,13 +2272,14 @@ GCode::retract(bool toolchange) std::string GCode::set_extruder(unsigned int extruder_id) { - m_placeholder_parser.set("current_extruder", extruder_id); if (!m_writer.need_toolchange(extruder_id)) return ""; // if we are running a single-extruder setup, just set the extruder and return nothing - if (!m_writer.multiple_extruders) + if (!m_writer.multiple_extruders) { + m_placeholder_parser.set("current_extruder", extruder_id); return m_writer.toolchange(extruder_id); + } // prepend retraction on the current extruder std::string gcode = this->retract(true); @@ -2155,21 +2287,41 @@ std::string GCode::set_extruder(unsigned int extruder_id) // Always reset the extrusion path, even if the tool change retract is set to zero. m_wipe.reset_path(); - // append custom toolchange G-code - if (m_writer.extruder() != nullptr && !m_config.toolchange_gcode.value.empty()) { - PlaceholderParser pp = m_placeholder_parser; - pp.set("previous_extruder", m_writer.extruder()->id()); - pp.set("next_extruder", extruder_id); - gcode += pp.process(m_config.toolchange_gcode.value, extruder_id) + '\n'; + if (m_writer.extruder() != nullptr) { + // Process the custom end_filament_gcode in case of single_extruder_multi_material. + 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); + if (m_config.single_extruder_multi_material && ! end_filament_gcode.empty()) { + gcode += placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id); + check_add_eol(gcode); + } + } + + m_placeholder_parser.set("current_extruder", extruder_id); + + if (m_writer.extruder() != nullptr && ! m_config.toolchange_gcode.value.empty()) { + // Process the custom toolchange_gcode. + DynamicConfig config; + config.set_key_value("previous_extruder", new ConfigOptionInt((int)m_writer.extruder()->id())); + config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id)); + gcode += placeholder_parser_process("toolchange_gcode", m_config.toolchange_gcode.value, extruder_id, &config); + check_add_eol(gcode); } - // if ooze prevention is enabled, park current extruder in the nearest - // standby point and set it to the standby temperature + // If ooze prevention is enabled, park current extruder in the nearest + // standby point and set it to the standby temperature. if (m_ooze_prevention.enable && m_writer.extruder() != nullptr) gcode += m_ooze_prevention.pre_toolchange(*this); - // append the toolchange command + // Append the toolchange command. gcode += m_writer.toolchange(extruder_id); - // set the new extruder to the operating temperature + // Append the filament start G-code for single_extruder_multi_material. + 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()) { + // Process the start_filament_gcode for the active filament only. + gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id); + check_add_eol(gcode); + } + // Set the new extruder to the operating temperature. if (m_ooze_prevention.enable) gcode += m_ooze_prevention.post_toolchange(*this); |