ENH: pre cooling and heating

1.Rely on nozzle change tag instead of T command to detect
extruder free range
2.Support enhance cooling for TPU filaments
3.Fix a negative temperature issue
4.M104 commands add params to specify that command is generated
by slicer

jira: NONE

Signed-off-by: xun.zhang <xun.zhang@bambulab.com>
Change-Id: I2d157e5a84da75e656d3281cf38eea8697bd897a
This commit is contained in:
xun.zhang 2024-11-15 19:05:55 +08:00 committed by lane.wei
parent a0c9fad052
commit b104e138dd
3 changed files with 227 additions and 61 deletions

View File

@ -65,8 +65,10 @@ const std::vector<std::string> GCodeProcessor::Reserved_Tags = {
"_GP_FILAMENT_USED_WEIGHT_PLACEHOLDER",
"_GP_FILAMENT_USED_VOLUME_PLACEHOLDER",
"_GP_FILAMENT_USED_LENGTH_PLACEHOLDER",
"_MACHINE_START_GCODE_END",
"_MACHINE_END_GCODE_START"
" MACHINE_START_GCODE_END",
" MACHINE_END_GCODE_START",
" NOZZLE_CHANGE_START",
" NOZZLE_CHANGE_END"
};
const std::string GCodeProcessor::Flush_Start_Tag = " FLUSH_START";
@ -424,6 +426,7 @@ void GCodeProcessor::TimeProcessor::reset()
void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<GCodeProcessorResult::MoveVertex>& moves, std::vector<size_t>& lines_ends, const TimeProcessContext& context)
{
using namespace ExtruderPreHeating;
FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") };
if (in.f == nullptr)
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for reading.\n"));
@ -480,10 +483,26 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
return std::string(line_M73);
};
constexpr unsigned int INVALID_GCODE_ID = (unsigned int)(-1);
auto format_line_M104 = [&context](int target_temp, int target_extruder = -1, const std::string& comment = std::string()) {
std::string buffer = "M104";
if (target_extruder != -1)
buffer += (" T" + std::to_string(context.physical_extruder_map[target_extruder]));
buffer += " S" + std::to_string(target_temp) + " N0"; // N0 means the gcode is generated by slicer
if (!comment.empty())
buffer += " ;" + comment;
buffer += '\n';
return buffer;
};
auto format_M73_remain_filament_changes = [](int filament_change_num, int total_filament_change)->std::string{
char buf[64];
snprintf(buf, sizeof(buf), "M73 E%d\n", total_filament_change - filament_change_num);
return std::string(buf);
};
// do not insert gcode into machine start & end gcode
unsigned int machine_start_gcode_end_line_id = INVALID_GCODE_ID; // mark the end line of machine start gcode
unsigned int machine_end_gcode_start_line_id = INVALID_GCODE_ID; // mark the start line of machine end gcode
unsigned int machine_start_gcode_end_line_id = (unsigned int)(-1); // mark the end line of machine start gcode
unsigned int machine_end_gcode_start_line_id = (unsigned int)(-1); // mark the start line of machine end gcode
// keeps track of last exported pair <percent, remaining time>
std::array<std::pair<int, int>, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> last_exported_main;
@ -623,6 +642,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
return std::tuple(!ret.empty(), (extra_lines_count == 0) ? extra_lines_count : extra_lines_count - 1);
};
// check for temporary lines
auto is_temporary_decoration = [](const std::string_view gcode_line) {
// remove trailing '\n'
@ -785,12 +805,17 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
write_string(filename_out, out, export_line, out_file_pos, line_ends);
};
struct FilamentChangeBlock
{
int filament_id;
unsigned int lower_gcode_id;
unsigned int upper_gcode_id; // [lower_gcode_id,upper_gcode_id) uses current filament , upper gcode id will be set after finding next block
FilamentChangeBlock(int filament_id_, unsigned int lower_gcode_id_, unsigned int upper_gcode_id_) :filament_id(filament_id_), lower_gcode_id(lower_gcode_id_), upper_gcode_id(upper_gcode_id_) {}
auto handle_nozzle_change_line = [&filament_maps=context.filament_maps](const std::string& line, int& old_filament, int& next_filament, int& extruder_id)->bool {
std::regex re(R"(OF(\d+)\s+NF(\d+))");
std::smatch match;
if (!std::regex_search(line, match, re))
return false;
old_filament = std::stoi(match[1]);
next_filament = std::stoi(match[2]);
extruder_id = filament_maps[next_filament];
return true;
};
enum InsertLineType
@ -804,12 +829,13 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
};
constexpr int buffer_size_in_KB = 64;
std::vector<FilamentChangeBlock> filament_blocks;
std::vector<FilamentUsageBlock> filament_blocks;
std::vector<ExtruderUsageBlcok> extruder_blocks = { ExtruderUsageBlcok() }; // the first use of extruder will not generate nozzle change tag, so manually add a dummy block
std::vector<std::pair<unsigned int, unsigned int>> offsets;
size_t g1_lines_counter = 0;
lines_ends.clear();
auto gcode_time_handler = [&filament_blocks, &offsets, &process_placeholders, &is_temporary_decoration, &process_line_move, &g1_lines_counter,&machine_start_gcode_end_line_id,&machine_end_gcode_start_line_id,&INVALID_GCODE_ID](std::string& gcode_line, std::string& gcode_buffer, int& line_id) {
ExtruderUsageBlcok temp_construct_block; // the temperarily constructed block, will be pushed into container after initializing
auto gcode_time_handler = [&temp_construct_block,&filament_blocks, &extruder_blocks, &offsets, &handle_nozzle_change_line , & process_placeholders, &is_temporary_decoration, & process_line_move, & g1_lines_counter, & machine_start_gcode_end_line_id, & machine_end_gcode_start_line_id](std::string& gcode_line, std::string& gcode_buffer, int& line_id) {
auto [processed, lines_added_count] = process_placeholders(gcode_line,line_id);
if (processed && lines_added_count > 0)
offsets.push_back({ line_id, lines_added_count });
@ -832,11 +858,33 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
std::istringstream str(gcode_line.substr(skips + 1)); // skip white spaces and T
str >> fid;
if (!str.fail() && 0 <= fid && fid < 255) {
// skip the filaments change in machine start/end gcode
if (machine_start_gcode_end_line_id == (unsigned int)(-1) && line_id<machine_start_gcode_end_line_id ||
machine_end_gcode_start_line_id != (unsigned int)(-1) && line_id>machine_end_gcode_start_line_id)
return;
if (!filament_blocks.empty())
filament_blocks.back().upper_gcode_id = line_id;
filament_blocks.emplace_back(fid, line_id, INVALID_GCODE_ID);
filament_blocks.emplace_back(fid, line_id, -1);
}
}
else if (GCodeReader::GCodeLine::cmd_start_with(gcode_line, (std::string(";") + reserved_tag(ETags::NozzleChangeStart)).c_str())) {
int prev_filament{ -1 }, next_filament{ -1 }, extruder_id{ -1 };
handle_nozzle_change_line(gcode_line, prev_filament, next_filament, extruder_id);
if (!extruder_blocks.empty()) {
extruder_blocks.back().initialize_step_2(line_id);
}
}
else if (GCodeReader::GCodeLine::cmd_start_with(gcode_line, (std::string(";") + reserved_tag(ETags::NozzleChangeEnd)).c_str())) {
int prev_filament{ -1 }, next_filament{ -1 }, extruder_id{ -1 };
handle_nozzle_change_line(gcode_line, prev_filament, next_filament, extruder_id);
if (!extruder_blocks.empty()) {
extruder_blocks.back().initialize_step_3(line_id, prev_filament, line_id);
}
temp_construct_block.initialize_step_1(extruder_id, line_id, next_filament);
extruder_blocks.emplace_back(temp_construct_block);
temp_construct_block.reset();
}
}
};
@ -844,8 +892,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
gcode_process(in, out, filename_in, filename_out, gcode_time_handler, nullptr, buffer_size_in_KB);
// updates moves' gcode ids which have been modified by the insertion of the M73 lines
unsigned int curr_offset_id = 0;
unsigned int total_offset = 0;
unsigned int curr_offset_id = 0, total_offset = 0;
for (GCodeProcessorResult::MoveVertex& move : moves) {
while (curr_offset_id < static_cast<unsigned int>(offsets.size()) && offsets[curr_offset_id].first <= move.gcode_id) {
total_offset += offsets[curr_offset_id].second;
@ -873,26 +920,9 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
// stores then strings to be inserted. first key is line id ,second key is content
std::map<unsigned int, std::vector<std::pair<std::string, InsertLineType>>> inserted_operation_lines;
auto format_line_M104 = [&context](int target_temp, int target_extruder = -1, const std::string& comment = std::string()) {
std::string buffer = "M104";
if (target_extruder != -1)
buffer += (" T" + std::to_string(context.physical_extruder_map[target_extruder]));
buffer += " S" + std::to_string(target_temp);
if (!comment.empty())
buffer += " ;" + comment;
buffer += '\n';
return buffer;
};
auto format_M73_remain_filament_changes = [](int filament_change_num, int total_filament_change)->std::string
{
char buf[64];
snprintf(buf, sizeof(buf), "M73 E%d\n", total_filament_change - filament_change_num);
return std::string(buf);
};
// save filament change block by extruder id
std::unordered_map<int, std::vector<FilamentChangeBlock>> extruder_change_info;
std::unordered_map<int, std::vector<ExtruderUsageBlcok>> extruder_change_info;
// collect the position to insert remaining filament changes
{
@ -907,7 +937,6 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
int curr_filament_change_num = 0;
for (const auto& fb : filament_blocks) {
int extruder_id = context.filament_maps[fb.filament_id];
extruder_change_info[extruder_id].emplace_back(fb);
if (curr_filament != -1 && curr_filament != fb.filament_id) {
curr_filament_change_num += 1;
inserted_operation_lines[fb.lower_gcode_id].emplace_back(format_M73_remain_filament_changes(curr_filament_change_num, total_filament_count), InsertLineType::FilamentChangePredict);
@ -916,6 +945,16 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
}
}
// After traversing the G-code, the first and last extruder blocks still have uncompleted initialization steps
if (!extruder_blocks.empty() && !filament_blocks.empty()) {
extruder_blocks.front().initialize_step_1(context.filament_maps[filament_blocks.front().filament_id], machine_start_gcode_end_line_id, filament_blocks.front().filament_id);
extruder_blocks.back().initialize_step_2(machine_end_gcode_start_line_id);
extruder_blocks.back().initialize_step_3(machine_end_gcode_start_line_id,filament_blocks.back().filament_id,machine_end_gcode_start_line_id);
}
for (auto& block : extruder_blocks)
extruder_change_info[block.extruder_id].emplace_back(block);
if (context.enable_pre_heating) {
// get the real speed mode used in slicing
size_t valid_machine_id = 0;
@ -926,25 +965,48 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
}
}
auto pre_cooling_heating_handle = [&inserted_operation_lines, &format_line_M104, &context = std::as_const(context), &moves = std::as_const(moves), valid_machine_id](const unsigned int extruder_free_range_lower, const unsigned int extruder_free_range_upper, const bool pre_cool, const bool pre_heat, const int curr_temp, const int target_temp, const int extruder_id)
auto pre_cooling_heating_handle = [&inserted_operation_lines, &format_line_M104, &context = std::as_const(context), &moves = std::as_const(moves), valid_machine_id](
const unsigned int extruder_free_range_lower, const unsigned int extruder_free_range_upper,
const unsigned int post_extrusion_lower, const unsigned int post_extrusion_upper,
const bool pre_cool, const bool pre_heat,
int curr_temp, int target_temp,
const int extruder_id, bool is_small_flow_filament)
{
if (!pre_cool && !pre_heat || extruder_free_range_upper < extruder_free_range_lower)
auto gcode_move_comp = [](const GCodeProcessorResult::MoveVertex& a, unsigned int gcode_id) { return a.gcode_id < gcode_id; };
if (!pre_cool && !pre_heat || extruder_free_range_upper <= extruder_free_range_lower)
return;
auto move_iter_lower = std::lower_bound(moves.begin(), moves.end(), extruder_free_range_lower, [](const GCodeProcessorResult::MoveVertex& a, unsigned int gcode_id) { return a.gcode_id < gcode_id; });
auto move_iter_upper = std::lower_bound(moves.begin(), moves.end(), extruder_free_range_upper, [](const GCodeProcessorResult::MoveVertex& a, unsigned int gcode_id) { return a.gcode_id < gcode_id; });
auto move_iter_lower = std::lower_bound(moves.begin(), moves.end(), extruder_free_range_lower, gcode_move_comp);
auto move_iter_upper = std::lower_bound(moves.begin(), moves.end(), extruder_free_range_upper, gcode_move_comp); // closed iter
if (move_iter_lower == moves.end() || move_iter_upper == moves.end() || move_iter_upper == moves.begin())
return;
--move_iter_upper;
if (move_iter_lower >= move_iter_upper)
return;
float time_gap = move_iter_upper->time[valid_machine_id] - move_iter_lower->time[valid_machine_id];
// the free time of extruder is too short, do not perform pre cooling and heating
if (time_gap < context.pre_heating_time_threshold)
return;
if (is_small_flow_filament && pre_cool) {
auto post_extrusion_move_lower = std::lower_bound(moves.begin(), moves.end(), post_extrusion_lower, gcode_move_comp);
auto post_extrusion_move_upper = std::lower_bound(moves.begin(), moves.end(), post_extrusion_upper, gcode_move_comp); // closed iter
if (post_extrusion_move_lower != moves.end() && post_extrusion_move_upper != moves.end() && post_extrusion_move_upper != moves.begin()){
--post_extrusion_move_upper;
if (post_extrusion_move_lower < post_extrusion_move_upper) {
float time_gap = post_extrusion_move_upper->time[valid_machine_id] - post_extrusion_move_lower->time[valid_machine_id];
float max_cooling_temp = std::min((float)(curr_temp), std::min(context.post_extrusion_cooling_threshold, time_gap * context.cooling_rate));
curr_temp -= max_cooling_temp; // set the temperature after doing cooling when post-extruding
inserted_operation_lines[post_extrusion_move_lower->gcode_id].emplace_back(format_line_M104(curr_temp, extruder_id, "Multi extruder pre cooling"), InsertLineType::PreCooling);
}
}
}
if (pre_cool && !pre_heat) {
// only perform cooling
if (target_temp > curr_temp)
return;
inserted_operation_lines[move_iter_lower->gcode_id].emplace_back(format_line_M104(target_temp, extruder_id, "Multi extruder pre cooling"), InsertLineType::PreCooling);
inserted_operation_lines[extruder_free_range_lower].emplace_back(format_line_M104(target_temp, extruder_id, "Multi extruder pre cooling"), InsertLineType::PreCooling);
return;
}
if (!pre_cool && pre_heat) {
@ -954,7 +1016,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
float heating_start_time = move_iter_upper->time[valid_machine_id] - (target_temp - curr_temp) / context.heating_rate;
auto heating_move_iter = std::upper_bound(move_iter_lower, move_iter_upper + 1, heating_start_time, [valid_machine_id](float time, const GCodeProcessorResult::MoveVertex& a) {return time < a.time[valid_machine_id]; });
if (heating_move_iter == move_iter_upper + 1 || heating_move_iter == move_iter_lower) {
inserted_operation_lines[move_iter_lower->gcode_id].emplace_back(format_line_M104(target_temp, extruder_id, "Multi extruder pre heating"), InsertLineType::PreHeating);
inserted_operation_lines[extruder_free_range_lower].emplace_back(format_line_M104(target_temp, extruder_id, "Multi extruder pre heating"), InsertLineType::PreHeating);
}
else {
--heating_move_iter;
@ -976,35 +1038,56 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
if (real_cooling_time < context.pre_heating_time_threshold / 2)
return;
float real_delta_temp = real_cooling_time * context.cooling_rate;
inserted_operation_lines[move_iter_lower->gcode_id].emplace_back(format_line_M104(curr_temp - real_delta_temp, extruder_id, "Multi extruder pre cooling"), InsertLineType::PreCooling);
int real_delta_temp = std::min((int)(real_cooling_time * context.cooling_rate), curr_temp);
inserted_operation_lines[extruder_free_range_lower].emplace_back(format_line_M104(curr_temp - real_delta_temp, extruder_id, "Multi extruder pre cooling"), InsertLineType::PreCooling);
inserted_operation_lines[heating_move_iter->gcode_id].emplace_back(format_line_M104(target_temp, extruder_id, "Multi extruder pre heating"), InsertLineType::PreHeating);
};
// collect the insert position of gcode line for pre heating and cooling
for (auto& extruder_changes : extruder_change_info) {
auto check_filament_type = [&filament_types = std::as_const(context.filament_types)](const int filament_id, const std::string& type)->bool {
return filament_types[filament_id] == type;
};
int extruder_id = extruder_changes.first;
const auto& filament_blocks = extruder_changes.second;
auto iter_lower = filament_blocks.begin();
auto iter_upper = filament_blocks.end();
const auto& extruder_blocks = extruder_changes.second;
if (extruder_blocks.empty())
continue;
auto iter_lower = extruder_blocks.begin();
auto iter_upper = extruder_blocks.end();
// only consider T command(filament change) in [machine_start_gcode_end,machine_end_gcode_start)
while (iter_lower != filament_blocks.end() && iter_lower->lower_gcode_id < machine_start_gcode_end_line_id)
while (iter_lower != extruder_blocks.end() && iter_lower->start_id < machine_start_gcode_end_line_id)
++iter_lower;
while (iter_upper != filament_blocks.begin() && std::prev(iter_upper)->lower_gcode_id >= machine_end_gcode_start_line_id)
while (iter_upper != extruder_blocks.begin() && std::prev(iter_upper)->start_id >= machine_end_gcode_start_line_id)
--iter_upper;
if (std::distance(iter_lower, iter_upper) > 0) {
pre_cooling_heating_handle(machine_start_gcode_end_line_id, std::min(filament_blocks.front().lower_gcode_id, machine_end_gcode_start_line_id), false, true, 0, context.filament_nozzle_temp[filament_blocks.front().filament_id], extruder_id);
}
if (iter_lower >= iter_upper)
continue;
pre_cooling_heating_handle(
machine_start_gcode_end_line_id, std::min(iter_lower->start_id, machine_end_gcode_start_line_id),
machine_start_gcode_end_line_id, machine_start_gcode_end_line_id,
true, true,
context.filament_nozzle_temp[iter_lower->start_filament], context.filament_nozzle_temp[iter_lower->start_filament],
extruder_id, check_filament_type(iter_lower->start_filament, "TPU"));
for (auto iter = iter_lower; iter != iter_upper; ++iter) {
auto niter = std::next(iter);
if (niter != iter_upper) {
pre_cooling_heating_handle(std::max(machine_start_gcode_end_line_id,iter->upper_gcode_id), std::min(machine_end_gcode_start_line_id,niter->lower_gcode_id), true, true, context.filament_nozzle_temp[iter->filament_id], context.filament_nozzle_temp[iter->filament_id], extruder_id);
pre_cooling_heating_handle(
std::max(machine_start_gcode_end_line_id, iter->end_id), std::min(machine_end_gcode_start_line_id, niter->start_id),
iter->post_extrusion_start_id, iter->post_extrusion_end_id,
true, true,
context.filament_nozzle_temp[iter->end_filament], context.filament_nozzle_temp[niter->start_filament],
extruder_id, check_filament_type(iter->end_filament,"TPU"));
}
else if (iter->upper_gcode_id != INVALID_GCODE_ID) {
// INVALID_GCODE_ID means that is the last filament block in gcode, it will be handled by machine end gcode
pre_cooling_heating_handle(std::max(machine_start_gcode_end_line_id, iter->upper_gcode_id), machine_end_gcode_start_line_id, true, false, context.filament_nozzle_temp[iter->filament_id], 0, extruder_id);
else {
pre_cooling_heating_handle(
std::max(machine_start_gcode_end_line_id, iter->end_id), machine_end_gcode_start_line_id,
iter->post_extrusion_start_id, iter->post_extrusion_end_id,
true, false,
context.filament_nozzle_temp[iter->end_filament], 0,
extruder_id, check_filament_type(iter->end_filament,"TPU"));
}
}
}
@ -1470,6 +1553,11 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_filament_nozzle_temp.resize(filament_count);
for (size_t idx = 0; idx < filament_count; ++idx)
m_filament_nozzle_temp[idx] = config.nozzle_temperature.get_at(idx);
m_filament_types.resize(filament_count);
for (size_t idx = 0; idx < filament_count; ++idx)
m_filament_types[idx] = config.filament_type.get_at(idx);
m_hotend_cooling_rate = config.hotend_cooling_rate.value;
m_hotend_heating_rate = config.hotend_heating_rate.value;
m_enable_pre_heating = config.enable_pre_heating;
@ -1570,6 +1658,13 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
m_filament_nozzle_temp[idx] = nozzle_temperature->get_at(idx);
}
const ConfigOptionStrings* filament_type = config.option<ConfigOptionStrings>("filament_type");
if (filament_type != nullptr) {
m_filament_types.resize(filament_type->size());
for (size_t idx = 0; idx < filament_type->size(); ++idx)
m_filament_types[idx] = filament_type->get_at(idx);
}
const ConfigOptionFloat* hotend_cooling_rate = config.option<ConfigOptionFloat>("hotend_cooling_rate");
if (hotend_cooling_rate != nullptr) {
m_hotend_cooling_rate = hotend_cooling_rate->value;
@ -2126,6 +2221,7 @@ void GCodeProcessor::finalize(bool post_process)
m_used_filaments,
m_filament_lists,
m_filament_maps,
m_filament_types,
m_filament_nozzle_temp,
m_physical_extruder_map,
m_layer_id,
@ -2134,7 +2230,6 @@ void GCodeProcessor::finalize(bool post_process)
pre_heating_time_threshold,
m_enable_pre_heating
);
m_time_processor.post_process(m_result.filename, m_result.moves, m_result.lines_ends, context);
}
#if ENABLE_GCODE_VIEWER_STATISTICS

View File

@ -297,6 +297,63 @@ namespace Slic3r {
void unlock() const { result_mutex.unlock(); }
};
namespace ExtruderPreHeating
{
struct FilamentUsageBlock
{
int filament_id;
unsigned int lower_gcode_id;
unsigned int upper_gcode_id; // [lower_gcode_id,upper_gcode_id) uses current filament , upper gcode id will be set after finding next block
FilamentUsageBlock(int filament_id_, unsigned int lower_gcode_id_, unsigned int upper_gcode_id_) :filament_id(filament_id_), lower_gcode_id(lower_gcode_id_), upper_gcode_id(upper_gcode_id_) {}
};
/**
* @brief Describle the usage of a exturder in a section
*
* The strucutre stores the start and end lines of the sections as well as
* the filament used at the beginning and end of the section.
* Post extrusion means the final extrusion before switching to the next extruder.
*
* Simplified GCode Flow:
* 1.Extruder Change Block (ext0 switch to ext1)
* 2.Extruder Usage Block (use ext1 to print)
* 3.Extruder Change Block (ext1 switch to ext0)
* 4.Extruder Usage Block (use ext0 to print)
* 5.Extruder Change Block (ext0 switch to ex1)
* ...
*
* So the construct of extruder usage block relys on two extruder change block
*/
struct ExtruderUsageBlcok
{
int extruder_id = -1;
unsigned int start_id = -1;
unsigned int end_id = -1;
int start_filament = -1;
int end_filament = -1;
unsigned int post_extrusion_start_id = -1;
unsigned int post_extrusion_end_id = -1;
void initialize_step_1(int extruder_id_, int start_id_, int start_filament_) {
extruder_id = extruder_id_;
start_id = start_id_;
start_filament = start_filament_;
};
void initialize_step_2(int post_extrusion_start_id_) {
post_extrusion_start_id = post_extrusion_start_id_;
}
void initialize_step_3(int end_id_, int end_filament_, int post_extrusion_end_id_) {
end_id = end_id_;
end_filament = end_filament_;
post_extrusion_end_id = post_extrusion_end_id_;
}
void reset() {
*this = ExtruderUsageBlcok();
}
ExtruderUsageBlcok() = default;
};
}
class GCodeProcessor
{
@ -327,7 +384,9 @@ namespace Slic3r {
Used_Filament_Volume_Placeholder,
Used_Filament_Length_Placeholder,
MachineStartGCodeEnd,
MachineEndGCodeStart
MachineEndGCodeStart,
NozzleChangeStart,
NozzleChangeEnd
};
static const std::string& reserved_tag(ETags tag) { return Reserved_Tags[static_cast<unsigned char>(tag)]; }
@ -552,6 +611,7 @@ namespace Slic3r {
{
UsedFilaments used_filaments; // stores the accurate filament usage info
std::vector<Extruder> filament_lists;
std::vector<std::string> filament_types;
std::vector<int> filament_maps; // map each filament to extruder
std::vector<float> filament_nozzle_temp;
std::vector<int> physical_extruder_map;
@ -560,12 +620,14 @@ namespace Slic3r {
float cooling_rate{ 2.f }; // Celsius degree per second
float heating_rate{ 2.f }; // Celsius degree per second
float pre_heating_time_threshold{ 30.f }; // only active pre cooling & heating if time gap is bigger than threshold
float post_extrusion_cooling_threshold{ 30.f }; // threshold of temp if do cooling in post extrusion
bool enable_pre_heating{ false };
TimeProcessContext(
const UsedFilaments& used_filaments_,
const std::vector<Extruder>& filament_lists_,
const std::vector<int>& filament_maps_,
const std::vector<std::string>& filament_types_,
const std::vector<float>& filament_nozzle_temp_,
const std::vector<int>& physical_extruder_map_,
const size_t total_layer_num_,
@ -577,6 +639,7 @@ namespace Slic3r {
used_filaments(used_filaments_),
filament_lists(filament_lists_),
filament_maps(filament_maps_),
filament_types(filament_types_),
filament_nozzle_temp(filament_nozzle_temp_),
physical_extruder_map(physical_extruder_map_),
total_layer_num(total_layer_num_),
@ -769,6 +832,7 @@ namespace Slic3r {
std::vector<float> m_remaining_volume;
std::vector<Extruder> m_filament_lists;
std::vector<float> m_filament_nozzle_temp;
std::vector<std::string> m_filament_types;
float m_hotend_cooling_rate{ 2.f };
float m_hotend_heating_rate{ 2.f };
float m_enable_pre_heating{ false };

View File

@ -966,6 +966,13 @@ WipeTower::NozzleChangeResult WipeTower::nozzle_change(int old_filament_id, int
// Otherwise we are going to Unload only. And m_layer_info would be invalid.
}
auto format_nozzle_change_line = [](bool start, int old_filament_id, int new_filament_id)->std::string {
char buff[64];
std::string tag = start ? GCodeProcessor::reserved_tag(GCodeProcessor::ETags::NozzleChangeStart) : GCodeProcessor::reserved_tag(GCodeProcessor::ETags::NozzleChangeEnd);
snprintf(buff, sizeof(buff), ";%s OF%d NF%d\n", tag.c_str(), old_filament_id, new_filament_id);
return std::string(buff);
};
float nozzle_change_speed = 60.0f * m_filpar[m_current_tool].max_e_speed / m_extrusion_flow;
if (is_tpu_filament(m_current_tool)) {
nozzle_change_speed *= 0.25;
@ -977,7 +984,7 @@ WipeTower::NozzleChangeResult WipeTower::nozzle_change(int old_filament_id, int
.set_initial_tool(m_current_tool)
.set_extrusion_flow(m_extrusion_flow)
.set_y_shift(m_y_shift + (new_filament_id != (unsigned int) (-1) && (m_current_shape == SHAPE_REVERSED) ? m_layer_info->depth - m_layer_info->toolchanges_depth() : 0.f))
.append("; Nozzle change start\n");
.append(format_nozzle_change_line(true,old_filament_id,new_filament_id));
writer.speed_override_backup();
writer.speed_override(100);
@ -1070,12 +1077,12 @@ WipeTower::NozzleChangeResult WipeTower::nozzle_change(int old_filament_id, int
writer.append(lift_gcode);
}
writer.append("; Nozzle change end\n");
writer.append(format_nozzle_change_line(false, old_filament_id, new_filament_id));
NozzleChangeResult result;
result.start_pos = initial_position;
result.end_pos = writer.pos();
result.gcode = std::move(writer.gcode());
result.gcode = writer.gcode();
return result;
}