mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-14 07:25: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/GCodeReader.cpp
|
||||
${LIBDIR}/libslic3r/GCodeSender.cpp
|
||||
${LIBDIR}/libslic3r/GCodeTimeEstimator.cpp
|
||||
${LIBDIR}/libslic3r/GCodeWriter.cpp
|
||||
${LIBDIR}/libslic3r/Geometry.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/GCodeSender.cpp
|
||||
src/libslic3r/GCodeSender.hpp
|
||||
src/libslic3r/GCodeTimeEstimator.cpp
|
||||
src/libslic3r/GCodeTimeEstimator.hpp
|
||||
src/libslic3r/GCodeWriter.cpp
|
||||
src/libslic3r/GCodeWriter.hpp
|
||||
src/libslic3r/Geometry.cpp
|
||||
@ -180,6 +182,7 @@ xsp/Filler.xsp
|
||||
xsp/Flow.xsp
|
||||
xsp/GCode.xsp
|
||||
xsp/GCodeSender.xsp
|
||||
xsp/GCodeTimeEstimator.xsp
|
||||
xsp/GCodeWriter.xsp
|
||||
xsp/Geometry.xsp
|
||||
xsp/GUI.xsp
|
||||
|
@ -39,7 +39,7 @@ SpiralVase::process_layer(const std::string &gcode)
|
||||
{
|
||||
GCodeReader r = this->_reader; // clone
|
||||
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.extruding()) {
|
||||
total_layer_length += line.dist_XY();
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "GCodeReader.hpp"
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
namespace Slic3r {
|
||||
@ -17,59 +18,73 @@ GCodeReader::parse(const std::string &gcode, callback_t callback)
|
||||
{
|
||||
std::istringstream ss(gcode);
|
||||
std::string line;
|
||||
while (std::getline(ss, line)) {
|
||||
GCodeLine gline(this);
|
||||
gline.raw = line;
|
||||
if (this->verbose)
|
||||
std::cout << line << std::endl;
|
||||
|
||||
// strip comment
|
||||
{
|
||||
size_t pos = line.find(';');
|
||||
if (pos != std::string::npos) {
|
||||
gline.comment = line.substr(pos+1);
|
||||
line.erase(pos);
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
while (std::getline(ss, line))
|
||||
this->parse_line(line, callback);
|
||||
}
|
||||
|
||||
void
|
||||
GCodeReader::parse_line(std::string line, callback_t callback)
|
||||
{
|
||||
GCodeLine gline(this);
|
||||
gline.raw = line;
|
||||
if (this->verbose)
|
||||
std::cout << line << std::endl;
|
||||
|
||||
// strip comment
|
||||
{
|
||||
size_t pos = line.find(';');
|
||||
if (pos != std::string::npos) {
|
||||
gline.comment = line.substr(pos+1);
|
||||
line.erase(pos);
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -25,6 +25,7 @@ class GCodeReader {
|
||||
GCodeLine(GCodeReader* _reader) : reader(_reader) {};
|
||||
|
||||
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_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; };
|
||||
@ -44,7 +45,7 @@ class GCodeReader {
|
||||
bool travel() const { return this->cmd == "G1" && !this->has('E'); };
|
||||
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;
|
||||
bool verbose;
|
||||
@ -53,6 +54,8 @@ class GCodeReader {
|
||||
GCodeReader() : X(0), Y(0), Z(0), E(0), F(0), verbose(false), _extrusion_axis('E') {};
|
||||
void apply_config(const PrintConfigBase &config);
|
||||
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:
|
||||
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(GCode, "GCode");
|
||||
REGISTER_CLASS(GCodeSender, "GCode::Sender");
|
||||
REGISTER_CLASS(GCodeTimeEstimator, "GCode::TimeEstimator");
|
||||
REGISTER_CLASS(GCodeWriter, "GCode::Writer");
|
||||
REGISTER_CLASS(Layer, "Layer");
|
||||
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
|
||||
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
|
||||
Ref<GCodeWriter> O_OBJECT_SLIC3R_T
|
||||
Clone<GCodeWriter> O_OBJECT_SLIC3R_T
|
||||
|
Loading…
x
Reference in New Issue
Block a user