remove TimeProcessor::post_process

This commit is contained in:
SoftFever 2025-02-16 18:38:38 +08:00
parent a4e37d428e
commit 14d49b738f
2 changed files with 9 additions and 364 deletions

View file

@ -404,359 +404,6 @@ void GCodeProcessor::TimeProcessor::reset()
machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].enabled = true;
}
void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<GCodeProcessorResult::MoveVertex>& moves, std::vector<size_t>& lines_ends, size_t total_layer_num)
{
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"));
const bool disable_m73 = this->disable_m73;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": before process %1%")%filename.c_str();
// temporary file to contain modified gcode
std::string out_path = filename + ".postprocess";
FilePtr out{ boost::nowide::fopen(out_path.c_str(), "wb") };
if (out.f == nullptr) {
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for writing.\n"));
}
auto time_in_minutes = [](float time_in_seconds) {
assert(time_in_seconds >= 0.f);
return int((time_in_seconds + 0.5f) / 60.0f);
};
auto time_in_last_minute = [](float time_in_seconds) {
assert(time_in_seconds <= 60.0f);
return time_in_seconds / 60.0f;
};
auto format_line_M73_main = [](const std::string& mask, int percent, int time) {
char line_M73[64];
sprintf(line_M73, mask.c_str(),
std::to_string(percent).c_str(),
std::to_string(time).c_str());
return std::string(line_M73);
};
auto format_line_M73_stop_int = [](const std::string& mask, int time) {
char line_M73[64];
sprintf(line_M73, mask.c_str(), std::to_string(time).c_str());
return std::string(line_M73);
};
auto format_line_exhaust_fan_control = [](const std::string& mask,int fan_index,int percent) {
char line_fan[64] = { 0 };
sprintf(line_fan,mask.c_str(),
std::to_string(fan_index).c_str(),
std::to_string(int((percent/100.0)*255)).c_str());
return std::string(line_fan);
};
auto format_time_float = [](float time) {
return Slic3r::float_to_string_decimal_point(time, 2);
};
auto format_line_M73_stop_float = [format_time_float](const std::string& mask, float time) {
char line_M73[64];
sprintf(line_M73, mask.c_str(), format_time_float(time).c_str());
return std::string(line_M73);
};
std::string gcode_line;
size_t g1_lines_counter = 0;
// 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;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
last_exported_main[i] = { 0, time_in_minutes(machines[i].time) };
}
// keeps track of last exported remaining time to next printer stop
std::array<int, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> last_exported_stop;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
last_exported_stop[i] = time_in_minutes(machines[i].time);
}
// buffer line to export only when greater than 64K to reduce writing calls
std::string export_line;
// replace placeholder lines with the proper final value
// gcode_line is in/out parameter, to reduce expensive memory allocation
auto process_placeholders = [&](std::string& gcode_line) {
int extra_lines_count = 0;
// remove trailing '\n'
auto line = std::string_view(gcode_line).substr(0, gcode_line.length() - 1);
std::string ret;
if (line.length() > 1) {
line = line.substr(1);
if (line == reserved_tag(ETags::First_Line_M73_Placeholder) || line == reserved_tag(ETags::Last_Line_M73_Placeholder)) {
if (disable_m73) {
// Remove current line
gcode_line = "";
return std::tuple(true, -1);
}
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
const TimeMachine& machine = machines[i];
if (machine.enabled) {
// export pair <percent, remaining time>
ret += format_line_M73_main(machine.line_m73_main_mask.c_str(),
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? 0 : 100,
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? time_in_minutes(machine.time) : 0);
++extra_lines_count;
// export remaining time to next printer stop
if (line == reserved_tag(ETags::First_Line_M73_Placeholder) && !machine.stop_times.empty()) {
int to_export_stop = time_in_minutes(machine.stop_times.front().elapsed_time);
ret += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
last_exported_stop[i] = to_export_stop;
++extra_lines_count;
}
}
}
}
else if (line == reserved_tag(ETags::Estimated_Printing_Time_Placeholder)) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
const TimeMachine& machine = machines[i];
PrintEstimatedStatistics::ETimeMode mode = static_cast<PrintEstimatedStatistics::ETimeMode>(i);
if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) {
char buf[128];
if(!s_IsBBLPrinter)
// SoftFever: compatibility with klipper_estimator
sprintf(buf, "; estimated printing time (normal mode) = %s\n", get_time_dhms(machine.time).c_str());
else {
//sprintf(buf, "; estimated printing time (%s mode) = %s\n",
// (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
// get_time_dhms(machine.time).c_str());
sprintf(buf, "; model printing time: %s; total estimated time: %s\n",
get_time_dhms(machine.time - machine.prepare_time).c_str(),
get_time_dhms(machine.time).c_str());
}
ret += buf;
}
}
}
//BBS: write total layer number
else if (line == reserved_tag(ETags::Total_Layer_Number_Placeholder)) {
char buf[128];
sprintf(buf, "; total layer number: %zd\n", total_layer_num);
ret += buf;
}
}
if (! ret.empty())
// Not moving the move operator on purpose, so that the gcode_line allocation will grow and it will not be reallocated after handful of lines are processed.
gcode_line = ret;
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'
assert(! gcode_line.empty());
assert(gcode_line.back() == '\n');
// return true for decorations which are used in processing the gcode but that should not be exported into the final gcode
// i.e.:
// bool ret = gcode_line.substr(0, gcode_line.length() - 1) == ";" + Layer_Change_Tag;
// ...
// return ret;
return false;
};
// Iterators for the normal and silent cached time estimate entry recently processed, used by process_line_G1.
auto g1_times_cache_it = Slic3r::reserve_vector<std::vector<TimeMachine::G1LinesCacheItem>::const_iterator>(machines.size());
for (const auto& machine : machines)
g1_times_cache_it.emplace_back(machine.g1_times_cache.begin());
// add lines M73 to exported gcode
auto process_line_move = [
// Lambdas, mostly for string formatting, all with an empty capture block.
time_in_minutes, format_time_float, format_line_M73_main, format_line_M73_stop_int, format_line_M73_stop_float, time_in_last_minute,format_line_exhaust_fan_control,
&self = std::as_const(*this),
// Caches, to be modified
&g1_times_cache_it, &last_exported_main, &last_exported_stop,
// String output
&export_line]
(const size_t g1_lines_counter) {
unsigned int exported_lines_count = 0;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
const TimeMachine& machine = self.machines[i];
if (machine.enabled) {
// export pair <percent, remaining time>
// Skip all machine.g1_times_cache below g1_lines_counter.
auto& it = g1_times_cache_it[i];
while (it != machine.g1_times_cache.end() && it->id < g1_lines_counter)
++it;
if (it != machine.g1_times_cache.end() && it->id == g1_lines_counter) {
std::pair<int, int> to_export_main = { int(100.0f * it->elapsed_time / machine.time),
time_in_minutes(machine.time - it->elapsed_time) };
if (last_exported_main[i] != to_export_main) {
export_line += format_line_M73_main(machine.line_m73_main_mask.c_str(),
to_export_main.first, to_export_main.second);
last_exported_main[i] = to_export_main;
++exported_lines_count;
}
// export remaining time to next printer stop
auto it_stop = std::upper_bound(machine.stop_times.begin(), machine.stop_times.end(), it->elapsed_time,
[](float value, const TimeMachine::StopTime& t) { return value < t.elapsed_time; });
if (it_stop != machine.stop_times.end()) {
int to_export_stop = time_in_minutes(it_stop->elapsed_time - it->elapsed_time);
if (last_exported_stop[i] != to_export_stop) {
if (to_export_stop > 0) {
if (last_exported_stop[i] != to_export_stop) {
export_line += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
last_exported_stop[i] = to_export_stop;
++exported_lines_count;
}
}
else {
bool is_last = false;
auto next_it = it + 1;
is_last |= (next_it == machine.g1_times_cache.end());
if (next_it != machine.g1_times_cache.end()) {
auto next_it_stop = std::upper_bound(machine.stop_times.begin(), machine.stop_times.end(), next_it->elapsed_time,
[](float value, const TimeMachine::StopTime& t) { return value < t.elapsed_time; });
is_last |= (next_it_stop != it_stop);
std::string time_float_str = format_time_float(time_in_last_minute(it_stop->elapsed_time - it->elapsed_time));
std::string next_time_float_str = format_time_float(time_in_last_minute(it_stop->elapsed_time - next_it->elapsed_time));
is_last |= (string_to_double_decimal_point(time_float_str) > 0. && string_to_double_decimal_point(next_time_float_str) == 0.);
}
if (is_last) {
if (std::distance(machine.stop_times.begin(), it_stop) == static_cast<ptrdiff_t>(machine.stop_times.size() - 1))
export_line += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
else
export_line += format_line_M73_stop_float(machine.line_m73_stop_mask.c_str(), time_in_last_minute(it_stop->elapsed_time - it->elapsed_time));
last_exported_stop[i] = to_export_stop;
++exported_lines_count;
}
}
}
}
}
}
}
return exported_lines_count;
};
// helper function to write to disk
size_t out_file_pos = 0;
lines_ends.clear();
auto write_string = [&export_line, &out, &out_path, &out_file_pos, &lines_ends](const std::string& str) {
fwrite((const void*)export_line.c_str(), 1, export_line.length(), out.f);
if (ferror(out.f)) {
out.close();
boost::nowide::remove(out_path.c_str());
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nIs the disk full?\n"));
}
for (size_t i = 0; i < export_line.size(); ++ i)
if (export_line[i] == '\n')
lines_ends.emplace_back(out_file_pos + i + 1);
out_file_pos += export_line.size();
export_line.clear();
};
unsigned int line_id = 0;
std::vector<std::pair<unsigned int, unsigned int>> offsets;
{
// Read the input stream 64kB at a time, extract lines and process them.
std::vector<char> buffer(65536 * 10, 0);
// Line buffer.
assert(gcode_line.empty());
for (;;) {
size_t cnt_read = ::fread(buffer.data(), 1, buffer.size(), in.f);
if (::ferror(in.f))
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nError while reading from file.\n"));
bool eof = cnt_read == 0;
auto it = buffer.begin();
auto it_bufend = buffer.begin() + cnt_read;
while (it != it_bufend || (eof && ! gcode_line.empty())) {
// Find end of line.
bool eol = false;
auto it_end = it;
for (; it_end != it_bufend && ! (eol = *it_end == '\r' || *it_end == '\n'); ++ it_end) ;
// End of line is indicated also if end of file was reached.
eol |= eof && it_end == it_bufend;
gcode_line.insert(gcode_line.end(), it, it_end);
if (eol) {
++line_id;
// determine the end of line character and pass to output
gcode_line += *it_end;
if(*it_end == '\r' && *(++ it_end) == '\n')
gcode_line += '\n';
// replace placeholder lines
auto [processed, lines_added_count] = process_placeholders(gcode_line);
if (processed && lines_added_count != 0)
offsets.push_back({ line_id, lines_added_count });
if (!disable_m73 && !processed &&!is_temporary_decoration(gcode_line) &&
(GCodeReader::GCodeLine::cmd_is(gcode_line, "G1") ||
GCodeReader::GCodeLine::cmd_is(gcode_line, "G2") ||
GCodeReader::GCodeLine::cmd_is(gcode_line, "G3") ||
GCodeReader::GCodeLine::cmd_is(gcode_line, "G10")||
GCodeReader::GCodeLine::cmd_is(gcode_line, "G11"))) {
// remove temporary lines, add lines M73 where needed
unsigned int extra_lines_count = process_line_move(g1_lines_counter ++);
if (extra_lines_count > 0)
offsets.push_back({ line_id, extra_lines_count });
}
if (disable_m73 && !processed && GCodeReader::GCodeLine::cmd_is(gcode_line, "M73")) {
// Remove any existing M73 command
gcode_line = "";
offsets.push_back({line_id, -1});
}
export_line += gcode_line;
if (export_line.length() > 65535)
write_string(export_line);
gcode_line.clear();
}
// Skip EOL.
it = it_end;
if (it != it_bufend && *it == '\r')
++ it;
if (it != it_bufend && *it == '\n')
++ it;
}
if (eof)
break;
}
}
if (!export_line.empty())
write_string(export_line);
out.close();
in.close();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": after process %1%")%filename.c_str();
// 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;
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;
++curr_offset_id;
}
move.gcode_id += total_offset;
}
if (rename_file(out_path, filename)) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": Failed to rename the output G-code file from %1% to %2%")%out_path.c_str() % filename.c_str();
throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + out_path + " to " + filename + '\n' +
"Is " + out_path + " locked?" + '\n');
}
}
void GCodeProcessor::UsedFilaments::reset()
{
color_change_cache = 0.0f;
@ -1136,7 +783,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
DEFAULT_TRAVEL_ACCELERATION;
}
m_time_processor.disable_m73 = config.disable_m73;
m_disable_m73 = config.disable_m73;
const ConfigOptionFloat* initial_layer_print_height = config.option<ConfigOptionFloat>("initial_layer_print_height");
if (initial_layer_print_height != nullptr)
@ -1694,9 +1341,6 @@ void GCodeProcessor::finalize(bool post_process)
m_height_compare.output();
m_width_compare.output();
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
if (post_process){
m_time_processor.post_process(m_result.filename, m_result.moves, m_result.lines_ends, m_layer_id);
}
#if ENABLE_GCODE_VIEWER_STATISTICS
m_result.time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_start_time).count();
#endif // ENABLE_GCODE_VIEWER_STATISTICS
@ -4411,7 +4055,10 @@ void GCodeProcessor::run_post_process()
return time_in_seconds / 60.0f;
};
auto format_line_M73_main = [](const std::string& mask, int percent, int time) {
auto format_line_M73_main = [this](const std::string& mask, int percent, int time) {
if(this->m_disable_m73)
return std::string("");
char line_M73[64];
sprintf(line_M73, mask.c_str(),
std::to_string(percent).c_str(),
@ -4419,7 +4066,9 @@ void GCodeProcessor::run_post_process()
return std::string(line_M73);
};
auto format_line_M73_stop_int = [](const std::string& mask, int time) {
auto format_line_M73_stop_int = [this](const std::string& mask, int time) {
if (this->m_disable_m73)
return std::string("");
char line_M73[64];
sprintf(line_M73, mask.c_str(), std::to_string(time).c_str());
return std::string(line_M73);

View file

@ -495,15 +495,10 @@ class Print;
float filament_unload_times;
//Orca: time for tool change
float machine_tool_change_time;
bool disable_m73;
std::array<TimeMachine, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> machines;
void reset();
// post process the file with the given filename to add remaining time lines M73
// and updates moves' gcode ids accordingly
void post_process(const std::string& filename, std::vector<GCodeProcessorResult::MoveVertex>& moves, std::vector<size_t>& lines_ends, size_t total_layer_num);
};
struct UsedFilaments // filaments per ColorChange
@ -735,6 +730,7 @@ class Print;
bool m_single_extruder_multi_material;
float m_preheat_time;
int m_preheat_steps;
bool m_disable_m73;
#if ENABLE_GCODE_VIEWER_STATISTICS
std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time;
#endif // ENABLE_GCODE_VIEWER_STATISTICS