mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-16 18:45:52 +08:00
New GCodeTimeEstimator class, with basic estimation code by @lordofhyphens. #3747
This commit is contained in:
parent
5d2626fd3a
commit
969f28f9d3
@ -58,6 +58,7 @@ add_library(libslic3r STATIC
|
|||||||
${LIBDIR}/libslic3r/GCode/SpiralVase.cpp
|
${LIBDIR}/libslic3r/GCode/SpiralVase.cpp
|
||||||
${LIBDIR}/libslic3r/GCodeReader.cpp
|
${LIBDIR}/libslic3r/GCodeReader.cpp
|
||||||
${LIBDIR}/libslic3r/GCodeSender.cpp
|
${LIBDIR}/libslic3r/GCodeSender.cpp
|
||||||
|
${LIBDIR}/libslic3r/GCodeTimeEstimator.cpp
|
||||||
${LIBDIR}/libslic3r/GCodeWriter.cpp
|
${LIBDIR}/libslic3r/GCodeWriter.cpp
|
||||||
${LIBDIR}/libslic3r/Geometry.cpp
|
${LIBDIR}/libslic3r/Geometry.cpp
|
||||||
${LIBDIR}/libslic3r/IO.cpp
|
${LIBDIR}/libslic3r/IO.cpp
|
||||||
|
21
utils/estimate-gcode-time.pl
Executable file
21
utils/estimate-gcode-time.pl
Executable file
@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
use FindBin;
|
||||||
|
use lib "$FindBin::Bin/../lib";
|
||||||
|
use local::lib "$FindBin::Bin/../local-lib";
|
||||||
|
}
|
||||||
|
|
||||||
|
use Slic3r;
|
||||||
|
|
||||||
|
die "Usage: estimate-gcode-time.pl FILE\n"
|
||||||
|
if @ARGV != 1;
|
||||||
|
|
||||||
|
my $estimator = Slic3r::GCode::TimeEstimator->new;
|
||||||
|
$estimator->parse_file($ARGV[0]);
|
||||||
|
printf "Time: %d minutes and %d seconds\n", int($estimator->time / 60), $estimator->time % 60;
|
||||||
|
|
||||||
|
__END__
|
@ -72,6 +72,8 @@ src/libslic3r/GCodeReader.cpp
|
|||||||
src/libslic3r/GCodeReader.hpp
|
src/libslic3r/GCodeReader.hpp
|
||||||
src/libslic3r/GCodeSender.cpp
|
src/libslic3r/GCodeSender.cpp
|
||||||
src/libslic3r/GCodeSender.hpp
|
src/libslic3r/GCodeSender.hpp
|
||||||
|
src/libslic3r/GCodeTimeEstimator.cpp
|
||||||
|
src/libslic3r/GCodeTimeEstimator.hpp
|
||||||
src/libslic3r/GCodeWriter.cpp
|
src/libslic3r/GCodeWriter.cpp
|
||||||
src/libslic3r/GCodeWriter.hpp
|
src/libslic3r/GCodeWriter.hpp
|
||||||
src/libslic3r/Geometry.cpp
|
src/libslic3r/Geometry.cpp
|
||||||
@ -180,6 +182,7 @@ xsp/Filler.xsp
|
|||||||
xsp/Flow.xsp
|
xsp/Flow.xsp
|
||||||
xsp/GCode.xsp
|
xsp/GCode.xsp
|
||||||
xsp/GCodeSender.xsp
|
xsp/GCodeSender.xsp
|
||||||
|
xsp/GCodeTimeEstimator.xsp
|
||||||
xsp/GCodeWriter.xsp
|
xsp/GCodeWriter.xsp
|
||||||
xsp/Geometry.xsp
|
xsp/Geometry.xsp
|
||||||
xsp/GUI.xsp
|
xsp/GUI.xsp
|
||||||
|
@ -39,7 +39,7 @@ SpiralVase::process_layer(const std::string &gcode)
|
|||||||
{
|
{
|
||||||
GCodeReader r = this->_reader; // clone
|
GCodeReader r = this->_reader; // clone
|
||||||
r.parse(gcode, [&total_layer_length, &layer_height, &z, &set_z]
|
r.parse(gcode, [&total_layer_length, &layer_height, &z, &set_z]
|
||||||
(GCodeReader &, GCodeReader::GCodeLine &line) {
|
(GCodeReader &, const GCodeReader::GCodeLine &line) {
|
||||||
if (line.cmd == "G1") {
|
if (line.cmd == "G1") {
|
||||||
if (line.extruding()) {
|
if (line.extruding()) {
|
||||||
total_layer_length += line.dist_XY();
|
total_layer_length += line.dist_XY();
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "GCodeReader.hpp"
|
#include "GCodeReader.hpp"
|
||||||
#include <boost/algorithm/string/classification.hpp>
|
#include <boost/algorithm/string/classification.hpp>
|
||||||
#include <boost/algorithm/string/split.hpp>
|
#include <boost/algorithm/string/split.hpp>
|
||||||
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
@ -17,59 +18,73 @@ GCodeReader::parse(const std::string &gcode, callback_t callback)
|
|||||||
{
|
{
|
||||||
std::istringstream ss(gcode);
|
std::istringstream ss(gcode);
|
||||||
std::string line;
|
std::string line;
|
||||||
while (std::getline(ss, line)) {
|
while (std::getline(ss, line))
|
||||||
GCodeLine gline(this);
|
this->parse_line(line, callback);
|
||||||
gline.raw = line;
|
}
|
||||||
if (this->verbose)
|
|
||||||
std::cout << line << std::endl;
|
void
|
||||||
|
GCodeReader::parse_line(std::string line, callback_t callback)
|
||||||
// strip comment
|
{
|
||||||
{
|
GCodeLine gline(this);
|
||||||
size_t pos = line.find(';');
|
gline.raw = line;
|
||||||
if (pos != std::string::npos) {
|
if (this->verbose)
|
||||||
gline.comment = line.substr(pos+1);
|
std::cout << line << std::endl;
|
||||||
line.erase(pos);
|
|
||||||
}
|
// strip comment
|
||||||
}
|
{
|
||||||
|
size_t pos = line.find(';');
|
||||||
// command and args
|
if (pos != std::string::npos) {
|
||||||
{
|
gline.comment = line.substr(pos+1);
|
||||||
std::vector<std::string> args;
|
line.erase(pos);
|
||||||
boost::split(args, line, boost::is_any_of(" "));
|
|
||||||
|
|
||||||
// first one is cmd
|
|
||||||
gline.cmd = args.front();
|
|
||||||
args.erase(args.begin());
|
|
||||||
|
|
||||||
for (std::string &arg : args) {
|
|
||||||
if (arg.size() < 2) continue;
|
|
||||||
gline.args.insert(std::make_pair(arg[0], arg.substr(1)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert extrusion axis
|
|
||||||
if (this->_extrusion_axis != 'E') {
|
|
||||||
const auto it = gline.args.find(this->_extrusion_axis);
|
|
||||||
if (it != gline.args.end()) {
|
|
||||||
std::swap(gline.args['E'], it->second);
|
|
||||||
gline.args.erase(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gline.has('E') && this->_config.use_relative_e_distances)
|
|
||||||
this->E = 0;
|
|
||||||
|
|
||||||
if (callback) callback(*this, gline);
|
|
||||||
|
|
||||||
// update coordinates
|
|
||||||
if (gline.cmd == "G0" || gline.cmd == "G1" || gline.cmd == "G92") {
|
|
||||||
this->X = gline.new_X();
|
|
||||||
this->Y = gline.new_Y();
|
|
||||||
this->Z = gline.new_Z();
|
|
||||||
this->E = gline.new_E();
|
|
||||||
this->F = gline.new_F();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// command and args
|
||||||
|
{
|
||||||
|
std::vector<std::string> args;
|
||||||
|
boost::split(args, line, boost::is_any_of(" "));
|
||||||
|
|
||||||
|
// first one is cmd
|
||||||
|
gline.cmd = args.front();
|
||||||
|
args.erase(args.begin());
|
||||||
|
|
||||||
|
for (std::string &arg : args) {
|
||||||
|
if (arg.size() < 2) continue;
|
||||||
|
gline.args.insert(std::make_pair(arg[0], arg.substr(1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert extrusion axis
|
||||||
|
if (this->_extrusion_axis != 'E') {
|
||||||
|
const auto it = gline.args.find(this->_extrusion_axis);
|
||||||
|
if (it != gline.args.end()) {
|
||||||
|
std::swap(gline.args['E'], it->second);
|
||||||
|
gline.args.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gline.has('E') && this->_config.use_relative_e_distances)
|
||||||
|
this->E = 0;
|
||||||
|
|
||||||
|
if (callback) callback(*this, gline);
|
||||||
|
|
||||||
|
// update coordinates
|
||||||
|
if (gline.cmd == "G0" || gline.cmd == "G1" || gline.cmd == "G92") {
|
||||||
|
this->X = gline.new_X();
|
||||||
|
this->Y = gline.new_Y();
|
||||||
|
this->Z = gline.new_Z();
|
||||||
|
this->E = gline.new_E();
|
||||||
|
this->F = gline.new_F();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GCodeReader::parse_file(const std::string &file, callback_t callback)
|
||||||
|
{
|
||||||
|
std::ifstream f(file);
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(f, line))
|
||||||
|
this->parse_line(line, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -25,6 +25,7 @@ class GCodeReader {
|
|||||||
GCodeLine(GCodeReader* _reader) : reader(_reader) {};
|
GCodeLine(GCodeReader* _reader) : reader(_reader) {};
|
||||||
|
|
||||||
bool has(char arg) const { return this->args.count(arg) > 0; };
|
bool has(char arg) const { return this->args.count(arg) > 0; };
|
||||||
|
float get_float(char arg) const { return atof(this->args.at(arg).c_str()); };
|
||||||
float new_X() const { return this->has('X') ? atof(this->args.at('X').c_str()) : this->reader->X; };
|
float new_X() const { return this->has('X') ? atof(this->args.at('X').c_str()) : this->reader->X; };
|
||||||
float new_Y() const { return this->has('Y') ? atof(this->args.at('Y').c_str()) : this->reader->Y; };
|
float new_Y() const { return this->has('Y') ? atof(this->args.at('Y').c_str()) : this->reader->Y; };
|
||||||
float new_Z() const { return this->has('Z') ? atof(this->args.at('Z').c_str()) : this->reader->Z; };
|
float new_Z() const { return this->has('Z') ? atof(this->args.at('Z').c_str()) : this->reader->Z; };
|
||||||
@ -44,7 +45,7 @@ class GCodeReader {
|
|||||||
bool travel() const { return this->cmd == "G1" && !this->has('E'); };
|
bool travel() const { return this->cmd == "G1" && !this->has('E'); };
|
||||||
void set(char arg, std::string value);
|
void set(char arg, std::string value);
|
||||||
};
|
};
|
||||||
typedef std::function<void(GCodeReader&, GCodeLine&)> callback_t;
|
typedef std::function<void(GCodeReader&, const GCodeLine&)> callback_t;
|
||||||
|
|
||||||
float X, Y, Z, E, F;
|
float X, Y, Z, E, F;
|
||||||
bool verbose;
|
bool verbose;
|
||||||
@ -53,6 +54,8 @@ class GCodeReader {
|
|||||||
GCodeReader() : X(0), Y(0), Z(0), E(0), F(0), verbose(false), _extrusion_axis('E') {};
|
GCodeReader() : X(0), Y(0), Z(0), E(0), F(0), verbose(false), _extrusion_axis('E') {};
|
||||||
void apply_config(const PrintConfigBase &config);
|
void apply_config(const PrintConfigBase &config);
|
||||||
void parse(const std::string &gcode, callback_t callback);
|
void parse(const std::string &gcode, callback_t callback);
|
||||||
|
void parse_line(std::string line, callback_t callback);
|
||||||
|
void parse_file(const std::string &file, callback_t callback);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GCodeConfig _config;
|
GCodeConfig _config;
|
||||||
|
78
xs/src/libslic3r/GCodeTimeEstimator.cpp
Normal file
78
xs/src/libslic3r/GCodeTimeEstimator.cpp
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
#include "GCodeTimeEstimator.hpp"
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
void
|
||||||
|
GCodeTimeEstimator::parse(const std::string &gcode)
|
||||||
|
{
|
||||||
|
GCodeReader::parse(gcode, boost::bind(&GCodeTimeEstimator::_parser, this, _1, _2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GCodeTimeEstimator::parse_file(const std::string &file)
|
||||||
|
{
|
||||||
|
GCodeReader::parse_file(file, boost::bind(&GCodeTimeEstimator::_parser, this, _1, _2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GCodeTimeEstimator::_parser(GCodeReader&, const GCodeReader::GCodeLine &line)
|
||||||
|
{
|
||||||
|
// std::cout << "[" << this->time << "] " << line.raw << std::endl;
|
||||||
|
if (line.cmd == "G1") {
|
||||||
|
const float dist_XY = line.dist_XY();
|
||||||
|
const float new_F = line.new_F();
|
||||||
|
|
||||||
|
if (dist_XY > 0) {
|
||||||
|
//this->time += dist_XY / new_F * 60;
|
||||||
|
this->time += _accelerated_move(dist_XY, new_F/60, this->acceleration);
|
||||||
|
} else {
|
||||||
|
//this->time += std::abs(line.dist_E()) / new_F * 60;
|
||||||
|
this->time += _accelerated_move(std::abs(line.dist_E()), new_F/60, this->acceleration);
|
||||||
|
}
|
||||||
|
//this->time += std::abs(line.dist_Z()) / new_F * 60;
|
||||||
|
this->time += _accelerated_move(std::abs(line.dist_Z()), new_F/60, this->acceleration);
|
||||||
|
} else if (line.cmd == "M204" && line.has('S')) {
|
||||||
|
this->acceleration = line.get_float('S');
|
||||||
|
} else if (line.cmd == "G4") { // swell
|
||||||
|
if (line.has('S')) {
|
||||||
|
this->time += line.get_float('S');
|
||||||
|
} else if (line.has('P')) {
|
||||||
|
this->time += line.get_float('P')/1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wildly optimistic acceleration "bell" curve modeling.
|
||||||
|
// Returns an estimate of how long the move with a given accel
|
||||||
|
// takes in seconds.
|
||||||
|
// It is assumed that the movement is smooth and uniform.
|
||||||
|
float
|
||||||
|
GCodeTimeEstimator::_accelerated_move(double length, double v, double acceleration)
|
||||||
|
{
|
||||||
|
// for half of the move, there are 2 zones, where the speed is increasing/decreasing and
|
||||||
|
// where the speed is constant.
|
||||||
|
// Since the slowdown is assumed to be uniform, calculate the average velocity for half of the
|
||||||
|
// expected displacement.
|
||||||
|
// final velocity v = a*t => a * (dx / 0.5v) => v^2 = 2*a*dx
|
||||||
|
// v_avg = 0.5v => 2*v_avg = v
|
||||||
|
// d_x = v_avg*t => t = d_x / v_avg
|
||||||
|
acceleration = (acceleration == 0.0 ? 4000.0 : acceleration); // Set a default accel to use for print time in case it's 0 somehow.
|
||||||
|
auto half_length = length / 2.0;
|
||||||
|
auto t_init = v / acceleration; // time to final velocity
|
||||||
|
auto dx_init = (0.5*v*t_init); // Initial displacement for the time to get to final velocity
|
||||||
|
auto t = 0.0;
|
||||||
|
if (half_length >= dx_init) {
|
||||||
|
half_length -= (0.5*v*t_init);
|
||||||
|
t += t_init;
|
||||||
|
t += (half_length / v); // rest of time is at constant speed.
|
||||||
|
} else {
|
||||||
|
// If too much displacement for the expected final velocity, we don't hit the max, so reduce
|
||||||
|
// the average velocity to fit the displacement we actually are looking for.
|
||||||
|
t += std::sqrt(std::abs(length) * 2.0 * acceleration) / acceleration;
|
||||||
|
}
|
||||||
|
return 2.0*t; // cut in half before, so double to get full time spent.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
24
xs/src/libslic3r/GCodeTimeEstimator.hpp
Normal file
24
xs/src/libslic3r/GCodeTimeEstimator.hpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef slic3r_GCodeTimeEstimator_hpp_
|
||||||
|
#define slic3r_GCodeTimeEstimator_hpp_
|
||||||
|
|
||||||
|
#include "libslic3r.h"
|
||||||
|
#include "GCodeReader.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
class GCodeTimeEstimator : public GCodeReader {
|
||||||
|
public:
|
||||||
|
float time = 0; // in seconds
|
||||||
|
|
||||||
|
void parse(const std::string &gcode);
|
||||||
|
void parse_file(const std::string &file);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
float acceleration = 9000;
|
||||||
|
void _parser(GCodeReader&, const GCodeReader::GCodeLine &line);
|
||||||
|
static float _accelerated_move(double length, double v, double acceleration);
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace Slic3r */
|
||||||
|
|
||||||
|
#endif /* slic3r_GCodeTimeEstimator_hpp_ */
|
@ -19,6 +19,7 @@ REGISTER_CLASS(SpiralVase, "GCode::SpiralVase");
|
|||||||
REGISTER_CLASS(Wipe, "GCode::Wipe");
|
REGISTER_CLASS(Wipe, "GCode::Wipe");
|
||||||
REGISTER_CLASS(GCode, "GCode");
|
REGISTER_CLASS(GCode, "GCode");
|
||||||
REGISTER_CLASS(GCodeSender, "GCode::Sender");
|
REGISTER_CLASS(GCodeSender, "GCode::Sender");
|
||||||
|
REGISTER_CLASS(GCodeTimeEstimator, "GCode::TimeEstimator");
|
||||||
REGISTER_CLASS(GCodeWriter, "GCode::Writer");
|
REGISTER_CLASS(GCodeWriter, "GCode::Writer");
|
||||||
REGISTER_CLASS(Layer, "Layer");
|
REGISTER_CLASS(Layer, "Layer");
|
||||||
REGISTER_CLASS(SupportLayer, "Layer::Support");
|
REGISTER_CLASS(SupportLayer, "Layer::Support");
|
||||||
|
15
xs/xsp/GCodeTimeEstimator.xsp
Normal file
15
xs/xsp/GCodeTimeEstimator.xsp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
%module{Slic3r::XS};
|
||||||
|
|
||||||
|
%{
|
||||||
|
#include <xsinit.h>
|
||||||
|
#include "libslic3r/GCodeTimeEstimator.hpp"
|
||||||
|
%}
|
||||||
|
|
||||||
|
%name{Slic3r::GCode::TimeEstimator} class GCodeTimeEstimator {
|
||||||
|
GCodeTimeEstimator();
|
||||||
|
~GCodeTimeEstimator();
|
||||||
|
|
||||||
|
float time %get{time};
|
||||||
|
void parse(std::string gcode);
|
||||||
|
void parse_file(std::string file);
|
||||||
|
};
|
@ -214,6 +214,10 @@ GCodeSender* O_OBJECT_SLIC3R
|
|||||||
Ref<GCodeSender> O_OBJECT_SLIC3R_T
|
Ref<GCodeSender> O_OBJECT_SLIC3R_T
|
||||||
Clone<GCodeSender> O_OBJECT_SLIC3R_T
|
Clone<GCodeSender> O_OBJECT_SLIC3R_T
|
||||||
|
|
||||||
|
GCodeTimeEstimator* O_OBJECT_SLIC3R
|
||||||
|
Ref<GCodeTimeEstimator> O_OBJECT_SLIC3R_T
|
||||||
|
Clone<GCodeTimeEstimator> O_OBJECT_SLIC3R_T
|
||||||
|
|
||||||
GCodeWriter* O_OBJECT_SLIC3R
|
GCodeWriter* O_OBJECT_SLIC3R
|
||||||
Ref<GCodeWriter> O_OBJECT_SLIC3R_T
|
Ref<GCodeWriter> O_OBJECT_SLIC3R_T
|
||||||
Clone<GCodeWriter> O_OBJECT_SLIC3R_T
|
Clone<GCodeWriter> O_OBJECT_SLIC3R_T
|
||||||
|
Loading…
x
Reference in New Issue
Block a user