1 #ifndef slic3r_GCodeProcessor_hpp_
2 #define slic3r_GCodeProcessor_hpp_
3 
4 #include "libslic3r/GCodeReader.hpp"
5 #include "libslic3r/Point.hpp"
6 #include "libslic3r/ExtrusionEntity.hpp"
7 #include "libslic3r/PrintConfig.hpp"
8 #include "libslic3r/CustomGCode.hpp"
9 
10 #include <cstdint>
11 #include <array>
12 #include <vector>
13 #include <string>
14 #include <string_view>
15 
16 namespace Slic3r {
17 
18     enum class EMoveType : unsigned char
19     {
20         Noop,
21         Retract,
22         Unretract,
23         Tool_change,
24         Color_change,
25         Pause_Print,
26         Custom_GCode,
27         Travel,
28         Wipe,
29         Extrude,
30         Count
31     };
32 
33     struct PrintEstimatedTimeStatistics
34     {
35         enum class ETimeMode : unsigned char
36         {
37             Normal,
38             Stealth,
39             Count
40         };
41 
42         struct Mode
43         {
44             float time;
45             std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> custom_gcode_times;
46             std::vector<std::pair<EMoveType, float>> moves_times;
47             std::vector<std::pair<ExtrusionRole, float>> roles_times;
48             std::vector<float> layers_times;
49 
resetSlic3r::PrintEstimatedTimeStatistics::Mode50             void reset() {
51                 time = 0.0f;
52                 custom_gcode_times.clear();
53                 moves_times.clear();
54                 roles_times.clear();
55                 layers_times.clear();
56             }
57         };
58 
59         std::array<Mode, static_cast<size_t>(ETimeMode::Count)> modes;
60 
PrintEstimatedTimeStatisticsSlic3r::PrintEstimatedTimeStatistics61         PrintEstimatedTimeStatistics() { reset(); }
62 
resetSlic3r::PrintEstimatedTimeStatistics63         void reset() {
64             for (auto m : modes) {
65                 m.reset();
66             }
67         }
68     };
69 
70     class GCodeProcessor
71     {
72     public:
73         static const std::string Extrusion_Role_Tag;
74         static const std::string Wipe_Start_Tag;
75         static const std::string Wipe_End_Tag;
76         static const std::string Height_Tag;
77         static const std::string Layer_Change_Tag;
78         static const std::string Color_Change_Tag;
79         static const std::string Pause_Print_Tag;
80         static const std::string Custom_Code_Tag;
81         static const std::string First_Line_M73_Placeholder_Tag;
82         static const std::string Last_Line_M73_Placeholder_Tag;
83         static const std::string Estimated_Printing_Time_Placeholder_Tag;
84 
85         static const float Wipe_Width;
86         static const float Wipe_Height;
87 
88 #if ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
89         static const std::string Width_Tag;
90 #endif // ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
91 #if ENABLE_GCODE_VIEWER_DATA_CHECKING
92 #if !ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
93         static const std::string Width_Tag;
94 #endif // !ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
95         static const std::string Mm3_Per_Mm_Tag;
96 #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
97 
98     private:
99         using AxisCoords = std::array<float, 4>;
100         using ExtruderColors = std::vector<unsigned char>;
101 
102         enum class EUnits : unsigned char
103         {
104             Millimeters,
105             Inches
106         };
107 
108         enum class EPositioningType : unsigned char
109         {
110             Absolute,
111             Relative
112         };
113 
114         struct CachedPosition
115         {
116             AxisCoords position; // mm
117             float feedrate; // mm/s
118 
119             void reset();
120         };
121 
122         struct CpColor
123         {
124             unsigned char counter;
125             unsigned char current;
126 
127             void reset();
128         };
129 
130     public:
131         struct FeedrateProfile
132         {
133             float entry{ 0.0f }; // mm/s
134             float cruise{ 0.0f }; // mm/s
135             float exit{ 0.0f }; // mm/s
136         };
137 
138         struct Trapezoid
139         {
140             float accelerate_until{ 0.0f }; // mm
141             float decelerate_after{ 0.0f }; // mm
142             float cruise_feedrate{ 0.0f }; // mm/sec
143 
144             float acceleration_time(float entry_feedrate, float acceleration) const;
145             float cruise_time() const;
146             float deceleration_time(float distance, float acceleration) const;
147             float cruise_distance() const;
148         };
149 
150         struct TimeBlock
151         {
152             struct Flags
153             {
154                 bool recalculate{ false };
155                 bool nominal_length{ false };
156             };
157 
158             EMoveType move_type{ EMoveType::Noop };
159             ExtrusionRole role{ erNone };
160             unsigned int g1_line_id{ 0 };
161             unsigned int layer_id{ 0 };
162             float distance{ 0.0f }; // mm
163             float acceleration{ 0.0f }; // mm/s^2
164             float max_entry_speed{ 0.0f }; // mm/s
165             float safe_feedrate{ 0.0f }; // mm/s
166             Flags flags;
167             FeedrateProfile feedrate_profile;
168             Trapezoid trapezoid;
169 
170             // Calculates this block's trapezoid
171             void calculate_trapezoid();
172 
173             float time() const;
174         };
175 
176     private:
177         struct TimeMachine
178         {
179             struct State
180             {
181                 float feedrate; // mm/s
182                 float safe_feedrate; // mm/s
183                 AxisCoords axis_feedrate; // mm/s
184                 AxisCoords abs_axis_feedrate; // mm/s
185 
186                 void reset();
187             };
188 
189             struct CustomGCodeTime
190             {
191                 bool needed;
192                 float cache;
193                 std::vector<std::pair<CustomGCode::Type, float>> times;
194 
195                 void reset();
196             };
197 
198             struct G1LinesCacheItem
199             {
200                 unsigned int id;
201                 float elapsed_time;
202             };
203 
204             bool enabled;
205             float acceleration; // mm/s^2
206             // hard limit for the acceleration, to which the firmware will clamp.
207             float max_acceleration; // mm/s^2
208             float extrude_factor_override_percentage;
209             float time; // s
210             std::string line_m73_mask;
211             State curr;
212             State prev;
213             CustomGCodeTime gcode_time;
214             std::vector<TimeBlock> blocks;
215             std::vector<G1LinesCacheItem> g1_times_cache;
216             std::array<float, static_cast<size_t>(EMoveType::Count)> moves_time;
217             std::array<float, static_cast<size_t>(ExtrusionRole::erCount)> roles_time;
218             std::vector<float> layers_time;
219 
220             void reset();
221 
222             // Simulates firmware st_synchronize() call
223             void simulate_st_synchronize(float additional_time = 0.0f);
224             void calculate_time(size_t keep_last_n_blocks = 0);
225         };
226 
227         struct TimeProcessor
228         {
229             struct Planner
230             {
231                 // Size of the firmware planner queue. The old 8-bit Marlins usually just managed 16 trapezoidal blocks.
232                 // Let's be conservative and plan for newer boards with more memory.
233                 static constexpr size_t queue_size = 64;
234                 // The firmware recalculates last planner_queue_size trapezoidal blocks each time a new block is added.
235                 // We are not simulating the firmware exactly, we calculate a sequence of blocks once a reasonable number of blocks accumulate.
236                 static constexpr size_t refresh_threshold = queue_size * 4;
237             };
238 
239             // extruder_id is currently used to correctly calculate filament load / unload times into the total print time.
240             // This is currently only really used by the MK3 MMU2:
241             // extruder_unloaded = true means no filament is loaded yet, all the filaments are parked in the MK3 MMU2 unit.
242             bool extruder_unloaded;
243             // whether or not to export post-process the gcode to export lines M73 in it
244             bool export_remaining_time_enabled;
245             // allow to skip the lines M201/M203/M204/M205 generated by GCode::print_machine_envelope() for non-Normal time estimate mode
246             bool machine_envelope_processing_enabled;
247             MachineEnvelopeConfig machine_limits;
248             // Additional load / unload times for a filament exchange sequence.
249             std::vector<float> filament_load_times;
250             std::vector<float> filament_unload_times;
251             std::array<TimeMachine, static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count)> machines;
252 
253             void reset();
254 
255             // post process the file with the given filename to add remaining time lines M73
256             void post_process(const std::string& filename);
257         };
258 
259     public:
260         struct MoveVertex
261         {
262             EMoveType type{ EMoveType::Noop };
263             ExtrusionRole extrusion_role{ erNone };
264             unsigned char extruder_id{ 0 };
265             unsigned char cp_color_id{ 0 };
266             Vec3f position{ Vec3f::Zero() }; // mm
267             float delta_extruder{ 0.0f }; // mm
268             float feedrate{ 0.0f }; // mm/s
269             float width{ 0.0f }; // mm
270             float height{ 0.0f }; // mm
271             float mm3_per_mm{ 0.0f };
272             float fan_speed{ 0.0f }; // percentage
273             float time{ 0.0f }; // s
274 
volumetric_rateSlic3r::GCodeProcessor::MoveVertex275             float volumetric_rate() const { return feedrate * mm3_per_mm; }
276         };
277 
278         struct Result
279         {
280             struct SettingsIds
281             {
282                 std::string print;
283                 std::vector<std::string> filament;
284                 std::string printer;
285 
resetSlic3r::GCodeProcessor::Result::SettingsIds286                 void reset()
287                 {
288                     print = "";
289                     filament = std::vector<std::string>();
290                     printer = "";
291                 }
292             };
293             unsigned int id;
294             std::vector<MoveVertex> moves;
295             Pointfs bed_shape;
296             SettingsIds settings_ids;
297             size_t extruders_count;
298             std::vector<std::string> extruder_colors;
299             PrintEstimatedTimeStatistics time_statistics;
300 
301 #if ENABLE_GCODE_VIEWER_STATISTICS
302             int64_t time{ 0 };
resetSlic3r::GCodeProcessor::Result303             void reset()
304             {
305                 time = 0;
306                 moves = std::vector<MoveVertex>();
307                 bed_shape = Pointfs();
308                 extruder_colors = std::vector<std::string>();
309                 extruders_count = 0;
310                 settings_ids.reset();
311             }
312 #else
resetSlic3r::GCodeProcessor::Result313             void reset()
314             {
315                 moves = std::vector<MoveVertex>();
316                 bed_shape = Pointfs();
317                 extruder_colors = std::vector<std::string>();
318                 extruders_count = 0;
319                 settings_ids.reset();
320             }
321 #endif // ENABLE_GCODE_VIEWER_STATISTICS
322         };
323 
324 #if ENABLE_GCODE_VIEWER_DATA_CHECKING
325         struct DataChecker
326         {
327             struct Error
328             {
329                 float value;
330                 float tag_value;
331                 ExtrusionRole role;
332             };
333 
334             std::string type;
335             float threshold{ 0.01f };
336             float last_tag_value{ 0.0f };
337             unsigned int count{ 0 };
338             std::vector<Error> errors;
339 
DataCheckerSlic3r::GCodeProcessor::DataChecker340             DataChecker(const std::string& type, float threshold)
341                 : type(type), threshold(threshold)
342             {}
343 
updateSlic3r::GCodeProcessor::DataChecker344             void update(float value, ExtrusionRole role) {
345                 if (role != erCustom) {
346                     ++count;
347                     if (last_tag_value != 0.0f) {
348                         if (std::abs(value - last_tag_value) / last_tag_value > threshold)
349                             errors.push_back({ value, last_tag_value, role });
350                     }
351                 }
352             }
353 
resetSlic3r::GCodeProcessor::DataChecker354             void reset() { last_tag_value = 0.0f; errors.clear(); count = 0; }
355 
get_minSlic3r::GCodeProcessor::DataChecker356             std::pair<float, float> get_min() const {
357                 float delta_min = FLT_MAX;
358                 float perc_min = 0.0f;
359                 for (const Error& e : errors) {
360                     if (delta_min > e.value - e.tag_value) {
361                         delta_min = e.value - e.tag_value;
362                         perc_min = 100.0f * delta_min / e.tag_value;
363                     }
364                 }
365                 return { delta_min, perc_min };
366             }
367 
get_maxSlic3r::GCodeProcessor::DataChecker368             std::pair<float, float> get_max() const {
369                 float delta_max = -FLT_MAX;
370                 float perc_max = 0.0f;
371                 for (const Error& e : errors) {
372                     if (delta_max < e.value - e.tag_value) {
373                         delta_max = e.value - e.tag_value;
374                         perc_max = 100.0f * delta_max / e.tag_value;
375                     }
376                 }
377                 return { delta_max, perc_max };
378             }
379 
outputSlic3r::GCodeProcessor::DataChecker380             void output() const {
381                 if (!errors.empty()) {
382                     std::cout << type << ":\n";
383                     std::cout << "Errors: " << errors.size() << " (" << 100.0f * float(errors.size()) / float(count) << "%)\n";
384                     auto [min, perc_min] = get_min();
385                     auto [max, perc_max] = get_max();
386                     std::cout << "min: " << min << "(" << perc_min << "%) - max: " << max << "(" << perc_max << "%)\n";
387                 }
388             }
389         };
390 #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
391 
392     private:
393         GCodeReader m_parser;
394 
395         EUnits m_units;
396         EPositioningType m_global_positioning_type;
397         EPositioningType m_e_local_positioning_type;
398         std::vector<Vec3f> m_extruder_offsets;
399         GCodeFlavor m_flavor;
400 
401         AxisCoords m_start_position; // mm
402         AxisCoords m_end_position; // mm
403         AxisCoords m_origin; // mm
404         CachedPosition m_cached_position;
405         bool m_wiping;
406 
407         float m_feedrate; // mm/s
408         float m_width; // mm
409         float m_height; // mm
410 #if ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
411         float m_forced_width; // mm
412         float m_forced_height; // mm
413 #endif // ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
414         float m_mm3_per_mm;
415         float m_fan_speed; // percentage
416         ExtrusionRole m_extrusion_role;
417         unsigned char m_extruder_id;
418         ExtruderColors m_extruder_colors;
419         std::vector<float> m_filament_diameters;
420         float m_extruded_last_z;
421         unsigned int m_g1_line_id;
422         unsigned int m_layer_id;
423         CpColor m_cp_color;
424 #if ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
425         bool m_use_volumetric_e;
426 #endif // ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
427 
428         enum class EProducer
429         {
430             Unknown,
431             PrusaSlicer,
432             Slic3rPE,
433             Slic3r,
434             Cura,
435             Simplify3D,
436             CraftWare,
437             ideaMaker,
438             KissSlicer
439         };
440 
441         static const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> Producers;
442         EProducer m_producer;
443         bool m_producers_enabled;
444 
445         TimeProcessor m_time_processor;
446 
447         Result m_result;
448         static unsigned int s_result_id;
449 
450 #if ENABLE_GCODE_VIEWER_DATA_CHECKING
451         DataChecker m_mm3_per_mm_compare{ "mm3_per_mm", 0.01f };
452         DataChecker m_height_compare{ "height", 0.01f };
453         DataChecker m_width_compare{ "width", 0.01f };
454 #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
455 
456     public:
457         GCodeProcessor();
458 
459         void apply_config(const PrintConfig& config);
460         void apply_config(const DynamicPrintConfig& config);
461         void enable_stealth_time_estimator(bool enabled);
is_stealth_time_estimator_enabled() const462         bool is_stealth_time_estimator_enabled() const {
463             return m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].enabled;
464         }
enable_machine_envelope_processing(bool enabled)465         void enable_machine_envelope_processing(bool enabled) { m_time_processor.machine_envelope_processing_enabled = enabled; }
enable_producers(bool enabled)466         void enable_producers(bool enabled) { m_producers_enabled = enabled; }
467         void reset();
468 
get_result() const469         const Result& get_result() const { return m_result; }
extract_result()470         Result&& extract_result() { return std::move(m_result); }
471 
472         // Process the gcode contained in the file with the given filename
473         // throws CanceledException through print->throw_if_canceled() (sent by the caller as callback).
474         void process_file(const std::string& filename, bool apply_postprocess, std::function<void()> cancel_callback = nullptr);
475 
476         float get_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
477         std::string get_time_dhm(PrintEstimatedTimeStatistics::ETimeMode mode) const;
478         std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> get_custom_gcode_times(PrintEstimatedTimeStatistics::ETimeMode mode, bool include_remaining) const;
479 
480         std::vector<std::pair<EMoveType, float>> get_moves_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
481         std::vector<std::pair<ExtrusionRole, float>> get_roles_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
482         std::vector<float> get_layers_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
483 
484     private:
485         void process_gcode_line(const GCodeReader::GCodeLine& line);
486 
487         // Process tags embedded into comments
488         void process_tags(const std::string_view comment);
489         bool process_producers_tags(const std::string_view comment);
490         bool process_prusaslicer_tags(const std::string_view comment);
491         bool process_cura_tags(const std::string_view comment);
492         bool process_simplify3d_tags(const std::string_view comment);
493         bool process_craftware_tags(const std::string_view comment);
494         bool process_ideamaker_tags(const std::string_view comment);
495         bool process_kissslicer_tags(const std::string_view comment);
496 
497         bool detect_producer(const std::string_view comment);
498 
499         // Move
500         void process_G0(const GCodeReader::GCodeLine& line);
501         void process_G1(const GCodeReader::GCodeLine& line);
502 
503         // Retract
504         void process_G10(const GCodeReader::GCodeLine& line);
505 
506         // Unretract
507         void process_G11(const GCodeReader::GCodeLine& line);
508 
509         // Set Units to Inches
510         void process_G20(const GCodeReader::GCodeLine& line);
511 
512         // Set Units to Millimeters
513         void process_G21(const GCodeReader::GCodeLine& line);
514 
515         // Firmware controlled Retract
516         void process_G22(const GCodeReader::GCodeLine& line);
517 
518         // Firmware controlled Unretract
519         void process_G23(const GCodeReader::GCodeLine& line);
520 
521         // Set to Absolute Positioning
522         void process_G90(const GCodeReader::GCodeLine& line);
523 
524         // Set to Relative Positioning
525         void process_G91(const GCodeReader::GCodeLine& line);
526 
527         // Set Position
528         void process_G92(const GCodeReader::GCodeLine& line);
529 
530         // Sleep or Conditional stop
531         void process_M1(const GCodeReader::GCodeLine& line);
532 
533         // Set extruder to absolute mode
534         void process_M82(const GCodeReader::GCodeLine& line);
535 
536         // Set extruder to relative mode
537         void process_M83(const GCodeReader::GCodeLine& line);
538 
539         // Set fan speed
540         void process_M106(const GCodeReader::GCodeLine& line);
541 
542         // Disable fan
543         void process_M107(const GCodeReader::GCodeLine& line);
544 
545         // Set tool (Sailfish)
546         void process_M108(const GCodeReader::GCodeLine& line);
547 
548         // Recall stored home offsets
549         void process_M132(const GCodeReader::GCodeLine& line);
550 
551         // Set tool (MakerWare)
552         void process_M135(const GCodeReader::GCodeLine& line);
553 
554         // Set max printing acceleration
555         void process_M201(const GCodeReader::GCodeLine& line);
556 
557         // Set maximum feedrate
558         void process_M203(const GCodeReader::GCodeLine& line);
559 
560         // Set default acceleration
561         void process_M204(const GCodeReader::GCodeLine& line);
562 
563         // Advanced settings
564         void process_M205(const GCodeReader::GCodeLine& line);
565 
566         // Set extrude factor override percentage
567         void process_M221(const GCodeReader::GCodeLine& line);
568 
569         // Repetier: Store x, y and z position
570         void process_M401(const GCodeReader::GCodeLine& line);
571 
572         // Repetier: Go to stored position
573         void process_M402(const GCodeReader::GCodeLine& line);
574 
575         // Set allowable instantaneous speed change
576         void process_M566(const GCodeReader::GCodeLine& line);
577 
578         // Unload the current filament into the MK3 MMU2 unit at the end of print.
579         void process_M702(const GCodeReader::GCodeLine& line);
580 
581         // Processes T line (Select Tool)
582         void process_T(const GCodeReader::GCodeLine& line);
583         void process_T(const std::string_view command);
584 
585         void store_move_vertex(EMoveType type);
586 
587         float minimum_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const;
588         float minimum_travel_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const;
589         float get_axis_max_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const;
590         float get_axis_max_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const;
591         float get_axis_max_jerk(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const;
592         float get_retract_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const;
593         float get_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const;
594         void set_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value);
595         float get_filament_load_time(size_t extruder_id);
596         float get_filament_unload_time(size_t extruder_id);
597 
598         void process_custom_gcode_time(CustomGCode::Type code);
599 
600         // Simulates firmware st_synchronize() call
601         void simulate_st_synchronize(float additional_time = 0.0f);
602 
603         void update_estimated_times_stats();
604    };
605 
606 } /* namespace Slic3r */
607 
608 #endif /* slic3r_GCodeProcessor_hpp_ */
609 
610 
611