2017-05-03 16:28:22 +00:00
|
|
|
#include "../GCode.hpp"
|
2016-12-21 22:09:58 +00:00
|
|
|
#include "CoolingBuffer.hpp"
|
|
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
|
|
#include <boost/algorithm/string/replace.hpp>
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
namespace Slic3r {
|
|
|
|
|
2017-06-22 10:59:23 +00:00
|
|
|
CoolingBuffer::CoolingBuffer(GCode &gcodegen) :
|
|
|
|
m_gcodegen(gcodegen), m_layer_id(0),
|
|
|
|
m_elapsed_time(new ElapsedTime)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
CoolingBuffer::~CoolingBuffer()
|
|
|
|
{
|
|
|
|
delete m_elapsed_time;
|
|
|
|
}
|
|
|
|
|
2017-05-03 16:28:22 +00:00
|
|
|
std::string CoolingBuffer::append(const std::string &gcode, size_t object_id, size_t layer_id, bool is_support)
|
2016-12-21 22:09:58 +00:00
|
|
|
{
|
|
|
|
std::string out;
|
2017-05-17 18:06:33 +00:00
|
|
|
size_t signature = object_id * 2 + (is_support ? 1 : 0);
|
2017-05-03 16:28:22 +00:00
|
|
|
if (m_object_ids_visited.find(signature) != m_object_ids_visited.end())
|
|
|
|
// For a single print_z, a combination of (object_id, is_support) could repeat once only.
|
|
|
|
// If the combination of (object_id, is_support) reappears, this must be for another print_z,
|
|
|
|
// therefore a layer has to be finalized.
|
2016-12-21 22:09:58 +00:00
|
|
|
out = this->flush();
|
2017-05-03 16:28:22 +00:00
|
|
|
|
|
|
|
m_object_ids_visited.insert(signature);
|
|
|
|
m_layer_id = layer_id;
|
|
|
|
m_gcode += gcode;
|
2016-12-21 22:09:58 +00:00
|
|
|
// This is a very rough estimate of the print time,
|
|
|
|
// not taking into account the acceleration curves generated by the printer firmware.
|
2017-06-22 10:59:23 +00:00
|
|
|
*m_elapsed_time += m_gcodegen.get_reset_elapsed_time();
|
2016-12-21 22:09:58 +00:00
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2017-05-03 16:28:22 +00:00
|
|
|
void apply_speed_factor(std::string &line, float speed_factor, float min_print_speed)
|
2016-12-21 22:09:58 +00:00
|
|
|
{
|
|
|
|
// find pos of F
|
|
|
|
size_t pos = line.find_first_of('F');
|
|
|
|
size_t last_pos = line.find_first_of(' ', pos+1);
|
|
|
|
|
|
|
|
// extract current speed
|
|
|
|
float speed;
|
|
|
|
{
|
|
|
|
std::istringstream iss(line.substr(pos+1, last_pos));
|
|
|
|
iss >> speed;
|
|
|
|
}
|
|
|
|
|
|
|
|
// change speed
|
|
|
|
speed *= speed_factor;
|
|
|
|
speed = std::max(speed, min_print_speed);
|
|
|
|
|
|
|
|
// replace speed in string
|
|
|
|
{
|
|
|
|
std::ostringstream oss;
|
|
|
|
oss << speed;
|
2017-06-22 10:59:23 +00:00
|
|
|
line.replace(pos+1, last_pos-pos, oss.str());
|
2016-12-21 22:09:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-03 16:28:22 +00:00
|
|
|
std::string CoolingBuffer::flush()
|
2016-12-21 22:09:58 +00:00
|
|
|
{
|
2017-05-03 16:28:22 +00:00
|
|
|
const FullPrintConfig &config = m_gcodegen.config();
|
2017-06-22 10:59:23 +00:00
|
|
|
|
|
|
|
std::string gcode = std::move(m_gcode);
|
2017-05-03 16:28:22 +00:00
|
|
|
m_gcode.clear();
|
|
|
|
|
2017-06-21 14:15:39 +00:00
|
|
|
int fan_speed = config.fan_always_on.values.front() ? config.min_fan_speed.values.front() : 0;
|
2016-12-21 22:09:58 +00:00
|
|
|
|
|
|
|
float speed_factor = 1.0;
|
2017-06-22 10:59:23 +00:00
|
|
|
bool slowdown_external = true;
|
|
|
|
|
2017-06-21 14:15:39 +00:00
|
|
|
if (config.cooling.values.front()) {
|
2016-12-21 22:09:58 +00:00
|
|
|
#ifdef SLIC3R_DEBUG
|
2017-06-22 10:59:23 +00:00
|
|
|
printf("Layer %zu estimated printing time: %f seconds\n", m_layer_id, m_elapsed_time->total);
|
|
|
|
#endif
|
|
|
|
if (m_elapsed_time->total < (float)config.slowdown_below_layer_time.values.front()) {
|
2016-12-21 22:09:58 +00:00
|
|
|
// Layer time very short. Enable the fan to a full throttle and slow down the print
|
|
|
|
// (stretch the layer print time to slowdown_below_layer_time).
|
2017-06-21 14:15:39 +00:00
|
|
|
fan_speed = config.max_fan_speed.values.front();
|
2017-06-22 10:59:23 +00:00
|
|
|
|
|
|
|
// We are not altering speed of bridges.
|
|
|
|
float time_to_stretch = m_elapsed_time->total - m_elapsed_time->bridges;
|
|
|
|
float target_time = (float)config.slowdown_below_layer_time.values.front() - m_elapsed_time->bridges;
|
|
|
|
|
|
|
|
// If we spend most of our time on external perimeters include them in the slowdown,
|
|
|
|
// otherwise only alter other extrusions.
|
|
|
|
if (m_elapsed_time->external_perimeters < 0.5f * time_to_stretch) {
|
|
|
|
time_to_stretch -= m_elapsed_time->external_perimeters;
|
|
|
|
target_time -= m_elapsed_time->external_perimeters;
|
|
|
|
slowdown_external = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
speed_factor = time_to_stretch / target_time;
|
|
|
|
} else if (m_elapsed_time->total < (float)config.fan_below_layer_time.values.front()) {
|
2016-12-21 22:09:58 +00:00
|
|
|
// Layer time quite short. Enable the fan proportionally according to the current layer time.
|
2017-06-21 14:15:39 +00:00
|
|
|
fan_speed = config.max_fan_speed.values.front()
|
|
|
|
- (config.max_fan_speed.values.front() - config.min_fan_speed.values.front())
|
2017-06-22 10:59:23 +00:00
|
|
|
* (m_elapsed_time->total - (float)config.slowdown_below_layer_time.values.front())
|
2017-06-21 14:15:39 +00:00
|
|
|
/ (config.fan_below_layer_time.values.front() - config.slowdown_below_layer_time.values.front());
|
2016-12-21 22:09:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef SLIC3R_DEBUG
|
|
|
|
printf(" fan = %d%%, speed = %f%%\n", fan_speed, speed_factor * 100);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (speed_factor < 1.0) {
|
|
|
|
// Adjust feed rate of G1 commands marked with an _EXTRUDE_SET_SPEED
|
|
|
|
// as long as they are not _WIPE moves (they cannot if they are _EXTRUDE_SET_SPEED)
|
|
|
|
// and they are not preceded directly by _BRIDGE_FAN_START (do not adjust bridging speed).
|
|
|
|
std::string new_gcode;
|
|
|
|
std::istringstream ss(gcode);
|
|
|
|
std::string line;
|
2017-05-03 16:28:22 +00:00
|
|
|
bool bridge_fan_start = false;
|
2017-06-21 14:15:39 +00:00
|
|
|
float min_print_speed = float(config.min_print_speed.values.front() * 60.);
|
2016-12-21 22:09:58 +00:00
|
|
|
while (std::getline(ss, line)) {
|
|
|
|
if (boost::starts_with(line, "G1")
|
|
|
|
&& boost::contains(line, ";_EXTRUDE_SET_SPEED")
|
|
|
|
&& !boost::contains(line, ";_WIPE")
|
2017-06-22 10:59:23 +00:00
|
|
|
&& !bridge_fan_start
|
|
|
|
&& (slowdown_external || !boost::contains(line, ";_EXTERNAL_PERIMETER"))) {
|
2017-05-03 16:28:22 +00:00
|
|
|
apply_speed_factor(line, speed_factor, min_print_speed);
|
2016-12-21 22:09:58 +00:00
|
|
|
boost::replace_first(line, ";_EXTRUDE_SET_SPEED", "");
|
|
|
|
}
|
2017-06-22 10:59:23 +00:00
|
|
|
bridge_fan_start = boost::starts_with(line, ";_BRIDGE_FAN_START");
|
2016-12-21 22:09:58 +00:00
|
|
|
new_gcode += line + '\n';
|
|
|
|
}
|
|
|
|
gcode = new_gcode;
|
|
|
|
}
|
|
|
|
}
|
2017-06-21 14:15:39 +00:00
|
|
|
if (m_layer_id < config.disable_fan_first_layers.values.front())
|
2016-12-21 22:09:58 +00:00
|
|
|
fan_speed = 0;
|
|
|
|
|
2017-05-03 16:28:22 +00:00
|
|
|
gcode = m_gcodegen.writer().set_fan(fan_speed) + gcode;
|
2016-12-21 22:09:58 +00:00
|
|
|
|
|
|
|
// bridge fan speed
|
2017-06-21 14:15:39 +00:00
|
|
|
if (!config.cooling.values.front() || config.bridge_fan_speed.values.front() == 0 || m_layer_id < config.disable_fan_first_layers.values.front()) {
|
2016-12-21 22:09:58 +00:00
|
|
|
boost::replace_all(gcode, ";_BRIDGE_FAN_START", "");
|
|
|
|
boost::replace_all(gcode, ";_BRIDGE_FAN_END", "");
|
|
|
|
} else {
|
2017-06-21 14:15:39 +00:00
|
|
|
boost::replace_all(gcode, ";_BRIDGE_FAN_START", m_gcodegen.writer().set_fan(config.bridge_fan_speed.values.front(), true));
|
2017-05-03 16:28:22 +00:00
|
|
|
boost::replace_all(gcode, ";_BRIDGE_FAN_END", m_gcodegen.writer().set_fan(fan_speed, true));
|
2016-12-21 22:09:58 +00:00
|
|
|
}
|
|
|
|
boost::replace_all(gcode, ";_WIPE", "");
|
|
|
|
boost::replace_all(gcode, ";_EXTRUDE_SET_SPEED", "");
|
2017-06-22 10:59:23 +00:00
|
|
|
boost::replace_all(gcode, ";_EXTERNAL_PERIMETER", "");
|
2016-12-21 22:09:58 +00:00
|
|
|
|
2017-05-03 16:28:22 +00:00
|
|
|
m_object_ids_visited.clear();
|
2017-06-22 10:59:23 +00:00
|
|
|
m_elapsed_time->reset();
|
2016-12-21 22:09:58 +00:00
|
|
|
return gcode;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|