From bc3d184d7cd5853677caaa75827b3ae5c19d9331 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 6 Dec 2017 14:12:10 +0100 Subject: [PATCH] time estimator wip --- cmake/modules/FindPerlEmbed.cmake | 2 - xs/src/libslic3r/GCode.cpp | 88 ++++ xs/src/libslic3r/GCodeTimeEstimator.cpp | 584 ++++++++++++++++++++++++ xs/src/libslic3r/GCodeTimeEstimator.hpp | 204 +++++++++ 4 files changed, 876 insertions(+), 2 deletions(-) diff --git a/cmake/modules/FindPerlEmbed.cmake b/cmake/modules/FindPerlEmbed.cmake index b12fc4063..7dc1e2b52 100644 --- a/cmake/modules/FindPerlEmbed.cmake +++ b/cmake/modules/FindPerlEmbed.cmake @@ -60,7 +60,6 @@ include(${PerlEmbed_TEMP_INCLUDE}) file(REMOVE ${PerlEmbed_TEMP_INCLUDE}) unset(PerlEmbed_TEMP_INCLUDE) -if (PerlEmbed_DEBUG) # First show the configuration extracted by FindPerl & FindPerlLibs: message(STATUS " PERL_INCLUDE_PATH = ${PERL_INCLUDE_PATH}") message(STATUS " PERL_LIBRARY = ${PERL_LIBRARY}") @@ -79,7 +78,6 @@ if (PerlEmbed_DEBUG) message(STATUS " LD = ${PerlEmbed_LD}") message(STATUS " PerlEmbed_LDFLAGS = ${PerlEmbed_LDFLAGS}") message(STATUS " PerlEmbed_LDDLFLAGS = ${PerlEmbed_LDDLFLAGS}") -endif() include(FindPackageHandleStandardArgs) diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 48bf28882..fad1e8ee2 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -5,6 +5,13 @@ #include "GCode/PrintExtents.hpp" #include "GCode/WipeTowerPrusaMM.hpp" +//############################################################################################################ +#include "GCodeTimeEstimator.hpp" +#ifdef WIN32 +#include "enrico/wintimer.h" +#endif // WIN32 +//############################################################################################################ + #include #include #include @@ -374,6 +381,87 @@ bool GCode::do_export(Print *print, const char *path) if (! result) boost::nowide::remove(path_tmp.c_str()); + +//############################################################################################################ +#ifdef WIN32 + WinTimer timer; + timer.Start(); +#endif // WIN32 + + My_GCodeTimeEstimator timeEstimator; + timeEstimator.parse_file(path); +#ifdef WIN32 + double timeParse = timer.GetElapsedTimeSec(); +#endif // WIN32 + timeEstimator.calculate_time(); +#ifdef WIN32 + double timeCalculate = timer.GetElapsedTimeSec(); +#endif // WIN32 + std::cout << std::endl << ">>> estimated time: " << timeEstimator.get_time() << " seconds." << std::endl << std::endl; + +#ifdef WIN32 + std::cout << std::endl << "parse_file() -> Time: " << timeParse << std::endl << std::endl; + std::cout << std::endl << "calculate_file() -> Time: " << timeCalculate - timeParse << std::endl << std::endl; +#endif // WIN32 + +/* + unsigned int i = 0; + const My_GCodeTimeEstimator::BlocksList& blocks = timeEstimator.get_blocks(); + float maxXYZ[3] = { 0.0f, 0.0f, 0.0f }; + unsigned int maxID[3] = { 0, 0, 0 }; + for (const My_GCodeTimeEstimator::Block& block : blocks) + { + ++i; + std::cout << std::endl << "Block: " + << i + << " (" + << block.delta_pos[My_GCodeTimeEstimator::X] + << ", " + << block.delta_pos[My_GCodeTimeEstimator::Y] + << ", " + << block.delta_pos[My_GCodeTimeEstimator::Z] + << ") - f: " + << block.feedrate + << " - a: " + << block.acceleration + << " - s: (" + << block.entry_feedrate + << ", " + << block.exit_feedrate + << ")" + << std::endl; + + float dx = ::abs(block.delta_pos[My_GCodeTimeEstimator::X]); + float dy = ::abs(block.delta_pos[My_GCodeTimeEstimator::Y]); + float dz = ::abs(block.delta_pos[My_GCodeTimeEstimator::Z]); + + if (maxXYZ[My_GCodeTimeEstimator::X] < dx) + { + maxXYZ[My_GCodeTimeEstimator::X] = dx; + maxID[My_GCodeTimeEstimator::X] = i; + } + + if (maxXYZ[My_GCodeTimeEstimator::Y] < dy) + { + maxXYZ[My_GCodeTimeEstimator::Y] = dy; + maxID[My_GCodeTimeEstimator::Y] = i; + } + + if (maxXYZ[My_GCodeTimeEstimator::Z] < dz) + { + maxXYZ[My_GCodeTimeEstimator::Z] = dz; + maxID[My_GCodeTimeEstimator::Z] = i; + } + } + + std::cout << std::endl << "MAX DX: " << maxID[My_GCodeTimeEstimator::X] << " - " << maxXYZ[My_GCodeTimeEstimator::X] << std::endl; + std::cout << std::endl << "MAX DY: " << maxID[My_GCodeTimeEstimator::Y] << " - " << maxXYZ[My_GCodeTimeEstimator::Y] << std::endl; + std::cout << std::endl << "MAX DZ: " << maxID[My_GCodeTimeEstimator::Z] << " - " << maxXYZ[My_GCodeTimeEstimator::Z] << std::endl; + + timeEstimator.print_counters(); +*/ +//############################################################################################################ + return result; } diff --git a/xs/src/libslic3r/GCodeTimeEstimator.cpp b/xs/src/libslic3r/GCodeTimeEstimator.cpp index c6fa353b4..b75745757 100644 --- a/xs/src/libslic3r/GCodeTimeEstimator.cpp +++ b/xs/src/libslic3r/GCodeTimeEstimator.cpp @@ -2,8 +2,592 @@ #include #include +//########################################################################################################### +#include +static const std::string AXIS_STR = "XYZE"; +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 = 0.0f; // <<<<<<<<< FIND A PROPER VALUE +static const float DEFAULT_ACCELERATION = 3000.0f; +static const float DEFAULT_AXIS_MAX_FEEDRATE[] = { 600.0f, 600.0f, 40.0f, 25.0f }; +static const float DEFAULT_AXIS_MAX_ACCELERATION[] = { 9000.0f, 9000.0f, 100.0f, 10000.0f }; + +static const float DEFAULT_AXIS_MAX_JERK[] = { 10.0f, 10.0f, 0.2f, 2.5f }; // from firmware +// static const float DEFAULT_AXIS_MAX_JERK[] = { 20.0f, 20.0f, 0.4f, 5.0f }; / from CURA + +static const float MINIMUM_FEEDRATE = 0.01f; +static const float MINIMUM_PLANNER_SPEED = 0.05f; // <<<<<<<< WHAT IS THIS ??? +static const float FEEDRATE_THRESHOLD = 0.0001f; +//########################################################################################################### + namespace Slic3r { +//########################################################################################################### + float My_GCodeTimeEstimator::Block::move_length() const + { + float length = ::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z])); + return (length > 0.0f) ? length : ::abs(delta_pos[E]); + } + + void My_GCodeTimeEstimator::Block::calculate_trapezoid() + { + float accelerate_distance = estimate_acceleration_distance(entry_feedrate, feedrate, acceleration); + float decelerate_distance = estimate_acceleration_distance(feedrate, exit_feedrate, -acceleration); + + float distance = move_length(); + + float plateau_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 (plateau_distance < 0.0f) + { + accelerate_distance = clamp(0.0f, distance, intersection_distance(entry_feedrate, exit_feedrate, acceleration, distance)); + plateau_distance = 0.0f; + } + + trapezoid.distance = distance; + trapezoid.accelerate_until = accelerate_distance; + trapezoid.decelerate_after = accelerate_distance + plateau_distance; + trapezoid.entry_feedrate = entry_feedrate; + trapezoid.exit_feedrate = exit_feedrate; + } + + float My_GCodeTimeEstimator::Block::max_allowable_speed(float acceleration, float target_velocity, float distance) + { + return ::sqrt(sqr(target_velocity) - 2.0f * acceleration * distance); + } + + float My_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 My_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); + } + + float My_GCodeTimeEstimator::Block::acceleration_time_from_distance(float initial_feedrate, float distance, float acceleration) + { + float discriminant = sqr(initial_feedrate) + 2.0f * acceleration * distance; + + // If discriminant is negative, we're moving in the wrong direction. + // Making the discriminant 0 then gives the extremum of the parabola instead of the intersection. + discriminant = std::max(0.0f, discriminant); + return (-initial_feedrate + ::sqrt(discriminant)) / acceleration; + } + + My_GCodeTimeEstimator::My_GCodeTimeEstimator() + { + } + + void My_GCodeTimeEstimator::parse(const std::string& gcode) + { + _reset(); + GCodeReader::parse(gcode, boost::bind(&My_GCodeTimeEstimator::_process_gcode_line, this, _1, _2)); + } + + void My_GCodeTimeEstimator::parse_file(const std::string& file) + { + _reset(); + GCodeReader::parse_file(file, boost::bind(&My_GCodeTimeEstimator::_process_gcode_line, this, _1, _2)); + } + + void My_GCodeTimeEstimator::calculate_time() + { + _time = get_additional_time(); + + for (const Block& block : _blocks) + { + const Block::Trapezoid& trapezoid = block.trapezoid; + float plateau_distance = trapezoid.decelerate_after - trapezoid.accelerate_until; + + _time += Block::acceleration_time_from_distance(block.entry_feedrate, trapezoid.accelerate_until, block.acceleration); + _time += plateau_distance / block.feedrate; + _time += Block::acceleration_time_from_distance(block.exit_feedrate, (trapezoid.distance - trapezoid.decelerate_after), block.acceleration); + } + } + + void My_GCodeTimeEstimator::set_axis_position(EAxis axis, float position) + { + _state.axis[axis].position = position; + } + + void My_GCodeTimeEstimator::set_axis_max_feedrate(EAxis axis, float feedrate_mm_sec) + { + _state.axis[axis].max_feedrate = feedrate_mm_sec; + } + + void My_GCodeTimeEstimator::set_axis_max_acceleration(EAxis axis, float acceleration) + { + _state.axis[axis].max_acceleration = acceleration; + } + + void My_GCodeTimeEstimator::set_axis_max_jerk(EAxis axis, float jerk) + { + _state.axis[axis].max_jerk = jerk; + } + + float My_GCodeTimeEstimator::get_axis_position(EAxis axis) const + { + return _state.axis[axis].position; + } + + float My_GCodeTimeEstimator::get_axis_max_feedrate(EAxis axis) const + { + return _state.axis[axis].max_feedrate; + } + + float My_GCodeTimeEstimator::get_axis_max_acceleration(EAxis axis) const + { + return _state.axis[axis].max_acceleration; + } + + float My_GCodeTimeEstimator::get_axis_max_jerk(EAxis axis) const + { + return _state.axis[axis].max_jerk; + } + + void My_GCodeTimeEstimator::set_feedrate(float feedrate_mm_sec) + { + _state.feedrate = std::max(feedrate_mm_sec, MINIMUM_FEEDRATE); + } + + float My_GCodeTimeEstimator::get_feedrate() const + { + return _state.feedrate; + } + + void My_GCodeTimeEstimator::set_acceleration(float acceleration) + { + _state.acceleration = acceleration; + } + + float My_GCodeTimeEstimator::get_acceleration() const + { + return _state.acceleration; + } + + void My_GCodeTimeEstimator::set_dialect(My_GCodeTimeEstimator::EDialect dialect) + { + _state.dialect = dialect; + } + + My_GCodeTimeEstimator::EDialect My_GCodeTimeEstimator::get_dialect() const + { + return _state.dialect; + } + + void My_GCodeTimeEstimator::set_units(My_GCodeTimeEstimator::EUnits units) + { + _state.units = units; + } + + My_GCodeTimeEstimator::EUnits My_GCodeTimeEstimator::get_units() const + { + return _state.units; + } + + void My_GCodeTimeEstimator::set_positioningType(My_GCodeTimeEstimator::EPositioningType type) + { + _state.positioningType = type; + } + + My_GCodeTimeEstimator::EPositioningType My_GCodeTimeEstimator::get_positioningType() const + { + return _state.positioningType; + } + + void My_GCodeTimeEstimator::add_additional_time(float timeSec) + { + _state.additional_time += timeSec; + } + + float My_GCodeTimeEstimator::get_additional_time() const + { + return _state.additional_time; + } + + void My_GCodeTimeEstimator::set_default() + { + set_units(Millimeters); + set_dialect(Unknown); + set_positioningType(Absolute); + + set_feedrate(DEFAULT_FEEDRATE); + set_acceleration(DEFAULT_ACCELERATION); + + 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]); + } + } + + float My_GCodeTimeEstimator::get_time() const + { + return _time; + } + + const My_GCodeTimeEstimator::BlocksList& My_GCodeTimeEstimator::get_blocks() const + { + return _blocks; + } + +// void My_GCodeTimeEstimator::print_counters() const +// { +// std::cout << std::endl; +// for (const CmdToCounterMap::value_type& counter : _cmdCounters) +// { +// std::cout << counter.first << " : " << counter.second << std::endl; +// } +// } + + void My_GCodeTimeEstimator::_reset() + { +// _cmdCounters.clear(); + + _blocks.clear(); + + set_default(); + set_axis_position(X, 0.0f); + set_axis_position(Y, 0.0f); + set_axis_position(Z, 0.0f); + + _state.additional_time = 0.0f; + } + + void My_GCodeTimeEstimator::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLine& line) + { + if (line.cmd.length() > 1) + { + switch (line.cmd[0]) + { + case 'G': + { + switch (::atoi(&line.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(&line.cmd[1])) + { + case 109: // Set Extruder Temperature and Wait + { + _processM109(line); + break; + } + case 203: // Set maximum feedrate + { + _processM203(line); + break; + } + case 204: // Set default acceleration + { + _processM204(line); + break; + } + case 566: // Set allowable instantaneous speed change + { + _processM566(line); + break; + } + } + + break; + } + } + +// CmdToCounterMap::iterator it = _cmdCounters.find(line.cmd); +// if (it == _cmdCounters.end()) +// _cmdCounters.insert(CmdToCounterMap::value_type(line.cmd, 1)); +// else +// ++it->second; + } + } + + void My_GCodeTimeEstimator::_processG1(const GCodeReader::GCodeLine& line) + { + float lengthsScaleFactor = (get_units() == Inches) ? INCHES_TO_MM : 1.0f; + + // gets position changes from line, if present + float new_pos[Num_Axis]; + + if (get_positioningType() == Absolute) + { + for (unsigned char a = X; a < Num_Axis; ++a) + { + new_pos[a] = line.has(AXIS_STR[a]) ? line.get_float(AXIS_STR[a]) * lengthsScaleFactor : get_axis_position((EAxis)a); + } + } + else // get_positioningType() == Relative + { + for (unsigned char a = X; a < Num_Axis; ++a) + { + new_pos[a] = get_axis_position((EAxis)a); + new_pos[a] += (line.has(AXIS_STR[a]) ? line.get_float(AXIS_STR[a]) * lengthsScaleFactor : 0.0f); + } + } + + // updates feedrate from line, if present + if (line.has('F')) + set_feedrate(line.get_float('F') * MMMIN_TO_MMSEC); + + // fills block data + Block block; + + // calculates block movement deltas + float max_abs_delta = 0.0f; + for (unsigned char a = X; a < Num_Axis; ++a) + { + block.delta_pos[a] = new_pos[a] - get_axis_position((EAxis)a); + max_abs_delta = std::max(max_abs_delta, ::abs(block.delta_pos[a])); + } + + // is it a move ? + if (max_abs_delta == 0.0f) + return; + + // calculates block feedrate + float feedrate = get_feedrate(); + + float distance = block.move_length(); + float invDistance = 1.0f / distance; + + float axis_feedrate[Num_Axis]; + float min_feedrate_factor = 1.0f; + for (unsigned char a = X; a < Num_Axis; ++a) + { + axis_feedrate[a] = feedrate * ::abs(block.delta_pos[a]) * invDistance; + if (axis_feedrate[a] > 0.0f) + min_feedrate_factor = std::min(min_feedrate_factor, get_axis_max_feedrate((EAxis)a) / axis_feedrate[a]); + } + + block.feedrate = min_feedrate_factor * feedrate; + for (unsigned char a = X; a < Num_Axis; ++a) + { + axis_feedrate[a] *= min_feedrate_factor; + } + + // calculates block acceleration + float acceleration = get_acceleration(); + + for (unsigned char a = X; a < Num_Axis; ++a) + { + float axis_max_acceleration = get_axis_max_acceleration((EAxis)a); + if (acceleration * ::abs(block.delta_pos[a]) * invDistance > axis_max_acceleration) + acceleration = axis_max_acceleration; + } + + block.acceleration = acceleration; + + // calculates block exit feedrate + float exit_feedrate = block.feedrate; + + for (unsigned char a = X; a < Num_Axis; ++a) + { + float half_axis_max_jerk = 0.5f * get_axis_max_jerk((EAxis)a); + if (axis_feedrate[a] > half_axis_max_jerk) + exit_feedrate = std::min(exit_feedrate, half_axis_max_jerk); + } + + block.exit_feedrate = exit_feedrate; + + // calculates block entry feedrate + float vmax_junction = exit_feedrate; + if (!_blocks.empty() && (_prev.feedrate > FEEDRATE_THRESHOLD)) + { + vmax_junction = block.feedrate; + float vmax_junction_factor = 1.0f; + + for (unsigned char a = X; a < Num_Axis; ++a) + { + float abs_delta_axis_feedrate = ::abs(axis_feedrate[a] - _prev.axis_feedrate[a]); + float axis_max_jerk = get_axis_max_jerk((EAxis)a); + if (abs_delta_axis_feedrate > axis_max_jerk) + vmax_junction_factor = std::min(vmax_junction_factor, axis_max_jerk / abs_delta_axis_feedrate); + } + + // limit vmax to not exceed previous feedrate + vmax_junction = std::min(_prev.feedrate, vmax_junction * vmax_junction_factor); + } + + block.entry_feedrate = std::min(vmax_junction, Block::max_allowable_speed(-acceleration, MINIMUM_PLANNER_SPEED, distance)); + + // calculates block trapezoid + block.calculate_trapezoid(); + + // updates previous cache + _prev.feedrate = feedrate; + for (unsigned char a = X; a < Num_Axis; ++a) + { + _prev.axis_feedrate[a] = axis_feedrate[a]; + } + + // updates axis positions + for (unsigned char a = X; a < Num_Axis; ++a) + { + set_axis_position((EAxis)a, new_pos[a]); + } + + // adds block to blocks list + _blocks.push_back(block); + } + + void My_GCodeTimeEstimator::_processG4(const GCodeReader::GCodeLine& line) + { + EDialect dialect = get_dialect(); + + if (line.has('P')) + add_additional_time(line.get_float('P') * MILLISEC_TO_SEC); + + // see: http://reprap.org/wiki/G-code#G4:_Dwell + if ((dialect == Repetier) || + (dialect == Marlin) || + (dialect == Smoothieware) || + (dialect == RepRapFirmware)) + { + if (line.has('S')) + add_additional_time(line.get_float('S')); + } + } + + void My_GCodeTimeEstimator::_processG20(const GCodeReader::GCodeLine& line) + { + set_units(Inches); + } + + void My_GCodeTimeEstimator::_processG21(const GCodeReader::GCodeLine& line) + { + set_units(Millimeters); + } + + void My_GCodeTimeEstimator::_processG28(const GCodeReader::GCodeLine& line) + { + // todo + } + + void My_GCodeTimeEstimator::_processG90(const GCodeReader::GCodeLine& line) + { + set_positioningType(Absolute); + } + + void My_GCodeTimeEstimator::_processG91(const GCodeReader::GCodeLine& line) + { + // >>>>>>>> THERE ARE DIALECT VARIANTS + + set_positioningType(Relative); + } + + void My_GCodeTimeEstimator::_processG92(const GCodeReader::GCodeLine& line) + { + // todo + } + + void My_GCodeTimeEstimator::_processM109(const GCodeReader::GCodeLine& line) + { + // todo + } + + void My_GCodeTimeEstimator::_processM203(const GCodeReader::GCodeLine& line) + { + EDialect dialect = get_dialect(); + + // see http://reprap.org/wiki/G-code#M203:_Set_maximum_feedrate + if (dialect == Repetier) + return; + + // see http://reprap.org/wiki/G-code#M203:_Set_maximum_feedrate + float factor = (dialect == Marlin) ? 1.0f : MMMIN_TO_MMSEC; + + if (line.has('X')) + set_axis_max_feedrate(X, line.get_float('X') * factor); + + if (line.has('Y')) + set_axis_max_feedrate(Y, line.get_float('Y') * factor); + + if (line.has('Z')) + set_axis_max_feedrate(Z, line.get_float('Z') * factor); + + if (line.has('E')) + set_axis_max_feedrate(E, line.get_float('E') * factor); + } + + void My_GCodeTimeEstimator::_processM204(const GCodeReader::GCodeLine& line) + { + if (line.has('S')) + set_acceleration(line.get_float('S')); // <<<< Is this correct ? + + if (line.has('T')) + { + // what to do ? + } + } + + void My_GCodeTimeEstimator::_processM566(const GCodeReader::GCodeLine& line) + { + if (line.has('X')) + set_axis_max_jerk(X, line.get_float('X') * MMMIN_TO_MMSEC); + + if (line.has('Y')) + set_axis_max_jerk(Y, line.get_float('Y') * MMMIN_TO_MMSEC); + + if (line.has('Z')) + set_axis_max_jerk(Z, line.get_float('Z') * MMMIN_TO_MMSEC); + + if (line.has('E')) + set_axis_max_jerk(E, line.get_float('E') * MMMIN_TO_MMSEC); + } +//########################################################################################################### + void GCodeTimeEstimator::parse(const std::string &gcode) { diff --git a/xs/src/libslic3r/GCodeTimeEstimator.hpp b/xs/src/libslic3r/GCodeTimeEstimator.hpp index dd301c929..6e3de493f 100644 --- a/xs/src/libslic3r/GCodeTimeEstimator.hpp +++ b/xs/src/libslic3r/GCodeTimeEstimator.hpp @@ -6,6 +6,210 @@ namespace Slic3r { +//########################################################################################################### + class My_GCodeTimeEstimator : public GCodeReader + { + public: + enum EUnits : unsigned char + { + Millimeters, + Inches + }; + + enum EAxis : unsigned char + { + X, + Y, + Z, + E, + Num_Axis + }; + + enum EDialect : unsigned char + { + Unknown, + Marlin, + Repetier, + Smoothieware, + RepRapFirmware, + Teacup, + Num_Dialects + }; + + enum EPositioningType + { + 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 State + { + EDialect dialect; + EUnits units; + EPositioningType positioningType; + Axis axis[Num_Axis]; + float feedrate; // mm/s + float acceleration; // mm/s^2 + float additional_time; // s + }; + + struct PreviousBlockCache + { + float feedrate; // mm/s + float axis_feedrate[Num_Axis]; // mm/s + }; + + public: + struct Block + { + struct Trapezoid + { + float distance; // mm + float accelerate_until; // mm + float decelerate_after; // mm + float entry_feedrate; // mm/s + float exit_feedrate; // mm/s + }; + + float delta_pos[Num_Axis]; // mm + float feedrate; // mm/s + float acceleration; // mm/s^2 + float entry_feedrate; // mm/s + float exit_feedrate; // mm/s + + Trapezoid trapezoid; + + // Returns the length of the move covered by this block, in mm + float move_length() const; + + 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); + + // This function gives the time it needs to accelerate from an initial speed to reach a final distance. + static float acceleration_time_from_distance(float initial_feedrate, float distance, float acceleration); + }; + + typedef std::vector BlocksList; + + private: +// typedef std::map CmdToCounterMap; +// CmdToCounterMap _cmdCounters; + + State _state; + PreviousBlockCache _prev; + BlocksList _blocks; + float _time; // s + + public: + My_GCodeTimeEstimator(); + + void parse(const std::string& gcode); + void parse_file(const std::string& file); + + void calculate_time(); + + 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); + + 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); + float get_acceleration() const; + + void set_dialect(EDialect dialect); + EDialect get_dialect() const; + + void set_units(EUnits units); + EUnits get_units() const; + + void set_positioningType(EPositioningType type); + EPositioningType get_positioningType() const; + + void add_additional_time(float timeSec); + float get_additional_time() const; + + void set_default(); + + // returns estimated time in seconds + float get_time() const; + + const BlocksList& get_blocks() const; + +// void print_counters() const; + + private: + void _reset(); + + // Processes 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); + + // Set Extruder Temperature and Wait + void _processM109(const GCodeReader::GCodeLine& line); + + // Set maximum feedrate + void _processM203(const GCodeReader::GCodeLine& line); + + // Set default acceleration + void _processM204(const GCodeReader::GCodeLine& line); + + // Set allowable instantaneous speed change + void _processM566(const GCodeReader::GCodeLine& line); + }; +//########################################################################################################### + class GCodeTimeEstimator : public GCodeReader { public: float time = 0; // in seconds