diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index de43bfaec..78965486b 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -662,15 +662,19 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ throw std::runtime_error(msg); } - if (print->config().remaining_times.value) { - BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for normal mode" << log_memory_info(); - m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f); + GCodeTimeEstimator::PostProcessData normal_data = m_normal_time_estimator.get_post_process_data(); + GCodeTimeEstimator::PostProcessData silent_data = m_silent_time_estimator.get_post_process_data(); + + bool remaining_times_enabled = print->config().remaining_times.value; + + BOOST_LOG_TRIVIAL(debug) << "Time estimator post processing" << log_memory_info(); + GCodeTimeEstimator::post_process(path_tmp, 60.0f, remaining_times_enabled ? &normal_data : nullptr, (remaining_times_enabled && m_silent_time_estimator_enabled) ? &silent_data : nullptr); + + if (remaining_times_enabled) + { m_normal_time_estimator.reset(); - if (m_silent_time_estimator_enabled) { - BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for silent mode" << log_memory_info(); - m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f); + if (m_silent_time_estimator_enabled) m_silent_time_estimator.reset(); - } } // starts analyzer calculations diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp index 00b409344..4693ba9e6 100644 --- a/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -173,9 +173,7 @@ namespace Slic3r { const std::string GCodeTimeEstimator::Normal_Last_M73_Output_Placeholder_Tag = "; _TE_NORMAL_LAST_M73_OUTPUT_PLACEHOLDER"; const std::string GCodeTimeEstimator::Silent_Last_M73_Output_Placeholder_Tag = "; _TE_SILENT_LAST_M73_OUTPUT_PLACEHOLDER"; - // temporary human readable form to use until not removed from gcode by the new post-process method const std::string GCodeTimeEstimator::Color_Change_Tag = "PRINT_COLOR_CHANGE"; -// const std::string GCodeTimeEstimator::Color_Change_Tag = "_TE_COLOR_CHANGE"; GCodeTimeEstimator::GCodeTimeEstimator(EMode mode) : m_mode(mode) @@ -273,130 +271,138 @@ namespace Slic3r { #endif // ENABLE_MOVE_STATS } - bool GCodeTimeEstimator::post_process_remaining_times(const std::string& filename, float interval) + bool GCodeTimeEstimator::post_process(const std::string& filename, float interval_sec, const PostProcessData* const normal_mode, const PostProcessData* const silent_mode) { boost::nowide::ifstream in(filename); if (!in.good()) - throw std::runtime_error(std::string("Remaining times export failed.\nCannot open file for reading.\n")); + throw std::runtime_error(std::string("Time estimator post process export failed.\nCannot open file for reading.\n")); - std::string path_tmp = filename + ".times"; + std::string path_tmp = filename + ".postprocess"; FILE* out = boost::nowide::fopen(path_tmp.c_str(), "wb"); if (out == nullptr) - throw std::runtime_error(std::string("Remaining times export failed.\nCannot open file for writing.\n")); + throw std::runtime_error(std::string("Time estimator post process export failed.\nCannot open file for writing.\n")); - std::string time_mask; - switch (m_mode) - { - default: - case Normal: - { - time_mask = "M73 P%s R%s\n"; - break; - } - case Silent: - { - time_mask = "M73 Q%s S%s\n"; - break; - } - } + std::string normal_time_mask = "M73 P%s R%s\n"; + std::string silent_time_mask = "M73 Q%s S%s\n"; + char line_M73[64]; - unsigned int g1_lines_count = 0; - float last_recorded_time = 0.0f; std::string gcode_line; // buffer line to export only when greater than 64K to reduce writing calls std::string export_line; - char time_line[64]; - G1LineIdToBlockIdMap::const_iterator it_line_id = m_g1_line_ids.begin(); - while (std::getline(in, gcode_line)) - { - if (!in.good()) - { - fclose(out); - throw std::runtime_error(std::string("Remaining times export failed.\nError while reading from file.\n")); - } - // replaces placeholders for initial line M73 with the real lines - if (((m_mode == Normal) && (gcode_line == Normal_First_M73_Output_Placeholder_Tag)) || - ((m_mode == Silent) && (gcode_line == Silent_First_M73_Output_Placeholder_Tag))) - { - sprintf(time_line, time_mask.c_str(), "0", _get_time_minutes(m_time).c_str()); - gcode_line = time_line; - } - // replaces placeholders for final line M73 with the real lines - else if (((m_mode == Normal) && (gcode_line == Normal_Last_M73_Output_Placeholder_Tag)) || - ((m_mode == Silent) && (gcode_line == Silent_Last_M73_Output_Placeholder_Tag))) - { - sprintf(time_line, time_mask.c_str(), "100", "0"); - gcode_line = time_line; - } - else - gcode_line += "\n"; - - - // add remaining time lines where needed - m_parser.parse_line(gcode_line, - [this, &it_line_id, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line) - { - if (line.cmd_is("G1")) - { - ++g1_lines_count; - - assert(it_line_id == m_g1_line_ids.end() || it_line_id->first >= g1_lines_count); - - const Block *block = nullptr; - if (it_line_id != m_g1_line_ids.end() && it_line_id->first == g1_lines_count) { - if (line.has_e() && it_line_id->second < (unsigned int)m_blocks.size()) - block = &m_blocks[it_line_id->second]; - ++it_line_id; - } - - if (block != nullptr && block->elapsed_time != -1.0f) { - float block_remaining_time = m_time - block->elapsed_time; - if (std::abs(last_recorded_time - block_remaining_time) > interval) - { - sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block->elapsed_time / m_time)).c_str(), _get_time_minutes(block_remaining_time).c_str()); - gcode_line += time_line; - - last_recorded_time = block_remaining_time; - } - } - } - }); - - export_line += gcode_line; - if (export_line.length() > 65535) - { - fwrite((const void*)export_line.c_str(), 1, export_line.length(), out); - if (ferror(out)) - { - in.close(); - fclose(out); - boost::nowide::remove(path_tmp.c_str()); - throw std::runtime_error(std::string("Remaining times export failed.\nIs the disk full?\n")); - } - export_line.clear(); - } - } - - if (export_line.length() > 0) - { + // helper function to write to disk + auto write_string = [&](const std::string& str) { fwrite((const void*)export_line.c_str(), 1, export_line.length(), out); if (ferror(out)) { in.close(); fclose(out); boost::nowide::remove(path_tmp.c_str()); - throw std::runtime_error(std::string("Remaining times export failed.\nIs the disk full?\n")); + throw std::runtime_error(std::string("Time estimator post process export failed.\nIs the disk full?\n")); } + export_line.clear(); + }; + + GCodeReader parser; + unsigned int g1_lines_count = 0; + int normal_g1_line_id = 0; + float normal_last_recorded_time = 0.0f; + int silent_g1_line_id = 0; + float silent_last_recorded_time = 0.0f; + + // helper function to process g1 lines + auto process_g1_line = [&](const PostProcessData* const data, const GCodeReader::GCodeLine& line, int& g1_line_id, float& last_recorded_time, const std::string& time_mask) { + if (data == nullptr) + return; + + assert((g1_line_id >= (int)data->g1_line_ids.size()) || (data->g1_line_ids[g1_line_id].first >= g1_lines_count)); + const Block* block = nullptr; + const G1LineIdToBlockId& map_item = data->g1_line_ids[g1_line_id]; + if ((g1_line_id < (int)data->g1_line_ids.size()) && (map_item.first == g1_lines_count)) + { + if (line.has_e() && (map_item.second < (unsigned int)data->blocks.size())) + block = &data->blocks[map_item.second]; + ++g1_line_id; + } + + if ((block != nullptr) && (block->elapsed_time != -1.0f)) + { + float block_remaining_time = data->time - block->elapsed_time; + if (std::abs(last_recorded_time - block_remaining_time) > interval_sec) + { + sprintf(line_M73, time_mask.c_str(), std::to_string((int)(100.0f * block->elapsed_time / data->time)).c_str(), _get_time_minutes(block_remaining_time).c_str()); + gcode_line += line_M73; + + last_recorded_time = block_remaining_time; + } + } + }; + + while (std::getline(in, gcode_line)) + { + if (!in.good()) + { + fclose(out); + throw std::runtime_error(std::string("Time estimator post process export failed.\nError while reading from file.\n")); + } + + // check tags + // remove color change tag + if (gcode_line == "; " + Color_Change_Tag) + continue; + + // replaces placeholders for initial line M73 with the real lines + if ((normal_mode != nullptr) && (gcode_line == Normal_First_M73_Output_Placeholder_Tag)) + { + sprintf(line_M73, normal_time_mask.c_str(), "0", _get_time_minutes(normal_mode->time).c_str()); + gcode_line = line_M73; + } + else if ((silent_mode != nullptr) && (gcode_line == Silent_First_M73_Output_Placeholder_Tag)) + { + sprintf(line_M73, silent_time_mask.c_str(), "0", _get_time_minutes(silent_mode->time).c_str()); + gcode_line = line_M73; + } + // replaces placeholders for final line M73 with the real lines + else if ((normal_mode != nullptr) && (gcode_line == Normal_Last_M73_Output_Placeholder_Tag)) + { + sprintf(line_M73, normal_time_mask.c_str(), "100", "0"); + gcode_line = line_M73; + } + else if ((silent_mode != nullptr) && (gcode_line == Silent_Last_M73_Output_Placeholder_Tag)) + { + sprintf(line_M73, silent_time_mask.c_str(), "100", "0"); + gcode_line = line_M73; + } + else + gcode_line += "\n"; + + // add remaining time lines where needed + parser.parse_line(gcode_line, + [&](GCodeReader& reader, const GCodeReader::GCodeLine& line) + { + if (line.cmd_is("G1")) + { + ++g1_lines_count; + process_g1_line(silent_mode, line, silent_g1_line_id, silent_last_recorded_time, silent_time_mask); + process_g1_line(normal_mode, line, normal_g1_line_id, normal_last_recorded_time, normal_time_mask); + } + }); + + export_line += gcode_line; + if (export_line.length() > 65535) + write_string(export_line); } + if (!export_line.empty()) + write_string(export_line); + fclose(out); in.close(); if (rename_file(path_tmp, filename)) throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' + - "Is " + path_tmp + " locked?" + '\n'); + "Is " + path_tmp + " locked?" + '\n'); return true; } diff --git a/src/libslic3r/GCodeTimeEstimator.hpp b/src/libslic3r/GCodeTimeEstimator.hpp index dd5d14b57..d9f3bc211 100644 --- a/src/libslic3r/GCodeTimeEstimator.hpp +++ b/src/libslic3r/GCodeTimeEstimator.hpp @@ -213,9 +213,19 @@ namespace Slic3r { typedef std::map MovesStatsMap; #endif // ENABLE_MOVE_STATS + public: typedef std::pair G1LineIdToBlockId; typedef std::vector G1LineIdToBlockIdMap; + struct PostProcessData + { + const G1LineIdToBlockIdMap& g1_line_ids; + const BlocksList& blocks; + float time; + + PostProcessData(const G1LineIdToBlockIdMap& g1_line_ids, const BlocksList& blocks, float time) : g1_line_ids(g1_line_ids), blocks(blocks), time(time) {} + }; + private: EMode m_mode; GCodeReader m_parser; @@ -263,11 +273,12 @@ namespace Slic3r { void calculate_time_from_lines(const std::vector& gcode_lines); // Process the gcode contained in the file with the given filename, - // placing in it new lines (M73) containing the remaining time, at the given interval in seconds - // and saving the result back in the same file - // This time estimator should have been already used to calculate the time estimate for the gcode - // contained in the given file before to call this method - bool post_process_remaining_times(const std::string& filename, float interval_sec); + // replacing placeholders with correspondent new lines M73 + // placing new lines M73 (containing the remaining time) where needed (in dependence of the given interval in seconds) + // and removing working tags (as those used for color changes) + // if normal_mode == nullptr no M73 line will be added for normal mode + // if silent_mode == nullptr no M73 line will be added for silent mode + static bool post_process(const std::string& filename, float interval_sec, const PostProcessData* const normal_mode, const PostProcessData* const silent_mode); // Set current position on the given axis with the given value void set_axis_position(EAxis axis, float position); @@ -362,6 +373,8 @@ namespace Slic3r { // Return an estimate of the memory consumed by the time estimator. size_t memory_used() const; + PostProcessData get_post_process_data() const { return PostProcessData(m_g1_line_ids, m_blocks, m_time); } + private: void _reset(); void _reset_time();