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