time estimator wip

This commit is contained in:
Enrico Turri 2017-12-06 14:12:10 +01:00
parent 7308017ee8
commit bc3d184d7c
4 changed files with 876 additions and 2 deletions

View file

@ -60,7 +60,6 @@ include(${PerlEmbed_TEMP_INCLUDE})
file(REMOVE ${PerlEmbed_TEMP_INCLUDE}) file(REMOVE ${PerlEmbed_TEMP_INCLUDE})
unset(PerlEmbed_TEMP_INCLUDE) unset(PerlEmbed_TEMP_INCLUDE)
if (PerlEmbed_DEBUG)
# First show the configuration extracted by FindPerl & FindPerlLibs: # First show the configuration extracted by FindPerl & FindPerlLibs:
message(STATUS " PERL_INCLUDE_PATH = ${PERL_INCLUDE_PATH}") message(STATUS " PERL_INCLUDE_PATH = ${PERL_INCLUDE_PATH}")
message(STATUS " PERL_LIBRARY = ${PERL_LIBRARY}") message(STATUS " PERL_LIBRARY = ${PERL_LIBRARY}")
@ -79,7 +78,6 @@ if (PerlEmbed_DEBUG)
message(STATUS " LD = ${PerlEmbed_LD}") message(STATUS " LD = ${PerlEmbed_LD}")
message(STATUS " PerlEmbed_LDFLAGS = ${PerlEmbed_LDFLAGS}") message(STATUS " PerlEmbed_LDFLAGS = ${PerlEmbed_LDFLAGS}")
message(STATUS " PerlEmbed_LDDLFLAGS = ${PerlEmbed_LDDLFLAGS}") message(STATUS " PerlEmbed_LDDLFLAGS = ${PerlEmbed_LDDLFLAGS}")
endif()
include(FindPackageHandleStandardArgs) include(FindPackageHandleStandardArgs)

View file

@ -5,6 +5,13 @@
#include "GCode/PrintExtents.hpp" #include "GCode/PrintExtents.hpp"
#include "GCode/WipeTowerPrusaMM.hpp" #include "GCode/WipeTowerPrusaMM.hpp"
//############################################################################################################
#include "GCodeTimeEstimator.hpp"
#ifdef WIN32
#include "enrico/wintimer.h"
#endif // WIN32
//############################################################################################################
#include <algorithm> #include <algorithm>
#include <cstdlib> #include <cstdlib>
#include <math.h> #include <math.h>
@ -374,6 +381,87 @@ bool GCode::do_export(Print *print, const char *path)
if (! result) if (! result)
boost::nowide::remove(path_tmp.c_str()); 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; return result;
} }

View file

@ -2,8 +2,592 @@
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include <cmath> #include <cmath>
//###########################################################################################################
#include <fstream>
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 { 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 void
GCodeTimeEstimator::parse(const std::string &gcode) GCodeTimeEstimator::parse(const std::string &gcode)
{ {

View file

@ -6,6 +6,210 @@
namespace Slic3r { 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<Block> BlocksList;
private:
// typedef std::map<std::string, unsigned int> 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 { class GCodeTimeEstimator : public GCodeReader {
public: public:
float time = 0; // in seconds float time = 0; // in seconds