1 //Copyright (c) 2019 Ultimaker B.V.
2 //CuraEngine is released under the terms of the AGPLv3 or higher.
3
4 #include <assert.h>
5 #include <cmath>
6 #include <iomanip>
7 #include <stdarg.h>
8
9 #include "Application.h" //To send layer view data.
10 #include "ExtruderTrain.h"
11 #include "gcodeExport.h"
12 #include "PrintFeature.h"
13 #include "RetractionConfig.h"
14 #include "Slice.h"
15 #include "communication/Communication.h" //To send layer view data.
16 #include "settings/types/LayerIndex.h"
17 #include "utils/Date.h"
18 #include "utils/logoutput.h"
19 #include "utils/string.h" // MMtoStream, PrecisionedDouble
20 #include "WipeScriptConfig.h"
21
22 namespace cura {
23
transliterate(const std::string & text)24 std::string transliterate(const std::string& text)
25 {
26 // For now, just replace all non-ascii characters with '?'.
27 // This function can be expaned if we need more complex transliteration.
28 std::ostringstream stream;
29 for (const char& c : text)
30 {
31 stream << static_cast<char>((c >= 0) ? c : '?');
32 }
33 return stream.str();
34 }
35
GCodeExport()36 GCodeExport::GCodeExport()
37 : output_stream(&std::cout)
38 , currentPosition(0,0,MM2INT(20))
39 , layer_nr(0)
40 , relative_extrusion(false)
41 {
42 *output_stream << std::fixed;
43
44 current_e_value = 0;
45 current_extruder = 0;
46 current_fan_speed = -1;
47
48 total_print_times = std::vector<Duration>(static_cast<unsigned char>(PrintFeatureType::NumPrintFeatureTypes), 0.0);
49
50 currentSpeed = 1;
51 current_print_acceleration = -1;
52 current_travel_acceleration = -1;
53 current_jerk = -1;
54
55 is_z_hopped = 0;
56 setFlavor(EGCodeFlavor::MARLIN);
57 initial_bed_temp = 0;
58 build_volume_temperature = 0;
59 machine_heated_build_volume = false;
60
61 fan_number = 0;
62 use_extruder_offset_to_offset_coords = false;
63 machine_name = "";
64 machine_buildplate_type = "";
65 relative_extrusion = false;
66 new_line = "\n";
67
68 total_bounding_box = AABB3D();
69 }
70
~GCodeExport()71 GCodeExport::~GCodeExport()
72 {
73 }
74
preSetup(const size_t start_extruder)75 void GCodeExport::preSetup(const size_t start_extruder)
76 {
77 current_extruder = start_extruder;
78
79 const Scene& scene = Application::getInstance().current_slice->scene;
80 std::vector<MeshGroup>::iterator mesh_group = scene.current_mesh_group;
81 setFlavor(mesh_group->settings.get<EGCodeFlavor>("machine_gcode_flavor"));
82 use_extruder_offset_to_offset_coords = mesh_group->settings.get<bool>("machine_use_extruder_offset_to_offset_coords");
83 const size_t extruder_count = Application::getInstance().current_slice->scene.extruders.size();
84
85 for (size_t extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++)
86 {
87 const ExtruderTrain& train = scene.extruders[extruder_nr];
88 setFilamentDiameter(extruder_nr, train.settings.get<coord_t>("material_diameter"));
89
90 extruder_attr[extruder_nr].last_retraction_prime_speed = train.settings.get<Velocity>("retraction_prime_speed"); // the alternative would be switch_extruder_prime_speed, but dual extrusion might not even be configured...
91 extruder_attr[extruder_nr].fan_number = train.settings.get<size_t>("machine_extruder_cooling_fan_number");
92 }
93
94 machine_name = mesh_group->settings.get<std::string>("machine_name");
95 machine_buildplate_type = mesh_group->settings.get<std::string>("machine_buildplate_type");
96
97 relative_extrusion = mesh_group->settings.get<bool>("relative_extrusion");
98 always_write_active_tool = mesh_group->settings.get<bool>("machine_always_write_active_tool");
99
100 if (flavor == EGCodeFlavor::BFB)
101 {
102 new_line = "\r\n";
103 }
104 else
105 {
106 new_line = "\n";
107 }
108
109 estimateCalculator.setFirmwareDefaults(mesh_group->settings);
110 }
111
setInitialAndBuildVolumeTemps(const unsigned int start_extruder_nr)112 void GCodeExport::setInitialAndBuildVolumeTemps(const unsigned int start_extruder_nr)
113 {
114 const Scene& scene = Application::getInstance().current_slice->scene;
115 const size_t extruder_count = Application::getInstance().current_slice->scene.extruders.size();
116 for (size_t extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++)
117 {
118 const ExtruderTrain& train = scene.extruders[extruder_nr];
119
120 const Temperature print_temp_0 = train.settings.get<Temperature>("material_print_temperature_layer_0");
121 const Temperature print_temp_here = (print_temp_0 != 0)? print_temp_0 : train.settings.get<Temperature>("material_print_temperature");
122 const Temperature temp = (extruder_nr == start_extruder_nr)? print_temp_here : train.settings.get<Temperature>("material_standby_temperature");
123 setInitialTemp(extruder_nr, temp);
124 }
125
126 initial_bed_temp = scene.current_mesh_group->settings.get<Temperature>("material_bed_temperature_layer_0");
127 machine_heated_build_volume = scene.current_mesh_group->settings.get<bool>("machine_heated_build_volume");
128 build_volume_temperature = machine_heated_build_volume ? scene.current_mesh_group->settings.get<Temperature>("build_volume_temperature") : Temperature(0);
129 }
130
setInitialTemp(int extruder_nr,double temp)131 void GCodeExport::setInitialTemp(int extruder_nr, double temp)
132 {
133 extruder_attr[extruder_nr].initial_temp = temp;
134 if (flavor == EGCodeFlavor::GRIFFIN || flavor == EGCodeFlavor::ULTIGCODE)
135 {
136 extruder_attr[extruder_nr].currentTemperature = temp;
137 }
138 }
139
flavorToString(const EGCodeFlavor & flavor) const140 const std::string GCodeExport::flavorToString(const EGCodeFlavor& flavor) const
141 {
142 switch (flavor)
143 {
144 case EGCodeFlavor::BFB:
145 return "BFB";
146 case EGCodeFlavor::MACH3:
147 return "Mach3";
148 case EGCodeFlavor::MAKERBOT:
149 return "Makerbot";
150 case EGCodeFlavor::ULTIGCODE:
151 return "UltiGCode";
152 case EGCodeFlavor::MARLIN_VOLUMATRIC:
153 return "Marlin(Volumetric)";
154 case EGCodeFlavor::GRIFFIN:
155 return "Griffin";
156 case EGCodeFlavor::REPETIER:
157 return "Repetier";
158 case EGCodeFlavor::REPRAP:
159 return "RepRap";
160 case EGCodeFlavor::MARLIN:
161 default:
162 return "Marlin";
163 }
164 }
165
getFileHeader(const std::vector<bool> & extruder_is_used,const Duration * print_time,const std::vector<double> & filament_used,const std::vector<std::string> & mat_ids)166 std::string GCodeExport::getFileHeader(const std::vector<bool>& extruder_is_used, const Duration* print_time, const std::vector<double>& filament_used, const std::vector<std::string>& mat_ids)
167 {
168 std::ostringstream prefix;
169
170 const size_t extruder_count = Application::getInstance().current_slice->scene.extruders.size();
171 switch (flavor)
172 {
173 case EGCodeFlavor::GRIFFIN:
174 prefix << ";START_OF_HEADER" << new_line;
175 prefix << ";HEADER_VERSION:0.1" << new_line;
176 prefix << ";FLAVOR:" << flavorToString(flavor) << new_line;
177 prefix << ";GENERATOR.NAME:Cura_SteamEngine" << new_line;
178 prefix << ";GENERATOR.VERSION:" << VERSION << new_line;
179 prefix << ";GENERATOR.BUILD_DATE:" << Date::getDate().toStringDashed() << new_line;
180 prefix << ";TARGET_MACHINE.NAME:" << transliterate(machine_name) << new_line;
181
182 for (size_t extr_nr = 0; extr_nr < extruder_count; extr_nr++)
183 {
184 if (!extruder_is_used[extr_nr])
185 {
186 continue;
187 }
188 prefix << ";EXTRUDER_TRAIN." << extr_nr << ".INITIAL_TEMPERATURE:" << extruder_attr[extr_nr].initial_temp << new_line;
189 if (filament_used.size() == extruder_count)
190 {
191 prefix << ";EXTRUDER_TRAIN." << extr_nr << ".MATERIAL.VOLUME_USED:" << static_cast<int>(filament_used[extr_nr]) << new_line;
192 }
193 if (mat_ids.size() == extruder_count && mat_ids[extr_nr] != "")
194 {
195 prefix << ";EXTRUDER_TRAIN." << extr_nr << ".MATERIAL.GUID:" << mat_ids[extr_nr] << new_line;
196 }
197 const Settings& extruder_settings = Application::getInstance().current_slice->scene.extruders[extr_nr].settings;
198 prefix << ";EXTRUDER_TRAIN." << extr_nr << ".NOZZLE.DIAMETER:" << extruder_settings.get<double>("machine_nozzle_size") << new_line;
199 prefix << ";EXTRUDER_TRAIN." << extr_nr << ".NOZZLE.NAME:" << extruder_settings.get<std::string>("machine_nozzle_id") << new_line;
200 }
201 prefix << ";BUILD_PLATE.TYPE:" << machine_buildplate_type << new_line;
202 prefix << ";BUILD_PLATE.INITIAL_TEMPERATURE:" << initial_bed_temp << new_line;
203
204 if (machine_heated_build_volume)
205 {
206 prefix << ";BUILD_VOLUME.TEMPERATURE:" << build_volume_temperature << new_line;
207 }
208
209 if (print_time)
210 {
211 prefix << ";PRINT.TIME:" << static_cast<int>(*print_time) << new_line;
212 }
213
214 prefix << ";PRINT.GROUPS:" << Application::getInstance().current_slice->scene.mesh_groups.size() << new_line;
215
216 if (total_bounding_box.min.x > total_bounding_box.max.x) //We haven't encountered any movement (yet). This probably means we're command-line slicing.
217 {
218 //Put some small default in there.
219 total_bounding_box.min = Point3(0, 0, 0);
220 total_bounding_box.max = Point3(10, 10, 10);
221 }
222 prefix << ";PRINT.SIZE.MIN.X:" << INT2MM(total_bounding_box.min.x) << new_line;
223 prefix << ";PRINT.SIZE.MIN.Y:" << INT2MM(total_bounding_box.min.y) << new_line;
224 prefix << ";PRINT.SIZE.MIN.Z:" << INT2MM(total_bounding_box.min.z) << new_line;
225 prefix << ";PRINT.SIZE.MAX.X:" << INT2MM(total_bounding_box.max.x) << new_line;
226 prefix << ";PRINT.SIZE.MAX.Y:" << INT2MM(total_bounding_box.max.y) << new_line;
227 prefix << ";PRINT.SIZE.MAX.Z:" << INT2MM(total_bounding_box.max.z) << new_line;
228 prefix << ";END_OF_HEADER" << new_line;
229 break;
230 default:
231 prefix << ";FLAVOR:" << flavorToString(flavor) << new_line;
232 prefix << ";TIME:" << ((print_time)? static_cast<int>(*print_time) : 6666) << new_line;
233 if (flavor == EGCodeFlavor::ULTIGCODE)
234 {
235 prefix << ";MATERIAL:" << ((filament_used.size() >= 1)? static_cast<int>(filament_used[0]) : 6666) << new_line;
236 prefix << ";MATERIAL2:" << ((filament_used.size() >= 2)? static_cast<int>(filament_used[1]) : 0) << new_line;
237
238 prefix << ";NOZZLE_DIAMETER:" << Application::getInstance().current_slice->scene.extruders[0].settings.get<double>("machine_nozzle_size") << new_line;
239 }
240 else if (flavor == EGCodeFlavor::REPRAP || flavor == EGCodeFlavor::MARLIN || flavor == EGCodeFlavor::MARLIN_VOLUMATRIC)
241 {
242 prefix << ";Filament used: ";
243 if (filament_used.size() > 0)
244 {
245 for (unsigned i = 0; i < filament_used.size(); ++i)
246 {
247 if (i > 0)
248 {
249 prefix << ", ";
250 }
251 if (flavor != EGCodeFlavor::MARLIN_VOLUMATRIC)
252 {
253 prefix << filament_used[i] / (1000 * extruder_attr[i].filament_area) << "m";
254 }
255 else //Use volumetric filament used.
256 {
257 prefix << filament_used[i] << "mm3";
258 }
259 }
260 }
261 else
262 {
263 prefix << "0m";
264 }
265 prefix << new_line;
266 prefix << ";Layer height: " << Application::getInstance().current_slice->scene.current_mesh_group->settings.get<double>("layer_height") << new_line;
267 }
268 prefix << ";MINX:" << INT2MM(total_bounding_box.min.x) << new_line;
269 prefix << ";MINY:" << INT2MM(total_bounding_box.min.y) << new_line;
270 prefix << ";MINZ:" << INT2MM(total_bounding_box.min.z) << new_line;
271 prefix << ";MAXX:" << INT2MM(total_bounding_box.max.x) << new_line;
272 prefix << ";MAXY:" << INT2MM(total_bounding_box.max.y) << new_line;
273 prefix << ";MAXZ:" << INT2MM(total_bounding_box.max.z) << new_line;
274 }
275
276 return prefix.str();
277 }
278
279
setLayerNr(unsigned int layer_nr_)280 void GCodeExport::setLayerNr(unsigned int layer_nr_) {
281 layer_nr = layer_nr_;
282 }
283
setOutputStream(std::ostream * stream)284 void GCodeExport::setOutputStream(std::ostream* stream)
285 {
286 output_stream = stream;
287 *output_stream << std::fixed;
288 }
289
getExtruderIsUsed(const int extruder_nr) const290 bool GCodeExport::getExtruderIsUsed(const int extruder_nr) const
291 {
292 assert(extruder_nr >= 0);
293 assert(extruder_nr < MAX_EXTRUDERS);
294 return extruder_attr[extruder_nr].is_used;
295 }
296
getGcodePos(const coord_t x,const coord_t y,const int extruder_train) const297 Point GCodeExport::getGcodePos(const coord_t x, const coord_t y, const int extruder_train) const
298 {
299 if (use_extruder_offset_to_offset_coords)
300 {
301 const Settings& extruder_settings = Application::getInstance().current_slice->scene.extruders[extruder_train].settings;
302 return Point(x - extruder_settings.get<coord_t>("machine_nozzle_offset_x"), y - extruder_settings.get<coord_t>("machine_nozzle_offset_y"));
303 }
304 else
305 {
306 return Point(x, y);
307 }
308 }
309
310
setFlavor(EGCodeFlavor flavor)311 void GCodeExport::setFlavor(EGCodeFlavor flavor)
312 {
313 this->flavor = flavor;
314 if (flavor == EGCodeFlavor::MACH3)
315 {
316 for(int n=0; n<MAX_EXTRUDERS; n++)
317 {
318 extruder_attr[n].extruderCharacter = 'A' + n;
319 }
320 }
321 else
322 {
323 for(int n=0; n<MAX_EXTRUDERS; n++)
324 {
325 extruder_attr[n].extruderCharacter = 'E';
326 }
327 }
328 if (flavor == EGCodeFlavor::ULTIGCODE || flavor == EGCodeFlavor::MARLIN_VOLUMATRIC)
329 {
330 is_volumetric = true;
331 }
332 else
333 {
334 is_volumetric = false;
335 }
336 }
337
getFlavor() const338 EGCodeFlavor GCodeExport::getFlavor() const
339 {
340 return flavor;
341 }
342
setZ(int z)343 void GCodeExport::setZ(int z)
344 {
345 current_layer_z = z;
346 }
347
setFlowRateExtrusionSettings(double max_extrusion_offset,double extrusion_offset_factor)348 void GCodeExport::setFlowRateExtrusionSettings(double max_extrusion_offset, double extrusion_offset_factor)
349 {
350 this->max_extrusion_offset = max_extrusion_offset;
351 this->extrusion_offset_factor = extrusion_offset_factor;
352 }
353
getPosition() const354 Point3 GCodeExport::getPosition() const
355 {
356 return currentPosition;
357 }
getPositionXY() const358 Point GCodeExport::getPositionXY() const
359 {
360 return Point(currentPosition.x, currentPosition.y);
361 }
362
getPositionZ() const363 int GCodeExport::getPositionZ() const
364 {
365 return currentPosition.z;
366 }
367
getExtruderNr() const368 int GCodeExport::getExtruderNr() const
369 {
370 return current_extruder;
371 }
372
setFilamentDiameter(const size_t extruder,const coord_t diameter)373 void GCodeExport::setFilamentDiameter(const size_t extruder, const coord_t diameter)
374 {
375 const double r = INT2MM(diameter) / 2.0;
376 const double area = M_PI * r * r;
377 extruder_attr[extruder].filament_area = area;
378 }
379
getCurrentExtrudedVolume() const380 double GCodeExport::getCurrentExtrudedVolume() const
381 {
382 double extrusion_amount = current_e_value;
383 const Settings& extruder_settings = Application::getInstance().current_slice->scene.extruders[current_extruder].settings;
384 if (!extruder_settings.get<bool>("machine_firmware_retract"))
385 { // no E values are changed to perform a retraction
386 extrusion_amount -= extruder_attr[current_extruder].retraction_e_amount_at_e_start; // subtract the increment in E which was used for the first unretraction instead of extrusion
387 extrusion_amount += extruder_attr[current_extruder].retraction_e_amount_current; // add the decrement in E which the filament is behind on extrusion due to the last retraction
388 }
389 if (is_volumetric)
390 {
391 return extrusion_amount;
392 }
393 else
394 {
395 return extrusion_amount * extruder_attr[current_extruder].filament_area;
396 }
397 }
398
eToMm(double e)399 double GCodeExport::eToMm(double e)
400 {
401 if (is_volumetric)
402 {
403 return e / extruder_attr[current_extruder].filament_area;
404 }
405 else
406 {
407 return e;
408 }
409 }
410
mm3ToE(double mm3)411 double GCodeExport::mm3ToE(double mm3)
412 {
413 if (is_volumetric)
414 {
415 return mm3;
416 }
417 else
418 {
419 return mm3 / extruder_attr[current_extruder].filament_area;
420 }
421 }
422
mmToE(double mm)423 double GCodeExport::mmToE(double mm)
424 {
425 if (is_volumetric)
426 {
427 return mm * extruder_attr[current_extruder].filament_area;
428 }
429 else
430 {
431 return mm;
432 }
433 }
434
eToMm3(double e,size_t extruder)435 double GCodeExport::eToMm3(double e, size_t extruder)
436 {
437 if (is_volumetric)
438 {
439 return e;
440 }
441 else
442 {
443 return e * extruder_attr[extruder].filament_area;
444 }
445 }
446
getTotalFilamentUsed(size_t extruder_nr)447 double GCodeExport::getTotalFilamentUsed(size_t extruder_nr)
448 {
449 if (extruder_nr == current_extruder)
450 return extruder_attr[extruder_nr].totalFilament + getCurrentExtrudedVolume();
451 return extruder_attr[extruder_nr].totalFilament;
452 }
453
getTotalPrintTimePerFeature()454 std::vector<Duration> GCodeExport::getTotalPrintTimePerFeature()
455 {
456 return total_print_times;
457 }
458
getSumTotalPrintTimes()459 double GCodeExport::getSumTotalPrintTimes()
460 {
461 double sum = 0.0;
462 for(double item : getTotalPrintTimePerFeature())
463 {
464 sum += item;
465 }
466 return sum;
467 }
468
resetTotalPrintTimeAndFilament()469 void GCodeExport::resetTotalPrintTimeAndFilament()
470 {
471 for(size_t i = 0; i < total_print_times.size(); i++)
472 {
473 total_print_times[i] = 0.0;
474 }
475 for(unsigned int e=0; e<MAX_EXTRUDERS; e++)
476 {
477 extruder_attr[e].totalFilament = 0.0;
478 extruder_attr[e].currentTemperature = 0;
479 extruder_attr[e].waited_for_temperature = false;
480 }
481 current_e_value = 0.0;
482 estimateCalculator.reset();
483 }
484
updateTotalPrintTime()485 void GCodeExport::updateTotalPrintTime()
486 {
487 std::vector<Duration> estimates = estimateCalculator.calculate();
488 for(size_t i = 0; i < estimates.size(); i++)
489 {
490 total_print_times[i] += estimates[i];
491 }
492 estimateCalculator.reset();
493 writeTimeComment(getSumTotalPrintTimes());
494 }
495
writeComment(const std::string & unsanitized_comment)496 void GCodeExport::writeComment(const std::string& unsanitized_comment)
497 {
498 const std::string comment = transliterate(unsanitized_comment);
499
500 *output_stream << ";";
501 for (unsigned int i = 0; i < comment.length(); i++)
502 {
503 if (comment[i] == '\n')
504 {
505 *output_stream << new_line << ";";
506 }
507 else
508 {
509 *output_stream << comment[i];
510 }
511 }
512 *output_stream << new_line;
513 }
514
writeTimeComment(const Duration time)515 void GCodeExport::writeTimeComment(const Duration time)
516 {
517 *output_stream << ";TIME_ELAPSED:" << time << new_line;
518 }
519
writeTypeComment(const PrintFeatureType & type)520 void GCodeExport::writeTypeComment(const PrintFeatureType& type)
521 {
522 switch (type)
523 {
524 case PrintFeatureType::OuterWall:
525 *output_stream << ";TYPE:WALL-OUTER" << new_line;
526 break;
527 case PrintFeatureType::InnerWall:
528 *output_stream << ";TYPE:WALL-INNER" << new_line;
529 break;
530 case PrintFeatureType::Skin:
531 *output_stream << ";TYPE:SKIN" << new_line;
532 break;
533 case PrintFeatureType::Support:
534 *output_stream << ";TYPE:SUPPORT" << new_line;
535 break;
536 case PrintFeatureType::SkirtBrim:
537 *output_stream << ";TYPE:SKIRT" << new_line;
538 break;
539 case PrintFeatureType::Infill:
540 *output_stream << ";TYPE:FILL" << new_line;
541 break;
542 case PrintFeatureType::SupportInfill:
543 *output_stream << ";TYPE:SUPPORT" << new_line;
544 break;
545 case PrintFeatureType::SupportInterface:
546 *output_stream << ";TYPE:SUPPORT-INTERFACE" << new_line;
547 break;
548 case PrintFeatureType::PrimeTower:
549 *output_stream << ";TYPE:PRIME-TOWER" << new_line;
550 break;
551 case PrintFeatureType::MoveCombing:
552 case PrintFeatureType::MoveRetraction:
553 case PrintFeatureType::NoneType:
554 case PrintFeatureType::NumPrintFeatureTypes:
555 // do nothing
556 break;
557 }
558 }
559
560
writeLayerComment(const LayerIndex layer_nr)561 void GCodeExport::writeLayerComment(const LayerIndex layer_nr)
562 {
563 *output_stream << ";LAYER:" << layer_nr << new_line;
564 }
565
writeLayerCountComment(const size_t layer_count)566 void GCodeExport::writeLayerCountComment(const size_t layer_count)
567 {
568 *output_stream << ";LAYER_COUNT:" << layer_count << new_line;
569 }
570
writeLine(const char * line)571 void GCodeExport::writeLine(const char* line)
572 {
573 *output_stream << line << new_line;
574 }
575
writeExtrusionMode(bool set_relative_extrusion_mode)576 void GCodeExport::writeExtrusionMode(bool set_relative_extrusion_mode)
577 {
578 if (set_relative_extrusion_mode)
579 {
580 *output_stream << "M83 ;relative extrusion mode" << new_line;
581 }
582 else
583 {
584 *output_stream << "M82 ;absolute extrusion mode" << new_line;
585 }
586 }
587
resetExtrusionValue()588 void GCodeExport::resetExtrusionValue()
589 {
590 if (!relative_extrusion)
591 {
592 *output_stream << "G92 " << extruder_attr[current_extruder].extruderCharacter << "0" << new_line;
593 }
594 double current_extruded_volume = getCurrentExtrudedVolume();
595 extruder_attr[current_extruder].totalFilament += current_extruded_volume;
596 for (double& extruded_volume_at_retraction : extruder_attr[current_extruder].extruded_volume_at_previous_n_retractions)
597 { // update the extruded_volume_at_previous_n_retractions only of the current extruder, since other extruders don't extrude the current volume
598 extruded_volume_at_retraction -= current_extruded_volume;
599 }
600 current_e_value = 0.0;
601 extruder_attr[current_extruder].retraction_e_amount_at_e_start = extruder_attr[current_extruder].retraction_e_amount_current;
602 }
603
writeDelay(const Duration & time_amount)604 void GCodeExport::writeDelay(const Duration& time_amount)
605 {
606 *output_stream << "G4 P" << int(time_amount * 1000) << new_line;
607 estimateCalculator.addTime(time_amount);
608 }
609
writeTravel(const Point & p,const Velocity & speed)610 void GCodeExport::writeTravel(const Point& p, const Velocity& speed)
611 {
612 writeTravel(Point3(p.X, p.Y, current_layer_z), speed);
613 }
writeExtrusion(const Point & p,const Velocity & speed,double extrusion_mm3_per_mm,PrintFeatureType feature,bool update_extrusion_offset)614 void GCodeExport::writeExtrusion(const Point& p, const Velocity& speed, double extrusion_mm3_per_mm, PrintFeatureType feature, bool update_extrusion_offset)
615 {
616 writeExtrusion(Point3(p.X, p.Y, current_layer_z), speed, extrusion_mm3_per_mm, feature, update_extrusion_offset);
617 }
618
writeTravel(const Point3 & p,const Velocity & speed)619 void GCodeExport::writeTravel(const Point3& p, const Velocity& speed)
620 {
621 if (flavor == EGCodeFlavor::BFB)
622 {
623 writeMoveBFB(p.x, p.y, p.z + is_z_hopped, speed, 0.0, PrintFeatureType::MoveCombing);
624 return;
625 }
626 writeTravel(p.x, p.y, p.z + is_z_hopped, speed);
627 }
628
writeExtrusion(const Point3 & p,const Velocity & speed,double extrusion_mm3_per_mm,PrintFeatureType feature,bool update_extrusion_offset)629 void GCodeExport::writeExtrusion(const Point3& p, const Velocity& speed, double extrusion_mm3_per_mm, PrintFeatureType feature, bool update_extrusion_offset)
630 {
631 if (flavor == EGCodeFlavor::BFB)
632 {
633 writeMoveBFB(p.x, p.y, p.z, speed, extrusion_mm3_per_mm, feature);
634 return;
635 }
636 writeExtrusion(p.x, p.y, p.z, speed, extrusion_mm3_per_mm, feature, update_extrusion_offset);
637 }
638
writeMoveBFB(const int x,const int y,const int z,const Velocity & speed,double extrusion_mm3_per_mm,PrintFeatureType feature)639 void GCodeExport::writeMoveBFB(const int x, const int y, const int z, const Velocity& speed, double extrusion_mm3_per_mm, PrintFeatureType feature)
640 {
641 if (std::isinf(extrusion_mm3_per_mm))
642 {
643 logError("Extrusion rate is infinite!");
644 assert(false && "Infinite extrusion move!");
645 std::exit(1);
646 }
647 if (std::isnan(extrusion_mm3_per_mm))
648 {
649 logError("Extrusion rate is not a number!");
650 assert(false && "NaN extrusion move!");
651 std::exit(1);
652 }
653
654 double extrusion_per_mm = mm3ToE(extrusion_mm3_per_mm);
655
656 Point gcode_pos = getGcodePos(x,y, current_extruder);
657
658 //For Bits From Bytes machines, we need to handle this completely differently. As they do not use E values but RPM values.
659 float fspeed = speed * 60;
660 float rpm = extrusion_per_mm * speed * 60;
661 const float mm_per_rpm = 4.0; //All BFB machines have 4mm per RPM extrusion.
662 rpm /= mm_per_rpm;
663 if (rpm > 0)
664 {
665 if (extruder_attr[current_extruder].retraction_e_amount_current)
666 {
667 if (currentSpeed != double(rpm))
668 {
669 //fprintf(f, "; %f e-per-mm %d mm-width %d mm/s\n", extrusion_per_mm, lineWidth, speed);
670 //fprintf(f, "M108 S%0.1f\r\n", rpm);
671 *output_stream << "M108 S" << PrecisionedDouble{1, rpm} << new_line;
672 currentSpeed = double(rpm);
673 }
674 //Add M101 or M201 to enable the proper extruder.
675 *output_stream << "M" << int((current_extruder + 1) * 100 + 1) << new_line;
676 extruder_attr[current_extruder].retraction_e_amount_current = 0.0;
677 }
678 //Fix the speed by the actual RPM we are asking, because of rounding errors we cannot get all RPM values, but we have a lot more resolution in the feedrate value.
679 // (Trick copied from KISSlicer, thanks Jonathan)
680 fspeed *= (rpm / (roundf(rpm * 100) / 100));
681
682 //Increase the extrusion amount to calculate the amount of filament used.
683 Point3 diff = Point3(x,y,z) - getPosition();
684
685 current_e_value += extrusion_per_mm * diff.vSizeMM();
686 }
687 else
688 {
689 //If we are not extruding, check if we still need to disable the extruder. This causes a retraction due to auto-retraction.
690 if (!extruder_attr[current_extruder].retraction_e_amount_current)
691 {
692 *output_stream << "M103" << new_line;
693 extruder_attr[current_extruder].retraction_e_amount_current = 1.0; // 1.0 used as stub; BFB doesn't use the actual retraction amount; it performs retraction on the firmware automatically
694 }
695 }
696 *output_stream << "G1 X" << MMtoStream{gcode_pos.X} << " Y" << MMtoStream{gcode_pos.Y} << " Z" << MMtoStream{z};
697 *output_stream << " F" << PrecisionedDouble{1, fspeed} << new_line;
698
699 currentPosition = Point3(x, y, z);
700 estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), speed, feature);
701 }
702
writeTravel(const coord_t & x,const coord_t & y,const coord_t & z,const Velocity & speed)703 void GCodeExport::writeTravel(const coord_t& x, const coord_t& y, const coord_t& z, const Velocity& speed)
704 {
705 if (currentPosition.x == x && currentPosition.y == y && currentPosition.z == z)
706 {
707 return;
708 }
709
710 #ifdef ASSERT_INSANE_OUTPUT
711 assert(speed < 400 && speed > 1); // normal F values occurring in UM2 gcode (this code should not be compiled for release)
712 assert(currentPosition != no_point3);
713 assert(Point3(x, y, z) != no_point3);
714 assert((Point3(x,y,z) - currentPosition).vSize() < MM2INT(1000)); // no crazy positions (this code should not be compiled for release)
715 #endif //ASSERT_INSANE_OUTPUT
716
717 const PrintFeatureType travel_move_type = extruder_attr[current_extruder].retraction_e_amount_current ? PrintFeatureType::MoveRetraction : PrintFeatureType::MoveCombing;
718 const int display_width = extruder_attr[current_extruder].retraction_e_amount_current ? MM2INT(0.2) : MM2INT(0.1);
719 const double layer_height = Application::getInstance().current_slice->scene.current_mesh_group->settings.get<double>("layer_height");
720 Application::getInstance().communication->sendLineTo(travel_move_type, Point(x, y), display_width, layer_height, speed);
721
722 *output_stream << "G0";
723 writeFXYZE(speed, x, y, z, current_e_value, travel_move_type);
724 }
725
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)726 void GCodeExport::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)
727 {
728 if (currentPosition.x == x && currentPosition.y == y && currentPosition.z == z)
729 {
730 return;
731 }
732
733 #ifdef ASSERT_INSANE_OUTPUT
734 assert(speed < 400 && speed > 1); // normal F values occurring in UM2 gcode (this code should not be compiled for release)
735 assert(currentPosition != no_point3);
736 assert(Point3(x, y, z) != no_point3);
737 assert((Point3(x,y,z) - currentPosition).vSize() < MM2INT(1000)); // no crazy positions (this code should not be compiled for release)
738 assert(extrusion_mm3_per_mm >= 0.0);
739 #endif //ASSERT_INSANE_OUTPUT
740
741 if (std::isinf(extrusion_mm3_per_mm))
742 {
743 logError("Extrusion rate is infinite!");
744 assert(false && "Infinite extrusion move!");
745 std::exit(1);
746 }
747
748 if (std::isnan(extrusion_mm3_per_mm))
749 {
750 logError("Extrusion rate is not a number!");
751 assert(false && "NaN extrusion move!");
752 std::exit(1);
753 }
754
755 if (extrusion_mm3_per_mm < 0.0)
756 {
757 logWarning("Warning! Negative extrusion move!\n");
758 }
759
760 double extrusion_per_mm = mm3ToE(extrusion_mm3_per_mm);
761
762 if (is_z_hopped > 0)
763 {
764 writeZhopEnd();
765 }
766
767 Point3 diff = Point3(x,y,z) - currentPosition;
768
769 writeUnretractionAndPrime();
770
771 //flow rate compensation
772 double extrusion_offset = 0;
773 if (diff.vSizeMM())
774 {
775 extrusion_offset = speed * extrusion_mm3_per_mm * extrusion_offset_factor;
776 if (extrusion_offset > max_extrusion_offset)
777 {
778 extrusion_offset = max_extrusion_offset;
779 }
780 }
781 // write new value of extrusion_offset, which will be remembered.
782 if (update_extrusion_offset && (extrusion_offset != current_e_offset))
783 {
784 current_e_offset = extrusion_offset;
785 *output_stream << ";FLOW_RATE_COMPENSATED_OFFSET = " << current_e_offset << new_line;
786 }
787
788 extruder_attr[current_extruder].last_e_value_after_wipe += extrusion_per_mm * diff.vSizeMM();
789 double new_e_value = current_e_value + extrusion_per_mm * diff.vSizeMM();
790
791 *output_stream << "G1";
792 writeFXYZE(speed, x, y, z, new_e_value, feature);
793 }
794
writeFXYZE(const Velocity & speed,const int x,const int y,const int z,const double e,const PrintFeatureType & feature)795 void GCodeExport::writeFXYZE(const Velocity& speed, const int x, const int y, const int z, const double e, const PrintFeatureType& feature)
796 {
797 if (currentSpeed != speed)
798 {
799 *output_stream << " F" << PrecisionedDouble{1, speed * 60};
800 currentSpeed = speed;
801 }
802
803 Point gcode_pos = getGcodePos(x, y, current_extruder);
804 total_bounding_box.include(Point3(gcode_pos.X, gcode_pos.Y, z));
805
806 *output_stream << " X" << MMtoStream{gcode_pos.X} << " Y" << MMtoStream{gcode_pos.Y};
807 if (z != currentPosition.z)
808 {
809 *output_stream << " Z" << MMtoStream{z};
810 }
811 if (e + current_e_offset != current_e_value)
812 {
813 const double output_e = (relative_extrusion)? e + current_e_offset - current_e_value : e + current_e_offset;
814 *output_stream << " " << extruder_attr[current_extruder].extruderCharacter << PrecisionedDouble{5, output_e};
815 }
816 *output_stream << new_line;
817
818 currentPosition = Point3(x, y, z);
819 current_e_value = e;
820 estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(x), INT2MM(y), INT2MM(z), eToMm(e)), speed, feature);
821 }
822
writeUnretractionAndPrime()823 void GCodeExport::writeUnretractionAndPrime()
824 {
825 const double prime_volume = extruder_attr[current_extruder].prime_volume;
826 const double prime_volume_e = mm3ToE(prime_volume);
827 current_e_value += prime_volume_e;
828 if (extruder_attr[current_extruder].retraction_e_amount_current)
829 {
830 const Settings& extruder_settings = Application::getInstance().current_slice->scene.extruders[current_extruder].settings;
831 if (extruder_settings.get<bool>("machine_firmware_retract"))
832 { // note that BFB is handled differently
833 *output_stream << "G11" << new_line;
834 //Assume default UM2 retraction settings.
835 if (prime_volume != 0)
836 {
837 const double output_e = (relative_extrusion)? prime_volume_e : current_e_value;
838 *output_stream << "G1 F" << PrecisionedDouble{1, extruder_attr[current_extruder].last_retraction_prime_speed * 60} << " " << extruder_attr[current_extruder].extruderCharacter << PrecisionedDouble{5, output_e} << new_line;
839 currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
840 }
841 estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), 25.0, PrintFeatureType::MoveRetraction);
842 }
843 else
844 {
845 current_e_value += extruder_attr[current_extruder].retraction_e_amount_current;
846 const double output_e = (relative_extrusion)? extruder_attr[current_extruder].retraction_e_amount_current + prime_volume_e : current_e_value;
847 *output_stream << "G1 F" << PrecisionedDouble{1, extruder_attr[current_extruder].last_retraction_prime_speed * 60} << " " << extruder_attr[current_extruder].extruderCharacter << PrecisionedDouble{5, output_e} << new_line;
848 currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
849 estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed, PrintFeatureType::MoveRetraction);
850 }
851 }
852 else if (prime_volume != 0.0)
853 {
854 const double output_e = (relative_extrusion)? prime_volume_e : current_e_value;
855 *output_stream << "G1 F" << PrecisionedDouble{1, extruder_attr[current_extruder].last_retraction_prime_speed * 60} << " " << extruder_attr[current_extruder].extruderCharacter;
856 *output_stream << PrecisionedDouble{5, output_e} << new_line;
857 currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
858 estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed, PrintFeatureType::NoneType);
859 }
860 extruder_attr[current_extruder].prime_volume = 0.0;
861
862 if (getCurrentExtrudedVolume() > 10000.0 && flavor != EGCodeFlavor::BFB && flavor != EGCodeFlavor::MAKERBOT) //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure.
863 {
864 resetExtrusionValue();
865 }
866 if (extruder_attr[current_extruder].retraction_e_amount_current)
867 {
868 extruder_attr[current_extruder].retraction_e_amount_current = 0.0;
869 }
870 }
871
writeRetraction(const RetractionConfig & config,bool force,bool extruder_switch)872 void GCodeExport::writeRetraction(const RetractionConfig& config, bool force, bool extruder_switch)
873 {
874 ExtruderTrainAttributes& extr_attr = extruder_attr[current_extruder];
875
876 if (flavor == EGCodeFlavor::BFB)//BitsFromBytes does automatic retraction.
877 {
878 if (extruder_switch)
879 {
880 if (!extr_attr.retraction_e_amount_current)
881 *output_stream << "M103" << new_line;
882
883 extr_attr.retraction_e_amount_current = 1.0; // 1.0 is a stub; BFB doesn't use the actual retracted amount; retraction is performed by firmware
884 }
885 return;
886 }
887
888 double old_retraction_e_amount = extr_attr.retraction_e_amount_current;
889 double new_retraction_e_amount = mmToE(config.distance);
890 double retraction_diff_e_amount = old_retraction_e_amount - new_retraction_e_amount;
891 if (std::abs(retraction_diff_e_amount) < 0.000001)
892 {
893 return;
894 }
895
896 { // handle retraction limitation
897 double current_extruded_volume = getCurrentExtrudedVolume();
898 std::deque<double>& extruded_volume_at_previous_n_retractions = extr_attr.extruded_volume_at_previous_n_retractions;
899 while (extruded_volume_at_previous_n_retractions.size() > config.retraction_count_max && !extruded_volume_at_previous_n_retractions.empty())
900 {
901 // extruder switch could have introduced data which falls outside the retraction window
902 // also the retraction_count_max could have changed between the last retraction and this
903 extruded_volume_at_previous_n_retractions.pop_back();
904 }
905 if (!force && config.retraction_count_max <= 0)
906 {
907 return;
908 }
909 if (!force && extruded_volume_at_previous_n_retractions.size() == config.retraction_count_max
910 && current_extruded_volume < extruded_volume_at_previous_n_retractions.back() + config.retraction_extrusion_window * extr_attr.filament_area)
911 {
912 return;
913 }
914 extruded_volume_at_previous_n_retractions.push_front(current_extruded_volume);
915 if (extruded_volume_at_previous_n_retractions.size() == config.retraction_count_max + 1)
916 {
917 extruded_volume_at_previous_n_retractions.pop_back();
918 }
919 }
920
921 const Settings& extruder_settings = Application::getInstance().current_slice->scene.extruders[current_extruder].settings;
922 if (extruder_settings.get<bool>("machine_firmware_retract"))
923 {
924 if (extruder_switch && extr_attr.retraction_e_amount_current)
925 {
926 return;
927 }
928 *output_stream << "G10";
929 if (extruder_switch && flavor == EGCodeFlavor::REPETIER)
930 {
931 *output_stream << " S1";
932 }
933 *output_stream << new_line;
934 //Assume default UM2 retraction settings.
935 estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value + retraction_diff_e_amount)), 25, PrintFeatureType::MoveRetraction); // TODO: hardcoded values!
936 }
937 else
938 {
939 double speed = ((retraction_diff_e_amount < 0.0)? config.speed : extr_attr.last_retraction_prime_speed) * 60;
940 current_e_value += retraction_diff_e_amount;
941 const double output_e = (relative_extrusion)? retraction_diff_e_amount : current_e_value;
942 *output_stream << "G1 F" << PrecisionedDouble{1, speed} << " " << extr_attr.extruderCharacter << PrecisionedDouble{5, output_e} << new_line;
943 currentSpeed = speed;
944 estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed, PrintFeatureType::MoveRetraction);
945 extr_attr.last_retraction_prime_speed = config.primeSpeed;
946 }
947
948 extr_attr.retraction_e_amount_current = new_retraction_e_amount; // suppose that for UM2 the retraction amount in the firmware is equal to the provided amount
949 extr_attr.prime_volume += config.prime_volume;
950
951 }
952
writeZhopStart(const coord_t hop_height,Velocity speed)953 void GCodeExport::writeZhopStart(const coord_t hop_height, Velocity speed/*= 0*/)
954 {
955 if (hop_height > 0)
956 {
957 if (speed == 0)
958 {
959 const ExtruderTrain& extruder = Application::getInstance().current_slice->scene.extruders[current_extruder];
960 speed = extruder.settings.get<Velocity>("speed_z_hop");
961 }
962 is_z_hopped = hop_height;
963 currentSpeed = speed;
964 *output_stream << "G1 F" << PrecisionedDouble{1, speed * 60} << " Z" << MMtoStream{current_layer_z + is_z_hopped} << new_line;
965 total_bounding_box.includeZ(current_layer_z + is_z_hopped);
966 assert(speed > 0.0 && "Z hop speed should be positive.");
967 }
968 }
969
writeZhopEnd(Velocity speed)970 void GCodeExport::writeZhopEnd(Velocity speed/*= 0*/)
971 {
972 if (is_z_hopped)
973 {
974 if (speed == 0)
975 {
976 const ExtruderTrain& extruder = Application::getInstance().current_slice->scene.extruders[current_extruder];
977 speed = extruder.settings.get<Velocity>("speed_z_hop");
978 }
979 is_z_hopped = 0;
980 currentPosition.z = current_layer_z;
981 currentSpeed = speed;
982 *output_stream << "G1 F" << PrecisionedDouble{1, speed * 60} << " Z" << MMtoStream{current_layer_z} << new_line;
983 assert(speed > 0.0 && "Z hop speed should be positive.");
984 }
985 }
986
startExtruder(const size_t new_extruder)987 void GCodeExport::startExtruder(const size_t new_extruder)
988 {
989 extruder_attr[new_extruder].is_used = true;
990 if (new_extruder != current_extruder) // wouldn't be the case on the very first extruder start if it's extruder 0
991 {
992 if (flavor == EGCodeFlavor::MAKERBOT)
993 {
994 *output_stream << "M135 T" << new_extruder << new_line;
995 }
996 else
997 {
998 *output_stream << "T" << new_extruder << new_line;
999 }
1000 }
1001
1002 current_extruder = new_extruder;
1003
1004 assert(getCurrentExtrudedVolume() == 0.0 && "Just after an extruder switch we haven't extruded anything yet!");
1005 resetExtrusionValue(); // zero the E value on the new extruder, just to be sure
1006
1007 const std::string start_code = Application::getInstance().current_slice->scene.extruders[new_extruder].settings.get<std::string>("machine_extruder_start_code");
1008
1009 if(!start_code.empty())
1010 {
1011 if (relative_extrusion)
1012 {
1013 writeExtrusionMode(false); // ensure absolute extrusion mode is set before the start gcode
1014 }
1015
1016 writeCode(start_code.c_str());
1017
1018 if (relative_extrusion)
1019 {
1020 writeExtrusionMode(true); // restore relative extrusion mode
1021 }
1022 }
1023
1024 Application::getInstance().communication->setExtruderForSend(Application::getInstance().current_slice->scene.extruders[new_extruder]);
1025 Application::getInstance().communication->sendCurrentPosition(getPositionXY());
1026
1027 //Change the Z position so it gets re-written again. We do not know if the switch code modified the Z position.
1028 currentPosition.z += 1;
1029
1030 setExtruderFanNumber(new_extruder);
1031 }
1032
switchExtruder(size_t new_extruder,const RetractionConfig & retraction_config_old_extruder,coord_t perform_z_hop)1033 void GCodeExport::switchExtruder(size_t new_extruder, const RetractionConfig& retraction_config_old_extruder, coord_t perform_z_hop /*= 0*/)
1034 {
1035 if (current_extruder == new_extruder)
1036 {
1037 return;
1038 }
1039
1040 const Settings& old_extruder_settings = Application::getInstance().current_slice->scene.extruders[current_extruder].settings;
1041 if(old_extruder_settings.get<bool>("retraction_enable"))
1042 {
1043 constexpr bool force = true;
1044 constexpr bool extruder_switch = true;
1045 writeRetraction(retraction_config_old_extruder, force, extruder_switch);
1046 }
1047
1048 if (perform_z_hop > 0)
1049 {
1050 writeZhopStart(perform_z_hop);
1051 }
1052
1053 resetExtrusionValue(); // zero the E value on the old extruder, so that the current_e_value is registered on the old extruder
1054
1055 const std::string end_code = old_extruder_settings.get<std::string>("machine_extruder_end_code");
1056
1057 if(!end_code.empty())
1058 {
1059 if (relative_extrusion)
1060 {
1061 writeExtrusionMode(false); // ensure absolute extrusion mode is set before the end gcode
1062 }
1063
1064 writeCode(end_code.c_str());
1065
1066 if (relative_extrusion)
1067 {
1068 writeExtrusionMode(true); // restore relative extrusion mode
1069 }
1070 }
1071
1072 startExtruder(new_extruder);
1073 }
1074
writeCode(const char * str)1075 void GCodeExport::writeCode(const char* str)
1076 {
1077 *output_stream << str << new_line;
1078 }
1079
writePrimeTrain(const Velocity & travel_speed)1080 void GCodeExport::writePrimeTrain(const Velocity& travel_speed)
1081 {
1082 if (extruder_attr[current_extruder].is_primed)
1083 { // extruder is already primed once!
1084 return;
1085 }
1086 const Settings& extruder_settings = Application::getInstance().current_slice->scene.extruders[current_extruder].settings;
1087 if (extruder_settings.get<bool>("prime_blob_enable"))
1088 { // only move to prime position if we do a blob/poop
1089 // ideally the prime position would be respected whether we do a blob or not,
1090 // but the frontend currently doesn't support a value function of an extruder setting depending on an fdmprinter setting,
1091 // which is needed to automatically ignore the prime position for the printer when blob is disabled
1092 Point3 prime_pos(extruder_settings.get<coord_t>("extruder_prime_pos_x"), extruder_settings.get<coord_t>("extruder_prime_pos_y"), extruder_settings.get<coord_t>("extruder_prime_pos_z"));
1093 if (!extruder_settings.get<bool>("extruder_prime_pos_abs"))
1094 {
1095 // currentPosition.z can be already z hopped
1096 prime_pos += Point3(currentPosition.x, currentPosition.y, current_layer_z);
1097 }
1098 writeTravel(prime_pos, travel_speed);
1099 }
1100
1101 if (flavor == EGCodeFlavor::GRIFFIN)
1102 {
1103 bool should_correct_z = false;
1104
1105 std::string command = "G280";
1106 if (!extruder_settings.get<bool>("prime_blob_enable"))
1107 {
1108 command += " S1"; // use S1 to disable prime blob
1109 should_correct_z = true;
1110 }
1111 *output_stream << command << new_line;
1112
1113 // There was an issue with the S1 strategy parameter, where it would only change the material-position,
1114 // as opposed to 'be a prime-blob maneuvre without actually printing the prime blob', as we assumed here.
1115 // After a chat, the firmware-team decided to change the S1 strategy behaviour,
1116 // but since people don't update their firmware at each opportunity, it was decided to fix it here as well.
1117 if (should_correct_z)
1118 {
1119 // Can't output via 'writeTravel', since if this is needed, the value saved for 'current height' will not be correct.
1120 // For similar reasons, this isn't written to the front-end via command-socket.
1121 *output_stream << "G0 Z" << MMtoStream{getPositionZ()} << new_line;
1122 }
1123 }
1124 else
1125 {
1126 // there is no prime gcode for other firmware versions...
1127 }
1128
1129 extruder_attr[current_extruder].is_primed = true;
1130 }
1131
setExtruderFanNumber(int extruder)1132 void GCodeExport::setExtruderFanNumber(int extruder)
1133 {
1134 if (extruder_attr[extruder].fan_number != fan_number)
1135 {
1136 fan_number = extruder_attr[extruder].fan_number;
1137 current_fan_speed = -1; // ensure fan speed gcode gets output for this fan
1138 }
1139 }
1140
writeFanCommand(double speed)1141 void GCodeExport::writeFanCommand(double speed)
1142 {
1143 if (std::abs(current_fan_speed - speed) < 0.1)
1144 {
1145 return;
1146 }
1147 if(flavor == EGCodeFlavor::MAKERBOT)
1148 {
1149 if(speed >= 50)
1150 {
1151 *output_stream << "M126 T0" << new_line; //Makerbot cannot PWM the fan speed...
1152 }
1153 else
1154 {
1155 *output_stream << "M127 T0" << new_line;
1156 }
1157 }
1158 else if (speed > 0)
1159 {
1160 *output_stream << "M106 S" << PrecisionedDouble{1, speed * 255 / 100};
1161 if (fan_number)
1162 {
1163 *output_stream << " P" << fan_number;
1164 }
1165 *output_stream << new_line;
1166 }
1167 else
1168 {
1169 *output_stream << "M107";
1170 if (fan_number)
1171 {
1172 *output_stream << " P" << fan_number;
1173 }
1174 *output_stream << new_line;
1175 }
1176
1177 current_fan_speed = speed;
1178 }
1179
writeTemperatureCommand(const size_t extruder,const Temperature & temperature,const bool wait)1180 void GCodeExport::writeTemperatureCommand(const size_t extruder, const Temperature& temperature, const bool wait)
1181 {
1182 const ExtruderTrain& extruder_train = Application::getInstance().current_slice->scene.extruders[extruder];
1183
1184 if (!extruder_train.settings.get<bool>("machine_nozzle_temp_enabled"))
1185 {
1186 return;
1187 }
1188
1189 if (extruder_train.settings.get<bool>("machine_extruders_share_heater"))
1190 {
1191 // extruders share a single heater
1192 if (extruder != current_extruder)
1193 {
1194 // ignore all changes to the non-current extruder
1195 return;
1196 }
1197
1198 // sync all extruders with the change to the current extruder
1199 const size_t extruder_count = Application::getInstance().current_slice->scene.extruders.size();
1200
1201 for (size_t extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++)
1202 {
1203 if (extruder_nr != extruder)
1204 {
1205 // only reset the other extruders' waited_for_temperature state when the new temperature
1206 // is greater than the old temperature
1207 if (wait || temperature > extruder_attr[extruder_nr].currentTemperature)
1208 {
1209 extruder_attr[extruder_nr].waited_for_temperature = wait;
1210 }
1211 extruder_attr[extruder_nr].currentTemperature = temperature;
1212 }
1213 }
1214 }
1215
1216 if ((!wait || extruder_attr[extruder].waited_for_temperature) && extruder_attr[extruder].currentTemperature == temperature)
1217 {
1218 return;
1219 }
1220
1221 if (wait && flavor != EGCodeFlavor::MAKERBOT)
1222 {
1223 if(flavor == EGCodeFlavor::MARLIN)
1224 {
1225 *output_stream << "M105" << new_line; // get temperatures from the last update, the M109 will not let get the target temperature
1226 }
1227 *output_stream << "M109";
1228 extruder_attr[extruder].waited_for_temperature = true;
1229 }
1230 else
1231 {
1232 *output_stream << "M104";
1233 extruder_attr[extruder].waited_for_temperature = false;
1234 }
1235 if (extruder != current_extruder)
1236 {
1237 *output_stream << " T" << extruder;
1238 }
1239 #ifdef ASSERT_INSANE_OUTPUT
1240 assert(temperature >= 0);
1241 #endif // ASSERT_INSANE_OUTPUT
1242 *output_stream << " S" << PrecisionedDouble{1, temperature} << new_line;
1243 if (extruder != current_extruder && always_write_active_tool)
1244 {
1245 //Some firmwares (ie Smoothieware) change tools every time a "T" command is read - even on a M104 line, so we need to switch back to the active tool.
1246 *output_stream << "T" << current_extruder << new_line;
1247 }
1248 if (wait && flavor == EGCodeFlavor::MAKERBOT)
1249 {
1250 //Makerbot doesn't use M109 for heat-and-wait. Instead, use M104 and then wait using M116.
1251 *output_stream << "M116" << new_line;
1252 }
1253 extruder_attr[extruder].currentTemperature = temperature;
1254 }
1255
writeBedTemperatureCommand(const Temperature & temperature,const bool wait)1256 void GCodeExport::writeBedTemperatureCommand(const Temperature& temperature, const bool wait)
1257 {
1258 if (flavor == EGCodeFlavor::ULTIGCODE)
1259 { // The UM2 family doesn't support temperature commands (they are fixed in the firmware)
1260 return;
1261 }
1262
1263 if (wait)
1264 {
1265 if(flavor == EGCodeFlavor::MARLIN)
1266 {
1267 *output_stream << "M140 S"; // set the temperature, it will be used as target temperature from M105
1268 *output_stream << PrecisionedDouble{1, temperature} << new_line;
1269 *output_stream << "M105" << new_line;
1270 }
1271
1272 *output_stream << "M190 S";
1273 }
1274 else
1275 *output_stream << "M140 S";
1276 *output_stream << PrecisionedDouble{1, temperature} << new_line;
1277 }
1278
writeBuildVolumeTemperatureCommand(const Temperature & temperature,const bool wait)1279 void GCodeExport::writeBuildVolumeTemperatureCommand(const Temperature& temperature, const bool wait)
1280 {
1281 if (flavor == EGCodeFlavor::ULTIGCODE || flavor == EGCodeFlavor::GRIFFIN)
1282 {
1283 //Ultimaker printers don't support build volume temperature commands.
1284 return;
1285 }
1286 if (wait)
1287 {
1288 *output_stream << "M191 S";
1289 }
1290 else
1291 {
1292 *output_stream << "M141 S";
1293 }
1294 *output_stream << PrecisionedDouble{1, temperature} << new_line;
1295 }
1296
writePrintAcceleration(const Acceleration & acceleration)1297 void GCodeExport::writePrintAcceleration(const Acceleration& acceleration)
1298 {
1299 switch (getFlavor())
1300 {
1301 case EGCodeFlavor::REPETIER:
1302 if (current_print_acceleration != acceleration)
1303 {
1304 *output_stream << "M201 X" << PrecisionedDouble{0, acceleration} << " Y" << PrecisionedDouble{0, acceleration} << new_line;
1305 }
1306 break;
1307 case EGCodeFlavor::REPRAP:
1308 if (current_print_acceleration != acceleration)
1309 {
1310 *output_stream << "M204 P" << PrecisionedDouble{0, acceleration} << new_line;
1311 }
1312 break;
1313 default:
1314 // MARLIN, etc. only have one acceleration for both print and travel
1315 if (current_print_acceleration != acceleration)
1316 {
1317 *output_stream << "M204 S" << PrecisionedDouble{0, acceleration} << new_line;
1318 }
1319 break;
1320 }
1321 current_print_acceleration = acceleration;
1322 estimateCalculator.setAcceleration(acceleration);
1323 }
1324
writeTravelAcceleration(const Acceleration & acceleration)1325 void GCodeExport::writeTravelAcceleration(const Acceleration& acceleration)
1326 {
1327 switch (getFlavor())
1328 {
1329 case EGCodeFlavor::REPETIER:
1330 if (current_travel_acceleration != acceleration)
1331 {
1332 *output_stream << "M202 X" << PrecisionedDouble{0, acceleration} << " Y" << PrecisionedDouble{0, acceleration} << new_line;
1333 }
1334 break;
1335 case EGCodeFlavor::REPRAP:
1336 if (current_travel_acceleration != acceleration)
1337 {
1338 *output_stream << "M204 T" << PrecisionedDouble{0, acceleration} << new_line;
1339 }
1340 break;
1341 default:
1342 // MARLIN, etc. only have one acceleration for both print and travel
1343 writePrintAcceleration(acceleration);
1344 break;
1345 }
1346 current_travel_acceleration = acceleration;
1347 estimateCalculator.setAcceleration(acceleration);
1348 }
1349
writeJerk(const Velocity & jerk)1350 void GCodeExport::writeJerk(const Velocity& jerk)
1351 {
1352 if (current_jerk != jerk)
1353 {
1354 switch (getFlavor())
1355 {
1356 case EGCodeFlavor::REPETIER:
1357 *output_stream << "M207 X" << PrecisionedDouble{2, jerk} << new_line;
1358 break;
1359 case EGCodeFlavor::REPRAP:
1360 *output_stream << "M566 X" << PrecisionedDouble{2, jerk * 60} << " Y" << PrecisionedDouble{2, jerk * 60} << new_line;
1361 break;
1362 default:
1363 *output_stream << "M205 X" << PrecisionedDouble{2, jerk} << " Y" << PrecisionedDouble{2, jerk} << new_line;
1364 break;
1365 }
1366 current_jerk = jerk;
1367 estimateCalculator.setMaxXyJerk(jerk);
1368 }
1369 }
1370
finalize(const char * endCode)1371 void GCodeExport::finalize(const char* endCode)
1372 {
1373 writeFanCommand(0);
1374 writeCode(endCode);
1375 int64_t print_time = getSumTotalPrintTimes();
1376 int mat_0 = getTotalFilamentUsed(0);
1377 log("Print time (s): %d\n", print_time);
1378 log("Print time (hr|min|s): %dh %dm %ds\n", int(print_time / 60 / 60), int((print_time / 60) % 60), int(print_time % 60));
1379 log("Filament (mm^3): %d\n", mat_0);
1380 for(int n=1; n<MAX_EXTRUDERS; n++)
1381 if (getTotalFilamentUsed(n) > 0)
1382 log("Filament%d: %d\n", n + 1, int(getTotalFilamentUsed(n)));
1383 output_stream->flush();
1384 }
1385
getExtrudedVolumeAfterLastWipe(size_t extruder)1386 double GCodeExport::getExtrudedVolumeAfterLastWipe(size_t extruder)
1387 {
1388 return eToMm3(extruder_attr[extruder].last_e_value_after_wipe, extruder);
1389 }
1390
ResetLastEValueAfterWipe(size_t extruder)1391 void GCodeExport::ResetLastEValueAfterWipe(size_t extruder)
1392 {
1393 extruder_attr[extruder].last_e_value_after_wipe = 0;
1394 }
1395
insertWipeScript(const WipeScriptConfig & wipe_config)1396 void GCodeExport::insertWipeScript(const WipeScriptConfig& wipe_config)
1397 {
1398 const Point3 prev_position = currentPosition;
1399 writeComment("WIPE_SCRIPT_BEGIN");
1400
1401 if (wipe_config.retraction_enable)
1402 {
1403 writeRetraction(wipe_config.retraction_config);
1404 }
1405
1406 if (wipe_config.hop_enable)
1407 {
1408 writeZhopStart(wipe_config.hop_amount, wipe_config.hop_speed);
1409 }
1410
1411 writeTravel(Point(wipe_config.brush_pos_x, currentPosition.y), wipe_config.move_speed);
1412 for (size_t i = 0; i < wipe_config.repeat_count; ++i)
1413 {
1414 coord_t x = currentPosition.x + (i % 2 ? -wipe_config.move_distance : wipe_config.move_distance);
1415 writeTravel(Point(x, currentPosition.y), wipe_config.move_speed);
1416 }
1417
1418 writeTravel(prev_position, wipe_config.move_speed);
1419
1420 if (wipe_config.hop_enable)
1421 {
1422 writeZhopEnd(wipe_config.hop_speed);
1423 }
1424
1425 if (wipe_config.retraction_enable)
1426 {
1427 writeUnretractionAndPrime();
1428 }
1429
1430 if (wipe_config.pause > 0)
1431 {
1432 writeDelay(wipe_config.pause);
1433 }
1434
1435 writeComment("WIPE_SCRIPT_END");
1436 }
1437
1438 }//namespace cura
1439
1440