diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 6a757ee9e..ffdb570f7 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2452,11 +2452,27 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill return gcode; } + +void GCode::_post_process(std::string& what) { + //if enabled, move the fan startup earlier. + if (this->config().fan_speedup_time.value != 0) { + FanMover fen_post_process(std::abs(this->config().fan_speedup_time.value), this->config().fan_speedup_time.value>0); + what = fen_post_process.process_gcode(what); + } +} + void GCode::_write(FILE* file, const char *what) { if (what != nullptr) { + + //const char * gcode_pp = _post_process(what).c_str(); + std::string str_preproc = what; + _post_process(str_preproc); + + const std::string str_ana = m_analyzer.process_gcode(str_preproc); + // apply analyzer, if enabled - const char* gcode = m_enable_analyzer ? m_analyzer.process_gcode(what).c_str() : what; + const char * gcode = m_enable_analyzer ? str_ana.c_str() : str_preproc.c_str(); // writes string to file fwrite(gcode, 1, ::strlen(gcode), file); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 15c0c5748..22bc364ab 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -353,6 +353,7 @@ protected: // Write a string into a file. void _write(FILE* file, const std::string& what) { this->_write(file, what.c_str()); } void _write(FILE* file, const char *what); + // Write a string into a file. // Add a newline, if the string does not end with a newline already. @@ -362,6 +363,9 @@ protected: // Formats and write into a file the given data. void _write_format(FILE* file, const char* format, ...); + //some post-processing on the file, before the analyzer + void _post_process(std::string& what); + std::string _extrude(const ExtrusionPath &path, const std::string &description, double speed = -1); std::string _before_extrude(const ExtrusionPath &path, const std::string &description, double speed = -1); std::string _after_extrude(const ExtrusionPath &path); diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp index c32acd4e9..641fc64ba 100644 --- a/src/libslic3r/GCode/Analyzer.cpp +++ b/src/libslic3r/GCode/Analyzer.cpp @@ -863,5 +863,110 @@ size_t GCodeAnalyzer::memory_used() const out += m_process_output.size(); return out; } +const std::string& FanMover::process_gcode(const std::string& gcode) { + m_process_output = ""; + buffer.clear(); + buffer_time_size = 0; + current_speed = 1000 / 60.0; + expected_fan_speed = 0; + current_fan_speed = 0; + + m_parser.parse_buffer(gcode, + [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { /*m_process_output += line.raw() + "\n";*/ this->_process_gcode_line(reader, line); }); + + while (!buffer.empty()) { + m_process_output += buffer.back().raw + "\n"; + buffer.pop_back(); + } + + return m_process_output; +} + +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; + if (cmd.length() > 1) { + if (line.has_f()) + current_speed = line.f() / 60.0f; + switch (::toupper(cmd[0])) { + case 'G': + { + if (::atoi(&cmd[1]) == 1 || ::atoi(&cmd[1]) == 0) { + double dist = line.dist_X(reader)*line.dist_X(reader) + line.dist_Y(reader)*line.dist_Y(reader) + line.dist_Z(reader)*line.dist_Z(reader); + if (dist > 0) { + dist = std::sqrt(dist); + time = dist / 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! + //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->speed < fan_speed) { + //found somethign that is lower than us -> change is speed by ours and delete us + it->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++; + } + } + + if (nb_M106_erased == 0) { + //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; + } + } + + //update + expected_fan_speed = fan_speed; + } + } + break; + } + } + } + + if (time >= 0) { + buffer.emplace_front(BufferData(line.raw(), time, 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.speed < 0 || (int)backdata.speed != (int)current_fan_speed) { + buffer_time_size -= backdata.time; + m_process_output += backdata.raw + "\n"; + if (backdata.speed >= 0) { + current_fan_speed = backdata.speed; + } + } + buffer.pop_back(); + } + } +} } // namespace Slic3r diff --git a/src/libslic3r/GCode/Analyzer.hpp b/src/libslic3r/GCode/Analyzer.hpp index 389c11cec..08373055a 100644 --- a/src/libslic3r/GCode/Analyzer.hpp +++ b/src/libslic3r/GCode/Analyzer.hpp @@ -7,6 +7,7 @@ #include "../Point.hpp" #include "../GCodeReader.hpp" +#include namespace Slic3r { @@ -239,6 +240,42 @@ private: void _calc_gcode_preview_unretractions(GCodePreviewData& preview_data); }; +class BufferData { +public: + std::string raw; + float time; + float speed; + BufferData(std::string line, float time = 0, float speed = 0) : raw(line), time(time), speed(speed) {} +}; +class FanMover +{ +private: + const std::regex regex_fan_speed; + const float nb_seconds_delay; + const bool with_D_option; + + // in unit/second + double current_speed; + float buffer_time_size; + GCodeReader m_parser; + int expected_fan_speed; + int current_fan_speed; + + // The output of process_layer() + std::list buffer; + std::string m_process_output; + +public: + FanMover(const float nb_seconds_delay, const bool with_D_option) : regex_fan_speed("S[0-9]+"), nb_seconds_delay(nb_seconds_delay), with_D_option(with_D_option){} + + // 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); + +private: + // Processes the given gcode line + void _process_gcode_line(GCodeReader& reader, const GCodeReader::GCodeLine& line); +}; + } // namespace Slic3r #endif /* slic3r_GCode_Analyzer_hpp_ */ diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index e0b4fcca5..f8d19396a 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -117,6 +117,7 @@ bool Print::invalidate_state_by_config_options(const std::vectoradd("remaining_times", coBool); def->label = L("Supports remaining times"); + def->category = L("Firmware"); def->tooltip = L("Emit M73 P[percent printed] R[remaining time in minutes] at 1 minute" " intervals into the G-code to let the firmware show accurate remaining time." " As of now only the Prusa i3 MK3 firmware recognizes M73." " Also the i3 MK3 firmware supports M73 Qxx Sxx for the silent mode."); + def->cli = "remaining-times!"; def->mode = comExpert; def->default_value = new ConfigOptionBool(false); def = this->add("silent_mode", coBool); - def->label = L("Supports silent mode"); + def->label = L("Supports silent mode"); + def->category = L("Firmware"); def->tooltip = L("Set silent mode for the G-code flavor"); def->mode = comExpert; - def->default_value = new ConfigOptionBool(true); + def->default_value = new ConfigOptionBool(true); + + def = this->add("fan_speedup_time", coFloat); + def->label = L("fan startup delay"); + def->category = L("Firmware"); + def->tooltip = L("Move the M106 in the past by at least this delay 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->cli = "fan-speedup-time=f"; + def->mode = comExpert; + def->default_value = new ConfigOptionFloat(0); const int machine_limits_opt_width = 70; { diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index bfd353bb3..5fcc83a21 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -716,6 +716,7 @@ public: ConfigOptionStrings end_filament_gcode; ConfigOptionString extrusion_axis; ConfigOptionFloats extrusion_multiplier; + ConfigOptionFloat fan_speedup_time; ConfigOptionFloats filament_diameter; ConfigOptionFloats filament_density; ConfigOptionStrings filament_type; @@ -795,6 +796,7 @@ protected: OPT_PTR(end_filament_gcode); OPT_PTR(extrusion_axis); OPT_PTR(extrusion_multiplier); + OPT_PTR(fan_speedup_time); OPT_PTR(filament_diameter); OPT_PTR(filament_density); OPT_PTR(filament_type); diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index c388b9d91..da8683b8b 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -861,7 +861,7 @@ namespace SupportMaterialInternal { for (const ExtrusionEntity *ee : perimeters.entities) { if (ee->is_collection()) { for (const ExtrusionEntity *ee2 : static_cast(ee)->entities) { - assert(! ee2->is_collection()); + //assert(! ee2->is_collection()); // there are loops for perimeters and collections for thin walls !! if (ee2->is_loop()) collect_bridging_perimeter_areas(*static_cast(ee2), expansion_scaled, out); } diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index d58e3e4a7..510d046d2 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -409,7 +409,8 @@ const std::vector& Preset::printer_options() "machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e", "machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e", "machine_min_extruding_rate", "machine_min_travel_rate", - "machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e" + "machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e", + "fan_speedup_time" }; s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end()); } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index f7ee5977a..604007fa1 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1939,7 +1939,8 @@ void TabPrinter::build_fff() optgroup = page->new_optgroup(_(L("Firmware"))); optgroup->append_single_option_line("gcode_flavor"); optgroup->append_single_option_line("silent_mode"); - optgroup->append_single_option_line("remaining_times"); + optgroup->append_single_option_line("remaining_times"); + optgroup->append_single_option_line("fan_speedup_time"); optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value) { wxTheApp->CallAfter([this, opt_key, value]() {