SPE-2219 - Added loading progress bar in GCodeViewer

This commit is contained in:
enricoturri1966 2024-06-18 17:03:47 +02:00 committed by Lukas Matena
parent 7db176ac84
commit 443879d798
5 changed files with 79 additions and 29 deletions

View File

@ -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<void()> cancel_callback)
void GCodeProcessor::process_file(const std::string& filename, GCodeReader::ProgressCallback progress_callback,
std::function<void(void)> 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<voi
fclose(file);
if (is_binary)
process_binary_file(filename, cancel_callback);
process_binary_file(filename, progress_callback, cancel_callback);
else
process_ascii_file(filename, cancel_callback);
process_ascii_file(filename, progress_callback, cancel_callback);
}
void GCodeProcessor::process_ascii_file(const std::string& filename, std::function<void()> cancel_callback)
void GCodeProcessor::process_ascii_file(const std::string& filename, GCodeReader::ProgressCallback progress_callback,
std::function<void(void)> 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<void()> cancel_callback)
void GCodeProcessor::process_binary_file(const std::string& filename, GCodeReader::ProgressCallback progress_callback,
std::function<void()> 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<std::byte> 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<std::string, std::string>& 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<size_t>& lines_ends = m_result.lines_ends.emplace_back(std::vector<size_t>());
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.

View File

@ -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<void()> cancel_callback = nullptr);
void process_file(const std::string& filename, GCodeReader::ProgressCallback progress_callback = nullptr,
std::function<void(void)> 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<void()> cancel_callback = nullptr);
void process_binary_file(const std::string& filename, std::function<void()> cancel_callback = nullptr);
void process_ascii_file(const std::string& filename, GCodeReader::ProgressCallback progress_callback = nullptr,
std::function<void(void)> cancel_callback = nullptr);
void process_binary_file(const std::string& filename, GCodeReader::ProgressCallback progress_callback = nullptr,
std::function<void(void)> cancel_callback = nullptr);
// Process tags embedded into comments
void process_tags(const std::string_view comment, bool producers_enabled);

View File

@ -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<char> 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<float>(file_pos) / static_cast<float>(file_size));
}
return true;
}

View File

@ -17,6 +17,8 @@ namespace Slic3r {
class GCodeReader {
public:
typedef std::function<void(float)> 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<typename ParseLineCallback, typename LineEndCallback>
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 */

View File

@ -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)
{