#ifndef slic3r_GCodeTimeEstimator_hpp_ #define slic3r_GCodeTimeEstimator_hpp_ #include "libslic3r.h" #include "PrintConfig.hpp" #include "GCodeReader.hpp" #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; 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 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 additional_time; // s 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 distance; // mm float accelerate_until; // mm float decelerate_after; // mm FeedrateProfile feedrate; float acceleration_time(float acceleration) const; float cruise_time() const; float deceleration_time(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 delta_pos[Num_Axis]; // mm float acceleration; // mm/s^2 float max_entry_speed; // mm/s float safe_feedrate; // mm/s FeedrateProfile feedrate; Trapezoid trapezoid; float elapsed_time; Block(); // Returns the length of the move covered by this block, in mm float move_length() const; // Returns true if this block is a retract/unretract move only float is_extruder_only_move() const; // Returns true if this block is a move with no extrusion float is_travel_move() const; // 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 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; State m_state; Feedrates m_curr; Feedrates m_prev; BlocksList m_blocks; // Map between g1 line id and blocks id, used to speed up export of remaining times G1LineIdToBlockIdMap m_g1_line_ids; // Index of the last block already st_synchronized int m_last_st_synchronized_block_id; float m_time; // s // data to calculate color print times bool m_needs_color_times; std::vector m_color_times; float m_color_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); 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; 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_extruder_id(unsigned int id); unsigned int get_extruder_id() const; void reset_extruder_id(); void add_additional_time(float timeSec); void set_additional_time(float timeSec); float get_additional_time() const; 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 minutes (integer) std::string get_time_minutes() const; // Returns the estimated time, in seconds, for each color std::vector get_color_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; // 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(); void _reset_blocks(); // Calculates the time estimate void _calculate_time(); // 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 color change tag void _process_color_change_tag(); // Simulates firmware st_synchronize() call void _simulate_st_synchronize(); 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, 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 /* slic3r_GCodeTimeEstimator_hpp_ */