diff --git a/include/gcode_export/FixedGCodePart.h b/include/gcode_export/FixedGCodePart.h index c19676df9e..88009e3df5 100644 --- a/include/gcode_export/FixedGCodePart.h +++ b/include/gcode_export/FixedGCodePart.h @@ -15,7 +15,7 @@ namespace cura class FixedGCodePart : public GCodePart { public: - explicit FixedGCodePart(); + 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 f154791fc4..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,8 +20,19 @@ class GCodePart /*! \brief Gets the full piece of GCode to be exported */ virtual std::string str() const = 0; + GCodePartType type() const + { + return type_; + } + protected: - explicit GCodePart() = default; + explicit GCodePart(const GCodePartType type = GCodePartType::Management) + : type_(type) + { + } + +private: + 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 6a5d6607f0..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" @@ -409,13 +410,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 GCodePartType type = GCodePartType::Management); /*! * \brief Write a piece of resolvable GCode @@ -556,9 +555,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; @@ -775,6 +771,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 3c46770762..351797933d 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -4288,7 +4288,7 @@ void FffGcodeWriter::finalize() } gcode.writeComment("End of Gcode"); - gcode.flushOutputStream(); + 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."); diff --git a/src/gcode_export/FixedGCodePart.cpp b/src/gcode_export/FixedGCodePart.cpp index efe8260adc..5b1b6b1140 100644 --- a/src/gcode_export/FixedGCodePart.cpp +++ b/src/gcode_export/FixedGCodePart.cpp @@ -7,7 +7,8 @@ namespace cura { -FixedGCodePart::FixedGCodePart() +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 d82113dc16..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. @@ -354,7 +355,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 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 @@ -1423,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(), @@ -2027,13 +2077,15 @@ 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_; - - communication->sendGCodePart(getFileHeader(is_extruder_used_bool, filaments_volumes, materials_ids)); - - sendFinalGCode(); + { + // 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); + } - 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) @@ -2043,14 +2095,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 GCodePartType type) { - auto fixed_gcode_part = std::make_shared(); + const auto fixed_gcode_part = std::make_shared(type); gcode_parts_.push_back(fixed_gcode_part); output_stream_ = &fixed_gcode_part->stream(); }