diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 1cdc9eef6..4d944f37b 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1288,7 +1288,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu break; } } - + m_processor.set_filaments(m_writer.extruders()); m_processor.finalize(true); // DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics); DoExport::update_print_estimated_stats(m_processor, m_writer.extruders(), print->m_print_statistics); @@ -1323,13 +1323,14 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu // free functions called by GCode::_do_export() namespace DoExport { - static void init_gcode_processor(const PrintConfig& config, GCodeProcessor& processor, bool& silent_time_estimator_enabled) + static void init_gcode_processor(const PrintConfig& config, GCodeProcessor& processor, bool& silent_time_estimator_enabled,const std::vector& filaments) { silent_time_estimator_enabled = (config.gcode_flavor == gcfMarlinLegacy || config.gcode_flavor == gcfMarlinFirmware) && config.silent_mode; processor.reset(); processor.apply_config(config); processor.enable_stealth_time_estimator(silent_time_estimator_enabled); + processor.set_filaments(filaments); } #if 0 @@ -1600,7 +1601,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato PROFILE_FUNC(); // modifies m_silent_time_estimator_enabled - DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled); + DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled, m_writer.extruders()); // resets analyzer's tracking data m_last_height = 0.f; m_last_layer_z = 0.f; @@ -1680,6 +1681,14 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str()); //BBS: total layer number file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Total_Layer_Number_Placeholder).c_str()); + + //BBS: total filament used in mm + file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Used_Filament_Length_Placeholder).c_str()); + //BBS: total filament used in cm3 + file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Used_Filament_Volume_Placeholder).c_str()); + //BBS: total filament used in g + file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Used_Filament_Weight_Placeholder).c_str()); + //BBS: judge whether support skipping, if yes, list all label_object_id with sorted order here if (print.num_object_instances() <= g_max_label_object && //Don't support too many objects on one plate (print.num_object_instances() > 1) && //Don't support skipping single object @@ -2373,11 +2382,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato m_writer.extruders(), // Modifies print.m_print_statistics)); - //file.write("\n"); - //file.write_format("; total filament weight [g] = %.2lf\n", print.m_print_statistics.total_weight); - //file.write_format("; total filament cost = %.2lf\n", print.m_print_statistics.total_cost); - //if (print.m_print_statistics.total_toolchanges > 0) - // file.write_format("; total filament change = %i\n", print.m_print_statistics.total_toolchanges); bool activate_air_filtration = false; for (const auto& extruder : m_writer.extruders()) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 342d81836..add988b12 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -61,7 +61,10 @@ const std::vector GCodeProcessor::Reserved_Tags = { "_GP_ESTIMATED_PRINTING_TIME_PLACEHOLDER", "_GP_TOTAL_LAYER_NUMBER_PLACEHOLDER", " WIPE_TOWER_START", - " WIPE_TOWER_END" + " WIPE_TOWER_END", + "_GP_FILAMENT_USED_WEIGHT_PLACEHOLDER", + "_GP_FILAMENT_USED_VOLUME_PLACEHOLDER", + "_GP_FILAMENT_USED_LENGTH_PLACEHOLDER" }; const std::string GCodeProcessor::Flush_Start_Tag = " FLUSH_START"; @@ -370,7 +373,7 @@ void GCodeProcessor::TimeProcessor::reset() machines[static_cast(PrintEstimatedStatistics::ETimeMode::Normal)].enabled = true; } -void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector& moves, std::vector& lines_ends, size_t total_layer_num) +void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector& moves, std::vector& lines_ends, const TimeProcessContext& context) { FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") }; if (in.f == nullptr) @@ -448,6 +451,20 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st auto process_placeholders = [&](std::string& gcode_line) { unsigned int extra_lines_count = 0; + auto format_filament_used_info = [](const std::string& info, std::mapval_per_extruder) { + auto double_to_fmt_string = [](double num) -> std::string { + char buf[20]; + sprintf(buf, "%.2f", num); + return std::string(buf); + }; + std::string buf = "; " + info + " : "; + size_t idx = 0; + for (auto item : val_per_extruder) + buf += (idx++ == 0 ? double_to_fmt_string(item.second) : "," + double_to_fmt_string(item.second)); + buf += '\n'; + return buf; + }; + // remove trailing '\n' auto line = std::string_view(gcode_line).substr(0, gcode_line.length() - 1); @@ -485,11 +502,12 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st sprintf(buf, "; estimated printing time (normal mode) = %s\n", get_time_dhms(machine.time).c_str()); ret += buf; - } else { + } + else { // BBS estimator 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()); + get_time_dhms(machine.time - machine.prepare_time).c_str(), + get_time_dhms(machine.time).c_str()); ret += buf; } } @@ -498,9 +516,48 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st //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); + sprintf(buf, "; total layer number: %zd\n", context.total_layer_num); ret += buf; } + else if (line == reserved_tag(ETags::Used_Filament_Weight_Placeholder)) { + std::maptotal_weight_per_extruder; + for (const auto& pair : context.used_filaments.total_volumes_per_extruder) { + auto filament_id = pair.first; + auto volume = pair.second; + auto iter = std::find_if(context.filament_lists.begin(), context.filament_lists.end(), [filament_id](const Extruder& filament) { return filament.id() == filament_id; }); + if (iter == context.filament_lists.end()) + continue; + double weight = volume * iter->filament_density() * 0.001; + total_weight_per_extruder[filament_id] += weight; + } + + ret += format_filament_used_info("total filament weight [g]", total_weight_per_extruder); + } + else if (line == reserved_tag(ETags::Used_Filament_Volume_Placeholder)) { + std::maptotal_volume_per_extruder; + for (const auto& pair : context.used_filaments.total_volumes_per_extruder) { + auto filament_id = pair.first; + auto volume = pair.second; + auto iter = std::find_if(context.filament_lists.begin(), context.filament_lists.end(), [filament_id](const Extruder& filament) { return filament.id() == filament_id; }); + if (iter == context.filament_lists.end()) + continue; + total_volume_per_extruder[filament_id] += volume; + } + ret += format_filament_used_info("total filament volume [cm^3]", total_volume_per_extruder); + } + else if (line == reserved_tag(ETags::Used_Filament_Length_Placeholder)) { + std::maptotal_length_per_extruder; + for (const auto& pair : context.used_filaments.total_volumes_per_extruder) { + auto filament_id = pair.first; + auto volume = pair.second; + auto iter = std::find_if(context.filament_lists.begin(), context.filament_lists.end(), [filament_id](const Extruder& filament) { return filament.id() == filament_id; }); + if (iter == context.filament_lists.end()) + continue; + double length = volume / (PI * sqr(0.5 * iter->filament_diameter())); + total_length_per_extruder[filament_id] += length; + } + ret += format_filament_used_info("total filament length [mm]", total_length_per_extruder); + } } if (! ret.empty()) @@ -1578,7 +1635,8 @@ void GCodeProcessor::finalize(bool post_process) 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); + TimeProcessContext context(m_layer_id,m_filament_lists,m_used_filaments); + m_time_processor.post_process(m_result.filename, m_result.moves, m_result.lines_ends, context); } #if ENABLE_GCODE_VIEWER_STATISTICS m_result.time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - m_start_time).count(); diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 67b702d45..88ac622c0 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -6,6 +6,7 @@ #include "libslic3r/ExtrusionEntity.hpp" #include "libslic3r/PrintConfig.hpp" #include "libslic3r/CustomGCode.hpp" +#include "libslic3r/Extruder.hpp" #include #include @@ -285,6 +286,9 @@ namespace Slic3r { Total_Layer_Number_Placeholder, Wipe_Tower_Start, Wipe_Tower_End, + Used_Filament_Weight_Placeholder, + Used_Filament_Volume_Placeholder, + Used_Filament_Length_Placeholder }; static const std::string& reserved_tag(ETags tag) { return Reserved_Tags[static_cast(tag)]; } @@ -460,38 +464,6 @@ namespace Slic3r { void calculate_time(size_t keep_last_n_blocks = 0, float additional_time = 0.0f); }; - struct TimeProcessor - { - struct Planner - { - // Size of the firmware planner queue. The old 8-bit Marlins usually just managed 16 trapezoidal blocks. - // Let's be conservative and plan for newer boards with more memory. - static constexpr size_t queue_size = 64; - // The firmware recalculates last planner_queue_size trapezoidal blocks each time a new block is added. - // We are not simulating the firmware exactly, we calculate a sequence of blocks once a reasonable number of blocks accumulate. - static constexpr size_t refresh_threshold = queue_size * 4; - }; - - // extruder_id is currently used to correctly calculate filament load / unload times into the total print time. - // This is currently only really used by the MK3 MMU2: - // extruder_unloaded = true means no filament is loaded yet, all the filaments are parked in the MK3 MMU2 unit. - bool extruder_unloaded; - // allow to skip the lines M201/M203/M204/M205 generated by GCode::print_machine_envelope() for non-Normal time estimate mode - bool machine_envelope_processing_enabled; - MachineEnvelopeConfig machine_limits; - // Additional load / unload times for a filament exchange sequence. - float filament_load_times; - float filament_unload_times; - - std::array(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& moves, std::vector& lines_ends, size_t total_layer_num); - }; - struct UsedFilaments // filaments per ColorChange { double color_change_cache; @@ -534,6 +506,48 @@ namespace Slic3r { friend class GCodeProcessor; }; + struct TimeProcessContext + { + size_t total_layer_num; + std::vector filament_lists; + UsedFilaments used_filaments; + TimeProcessContext( size_t total_layer_num_, + const std::vector& filament_lists_, + const UsedFilaments& used_filaments_) + :total_layer_num(total_layer_num_), filament_lists(filament_lists_), used_filaments(used_filaments_) {} + }; + + struct TimeProcessor + { + struct Planner + { + // Size of the firmware planner queue. The old 8-bit Marlins usually just managed 16 trapezoidal blocks. + // Let's be conservative and plan for newer boards with more memory. + static constexpr size_t queue_size = 64; + // The firmware recalculates last planner_queue_size trapezoidal blocks each time a new block is added. + // We are not simulating the firmware exactly, we calculate a sequence of blocks once a reasonable number of blocks accumulate. + static constexpr size_t refresh_threshold = queue_size * 4; + }; + + // extruder_id is currently used to correctly calculate filament load / unload times into the total print time. + // This is currently only really used by the MK3 MMU2: + // extruder_unloaded = true means no filament is loaded yet, all the filaments are parked in the MK3 MMU2 unit. + bool extruder_unloaded; + // allow to skip the lines M201/M203/M204/M205 generated by GCode::print_machine_envelope() for non-Normal time estimate mode + bool machine_envelope_processing_enabled; + MachineEnvelopeConfig machine_limits; + // Additional load / unload times for a filament exchange sequence. + float filament_load_times; + float filament_unload_times; + + std::array(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& moves, std::vector& lines_ends, const TimeProcessContext& context); + }; public: class SeamsDetector { @@ -677,6 +691,7 @@ namespace Slic3r { bool m_flushing; bool m_wipe_tower; float m_remaining_volume; + std::vector m_filament_lists; //BBS: x, y offset for gcode generated double m_x_offset{ 0 }; @@ -750,6 +765,9 @@ namespace Slic3r { GCodeProcessor(); void apply_config(const PrintConfig& config); + + void set_filaments(const std::vector&filament_lists) { m_filament_lists=filament_lists;} + void enable_stealth_time_estimator(bool enabled); bool is_stealth_time_estimator_enabled() const { return m_time_processor.machines[static_cast(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled;