1 //Copyright (c) 2019 Ultimaker B.V.
2 //CuraEngine is released under the terms of the AGPLv3 or higher.
3 
4 #ifndef GCODEEXPORT_H
5 #define GCODEEXPORT_H
6 
7 #include <deque> // for extrusionAmountAtPreviousRetractions
8 #ifdef BUILD_TESTS
9     #include <gtest/gtest_prod.h> //To allow tests to use protected members.
10 #endif
11 #include <sstream> // for stream.str()
12 #include <stdio.h>
13 
14 #include "utils/AABB3D.h" //To track the used build volume for the Griffin header.
15 #include "timeEstimate.h"
16 #include "settings/EnumSettings.h"
17 #include "settings/Settings.h" //For MAX_EXTRUDERS.
18 #include "settings/types/Temperature.h" //Bed temperature.
19 #include "settings/types/Velocity.h"
20 #include "utils/IntPoint.h"
21 #include "utils/NoCopy.h"
22 
23 namespace cura
24 {
25 
26 struct LayerIndex;
27 class RetractionConfig;
28 struct WipeScriptConfig;
29 
30 //The GCodeExport class writes the actual GCode. This is the only class that knows how GCode looks and feels.
31 //  Any customizations on GCodes flavors are done in this class.
32 class GCodeExport : public NoCopy
33 {
34 #ifdef BUILD_TESTS
35     friend class GCodeExportTest;
36     friend class GriffinHeaderTest;
37     FRIEND_TEST(GCodeExportTest, CommentEmpty);
38     FRIEND_TEST(GCodeExportTest, CommentSimple);
39     FRIEND_TEST(GCodeExportTest, CommentMultiLine);
40     FRIEND_TEST(GCodeExportTest, CommentMultiple);
41     FRIEND_TEST(GCodeExportTest, CommentTimeZero);
42     FRIEND_TEST(GCodeExportTest, CommentTimeInteger);
43     FRIEND_TEST(GCodeExportTest, CommentTimeFloatRoundingError);
44     FRIEND_TEST(GCodeExportTest, CommentTypeAllTypesCovered);
45     FRIEND_TEST(GCodeExportTest, CommentLayer);
46     FRIEND_TEST(GCodeExportTest, CommentLayerNegative);
47     FRIEND_TEST(GCodeExportTest, CommentLayerCount);
48     FRIEND_TEST(GriffinHeaderTest, HeaderGriffinFormat);
49     FRIEND_TEST(GCodeExportTest, HeaderUltiGCode);
50     FRIEND_TEST(GCodeExportTest, HeaderRepRap);
51     FRIEND_TEST(GCodeExportTest, HeaderMarlin);
52     FRIEND_TEST(GCodeExportTest, HeaderMarlinVolumetric);
53     FRIEND_TEST(GCodeExportTest, EVsMmVolumetric);
54     FRIEND_TEST(GCodeExportTest, EVsMmLinear);
55     FRIEND_TEST(GCodeExportTest, WriteZHopStartDefaultSpeed);
56     FRIEND_TEST(GCodeExportTest, WriteZHopStartCustomSpeed);
57     FRIEND_TEST(GCodeExportTest, WriteZHopEndZero);
58     FRIEND_TEST(GCodeExportTest, WriteZHopEndDefaultSpeed);
59     FRIEND_TEST(GCodeExportTest, WriteZHopEndCustomSpeed);
60     FRIEND_TEST(GCodeExportTest, insertWipeScriptSingleMove);
61     FRIEND_TEST(GCodeExportTest, insertWipeScriptMultipleMoves);
62     FRIEND_TEST(GCodeExportTest, insertWipeScriptOptionalDelay);
63     FRIEND_TEST(GCodeExportTest, insertWipeScriptRetractionEnable);
64     FRIEND_TEST(GCodeExportTest, insertWipeScriptHopEnable);
65 #endif
66 private:
67     struct ExtruderTrainAttributes
68     {
69         bool is_primed; //!< Whether this extruder has currently already been primed in this print
70 
71         bool is_used; //!< Whether this extruder train is actually used during the printing of all meshgroups
72         char extruderCharacter;
73 
74         double filament_area; //!< in mm^2 for non-volumetric, cylindrical filament
75 
76         double totalFilament; //!< total filament used per extruder in mm^3
77         Temperature currentTemperature;
78         bool waited_for_temperature; //!< Whether the most recent temperature command has been a heat-and-wait command (M109) or not (M104).
79         Temperature initial_temp; //!< Temperature this nozzle needs to be at the start of the print.
80 
81         double retraction_e_amount_current; //!< The current retracted amount (in mm or mm^3), or zero(i.e. false) if it is not currently retracted (positive values mean retracted amount, so negative impact on E values)
82         double retraction_e_amount_at_e_start; //!< The ExtruderTrainAttributes::retraction_amount_current value at E0, i.e. the offset (in mm or mm^3) from E0 to the situation where the filament is at the tip of the nozzle.
83 
84         double prime_volume; //!< Amount of material (in mm^3) to be primed after an unretration (due to oozing and/or coasting)
85         Velocity last_retraction_prime_speed; //!< The last prime speed (in mm/s) of the to-be-primed amount
86 
87         double last_e_value_after_wipe; //!< The current material amount extruded since last wipe
88 
89         unsigned fan_number; // nozzle print cooling fan number
90 
91         std::deque<double> extruded_volume_at_previous_n_retractions; // in mm^3
92 
ExtruderTrainAttributesExtruderTrainAttributes93         ExtruderTrainAttributes()
94         : is_primed(false)
95         , is_used(false)
96         , extruderCharacter(0)
97         , filament_area(0)
98         , totalFilament(0)
99         , currentTemperature(0)
100         , waited_for_temperature(false)
101         , initial_temp(0)
102         , retraction_e_amount_current(0.0)
103         , retraction_e_amount_at_e_start(0.0)
104         , prime_volume(0.0)
105         , last_retraction_prime_speed(0.0)
106         , fan_number(0)
107         { }
108     };
109     ExtruderTrainAttributes extruder_attr[MAX_EXTRUDERS];
110     bool use_extruder_offset_to_offset_coords;
111     std::string machine_name;
112     std::string machine_buildplate_type;
113 
114     std::ostream* output_stream;
115     std::string new_line;
116 
117     double current_e_value; //!< The last E value written to gcode (in mm or mm^3)
118 
119     // flow-rate compensation
120     double current_e_offset; //!< Offset to compensate for flow rate (mm or mm^3)
121     double max_extrusion_offset; //!< 0 to turn it off, normally 4
122     double extrusion_offset_factor; //!< default 1
123 
124     Point3 currentPosition; //!< The last build plate coordinates written to gcode (which might be different from actually written gcode coordinates when the extruder offset is encoded in the gcode)
125     Velocity currentSpeed; //!< The current speed (F values / 60) in mm/s
126     Acceleration current_print_acceleration; //!< The current acceleration (in mm/s^2) used for print moves (and also for travel moves if the gcode flavor doesn't have separate travel acceleration)
127     Acceleration current_travel_acceleration; //!< The current acceleration (in mm/s^2) used for travel moves for those gcode flavors that have separate print and travel accelerations
128     Velocity current_jerk; //!< The current jerk in the XY direction (in mm/s^3)
129 
130     AABB3D total_bounding_box; //!< The bounding box of all g-code.
131 
132     /*!
133      * The z position to be used on the next xy move, if the head wasn't in the correct z position yet.
134      *
135      * \see GCodeExport::writeExtrusion(Point, double, double)
136      *
137      * \note After GCodeExport::writeExtrusion(Point, double, double) has been called currentPosition.z coincides with this value
138      */
139     coord_t current_layer_z;
140     coord_t is_z_hopped; //!< The amount by which the print head is currently z hopped, or zero if it is not z hopped. (A z hop is used during travel moves to avoid collision with other layer parts)
141 
142     size_t current_extruder;
143     double current_fan_speed;
144     unsigned fan_number; // current print cooling fan number
145     EGCodeFlavor flavor;
146 
147     std::vector<Duration> total_print_times; //!< The total estimated print time in seconds for each feature
148     TimeEstimateCalculator estimateCalculator;
149 
150     unsigned int layer_nr; //!< for sending travel data
151 
152     bool is_volumetric;
153     bool relative_extrusion; //!< whether to use relative extrusion distances rather than absolute
154     bool always_write_active_tool; //!< whether to write the active tool after sending commands to inactive tool
155 
156     Temperature initial_bed_temp; //!< bed temperature at the beginning of the print.
157     Temperature build_volume_temperature;  //!< build volume temperature
158     bool machine_heated_build_volume;  //!< does the machine have the ability to control/stabilize build-volume-temperature
159 protected:
160     /*!
161      * Convert an E value to a value in mm (if it wasn't already in mm) for the current extruder.
162      *
163      * E values are either in mm or in mm^3
164      * The current extruder is used to determine the filament area to make the conversion.
165      *
166      * \param e the value to convert
167      * \return the value converted to mm
168      */
169     double eToMm(double e);
170 
171     /*!
172      * Convert a volume value to an E value (which might be volumetric as well) for the current extruder.
173      *
174      * E values are either in mm or in mm^3
175      * The current extruder is used to determine the filament area to make the conversion.
176      *
177      * \param mm3 the value to convert
178      * \return the value converted to mm or mm3 depending on whether the E axis is volumetric
179      */
180     double mm3ToE(double mm3);
181 
182     /*!
183      * Convert a distance value to an E value (which might be linear/distance based as well) for the current extruder.
184      *
185      * E values are either in mm or in mm^3
186      * The current extruder is used to determine the filament area to make the conversion.
187      *
188      * \param mm the value to convert
189      * \return the value converted to mm or mm3 depending on whether the E axis is volumetric
190      */
191     double mmToE(double mm);
192 
193     /*!
194      * Convert an E value to a value in mm3 (if it wasn't already in mm3) for the provided extruder.
195      *
196      * E values are either in mm or in mm^3
197      * The given extruder is used to determine the filament area to make the conversion.
198      *
199      * \param e the value to convert
200      * \param extruder Extruder number
201      * \return the value converted to mm3
202      */
203     double eToMm3(double e, size_t extruder);
204 
205 public:
206 
207     GCodeExport();
208     ~GCodeExport();
209 
210     /*
211      * \brief Converts the g-code flavor to a string as it must be printed in
212      * the g-code.
213      * \param flavor The g-code flavor to print.
214      * \return A serialized form of this flavor.
215      */
216     const std::string flavorToString(const EGCodeFlavor& flavor) const;
217 
218     /*!
219      * Get the gcode file header (e.g. ";FLAVOR:UltiGCode\n")
220      *
221      * \param extruder_is_used For each extruder whether it is used in the print
222      * \param print_time The total print time in seconds of the whole gcode (if known)
223      * \param filament_used The total mm^3 filament used for each extruder or a vector of the wrong size of unknown
224      * \param mat_ids The material GUIDs for each material.
225      * \return The string representing the file header
226      */
227     std::string getFileHeader(const std::vector<bool>& extruder_is_used, const Duration* print_time = nullptr, const std::vector<double>& filament_used = std::vector<double>(), const std::vector<std::string>& mat_ids = std::vector<std::string>());
228 
229     void setLayerNr(unsigned int layer_nr);
230 
231     void setOutputStream(std::ostream* stream);
232 
233     bool getExtruderIsUsed(const int extruder_nr) const; //!< return whether the extruder has been used throughout printing all meshgroup up till now
234 
235     Point getGcodePos(const coord_t x, const coord_t y, const int extruder_train) const;
236 
237     void setFlavor(EGCodeFlavor flavor);
238     EGCodeFlavor getFlavor() const;
239 
240     void setZ(int z);
241 
242     void setFlowRateExtrusionSettings(double max_extrusion_offset, double extrusion_offset_factor);
243 
244     /*!
245      * Add extra amount of material to be primed after an unretraction.
246      *
247      * \param extra_prime_distance Amount of material in mm.
248      */
addExtraPrimeAmount(double extra_prime_volume)249     void addExtraPrimeAmount(double extra_prime_volume)
250     {
251         extruder_attr[current_extruder].prime_volume += extra_prime_volume;
252     }
253 
254     Point3 getPosition() const;
255 
256     Point getPositionXY() const;
257 
258     int getPositionZ() const;
259 
260     int getExtruderNr() const;
261 
262     void setFilamentDiameter(size_t extruder, const coord_t diameter);
263 
264     double getCurrentExtrudedVolume() const;
265 
266     /*!
267      * Get the total extruded volume for a specific extruder in mm^3
268      *
269      * Retractions and unretractions don't contribute to this.
270      *
271      * \param extruder_nr The extruder number for which to get the total netto extruded volume
272      * \return total filament printed in mm^3
273      */
274     double getTotalFilamentUsed(size_t extruder_nr);
275 
276     /*!
277      * Get the total estimated print time in seconds for each feature
278      *
279      * \return total print time in seconds for each feature
280      */
281     std::vector<Duration> getTotalPrintTimePerFeature();
282     /*!
283      * Get the total print time in seconds for the complete print
284      *
285      * \return total print time in seconds for the complete print
286      */
287     double getSumTotalPrintTimes();
288     void updateTotalPrintTime();
289     void resetTotalPrintTimeAndFilament();
290 
291     void writeComment(const std::string& comment);
292     void writeTypeComment(const PrintFeatureType& type);
293 
294     /*!
295      * Write an M82 (absolute) or M83 (relative)
296      *
297      * \param set_relative_extrusion_mode If true, write an M83, otherwise write an M82
298      */
299     void writeExtrusionMode(bool set_relative_extrusion_mode);
300 
301     /*!
302      * Write a comment saying what (estimated) time has passed up to this point
303      *
304      * \param time The time passed up till this point
305      */
306     void writeTimeComment(const Duration time);
307 
308     /*!
309      * Write a comment saying that we're starting a certain layer.
310      */
311     void writeLayerComment(const LayerIndex layer_nr);
312 
313     /*!
314      * Write a comment saying that the print has a certain number of layers.
315      */
316     void writeLayerCountComment(const size_t layer_count);
317 
318     void writeLine(const char* line);
319 
320     /*!
321      * Reset the current_e_value to prevent too high E values.
322      *
323      * The current extruded volume is added to the current extruder_attr.
324      */
325     void resetExtrusionValue();
326 
327     void writeDelay(const Duration& time_amount);
328 
329     /*!
330      * Coordinates are build plate coordinates, which might be offsetted when extruder offsets are encoded in the gcode.
331      *
332      * \param p location to go to
333      * \param speed movement speed
334      */
335     void writeTravel(const Point& p, const Velocity& speed);
336 
337     /*!
338      * Coordinates are build plate coordinates, which might be offsetted when extruder offsets are encoded in the gcode.
339      *
340      * \param p location to go to
341      * \param speed movement speed
342      * \param feature the feature that's currently printing
343      * \param update_extrusion_offset whether to update the extrusion offset to match the current flow rate
344      */
345     void writeExtrusion(const Point& p, const Velocity& speed, double extrusion_mm3_per_mm, PrintFeatureType feature, bool update_extrusion_offset = false);
346 
347     /*!
348      * Go to a X/Y location with the z-hopped Z value
349      * Coordinates are build plate coordinates, which might be offsetted when extruder offsets are encoded in the gcode.
350      *
351      * \param p location to go to
352      * \param speed movement speed
353      */
354     void writeTravel(const Point3& p, const Velocity& speed);
355 
356     /*!
357      * Go to a X/Y location with the extrusion Z
358      * Perform un-z-hop
359      * Perform unretraction
360      *
361      * Coordinates are build plate coordinates, which might be offsetted when extruder offsets are encoded in the gcode.
362      *
363      * \param p location to go to
364      * \param speed movement speed
365      * \param feature the feature that's currently printing
366      * \param update_extrusion_offset whether to update the extrusion offset to match the current flow rate
367      */
368     void writeExtrusion(const Point3& p, const Velocity& speed, double extrusion_mm3_per_mm, PrintFeatureType feature, bool update_extrusion_offset = false);
369 private:
370     /*!
371      * Coordinates are build plate coordinates, which might be offsetted when extruder offsets are encoded in the gcode.
372      *
373      * \param x build plate x
374      * \param y build plate y
375      * \param z build plate z
376      * \param speed movement speed
377      */
378     void writeTravel(const coord_t& x, const coord_t& y, const coord_t& z, const Velocity& speed);
379 
380     /*!
381      * Perform un-z-hop
382      * Perform unretract
383      * Write extrusion move
384      * Coordinates are build plate coordinates, which might be offsetted when extruder offsets are encoded in the gcode.
385      *
386      * \param x build plate x
387      * \param y build plate y
388      * \param z build plate z
389      * \param speed movement speed
390      * \param extrusion_mm3_per_mm flow
391      * \param feature the print feature that's currently printing
392      * \param update_extrusion_offset whether to update the extrusion offset to match the current flow rate
393      */
394     void writeExtrusion(const int x, const int y, const int z, const Velocity& speed, const double extrusion_mm3_per_mm, const PrintFeatureType& feature, const bool update_extrusion_offset = false);
395 
396     /*!
397      * Write the F, X, Y, Z and E value (if they are not different from the last)
398      *
399      * convenience function called from writeExtrusion and writeTravel
400      *
401      * This function also applies the gcode offset by calling \ref GCodeExport::getGcodePos
402      * This function updates the \ref GCodeExport::total_bounding_box
403      * It estimates the time in \ref GCodeExport::estimateCalculator for the correct feature
404      * It updates \ref GCodeExport::currentPosition, \ref GCodeExport::current_e_value and \ref GCodeExport::currentSpeed
405      */
406     void writeFXYZE(const Velocity& speed, const int x, const int y, const int z, const double e, const PrintFeatureType& feature);
407 
408     /*!
409      * The writeTravel and/or writeExtrusion when flavor == BFB
410      * \param x build plate x
411      * \param y build plate y
412      * \param z build plate z
413      * \param speed movement speed
414      * \param extrusion_mm3_per_mm flow
415      * \param feature print feature to track print time for
416      */
417     void writeMoveBFB(const int x, const int y, const int z, const Velocity& speed, double extrusion_mm3_per_mm, PrintFeatureType feature);
418 public:
419     /*!
420      * Get ready for extrusion moves:
421      * - unretract (G11 or G1 E.)
422      * - prime blob (G1 E)
423      *
424      * It estimates the time in \ref GCodeExport::estimateCalculator
425      * It updates \ref GCodeExport::current_e_value and \ref GCodeExport::currentSpeed
426      */
427     void writeUnretractionAndPrime();
428     void writeRetraction(const RetractionConfig& config, bool force = false, bool extruder_switch = false);
429 
430     /*!
431      * Start a z hop with the given \p hop_height.
432      *
433      * \param hop_height The height to move above the current layer.
434      * \param speed The speed used for moving.
435      */
436     void writeZhopStart(const coord_t hop_height, Velocity speed = 0);
437 
438     /*!
439      * End a z hop: go back to the layer height
440      *
441      * \param speed The speed used for moving.
442      */
443     void writeZhopEnd(Velocity speed = 0);
444 
445     /*!
446      * Start the new_extruder:
447      * - set new extruder
448      * - zero E value
449      * - write extruder start gcode
450      *
451      * \param new_extruder The extruder to start with
452      */
453     void startExtruder(const size_t new_extruder);
454 
455     /*!
456      * Switch to the new_extruder:
457      * - perform neccessary retractions
458      * - fiddle with E-values
459      * - write extruder end gcode
460      * - set new extruder
461      * - write extruder start gcode
462      *
463      * \param new_extruder The extruder to switch to
464      * \param retraction_config_old_extruder The extruder switch retraction config of the old extruder, to perform the extruder switch retraction with.
465      * \param perform_z_hop The amount by which the print head should be z hopped during extruder switch, or zero if it should not z hop.
466      */
467     void switchExtruder(size_t new_extruder, const RetractionConfig& retraction_config_old_extruder, coord_t perform_z_hop = 0);
468 
469     void writeCode(const char* str);
470 
471     /*!
472      * Write the gcode for priming the current extruder train so that it can be used.
473      *
474      * \param travel_speed The travel speed when priming involves a movement
475      */
476     void writePrimeTrain(const Velocity& travel_speed);
477 
478     /*!
479      * Set the print cooling fan number (used as P parameter to M10[67]) for the specified extruder
480      *
481      * \param extruder The current extruder
482      */
483     void setExtruderFanNumber(int extruder);
484 
485     void writeFanCommand(double speed);
486 
487     void writeTemperatureCommand(const size_t extruder, const Temperature& temperature, const bool wait = false);
488     void writeBedTemperatureCommand(const Temperature& temperature, const bool wait = false);
489     void writeBuildVolumeTemperatureCommand(const Temperature& temperature, const bool wait = false);
490 
491     /*!
492      * Write the command for setting the acceleration for print moves to a specific value
493      */
494     void writePrintAcceleration(const Acceleration& acceleration);
495 
496     /*!
497      * Write the command for setting the acceleration for travel moves to a specific value
498      */
499     void writeTravelAcceleration(const Acceleration& acceleration);
500 
501     /*!
502      * Write the command for setting the jerk to a specific value
503      */
504     void writeJerk(const Velocity& jerk);
505 
506     /*!
507      * Set member variables using the settings in \p settings.
508      */
509     void preSetup(const size_t start_extruder);
510 
511     /*!
512      * Handle the initial (bed/nozzle) temperatures before any gcode is processed.
513      * These temperatures are set in the pre-print setup in the firmware.
514      *
515      * See FffGcodeWriter::processStartingCode
516      * \param start_extruder_nr The extruder with which to start this print
517      */
518     void setInitialAndBuildVolumeTemps(const unsigned int start_extruder_nr);
519 
520     /*!
521      * Override or set an initial nozzle temperature as written by GCodeExport::setInitialTemps
522      * This is used primarily during better specification of temperatures in LayerPlanBuffer::insertPreheatCommand
523      *
524      * \warning This function must be called before any of the layers in the meshgroup are written to file!
525      * That's because it sets the current temperature in the gcode!
526      *
527      * \param extruder_nr The extruder number for which to better specify the temp
528      * \param temp The temp at which the nozzle should be at startup
529      */
530     void setInitialTemp(int extruder_nr, double temp);
531 
532     /*!
533      * Finish the gcode: turn fans off, write end gcode and flush all gcode left in the buffer.
534      *
535      * \param endCode The end gcode to be appended at the very end.
536      */
537     void finalize(const char* endCode);
538 
539     /*!
540      * Get amount of material extruded since last wipe script was inserted.
541      *
542      * \param extruder Extruder number to check.
543      */
544     double getExtrudedVolumeAfterLastWipe(size_t extruder);
545 
546     /*!
547      *  Reset the last_e_value_after_wipe.
548      *
549      * \param extruder Extruder number which last_e_value_after_wipe value to reset.
550      */
551      void ResetLastEValueAfterWipe(size_t extruder);
552 
553     /*!
554      *  Generate g-code for wiping current nozzle using provided config.
555      *
556      * \param wipe_config Config with wipe script settings.
557      */
558     void insertWipeScript(const WipeScriptConfig& wipe_config);
559 };
560 
561 }
562 
563 #endif//GCODEEXPORT_H
564 
565