diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 9ecc6b296..ba0b299cd 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -109,8 +109,6 @@ add_library(libslic3r STATIC GCodeReader.hpp # GCodeSender.cpp # GCodeSender.hpp - GCodeTimeEstimator.cpp - GCodeTimeEstimator.hpp GCodeWriter.cpp GCodeWriter.hpp Geometry.cpp diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp deleted file mode 100644 index 68075c123..000000000 --- a/src/libslic3r/GCodeTimeEstimator.cpp +++ /dev/null @@ -1,1679 +0,0 @@ -#include "Exception.hpp" -#include "GCodeTimeEstimator.hpp" -#include "Utils.hpp" -#include -#include - -#include - -#include -#include -#include - -#if !ENABLE_GCODE_VIEWER - -static const float MMMIN_TO_MMSEC = 1.0f / 60.0f; -static const float MILLISEC_TO_SEC = 0.001f; -static const float INCHES_TO_MM = 25.4f; - -static const float DEFAULT_FEEDRATE = 1500.0f; // from Prusa Firmware (Marlin_main.cpp) -static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2 -static const float DEFAULT_RETRACT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2 -static const float DEFAULT_AXIS_MAX_FEEDRATE[] = { 500.0f, 500.0f, 12.0f, 120.0f }; // Prusa Firmware 1_75mm_MK2 -static const float DEFAULT_AXIS_MAX_ACCELERATION[] = { 9000.0f, 9000.0f, 500.0f, 10000.0f }; // Prusa Firmware 1_75mm_MK2 -static const float DEFAULT_AXIS_MAX_JERK[] = { 10.0f, 10.0f, 0.4f, 2.5f }; // from Prusa Firmware (Configuration.h) -static const float DEFAULT_MINIMUM_FEEDRATE = 0.0f; // from Prusa Firmware (Configuration_adv.h) -static const float DEFAULT_MINIMUM_TRAVEL_FEEDRATE = 0.0f; // from Prusa Firmware (Configuration_adv.h) -static const float DEFAULT_EXTRUDE_FACTOR_OVERRIDE_PERCENTAGE = 1.0f; // 100 percent - -static const float PREVIOUS_FEEDRATE_THRESHOLD = 0.0001f; - -#if ENABLE_MOVE_STATS -static const std::string MOVE_TYPE_STR[Slic3r::GCodeTimeEstimator::Block::Num_Types] = -{ - "Noop", - "Retract", - "Unretract", - "Tool_change", - "Move", - "Extrude" -}; -#endif // ENABLE_MOVE_STATS - -namespace Slic3r { - void GCodeTimeEstimator::Feedrates::reset() - { - feedrate = 0.0f; - safe_feedrate = 0.0f; - ::memset(axis_feedrate, 0, Num_Axis * sizeof(float)); - ::memset(abs_axis_feedrate, 0, Num_Axis * sizeof(float)); - } - - float GCodeTimeEstimator::Block::Trapezoid::acceleration_time(float entry_feedrate, float acceleration) const - { - return acceleration_time_from_distance(entry_feedrate, accelerate_until, acceleration); - } - - float GCodeTimeEstimator::Block::Trapezoid::cruise_time() const - { - return (cruise_feedrate != 0.0f) ? cruise_distance() / cruise_feedrate : 0.0f; - } - - float GCodeTimeEstimator::Block::Trapezoid::deceleration_time(float distance, float acceleration) const - { - return acceleration_time_from_distance(cruise_feedrate, (distance - decelerate_after), -acceleration); - } - - float GCodeTimeEstimator::Block::Trapezoid::cruise_distance() const - { - return decelerate_after - accelerate_until; - } - - float GCodeTimeEstimator::Block::Trapezoid::acceleration_time_from_distance(float initial_feedrate, float distance, float acceleration) - { - return (acceleration != 0.0f) ? (speed_from_distance(initial_feedrate, distance, acceleration) - initial_feedrate) / acceleration : 0.0f; - } - - float GCodeTimeEstimator::Block::Trapezoid::speed_from_distance(float initial_feedrate, float distance, float acceleration) - { - // to avoid invalid negative numbers due to numerical imprecision - float value = std::max(0.0f, sqr(initial_feedrate) + 2.0f * acceleration * distance); - return ::sqrt(value); - } - - float GCodeTimeEstimator::Block::acceleration_time() const - { - return trapezoid.acceleration_time(feedrate.entry, acceleration); - } - - float GCodeTimeEstimator::Block::cruise_time() const - { - return trapezoid.cruise_time(); - } - - float GCodeTimeEstimator::Block::deceleration_time() const - { - return trapezoid.deceleration_time(distance, acceleration); - } - - float GCodeTimeEstimator::Block::cruise_distance() const - { - return trapezoid.cruise_distance(); - } - - void GCodeTimeEstimator::Block::calculate_trapezoid() - { - trapezoid.cruise_feedrate = feedrate.cruise; - - float accelerate_distance = std::max(0.0f, estimate_acceleration_distance(feedrate.entry, feedrate.cruise, acceleration)); - float decelerate_distance = std::max(0.0f, estimate_acceleration_distance(feedrate.cruise, feedrate.exit, -acceleration)); - float cruise_distance = distance - accelerate_distance - decelerate_distance; - - // Not enough space to reach the nominal feedrate. - // This means no cruising, and we'll have to use intersection_distance() to calculate when to abort acceleration - // and start braking in order to reach the exit_feedrate exactly at the end of this block. - if (cruise_distance < 0.0f) - { - accelerate_distance = std::clamp(intersection_distance(feedrate.entry, feedrate.exit, acceleration, distance), 0.0f, distance); - cruise_distance = 0.0f; - trapezoid.cruise_feedrate = Trapezoid::speed_from_distance(feedrate.entry, accelerate_distance, acceleration); - } - - trapezoid.accelerate_until = accelerate_distance; - trapezoid.decelerate_after = accelerate_distance + cruise_distance; - } - - float GCodeTimeEstimator::Block::max_allowable_speed(float acceleration, float target_velocity, float distance) - { - // to avoid invalid negative numbers due to numerical imprecision - float value = std::max(0.0f, sqr(target_velocity) - 2.0f * acceleration * distance); - return ::sqrt(value); - } - - float GCodeTimeEstimator::Block::estimate_acceleration_distance(float initial_rate, float target_rate, float acceleration) - { - return (acceleration == 0.0f) ? 0.0f : (sqr(target_rate) - sqr(initial_rate)) / (2.0f * acceleration); - } - - float GCodeTimeEstimator::Block::intersection_distance(float initial_rate, float final_rate, float acceleration, float distance) - { - return (acceleration == 0.0f) ? 0.0f : (2.0f * acceleration * distance - sqr(initial_rate) + sqr(final_rate)) / (4.0f * acceleration); - } - -#if ENABLE_MOVE_STATS - GCodeTimeEstimator::MoveStats::MoveStats() - : count(0) - , time(0.0f) - { - } -#endif // ENABLE_MOVE_STATS - - const std::string GCodeTimeEstimator::Normal_First_M73_Output_Placeholder_Tag = "; _TE_NORMAL_FIRST_M73_OUTPUT_PLACEHOLDER"; - const std::string GCodeTimeEstimator::Silent_First_M73_Output_Placeholder_Tag = "; _TE_SILENT_FIRST_M73_OUTPUT_PLACEHOLDER"; - 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"; - - const std::string GCodeTimeEstimator::Color_Change_Tag = "PRINT_COLOR_CHANGE"; - const std::string GCodeTimeEstimator::Pause_Print_Tag = "PRINT_PAUSE"; - - GCodeTimeEstimator::GCodeTimeEstimator(EMode mode) - : m_mode(mode) - { - reset(); - set_default(); - } - - void GCodeTimeEstimator::add_gcode_line(const std::string& gcode_line) - { - PROFILE_FUNC(); - m_parser.parse_line(gcode_line, - [this](GCodeReader &reader, const GCodeReader::GCodeLine &line) - { this->_process_gcode_line(reader, line); }); - } - - void GCodeTimeEstimator::add_gcode_block(const char *ptr) - { - PROFILE_FUNC(); - GCodeReader::GCodeLine gline; - auto action = [this](GCodeReader &reader, const GCodeReader::GCodeLine &line) - { this->_process_gcode_line(reader, line); }; - for (; *ptr != 0;) { - gline.reset(); - ptr = m_parser.parse_line(ptr, gline, action); - } - } - - void GCodeTimeEstimator::calculate_time(bool start_from_beginning) - { - PROFILE_FUNC(); - if (start_from_beginning) - _reset_time(); - _calculate_time(0); - - if (m_needs_custom_gcode_times && (m_custom_gcode_time_cache != 0.0f)) - m_custom_gcode_times.push_back({CustomGCode::ColorChange, m_custom_gcode_time_cache }); - -#if ENABLE_MOVE_STATS - _log_moves_stats(); -#endif // ENABLE_MOVE_STATS - } - -#if 0 - void GCodeTimeEstimator::calculate_time_from_text(const std::string& gcode) - { - reset(); - - m_parser.parse_buffer(gcode, - [this](GCodeReader &reader, const GCodeReader::GCodeLine &line) - { this->_process_gcode_line(reader, line); }); - - _calculate_time(0); - - if (m_needs_custom_gcode_times && (m_custom_gcode_time_cache != 0.0f)) - m_custom_gcode_times.push_back({ cgtColorChange, m_custom_gcode_time_cache }); - -#if ENABLE_MOVE_STATS - _log_moves_stats(); -#endif // ENABLE_MOVE_STATS - } - - void GCodeTimeEstimator::calculate_time_from_file(const std::string& file) - { - reset(); - - m_parser.parse_file(file, boost::bind(&GCodeTimeEstimator::_process_gcode_line, this, _1, _2)); - _calculate_time(0); - - if (m_needs_custom_gcode_times && (m_custom_gcode_time_cache != 0.0f)) - m_custom_gcode_times.push_back({ cgtColorChange, m_custom_gcode_time_cache }); - -#if ENABLE_MOVE_STATS - _log_moves_stats(); -#endif // ENABLE_MOVE_STATS - } - - void GCodeTimeEstimator::calculate_time_from_lines(const std::vector& gcode_lines) - { - reset(); - - auto action = [this](GCodeReader &reader, const GCodeReader::GCodeLine &line) - { this->_process_gcode_line(reader, line); }; - for (const std::string& line : gcode_lines) - m_parser.parse_line(line, action); - _calculate_time(0); - - if (m_needs_custom_gcode_times && (m_custom_gcode_time_cache != 0.0f)) - m_custom_gcode_times.push_back({ cgtColorChange, m_custom_gcode_time_cache}); - -#if ENABLE_MOVE_STATS - _log_moves_stats(); -#endif // ENABLE_MOVE_STATS - } -#endif - - 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 Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for reading.\n")); - - std::string path_tmp = filename + ".postprocess"; - - FILE* out = boost::nowide::fopen(path_tmp.c_str(), "wb"); - if (out == nullptr) - throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for writing.\n")); - - 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]; - - std::string gcode_line; - // buffer line to export only when greater than 64K to reduce writing calls - std::string export_line; - - // 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 Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nIs the disk full?\n")); - } - export_line.clear(); - }; - - GCodeReader parser; - 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_times.size()) || (data->g1_times[g1_line_id].first >= (int)g1_lines_count)); - float elapsed_time = -1.0f; - if (g1_line_id < (int)data->g1_times.size()) - { - const G1LineIdTime& map_item = data->g1_times[g1_line_id]; - if (map_item.first == g1_lines_count) - { - if (line.has_e()) - elapsed_time = map_item.second; - ++g1_line_id; - } - } - - if (elapsed_time != -1.0f) - { - float block_remaining_time = data->time - 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 * 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 Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nError while reading from file.\n")); - } - - // check tags - // remove Color_Change_Tag and Pause_Print_Tag - if (gcode_line == "; " + Color_Change_Tag || gcode_line == "; " + Pause_Print_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 Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' + - "Is " + path_tmp + " locked?" + '\n'); - - return true; - } - - void GCodeTimeEstimator::set_axis_position(EAxis axis, float position) - { - m_state.axis[axis].position = position; - } - - void GCodeTimeEstimator::set_axis_origin(EAxis axis, float position) - { - m_state.axis[axis].origin = position; - } - - void GCodeTimeEstimator::set_axis_max_feedrate(EAxis axis, float feedrate_mm_sec) - { - m_state.axis[axis].max_feedrate = feedrate_mm_sec; - } - - void GCodeTimeEstimator::set_axis_max_acceleration(EAxis axis, float acceleration) - { - m_state.axis[axis].max_acceleration = acceleration; - } - - void GCodeTimeEstimator::set_axis_max_jerk(EAxis axis, float jerk) - { - m_state.axis[axis].max_jerk = jerk; - } - - float GCodeTimeEstimator::get_axis_position(EAxis axis) const - { - return m_state.axis[axis].position; - } - - float GCodeTimeEstimator::get_axis_origin(EAxis axis) const - { - return m_state.axis[axis].origin; - } - - float GCodeTimeEstimator::get_axis_max_feedrate(EAxis axis) const - { - return m_state.axis[axis].max_feedrate; - } - - float GCodeTimeEstimator::get_axis_max_acceleration(EAxis axis) const - { - return m_state.axis[axis].max_acceleration; - } - - float GCodeTimeEstimator::get_axis_max_jerk(EAxis axis) const - { - return m_state.axis[axis].max_jerk; - } - - void GCodeTimeEstimator::set_feedrate(float feedrate_mm_sec) - { - m_state.feedrate = feedrate_mm_sec; - } - - float GCodeTimeEstimator::get_feedrate() const - { - return m_state.feedrate; - } - - void GCodeTimeEstimator::set_acceleration(float acceleration_mm_sec2) - { - m_state.acceleration = (m_state.max_acceleration == 0) ? - acceleration_mm_sec2 : - // Clamp the acceleration with the maximum. - std::min(m_state.max_acceleration, acceleration_mm_sec2); - } - - float GCodeTimeEstimator::get_acceleration() const - { - return m_state.acceleration; - } - - void GCodeTimeEstimator::set_max_acceleration(float acceleration_mm_sec2) - { - m_state.max_acceleration = acceleration_mm_sec2; - if (acceleration_mm_sec2 > 0) - m_state.acceleration = acceleration_mm_sec2; - } - - float GCodeTimeEstimator::get_max_acceleration() const - { - return m_state.max_acceleration; - } - - void GCodeTimeEstimator::set_retract_acceleration(float acceleration_mm_sec2) - { - m_state.retract_acceleration = acceleration_mm_sec2; - } - - float GCodeTimeEstimator::get_retract_acceleration() const - { - return m_state.retract_acceleration; - } - - void GCodeTimeEstimator::set_minimum_feedrate(float feedrate_mm_sec) - { - m_state.minimum_feedrate = feedrate_mm_sec; - } - - float GCodeTimeEstimator::get_minimum_feedrate() const - { - return m_state.minimum_feedrate; - } - - void GCodeTimeEstimator::set_minimum_travel_feedrate(float feedrate_mm_sec) - { - m_state.minimum_travel_feedrate = feedrate_mm_sec; - } - - float GCodeTimeEstimator::get_minimum_travel_feedrate() const - { - return m_state.minimum_travel_feedrate; - } - - void GCodeTimeEstimator::set_filament_load_times(const std::vector &filament_load_times) - { - m_state.filament_load_times.clear(); - for (double t : filament_load_times) - m_state.filament_load_times.push_back((float)t); - } - - void GCodeTimeEstimator::set_filament_unload_times(const std::vector &filament_unload_times) - { - m_state.filament_unload_times.clear(); - for (double t : filament_unload_times) - m_state.filament_unload_times.push_back((float)t); - } - - float GCodeTimeEstimator::get_filament_load_time(unsigned int id_extruder) - { - return - (m_state.filament_load_times.empty() || id_extruder == m_state.extruder_id_unloaded) ? - 0 : - (m_state.filament_load_times.size() <= id_extruder) ? - m_state.filament_load_times.front() : - m_state.filament_load_times[id_extruder]; - } - - float GCodeTimeEstimator::get_filament_unload_time(unsigned int id_extruder) - { - return - (m_state.filament_unload_times.empty() || id_extruder == m_state.extruder_id_unloaded) ? - 0 : - (m_state.filament_unload_times.size() <= id_extruder) ? - m_state.filament_unload_times.front() : - m_state.filament_unload_times[id_extruder]; - } - - void GCodeTimeEstimator::set_extrude_factor_override_percentage(float percentage) - { - m_state.extrude_factor_override_percentage = percentage; - } - - float GCodeTimeEstimator::get_extrude_factor_override_percentage() const - { - return m_state.extrude_factor_override_percentage; - } - - void GCodeTimeEstimator::set_dialect(GCodeFlavor dialect) - { - m_state.dialect = dialect; - } - - GCodeFlavor GCodeTimeEstimator::get_dialect() const - { - PROFILE_FUNC(); - return m_state.dialect; - } - - void GCodeTimeEstimator::set_units(GCodeTimeEstimator::EUnits units) - { - m_state.units = units; - } - - GCodeTimeEstimator::EUnits GCodeTimeEstimator::get_units() const - { - return m_state.units; - } - - void GCodeTimeEstimator::set_global_positioning_type(GCodeTimeEstimator::EPositioningType type) - { - m_state.global_positioning_type = type; - } - - GCodeTimeEstimator::EPositioningType GCodeTimeEstimator::get_global_positioning_type() const - { - return m_state.global_positioning_type; - } - - void GCodeTimeEstimator::set_e_local_positioning_type(GCodeTimeEstimator::EPositioningType type) - { - m_state.e_local_positioning_type = type; - } - - GCodeTimeEstimator::EPositioningType GCodeTimeEstimator::get_e_local_positioning_type() const - { - return m_state.e_local_positioning_type; - } - - int GCodeTimeEstimator::get_g1_line_id() const - { - return m_state.g1_line_id; - } - - void GCodeTimeEstimator::increment_g1_line_id() - { - ++m_state.g1_line_id; - } - - void GCodeTimeEstimator::reset_g1_line_id() - { - m_state.g1_line_id = 0; - } - - void GCodeTimeEstimator::set_extruder_id(unsigned int id) - { - m_state.extruder_id = id; - } - - unsigned int GCodeTimeEstimator::get_extruder_id() const - { - return m_state.extruder_id; - } - - void GCodeTimeEstimator::reset_extruder_id() - { - // Set the initial extruder ID to unknown. For the multi-material setup it means - // that all the filaments are parked in the MMU and no filament is loaded yet. - m_state.extruder_id = m_state.extruder_id_unloaded; - } - - void GCodeTimeEstimator::set_default() - { - set_units(Millimeters); - set_dialect(gcfRepRapSprinter); - set_global_positioning_type(Absolute); - set_e_local_positioning_type(Absolute); - - set_feedrate(DEFAULT_FEEDRATE); - // Setting the maximum acceleration to zero means that the there is no limit and the G-code - // is allowed to set excessive values. - set_max_acceleration(0); - set_acceleration(DEFAULT_ACCELERATION); - set_retract_acceleration(DEFAULT_RETRACT_ACCELERATION); - set_minimum_feedrate(DEFAULT_MINIMUM_FEEDRATE); - set_minimum_travel_feedrate(DEFAULT_MINIMUM_TRAVEL_FEEDRATE); - set_extrude_factor_override_percentage(DEFAULT_EXTRUDE_FACTOR_OVERRIDE_PERCENTAGE); - - for (unsigned char a = X; a < Num_Axis; ++a) - { - EAxis axis = (EAxis)a; - set_axis_max_feedrate(axis, DEFAULT_AXIS_MAX_FEEDRATE[a]); - set_axis_max_acceleration(axis, DEFAULT_AXIS_MAX_ACCELERATION[a]); - set_axis_max_jerk(axis, DEFAULT_AXIS_MAX_JERK[a]); - } - - m_state.filament_load_times.clear(); - m_state.filament_unload_times.clear(); - } - - void GCodeTimeEstimator::reset() - { - _reset_time(); -#if ENABLE_MOVE_STATS - _moves_stats.clear(); -#endif // ENABLE_MOVE_STATS - _reset_blocks(); - _reset(); - } - - float GCodeTimeEstimator::get_time() const - { - return m_time; - } - - std::string GCodeTimeEstimator::get_time_dhms() const - { - return _get_time_dhms(get_time()); - } - - std::string GCodeTimeEstimator::get_time_dhm() const - { - return _get_time_dhm(get_time()); - } - - std::string GCodeTimeEstimator::get_time_minutes() const - { - return _get_time_minutes(get_time()); - } - - std::vector> GCodeTimeEstimator::get_custom_gcode_times() const - { - return m_custom_gcode_times; - } - - std::vector GCodeTimeEstimator::get_color_times_dhms(bool include_remaining) const - { - std::vector ret; - float total_time = 0.0f; -// for (float t : m_color_times) - for (auto t : m_custom_gcode_times) - { - std::string time = _get_time_dhms(t.second); - if (include_remaining) - { - time += " ("; - time += _get_time_dhms(m_time - total_time); - time += ")"; - } - total_time += t.second; - ret.push_back(time); - } - return ret; - } - - std::vector GCodeTimeEstimator::get_color_times_minutes(bool include_remaining) const - { - std::vector ret; - float total_time = 0.0f; -// for (float t : m_color_times) - for (auto t : m_custom_gcode_times) - { - std::string time = _get_time_minutes(t.second); - if (include_remaining) - { - time += " ("; - time += _get_time_minutes(m_time - total_time); - time += ")"; - } - total_time += t.second; - } - return ret; - } - - std::vector> GCodeTimeEstimator::get_custom_gcode_times_dhm(bool include_remaining) const - { - std::vector> ret; - - float total_time = 0.0f; - for (auto t : m_custom_gcode_times) - { - std::string time = _get_time_dhm(t.second); - if (include_remaining) - { - time += " ("; - time += _get_time_dhm(m_time - total_time); - time += ")"; - } - total_time += t.second; - ret.push_back({t.first, time}); - } - - return ret; - } - - // Return an estimate of the memory consumed by the time estimator. - size_t GCodeTimeEstimator::memory_used() const - { - size_t out = sizeof(*this); - out += SLIC3R_STDVEC_MEMSIZE(this->m_blocks, Block); - out += SLIC3R_STDVEC_MEMSIZE(this->m_g1_times, G1LineIdTime); - return out; - } - - void GCodeTimeEstimator::_reset() - { - m_curr.reset(); - m_prev.reset(); - - set_axis_position(X, 0.0f); - set_axis_position(Y, 0.0f); - set_axis_position(Z, 0.0f); - set_axis_origin(X, 0.0f); - set_axis_origin(Y, 0.0f); - set_axis_origin(Z, 0.0f); - - if (get_e_local_positioning_type() == Absolute) - set_axis_position(E, 0.0f); - - reset_extruder_id(); - reset_g1_line_id(); - m_g1_times.clear(); - - m_needs_custom_gcode_times = false; - m_custom_gcode_times.clear(); - m_custom_gcode_time_cache = 0.0f; - } - - void GCodeTimeEstimator::_reset_time() - { - m_time = 0.0f; - } - - void GCodeTimeEstimator::_reset_blocks() - { - m_blocks.clear(); - } - - void GCodeTimeEstimator::_calculate_time(size_t keep_last_n_blocks) - { - PROFILE_FUNC(); - - assert(keep_last_n_blocks <= m_blocks.size()); - - _forward_pass(); - _reverse_pass(); - _recalculate_trapezoids(); - - size_t n_blocks_process = m_blocks.size() - keep_last_n_blocks; - m_g1_times.reserve(m_g1_times.size() + n_blocks_process); - for (size_t i = 0; i < n_blocks_process; ++ i) - { - Block& block = m_blocks[i]; - float block_time = 0.0f; - block_time += block.acceleration_time(); - block_time += block.cruise_time(); - block_time += block.deceleration_time(); - m_time += block_time; - if (block.g1_line_id >= 0) - m_g1_times.emplace_back(block.g1_line_id, m_time); - -#if ENABLE_MOVE_STATS - MovesStatsMap::iterator it = _moves_stats.find(block.move_type); - if (it == _moves_stats.end()) - it = _moves_stats.insert(MovesStatsMap::value_type(block.move_type, MoveStats())).first; - - it->second.count += 1; - it->second.time += block_time; -#endif // ENABLE_MOVE_STATS - - m_custom_gcode_time_cache += block_time; - } - - if (keep_last_n_blocks) - m_blocks.erase(m_blocks.begin(), m_blocks.begin() + n_blocks_process); - else - m_blocks.clear(); - } - - void GCodeTimeEstimator::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLine& line) - { - PROFILE_FUNC(); - - // processes 'special' comments contained in line - if (_process_tags(line)) - return; - - std::string cmd = line.cmd(); - if (cmd.length() > 1) - { - switch (::toupper(cmd[0])) - { - case 'G': - { - switch (::atoi(&cmd[1])) - { - case 1: // Move - { - _processG1(line); - break; - } - case 4: // Dwell - { - _processG4(line); - break; - } - case 20: // Set Units to Inches - { - _processG20(line); - break; - } - case 21: // Set Units to Millimeters - { - _processG21(line); - break; - } - case 28: // Move to Origin (Home) - { - _processG28(line); - break; - } - case 90: // Set to Absolute Positioning - { - _processG90(line); - break; - } - case 91: // Set to Relative Positioning - { - _processG91(line); - break; - } - case 92: // Set Position - { - _processG92(line); - break; - } - } - - break; - } - case 'M': - { - switch (::atoi(&cmd[1])) - { - case 1: // Sleep or Conditional stop - { - _processM1(line); - break; - } - case 82: // Set extruder to absolute mode - { - _processM82(line); - break; - } - case 83: // Set extruder to relative mode - { - _processM83(line); - break; - } - case 109: // Set Extruder Temperature and Wait - { - _processM109(line); - break; - } - case 201: // Set max printing acceleration - { - _processM201(line); - break; - } - case 203: // Set maximum feedrate - { - _processM203(line); - break; - } - case 204: // Set default acceleration - { - _processM204(line); - break; - } - case 205: // Advanced settings - { - _processM205(line); - break; - } - case 221: // Set extrude factor override percentage - { - _processM221(line); - break; - } - case 566: // Set allowable instantaneous speed change - { - _processM566(line); - break; - } - case 702: // MK3 MMU2: Process the final filament unload. - { - _processM702(line); - break; - } - } - - break; - } - case 'T': // Select Tools - { - _processT(line); - break; - } - } - } - } - - void GCodeTimeEstimator::_processG1(const GCodeReader::GCodeLine& line) - { - auto axis_absolute_position = [this](GCodeTimeEstimator::EAxis axis, const GCodeReader::GCodeLine& lineG1) -> float - { - float current_absolute_position = get_axis_position(axis); - float current_origin = get_axis_origin(axis); - float lengthsScaleFactor = (get_units() == GCodeTimeEstimator::Inches) ? INCHES_TO_MM : 1.0f; - - bool is_relative = (get_global_positioning_type() == Relative); - if (axis == E) - is_relative |= (get_e_local_positioning_type() == Relative); - - if (lineG1.has(Slic3r::Axis(axis))) - { - float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor; - return is_relative ? current_absolute_position + ret : ret + current_origin; - } - else - return current_absolute_position; - }; - - // delta_pos must have size >= Num_Axis - auto move_length = [](const float* delta_pos) { - float xyz_length = std::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z])); - return (xyz_length > 0.0f) ? xyz_length : std::abs(delta_pos[E]); - }; - - // delta_pos must have size >= Num_Axis - auto is_extruder_only_move = [](const float* delta_pos) { - return (delta_pos[X] == 0.0f) && (delta_pos[Y] == 0.0f) && (delta_pos[Z] == 0.0f) && (delta_pos[E] != 0.0f); - }; - - PROFILE_FUNC(); - increment_g1_line_id(); - - // updates axes positions from line - float new_pos[Num_Axis]; - for (unsigned char a = X; a < Num_Axis; ++a) - { - new_pos[a] = axis_absolute_position((EAxis)a, line); - } - - // updates feedrate from line, if present - if (line.has_f()) - set_feedrate(std::max(line.f() * MMMIN_TO_MMSEC, get_minimum_feedrate())); - - // fills block data - Block block; - - // calculates block movement deltas - float max_abs_delta = 0.0f; - float delta_pos[Num_Axis]; - for (unsigned char a = X; a < Num_Axis; ++a) - { - delta_pos[a] = new_pos[a] - get_axis_position((EAxis)a); - max_abs_delta = std::max(max_abs_delta, std::abs(delta_pos[a])); - } - - // is it a move ? - if (max_abs_delta == 0.0f) - return; - - // calculates block feedrate - m_curr.feedrate = std::max(get_feedrate(), (delta_pos[E] == 0.0f) ? get_minimum_travel_feedrate() : get_minimum_feedrate()); - - block.distance = move_length(delta_pos); - float invDistance = 1.0f / block.distance; - - float min_feedrate_factor = 1.0f; - for (unsigned char a = X; a < Num_Axis; ++a) - { - m_curr.axis_feedrate[a] = m_curr.feedrate * delta_pos[a] * invDistance; - if (a == E) - m_curr.axis_feedrate[a] *= get_extrude_factor_override_percentage(); - - m_curr.abs_axis_feedrate[a] = std::abs(m_curr.axis_feedrate[a]); - if (m_curr.abs_axis_feedrate[a] > 0.0f) - min_feedrate_factor = std::min(min_feedrate_factor, get_axis_max_feedrate((EAxis)a) / m_curr.abs_axis_feedrate[a]); - } - - block.feedrate.cruise = min_feedrate_factor * m_curr.feedrate; - - if (min_feedrate_factor < 1.0f) - { - for (unsigned char a = X; a < Num_Axis; ++a) - { - m_curr.axis_feedrate[a] *= min_feedrate_factor; - m_curr.abs_axis_feedrate[a] *= min_feedrate_factor; - } - } - - // calculates block acceleration - float acceleration = is_extruder_only_move(delta_pos) ? get_retract_acceleration() : get_acceleration(); - - for (unsigned char a = X; a < Num_Axis; ++a) - { - float axis_max_acceleration = get_axis_max_acceleration((EAxis)a); - if (acceleration * std::abs(delta_pos[a]) * invDistance > axis_max_acceleration) - acceleration = axis_max_acceleration; - } - - block.acceleration = acceleration; - - // calculates block exit feedrate - m_curr.safe_feedrate = block.feedrate.cruise; - - for (unsigned char a = X; a < Num_Axis; ++a) - { - float axis_max_jerk = get_axis_max_jerk((EAxis)a); - if (m_curr.abs_axis_feedrate[a] > axis_max_jerk) - m_curr.safe_feedrate = std::min(m_curr.safe_feedrate, axis_max_jerk); - } - - block.feedrate.exit = m_curr.safe_feedrate; - - // calculates block entry feedrate - float vmax_junction = m_curr.safe_feedrate; - if (!m_blocks.empty() && (m_prev.feedrate > PREVIOUS_FEEDRATE_THRESHOLD)) - { - bool prev_speed_larger = m_prev.feedrate > block.feedrate.cruise; - float smaller_speed_factor = prev_speed_larger ? (block.feedrate.cruise / m_prev.feedrate) : (m_prev.feedrate / block.feedrate.cruise); - // Pick the smaller of the nominal speeds. Higher speed shall not be achieved at the junction during coasting. - vmax_junction = prev_speed_larger ? block.feedrate.cruise : m_prev.feedrate; - - float v_factor = 1.0f; - bool limited = false; - - for (unsigned char a = X; a < Num_Axis; ++a) - { - // Limit an axis. We have to differentiate coasting from the reversal of an axis movement, or a full stop. - float v_exit = m_prev.axis_feedrate[a]; - float v_entry = m_curr.axis_feedrate[a]; - - if (prev_speed_larger) - v_exit *= smaller_speed_factor; - - if (limited) - { - v_exit *= v_factor; - v_entry *= v_factor; - } - - // Calculate the jerk depending on whether the axis is coasting in the same direction or reversing a direction. - float jerk = - (v_exit > v_entry) ? - (((v_entry > 0.0f) || (v_exit < 0.0f)) ? - // coasting - (v_exit - v_entry) : - // axis reversal - std::max(v_exit, -v_entry)) : - // v_exit <= v_entry - (((v_entry < 0.0f) || (v_exit > 0.0f)) ? - // coasting - (v_entry - v_exit) : - // axis reversal - std::max(-v_exit, v_entry)); - - float axis_max_jerk = get_axis_max_jerk((EAxis)a); - if (jerk > axis_max_jerk) - { - v_factor *= axis_max_jerk / jerk; - limited = true; - } - } - - if (limited) - vmax_junction *= v_factor; - - // Now the transition velocity is known, which maximizes the shared exit / entry velocity while - // respecting the jerk factors, it may be possible, that applying separate safe exit / entry velocities will achieve faster prints. - float vmax_junction_threshold = vmax_junction * 0.99f; - - // Not coasting. The machine will stop and start the movements anyway, better to start the segment from start. - if ((m_prev.safe_feedrate > vmax_junction_threshold) && (m_curr.safe_feedrate > vmax_junction_threshold)) - vmax_junction = m_curr.safe_feedrate; - } - - float v_allowable = Block::max_allowable_speed(-acceleration, m_curr.safe_feedrate, block.distance); - block.feedrate.entry = std::min(vmax_junction, v_allowable); - - block.max_entry_speed = vmax_junction; - block.flags.nominal_length = (block.feedrate.cruise <= v_allowable); - block.flags.recalculate = true; - block.safe_feedrate = m_curr.safe_feedrate; - - // calculates block trapezoid - block.calculate_trapezoid(); - - // updates previous - m_prev = m_curr; - - // updates axis positions - for (unsigned char a = X; a < Num_Axis; ++a) - { - set_axis_position((EAxis)a, new_pos[a]); - } - -#if ENABLE_MOVE_STATS - // detects block move type - block.move_type = Block::Noop; - - if (delta_pos[E] < 0.0f) - { - if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f) || (delta_pos[Z] != 0.0f)) - block.move_type = Block::Move; - else - block.move_type = Block::Retract; - } - else if (delta_pos[E] > 0.0f) - { - if ((delta_pos[X] == 0.0f) && (delta_pos[Y] == 0.0f) && (delta_pos[Z] == 0.0f)) - block.move_type = Block::Unretract; - else if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f)) - block.move_type = Block::Extrude; - } - else if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f) || (delta_pos[Z] != 0.0f)) - block.move_type = Block::Move; -#endif // ENABLE_MOVE_STATS - - // adds block to blocks list - block.g1_line_id = this->get_g1_line_id(); - m_blocks.emplace_back(block); - - if (m_blocks.size() > planner_refresh_if_larger) - _calculate_time(planner_queue_size); - } - - void GCodeTimeEstimator::_processG4(const GCodeReader::GCodeLine& line) - { - PROFILE_FUNC(); - GCodeFlavor dialect = get_dialect(); - - float value; - float extra_time = 0.f; - if (line.has_value('P', value)) - extra_time += value * MILLISEC_TO_SEC; - - // see: http://reprap.org/wiki/G-code#G4:_Dwell - if ((dialect == gcfRepetier) || - (dialect == gcfMarlin) || - (dialect == gcfSmoothie) || - (dialect == gcfRepRapSprinter) || - (dialect == gcfRepRapFirmware)) - { - if (line.has_value('S', value)) - extra_time += value; - } - - _simulate_st_synchronize(extra_time); - } - - void GCodeTimeEstimator::_processG20(const GCodeReader::GCodeLine& line) - { - PROFILE_FUNC(); - set_units(Inches); - } - - void GCodeTimeEstimator::_processG21(const GCodeReader::GCodeLine& line) - { - PROFILE_FUNC(); - set_units(Millimeters); - } - - void GCodeTimeEstimator::_processG28(const GCodeReader::GCodeLine& line) - { - PROFILE_FUNC(); - // TODO - } - - void GCodeTimeEstimator::_processG90(const GCodeReader::GCodeLine& line) - { - PROFILE_FUNC(); - set_global_positioning_type(Absolute); - } - - void GCodeTimeEstimator::_processG91(const GCodeReader::GCodeLine& line) - { - PROFILE_FUNC(); - set_global_positioning_type(Relative); - } - - void GCodeTimeEstimator::_processG92(const GCodeReader::GCodeLine& line) - { - PROFILE_FUNC(); - float lengthsScaleFactor = (get_units() == Inches) ? INCHES_TO_MM : 1.0f; - bool anyFound = false; - - if (line.has_x()) - { - set_axis_origin(X, get_axis_position(X) - line.x() * lengthsScaleFactor); - anyFound = true; - } - - if (line.has_y()) - { - set_axis_origin(Y, get_axis_position(Y) - line.y() * lengthsScaleFactor); - anyFound = true; - } - - if (line.has_z()) - { - set_axis_origin(Z, get_axis_position(Z) - line.z() * lengthsScaleFactor); - anyFound = true; - } - - if (line.has_e()) - { - // extruder coordinate can grow to the point where its float representation does not allow for proper addition with small increments, - // we set the value taken from the G92 line as the new current position for it - set_axis_position(E, line.e() * lengthsScaleFactor); - anyFound = true; - } - else - _simulate_st_synchronize(0.f); - - if (!anyFound) - { - for (unsigned char a = X; a < Num_Axis; ++a) - { - set_axis_origin((EAxis)a, get_axis_position((EAxis)a)); - } - } - } - - void GCodeTimeEstimator::_processM1(const GCodeReader::GCodeLine& line) - { - PROFILE_FUNC(); - _simulate_st_synchronize(0.f); - } - - void GCodeTimeEstimator::_processM82(const GCodeReader::GCodeLine& line) - { - PROFILE_FUNC(); - set_e_local_positioning_type(Absolute); - } - - void GCodeTimeEstimator::_processM83(const GCodeReader::GCodeLine& line) - { - PROFILE_FUNC(); - set_e_local_positioning_type(Relative); - } - - void GCodeTimeEstimator::_processM109(const GCodeReader::GCodeLine& line) - { - PROFILE_FUNC(); - // TODO - } - - void GCodeTimeEstimator::_processM201(const GCodeReader::GCodeLine& line) - { - PROFILE_FUNC(); - GCodeFlavor dialect = get_dialect(); - - // see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration - float factor = ((dialect != gcfRepRapSprinter && dialect != gcfRepRapFirmware) && (get_units() == GCodeTimeEstimator::Inches)) ? INCHES_TO_MM : 1.0f; - - if (line.has_x()) - set_axis_max_acceleration(X, line.x() * factor); - - if (line.has_y()) - set_axis_max_acceleration(Y, line.y() * factor); - - if (line.has_z()) - set_axis_max_acceleration(Z, line.z() * factor); - - if (line.has_e()) - set_axis_max_acceleration(E, line.e() * factor); - } - - void GCodeTimeEstimator::_processM203(const GCodeReader::GCodeLine& line) - { - PROFILE_FUNC(); - GCodeFlavor dialect = get_dialect(); - - // see http://reprap.org/wiki/G-code#M203:_Set_maximum_feedrate - if (dialect == gcfRepetier) - return; - - // see http://reprap.org/wiki/G-code#M203:_Set_maximum_feedrate - // http://smoothieware.org/supported-g-codes - float factor = (dialect == gcfMarlin || dialect == gcfSmoothie) ? 1.0f : MMMIN_TO_MMSEC; - - if (line.has_x()) - set_axis_max_feedrate(X, line.x() * factor); - - if (line.has_y()) - set_axis_max_feedrate(Y, line.y() * factor); - - if (line.has_z()) - set_axis_max_feedrate(Z, line.z() * factor); - - if (line.has_e()) - set_axis_max_feedrate(E, line.e() * factor); - } - - void GCodeTimeEstimator::_processM204(const GCodeReader::GCodeLine& line) - { - PROFILE_FUNC(); - float value; - if (line.has_value('S', value)) { - // Legacy acceleration format. This format is used by the legacy Marlin, MK2 or MK3 firmware, - // and it is also generated by Slic3r to control acceleration per extrusion type - // (there is a separate acceleration settings in Slicer for perimeter, first layer etc). - set_acceleration(value); - if (line.has_value('T', value)) - set_retract_acceleration(value); - } else { - // New acceleration format, compatible with the upstream Marlin. - if (line.has_value('P', value)) - set_acceleration(value); - if (line.has_value('R', value)) - set_retract_acceleration(value); - if (line.has_value('T', value)) { - // Interpret the T value as the travel acceleration in the new Marlin format. - //FIXME Prusa3D firmware currently does not support travel acceleration value independent from the extruding acceleration value. - // set_travel_acceleration(value); - } - } - } - - void GCodeTimeEstimator::_processM205(const GCodeReader::GCodeLine& line) - { - PROFILE_FUNC(); - if (line.has_x()) - { - float max_jerk = line.x(); - set_axis_max_jerk(X, max_jerk); - set_axis_max_jerk(Y, max_jerk); - } - - if (line.has_y()) - set_axis_max_jerk(Y, line.y()); - - if (line.has_z()) - set_axis_max_jerk(Z, line.z()); - - if (line.has_e()) - set_axis_max_jerk(E, line.e()); - - float value; - if (line.has_value('S', value)) - set_minimum_feedrate(value); - - if (line.has_value('T', value)) - set_minimum_travel_feedrate(value); - } - - void GCodeTimeEstimator::_processM221(const GCodeReader::GCodeLine& line) - { - PROFILE_FUNC(); - float value_s; - float value_t; - if (line.has_value('S', value_s) && !line.has_value('T', value_t)) - set_extrude_factor_override_percentage(value_s * 0.01f); - } - - void GCodeTimeEstimator::_processM566(const GCodeReader::GCodeLine& line) - { - PROFILE_FUNC(); - if (line.has_x()) - set_axis_max_jerk(X, line.x() * MMMIN_TO_MMSEC); - - if (line.has_y()) - set_axis_max_jerk(Y, line.y() * MMMIN_TO_MMSEC); - - if (line.has_z()) - set_axis_max_jerk(Z, line.z() * MMMIN_TO_MMSEC); - - if (line.has_e()) - set_axis_max_jerk(E, line.e() * MMMIN_TO_MMSEC); - } - - void GCodeTimeEstimator::_processM702(const GCodeReader::GCodeLine& line) - { - PROFILE_FUNC(); - if (line.has('C')) { - // MK3 MMU2 specific M code: - // M702 C is expected to be sent by the custom end G-code when finalizing a print. - // The MK3 unit shall unload and park the active filament into the MMU2 unit. - float extra_time = get_filament_unload_time(get_extruder_id()); - reset_extruder_id(); - _simulate_st_synchronize(extra_time); - } - } - - void GCodeTimeEstimator::_processT(const GCodeReader::GCodeLine& line) - { - std::string cmd = line.cmd(); - if (cmd.length() > 1) - { - unsigned int id = (unsigned int)::strtol(cmd.substr(1).c_str(), nullptr, 10); - if (get_extruder_id() != id) - { - // Specific to the MK3 MMU2: The initial extruder ID is set to -1 indicating - // that the filament is parked in the MMU2 unit and there is nothing to be unloaded yet. - float extra_time = get_filament_unload_time(get_extruder_id()); - set_extruder_id(id); - extra_time += get_filament_load_time(get_extruder_id()); - _simulate_st_synchronize(extra_time); - } - } - } - - bool GCodeTimeEstimator::_process_tags(const GCodeReader::GCodeLine& line) - { - std::string comment = line.comment(); - - // Color_Change_Tag - size_t pos = comment.find(Color_Change_Tag); - if (pos != comment.npos) - { - _process_custom_gcode_tag(CustomGCode::ColorChange); - return true; - } - - // Pause_Print_Tag - pos = comment.find(Pause_Print_Tag); - if (pos != comment.npos) - { - _process_custom_gcode_tag(CustomGCode::PausePrint); - return true; - } - - return false; - } - - void GCodeTimeEstimator::_process_custom_gcode_tag(CustomGCode::Type code) - { - PROFILE_FUNC(); - m_needs_custom_gcode_times = true; - //FIXME this simulates st_synchronize! is it correct? - // The estimated time may be longer than the real print time. - _simulate_st_synchronize(0.f); - if (m_custom_gcode_time_cache != 0.0f) - { - m_custom_gcode_times.push_back({code, m_custom_gcode_time_cache}); - m_custom_gcode_time_cache = 0.0f; - } - } - - void GCodeTimeEstimator::_simulate_st_synchronize(float extra_time) - { - PROFILE_FUNC(); - m_time += extra_time; - m_custom_gcode_time_cache += extra_time; - _calculate_time(0); - } - - void GCodeTimeEstimator::_forward_pass() - { - PROFILE_FUNC(); - for (int i = 0; i + 1 < (int)m_blocks.size(); ++i) - _planner_forward_pass_kernel(m_blocks[i], m_blocks[i + 1]); - } - - void GCodeTimeEstimator::_reverse_pass() - { - PROFILE_FUNC(); - for (int i = (int)m_blocks.size() - 1; i > 0; -- i) - _planner_reverse_pass_kernel(m_blocks[i - 1], m_blocks[i]); - } - - void GCodeTimeEstimator::_planner_forward_pass_kernel(Block& prev, Block& curr) - { - PROFILE_FUNC(); - // If the previous block is an acceleration block, but it is not long enough to complete the - // full speed change within the block, we need to adjust the entry speed accordingly. Entry - // speeds have already been reset, maximized, and reverse planned by reverse planner. - // If nominal length is true, max junction speed is guaranteed to be reached. No need to recheck. - if (!prev.flags.nominal_length) - { - if (prev.feedrate.entry < curr.feedrate.entry) - { - float entry_speed = std::min(curr.feedrate.entry, Block::max_allowable_speed(-prev.acceleration, prev.feedrate.entry, prev.distance)); - - // Check for junction speed change - if (curr.feedrate.entry != entry_speed) - { - curr.feedrate.entry = entry_speed; - curr.flags.recalculate = true; - } - } - } - } - - void GCodeTimeEstimator::_planner_reverse_pass_kernel(Block& curr, Block& next) - { - // If entry speed is already at the maximum entry speed, no need to recheck. Block is cruising. - // If not, block in state of acceleration or deceleration. Reset entry speed to maximum and - // check for maximum allowable speed reductions to ensure maximum possible planned speed. - if (curr.feedrate.entry != curr.max_entry_speed) - { - // If nominal length true, max junction speed is guaranteed to be reached. Only compute - // for max allowable speed if block is decelerating and nominal length is false. - if (!curr.flags.nominal_length && (curr.max_entry_speed > next.feedrate.entry)) - curr.feedrate.entry = std::min(curr.max_entry_speed, Block::max_allowable_speed(-curr.acceleration, next.feedrate.entry, curr.distance)); - else - curr.feedrate.entry = curr.max_entry_speed; - - curr.flags.recalculate = true; - } - } - - void GCodeTimeEstimator::_recalculate_trapezoids() - { - PROFILE_FUNC(); - Block* curr = nullptr; - Block* next = nullptr; - - for (size_t i = 0; i < m_blocks.size(); ++ i) - { - Block& b = m_blocks[i]; - - curr = next; - next = &b; - - if (curr != nullptr) - { - // Recalculate if current block entry or exit junction speed has changed. - if (curr->flags.recalculate || next->flags.recalculate) - { - // NOTE: Entry and exit factors always > 0 by all previous logic operations. - Block block = *curr; - block.feedrate.exit = next->feedrate.entry; - block.calculate_trapezoid(); - curr->trapezoid = block.trapezoid; - curr->flags.recalculate = false; // Reset current only to ensure next trapezoid is computed - } - } - } - - // Last/newest block in buffer. Always recalculated. - if (next != nullptr) - { - Block block = *next; - block.feedrate.exit = next->safe_feedrate; - block.calculate_trapezoid(); - next->trapezoid = block.trapezoid; - next->flags.recalculate = false; - } - } - - std::string GCodeTimeEstimator::_get_time_dhms(float time_in_secs) - { - int days = (int)(time_in_secs / 86400.0f); - time_in_secs -= (float)days * 86400.0f; - int hours = (int)(time_in_secs / 3600.0f); - time_in_secs -= (float)hours * 3600.0f; - int minutes = (int)(time_in_secs / 60.0f); - time_in_secs -= (float)minutes * 60.0f; - - char buffer[64]; - if (days > 0) - ::sprintf(buffer, "%dd %dh %dm %ds", days, hours, minutes, (int)time_in_secs); - else if (hours > 0) - ::sprintf(buffer, "%dh %dm %ds", hours, minutes, (int)time_in_secs); - else if (minutes > 0) - ::sprintf(buffer, "%dm %ds", minutes, (int)time_in_secs); - else - ::sprintf(buffer, "%ds", (int)time_in_secs); - - return buffer; - } - - std::string GCodeTimeEstimator::_get_time_dhm(float time_in_secs) - { - char buffer[64]; - - int minutes = int(std::round(time_in_secs / 60.)); - if (minutes <= 0) { - ::sprintf(buffer, "%ds", (int)time_in_secs); - } else { - int days = minutes / 1440; - minutes -= days * 1440; - int hours = minutes / 60; - minutes -= hours * 60; - if (days > 0) - ::sprintf(buffer, "%dd %dh %dm", days, hours, minutes); - else if (hours > 0) - ::sprintf(buffer, "%dh %dm", hours, minutes); - else - ::sprintf(buffer, "%dm", minutes); - } - - return buffer; - } - - std::string GCodeTimeEstimator::_get_time_minutes(float time_in_secs) - { - return std::to_string((int)(::roundf(time_in_secs / 60.0f))); - } - -#if ENABLE_MOVE_STATS - void GCodeTimeEstimator::_log_moves_stats() const - { - float moves_count = 0.0f; - for (const MovesStatsMap::value_type& move : _moves_stats) - { - moves_count += (float)move.second.count; - } - - for (const MovesStatsMap::value_type& move : _moves_stats) - { - std::cout << MOVE_TYPE_STR[move.first]; - std::cout << ": count " << move.second.count << " (" << 100.0f * (float)move.second.count / moves_count << "%)"; - std::cout << " - time: " << move.second.time << "s (" << 100.0f * move.second.time / m_time << "%)"; - std::cout << std::endl; - } - std::cout << std::endl; - } -#endif // ENABLE_MOVE_STATS -} - -#endif // !ENABLE_GCODE_VIEWER diff --git a/src/libslic3r/GCodeTimeEstimator.hpp b/src/libslic3r/GCodeTimeEstimator.hpp deleted file mode 100644 index 0dd3407cb..000000000 --- a/src/libslic3r/GCodeTimeEstimator.hpp +++ /dev/null @@ -1,488 +0,0 @@ -#ifndef slic3r_GCodeTimeEstimator_hpp_ -#define slic3r_GCodeTimeEstimator_hpp_ - -#include "libslic3r.h" -#include "PrintConfig.hpp" -#include "GCodeReader.hpp" -#include "CustomGCode.hpp" - -#if !ENABLE_GCODE_VIEWER - -#define ENABLE_MOVE_STATS 0 - -namespace Slic3r { - - // - // Some of the algorithms used by class GCodeTimeEstimator were inpired by - // Cura Engine's class TimeEstimateCalculator - // https://github.com/Ultimaker/CuraEngine/blob/master/src/timeEstimate.h - // - class GCodeTimeEstimator - { - public: - static const std::string Normal_First_M73_Output_Placeholder_Tag; - static const std::string Silent_First_M73_Output_Placeholder_Tag; - static const std::string Normal_Last_M73_Output_Placeholder_Tag; - static const std::string Silent_Last_M73_Output_Placeholder_Tag; - - static const std::string Color_Change_Tag; - static const std::string Pause_Print_Tag; - - enum EMode : unsigned char - { - Normal, - Silent - }; - - enum EUnits : unsigned char - { - Millimeters, - Inches - }; - - enum EAxis : unsigned char - { - X, - Y, - Z, - E, - Num_Axis - }; - - enum EPositioningType : unsigned char - { - Absolute, - Relative - }; - - private: - struct Axis - { - float position; // mm - float origin; // mm - float max_feedrate; // mm/s - float max_acceleration; // mm/s^2 - float max_jerk; // mm/s - }; - - struct Feedrates - { - float feedrate; // mm/s - float axis_feedrate[Num_Axis]; // mm/s - float abs_axis_feedrate[Num_Axis]; // mm/s - float safe_feedrate; // mm/s - - void reset(); - }; - - struct State - { - GCodeFlavor dialect; - EUnits units; - EPositioningType global_positioning_type; - EPositioningType e_local_positioning_type; - Axis axis[Num_Axis]; - float feedrate; // mm/s - float acceleration; // mm/s^2 - // hard limit for the acceleration, to which the firmware will clamp. - float max_acceleration; // mm/s^2 - float retract_acceleration; // mm/s^2 - float minimum_feedrate; // mm/s - float minimum_travel_feedrate; // mm/s - float extrude_factor_override_percentage; - // Additional load / unload times for a filament exchange sequence. - std::vector filament_load_times; - std::vector filament_unload_times; - unsigned int g1_line_id; - // extruder_id is currently used to correctly calculate filament load / unload times - // into the total print time. This is currently only really used by the MK3 MMU2: - // Extruder id (-1) means no filament is loaded yet, all the filaments are parked in the MK3 MMU2 unit. - static const unsigned int extruder_id_unloaded = (unsigned int)-1; - unsigned int extruder_id; - }; - - public: - struct Block - { -#if ENABLE_MOVE_STATS - enum EMoveType : unsigned char - { - Noop, - Retract, - Unretract, - Tool_change, - Move, - Extrude, - Num_Types - }; -#endif // ENABLE_MOVE_STATS - - struct FeedrateProfile - { - float entry; // mm/s - float cruise; // mm/s - float exit; // mm/s - }; - - struct Trapezoid - { - float accelerate_until; // mm - float decelerate_after; // mm - float cruise_feedrate; // mm/sec - - float acceleration_time(float entry_feedrate, float acceleration) const; - float cruise_time() const; - float deceleration_time(float distance, float acceleration) const; - float cruise_distance() const; - - // This function gives the time needed to accelerate from an initial speed to reach a final distance. - static float acceleration_time_from_distance(float initial_feedrate, float distance, float acceleration); - - // This function gives the final speed while accelerating at the given constant acceleration from the given initial speed along the given distance. - static float speed_from_distance(float initial_feedrate, float distance, float acceleration); - }; - - struct Flags - { - bool recalculate; - bool nominal_length; - }; - -#if ENABLE_MOVE_STATS - EMoveType move_type; -#endif // ENABLE_MOVE_STATS - Flags flags; - - float distance; // mm - float acceleration; // mm/s^2 - float max_entry_speed; // mm/s - float safe_feedrate; // mm/s - - FeedrateProfile feedrate; - Trapezoid trapezoid; - - // Ordnary index of this G1 line in the file. - int g1_line_id { -1 }; - - // Returns the time spent accelerating toward cruise speed, in seconds - float acceleration_time() const; - - // Returns the time spent at cruise speed, in seconds - float cruise_time() const; - - // Returns the time spent decelerating from cruise speed, in seconds - float deceleration_time() const; - - // Returns the distance covered at cruise speed, in mm - float cruise_distance() const; - - // Calculates this block's trapezoid - void calculate_trapezoid(); - - // Calculates the maximum allowable speed at this point when you must be able to reach target_velocity using the - // acceleration within the allotted distance. - static float max_allowable_speed(float acceleration, float target_velocity, float distance); - - // Calculates the distance (not time) it takes to accelerate from initial_rate to target_rate using the given acceleration: - static float estimate_acceleration_distance(float initial_rate, float target_rate, float acceleration); - - // This function gives you the point at which you must start braking (at the rate of -acceleration) if - // you started at speed initial_rate and accelerated until this point and want to end at the final_rate after - // a total travel of distance. This can be used to compute the intersection point between acceleration and - // deceleration in the cases where the trapezoid has no plateau (i.e. never reaches maximum speed) - static float intersection_distance(float initial_rate, float final_rate, float acceleration, float distance); - }; - - typedef std::vector BlocksList; - -#if ENABLE_MOVE_STATS - struct MoveStats - { - unsigned int count; - float time; - - MoveStats(); - }; - - typedef std::map MovesStatsMap; -#endif // ENABLE_MOVE_STATS - - public: - typedef std::pair G1LineIdTime; - typedef std::vector G1LineIdsTimes; - - struct PostProcessData - { - const G1LineIdsTimes& g1_times; - float time; - }; - - private: - EMode m_mode; - GCodeReader m_parser; - State m_state; - Feedrates m_curr; - Feedrates m_prev; - BlocksList m_blocks; - // Size of the firmware planner queue. The old 8-bit Marlins usually just managed 16 trapezoidal blocks. - // Let's be conservative and plan for newer boards with more memory. - static constexpr size_t planner_queue_size = 64; - // The firmware recalculates last planner_queue_size trapezoidal blocks each time a new block is added. - // We are not simulating the firmware exactly, we calculate a sequence of blocks once a reasonable number of blocks accumulate. - static constexpr size_t planner_refresh_if_larger = planner_queue_size * 4; - // Map from g1 line id to its elapsed time from the start of the print. - G1LineIdsTimes m_g1_times; - float m_time; // s - - // data to calculate custom code times - bool m_needs_custom_gcode_times; - std::vector> m_custom_gcode_times; - float m_custom_gcode_time_cache; - -#if ENABLE_MOVE_STATS - MovesStatsMap _moves_stats; -#endif // ENABLE_MOVE_STATS - - public: - explicit GCodeTimeEstimator(EMode mode); - - // Adds the given gcode line - void add_gcode_line(const std::string& gcode_line); - - void add_gcode_block(const char *ptr); - void add_gcode_block(const std::string &str) { this->add_gcode_block(str.c_str()); } - - // Calculates the time estimate from the gcode lines added using add_gcode_line() or add_gcode_block() - // start_from_beginning: - // if set to true all blocks will be used to calculate the time estimate, - // if set to false only the blocks not yet processed will be used and the calculated time will be added to the current calculated time - void calculate_time(bool start_from_beginning); - - // Calculates the time estimate from the given gcode in string format - //void calculate_time_from_text(const std::string& gcode); - - // Calculates the time estimate from the gcode contained in the file with the given filename - //void calculate_time_from_file(const std::string& file); - - // Calculates the time estimate from the gcode contained in given list of gcode lines - //void calculate_time_from_lines(const std::vector& gcode_lines); - - // Process the gcode contained in the file with the given filename, - // 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); - // Set current origin on the given axis with the given value - void set_axis_origin(EAxis axis, float position); - - void set_axis_max_feedrate(EAxis axis, float feedrate_mm_sec); - void set_axis_max_acceleration(EAxis axis, float acceleration); - void set_axis_max_jerk(EAxis axis, float jerk); - - // Returns current position on the given axis - float get_axis_position(EAxis axis) const; - // Returns current origin on the given axis - float get_axis_origin(EAxis axis) const; - - float get_axis_max_feedrate(EAxis axis) const; - float get_axis_max_acceleration(EAxis axis) const; - float get_axis_max_jerk(EAxis axis) const; - - void set_feedrate(float feedrate_mm_sec); - float get_feedrate() const; - - void set_acceleration(float acceleration_mm_sec2); - float get_acceleration() const; - - // Maximum acceleration for the machine. The firmware simulator will clamp the M204 Sxxx to this maximum. - void set_max_acceleration(float acceleration_mm_sec2); - float get_max_acceleration() const; - - void set_retract_acceleration(float acceleration_mm_sec2); - float get_retract_acceleration() const; - - void set_minimum_feedrate(float feedrate_mm_sec); - float get_minimum_feedrate() const; - - void set_minimum_travel_feedrate(float feedrate_mm_sec); - float get_minimum_travel_feedrate() const; - - void set_filament_load_times(const std::vector &filament_load_times); - void set_filament_unload_times(const std::vector &filament_unload_times); - float get_filament_load_time(unsigned int id_extruder); - float get_filament_unload_time(unsigned int id_extruder); - - void set_extrude_factor_override_percentage(float percentage); - float get_extrude_factor_override_percentage() const; - - void set_dialect(GCodeFlavor dialect); - GCodeFlavor get_dialect() const; - - void set_units(EUnits units); - EUnits get_units() const; - - void set_global_positioning_type(EPositioningType type); - EPositioningType get_global_positioning_type() const; - - void set_e_local_positioning_type(EPositioningType type); - EPositioningType get_e_local_positioning_type() const; - - int get_g1_line_id() const; - void increment_g1_line_id(); - void reset_g1_line_id(); - - void set_extrusion_axis(char axis) { m_parser.set_extrusion_axis(axis); } - - void set_extruder_id(unsigned int id); - unsigned int get_extruder_id() const; - void reset_extruder_id(); - - void set_default(); - - // Call this method before to start adding lines using add_gcode_line() when reusing an instance of GCodeTimeEstimator - void reset(); - - // Returns the estimated time, in seconds - float get_time() const; - - // Returns the estimated time, in format DDd HHh MMm SSs - std::string get_time_dhms() const; - - // Returns the estimated time, in format DDd HHh MMm - std::string get_time_dhm() const; - - // Returns the estimated time, in minutes (integer) - std::string get_time_minutes() const; - - // Returns the estimated time, in seconds, for each custom gcode - std::vector> get_custom_gcode_times() const; - - // Returns the estimated time, in format DDd HHh MMm SSs, for each color - // If include_remaining==true the strings will be formatted as: "time for color (remaining time at color start)" - std::vector get_color_times_dhms(bool include_remaining) const; - - // Returns the estimated time, in minutes (integer), for each color - // If include_remaining==true the strings will be formatted as: "time for color (remaining time at color start)" - std::vector get_color_times_minutes(bool include_remaining) const; - - // Returns the estimated time, in format DDd HHh MMm, for each custom_gcode - // If include_remaining==true the strings will be formatted as: "time for custom_gcode (remaining time at color start)" - std::vector> get_custom_gcode_times_dhm(bool include_remaining) const; - - // 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_times, m_time }; } - - private: - void _reset(); - void _reset_time(); - void _reset_blocks(); - - // Calculates the time estimate - void _calculate_time(size_t keep_last_n_blocks); - - // Processes the given gcode line - void _process_gcode_line(GCodeReader&, const GCodeReader::GCodeLine& line); - - // Move - void _processG1(const GCodeReader::GCodeLine& line); - - // Dwell - void _processG4(const GCodeReader::GCodeLine& line); - - // Set Units to Inches - void _processG20(const GCodeReader::GCodeLine& line); - - // Set Units to Millimeters - void _processG21(const GCodeReader::GCodeLine& line); - - // Move to Origin (Home) - void _processG28(const GCodeReader::GCodeLine& line); - - // Set to Absolute Positioning - void _processG90(const GCodeReader::GCodeLine& line); - - // Set to Relative Positioning - void _processG91(const GCodeReader::GCodeLine& line); - - // Set Position - void _processG92(const GCodeReader::GCodeLine& line); - - // Sleep or Conditional stop - void _processM1(const GCodeReader::GCodeLine& line); - - // Set extruder to absolute mode - void _processM82(const GCodeReader::GCodeLine& line); - - // Set extruder to relative mode - void _processM83(const GCodeReader::GCodeLine& line); - - // Set Extruder Temperature and Wait - void _processM109(const GCodeReader::GCodeLine& line); - - // Set max printing acceleration - void _processM201(const GCodeReader::GCodeLine& line); - - // Set maximum feedrate - void _processM203(const GCodeReader::GCodeLine& line); - - // Set default acceleration - void _processM204(const GCodeReader::GCodeLine& line); - - // Advanced settings - void _processM205(const GCodeReader::GCodeLine& line); - - // Set extrude factor override percentage - void _processM221(const GCodeReader::GCodeLine& line); - - // Set allowable instantaneous speed change - void _processM566(const GCodeReader::GCodeLine& line); - - // Unload the current filament into the MK3 MMU2 unit at the end of print. - void _processM702(const GCodeReader::GCodeLine& line); - - // Processes T line (Select Tool) - void _processT(const GCodeReader::GCodeLine& line); - - // Processes the tags - // Returns true if any tag has been processed - bool _process_tags(const GCodeReader::GCodeLine& line); - - // Processes ColorChangeTag and PausePrintTag - void _process_custom_gcode_tag(CustomGCode::Type code); - - // Simulates firmware st_synchronize() call - void _simulate_st_synchronize(float additional_time); - - void _forward_pass(); - void _reverse_pass(); - - void _planner_forward_pass_kernel(Block& prev, Block& curr); - void _planner_reverse_pass_kernel(Block& curr, Block& next); - - void _recalculate_trapezoids(); - - // Returns the given time is seconds in format DDd HHh MMm SSs - static std::string _get_time_dhms(float time_in_secs); - // Returns the given time is minutes in format DDd HHh MMm - static std::string _get_time_dhm(float time_in_secs); - - // Returns the given, in minutes (integer) - static std::string _get_time_minutes(float time_in_secs); - -#if ENABLE_MOVE_STATS - void _log_moves_stats() const; -#endif // ENABLE_MOVE_STATS - }; - -} /* namespace Slic3r */ - -#endif // !ENABLE_GCODE_VIEWER - -#endif /* slic3r_GCodeTimeEstimator_hpp_ */