diff --git a/resources/profiles/Creality/machine/Creality Ender-3 V3 SE 0.2 nozzle.json b/resources/profiles/Creality/machine/Creality Ender-3 V3 SE 0.2 nozzle.json index d11c81c658..61a5cdf05f 100644 --- a/resources/profiles/Creality/machine/Creality Ender-3 V3 SE 0.2 nozzle.json +++ b/resources/profiles/Creality/machine/Creality Ender-3 V3 SE 0.2 nozzle.json @@ -116,6 +116,7 @@ "default_filament_profile": [ "Creality Generic PLA @Ender-3V3-all" ], + "file_start_gcode": ";FLAVOR:Marlin\\n;TIME:{print_time_sec}\\n;Filament used:{used_filament_length}m\\n;Layer height:{layer_height}", "machine_start_gcode": "M220 S100 ;Reset Feedrate \nM221 S100 ;Reset Flowrate \n \nM104 S[nozzle_temperature_initial_layer] ;Set final nozzle temp \nM190 S[bed_temperature_initial_layer_single] ;Set and wait for bed temp to stabilize \nG28 ;Home \nG92 E0 ;Reset Extruder \nG1 Z2.0 F3000 ;Move Z Axis up \nG1 X-2.1 Y20 Z0.28 F5000.0 ;Move to start position \nM109 S[nozzle_temperature_initial_layer] ;Wait for nozzle temp to stabilize \nG1 X-2.1 Y145.0 Z0.28 F1500.0 E15 ;Draw the first line \nG1 X-2.4 Y145.0 Z0.28 F5000.0 ;Move to side a little \nG1 X-2.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line \nG92 E0 ;Reset Extruder \nG1 E-1.0000 F1800 ;Retract a bit \nG1 Z2.0 F3000 ;Move Z Axis up \nG1 E0.0000 F1800", "machine_end_gcode": "G91 ;Relative positionning \nG1 E-2 F2700 ;Retract a bit \nG1 E-2 Z0.2 F2400 ;Retract and raise Z \nG1 X5 Y5 F3000 ;Wipe out \nG1 Z10 ;Raise Z more \nG90 ;Absolute positionning \n \nG1 X0 Y220 ;Present print \nM106 S0 ;Turn-off fan \nM104 S0 ;Turn-off hotend \nM140 S0 ;Turn-off bed \n \nM84 X Y E ;Disable all steppers but Z", "scan_first_layer": "0", diff --git a/resources/profiles/Creality/machine/Creality Ender-3 V3 SE 0.4 nozzle.json b/resources/profiles/Creality/machine/Creality Ender-3 V3 SE 0.4 nozzle.json index 3264d668a0..36874956c6 100644 --- a/resources/profiles/Creality/machine/Creality Ender-3 V3 SE 0.4 nozzle.json +++ b/resources/profiles/Creality/machine/Creality Ender-3 V3 SE 0.4 nozzle.json @@ -116,6 +116,7 @@ "default_filament_profile": [ "Creality Generic PLA @Ender-3V3-all" ], + "file_start_gcode": ";FLAVOR:Marlin\\n;TIME:{print_time_sec}\\n;Filament used:{used_filament_length}m\\n;Layer height:{layer_height}", "machine_start_gcode": "M220 S100 ;Reset Feedrate \nM221 S100 ;Reset Flowrate \n \nM104 S[nozzle_temperature_initial_layer] ;Set final nozzle temp \nM190 S[bed_temperature_initial_layer_single] ;Set and wait for bed temp to stabilize \nG28 ;Home \nG92 E0 ;Reset Extruder \nG1 Z2.0 F3000 ;Move Z Axis up \nG1 X-2.1 Y20 Z0.28 F5000.0 ;Move to start position \nM109 S[nozzle_temperature_initial_layer] ;Wait for nozzle temp to stabilize \nG1 X-2.1 Y145.0 Z0.28 F1500.0 E15 ;Draw the first line \nG1 X-2.4 Y145.0 Z0.28 F5000.0 ;Move to side a little \nG1 X-2.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line \nG92 E0 ;Reset Extruder \nG1 E-1.0000 F1800 ;Retract a bit \nG1 Z2.0 F3000 ;Move Z Axis up \nG1 E0.0000 F1800", "machine_end_gcode": "G91 ;Relative positionning \nG1 E-2 F2700 ;Retract a bit \nG1 E-2 Z0.2 F2400 ;Retract and raise Z \nG1 X5 Y5 F3000 ;Wipe out \nG1 Z10 ;Raise Z more \nG90 ;Absolute positionning \n \nG1 X0 Y220 ;Present print \nM106 S0 ;Turn-off fan \nM104 S0 ;Turn-off hotend \nM140 S0 ;Turn-off bed \n \nM84 X Y E ;Disable all steppers but Z", "scan_first_layer": "0", diff --git a/resources/profiles/Creality/machine/Creality Ender-3 V3 SE 0.6 nozzle.json b/resources/profiles/Creality/machine/Creality Ender-3 V3 SE 0.6 nozzle.json index 5b17184bb0..ea8b406de4 100644 --- a/resources/profiles/Creality/machine/Creality Ender-3 V3 SE 0.6 nozzle.json +++ b/resources/profiles/Creality/machine/Creality Ender-3 V3 SE 0.6 nozzle.json @@ -116,6 +116,7 @@ "default_filament_profile": [ "Creality Generic PLA @Ender-3V3-all" ], + "file_start_gcode": ";FLAVOR:Marlin\\n;TIME:{print_time_sec}\\n;Filament used:{used_filament_length}m\\n;Layer height:{layer_height}", "machine_start_gcode": "M220 S100 ;Reset Feedrate \nM221 S100 ;Reset Flowrate \n \nM104 S[nozzle_temperature_initial_layer] ;Set final nozzle temp \nM190 S[bed_temperature_initial_layer_single] ;Set and wait for bed temp to stabilize \nG28 ;Home \nG92 E0 ;Reset Extruder \nG1 Z2.0 F3000 ;Move Z Axis up \nG1 X-2.1 Y20 Z0.28 F5000.0 ;Move to start position \nM109 S[nozzle_temperature_initial_layer] ;Wait for nozzle temp to stabilize \nG1 X-2.1 Y145.0 Z0.28 F1500.0 E15 ;Draw the first line \nG1 X-2.4 Y145.0 Z0.28 F5000.0 ;Move to side a little \nG1 X-2.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line \nG92 E0 ;Reset Extruder \nG1 E-1.0000 F1800 ;Retract a bit \nG1 Z2.0 F3000 ;Move Z Axis up \nG1 E0.0000 F1800", "machine_end_gcode": "G91 ;Relative positionning \nG1 E-2 F2700 ;Retract a bit \nG1 E-2 Z0.2 F2400 ;Retract and raise Z \nG1 X5 Y5 F3000 ;Wipe out \nG1 Z10 ;Raise Z more \nG90 ;Absolute positionning \n \nG1 X0 Y220 ;Present print \nM106 S0 ;Turn-off fan \nM104 S0 ;Turn-off hotend \nM140 S0 ;Turn-off bed \n \nM84 X Y E ;Disable all steppers but Z", "scan_first_layer": "0", diff --git a/resources/profiles/Creality/machine/Creality Ender-3 V3 SE 0.8 nozzle.json b/resources/profiles/Creality/machine/Creality Ender-3 V3 SE 0.8 nozzle.json index 06a93521fc..7868be9b55 100644 --- a/resources/profiles/Creality/machine/Creality Ender-3 V3 SE 0.8 nozzle.json +++ b/resources/profiles/Creality/machine/Creality Ender-3 V3 SE 0.8 nozzle.json @@ -116,6 +116,7 @@ "default_filament_profile": [ "Creality Generic PLA @Ender-3V3-all" ], + "file_start_gcode": ";FLAVOR:Marlin\\n;TIME:{print_time_sec}\\n;Filament used:{used_filament_length}m\\n;Layer height:{layer_height}", "machine_start_gcode": "M220 S100 ;Reset Feedrate \nM221 S100 ;Reset Flowrate \n \nM104 S[nozzle_temperature_initial_layer] ;Set final nozzle temp \nM190 S[bed_temperature_initial_layer_single] ;Set and wait for bed temp to stabilize \nG28 ;Home \nG92 E0 ;Reset Extruder \nG1 Z2.0 F3000 ;Move Z Axis up \nG1 X-2.1 Y20 Z0.28 F5000.0 ;Move to start position \nM109 S[nozzle_temperature_initial_layer] ;Wait for nozzle temp to stabilize \nG1 X-2.1 Y145.0 Z0.28 F1500.0 E15 ;Draw the first line \nG1 X-2.4 Y145.0 Z0.28 F5000.0 ;Move to side a little \nG1 X-2.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line \nG92 E0 ;Reset Extruder \nG1 E-1.0000 F1800 ;Retract a bit \nG1 Z2.0 F3000 ;Move Z Axis up \nG1 E0.0000 F1800", "machine_end_gcode": "G91 ;Relative positionning \nG1 E-2 F2700 ;Retract a bit \nG1 E-2 Z0.2 F2400 ;Retract and raise Z \nG1 X5 Y5 F3000 ;Wipe out \nG1 Z10 ;Raise Z more \nG90 ;Absolute positionning \n \nG1 X0 Y220 ;Present print \nM106 S0 ;Turn-off fan \nM104 S0 ;Turn-off hotend \nM140 S0 ;Turn-off bed \n \nM84 X Y E ;Disable all steppers but Z", "scan_first_layer": "0", diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 2d1940e448..9b82b60b06 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2313,6 +2313,19 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato if (!print.config().small_area_infill_flow_compensation_model.empty()) m_small_area_infill_flow_compensator = make_unique(print.config()); + // Process file_start_gcode - written at the very top of the file, before any header + { + std::string top_gcode_template = print.config().file_start_gcode.value; + if (!top_gcode_template.empty()) { + DynamicConfig top_config; + top_config.set_key_value("print_time_sec", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Print_Time_Sec_Placeholder))); + top_config.set_key_value("used_filament_length", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Used_Filament_Length_Placeholder))); + std::string top_gcode = print.placeholder_parser().process(top_gcode_template, 0, &top_config); + if (!top_gcode.empty()) + file.writeln(top_gcode); + } + } + // Orca: Don't output Header block if BTT thumbnail is identified in the list // Get the thumbnails value as a string std::string thumbnails_value = print.config().option("thumbnails")->value; @@ -2854,6 +2867,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato this->placeholder_parser().set("hold_chamber_temp_for_flat_print", new ConfigOptionBool(hold_chamber_temp_for_flat_print)); } + this->placeholder_parser().set("print_time_sec", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Print_Time_Sec_Placeholder))); + this->placeholder_parser().set("used_filament_length", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Used_Filament_Length_Placeholder))); std::string machine_start_gcode = this->placeholder_parser_process("machine_start_gcode", print.config().machine_start_gcode.value, initial_extruder_id); if (print.config().gcode_flavor != gcfKlipper) { diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 6332cadb4a..01e26b890d 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -73,7 +73,9 @@ const std::vector GCodeProcessor::Reserved_Tags = { "_DURING_PRINT_EXHAUST_FAN", " WIPE_TOWER_START", " WIPE_TOWER_END", - " PA_CHANGE:" + " PA_CHANGE:", + "@PRINT_TIME_SEC@", + "@USED_FILAMENT_LENGTH@" }; const std::vector GCodeProcessor::Reserved_Tags_compatible = { @@ -94,7 +96,9 @@ const std::vector GCodeProcessor::Reserved_Tags_compatible = { "_DURING_PRINT_EXHAUST_FAN", " WIPE_TOWER_START", " WIPE_TOWER_END", - " PA_CHANGE:" + " PA_CHANGE:", + "@PRINT_TIME_SEC@", + "@USED_FILAMENT_LENGTH@" }; @@ -1101,6 +1105,42 @@ void GCodeProcessor::run_post_process() return ret; }; + // Process inline placeholders (print_time_sec and used_filament_length) + auto process_inline_placeholders = [&](std::string& gcode_line) { + bool processed = false; + + const std::string& print_time_placeholder = reserved_tag(ETags::Print_Time_Sec_Placeholder); + const std::string& used_filament_placeholder = reserved_tag(ETags::Used_Filament_Length_Placeholder); + + // Replace print_time_sec + size_t pos = gcode_line.find(print_time_placeholder); + while (pos != std::string::npos) { + double print_time_sec = m_time_processor.machines[static_cast(PrintEstimatedStatistics::ETimeMode::Normal)].time; + char buf[64]; + sprintf(buf, "%.2f", print_time_sec); + gcode_line.replace(pos, print_time_placeholder.length(), buf); + processed = true; + pos = gcode_line.find(print_time_placeholder, pos + strlen(buf)); + } + + // Replace used_filament_length + pos = gcode_line.find(used_filament_placeholder); + while (pos != std::string::npos) { + double total_filament_mm = 0.0; + for (const auto& mm : filament_mm) { + total_filament_mm += mm; + } + double used_filament_length = total_filament_mm / 1000.0; // Convert mm to m + char buf[64]; + sprintf(buf, "%.2f", used_filament_length); + gcode_line.replace(pos, used_filament_placeholder.length(), buf); + processed = true; + pos = gcode_line.find(used_filament_placeholder, pos + strlen(buf)); + } + + return processed; + }; + // check for temporary lines auto is_temporary_decoration = [](const std::string_view gcode_line) { // remove trailing '\n' @@ -1317,6 +1357,8 @@ void GCodeProcessor::run_post_process() gcode_line.clear(); if (!processed) processed = process_used_filament(gcode_line); + if (!gcode_line.empty()) + process_inline_placeholders(gcode_line); if (!processed && !is_temporary_decoration(gcode_line)) { if (GCodeReader::GCodeLine::cmd_is(gcode_line, "G0") || GCodeReader::GCodeLine::cmd_is(gcode_line, "G1")) { export_line.append_line(gcode_line); diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 126efbc5cb..06a448eac2 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -342,6 +342,8 @@ class Print; Wipe_Tower_Start, Wipe_Tower_End, PA_Change, + Print_Time_Sec_Placeholder, + Used_Filament_Length_Placeholder, }; static const std::string& reserved_tag(ETags tag) { return s_IsBBLPrinter ? Reserved_Tags[static_cast(tag)] : Reserved_Tags_compatible[static_cast(tag)]; } diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index d0353e2ed1..4656665a18 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1008,7 +1008,7 @@ static std::vector s_Preset_printer_options { "printer_technology", "printable_area", "extruder_printable_area", "bed_exclude_area","bed_custom_texture", "bed_custom_model", "gcode_flavor", "fan_kickstart", "fan_speedup_time", "fan_speedup_overhangs", - "single_extruder_multi_material", "manual_filament_change", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "printing_by_object_gcode", "layer_change_gcode", "time_lapse_gcode", "wrapping_detection_gcode", "change_filament_gcode", "change_extrusion_role_gcode", + "single_extruder_multi_material", "manual_filament_change", "file_start_gcode", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "printing_by_object_gcode", "layer_change_gcode", "time_lapse_gcode", "wrapping_detection_gcode", "change_filament_gcode", "change_extrusion_role_gcode", "printer_model", "printer_variant", "printer_extruder_id", "printer_extruder_variant", "extruder_variant_list", "default_nozzle_volume_type", "printable_height", "extruder_printable_height", "extruder_clearance_radius", "extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod", "nozzle_height", "master_extruder_id", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 71fb64cafa..4ca894483d 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -5398,6 +5398,18 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionInt(1)); + def = this->add("file_start_gcode", coString); + def->label = L("File header G-code"); + def->tooltip = L("G-code written at the very top of the output file, before any other content. " + "Useful for adding metadata that printer firmware reads from the first lines of the file " + "(e.g. estimated print time, filament usage). " + "Supports placeholders like {print_time_sec} and {used_filament_length}."); + def->multiline = true; + def->full_width = true; + def->height = 8; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionString("")); + def = this->add("machine_start_gcode", coString); def->label = L("Start G-code"); def->tooltip = L("Start G-code when starting the entire print."); @@ -10299,6 +10311,14 @@ PrintStatisticsConfigDef::PrintStatisticsConfigDef() def = this->add("used_filament", coFloat); def->label = L("Used filament"); def->tooltip = L("Total length of filament used in the print."); + + def = this->add("print_time_sec", coString); + def->label = L("Print time (seconds)"); + def->tooltip = L("Total estimated print time in seconds. Replaced with actual value during post-processing."); + + def = this->add("used_filament_length", coString); + def->label = L("Filament length (meters)"); + def->tooltip = L("Total filament length used in meters. Replaced with actual value during post-processing."); } ObjectsInfoConfigDef::ObjectsInfoConfigDef() @@ -10433,6 +10453,7 @@ OtherPresetsConfigDef::OtherPresetsConfigDef() static std::map s_CustomGcodeSpecificPlaceholders{ // Machine G-code + {"file_start_gcode", {}}, {"machine_start_gcode", {}}, {"machine_end_gcode", {"layer_num", "layer_z", "max_layer_z", "filament_extruder_id"}}, {"before_layer_change_gcode", {"layer_num", "layer_z", "max_layer_z"}}, diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 64fc0ddfbf..edc759d7d6 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1321,6 +1321,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloats, retract_restart_extra)) ((ConfigOptionFloats, retract_restart_extra_toolchange)) ((ConfigOptionFloats, retraction_speed)) + ((ConfigOptionString, file_start_gcode)) ((ConfigOptionString, machine_start_gcode)) ((ConfigOptionStrings, filament_start_gcode)) ((ConfigOptionBool, single_extruder_multi_material)) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 2ef5c7ba12..566aaa93cd 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -4434,6 +4434,17 @@ void TabPrinter::build_fff() const int gcode_field_height = 15; // 150 const int notes_field_height = 25; // 250 page = add_options_page(L("Machine G-code"), "custom-gcode_gcode"); // ORCA: icon only visible on placeholders + optgroup = page->new_optgroup(L("File header G-code"), L"param_gcode", 0); + optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(this, optgroup_title, opt_key, value); + }; + optgroup->edit_custom_gcode = edit_custom_gcode_fn; + option = optgroup->get_option("file_start_gcode"); + option.opt.full_width = true; + option.opt.is_code = true; + option.opt.height = 8; + optgroup->append_single_option_line(option); + optgroup = page->new_optgroup(L("Machine start G-code"), L"param_gcode", 0); optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value);