From 443879d798ec4bc4067b2082c52653d41212194b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 18 Jun 2024 17:03:47 +0200 Subject: [PATCH] SPE-2219 - Added loading progress bar in GCodeViewer --- src/libslic3r/GCode/GCodeProcessor.cpp | 80 ++++++++++++++++++-------- src/libslic3r/GCode/GCodeProcessor.hpp | 9 ++- src/libslic3r/GCodeReader.cpp | 7 +++ src/libslic3r/GCodeReader.hpp | 6 ++ src/slic3r/GUI/Plater.cpp | 6 +- 5 files changed, 79 insertions(+), 29 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index e7ef65e83b..2e86a65ffc 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1083,7 +1083,8 @@ static inline const char* remove_eols(const char *begin, const char *end) { // Load a G-code into a stand-alone G-code viewer. // throws CanceledException through print->throw_if_canceled() (sent by the caller as callback). -void GCodeProcessor::process_file(const std::string& filename, std::function cancel_callback) +void GCodeProcessor::process_file(const std::string& filename, GCodeReader::ProgressCallback progress_callback, + std::function cancel_callback) { FILE* file = boost::nowide::fopen(filename.c_str(), "rb"); if (file == nullptr) @@ -1095,12 +1096,13 @@ void GCodeProcessor::process_file(const std::string& filename, std::function cancel_callback) +void GCodeProcessor::process_ascii_file(const std::string& filename, GCodeReader::ProgressCallback progress_callback, + std::function cancel_callback) { CNumericLocalesSetter locales_setter; @@ -1151,6 +1153,7 @@ void GCodeProcessor::process_ascii_file(const std::string& filename, std::functi m_result.id = ++s_result_id; initialize_result_moves(); size_t parse_line_callback_cntr = 10000; + m_parser.set_progress_callback(progress_callback); m_parser.parse_file(filename, [this, cancel_callback, &parse_line_callback_cntr](GCodeReader& reader, const GCodeReader::GCodeLine& line) { if (-- parse_line_callback_cntr == 0) { // Don't call the cancel_callback() too often, do it every at every 10000'th line. @@ -1175,7 +1178,8 @@ static void update_lines_ends_and_out_file_pos(const std::string& out_string, st *out_file_pos += out_string.size(); } -void GCodeProcessor::process_binary_file(const std::string& filename, std::function cancel_callback) +void GCodeProcessor::process_binary_file(const std::string& filename, GCodeReader::ProgressCallback progress_callback, + std::function cancel_callback) { FilePtr file{ boost::nowide::fopen(filename.c_str(), "rb") }; if (file.f == nullptr) @@ -1185,29 +1189,44 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct const long file_size = ftell(file.f); rewind(file.f); + auto update_progress = [progress_callback, file_size, &file]() { + const long pos = ftell(file.f); + if (progress_callback != nullptr) + progress_callback(float(pos) / float(file_size)); + }; + + auto throw_error = [progress_callback](const std::string& msg) { + if (progress_callback != nullptr) + progress_callback(1.0f); + throw Slic3r::RuntimeError(msg.c_str()); + }; + // read file header using namespace bgcode::core; using namespace bgcode::binarize; FileHeader file_header; EResult res = read_header(*file.f, file_header, nullptr); + update_progress(); if (res != EResult::Success) - throw Slic3r::RuntimeError(format("File %1% does not contain a valid binary gcode\nError: %2%", filename, + throw_error(format("File %1% does not contain a valid binary gcode\nError: %2%", filename, std::string(translate_result(res)))); // read file metadata block, if present BlockHeader block_header; std::vector cs_buffer(65536); res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + update_progress(); if (res != EResult::Success) - throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); - if ((EBlockType)block_header.type != EBlockType::FileMetadata && + throw_error(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + if ((EBlockType)block_header.type != EBlockType::FileMetadata && (EBlockType)block_header.type != EBlockType::PrinterMetadata) - throw Slic3r::RuntimeError(format("Unable to find file metadata block in file %1%", filename)); + throw_error(format("Unable to find file metadata block in file %1%", filename)); if ((EBlockType)block_header.type == EBlockType::FileMetadata) { FileMetadataBlock file_metadata_block; res = file_metadata_block.read_data(*file.f, file_header, block_header); + update_progress(); if (res != EResult::Success) - throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + throw_error(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); auto producer_it = std::find_if(file_metadata_block.raw_data.begin(), file_metadata_block.raw_data.end(), [](const std::pair& item) { return item.first == "Producer"; }); if (producer_it != file_metadata_block.raw_data.end() && boost::starts_with(producer_it->second, std::string(SLIC3R_APP_NAME))) @@ -1215,8 +1234,9 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct else m_producer = EProducer::Unknown; res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + update_progress(); if (res != EResult::Success) - throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + throw_error(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); } else { m_producer = EProducer::Unknown; @@ -1224,45 +1244,52 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct // read printer metadata block if ((EBlockType)block_header.type != EBlockType::PrinterMetadata) - throw Slic3r::RuntimeError(format("Unable to find printer metadata block in file %1%", filename)); + throw_error(format("Unable to find printer metadata block in file %1%", filename)); PrinterMetadataBlock printer_metadata_block; res = printer_metadata_block.read_data(*file.f, file_header, block_header); + update_progress(); if (res != EResult::Success) - throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + throw_error(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); // read thumbnail blocks res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + update_progress(); if (res != EResult::Success) - throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + throw_error(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); while ((EBlockType)block_header.type == EBlockType::Thumbnail) { ThumbnailBlock thumbnail_block; res = thumbnail_block.read_data(*file.f, file_header, block_header); + update_progress(); if (res != EResult::Success) - throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + throw_error(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + update_progress(); if (res != EResult::Success) - throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + throw_error(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); } // read print metadata block if ((EBlockType)block_header.type != EBlockType::PrintMetadata) - throw Slic3r::RuntimeError(format("Unable to find print metadata block in file %1%", filename)); + throw_error(format("Unable to find print metadata block in file %1%", filename)); PrintMetadataBlock print_metadata_block; res = print_metadata_block.read_data(*file.f, file_header, block_header); + update_progress(); if (res != EResult::Success) - throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + throw_error(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); // read slicer metadata block res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + update_progress(); if (res != EResult::Success) - throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + throw_error(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); if ((EBlockType)block_header.type != EBlockType::SlicerMetadata) - throw Slic3r::RuntimeError(format("Unable to find slicer metadata block in file %1%", filename)); + throw_error(format("Unable to find slicer metadata block in file %1%", filename)); SlicerMetadataBlock slicer_metadata_block; res = slicer_metadata_block.read_data(*file.f, file_header, block_header); + update_progress(); if (res != EResult::Success) - throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + throw_error(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); DynamicPrintConfig config; config.apply(FullPrintConfig::defaults()); std::string str; @@ -1282,15 +1309,17 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct // read gcodes block res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + update_progress(); if (res != EResult::Success) - throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + throw_error(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); if ((EBlockType)block_header.type != EBlockType::GCode) - throw Slic3r::RuntimeError(format("Unable to find gcode block in file %1%", filename)); + throw_error(format("Unable to find gcode block in file %1%", filename)); while ((EBlockType)block_header.type == EBlockType::GCode) { GCodeBlock block; res = block.read_data(*file.f, file_header, block_header); + update_progress(); if (res != EResult::Success) - throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + throw_error(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); std::vector& lines_ends = m_result.lines_ends.emplace_back(std::vector()); update_lines_ends_and_out_file_pos(block.raw_data, lines_ends, nullptr); @@ -1303,8 +1332,9 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct break; res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + update_progress(); if (res != EResult::Success) - throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + throw_error(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); } // Don't post-process the G-code to update time stamps. diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 19bee0b29f..6048144e96 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -587,7 +587,8 @@ namespace Slic3r { // Load a G-code into a stand-alone G-code viewer. // throws CanceledException through print->throw_if_canceled() (sent by the caller as callback). - void process_file(const std::string& filename, std::function cancel_callback = nullptr); + void process_file(const std::string& filename, GCodeReader::ProgressCallback progress_callback = nullptr, + std::function cancel_callback = nullptr); // Streaming interface, for processing G-codes just generated by PrusaSlicer in a pipelined fashion. void initialize(const std::string& filename); @@ -612,8 +613,10 @@ namespace Slic3r { void apply_config_kissslicer(const std::string& filename); void process_gcode_line(const GCodeReader::GCodeLine& line, bool producers_enabled); - void process_ascii_file(const std::string& filename, std::function cancel_callback = nullptr); - void process_binary_file(const std::string& filename, std::function cancel_callback = nullptr); + void process_ascii_file(const std::string& filename, GCodeReader::ProgressCallback progress_callback = nullptr, + std::function cancel_callback = nullptr); + void process_binary_file(const std::string& filename, GCodeReader::ProgressCallback progress_callback = nullptr, + std::function cancel_callback = nullptr); // Process tags embedded into comments void process_tags(const std::string_view comment, bool producers_enabled); diff --git a/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp index 9cee82d048..1745b678da 100644 --- a/src/libslic3r/GCodeReader.cpp +++ b/src/libslic3r/GCodeReader.cpp @@ -131,6 +131,10 @@ bool GCodeReader::parse_file_raw_internal(const std::string &filename, ParseLine { FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") }; + fseek(in.f, 0, SEEK_END); + const long file_size = ftell(in.f); + rewind(in.f); + // Read the input stream 64kB at a time, extract lines and process them. std::vector buffer(65536 * 10, 0); // Line buffer. @@ -141,6 +145,7 @@ bool GCodeReader::parse_file_raw_internal(const std::string &filename, ParseLine size_t cnt_read = ::fread(buffer.data(), 1, buffer.size(), in.f); if (::ferror(in.f)) return false; + bool eof = cnt_read == 0; auto it = buffer.begin(); auto it_bufend = buffer.begin() + cnt_read; @@ -178,6 +183,8 @@ bool GCodeReader::parse_file_raw_internal(const std::string &filename, ParseLine if (eof) break; file_pos += cnt_read; + if (m_progress_callback != nullptr) + m_progress_callback(static_cast(file_pos) / static_cast(file_size)); } return true; } diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp index 38e1a91d82..957aa92694 100644 --- a/src/libslic3r/GCodeReader.hpp +++ b/src/libslic3r/GCodeReader.hpp @@ -17,6 +17,8 @@ namespace Slic3r { class GCodeReader { public: + typedef std::function ProgressCallback; + class GCodeLine { public: GCodeLine() { reset(); } @@ -164,6 +166,8 @@ public: char extrusion_axis() const { return m_extrusion_axis; } // void set_extrusion_axis(char axis) { m_extrusion_axis = axis; } + void set_progress_callback(ProgressCallback cb) { m_progress_callback = cb; } + private: template bool parse_file_raw_internal(const std::string &filename, ParseLineCallback parse_line_callback, LineEndCallback line_end_callback); @@ -195,6 +199,8 @@ private: bool m_verbose; // To be set by the callback to stop parsing. bool m_parsing{ false }; + + ProgressCallback m_progress_callback{ nullptr }; }; } /* namespace Slic3r */ diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b7870b3896..81e06b8dcf 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4144,7 +4144,11 @@ void Plater::load_gcode(const wxString& filename) GCodeProcessor processor; try { - processor.process_file(filename.ToUTF8().data()); + p->notification_manager->push_download_progress_notification("Loading...", []() { return false; }); + processor.process_file(filename.ToUTF8().data(), [this](float value) { + p->notification_manager->set_download_progress_percentage(value); + p->get_current_canvas3D()->render(); + }); } catch (const std::exception& ex) {