diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 6f148cc695..f6b772e946 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -997,94 +997,107 @@ namespace DoExport { { std::string filament_stats_string_out; - print_statistics.clear(); + print_statistics.clear(); print_statistics.total_toolchanges = std::max(0, wipe_tower_data.number_of_toolchanges); print_statistics.initial_extruder_id = initial_extruder_id; std::vector filament_types; - if (! extruders.empty()) { - std::pair out_filament_used_mm ("; filament used [mm] = ", 0); - std::pair out_filament_used_cm3("; filament used [cm3] = ", 0); - std::pair out_filament_used_g ("; filament used [g] = ", 0); - std::pair out_filament_cost ("; filament cost = ", 0); - for (const Extruder &extruder : extruders) { - print_statistics.printing_extruders.emplace_back(extruder.id()); - filament_types.emplace_back(config.filament_type.get_at(extruder.id())); + if (! extruders.empty()) { +#if ENABLE_BINARIZED_GCODE + std::pair out_filament_used_mm(PrintStatistics::FilamentUsedMmMask + " ", 0); + std::pair out_filament_used_cm3(PrintStatistics::FilamentUsedCm3Mask + " ", 0); + std::pair out_filament_used_g(PrintStatistics::FilamentUsedGMask + " ", 0); + std::pair out_filament_cost(PrintStatistics::FilamentCostMask + " ", 0); +#else + std::pair out_filament_used_mm("; filament used [mm] = ", 0); + std::pair out_filament_used_cm3("; filament used [cm3] = ", 0); + std::pair out_filament_used_g ("; filament used [g] = ", 0); + std::pair out_filament_cost("; filament cost = ", 0); +#endif // ENABLE_BINARIZED_GCODE + for (const Extruder &extruder : extruders) { + print_statistics.printing_extruders.emplace_back(extruder.id()); + filament_types.emplace_back(config.filament_type.get_at(extruder.id())); - double used_filament = extruder.used_filament() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] : 0.f); - double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter - double filament_weight = extruded_volume * extruder.filament_density() * 0.001; - double filament_cost = filament_weight * extruder.filament_cost() * 0.001; - auto append = [&extruder](std::pair &dst, const char *tmpl, double value) { - assert(is_decimal_separator_point()); - while (dst.second < extruder.id()) { - // Fill in the non-printing extruders with zeros. - dst.first += (dst.second > 0) ? ", 0" : "0"; - ++ dst.second; - } - if (dst.second > 0) - dst.first += ", "; - char buf[64]; - sprintf(buf, tmpl, value); - dst.first += buf; - ++ dst.second; - }; + double used_filament = extruder.used_filament() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] : 0.f); + double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter + double filament_weight = extruded_volume * extruder.filament_density() * 0.001; + double filament_cost = filament_weight * extruder.filament_cost() * 0.001; + auto append = [&extruder](std::pair &dst, const char *tmpl, double value) { + assert(is_decimal_separator_point()); + while (dst.second < extruder.id()) { + // Fill in the non-printing extruders with zeros. + dst.first += (dst.second > 0) ? ", 0" : "0"; + ++ dst.second; + } + if (dst.second > 0) + dst.first += ", "; + char buf[64]; + sprintf(buf, tmpl, value); + dst.first += buf; + ++ dst.second; + }; #if ENABLE_BINARIZED_GCODE - if (export_binary_data) { - char buf[128]; - sprintf(buf, "%.2lf", used_filament); - binary_data.print_metadata.raw_data.push_back({ "filament used [mm]", std::string(buf) }); - sprintf(buf, "%.2lf", extruded_volume * 0.001); - binary_data.print_metadata.raw_data.push_back({ "filament used [cm3]", std::string(buf) }); - } - else { + if (export_binary_data) { + char buf[128]; + sprintf(buf, "%.2lf", used_filament); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedMm, std::string(buf) }); + sprintf(buf, "%.2lf", extruded_volume * 0.001); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedCm3, std::string(buf) }); + } + else { #endif // ENABLE_BINARIZED_GCODE - append(out_filament_used_mm, "%.2lf", used_filament); - append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001); + append(out_filament_used_mm, "%.2lf", used_filament); + append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001); #if ENABLE_BINARIZED_GCODE - } + } #endif // ENABLE_BINARIZED_GCODE - if (filament_weight > 0.) { - print_statistics.total_weight = print_statistics.total_weight + filament_weight; + if (filament_weight > 0.) { + print_statistics.total_weight = print_statistics.total_weight + filament_weight; #if ENABLE_BINARIZED_GCODE - if (export_binary_data) { - char buf[128]; - sprintf(buf, "%.2lf", filament_weight); - binary_data.print_metadata.raw_data.push_back({ "filament used [g]", std::string(buf) }); - } - else + if (export_binary_data) { + char buf[128]; + sprintf(buf, "%.2lf", filament_weight); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedG, std::string(buf) }); + } + else #endif // ENABLE_BINARIZED_GCODE - append(out_filament_used_g, "%.2lf", filament_weight); - if (filament_cost > 0.) { - print_statistics.total_cost = print_statistics.total_cost + filament_cost; + append(out_filament_used_g, "%.2lf", filament_weight); + if (filament_cost > 0.) { + print_statistics.total_cost = print_statistics.total_cost + filament_cost; #if ENABLE_BINARIZED_GCODE - if (export_binary_data) { - char buf[128]; - sprintf(buf, "%.2lf", filament_cost); - binary_data.print_metadata.raw_data.push_back({ "filament cost", std::string(buf) }); - } - else + if (export_binary_data) { + char buf[128]; + sprintf(buf, "%.2lf", filament_cost); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentCost, std::string(buf) }); + } + else #endif // ENABLE_BINARIZED_GCODE - append(out_filament_cost, "%.2lf", filament_cost); - } - } - print_statistics.total_used_filament += used_filament; - print_statistics.total_extruded_volume += extruded_volume; - print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.; - print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.; - } - filament_stats_string_out += out_filament_used_mm.first; - filament_stats_string_out += "\n" + out_filament_used_cm3.first; - if (out_filament_used_g.second) - filament_stats_string_out += "\n" + out_filament_used_g.first; - if (out_filament_cost.second) - filament_stats_string_out += "\n" + out_filament_cost.first; - print_statistics.initial_filament_type = config.filament_type.get_at(initial_extruder_id); - std::sort(filament_types.begin(), filament_types.end()); - print_statistics.printing_filament_types = filament_types.front(); - for (size_t i = 1; i < filament_types.size(); ++ i) { - print_statistics.printing_filament_types += ","; - print_statistics.printing_filament_types += filament_types[i]; - } + append(out_filament_cost, "%.2lf", filament_cost); + } + } + print_statistics.total_used_filament += used_filament; + print_statistics.total_extruded_volume += extruded_volume; + print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.; + print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.; + } +#if ENABLE_BINARIZED_GCODE + if (!export_binary_data) { +#endif // ENABLE_BINARIZED_GCODE + filament_stats_string_out += out_filament_used_mm.first; + filament_stats_string_out += "\n" + out_filament_used_cm3.first; + if (out_filament_used_g.second) + filament_stats_string_out += "\n" + out_filament_used_g.first; + if (out_filament_cost.second) + filament_stats_string_out += "\n" + out_filament_cost.first; +#if ENABLE_BINARIZED_GCODE + } +#endif // ENABLE_BINARIZED_GCODE + print_statistics.initial_filament_type = config.filament_type.get_at(initial_extruder_id); + std::sort(filament_types.begin(), filament_types.end()); + print_statistics.printing_filament_types = filament_types.front(); + for (size_t i = 1; i < filament_types.size(); ++ i) { + print_statistics.printing_filament_types += ","; + print_statistics.printing_filament_types += filament_types[i]; + } } return filament_stats_string_out; } @@ -1216,8 +1229,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato this->m_avoid_crossing_curled_overhangs.init_bed_shape(get_bed_shape(print.config())); } - // Write information on the generator. - file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str()); +#if ENABLE_BINARIZED_GCODE + if (!export_to_binary_gcode) +#endif // ENABLE_BINARIZED_GCODE + // Write information on the generator. + file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str()); #if ENABLE_BINARIZED_GCODE // if exporting gcode in ascii format, generate the thumbnails here @@ -1582,7 +1598,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Get filament stats. #if ENABLE_BINARIZED_GCODE - file.write(DoExport::update_print_stats_and_format_filament_stats( + const std::string filament_stats_string_out = DoExport::update_print_stats_and_format_filament_stats( // Const inputs has_wipe_tower, print.wipe_tower_data(), this->config(), @@ -1592,15 +1608,18 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato print.m_print_statistics, export_to_binary_gcode, m_processor.get_binary_data() - )); + ); + + if (!export_to_binary_gcode) + file.write(filament_stats_string_out); if (export_to_binary_gcode) { BinaryGCode::BinaryData& binary_data = m_processor.get_binary_data(); char buf[128]; sprintf(buf, "%.2lf", print.m_print_statistics.total_weight); - binary_data.print_metadata.raw_data.push_back({ "total filament used [g]", std::string(buf) }); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::TotalFilamentUsedG, std::string(buf) }); sprintf(buf, "%.2lf", print.m_print_statistics.total_cost); - binary_data.print_metadata.raw_data.push_back({ "total filament cost", std::string(buf) }); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::TotalFilamentCost, std::string(buf) }); if (print.m_print_statistics.total_toolchanges > 0) binary_data.print_metadata.raw_data.push_back({ "total toolchanges", std::to_string(print.m_print_statistics.total_toolchanges) }); } @@ -1619,8 +1638,13 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // if exporting gcode in ascii format, statistics export is done here #endif // ENABLE_BINARIZED_GCODE file.write("\n"); +#if ENABLE_BINARIZED_GCODE + file.write_format(PrintStatistics::TotalFilamentUsedGValueMask.c_str(), print.m_print_statistics.total_weight); + file.write_format(PrintStatistics::TotalFilamentCostValueMask.c_str(), print.m_print_statistics.total_cost); +#else file.write_format("; total filament used [g] = %.2lf\n", print.m_print_statistics.total_weight); file.write_format("; total filament cost = %.2lf\n", print.m_print_statistics.total_cost); +#endif // ENABLE_BINARIZED_GCODE if (print.m_print_statistics.total_toolchanges > 0) file.write_format("; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges); file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str()); diff --git a/src/libslic3r/GCode/GCodeBinarizer.cpp b/src/libslic3r/GCode/GCodeBinarizer.cpp index 9a205f5327..ddb1816368 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.cpp +++ b/src/libslic3r/GCode/GCodeBinarizer.cpp @@ -12,6 +12,7 @@ namespace BinaryGCode { static size_t g_checksum_max_cache_size = 65536; +static const size_t MAX_GCODE_CACHE_SIZE = 65536; std::string translate_result(BinaryGCode::EResult result) { @@ -26,10 +27,13 @@ std::string translate_result(BinaryGCode::EResult result) case BinaryGCode::EResult::InvalidBlockType: { return "Invalid block type"; } case BinaryGCode::EResult::InvalidCompressionType: { return "Invalid compression type"; } case BinaryGCode::EResult::InvalidMetadataEncodingType: { return "Invalid metadata encoding type"; } + case BinaryGCode::EResult::InvalidGCodeEncodingType: { return "Invalid gcode encoding type"; } case BinaryGCode::EResult::DataCompressionError: { return "Data compression error"; } case BinaryGCode::EResult::DataUncompressionError: { return "Data uncompression error"; } case BinaryGCode::EResult::MetadataEncodingError: { return "Data encoding error"; } case BinaryGCode::EResult::MetadataDecodingError: { return "Data decoding error"; } + case BinaryGCode::EResult::GCodeEncodingError: { return "GCode encoding error"; } + case BinaryGCode::EResult::GCodeDecodingError: { return "GCode decoding error"; } case BinaryGCode::EResult::BlockNotFound: { return "Block not found"; } case BinaryGCode::EResult::InvalidChecksum: { return "Invalid checksum"; } case BinaryGCode::EResult::InvalidThumbnailFormat: { return "Invalid thumbnail format"; } @@ -48,6 +52,7 @@ static uint16_t block_types_count() { return 1 + (uint16_t)EBlockTyp static uint16_t compression_types_count() { return 1 + (uint16_t)ECompressionType::None; } static uint16_t thumbnail_formats_count() { return 1 + (uint16_t)EThumbnailFormat::QOI; } static uint16_t metadata_encoding_types_count() { return 1 + (uint16_t)EMetadataEncodingType::INI; } +static uint16_t gcode_encoding_types_count() { return 1 + (uint16_t)EGCodeEncodingType::MeatPack; } static bool write_to_file(FILE& file, const void* data, size_t data_size) { @@ -61,43 +66,60 @@ static bool read_from_file(FILE& file, void* data, size_t data_size) return !ferror(&file); } -static bool encode_metadata(const std::vector>& data_in, std::vector& data_out, +static bool encode_metadata(const std::vector>& src, std::vector& dst, EMetadataEncodingType encoding_type) { - for (const auto& [key, value] : data_in) { + for (const auto& [key, value] : src) { switch (encoding_type) { case EMetadataEncodingType::INI: { - data_out.insert(data_out.end(), key.begin(), key.end()); - data_out.emplace_back('='); - data_out.insert(data_out.end(), value.begin(), value.end()); - data_out.emplace_back('\n'); + dst.insert(dst.end(), key.begin(), key.end()); + dst.emplace_back('='); + dst.insert(dst.end(), value.begin(), value.end()); + dst.emplace_back('\n'); break; } } } - return true; } -static bool decode_metadata(const std::vector& data_in, std::vector>& data_out, +static bool encode_gcode(const std::string& src, std::vector& dst, EGCodeEncodingType encoding_type) +{ + switch (encoding_type) + { + case EGCodeEncodingType::None: + { + dst.insert(dst.end(), src.begin(), src.end()); + break; + } + case EGCodeEncodingType::MeatPack: + { + // TODO + break; + } + } + return true; +} + +static bool decode_metadata(const std::vector& src, std::vector>& dst, EMetadataEncodingType encoding_type) { switch (encoding_type) { case EMetadataEncodingType::INI: { - auto start_it = data_in.begin(); - auto end_it = data_in.begin(); - while (end_it != data_in.end()) { - while (end_it != data_in.end() && *end_it != '\n') { + auto start_it = src.begin(); + auto end_it = src.begin(); + while (end_it != src.end()) { + while (end_it != src.end() && *end_it != '\n') { ++end_it; } const std::string item(start_it, end_it); const size_t pos = item.find_first_of('='); if (pos != std::string::npos) { - data_out.emplace_back(std::make_pair(item.substr(0, pos), item.substr(pos + 1))); + dst.emplace_back(std::make_pair(item.substr(0, pos), item.substr(pos + 1))); start_it = ++end_it; } } @@ -108,12 +130,30 @@ static bool decode_metadata(const std::vector& data_in, std::vector& data_in, std::vector& data_out, ECompressionType compression_type) +static bool decode_gcode(const std::vector& src, std::string& dst, EGCodeEncodingType encoding_type) +{ + switch (encoding_type) + { + case EGCodeEncodingType::None: + { + dst.insert(dst.end(), src.begin(), src.end()); + break; + } + case EGCodeEncodingType::MeatPack: + { + // TODO + break; + } + } + return true; +} + +static bool compress(const std::vector& src, std::vector& data_out, ECompressionType compression_type) { return true; } -static bool uncompress(const std::vector& data_in, std::vector& data_out, ECompressionType compression_type) +static bool uncompress(const std::vector& src, std::vector& data_out, ECompressionType compression_type) { return true; } @@ -357,7 +397,6 @@ EResult BaseMetadataBlock::read_data(FILE& file, const BlockHeader& block_header if (!read_from_file(file, (void*)&encoding_type, sizeof(encoding_type))) return EResult::ReadError; if (encoding_type > metadata_encoding_types_count()) - // Found invalid metadata encoding type return EResult::InvalidMetadataEncodingType; std::vector data; @@ -620,11 +659,99 @@ EResult SlicerMetadataBlock::read_data(FILE& file, const FileHeader& file_header EResult GCodeBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const { + if (encoding_type > gcode_encoding_types_count()) + return EResult::InvalidGCodeEncodingType; + + BlockHeader block_header = { (uint16_t)EBlockType::GCode, (uint16_t)compression_type, (uint32_t)0 }; + std::vector out_data; + if (!raw_data.empty()) { + // process payload encoding + std::vector uncompressed_data; + if (!encode_gcode(raw_data, uncompressed_data, (EGCodeEncodingType)encoding_type)) + return EResult::GCodeEncodingError; + // process payload compression + block_header.uncompressed_size = (uint32_t)uncompressed_data.size(); + std::vector compressed_data; + if (compression_type != ECompressionType::None) { + if (!compress(uncompressed_data, compressed_data, compression_type)) + return EResult::DataCompressionError; + block_header.compressed_size = (uint32_t)compressed_data.size(); + } + out_data.swap((compression_type == ECompressionType::None) ? uncompressed_data : compressed_data); + } + + // write block header + EResult res = block_header.write(file); + if (res != EResult::Success) + // propagate error + return res; + + // write block payload + if (!write_to_file(file, (const void*)&encoding_type, sizeof(encoding_type))) + return EResult::WriteError; + if (!out_data.empty()) { +#if ENABLE_BINARIZED_GCODE_DEBUG + const std::string out = "GCodeBlock data size:" + std::to_string(out_data.size()) + "\n"; + OutputDebugStringA(out.c_str()); +#endif // ENABLE_BINARIZED_GCODE_DEBUG + if (!write_to_file(file, (const void*)out_data.data(), out_data.size())) + return EResult::WriteError; + } + + // write checksum + if (checksum_type != EChecksumType::None) { + Checksum cs(checksum_type); + // update checksum with block header + block_header.update_checksum(cs); + // update checksum with block payload + cs.append(encode((const void*)&encoding_type, sizeof(encoding_type))); + if (!out_data.empty()) + cs.append(out_data); + res = cs.write(file); + if (res != EResult::Success) + // propagate error + return res; + } + return EResult::Success; } EResult GCodeBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) { + const ECompressionType compression_type = (ECompressionType)block_header.compression; + + if (!read_from_file(file, (void*)&encoding_type, sizeof(encoding_type))) + return EResult::ReadError; + if (encoding_type > gcode_encoding_types_count()) + return EResult::InvalidGCodeEncodingType; + + std::vector data; + const size_t data_size = (compression_type == ECompressionType::None) ? block_header.uncompressed_size : block_header.compressed_size; + if (data_size > 0) { + data.resize(data_size); + if (!read_from_file(file, (void*)data.data(), data_size)) + return EResult::ReadError; + } + + std::vector uncompressed_data; + if (compression_type != ECompressionType::None) { + if (!uncompress(data, uncompressed_data, compression_type)) + return EResult::DataUncompressionError; + } + + if (!decode_gcode((compression_type == ECompressionType::None) ? data : uncompressed_data, raw_data, (EGCodeEncodingType)encoding_type)) + return EResult::GCodeDecodingError; + + const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; + if (checksum_type != EChecksumType::None) { + // read block checksum + Checksum cs(checksum_type); + const EResult res = cs.read(file); + if (res != EResult::Success) + // propagate error + return res; + } + return EResult::Success; } @@ -660,60 +787,116 @@ EResult ChecksumBlock::read_data(FILE& file, const BlockHeader& block_header) } #endif // ENABLE_CHECKSUM_BLOCK -EResult Binarizer::initialize(FILE& file, EChecksumType checksum_type) +EResult Binarizer::initialize(FILE& file, EGCodeEncodingType gcode_encoding_type, EChecksumType checksum_type) { if (!m_enabled) return EResult::Success; - // initialize checksum + m_file = &file; + + m_gcode_encoding_type = gcode_encoding_type; m_checksum_type = checksum_type; #if ENABLE_CHECKSUM_BLOCK + // initialize checksum m_checksum = ChecksumBlock(); #endif // ENABLE_CHECKSUM_BLOCK // save header FileHeader file_header; file_header.checksum_type = (uint16_t)m_checksum_type; - EResult res = file_header.write(file); + EResult res = file_header.write(*m_file); if (res != EResult::Success) return res; // save file metadata block - res = m_binary_data.file_metadata.write(file, ECompressionType::None, m_checksum_type); + res = m_binary_data.file_metadata.write(*m_file, m_compression_type, m_checksum_type); if (res != EResult::Success) return res; // save printer metadata block - res = m_binary_data.printer_metadata.write(file, ECompressionType::None, m_checksum_type); + res = m_binary_data.printer_metadata.write(*m_file, m_compression_type, m_checksum_type); if (res != EResult::Success) return res; // save thumbnail blocks for (const ThumbnailBlock& block : m_binary_data.thumbnails) { - res = block.write(file, m_checksum_type); + res = block.write(*m_file, m_checksum_type); if (res != EResult::Success) return res; } - // save slicer metadata block - res = m_binary_data.slicer_metadata.write(file, ECompressionType::None, m_checksum_type); + // save print metadata block + res = m_binary_data.print_metadata.write(*m_file, m_compression_type, m_checksum_type); if (res != EResult::Success) return res; - // save gcode block + // save slicer metadata block + res = m_binary_data.slicer_metadata.write(*m_file, m_compression_type, m_checksum_type); + if (res != EResult::Success) + return res; return EResult::Success; } -EResult Binarizer::finalize(FILE& file) +static EResult write_gcode_block(FILE& file, const std::string& raw_data, EGCodeEncodingType encoding_type, ECompressionType compression_type, + EChecksumType checksum_type) +{ + GCodeBlock block; + block.encoding_type = (uint16_t)encoding_type; + block.raw_data = raw_data; + return block.write(file, compression_type, checksum_type); +} + +EResult Binarizer::append_gcode(const std::string& gcode) +{ + if (gcode.empty()) + return EResult::Success; + + assert(m_file != nullptr); + if (m_file == nullptr) + return EResult::WriteError; + + auto it_begin = gcode.begin(); + do { + const size_t begin_pos = std::distance(gcode.begin(), it_begin); + const size_t end_line_pos = gcode.find_first_of('\n', begin_pos); + if (end_line_pos == std::string::npos) + return EResult::WriteError; + + const size_t line_size = 1 + end_line_pos - begin_pos; + if (line_size + m_gcode_cache.length() > MAX_GCODE_CACHE_SIZE) { + if (!m_gcode_cache.empty()) { + const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_gcode_encoding_type, m_compression_type, m_checksum_type); + if (res != EResult::Success) + // propagate error + return res; + m_gcode_cache.clear(); + } + } + + if (line_size > MAX_GCODE_CACHE_SIZE) + return EResult::WriteError; + + m_gcode_cache.insert(m_gcode_cache.end(), it_begin, it_begin + line_size); + it_begin += line_size; + } + while (it_begin != gcode.end()); + + return EResult::Success; +} + +EResult Binarizer::finalize() { if (!m_enabled) return EResult::Success; - // save print metadata block - EResult res = m_binary_data.print_metadata.write(file, ECompressionType::None, m_checksum_type); - if (res != EResult::Success) - return res; + // save gcode cache, if not empty + if (!m_gcode_cache.empty()) { + const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_gcode_encoding_type, m_compression_type, m_checksum_type); + if (res != EResult::Success) + // propagate error + return res; + } #if ENABLE_CHECKSUM_BLOCK if (m_checksum_type != EChecksumType::None) { diff --git a/src/libslic3r/GCode/GCodeBinarizer.hpp b/src/libslic3r/GCode/GCodeBinarizer.hpp index 058cabd475..3dd2d14ca7 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.hpp +++ b/src/libslic3r/GCode/GCodeBinarizer.hpp @@ -29,10 +29,13 @@ enum class EResult : uint16_t InvalidBlockType, InvalidCompressionType, InvalidMetadataEncodingType, + InvalidGCodeEncodingType, DataCompressionError, DataUncompressionError, MetadataEncodingError, MetadataDecodingError, + GCodeEncodingError, + GCodeDecodingError, BlockNotFound, InvalidChecksum, InvalidThumbnailFormat, @@ -195,12 +198,23 @@ struct SlicerMetadataBlock : public BaseMetadataBlock EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); }; -struct GCodeBlock : public BaseMetadataBlock +enum class EGCodeEncodingType : uint16_t { + None, + MeatPack, +}; + +struct GCodeBlock +{ + uint16_t encoding_type{ 0 }; + std::string raw_data; + // write block header and data EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; // read block data EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); + + static size_t get_parameters_size() { return sizeof(encoding_type); } }; #if ENABLE_CHECKSUM_BLOCK @@ -248,16 +262,20 @@ public: BinaryData& get_binary_data() { return m_binary_data; } const BinaryData& get_binary_data() const { return m_binary_data; } - EResult initialize(FILE& file, EChecksumType checksum_type); - EResult finalize(FILE& file); + EResult initialize(FILE& file, EGCodeEncodingType gcode_encoding_type, EChecksumType checksum_type); + EResult append_gcode(const std::string& gcode); + EResult finalize(); private: bool m_enabled{ false }; EChecksumType m_checksum_type{ EChecksumType::None }; ECompressionType m_compression_type{ ECompressionType::None }; + EGCodeEncodingType m_gcode_encoding_type{ EGCodeEncodingType::None }; + FILE* m_file{ nullptr }; BinaryData m_binary_data; + std::string m_gcode_cache; #if ENABLE_CHECKSUM_BLOCK ChecksumBlock m_checksum; #endif // ENABLE_CHECKSUM_BLOCK diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index e6d97a3f8c..1cdcba4115 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1127,10 +1127,18 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct FILE* m_file{ nullptr }; }; +#if ENABLE_GCODE_VIEWER_STATISTICS + m_start_time = std::chrono::high_resolution_clock::now(); +#endif // ENABLE_GCODE_VIEWER_STATISTICS + FILE* file = boost::nowide::fopen(filename.c_str(), "rb"); if (file == nullptr) throw Slic3r::RuntimeError("Unable to open file: " + filename + "\n"); + fseek(file, 0, SEEK_END); + const long file_size = ftell(file); + rewind(file); + ScopedFile scoped_file(file); BinaryGCode::set_checksum_max_cache_size(1024); @@ -1141,7 +1149,6 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct if (res != BinaryGCode::EResult::Success) throw Slic3r::RuntimeError("File: " + filename + "does not contain a valid binary gcode\n Error: " + BinaryGCode::translate_result(res) + "\n"); - const long first_block_header_position = ftell(file); const bool verify_checksum = true; // read file metadata block @@ -1222,7 +1229,27 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); } + // read print metadata block + if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::PrintMetadata) + throw Slic3r::RuntimeError("Unable to find print metadata block in file: " + filename + "\n"); + BinaryGCode::PrintMetadataBlock print_metadata_block; + res = print_metadata_block.read_data(*file, file_header, block_header); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); +#if ENABLE_BINARIZED_GCODE_DEBUG + OutputDebugStringA("Print metadata:\n"); + for (const auto& [key, value] : print_metadata_block.raw_data) { + OutputDebugStringA(key.c_str()); + OutputDebugStringA("->"); + OutputDebugStringA(value.c_str()); + OutputDebugStringA("\n"); + } +#endif // ENABLE_BINARIZED_GCODE_DEBUG + // read slicer metadata block + res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::SlicerMetadata) throw Slic3r::RuntimeError("Unable to find slicer metadata block in file: " + filename + "\n"); BinaryGCode::SlicerMetadataBlock slicer_metadata_block; @@ -1250,25 +1277,38 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct config.load_from_ini_string(str, ForwardCompatibilitySubstitutionRule::EnableSilent); apply_config(config); - // read print metadata block + m_result.filename = filename; + m_result.id = ++s_result_id; + initialize_result_moves(); + + // read gcodes block res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); if (res != BinaryGCode::EResult::Success) throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); - if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::PrintMetadata) - throw Slic3r::RuntimeError("Unable to find print metadata block in file: " + filename + "\n"); - BinaryGCode::PrintMetadataBlock print_metadata_block; - res = print_metadata_block.read_data(*file, file_header, block_header); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); -#if ENABLE_BINARIZED_GCODE_DEBUG - OutputDebugStringA("Print metadata:\n"); - for (const auto& [key, value] : print_metadata_block.raw_data) { - OutputDebugStringA(key.c_str()); - OutputDebugStringA("->"); - OutputDebugStringA(value.c_str()); - OutputDebugStringA("\n"); + if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::GCode) + throw Slic3r::RuntimeError("Unable to find gcode block in file: " + filename + "\n"); + while ((BinaryGCode::EBlockType)block_header.type == BinaryGCode::EBlockType::GCode) { + BinaryGCode::GCodeBlock block; + res = block.read_data(*file, file_header, block_header); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + + // TODO: Update m_result.lines_ends + + m_parser.parse_buffer(block.raw_data, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { + this->process_gcode_line(line, true); + }); + + if (ftell(file) == file_size) + break; + + res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); } -#endif // ENABLE_BINARIZED_GCODE_DEBUG + + // Don't post-process the G-code to update time stamps. + this->finalize(false); } #endif // ENABLE_BINARIZED_GCODE @@ -3622,9 +3662,57 @@ void GCodeProcessor::post_process() } #if ENABLE_BINARIZED_GCODE + std::vector filament_mm(m_result.extruders_count, 0.0); + std::vector filament_cm3(m_result.extruders_count, 0.0); + std::vector filament_g(m_result.extruders_count, 0.0); + std::vector filament_cost(m_result.extruders_count, 0.0); + + double filament_total_g = 0.0; + double filament_total_cost = 0.0; + + for (const auto& [id, volume] : m_result.print_statistics.volumes_per_extruder) { + filament_mm[id] = volume / (static_cast(M_PI) * sqr(0.5 * m_result.filament_diameters[id])); + filament_cm3[id] = volume * 0.001; + filament_g[id] = filament_cm3[id] * double(m_result.filament_densities[id]); + filament_cost[id] = filament_g[id] * double(m_result.filament_cost[id]) * 0.001; + filament_total_g += filament_g[id]; + filament_total_cost += filament_cost[id]; + } + if (m_binarizer.is_enabled()) { - BinaryGCode::set_checksum_max_cache_size(4096); - const BinaryGCode::EResult res = m_binarizer.initialize(*out.f, BinaryGCode::EChecksumType::CRC32); + // update print metadata + auto update_value = [](std::string& value, const std::vector& values) { + char buf[1024]; + value.clear(); + for (size_t i = 0; i < values.size(); ++i) { + sprintf(buf, i == values.size() - 1 ? " %.2lf" : " %.2lf,", values[i]); + value += buf; + } + }; + + // update binary data + BinaryGCode::BinaryData& binary_data = m_binarizer.get_binary_data(); + for (auto& [key, value] : binary_data.print_metadata.raw_data) { + if (key == PrintStatistics::FilamentUsedMm) update_value(value, filament_mm); + else if (key == PrintStatistics::FilamentUsedG) update_value(value, filament_g); + else if (key == PrintStatistics::TotalFilamentUsedG) update_value(value, { filament_total_g }); + else if (key == PrintStatistics::FilamentUsedCm3) update_value(value, filament_cm3); + else if (key == PrintStatistics::FilamentCost) update_value(value, filament_cost); + else if (key == PrintStatistics::TotalFilamentCost) update_value(value, { filament_total_cost }); + } + + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { + const TimeMachine& machine = m_time_processor.machines[i]; + PrintEstimatedStatistics::ETimeMode mode = static_cast(i); + if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) { + char buf[128]; + sprintf(buf, "(%s mode)", (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent"); + binary_data.print_metadata.raw_data.push_back({ "estimated printing time " + std::string(buf), get_time_dhms(machine.time) }); + binary_data.print_metadata.raw_data.push_back({ "estimated first layer printing time " + std::string(buf), get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()) }); + } + } + + const BinaryGCode::EResult res = m_binarizer.initialize(*out.f, BinaryGCode::EGCodeEncodingType::None, BinaryGCode::EChecksumType::CRC32); if (res != BinaryGCode::EResult::Success) throw Slic3r::RuntimeError(std::string("Unable to initialize the gcode binarizer.\n")); } @@ -3876,9 +3964,30 @@ void GCodeProcessor::post_process() } } - write_to_file(out, out_string, result, out_path); +#if ENABLE_BINARIZED_GCODE + if (m_binarizer.is_enabled()) { + BinaryGCode::EResult res = m_binarizer.append_gcode(out_string); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); + } + else +#endif // ENABLE_BINARIZED_GCODE + write_to_file(out, out_string, result, out_path); +#if ENABLE_BINARIZED_GCODE + update_out_file_pos(out, out_string, result); +#endif // ENABLE_BINARIZED_GCODE } +#if ENABLE_BINARIZED_GCODE + void update_out_file_pos(FilePtr& out, const std::string& out_string, GCodeProcessorResult& result) { + for (size_t i = 0; i < out_string.size(); ++i) { + if (out_string[i] == '\n') + result.lines_ends.emplace_back(m_out_file_pos + i + 1); + } + m_out_file_pos += out_string.size(); + } +#endif // ENABLE_BINARIZED_GCODE + // flush the current content of the cache to file void flush(FilePtr& out, GCodeProcessorResult& result, const std::string& out_path) { // collect lines to flush into a single string @@ -3892,7 +4001,18 @@ void GCodeProcessor::post_process() m_statistics.remove_all_lines(); #endif // NDEBUG - write_to_file(out, out_string, result, out_path); +#if ENABLE_BINARIZED_GCODE + if (m_binarizer.is_enabled()) { + BinaryGCode::EResult res = m_binarizer.append_gcode(out_string); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); + } + else +#endif // ENABLE_BINARIZED_GCODE + write_to_file(out, out_string, result, out_path); +#if ENABLE_BINARIZED_GCODE + update_out_file_pos(out, out_string, result); +#endif // ENABLE_BINARIZED_GCODE } void synchronize_moves(GCodeProcessorResult& result) const { @@ -3922,12 +4042,13 @@ void GCodeProcessor::post_process() } #if ENABLE_BINARIZED_GCODE } -#endif // ENABLE_BINARIZED_GCODE +#else for (size_t i = 0; i < out_string.size(); ++i) { if (out_string[i] == '\n') result.lines_ends.emplace_back(m_out_file_pos + i + 1); } m_out_file_pos += out_string.size(); +#endif // ENABLE_BINARIZED_GCODE } } }; @@ -3999,6 +4120,7 @@ void GCodeProcessor::post_process() return processed; }; +#if !ENABLE_BINARIZED_GCODE std::vector filament_mm(m_result.extruders_count, 0.0); std::vector filament_cm3(m_result.extruders_count, 0.0); std::vector filament_g(m_result.extruders_count, 0.0); @@ -4015,6 +4137,7 @@ void GCodeProcessor::post_process() filament_total_g += filament_g[id]; filament_total_cost += filament_cost[id]; } +#endif // !ENABLE_BINARIZED_GCODE auto process_used_filament = [&](std::string& gcode_line) { // Prefilter for parsing speed. @@ -4036,12 +4159,21 @@ void GCodeProcessor::post_process() }; bool ret = false; +#if ENABLE_BINARIZED_GCODE + ret |= process_tag(gcode_line, PrintStatistics::FilamentUsedMmMask, filament_mm); + ret |= process_tag(gcode_line, PrintStatistics::FilamentUsedGMask, filament_g); + ret |= process_tag(gcode_line, PrintStatistics::TotalFilamentUsedGMask, { filament_total_g }); + ret |= process_tag(gcode_line, PrintStatistics::FilamentUsedCm3Mask, filament_cm3); + ret |= process_tag(gcode_line, PrintStatistics::FilamentCostMask, filament_cost); + ret |= process_tag(gcode_line, PrintStatistics::TotalFilamentCostMask, { filament_total_cost }); +#else ret |= process_tag(gcode_line, "; filament used [mm] =", filament_mm); ret |= process_tag(gcode_line, "; filament used [g] =", filament_g); ret |= process_tag(gcode_line, "; total filament used [g] =", { filament_total_g }); ret |= process_tag(gcode_line, "; filament used [cm3] =", filament_cm3); ret |= process_tag(gcode_line, "; filament cost =", filament_cost); ret |= process_tag(gcode_line, "; total filament cost =", { filament_total_cost }); +#endif // ENABLE_BINARIZED_GCODE return ret; }; @@ -4251,39 +4383,7 @@ void GCodeProcessor::post_process() #if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { - // update print metadata - auto update_value = [](std::string& value, const std::vector& values) { - char buf[1024]; - value.clear(); - for (size_t i = 0; i < values.size(); ++i) { - sprintf(buf, i == values.size() - 1 ? " %.2lf" : " %.2lf,", values[i]); - value += buf; - } - }; - - // update binary data - BinaryGCode::BinaryData& binary_data = m_binarizer.get_binary_data(); - for (auto& [key, value] : binary_data.print_metadata.raw_data) { - if (key == "filament used [mm]") update_value(value, filament_mm); - else if (key == "filament used [g]") update_value(value, filament_g); - else if (key == "total filament used [g]") update_value(value, { filament_total_g }); - else if (key == "filament used [cm3]") update_value(value, filament_cm3); - else if (key == "filament cost") update_value(value, filament_cost); - else if (key == "total filament cost") update_value(value, { filament_total_cost }); - } - - for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { - const TimeMachine& machine = m_time_processor.machines[i]; - PrintEstimatedStatistics::ETimeMode mode = static_cast(i); - if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) { - char buf[128]; - sprintf(buf, "(%s mode)", (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent"); - binary_data.print_metadata.raw_data.push_back({ "estimated printing time " + std::string(buf), get_time_dhms(machine.time) }); - binary_data.print_metadata.raw_data.push_back({ "estimated first layer printing time " + std::string(buf), get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()) }); - } - } - - const BinaryGCode::EResult res = m_binarizer.finalize(*out.f); + const BinaryGCode::EResult res = m_binarizer.finalize(); if (res != BinaryGCode::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while finalizing the gcode binarizer.\n")); } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 2bd92340d8..c8de08d107 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1548,6 +1548,28 @@ std::string Print::output_filename(const std::string &filename_base) const return this->PrintBase::output_filename(m_config.output_filename_format.value, ".gcode", filename_base, &config); } +#if ENABLE_BINARIZED_GCODE +const std::string PrintStatistics::FilamentUsedG = "filament used [g]"; +const std::string PrintStatistics::FilamentUsedGMask = "; " + PrintStatistics::FilamentUsedG + " ="; + +const std::string PrintStatistics::TotalFilamentUsedG = "total " + PrintStatistics::FilamentUsedG; +const std::string PrintStatistics::TotalFilamentUsedGMask = "; " + PrintStatistics::TotalFilamentUsedG + " ="; +const std::string PrintStatistics::TotalFilamentUsedGValueMask = TotalFilamentUsedGMask + " %.2lf\n"; + +const std::string PrintStatistics::FilamentUsedCm3 = "filament used [cm3]"; +const std::string PrintStatistics::FilamentUsedCm3Mask = "; " + PrintStatistics::FilamentUsedCm3 + " ="; + +const std::string PrintStatistics::FilamentUsedMm = "filament used [mm]"; +const std::string PrintStatistics::FilamentUsedMmMask = "; " + PrintStatistics::FilamentUsedMm + " ="; + +const std::string PrintStatistics::FilamentCost = "filament cost"; +const std::string PrintStatistics::FilamentCostMask = "; " + PrintStatistics::FilamentCost + " ="; + +const std::string PrintStatistics::TotalFilamentCost = "total " + PrintStatistics::FilamentCost; +const std::string PrintStatistics::TotalFilamentCostMask = "; " + PrintStatistics::TotalFilamentCost + " ="; +const std::string PrintStatistics::TotalFilamentCostValueMask = PrintStatistics::TotalFilamentCostMask + " %.2lf\n"; +#endif // ENABLE_BINARIZED_GCODE + DynamicConfig PrintStatistics::config() const { DynamicConfig config; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 059491951e..9490e07c5d 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -528,6 +528,23 @@ struct PrintStatistics filament_stats.clear(); printing_extruders.clear(); } + +#if ENABLE_BINARIZED_GCODE + static const std::string FilamentUsedG; + static const std::string FilamentUsedGMask; + static const std::string TotalFilamentUsedG; + static const std::string TotalFilamentUsedGMask; + static const std::string TotalFilamentUsedGValueMask; + static const std::string FilamentUsedCm3; + static const std::string FilamentUsedCm3Mask; + static const std::string FilamentUsedMm; + static const std::string FilamentUsedMmMask; + static const std::string FilamentCost; + static const std::string FilamentCostMask; + static const std::string TotalFilamentCost; + static const std::string TotalFilamentCostMask; + static const std::string TotalFilamentCostValueMask; +#endif // ENABLE_BINARIZED_GCODE }; using PrintObjectPtrs = std::vector;