From 666e6faa822b1f397da082ee8b660d9d11d8bd5c Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 17 Jul 2026 10:00:42 +0200 Subject: [PATCH 1/5] Merge header with first GCode part CURA-13249 --- src/gcode_export/gcodeExport.cpp | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/gcode_export/gcodeExport.cpp b/src/gcode_export/gcodeExport.cpp index d82113dc16..fa7ab2b423 100644 --- a/src/gcode_export/gcodeExport.cpp +++ b/src/gcode_export/gcodeExport.cpp @@ -2027,13 +2027,30 @@ void GCodeExport::finalize(const std::string& end_code, PrintInformation& print_ template_resolver_->prepareForResolving(print_info.initial_extruder_nr.value_or(0), extra_global_settings); - std::shared_ptr communication = Application::getInstance().communication_; + { + // Prepend the header to the first gcode part, so that they become a single unsplittable part + const std::string header = getFileHeader(is_extruder_used_bool, filaments_volumes, materials_ids); + + // Since the gcode is stored in a stream, we cannot prepend data to it, so create a new part with the assembled strings + auto header_part = std::make_shared(); + header_part->stream() << header; - communication->sendGCodePart(getFileHeader(is_extruder_used_bool, filaments_volumes, materials_ids)); + std::shared_ptr first_gcode_part = gcode_parts_.front(); + if (const auto fixed_first_gcode_part = std::dynamic_pointer_cast(first_gcode_part)) + { + header_part->stream() << fixed_first_gcode_part->str(); + gcode_parts_.front() = header_part; + } + else + { + // First GCode part is a resolvable part, which should not happen, but in case it does, just prepend the header as a fixed part + gcode_parts_.insert(gcode_parts_.begin(), header_part); + } + } sendFinalGCode(); - communication->sendPrintInformation(total_print_times_, print_info); + Application::getInstance().communication_->sendPrintInformation(total_print_times_, print_info); } void GCodeExport::finalizeExtruder(const std::string& extruder_end_code) From 5960a649371fee5e2a9f064a513efb91a8b25fdc Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 13 Aug 2026 12:02:49 +0200 Subject: [PATCH 2/5] Restore gcode parts sending packets CURA-13249 --- include/gcode_export/FixedGCodePart.h | 6 ++- include/gcode_export/GCodePart.h | 18 ++++++- include/gcode_export/gcodeExport.h | 10 ++-- src/FffGcodeWriter.cpp | 1 - src/gcode_export/FixedGCodePart.cpp | 3 +- src/gcode_export/gcodeExport.cpp | 71 ++++++++++++++++----------- 6 files changed, 71 insertions(+), 38 deletions(-) diff --git a/include/gcode_export/FixedGCodePart.h b/include/gcode_export/FixedGCodePart.h index c19676df9e..e373f8ae74 100644 --- a/include/gcode_export/FixedGCodePart.h +++ b/include/gcode_export/FixedGCodePart.h @@ -15,7 +15,11 @@ namespace cura class FixedGCodePart : public GCodePart { public: - explicit FixedGCodePart(); + /*! + * @brief Constructor + * @param print_code Whether the GCode actually contains print instructions, or management commands (heating, extruder switch, ...) + */ + explicit FixedGCodePart(const bool print_code); /*! \brief Gets the full piece of GCode to be exported */ std::string str() const override; diff --git a/include/gcode_export/GCodePart.h b/include/gcode_export/GCodePart.h index f154791fc4..1cd3e7ba5f 100644 --- a/include/gcode_export/GCodePart.h +++ b/include/gcode_export/GCodePart.h @@ -18,8 +18,24 @@ class GCodePart /*! \brief Gets the full piece of GCode to be exported */ virtual std::string str() const = 0; + /*! @return Whether the GCode actually contains print instructions, or management commands (header, heating, custom start gcode, ...) */ + bool isPrintCode() const + { + return print_code_; + } + protected: - explicit GCodePart() = default; + /*! + * Constructor + * @param print_code Whether the GCode actually contains print instructions, or management commands (header, heating, custom start gcode, ...) + */ + explicit GCodePart(const bool print_code = false) + : print_code_(print_code) + { + } + +private: + const bool print_code_; // Whether the GCode actually contains print instructions, or management commands (header, heating, custom start gcode, ...) }; } // namespace cura diff --git a/include/gcode_export/gcodeExport.h b/include/gcode_export/gcodeExport.h index 6a5d6607f0..1e58c33b2f 100644 --- a/include/gcode_export/gcodeExport.h +++ b/include/gcode_export/gcodeExport.h @@ -409,13 +409,11 @@ class GCodeExport : public NoCopy */ bool needPrimeBlob() const; - /* - * Function is used to write the content of output_stream to the gcode file + /*! + * \brief Creates a new instance of fixed GCode part and sets it as the current container for fixed GCode parts. + * \param print_code Whether the GCode actually contains print instructions, or management commands (heating, extruder switch, ...) */ - void flushOutputStream(); - - /*! \brief Creates a new instance of fixed GCode part and sets it as the current container for fixed GCode parts. */ - void prepareNewFixedGCodePart(); + void prepareNewFixedGCodePart(const bool print_code = false); /*! * \brief Write a piece of resolvable GCode diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 3c46770762..f82ca5c929 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -4288,7 +4288,6 @@ void FffGcodeWriter::finalize() } gcode.writeComment("End of Gcode"); - gcode.flushOutputStream(); /* the profile string below can be executed since the M25 doesn't end the gcode on an UMO and when printing via USB. gcode.writeCode("M25 ;Stop reading from this point on."); diff --git a/src/gcode_export/FixedGCodePart.cpp b/src/gcode_export/FixedGCodePart.cpp index efe8260adc..d503979636 100644 --- a/src/gcode_export/FixedGCodePart.cpp +++ b/src/gcode_export/FixedGCodePart.cpp @@ -7,7 +7,8 @@ namespace cura { -FixedGCodePart::FixedGCodePart() +FixedGCodePart::FixedGCodePart(const bool print_code) + : GCodePart(print_code) { stream_ << std::fixed; } diff --git a/src/gcode_export/gcodeExport.cpp b/src/gcode_export/gcodeExport.cpp index fa7ab2b423..78dc68dbeb 100644 --- a/src/gcode_export/gcodeExport.cpp +++ b/src/gcode_export/gcodeExport.cpp @@ -354,7 +354,9 @@ std::string GCodeExport::getFileHeader(const std::vector& extruder_is_used void GCodeExport::setLayerNr(const LayerIndex& layer_nr) { layer_nr_ = layer_nr; - prepareNewFixedGCodePart(); // Now is a good time to switch to a new buffer + + constexpr bool print_code = true; // Now we are actually going to send print commands + prepareNewFixedGCodePart(print_code); // Now is a good time to switch to a new buffer } bool GCodeExport::getExtruderIsUsed(const int extruder_nr) const @@ -2027,26 +2029,44 @@ void GCodeExport::finalize(const std::string& end_code, PrintInformation& print_ template_resolver_->prepareForResolving(print_info.initial_extruder_nr.value_or(0), extra_global_settings); - { - // Prepend the header to the first gcode part, so that they become a single unsplittable part - const std::string header = getFileHeader(is_extruder_used_bool, filaments_volumes, materials_ids); - - // Since the gcode is stored in a stream, we cannot prepend data to it, so create a new part with the assembled strings - auto header_part = std::make_shared(); - header_part->stream() << header; - - std::shared_ptr first_gcode_part = gcode_parts_.front(); - if (const auto fixed_first_gcode_part = std::dynamic_pointer_cast(first_gcode_part)) - { - header_part->stream() << fixed_first_gcode_part->str(); - gcode_parts_.front() = header_part; - } - else - { - // First GCode part is a resolvable part, which should not happen, but in case it does, just prepend the header as a fixed part - gcode_parts_.insert(gcode_parts_.begin(), header_part); - } - } + // For retro-compatibility with the post-processing plugins, merge all the initial gcode parts into a single one + constexpr bool print_code = false; + auto init_part = std::make_shared(print_code); + auto iterator_first_print_part = gcode_parts_.begin(); + while (iterator_first_print_part != gcode_parts_.end() && ! (*iterator_first_print_part)->isPrintCode()) // Stop when we reach the first actual layer-print code + { + init_part->stream() << (*iterator_first_print_part)->str(); + ++iterator_first_print_part; + } + gcode_parts_.erase(gcode_parts_.begin(), iterator_first_print_part); + gcode_parts_.insert(gcode_parts_.begin(), init_part); + + // Prepend the header as a full separate part + const std::string header = getFileHeader(is_extruder_used_bool, filaments_volumes, materials_ids); + auto header_part = std::make_shared(print_code); + header_part->stream() << header; + gcode_parts_.insert(gcode_parts_.begin(), header_part); + + // { + // // Prepend the header to the first gcode part, so that they become a single unsplittable part + // const std::string header = getFileHeader(is_extruder_used_bool, filaments_volumes, materials_ids); + // + // // Since the gcode is stored in a stream, we cannot prepend data to it, so create a new part with the assembled strings + // auto header_part = std::make_shared(); + // header_part->stream() << header; + // + // std::shared_ptr first_gcode_part = gcode_parts_.front(); + // if (const auto fixed_first_gcode_part = std::dynamic_pointer_cast(first_gcode_part)) + // { + // header_part->stream() << fixed_first_gcode_part->str(); + // gcode_parts_.front() = header_part; + // } + // else + // { + // // First GCode part is a resolvable part, which should not happen, but in case it does, just prepend the header as a fixed part + // gcode_parts_.insert(gcode_parts_.begin(), header_part); + // } + // } sendFinalGCode(); @@ -2060,14 +2080,9 @@ void GCodeExport::finalizeExtruder(const std::string& extruder_end_code) writeCodeWithAbsoluteExtrusion(extruder_end_code, getExtruderNr(), extra_settings); } -void GCodeExport::flushOutputStream() -{ - prepareNewFixedGCodePart(); -} - -void GCodeExport::prepareNewFixedGCodePart() +void GCodeExport::prepareNewFixedGCodePart(const bool print_code) { - auto fixed_gcode_part = std::make_shared(); + const auto fixed_gcode_part = std::make_shared(print_code); gcode_parts_.push_back(fixed_gcode_part); output_stream_ = &fixed_gcode_part->stream(); } From b3473a4a36e136b576a5c3015d42a0f553ac1b77 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 13 Aug 2026 12:20:59 +0200 Subject: [PATCH 3/5] Send missing end gcode part CURA-13249 --- include/gcode_export/gcodeExport.h | 6 +++--- src/FffGcodeWriter.cpp | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/gcode_export/gcodeExport.h b/include/gcode_export/gcodeExport.h index 1e58c33b2f..ca5bb2abe5 100644 --- a/include/gcode_export/gcodeExport.h +++ b/include/gcode_export/gcodeExport.h @@ -554,9 +554,6 @@ class GCodeExport : public NoCopy static PrintFeatureType sendTravel(const Point3LL& p, const Velocity& speed, const ExtruderTrainAttributes& extruder_attr, const std::optional& retraction_amounts); - /*! \brief Resolves and sends all the pieces of GCode that have been created during slicing */ - void sendFinalGCode(); - /*! \brief Calculates the end-of-print data about material consumption */ std::vector> calculateMaterialPrintInformation() const; @@ -773,6 +770,9 @@ class GCodeExport : public NoCopy * Indicates whether the printer handles the retraction/priming, totally or with specific commands */ bool machineHandlesRetraction() const; + + /*! \brief Resolves and sends all the pieces of GCode that have been created during slicing */ + void sendFinalGCode(); }; } // namespace cura diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index f82ca5c929..351797933d 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -4288,6 +4288,7 @@ void FffGcodeWriter::finalize() } gcode.writeComment("End of Gcode"); + gcode.sendFinalGCode(); /* the profile string below can be executed since the M25 doesn't end the gcode on an UMO and when printing via USB. gcode.writeCode("M25 ;Stop reading from this point on."); From fc40867b2437b2d7e344c19342dc357ccc1676f4 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 13 Aug 2026 12:21:09 +0200 Subject: [PATCH 4/5] Clean code CURA-13249 --- src/gcode_export/gcodeExport.cpp | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/src/gcode_export/gcodeExport.cpp b/src/gcode_export/gcodeExport.cpp index 78dc68dbeb..c528d81ec3 100644 --- a/src/gcode_export/gcodeExport.cpp +++ b/src/gcode_export/gcodeExport.cpp @@ -2047,29 +2047,6 @@ void GCodeExport::finalize(const std::string& end_code, PrintInformation& print_ header_part->stream() << header; gcode_parts_.insert(gcode_parts_.begin(), header_part); - // { - // // Prepend the header to the first gcode part, so that they become a single unsplittable part - // const std::string header = getFileHeader(is_extruder_used_bool, filaments_volumes, materials_ids); - // - // // Since the gcode is stored in a stream, we cannot prepend data to it, so create a new part with the assembled strings - // auto header_part = std::make_shared(); - // header_part->stream() << header; - // - // std::shared_ptr first_gcode_part = gcode_parts_.front(); - // if (const auto fixed_first_gcode_part = std::dynamic_pointer_cast(first_gcode_part)) - // { - // header_part->stream() << fixed_first_gcode_part->str(); - // gcode_parts_.front() = header_part; - // } - // else - // { - // // First GCode part is a resolvable part, which should not happen, but in case it does, just prepend the header as a fixed part - // gcode_parts_.insert(gcode_parts_.begin(), header_part); - // } - // } - - sendFinalGCode(); - Application::getInstance().communication_->sendPrintInformation(total_print_times_, print_info); } From 27ef614f8391d1c6422043b8ffb9483963fad8ef Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 13 Aug 2026 14:47:09 +0200 Subject: [PATCH 5/5] Fix and consolidate end of gcode CURA-13249 --- include/gcode_export/FixedGCodePart.h | 6 +-- include/gcode_export/GCodePart.h | 17 +++--- include/gcode_export/GCodePartType.h | 20 +++++++ include/gcode_export/gcodeExport.h | 3 +- src/gcode_export/FixedGCodePart.cpp | 4 +- src/gcode_export/gcodeExport.cpp | 78 ++++++++++++++++++++------- 6 files changed, 90 insertions(+), 38 deletions(-) create mode 100644 include/gcode_export/GCodePartType.h diff --git a/include/gcode_export/FixedGCodePart.h b/include/gcode_export/FixedGCodePart.h index e373f8ae74..88009e3df5 100644 --- a/include/gcode_export/FixedGCodePart.h +++ b/include/gcode_export/FixedGCodePart.h @@ -15,11 +15,7 @@ namespace cura class FixedGCodePart : public GCodePart { public: - /*! - * @brief Constructor - * @param print_code Whether the GCode actually contains print instructions, or management commands (heating, extruder switch, ...) - */ - explicit FixedGCodePart(const bool print_code); + explicit FixedGCodePart(const GCodePartType type); /*! \brief Gets the full piece of GCode to be exported */ std::string str() const override; diff --git a/include/gcode_export/GCodePart.h b/include/gcode_export/GCodePart.h index 1cd3e7ba5f..acd0ac99f3 100644 --- a/include/gcode_export/GCodePart.h +++ b/include/gcode_export/GCodePart.h @@ -6,6 +6,8 @@ #include +#include "gcode_export/GCodePartType.h" + namespace cura { @@ -18,24 +20,19 @@ class GCodePart /*! \brief Gets the full piece of GCode to be exported */ virtual std::string str() const = 0; - /*! @return Whether the GCode actually contains print instructions, or management commands (header, heating, custom start gcode, ...) */ - bool isPrintCode() const + GCodePartType type() const { - return print_code_; + return type_; } protected: - /*! - * Constructor - * @param print_code Whether the GCode actually contains print instructions, or management commands (header, heating, custom start gcode, ...) - */ - explicit GCodePart(const bool print_code = false) - : print_code_(print_code) + explicit GCodePart(const GCodePartType type = GCodePartType::Management) + : type_(type) { } private: - const bool print_code_; // Whether the GCode actually contains print instructions, or management commands (header, heating, custom start gcode, ...) + const GCodePartType type_; }; } // namespace cura diff --git a/include/gcode_export/GCodePartType.h b/include/gcode_export/GCodePartType.h new file mode 100644 index 0000000000..19b2f12252 --- /dev/null +++ b/include/gcode_export/GCodePartType.h @@ -0,0 +1,20 @@ +// Copyright (c) 2026 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef GCODEEXPORT_GCODEPARTTYPE_H +#define GCODEEXPORT_GCODEPARTTYPE_H + +namespace cura +{ + +enum class GCodePartType +{ + Header, // The header, only made of useful comments + Management, // Management commands, like heating, extruder switching, custom gcode, ... + Print, // Actual model print commands + +}; + +} // namespace cura + +#endif diff --git a/include/gcode_export/gcodeExport.h b/include/gcode_export/gcodeExport.h index ca5bb2abe5..c17ffa8f93 100644 --- a/include/gcode_export/gcodeExport.h +++ b/include/gcode_export/gcodeExport.h @@ -15,6 +15,7 @@ #include "PrintInformation.h" #include "TravelAntiOozing.h" +#include "gcode_export/GCodePartType.h" #include "gcode_export/ResolvingExtruderContext.h" #include "geometry/Point2LL.h" #include "settings/EnumSettings.h" @@ -413,7 +414,7 @@ class GCodeExport : public NoCopy * \brief Creates a new instance of fixed GCode part and sets it as the current container for fixed GCode parts. * \param print_code Whether the GCode actually contains print instructions, or management commands (heating, extruder switch, ...) */ - void prepareNewFixedGCodePart(const bool print_code = false); + void prepareNewFixedGCodePart(const GCodePartType type = GCodePartType::Management); /*! * \brief Write a piece of resolvable GCode diff --git a/src/gcode_export/FixedGCodePart.cpp b/src/gcode_export/FixedGCodePart.cpp index d503979636..5b1b6b1140 100644 --- a/src/gcode_export/FixedGCodePart.cpp +++ b/src/gcode_export/FixedGCodePart.cpp @@ -7,8 +7,8 @@ namespace cura { -FixedGCodePart::FixedGCodePart(const bool print_code) - : GCodePart(print_code) +FixedGCodePart::FixedGCodePart(const GCodePartType type) + : GCodePart(type) { stream_ << std::fixed; } diff --git a/src/gcode_export/gcodeExport.cpp b/src/gcode_export/gcodeExport.cpp index c528d81ec3..4be1d763a3 100644 --- a/src/gcode_export/gcodeExport.cpp +++ b/src/gcode_export/gcodeExport.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include "Application.h" //To send layer view data. @@ -355,8 +356,8 @@ void GCodeExport::setLayerNr(const LayerIndex& layer_nr) { layer_nr_ = layer_nr; - constexpr bool print_code = true; // Now we are actually going to send print commands - prepareNewFixedGCodePart(print_code); // Now is a good time to switch to a new buffer + constexpr GCodePartType type = GCodePartType::Print; // Now we are actually going to send print commands + prepareNewFixedGCodePart(type); // Now is a good time to switch to a new buffer } bool GCodeExport::getExtruderIsUsed(const int extruder_nr) const @@ -1425,8 +1426,55 @@ PrintFeatureType void GCodeExport::sendFinalGCode() { - std::shared_ptr communication = Application::getInstance().communication_; + { + // For retro-compatibility with the post-processing plugins, merge all the initial gcode parts into a single one + const auto iterator_first_init_part = ranges::find_if( + gcode_parts_, + [](const std::shared_ptr& part) + { + return part->type() != GCodePartType::Header; + }); + const auto iterator_first_print_part = ranges::find_if( + iterator_first_init_part, + gcode_parts_.end(), + [](const std::shared_ptr& part) + { + return part->type() == GCodePartType::Print; + }); + if (std::distance(iterator_first_init_part, iterator_first_print_part) > 1) // If there is none or a single init part, we can skip + { + constexpr auto type = GCodePartType::Management; + auto init_part = std::make_shared(type); + for (auto iterator = iterator_first_init_part; iterator != iterator_first_print_part; ++iterator) + { + init_part->stream() << (*iterator)->str(); + } + + // To avoid invalidating iterators, replace the first init part and erase the rest + *iterator_first_init_part = init_part; + gcode_parts_.erase(iterator_first_init_part + 1, iterator_first_print_part); + } + } + { + // For retro-compatibility with the post-processing plugins, merge all the end gcode parts into a single one + auto iterator_last_print_part = gcode_parts_.end() - 1; + while (iterator_last_print_part != gcode_parts_.begin() && (*iterator_last_print_part)->type() != GCodePartType::Print) + { + --iterator_last_print_part; + } + + constexpr auto type = GCodePartType::Management; + auto end_part = std::make_shared(type); + for (auto iterator = iterator_last_print_part + 1; iterator != gcode_parts_.end(); ++iterator) + { + end_part->stream() << (*iterator)->str(); + } + gcode_parts_.erase(iterator_last_print_part + 1, gcode_parts_.end()); + gcode_parts_.push_back(end_part); + } + + std::shared_ptr communication = Application::getInstance().communication_; run_multiple_producers_ordered_consumer( 0, gcode_parts_.size(), @@ -2029,23 +2077,13 @@ void GCodeExport::finalize(const std::string& end_code, PrintInformation& print_ template_resolver_->prepareForResolving(print_info.initial_extruder_nr.value_or(0), extra_global_settings); - // For retro-compatibility with the post-processing plugins, merge all the initial gcode parts into a single one - constexpr bool print_code = false; - auto init_part = std::make_shared(print_code); - auto iterator_first_print_part = gcode_parts_.begin(); - while (iterator_first_print_part != gcode_parts_.end() && ! (*iterator_first_print_part)->isPrintCode()) // Stop when we reach the first actual layer-print code { - init_part->stream() << (*iterator_first_print_part)->str(); - ++iterator_first_print_part; + // Prepend the header as a full separate part + constexpr GCodePartType type = GCodePartType::Header; + auto header_part = std::make_shared(type); + header_part->stream() << getFileHeader(is_extruder_used_bool, filaments_volumes, materials_ids); + gcode_parts_.insert(gcode_parts_.begin(), header_part); } - gcode_parts_.erase(gcode_parts_.begin(), iterator_first_print_part); - gcode_parts_.insert(gcode_parts_.begin(), init_part); - - // Prepend the header as a full separate part - const std::string header = getFileHeader(is_extruder_used_bool, filaments_volumes, materials_ids); - auto header_part = std::make_shared(print_code); - header_part->stream() << header; - gcode_parts_.insert(gcode_parts_.begin(), header_part); Application::getInstance().communication_->sendPrintInformation(total_print_times_, print_info); } @@ -2057,9 +2095,9 @@ void GCodeExport::finalizeExtruder(const std::string& extruder_end_code) writeCodeWithAbsoluteExtrusion(extruder_end_code, getExtruderNr(), extra_settings); } -void GCodeExport::prepareNewFixedGCodePart(const bool print_code) +void GCodeExport::prepareNewFixedGCodePart(const GCodePartType type) { - const auto fixed_gcode_part = std::make_shared(print_code); + const auto fixed_gcode_part = std::make_shared(type); gcode_parts_.push_back(fixed_gcode_part); output_stream_ = &fixed_gcode_part->stream(); }