FanMover rework

- new code path
 - add kickstart
 - add bypass for not-overhangs fan calls
todo: unit test
This commit is contained in:
supermerill 2020-11-02 04:09:06 +01:00
parent 62d64b4f61
commit b66901672f
10 changed files with 331 additions and 138 deletions

View File

@ -15,7 +15,12 @@ group:silent_mode_event:Firmware
setting:gcode_flavor
setting:silent_mode
setting:remaining_times
setting:fan_speedup_time
group:Cooling fan
line:Speedup
setting:label$Speedup time:fan_speedup_time
setting:label$Only for overhangs:fan_speedup_overhangs
end_line
setting:label$Small:fan_kickstart
group:Thumbnails
line:Size for Gcode
setting:id$0:label$Small:thumbnails

View File

@ -263,7 +263,6 @@ double ExtrusionLoop::min_mm3_per_mm() const
return min_mm3_per_mm;
}
std::string ExtrusionEntity::role_to_string(ExtrusionRole role)
{
switch (role) {

View File

@ -3495,30 +3495,6 @@ void GCode::use(const ExtrusionEntityCollection &collection) {
}
}
std::string extrusion_role_2_string(const ExtrusionRole &er) {
switch (er) {
case erNone: return "none";
case erPerimeter: return "perimeter";
case erExternalPerimeter: return "perimeter external";
case erOverhangPerimeter: return "perimeter overhang";
case erInternalInfill: return "infill internal";
case erSolidInfill: return "infill solid";
case erTopSolidInfill: return "infill solid top";
case erBridgeInfill: return "infill bridge";
case erThinWall: return "thin_wall";
case erGapFill: return "gap_fill";
case erSkirt: return "skirt";
case erSupportMaterial: return "support_material";
case erSupportMaterialInterface: return "support_material_interface";
case erWipeTower: return "wipe_tower";
case erMilling: return "milling";
case erCustom: return "custom";
case erMixed: return "mixed";
case erCount: return "count";
}
return " unkown";
}
std::string GCode::extrude_path(const ExtrusionPath &path, const std::string &description, double speed) {
ExtrusionPath simplifed_path = path;
@ -3676,12 +3652,15 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill
void GCode::_post_process(std::string& what, bool flush) {
//if enabled, move the fan startup earlier.
if (this->config().fan_speedup_time.value != 0) {
if (this->config().fan_speedup_time.value != 0 || this->config().fan_kickstart.value > 0) {
if (this->m_fan_mover.get() == nullptr)
this->m_fan_mover.reset(new Slic3r::FanMover(
std::abs(this->config().fan_speedup_time.value),
this->config().fan_speedup_time.value > 0,
this->config().use_relative_e_distances.value));
this->m_writer,
std::abs(this->config().fan_speedup_time.value),
this->config().fan_speedup_time.value > 0,
this->config().use_relative_e_distances.value,
this->config().fan_speedup_overhangs.value,
this->config().fan_kickstart.value));
what = this->m_fan_mover->process_gcode(what, flush);
}
@ -3769,7 +3748,7 @@ std::vector<double> cut_corner_cache = {
std::string GCode::_extrude(const ExtrusionPath &path, const std::string &description, double speed) {
std::string descr = description + extrusion_role_2_string(path.role());
std::string descr = description + ExtrusionEntity::role_to_string(path.role());
std::string gcode = this->_before_extrude(path, descr, speed);
// calculate extrusion length per distance unit

View File

@ -22,13 +22,17 @@ const std::string& FanMover::process_gcode(const std::string& gcode, bool flush)
{
m_process_output = "";
// recompute buffer time to recover from rounding
m_buffer_time_size = 0;
for (auto& data : m_buffer) m_buffer_time_size += data.time;
m_parser.parse_buffer(gcode,
[this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { /*m_process_output += line.raw() + "\n";*/ this->_process_gcode_line(reader, line); });
if (flush) {
while (!buffer.empty()) {
m_process_output += buffer.back().raw + "\n";
buffer.pop_back();
while (!m_buffer.empty()) {
m_process_output += m_buffer.front().raw + "\n";
remove_from_buffer(m_buffer.begin());
}
}
@ -71,15 +75,152 @@ void change_axis_value(std::string& line, char axis, const float new_value, cons
line = line.replace(pos, end - pos, ss.str());
}
int16_t get_fan_speed(const std::string &line, GCodeFlavor flavor) {
if (line.compare(0, 4, "M106") == 0) {
if (flavor == (gcfMach3) || flavor == (gcfMachinekit)) {
return (int16_t)get_axis_value(line, 'P');
} else {
return (int16_t)get_axis_value(line, 'S');
}
} else if (line.compare(0, 4, "M127") == 0 || line.compare(0, 4, "M107") == 0) {
return 0;
} else if ((flavor == (gcfMakerWare) || flavor == (gcfSailfish)) && line.compare(0, 4, "M126") == 0) {
return (int16_t)get_axis_value(line, 'T');
} else {
return -1;
}
}
void FanMover::_put_in_middle_G1(std::list<BufferData>::iterator &item_to_split, float nb_sec, BufferData &&line_to_write) {
//std::cout << "_put_in_middle_G1\n";
assert(item_to_split != m_buffer.end());
if (nb_sec < item_to_split->time * 0.1) {
// doesn't really need to be split, print it after
m_buffer.insert(next(item_to_split), line_to_write);
} else if (nb_sec > item_to_split->time * 0.9) {
// doesn't really need to be split, print it before
//will also print before if line_to_split.time == 0
m_buffer.insert(item_to_split, line_to_write);
} else if (item_to_split->raw.size() > 2
&& item_to_split->raw[0] == 'G' && item_to_split->raw[1] == '1' && item_to_split->raw[2] == ' ') {
float percent = nb_sec / item_to_split->time;
BufferData before = *item_to_split;
before.time *= percent;
item_to_split->time *= (1-percent);
if (item_to_split->dx != 0) {
before.dx = item_to_split->dx * percent;
item_to_split->x += before.dx;
item_to_split->dx = item_to_split->dx * (1-percent);
change_axis_value(before.raw, 'X', before.x + before.dx, 3);
}
if (item_to_split->dy != 0) {
before.dy = item_to_split->dy * percent;
item_to_split->y += before.dy;
item_to_split->dy = item_to_split->dy * (1 - percent);
change_axis_value(before.raw, 'Y', before.y + before.dy, 3);
}
if (item_to_split->dz != 0) {
before.dz = item_to_split->dz * percent;
item_to_split->z += before.dz;
item_to_split->dz = item_to_split->dz * (1 - percent);
change_axis_value(before.raw, 'Z', before.z + before.dz, 3);
}
if (item_to_split->de != 0) {
if (relative_e) {
before.de = item_to_split->de * percent;
change_axis_value(before.raw, 'E', before.de, 5);
item_to_split->de = item_to_split->de * (1 - percent);
change_axis_value(item_to_split->raw, 'E', item_to_split->de, 5);
} else {
before.de = item_to_split->de * percent;
item_to_split->e += before.de;
item_to_split->de = item_to_split->de * (1 - percent);
change_axis_value(before.raw, 'E', before.e + before.de, 5);
change_axis_value(item_to_split->raw, 'E', item_to_split->e + item_to_split->de, 5);
}
}
//add before then line_to_write, then there is the modified data.
m_buffer.insert(item_to_split, before);
m_buffer.insert(item_to_split, line_to_write);
} else {
//not a G1, print it before
m_buffer.insert(item_to_split, line_to_write);
}
}
void FanMover::_print_in_middle_G1(BufferData& line_to_split, float nb_sec, const std::string &line_to_write) {
//std::cout << "_print_in_middle_G1\n";
if (nb_sec < line_to_split.time * 0.1) {
// doesn't really need to be split, print it after
m_process_output += line_to_split.raw + "\n";
m_process_output += line_to_write + (line_to_write.back() == '\n'?"":"\n");
} else if (nb_sec > line_to_split.time * 0.9) {
// doesn't really need to be split, print it before
//will also print before if line_to_split.time == 0
m_process_output += line_to_write + (line_to_write.back() == '\n' ? "" : "\n");
m_process_output += line_to_split.raw + "\n";
}else if(line_to_split.raw.size() > 2
&& line_to_split.raw[0] == 'G' && line_to_split.raw[1] == '1' && line_to_split.raw[2] == ' ') {
float percent = nb_sec / line_to_split.time;
std::string before = line_to_split.raw;
std::string& after = line_to_split.raw;
if (line_to_split.dx != 0) {
change_axis_value(before, 'X', line_to_split.x + line_to_split.dx * percent, 3);
}
if (line_to_split.dy != 0) {
change_axis_value(before, 'Y', line_to_split.y + line_to_split.dy * percent, 3);
}
if (line_to_split.dz != 0) {
change_axis_value(before, 'Z', line_to_split.z + line_to_split.dz * percent, 3);
}
if (line_to_split.de != 0) {
if (relative_e) {
change_axis_value(before, 'E', line_to_split.de * percent, 5);
change_axis_value(after, 'E', line_to_split.de * (1 - percent), 5);
} else {
change_axis_value(before, 'E', line_to_split.e + line_to_split.de * percent, 5);
change_axis_value(after, 'E', line_to_split.e + line_to_split.de * (1 - percent), 5);
}
}
m_process_output += before + "\n";
m_process_output += line_to_write + (line_to_write.back() == '\n' ? "" : "\n");
m_process_output += line_to_split.raw + "\n";
} else {
//not a G1, print it before
m_process_output += line_to_write + (line_to_write.back() == '\n' ? "" : "\n");
m_process_output += line_to_split.raw + "\n";
}
}
void FanMover::_remove_slow_fan(int16_t min_speed, float past_sec) {
//erase fan in the buffer -> don't slowdown if you are in the process of step-up.
//we began at the "recent" side , and remove as long as we don't push past_sec to 0
auto it = m_buffer.begin();
while (it != m_buffer.end() && past_sec > 0) {
past_sec -= it->time;
if (it->fan_speed >= 0 && it->fan_speed < min_speed){
//found something that is lower than us
it = remove_from_buffer(it);
} else {
++it;
}
}
}
void FanMover::_process_gcode_line(GCodeReader& reader, const GCodeReader::GCodeLine& line)
{
// processes 'normal' gcode lines
std::string cmd = line.cmd();
double time = 0;
float fan_speed = -1;
int16_t fan_speed = -1;
if (cmd.length() > 1) {
if (line.has_f())
current_speed = line.f() / 60.0f;
m_current_speed = line.f() / 60.0f;
switch (::toupper(cmd[0])) {
case 'G':
{
@ -90,129 +231,141 @@ void FanMover::_process_gcode_line(GCodeReader& reader, const GCodeReader::GCode
double dist = distx * distx + disty * disty + distz * distz;
if (dist > 0) {
dist = std::sqrt(dist);
time = dist / current_speed;
time = dist / m_current_speed;
}
}
break;
}
case 'M':
{
if (::atoi(&cmd[1]) == 106) {
if (line.has_value('S', fan_speed) ) {
int nb_M106_erased = 0;
if (fan_speed > expected_fan_speed) {
time = -1; // don't write!
buffer.emplace_front(BufferData("; erased: "+line.raw(), 0, -1));
//erase M106 in the buffer -> don't slowdown if you are in the process of step-up.
auto it = buffer.begin();
int i = 0;
while (it != buffer.end()) {
if (it->raw.compare(0, 4, "M106") == 0 && it->fan_speed < fan_speed) {
//found something that is lower than us -> change is speed by ours and delete us
it->fan_speed = fan_speed;
std::stringstream ss; ss << "S" << (int)fan_speed;
it->raw = std::regex_replace(it->raw, regex_fan_speed, ss.str());
nb_M106_erased++;
} else {
++it;
i++;
}
fan_speed = get_fan_speed(line.raw(), m_writer.config.gcode_flavor);
if (fan_speed > 0) {
if (!only_overhangs || current_role != ExtrusionRole::erOverhangPerimeter) {
// this M106 need to go in the past
//check if we have !( kickstart and not in slowdown )
if (kickstart <= 0 || fan_speed < m_current_fan_speed) {
// first erase everything lower that that value
_remove_slow_fan(fan_speed, m_buffer_time_size + 1);
// then write the fan command
if (m_buffer_time_size > nb_seconds_delay) {
_print_in_middle_G1(m_buffer.front(), m_buffer_time_size - nb_seconds_delay, line.raw());
remove_from_buffer(m_buffer.begin());
} else {
m_process_output += line.raw() + "\n";
}
if (nb_M106_erased == 0) {
//try to split the G1/G0 line to increae precision
if (!buffer.empty()) {
BufferData& backdata = buffer.back();
if (buffer_time_size > nb_seconds_delay * 1.1f && backdata.raw.size() > 2
&& backdata.raw[0] == 'G' && backdata.raw[1] == '1' && backdata.raw[2] == ' ') {
float percent = (buffer_time_size - nb_seconds_delay) / backdata.time;
std::string before = backdata.raw;
std::string& after = backdata.raw;
if (backdata.dx != 0) {
change_axis_value(before, 'X', backdata.x + backdata.dx * percent, 3);
}
if (backdata.dy != 0) {
change_axis_value(before, 'Y', backdata.y + backdata.dy * percent, 3);
}
if (backdata.dz != 0) {
change_axis_value(before, 'Z', backdata.z + backdata.dz * percent, 3);
}
if (backdata.de != 0) {
if (relative_e) {
change_axis_value(before, 'E', backdata.de * percent, 5);
change_axis_value(after, 'E', backdata.de * (1 - percent), 5);
} else {
change_axis_value(before, 'E', backdata.e + backdata.de * percent, 5);
change_axis_value(after, 'E', backdata.e + backdata.de * (1 - percent), 5);
}
}
m_process_output += before + "\n";
buffer_time_size -= backdata.time * percent;
backdata.time -= backdata.time * percent;
}
} else {
//if kickstart
// first erase everything lower that that value
_remove_slow_fan(fan_speed, m_buffer_time_size + 1);
// first erase everything lower that kickstart
_remove_slow_fan(255, kickstart);
// print me
if (m_buffer_time_size > nb_seconds_delay) {
_print_in_middle_G1(m_buffer.front(), m_buffer_time_size - nb_seconds_delay, m_writer.set_fan(100, true));
remove_from_buffer(m_buffer.begin());
} else {
m_process_output += m_writer.set_fan(100, true);
}
//write it in the queue if possible
float time_count = kickstart;
auto it = m_buffer.begin();
while (it != m_buffer.end() && time_count > 0) {
if (time_count - it->time < 0) {
//found something that is lower than us
_put_in_middle_G1(it, time_count, BufferData{ line.cmd(), 0, fan_speed });
//found, stop
break;
}
//print it
if (with_D_option) {
std::stringstream ss;
ss << " D" << (uint32_t)(buffer_time_size * 1000) << "\n";
m_process_output += line.raw() + ss.str();
} else {
m_process_output += line.raw() + "\n";
}
current_fan_speed = fan_speed;
++it;
time_count -= it->time;
}
}
} else {
if (kickstart <= 0) {
//nothing to do
//we don't put time = -1; so it will printed in the buffer as other line are done
} else {
//if kickstart, write the M106 255 first
time = -1;
//set the target speed and set the kickstart flag
put_in_buffer(BufferData(m_writer.set_fan(100, true), 0, fan_speed, true));
//add the normal speed line for the future
m_current_kickstart.fan_speed = fan_speed;
m_current_kickstart.time = kickstart;
m_current_kickstart.raw = line.raw();
}
//update
expected_fan_speed = fan_speed;
}
}
break;
}
}
} else {
if(!line.raw().empty() && line.raw().front() == ';')
{
if (line.raw().size() > 10 && line.raw().rfind(";TYPE:", 0) == 0) {
// get the type of the next extrusions
std::string extrusion_string = line.raw().substr(6, line.raw().size() - 6);
current_role = ExtrusionEntity::string_to_role(extrusion_string);
}
}
}
if (time >= 0) {
buffer.emplace_front(BufferData(line.raw(), time, fan_speed));
BufferData& front = buffer.front();
BufferData& new_data = put_in_buffer(BufferData(line.raw(), time, fan_speed));
if (line.has(Axis::X)) {
front.x = reader.x();
front.dx = line.dist_X(reader);
new_data.x = reader.x();
new_data.dx = line.dist_X(reader);
}
if (line.has(Axis::Y)) {
front.y = reader.y();
front.dy = line.dist_Y(reader);
new_data.y = reader.y();
new_data.dy = line.dist_Y(reader);
}
if (line.has(Axis::Z)) {
front.z = reader.z();
front.dz = line.dist_Z(reader);
new_data.z = reader.z();
new_data.dz = line.dist_Z(reader);
}
if (line.has(Axis::E)) {
front.e = reader.e();
if(relative_e)
front.de = line.e();
new_data.e = reader.e();
if (relative_e)
new_data.de = line.e();
else
front.de = line.dist_E(reader);
new_data.de = line.dist_E(reader);
}
if (m_current_kickstart.time > 0) {
m_current_kickstart.time -= time;
if (m_current_kickstart.time < 0) {
//prev is possible because we just do a emplace_back.
_put_in_middle_G1(prev(m_buffer.end()), time + m_current_kickstart.time, BufferData{ m_current_kickstart.raw, 0, m_current_kickstart.fan_speed });
}
}
buffer_time_size += time;
}
// puts the line back into the gcode
//if buffer too big, flush it.
if (time > 0) {
while (buffer_time_size - buffer.back().time > nb_seconds_delay) {
BufferData &backdata = buffer.back();
if (backdata.fan_speed < 0 || (int)backdata.fan_speed != (int)current_fan_speed) {
buffer_time_size -= backdata.time;
m_process_output += backdata.raw + "\n";
if (backdata.fan_speed >= 0) {
current_fan_speed = backdata.fan_speed;
while (!m_buffer.empty() && m_buffer_time_size - m_buffer.front().time > nb_seconds_delay) {
BufferData& backdata = m_buffer.front();
if (backdata.fan_speed < 0 || backdata.fan_speed != m_current_fan_speed) {
if (backdata.is_kickstart && backdata.fan_speed < m_current_fan_speed) {
//you have to slow down! not kickstart! rewrite the fan speed.
m_process_output += m_writer.set_fan(backdata.fan_speed,true);
m_current_fan_speed = backdata.fan_speed;
} else {
m_process_output += backdata.raw + "\n";
if (backdata.fan_speed >= 0) {
//note that this is the only plce where the fan_speed is set and we print from the buffer, as if the fan_speed >= 0 => time == 0
//and as this flush all time == 0 lines fromt he back of the queue...
m_current_fan_speed = backdata.fan_speed;
}
}
}
buffer.pop_back();
remove_from_buffer(m_buffer.begin());
}
}
float sum = 0;
for (auto& data : m_buffer) sum += data.time;
assert( std::abs(m_buffer_time_size - sum) < 0.01);
}
} // namespace Slic3r

View File

@ -8,6 +8,7 @@
#include "../Point.hpp"
#include "../GCodeReader.hpp"
#include "../GCodeWriter.hpp"
#include <regex>
namespace Slic3r {
@ -16,10 +17,14 @@ class BufferData {
public:
std::string raw;
float time;
float fan_speed;
int16_t fan_speed;
bool is_kickstart;
float x = 0, y = 0, z = 0, e = 0;
float dx = 0, dy = 0, dz = 0, de = 0;
BufferData(std::string line, float time = 0, float fan_speed = 0) : raw(line), time(time), fan_speed(fan_speed) {}
BufferData(std::string line, float time = 0, int16_t fan_speed = 0, float is_kickstart = false) : raw(line), time(time), fan_speed(fan_speed), is_kickstart(is_kickstart){
//avoid double \n
if(!line.empty() && line.back() == '\n') line.pop_back();
}
};
class FanMover
@ -29,28 +34,54 @@ private:
const float nb_seconds_delay;
const bool with_D_option;
const bool relative_e;
const bool only_overhangs;
const float kickstart;
// in unit/second
double current_speed = 1000 / 60.0;;
float buffer_time_size = 0;
GCodeReader m_parser{};
int expected_fan_speed = 0;
int current_fan_speed = 0;
GCodeWriter& m_writer;
//current value (at the back of the buffer), when parsing a new line
ExtrusionRole current_role = ExtrusionRole::erCustom;
// in unit/second
double m_current_speed = 1000 / 60.0;
// variable for when you add a line (front of the buffer)
int m_current_fan_speed = 0;
BufferData m_current_kickstart{"",-1,0};
//buffer
std::list<BufferData> m_buffer;
double m_buffer_time_size = 0;
// The output of process_layer()
std::list<BufferData> buffer;
std::string m_process_output;
public:
FanMover(const float nb_seconds_delay, const bool with_D_option, const bool relative_e)
: regex_fan_speed("S[0-9]+"), nb_seconds_delay(nb_seconds_delay), with_D_option(with_D_option), relative_e(relative_e){}
FanMover(GCodeWriter& writer, const float nb_seconds_delay, const bool with_D_option, const bool relative_e,
const bool only_overhangs, const float kickstart)
: regex_fan_speed("S[0-9]+"),
nb_seconds_delay(nb_seconds_delay>0 ? std::max(0.01f,nb_seconds_delay) : 0),
with_D_option(with_D_option)
, relative_e(relative_e), only_overhangs(only_overhangs), kickstart(kickstart), m_writer(writer){}
// Adds the gcode contained in the given string to the analysis and returns it after removing the workcodes
const std::string& process_gcode(const std::string& gcode, bool flush);
private:
BufferData& put_in_buffer(BufferData&& data) {
m_buffer_time_size += data.time;
m_buffer.emplace_back(data);
return m_buffer.back();
}
std::list<BufferData>::iterator remove_from_buffer(std::list<BufferData>::iterator& data) {
m_buffer_time_size -= data->time;
return m_buffer.erase(data);
}
// Processes the given gcode line
void _process_gcode_line(GCodeReader& reader, const GCodeReader::GCodeLine& line);
void _put_in_middle_G1(std::list<BufferData>::iterator& item_to_split, float nb_sec, BufferData&& line_to_write);
void _print_in_middle_G1(BufferData& line_to_split, float nb_sec, const std::string& line_to_write);
void _remove_slow_fan(int16_t min_speed, float past_sec);
};
} // namespace Slic3r

View File

@ -252,7 +252,7 @@ std::string GCodeWriter::set_fan(const unsigned int speed, bool dont_save, uint1
gcode << "\n";
} else {
if (FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish)) {
gcode << "M126";
gcode << "M126 T";
} else {
gcode << "M106 ";
if (FLAVOR_IS(gcfMach3) || FLAVOR_IS(gcfMachinekit)) {

View File

@ -641,7 +641,11 @@ const std::vector<std::string>& Preset::printer_options()
if (s_opts.empty()) {
s_opts = {
"printer_technology",
"bed_shape", "bed_custom_texture", "bed_custom_model", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed",
"bed_shape", "bed_custom_texture", "bed_custom_model", "z_offset",
"fan_kickstart",
"fan_speedup_overhangs",
"fan_speedup_time",
"gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed",
"use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
"min_length",
"host_type", "print_host", "printhost_apikey", "printhost_cafile", "printhost_slug",
@ -662,7 +666,6 @@ const std::vector<std::string>& Preset::printer_options()
"wipe_advanced_nozzle_melted_volume",
"wipe_advanced_multiplier",
"wipe_advanced_algo",
"fan_speedup_time",
"time_estimation_compensation",
};
s_opts.insert(s_opts.end(), Preset::machine_limits_options().begin(), Preset::machine_limits_options().end());

View File

@ -101,6 +101,8 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
"extrusion_multiplier",
"fan_always_on",
"fan_below_layer_time",
"fan_kickstart",
"fan_speedup_overhangs",
"fan_speedup_time",
"filament_colour",
"filament_diameter",

View File

@ -2072,12 +2072,29 @@ void PrintConfigDef::init_fff_params()
def = this->add("fan_speedup_time", coFloat);
def->label = L("Fan startup delay");
def->category = OptionCategory::firmware;
def->tooltip = L("Move the M106 in the past by at least this delay (in seconds, you can use decimals) and add the 'D' option to it to tell to the firware when the fan have to be at this speed."
" It assume infinite acceleration for this time estimation, and only takes into account G1 and G0 moves. Use 0 to deactivate, negative to remove the 'D' option.");
def->tooltip = L("Move the fan start in the past by at least this delay (in seconds, you can use decimals)."
" It assumes infinite acceleration for this time estimation, and will only take into account G1 and G0 moves. Use 0 to deactivate.");
def->sidetext = L("s");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(-0.5));
def = this->add("fan_speedup_overhangs", coBool);
def->label = L("Fan startup delay");
def->category = OptionCategory::firmware;
def->tooltip = L("Will only take into account the delay for the cooling of overhangs.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(true));
def = this->add("fan_kickstart", coFloat);
def->label = L("Fan KickStart time");
def->category = OptionCategory::firmware;
def->tooltip = L("Add a M106 S255 (max speed for fan) for this amount of seconds before gonig down to the desired speed to kick-start the cooling fan."
"\nSet to 0 to deactivate.");
def->sidetext = L("s");
def->min = 0;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("machine_limits_usage", coEnum);
def->label = L("How to apply");
def->full_label = L("Purpose of Machine Limits");

View File

@ -964,6 +964,8 @@ public:
ConfigOptionFloats extruder_temperature_offset;
ConfigOptionString extrusion_axis;
ConfigOptionFloats extrusion_multiplier;
ConfigOptionFloat fan_kickstart;
ConfigOptionBool fan_speedup_overhangs;
ConfigOptionFloat fan_speedup_time;
ConfigOptionFloats filament_cost;
ConfigOptionFloats filament_density;
@ -1067,6 +1069,8 @@ protected:
OPT_PTR(extruder_temperature_offset);
OPT_PTR(extrusion_axis);
OPT_PTR(extrusion_multiplier);
OPT_PTR(fan_kickstart);
OPT_PTR(fan_speedup_overhangs);
OPT_PTR(fan_speedup_time);
OPT_PTR(filament_diameter);
OPT_PTR(filament_density);