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