From 7d0fb4f42a3f6a1f43503a601491071b8e29052c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 18 Oct 2023 10:47:11 +0200 Subject: [PATCH] preheat work - part 1 --- src/libslic3r/GCode.cpp | 1 + src/libslic3r/GCode/GCodeProcessor.cpp | 757 ++++++++++++++++++++++++- src/libslic3r/GCode/GCodeProcessor.hpp | 21 +- src/libslic3r/GCode/WipeTower2.cpp | 44 -- src/libslic3r/GCode/WipeTower2.hpp | 3 - src/libslic3r/GCodeReader.hpp | 10 + src/libslic3r/GCodeWriter.cpp | 44 +- src/libslic3r/GCodeWriter.hpp | 2 + src/libslic3r/Print.cpp | 24 + src/libslic3r/Print.hpp | 17 + src/libslic3r/PrintConfig.cpp | 16 + src/libslic3r/PrintConfig.hpp | 3 + 12 files changed, 866 insertions(+), 76 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 599292acbe..a2d4516022 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1527,6 +1527,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu path_tmp += ".tmp"; m_processor.initialize(path_tmp); + m_processor.set_print(print); GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb"), m_processor); if (! file.is_open()) { BOOST_LOG_TRIVIAL(error) << std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n" << std::endl; diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 648b570d82..a2393383e9 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1,4 +1,5 @@ #include "ExtrusionEntity.hpp" +#include "GCodeWriter.hpp" #include "PrintConfig.hpp" #include "libslic3r/libslic3r.h" #include "libslic3r/Utils.hpp" @@ -20,6 +21,7 @@ #include #include #include +#include #include #if __has_include() @@ -369,7 +371,7 @@ void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks, floa //BBS if (block.flags.prepare_stage) prepare_time += block_time; - g1_times_cache.push_back({ block.g1_line_id, time }); + g1_times_cache.push_back({ block.g1_line_id, block.remaining_internal_g1_lines, time }); // update times for remaining time to printer stop placeholders auto it_stop_time = std::lower_bound(stop_times.begin(), stop_times.end(), block.g1_line_id, [](const StopTime& t, unsigned int value) { return t.g1_line_id < value; }); @@ -941,6 +943,7 @@ void GCodeProcessorResult::reset() { printable_height = 0.0f; settings_ids.reset(); extruders_count = 0; + backtrace_enabled = false; extruder_colors = std::vector(); filament_diameters = std::vector(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER); required_nozzle_HRC = std::vector(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_HRC); @@ -1048,10 +1051,15 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_flavor = config.gcode_flavor; - // BBS + m_single_extruder_multi_material = config.single_extruder_multi_material; + size_t extruders_count = config.filament_diameter.values.size(); m_result.extruders_count = extruders_count; + // Orca: + m_is_XL_printer = is_XL_printer(config); + m_result.backtrace_enabled = m_is_XL_printer || ( !m_single_extruder_multi_material && extruders_count > 1); + m_extruder_offsets.resize(extruders_count); m_extruder_colors.resize(extruders_count); m_result.filament_diameters.resize(extruders_count); @@ -1061,11 +1069,19 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_result.filament_costs.resize(extruders_count); m_result.filament_flow_ratios.resize(extruders_count); m_extruder_temps.resize(extruders_count); + m_extruder_temps_config.resize(extruders_count); + m_extruder_temps_first_layer_config.resize(extruders_count); m_result.nozzle_hrc = static_cast(config.nozzle_hrc.getInt()); m_result.nozzle_type = config.nozzle_type; for (size_t i = 0; i < extruders_count; ++ i) { m_extruder_offsets[i] = to_3d(config.extruder_offset.get_at(i).cast().eval(), 0.f); m_extruder_colors[i] = static_cast(i); + m_extruder_temps_first_layer_config[i] = static_cast(config.nozzle_temperature_initial_layer.get_at(i)); + m_extruder_temps_config[i] = static_cast(config.nozzle_temperature.get_at(i)); + if (m_extruder_temps_config[i] == 0) { + // This means the value should be ignored and first layer temp should be used. + m_extruder_temps_config[i] = m_extruder_temps_first_layer_config[i]; + } m_result.filament_diameters[i] = static_cast(config.filament_diameter.get_at(i)); m_result.required_nozzle_HRC[i] = static_cast(config.required_nozzle_HRC.get_at(i)); m_result.filament_densities[i] = static_cast(config.filament_density.get_at(i)); @@ -1716,6 +1732,9 @@ void GCodeProcessor::finalize(bool post_process) #endif // ENABLE_GCODE_VIEWER_STATISTICS //BBS: update slice warning update_slice_warnings(); + + if (post_process) + run_post_process(); } float GCodeProcessor::get_time(PrintEstimatedStatistics::ETimeMode mode) const @@ -2927,7 +2946,7 @@ void GCodeProcessor::process_G0(const GCodeReader::GCodeLine& line) process_G1(line); } -void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) +void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line, const std::optional& remaining_internal_g1_lines) { float filament_diameter = (static_cast(m_extruder_id) < m_result.filament_diameters.size()) ? m_result.filament_diameters[m_extruder_id] : m_result.filament_diameters.back(); float filament_flowratio = (static_cast(m_extruder_id) < m_result.filament_flow_ratios.size()) ? m_result.filament_flow_ratios[m_extruder_id] : m_result.filament_flow_ratios.back(); @@ -2959,7 +2978,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) type = (delta_pos[Z] == 0.0f) ? EMoveType::Unretract : EMoveType::Travel; else if (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f) type = EMoveType::Extrude; - } + } else if (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f) type = EMoveType::Travel; @@ -3109,6 +3128,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) block.role = (type != EMoveType::Travel || m_extrusion_role == erCustom) ? m_extrusion_role : erNone; block.distance = distance; block.g1_line_id = m_g1_line_id; + block.remaining_internal_g1_lines = remaining_internal_g1_lines.has_value() ? *remaining_internal_g1_lines : 0; block.layer_id = std::max(1, m_layer_id); block.flags.prepare_stage = m_processing_start_custom_gcode; @@ -3163,7 +3183,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) } // calculates block acceleration - float acceleration = + float acceleration = (type == EMoveType::Travel) ? get_travel_acceleration(static_cast(i)) : (is_extrusion_only_move(delta_pos) ? get_retract_acceleration(static_cast(i)) : @@ -3303,10 +3323,10 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) if (!m_seams_detector.has_first_vertex()) { m_seams_detector.set_first_vertex(new_pos); } else if (m_detect_layer_based_on_tag) { - // We may have sloped loop, drop any previous start pos if we have z increment - const std::optional first_vertex = m_seams_detector.get_first_vertex(); - if (new_pos.z() > first_vertex->z()) { - m_seams_detector.set_first_vertex(new_pos); + // We may have sloped loop, drop any previous start pos if we have z increment + const std::optional first_vertex = m_seams_detector.get_first_vertex(); + if (new_pos.z() > first_vertex->z()) { + m_seams_detector.set_first_vertex(new_pos); } } } @@ -4348,6 +4368,725 @@ void GCodeProcessor::process_T(const std::string_view command) } } } +static void update_lines_ends_and_out_file_pos(const std::string& out_string, std::vector& lines_ends, size_t* out_file_pos) +{ + for (size_t i = 0; i < out_string.size(); ++i) { + if (out_string[i] == '\n') + lines_ends.emplace_back((out_file_pos != nullptr) ? *out_file_pos + i + 1 : i + 1); + } + if (out_file_pos != nullptr) + *out_file_pos += out_string.size(); +} + +void GCodeProcessor::run_post_process() +{ + FilePtr in{ boost::nowide::fopen(m_result.filename.c_str(), "rb") }; + if (in.f == nullptr) + throw Slic3r::RuntimeError(std::string("GCode processor post process export failed.\nCannot open file for reading.\n")); + + // temporary file to contain modified gcode + std::string out_path = m_result.filename + ".postprocess"; + FilePtr out{ boost::nowide::fopen(out_path.c_str(), "wb") }; + if (out.f == nullptr) + throw Slic3r::RuntimeError(std::string("GCode processor post process export failed.\nCannot open file for writing.\n")); + + std::vector filament_mm(m_result.extruders_count, 0.0); + std::vector filament_cm3(m_result.extruders_count, 0.0); + std::vector filament_g(m_result.extruders_count, 0.0); + std::vector filament_cost(m_result.extruders_count, 0.0); + + double filament_total_g = 0.0; + double filament_total_cost = 0.0; + + for (const auto& [id, volume] : m_result.print_statistics.total_volumes_per_extruder) { + filament_mm[id] = volume / (static_cast(M_PI) * sqr(0.5 * m_result.filament_diameters[id])); + filament_cm3[id] = volume * 0.001; + filament_g[id] = filament_cm3[id] * double(m_result.filament_densities[id]); + filament_cost[id] = filament_g[id] * double(m_result.filament_costs[id]) * 0.001; + filament_total_g += filament_g[id]; + filament_total_cost += filament_cost[id]; + } + + double total_g_wipe_tower = m_print->print_statistics().total_wipe_tower_filament; + + + auto time_in_minutes = [](float time_in_seconds) { + assert(time_in_seconds >= 0.f); + return int((time_in_seconds + 0.5f) / 60.0f); + }; + + auto time_in_last_minute = [](float time_in_seconds) { + assert(time_in_seconds <= 60.0f); + return time_in_seconds / 60.0f; + }; + + auto format_line_M73_main = [](const std::string& mask, int percent, int time) { + char line_M73[64]; + sprintf(line_M73, mask.c_str(), + std::to_string(percent).c_str(), + std::to_string(time).c_str()); + return std::string(line_M73); + }; + + auto format_line_M73_stop_int = [](const std::string& mask, int time) { + char line_M73[64]; + sprintf(line_M73, mask.c_str(), std::to_string(time).c_str()); + return std::string(line_M73); + }; + + auto format_time_float = [](float time) { + return Slic3r::float_to_string_decimal_point(time, 2); + }; + + auto format_line_M73_stop_float = [format_time_float](const std::string& mask, float time) { + char line_M73[64]; + sprintf(line_M73, mask.c_str(), format_time_float(time).c_str()); + return std::string(line_M73); + }; + + std::string gcode_line; + size_t g1_lines_counter = 0; + // keeps track of last exported pair + std::array, static_cast(PrintEstimatedStatistics::ETimeMode::Count)> last_exported_main; + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { + last_exported_main[i] = { 0, time_in_minutes(m_time_processor.machines[i].time) }; + } + + // keeps track of last exported remaining time to next printer stop + std::array(PrintEstimatedStatistics::ETimeMode::Count)> last_exported_stop; + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { + last_exported_stop[i] = time_in_minutes(m_time_processor.machines[i].time); + } + + // Helper class to modify and export gcode to file + class ExportLines + { + public: + struct Backtrace + { + float time{ 60.0f }; + unsigned int steps{ 10 }; + float time_step() const { return time / float(steps); } + }; + + enum class EWriteType + { + BySize, + ByTime + }; + + private: + struct LineData + { + std::string line; + std::array(PrintEstimatedStatistics::ETimeMode::Count)> times{ 0.0f, 0.0f }; + }; + + enum ETimeMode + { + Normal = static_cast(PrintEstimatedStatistics::ETimeMode::Normal), + Stealth = static_cast(PrintEstimatedStatistics::ETimeMode::Stealth) + }; + +#ifndef NDEBUG + class Statistics + { + ExportLines& m_parent; + size_t m_max_size{ 0 }; + size_t m_lines_count{ 0 }; + size_t m_max_lines_count{ 0 }; + + public: + explicit Statistics(ExportLines& parent) + : m_parent(parent) + {} + + void add_line(size_t line_size) { + ++m_lines_count; + m_max_size = std::max(m_max_size, m_parent.get_size() + line_size); + m_max_lines_count = std::max(m_max_lines_count, m_lines_count); + } + + void remove_line() { --m_lines_count; } + void remove_all_lines() { m_lines_count = 0; } + }; + + Statistics m_statistics; +#endif // NDEBUG + + EWriteType m_write_type{ EWriteType::BySize }; + // Time machines containing g1 times cache + const std::array(PrintEstimatedStatistics::ETimeMode::Count)>& m_machines; + // Current time + std::array(PrintEstimatedStatistics::ETimeMode::Count)> m_times{ 0.0f, 0.0f }; + // Current size in bytes + size_t m_size{ 0 }; + + // gcode lines cache + std::deque m_lines; + size_t m_added_lines_counter{ 0 }; + // map of gcode line ids from original to final + // used to update m_result.moves[].gcode_id + std::vector> m_gcode_lines_map; + + size_t m_times_cache_id{ 0 }; + size_t m_out_file_pos{ 0 }; + + + public: + ExportLines(EWriteType type, + const std::array(PrintEstimatedStatistics::ETimeMode::Count)>& machines) +#ifndef NDEBUG + : m_statistics(*this), m_write_type(type), m_machines(machines) {} +#else + : m_write_type(type), m_machines(machines) {} +#endif // NDEBUG + + // return: number of internal G1 lines (from G2/G3 splitting) processed + unsigned int update(const std::string& line, size_t lines_counter, size_t g1_lines_counter) { + unsigned int ret = 0; + m_gcode_lines_map.push_back({ lines_counter, 0 }); + + if (GCodeReader::GCodeLine::cmd_is(line, "G0") || + GCodeReader::GCodeLine::cmd_is(line, "G1") || + GCodeReader::GCodeLine::cmd_is(line, "G2") || + GCodeReader::GCodeLine::cmd_is(line, "G3") || + GCodeReader::GCodeLine::cmd_is(line, "G28")) + ++g1_lines_counter; + else + return ret; + + auto init_it = m_machines[Normal].g1_times_cache.begin() + m_times_cache_id; + auto it = init_it; + while (it != m_machines[Normal].g1_times_cache.end() && it->id < g1_lines_counter) { + ++it; + ++m_times_cache_id; + } + + if (it == m_machines[Normal].g1_times_cache.end() || it->id > g1_lines_counter) + return ret; + + // search for internal G1 lines + if (GCodeReader::GCodeLine::cmd_is(line, "G2") || GCodeReader::GCodeLine::cmd_is(line, "G3")) { + while (it != m_machines[Normal].g1_times_cache.end() && it->remaining_internal_g1_lines > 0) { + ++it; + ++m_times_cache_id; + ++g1_lines_counter; + ++ret; + } + } + + if (it != m_machines[Normal].g1_times_cache.end() && it->id == g1_lines_counter) { + m_times[Normal] = it->elapsed_time; + if (!m_machines[Stealth].g1_times_cache.empty()) + m_times[Stealth] = (m_machines[Stealth].g1_times_cache.begin() + std::distance(m_machines[Normal].g1_times_cache.begin(), it))->elapsed_time; + } + + return ret; + } + + // add the given gcode line to the cache + void append_line(const std::string& line) { + m_lines.push_back({ line, m_times }); +#ifndef NDEBUG + m_statistics.add_line(line.length()); +#endif // NDEBUG + m_size += line.length(); + ++m_added_lines_counter; + assert(!m_gcode_lines_map.empty()); + m_gcode_lines_map.back().second = m_added_lines_counter; + } + + // Insert the gcode lines required by the command cmd by backtracing into the cache + void insert_lines(const Backtrace& backtrace, const std::string& cmd, + std::function&)> line_inserter, + std::function line_replacer) { + assert(!m_lines.empty()); + const float time_step = backtrace.time_step(); + size_t rev_it_dist = 0; // distance from the end of the cache of the starting point of the backtrace + float last_time_insertion = 0.0f; // used to avoid inserting two lines at the same time + for (unsigned int i = 0; i < backtrace.steps; ++i) { + const float backtrace_time_i = (i + 1) * time_step; + const float time_threshold_i = m_times[Normal] - backtrace_time_i; + auto rev_it = m_lines.rbegin() + rev_it_dist; + auto start_rev_it = rev_it; + + std::string curr_cmd = GCodeReader::GCodeLine::extract_cmd(rev_it->line); + // backtrace into the cache to find the place where to insert the line + while (rev_it != m_lines.rend() && rev_it->times[Normal] > time_threshold_i && curr_cmd != cmd && curr_cmd != "G28" && curr_cmd != "G29") { + rev_it->line = line_replacer(rev_it->line); + ++rev_it; + if (rev_it != m_lines.rend()) + curr_cmd = GCodeReader::GCodeLine::extract_cmd(rev_it->line); + } + + // we met the previous evenience of cmd, or a G28/G29 command. stop inserting lines + // Orca: 1. Use boost::iequals to handle g28/g29 cases + // 2. Handle PRINT_START and START_PRINT to the stop condition + if (rev_it != m_lines.rend() && (curr_cmd == cmd || boost::iequals(curr_cmd, "G28") || boost::iequals(curr_cmd, "G29") || + boost::iequals(curr_cmd, "PRINT_START") || boost::iequals(curr_cmd, "START_PRINT"))) + break; + + // insert the line for the current step + if (rev_it != m_lines.rend() && rev_it != start_rev_it && rev_it->times[Normal] != last_time_insertion) { + last_time_insertion = rev_it->times[Normal]; + std::vector time_diffs; + time_diffs.push_back(m_times[Normal] - last_time_insertion); + if (!m_machines[Stealth].g1_times_cache.empty()) + time_diffs.push_back(m_times[Stealth] - rev_it->times[Stealth]); + const std::string out_line = line_inserter(i + 1, time_diffs); + rev_it_dist = std::distance(m_lines.rbegin(), rev_it) + 1; + m_lines.insert(rev_it.base(), { out_line, rev_it->times }); +#ifndef NDEBUG + m_statistics.add_line(out_line.length()); +#endif // NDEBUG + m_size += out_line.length(); + // synchronize gcode lines map + for (auto map_it = m_gcode_lines_map.rbegin(); map_it != m_gcode_lines_map.rbegin() + rev_it_dist - 1; ++map_it) { + ++map_it->second; + } + + ++m_added_lines_counter; + } + } + } + + // write to file: + // m_write_type == EWriteType::ByTime - all lines older than m_time - backtrace_time + // m_write_type == EWriteType::BySize - all lines if current size is greater than 65535 bytes + void write(FilePtr& out, float backtrace_time, GCodeProcessorResult& result, const std::string& out_path) { + if (m_lines.empty()) + return; + + // collect lines to write into a single string + std::string out_string; + if (!m_lines.empty()) { + if (m_write_type == EWriteType::ByTime) { + while (m_lines.front().times[Normal] < m_times[Normal] - backtrace_time) { + const LineData& data = m_lines.front(); + out_string += data.line; + m_size -= data.line.length(); + m_lines.pop_front(); +#ifndef NDEBUG + m_statistics.remove_line(); +#endif // NDEBUG + } + } + else { + if (m_size > 65535) { + while (!m_lines.empty()) { + out_string += m_lines.front().line; + m_lines.pop_front(); + } + m_size = 0; +#ifndef NDEBUG + m_statistics.remove_all_lines(); +#endif // NDEBUG + } + } + } + + { + write_to_file(out, out_string, result, out_path); + update_lines_ends_and_out_file_pos(out_string, result.lines_ends, &m_out_file_pos); + } + } + + // flush the current content of the cache to file + void flush(FilePtr& out, GCodeProcessorResult& result, const std::string& out_path) { + // collect lines to flush into a single string + std::string out_string; + while (!m_lines.empty()) { + out_string += m_lines.front().line; + m_lines.pop_front(); + } + m_size = 0; +#ifndef NDEBUG + m_statistics.remove_all_lines(); +#endif // NDEBUG + + { + write_to_file(out, out_string, result, out_path); + update_lines_ends_and_out_file_pos(out_string, result.lines_ends, &m_out_file_pos); + } + } + + void synchronize_moves(GCodeProcessorResult& result) const { + auto it = m_gcode_lines_map.begin(); + for (GCodeProcessorResult::MoveVertex& move : result.moves) { + while (it != m_gcode_lines_map.end() && it->first < move.gcode_id) { + ++it; + } + if (it != m_gcode_lines_map.end() && it->first == move.gcode_id) + move.gcode_id = it->second; + } + } + + size_t get_size() const { return m_size; } + + private: + void write_to_file(FilePtr& out, const std::string& out_string, GCodeProcessorResult& result, const std::string& out_path) { + if (!out_string.empty()) { + if (true) { + fwrite((const void*)out_string.c_str(), 1, out_string.length(), out.f); + if (ferror(out.f)) { + out.close(); + boost::nowide::remove(out_path.c_str()); + throw Slic3r::RuntimeError("GCode processor post process export failed.\nIs the disk full?"); + } + } + } + } + }; + + ExportLines export_lines(m_result.backtrace_enabled ? ExportLines::EWriteType::ByTime : ExportLines::EWriteType::BySize, + m_time_processor.machines); + + // replace placeholder lines with the proper final value + // gcode_line is in/out parameter, to reduce expensive memory allocation + auto process_placeholders = [&](std::string& gcode_line) { + bool processed = false; + + // remove trailing '\n' + auto line = std::string_view(gcode_line).substr(0, gcode_line.length() - 1); + + if (line.length() > 1) { + line = line.substr(1); + if (true && + (line == reserved_tag(ETags::First_Line_M73_Placeholder) || line == reserved_tag(ETags::Last_Line_M73_Placeholder))) { + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { + const TimeMachine& machine = m_time_processor.machines[i]; + if (machine.enabled) { + // export pair + export_lines.append_line(format_line_M73_main(machine.line_m73_main_mask.c_str(), + (line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? 0 : 100, + (line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? time_in_minutes(machine.time) : 0)); + processed = true; + + // export remaining time to next printer stop + if (line == reserved_tag(ETags::First_Line_M73_Placeholder) && !machine.stop_times.empty()) { + const int to_export_stop = time_in_minutes(machine.stop_times.front().elapsed_time); + export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop)); + last_exported_stop[i] = to_export_stop; + } + } + } + } + else if (line == reserved_tag(ETags::Estimated_Printing_Time_Placeholder)) { + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { + const TimeMachine& machine = m_time_processor.machines[i]; + PrintEstimatedStatistics::ETimeMode mode = static_cast(i); + if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) { + char buf[128]; + sprintf(buf, "; estimated printing time (%s mode) = %s\n", + (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent", + get_time_dhms(machine.time).c_str()); + export_lines.append_line(buf); + processed = true; + } + } + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { + const TimeMachine& machine = m_time_processor.machines[i]; + PrintEstimatedStatistics::ETimeMode mode = static_cast(i); + if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) { + char buf[128]; + sprintf(buf, "; estimated first layer printing time (%s mode) = %s\n", + (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent", + get_time_dhms(machine.prepare_time).c_str()); + export_lines.append_line(buf); + processed = true; + } + } + } + } + + return processed; + }; + + auto process_used_filament = [&](std::string& gcode_line) { + // Prefilter for parsing speed. + if (gcode_line.size() < 8 || gcode_line[0] != ';' || gcode_line[1] != ' ') + return false; + if (const char c = gcode_line[2]; c != 'f' && c != 't') + return false; + auto process_tag = [](std::string& gcode_line, const std::string_view tag, const std::vector& values) { + if (boost::algorithm::starts_with(gcode_line, tag)) { + gcode_line = tag; + char buf[1024]; + for (size_t i = 0; i < values.size(); ++i) { + sprintf(buf, i == values.size() - 1 ? " %.2lf\n" : " %.2lf,", values[i]); + gcode_line += buf; + } + return true; + } + return false; + }; + + bool ret = false; + ret |= process_tag(gcode_line, PrintStatistics::FilamentUsedMmMask, filament_mm); + ret |= process_tag(gcode_line, PrintStatistics::FilamentUsedGMask, filament_g); + ret |= process_tag(gcode_line, PrintStatistics::TotalFilamentUsedGMask, { filament_total_g }); + ret |= process_tag(gcode_line, PrintStatistics::FilamentUsedCm3Mask, filament_cm3); + ret |= process_tag(gcode_line, PrintStatistics::FilamentCostMask, filament_cost); + ret |= process_tag(gcode_line, PrintStatistics::TotalFilamentCostMask, { filament_total_cost }); + return ret; + }; + + // check for temporary lines + auto is_temporary_decoration = [](const std::string_view gcode_line) { + // remove trailing '\n' + assert(!gcode_line.empty()); + assert(gcode_line.back() == '\n'); + + // return true for decorations which are used in processing the gcode but that should not be exported into the final gcode + // i.e.: + // bool ret = gcode_line.substr(0, gcode_line.length() - 1) == ";" + Layer_Change_Tag; + // ... + // return ret; + return false; + }; + + // Iterators for the normal and silent cached time estimate entry recently processed, used by process_line_G1. + auto g1_times_cache_it = Slic3r::reserve_vector::const_iterator>(m_time_processor.machines.size()); + for (const auto& machine : m_time_processor.machines) + g1_times_cache_it.emplace_back(machine.g1_times_cache.begin()); + + // add lines M73 to exported gcode + auto process_line_G1 = [this, + // Lambdas, mostly for string formatting, all with an empty capture block. + time_in_minutes, format_time_float, format_line_M73_main, format_line_M73_stop_int, format_line_M73_stop_float, time_in_last_minute, + // Caches, to be modified + &g1_times_cache_it, &last_exported_main, &last_exported_stop, + &export_lines] + (const size_t g1_lines_counter) { + if (true) { + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { + const TimeMachine& machine = m_time_processor.machines[i]; + if (machine.enabled) { + // export pair + // Skip all machine.g1_times_cache below g1_lines_counter. + auto& it = g1_times_cache_it[i]; + while (it != machine.g1_times_cache.end() && it->id < g1_lines_counter) + ++it; + if (it != machine.g1_times_cache.end() && it->id == g1_lines_counter) { + std::pair to_export_main = { int(100.0f * it->elapsed_time / machine.time), + time_in_minutes(machine.time - it->elapsed_time) }; + if (last_exported_main[i] != to_export_main) { + export_lines.append_line(format_line_M73_main(machine.line_m73_main_mask.c_str(), + to_export_main.first, to_export_main.second)); + last_exported_main[i] = to_export_main; + } + // export remaining time to next printer stop + auto it_stop = std::upper_bound(machine.stop_times.begin(), machine.stop_times.end(), it->elapsed_time, + [](float value, const TimeMachine::StopTime& t) { return value < t.elapsed_time; }); + if (it_stop != machine.stop_times.end()) { + int to_export_stop = time_in_minutes(it_stop->elapsed_time - it->elapsed_time); + if (last_exported_stop[i] != to_export_stop) { + if (to_export_stop > 0) { + if (last_exported_stop[i] != to_export_stop) { + export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop)); + last_exported_stop[i] = to_export_stop; + } + } + else { + bool is_last = false; + auto next_it = it + 1; + is_last |= (next_it == machine.g1_times_cache.end()); + + if (next_it != machine.g1_times_cache.end()) { + auto next_it_stop = std::upper_bound(machine.stop_times.begin(), machine.stop_times.end(), next_it->elapsed_time, + [](float value, const TimeMachine::StopTime& t) { return value < t.elapsed_time; }); + is_last |= (next_it_stop != it_stop); + + std::string time_float_str = format_time_float(time_in_last_minute(it_stop->elapsed_time - it->elapsed_time)); + std::string next_time_float_str = format_time_float(time_in_last_minute(it_stop->elapsed_time - next_it->elapsed_time)); + is_last |= (string_to_double_decimal_point(time_float_str) > 0. && string_to_double_decimal_point(next_time_float_str) == 0.); + } + + if (is_last) { + if (std::distance(machine.stop_times.begin(), it_stop) == static_cast(machine.stop_times.size() - 1)) + export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop)); + else + export_lines.append_line(format_line_M73_stop_float(machine.line_m73_stop_mask.c_str(), time_in_last_minute(it_stop->elapsed_time - it->elapsed_time))); + + last_exported_stop[i] = to_export_stop; + } + } + } + } + } + } + } + } + }; + + // add lines M104 to exported gcode + auto process_line_T = [this, &export_lines](const std::string& gcode_line, const size_t g1_lines_counter, const ExportLines::Backtrace& backtrace) { + const std::string cmd = GCodeReader::GCodeLine::extract_cmd(gcode_line); + if (cmd.size() >= 2) { + std::stringstream ss(cmd.substr(1)); + int tool_number = -1; + ss >> tool_number; + if (tool_number != -1) { + if (tool_number < 0 || (int)m_extruder_temps_config.size() <= tool_number) { + // found an invalid value, clamp it to a valid one + tool_number = std::clamp(0, m_extruder_temps_config.size() - 1, tool_number); + // emit warning + std::string warning = _u8L("GCode Post-Processor encountered an invalid toolchange, maybe from a custom gcode:"); + warning += "\n> "; + warning += gcode_line; + warning += _u8L("Generated M104 lines may be incorrect."); + BOOST_LOG_TRIVIAL(error) << warning; + // Orca todo + // if (m_print != nullptr) + // m_print->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning); + } + } + export_lines.insert_lines( + backtrace, cmd, + // line inserter + [tool_number, this](unsigned int id, const std::vector& time_diffs) { + const int temperature = int(m_layer_id != 1 ? m_extruder_temps_config[tool_number] : + m_extruder_temps_first_layer_config[tool_number]); + // Orca: M104.1 for XL printers, I can't find the documentation for this so I copied the C++ comments from + // Prusa-Firmware-Buddy here + /** + * M104.1: Early Set Hotend Temperature (preheat, and with stealth mode support) + * + * This GCode is used to tell the XL printer the time estimate when a tool will be used next, + * so that the printer can start preheating the tool in advance. + * + * ## Parameters + * - `P` - - time in seconds till the temperature S is required (in standard mode) + * - `Q` - - time in seconds till the temperature S is required (in stealth mode) + * The rest is same as M104 + */ + if (this->m_is_XL_printer) { + std::string out = "M104.1 T" + std::to_string(tool_number); + if (time_diffs.size() > 0) + out += " P" + std::to_string(int(std::round(time_diffs[0]))); + if (time_diffs.size() > 1) + out += " Q" + std::to_string(int(std::round(time_diffs[1]))); + out += " S" + std::to_string(temperature) + "\n"; + return out; + } else { + std::string comment = "preheat tool " + std::to_string(tool_number) + + "time: " + std::to_string(std::round(time_diffs[0])) + "s"; + return GCodeWriter::set_temperature(temperature, this->m_flavor, false, tool_number, comment); + } + }, + // line replacer + [this, tool_number](const std::string& line) { + if (GCodeReader::GCodeLine::cmd_is(line, "M104")) { + GCodeReader::GCodeLine gline; + GCodeReader reader; + reader.parse_line(line, [&gline](GCodeReader& reader, const GCodeReader::GCodeLine& l) { gline = l; }); + + float val; + if (gline.has_value('T', val) && gline.raw().find("cooldown") != std::string::npos && m_is_XL_printer) { + if (static_cast(val) == tool_number) + return std::string("; removed M104\n"); + } + } + return line; + }); + } + }; + + m_result.lines_ends.clear(); + // m_result.lines_ends.emplace_back(std::vector()); + + unsigned int line_id = 0; + // Backtrace data for Tx gcode lines + static const ExportLines::Backtrace backtrace_T = { 120.0f, 10 }; + // In case there are multiple sources of backtracing, keeps track of the longest backtrack time needed + // to flush the backtrace cache accordingly + float max_backtrace_time = 120.0f; + + { + // Read the input stream 64kB at a time, extract lines and process them. + std::vector buffer(65536 * 10, 0); + // Line buffer. + assert(gcode_line.empty()); + for (;;) { + size_t cnt_read = ::fread(buffer.data(), 1, buffer.size(), in.f); + if (::ferror(in.f)) + throw Slic3r::RuntimeError(std::string("GCode processor post process export failed.\nError while reading from file.\n")); + bool eof = cnt_read == 0; + auto it = buffer.begin(); + auto it_bufend = buffer.begin() + cnt_read; + while (it != it_bufend || (eof && !gcode_line.empty())) { + // Find end of line. + bool eol = false; + auto it_end = it; + for (; it_end != it_bufend && !(eol = *it_end == '\r' || *it_end == '\n'); ++it_end); + // End of line is indicated also if end of file was reached. + eol |= eof && it_end == it_bufend; + gcode_line.insert(gcode_line.end(), it, it_end); + if (eol) { + ++line_id; + gcode_line += "\n"; + const unsigned int internal_g1_lines_counter = export_lines.update(gcode_line, line_id, g1_lines_counter); + // replace placeholder lines + bool processed = process_placeholders(gcode_line); + if (processed) + gcode_line.clear(); + if (!processed) + processed = process_used_filament(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_lines.append_line(gcode_line); + // add lines M73 where needed + process_line_G1(g1_lines_counter++); + gcode_line.clear(); + } + else if (GCodeReader::GCodeLine::cmd_is(gcode_line, "G2") || GCodeReader::GCodeLine::cmd_is(gcode_line, "G3")) { + export_lines.append_line(gcode_line); + // add lines M73 where needed + process_line_G1(g1_lines_counter + internal_g1_lines_counter); + g1_lines_counter += (1 + internal_g1_lines_counter); + gcode_line.clear(); + } + else if (GCodeReader::GCodeLine::cmd_is(gcode_line, "G28")) { + ++g1_lines_counter; + } + else if (m_result.backtrace_enabled && GCodeReader::GCodeLine::cmd_starts_with(gcode_line, "T")) { + // add lines M104 where needed + process_line_T(gcode_line, g1_lines_counter, backtrace_T); + max_backtrace_time = std::max(max_backtrace_time, backtrace_T.time); + } + } + + if (!gcode_line.empty()) + export_lines.append_line(gcode_line); + export_lines.write(out, 1.1f * max_backtrace_time, m_result, out_path); + gcode_line.clear(); + } + // Skip EOL. + it = it_end; + if (it != it_bufend && *it == '\r') + ++it; + if (it != it_bufend && *it == '\n') + ++it; + } + if (eof) + break; + } + } + + export_lines.flush(out, m_result, out_path); + + + out.close(); + in.close(); + + const std::string result_filename = m_result.filename; + export_lines.synchronize_moves(m_result); + + if (rename_file(out_path, result_filename)) + throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + out_path + " to " + result_filename + '\n' + + "Is " + out_path + " locked?" + '\n'); +} void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type) { diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index a1fd5237e3..2896583786 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -17,6 +17,8 @@ namespace Slic3r { +class Print; + // slice warnings enum strings #define NOZZLE_HRC_CHECKER "the_actual_nozzle_hrc_smaller_than_the_required_nozzle_hrc" #define BED_TEMP_TOO_HIGH_THAN_FILAMENT "bed_temperature_too_high_than_filament" @@ -207,6 +209,7 @@ namespace Slic3r { float printable_height; SettingsIds settings_ids; size_t extruders_count; + bool backtrace_enabled; std::vector extruder_colors; std::vector filament_diameters; std::vector required_nozzle_HRC; @@ -377,6 +380,7 @@ namespace Slic3r { EMoveType move_type{ EMoveType::Noop }; ExtrusionRole role{ erNone }; unsigned int g1_line_id{ 0 }; + unsigned int remaining_internal_g1_lines; unsigned int layer_id{ 0 }; float distance{ 0.0f }; // mm float acceleration{ 0.0f }; // mm/s^2 @@ -425,6 +429,7 @@ namespace Slic3r { struct G1LinesCacheItem { unsigned int id; + unsigned int remaining_internal_g1_lines; float elapsed_time; }; @@ -709,6 +714,9 @@ namespace Slic3r { unsigned char m_last_extruder_id; ExtruderColors m_extruder_colors; ExtruderTemps m_extruder_temps; + ExtruderTemps m_extruder_temps_config; + ExtruderTemps m_extruder_temps_first_layer_config; + bool m_is_XL_printer = false; int m_highest_bed_temp; float m_extruded_last_z; float m_first_layer_height; // mm @@ -722,6 +730,7 @@ namespace Slic3r { size_t m_last_default_color_id; bool m_detect_layer_based_on_tag {false}; int m_seams_count; + bool m_single_extruder_multi_material; #if ENABLE_GCODE_VIEWER_STATISTICS std::chrono::time_point m_start_time; #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -746,6 +755,8 @@ namespace Slic3r { TimeProcessor m_time_processor; UsedFilaments m_used_filaments; + Print* m_print{ nullptr }; + GCodeProcessorResult m_result; static unsigned int s_result_id; @@ -759,6 +770,7 @@ namespace Slic3r { GCodeProcessor(); void apply_config(const PrintConfig& config); + void set_print(Print* print) { m_print = print; } void enable_stealth_time_estimator(bool enabled); bool is_stealth_time_estimator_enabled() const { return m_time_processor.machines[static_cast(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled; @@ -815,7 +827,7 @@ namespace Slic3r { // Move void process_G0(const GCodeReader::GCodeLine& line); - void process_G1(const GCodeReader::GCodeLine& line); + void process_G1(const GCodeReader::GCodeLine& line, const std::optional& remaining_internal_g1_lines = std::nullopt); void process_G2_G3(const GCodeReader::GCodeLine& line); // BBS: handle delay command @@ -930,6 +942,11 @@ namespace Slic3r { void process_T(const GCodeReader::GCodeLine& line); void process_T(const std::string_view command); + // post process the file with the given filename to: + // 1) add remaining time lines M73 and update moves' gcode ids accordingly + // 2) update used filament data + void run_post_process(); + //BBS: different path_type is only used for arc move void store_move_vertex(EMoveType type, EMovePathType path_type = EMovePathType::Noop_move); @@ -943,7 +960,7 @@ namespace Slic3r { Vec3f get_xyz_max_jerk(PrintEstimatedStatistics::ETimeMode mode) const; float get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const; void set_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value); - float get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const; + float get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const; void set_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value); float get_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode) const; void set_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value); diff --git a/src/libslic3r/GCode/WipeTower2.cpp b/src/libslic3r/GCode/WipeTower2.cpp index 71e532122b..521dd9b746 100644 --- a/src/libslic3r/GCode/WipeTower2.cpp +++ b/src/libslic3r/GCode/WipeTower2.cpp @@ -781,49 +781,7 @@ std::vector WipeTower2::prime( return results; } -#define FLAVOR_IS(val) this->m_gcode_flavor == val -#define FLAVOR_IS_NOT(val) this->m_gcode_flavor != val -std::string WipeTower2::set_preheat_temperature(unsigned int temperature, bool wait, int tool) -{ - if (wait && (FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish))) - return ""; - - std::string code, comment; - if (wait && FLAVOR_IS_NOT(gcfTeacup) && FLAVOR_IS_NOT(gcfRepRapFirmware)) { - code = "M109"; - comment = "set nozzle temperature and wait for it to be reached"; - } else { - if (FLAVOR_IS(gcfRepRapFirmware)) { // M104 is deprecated on RepRapFirmware - code = "G10"; - } else { - code = "M104"; - } - comment = "preheat next nozzle"; - } - - std::ostringstream gcode; - gcode << code << " "; - if (FLAVOR_IS(gcfMach3) || FLAVOR_IS(gcfMachinekit)) { - gcode << "P"; - } else { - gcode << "S"; - } - gcode << temperature; - if (tool != -1) { - if (FLAVOR_IS(gcfRepRapFirmware)) { - gcode << " P" << tool; - } else { - gcode << " T" << tool; - } - } - gcode << " ; " << comment << "\n"; - - if ((FLAVOR_IS(gcfTeacup) || FLAVOR_IS(gcfRepRapFirmware)) && wait) - gcode << "M116 ; wait for temperature to be reached\n"; - - return gcode.str(); -} WipeTower::ToolChangeResult WipeTower2::tool_change(size_t tool) { size_t old_tool = m_current_tool; @@ -879,8 +837,6 @@ WipeTower::ToolChangeResult WipeTower2::tool_change(size_t tool) // Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool. if (tool != (unsigned int)-1){ // This is not the last change. auto new_tool_temp = is_first_layer() ? m_filpar[tool].first_layer_temperature : m_filpar[tool].temperature; - // Orca: pre-heat next tool, it's a temperary solution before impelment the proper preheat. - writer.append(set_preheat_temperature(new_tool_temp, false, tool)); toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, (is_first_layer() ? m_filpar[m_current_tool].first_layer_temperature : m_filpar[m_current_tool].temperature), new_tool_temp); diff --git a/src/libslic3r/GCode/WipeTower2.hpp b/src/libslic3r/GCode/WipeTower2.hpp index 1d37cbe791..0f377d00f7 100644 --- a/src/libslic3r/GCode/WipeTower2.hpp +++ b/src/libslic3r/GCode/WipeTower2.hpp @@ -255,9 +255,6 @@ private: // Goes through m_plan, calculates border and finish_layer extrusions and subtracts them from last wipe void save_on_last_wipe(); - // Orca: temp help function to set temperature - std::string set_preheat_temperature(unsigned int temperature, bool wait, int tool); - // to store information about tool changes for a given layer struct WipeTowerInfo{ struct ToolChange { diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp index 52a37dde55..5d3b6a39ed 100644 --- a/src/libslic3r/GCodeReader.hpp +++ b/src/libslic3r/GCodeReader.hpp @@ -79,6 +79,16 @@ public: return strncmp(cmd, cmd_test, len) == 0 && GCodeReader::is_end_of_word(cmd[len]); } + static bool cmd_starts_with(const std::string& gcode_line, const char* cmd_test) { + return strncmp(GCodeReader::skip_whitespaces(gcode_line.c_str()), cmd_test, strlen(cmd_test)) == 0; + } + + static std::string extract_cmd(const std::string& gcode_line) { + GCodeLine temp; + temp.m_raw = gcode_line; + const std::string_view cmd = temp.cmd(); + return { cmd.begin(), cmd.end() }; + } private: std::string m_raw; float m_axis[NUM_AXES]; diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index d4ebb64362..f60d81a95a 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -90,48 +90,56 @@ std::string GCodeWriter::postamble() const return gcode.str(); } -std::string GCodeWriter::set_temperature(unsigned int temperature, bool wait, int tool) const -{ - if (wait && (FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish))) +std::string GCodeWriter::set_temperature(unsigned int temperature, GCodeFlavor flavor, bool wait, int tool, std::string comment){ + if (wait && (flavor == gcfMakerWare || flavor == gcfSailfish)) return ""; - - std::string code, comment; - if (wait && FLAVOR_IS_NOT(gcfTeacup) && FLAVOR_IS_NOT(gcfRepRapFirmware)) { - code = "M109"; - comment = "set nozzle temperature and wait for it to be reached"; + + std::string code; + if (wait && flavor != gcfTeacup && flavor != gcfRepRapFirmware) { + code = "M109"; + if(comment.empty()) + comment = "set nozzle temperature and wait for it to be reached"; } else { - if (FLAVOR_IS(gcfRepRapFirmware)) { // M104 is deprecated on RepRapFirmware + if (flavor == gcfRepRapFirmware) { // M104 is deprecated on RepRapFirmware code = "G10"; } else { code = "M104"; } - comment = "set nozzle temperature"; + if(comment.empty()) + comment = "set nozzle temperature"; } - + std::ostringstream gcode; gcode << code << " "; - if (FLAVOR_IS(gcfMach3) || FLAVOR_IS(gcfMachinekit)) { + if (flavor == gcfMach3 || flavor == gcfMachinekit) { gcode << "P"; } else { gcode << "S"; } gcode << temperature; - bool multiple_tools = this->multiple_extruders && ! m_single_extruder_multi_material; - if (tool != -1 && (multiple_tools || FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish)) ) { - if (FLAVOR_IS(gcfRepRapFirmware)) { + if (tool != -1) { + if (flavor == gcfRepRapFirmware) { gcode << " P" << tool; } else { gcode << " T" << tool; } } gcode << " ; " << comment << "\n"; - - if ((FLAVOR_IS(gcfTeacup) || FLAVOR_IS(gcfRepRapFirmware)) && wait) + + if ((flavor == gcfTeacup || flavor == gcfRepRapFirmware) && wait) gcode << "M116 ; wait for temperature to be reached\n"; - + return gcode.str(); } +std::string GCodeWriter::set_temperature(unsigned int temperature, bool wait, int tool) const +{ + // set tool to -1 to make sure we won't emit T parameter for single extruder or SEMM + if (!this->multiple_extruders || m_single_extruder_multi_material) + tool = -1; + return set_temperature(temperature, this->config.gcode_flavor, wait, tool); +} + // BBS std::string GCodeWriter::set_bed_temperature(int temperature, bool wait) { diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index 8e1effcc4e..df68f549fd 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -43,6 +43,8 @@ public: } std::string preamble(); std::string postamble() const; + static std::string set_temperature(unsigned int temperature, GCodeFlavor flavor, bool wait = false, int tool = -1, std::string comment = std::string()); + std::string set_temperature(unsigned int temperature, bool wait = false, int tool = -1) const; std::string set_bed_temperature(int temperature, bool wait = false); std::string set_chamber_temperature(int temperature, bool wait = false); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 3650b4ef90..dd401776a4 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -2935,6 +2935,30 @@ std::string PrintStatistics::finalize_output_path(const std::string &path_in) co return final_path; } +const std::string PrintStatistics::FilamentUsedG = "filament used [g]"; +const std::string PrintStatistics::FilamentUsedGMask = "; filament used [g] ="; + +const std::string PrintStatistics::TotalFilamentUsedG = "total filament used [g]"; +const std::string PrintStatistics::TotalFilamentUsedGMask = "; total filament used [g] ="; +const std::string PrintStatistics::TotalFilamentUsedGValueMask = "; total filament used [g] = %.2lf\n"; + +const std::string PrintStatistics::FilamentUsedCm3 = "filament used [cm3]"; +const std::string PrintStatistics::FilamentUsedCm3Mask = "; filament used [cm3] ="; + +const std::string PrintStatistics::FilamentUsedMm = "filament used [mm]"; +const std::string PrintStatistics::FilamentUsedMmMask = "; filament used [mm] ="; + +const std::string PrintStatistics::FilamentCost = "filament cost"; +const std::string PrintStatistics::FilamentCostMask = "; filament cost ="; + +const std::string PrintStatistics::TotalFilamentCost = "total filament cost"; +const std::string PrintStatistics::TotalFilamentCostMask = "; total filament cost ="; +const std::string PrintStatistics::TotalFilamentCostValueMask = "; total filament cost = %.2lf\n"; + +const std::string PrintStatistics::TotalFilamentUsedWipeTower = "total filament used for wipe tower [g]"; +const std::string PrintStatistics::TotalFilamentUsedWipeTowerValueMask = "; total filament used for wipe tower [g] = %.2lf\n"; + + /*add json export/import related functions */ #define JSON_POLYGON_CONTOUR "contour" #define JSON_POLYGON_HOLES "holes" diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index bd7b867b99..aebb46899f 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -771,6 +771,23 @@ struct PrintStatistics initial_tool = 0; filament_stats.clear(); } + static const std::string FilamentUsedG; + static const std::string FilamentUsedGMask; + static const std::string TotalFilamentUsedG; + static const std::string TotalFilamentUsedGMask; + static const std::string TotalFilamentUsedGValueMask; + static const std::string FilamentUsedCm3; + static const std::string FilamentUsedCm3Mask; + static const std::string FilamentUsedMm; + static const std::string FilamentUsedMmMask; + static const std::string FilamentCost; + static const std::string FilamentCostMask; + static const std::string TotalFilamentCost; + static const std::string TotalFilamentCostMask; + static const std::string TotalFilamentCostValueMask; + static const std::string TotalFilamentUsedWipeTower; + static const std::string TotalFilamentUsedWipeTowerValueMask; + }; typedef std::vector PrintObjectPtrs; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 57697dd8d1..7856bd6ba6 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -7677,6 +7677,22 @@ bool has_skirt(const DynamicPrintConfig& cfg) float get_real_skirt_dist(const DynamicPrintConfig& cfg) { return has_skirt(cfg) ? cfg.opt_float("skirt_distance") : 0; } +static bool is_XL_printer(const std::string& printer_notes) +{ + return boost::algorithm::contains(printer_notes, "PRINTER_VENDOR_PRUSA3D") + && boost::algorithm::contains(printer_notes, "PRINTER_MODEL_XL"); +} + +bool is_XL_printer(const DynamicPrintConfig &cfg) +{ + auto *printer_notes = cfg.opt("printer_notes"); + return printer_notes && is_XL_printer(printer_notes->value); +} + +bool is_XL_printer(const PrintConfig &cfg) +{ + return is_XL_printer(cfg.printer_notes.value); +} } // namespace Slic3r #include diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 89a020774e..620a24b381 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1651,6 +1651,9 @@ private: static PrintAndCLIConfigDef s_def; }; +bool is_XL_printer(const DynamicPrintConfig &cfg); +bool is_XL_printer(const PrintConfig &cfg); + Points get_bed_shape(const DynamicPrintConfig &cfg); Points get_bed_shape(const PrintConfig &cfg); Points get_bed_shape(const SLAPrinterConfig &cfg);