diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 21a48898e..8c682b73a 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -170,7 +170,11 @@ namespace Slic3r { // subdivide the retraction in segments if (!wipe_path.empty()) { // add tag for processor +#if ENABLE_VALIDATE_CUSTOM_GCODE + gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Start) + "\n"; +#else gcode += ";" + GCodeProcessor::Wipe_Start_Tag + "\n"; +#endif // ENABLE_VALIDATE_CUSTOM_GCODE for (const Line& line : wipe_path.lines()) { double segment_length = line.length(); /* Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one @@ -186,7 +190,11 @@ namespace Slic3r { ); } // add tag for processor +#if ENABLE_VALIDATE_CUSTOM_GCODE + gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_End) + "\n"; +#else gcode += ";" + GCodeProcessor::Wipe_End_Tag + "\n"; +#endif // ENABLE_VALIDATE_CUSTOM_GCODE gcodegen.set_last_pos(wipe_path.points.back()); } @@ -610,6 +618,59 @@ namespace DoExport { print_statistics.estimated_silent_print_time = processor.is_stealth_time_estimator_enabled() ? get_time_dhms(result.time_statistics.modes[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].time) : "N/A"; } + +#if ENABLE_VALIDATE_CUSTOM_GCODE + // if any reserved keyword is found, returns a std::vector containing the first MAX_COUNT keywords found + // into pairs containing: + // first: source + // second: keyword + // to be shown in the warning notification + // The returned vector is empty if no keyword has been found + static std::vector> validate_custom_gcode(const Print& print) { + const unsigned int MAX_COUNT = 5; + std::vector> ret; + + auto check = [&ret, MAX_COUNT](const std::string& source, const std::string& gcode) { + std::vector tags; + if (GCodeProcessor::contains_reserved_tags(gcode, MAX_COUNT, tags)) { + if (!tags.empty()) { + size_t i = 0; + while (ret.size() < MAX_COUNT && i < tags.size()) { + ret.push_back({ source, tags[i] }); + ++i; + } + } + } + }; + + const GCodeConfig& config = print.config(); + check(_(L("Start G-code")), config.start_gcode.value); + if (ret.size() < MAX_COUNT) check(_(L("End G-code")), config.end_gcode.value); + if (ret.size() < MAX_COUNT) check(_(L("Before layer change G-code")), config.before_layer_gcode.value); + if (ret.size() < MAX_COUNT) check(_(L("After layer change G-code")), config.layer_gcode.value); + if (ret.size() < MAX_COUNT) check(_(L("Tool change G-code")), config.toolchange_gcode.value); + if (ret.size() < MAX_COUNT) check(_(L("Between objects G-code (for sequential printing)")), config.between_objects_gcode.value); + if (ret.size() < MAX_COUNT) check(_(L("Color Change G-code")), config.color_change_gcode.value); + if (ret.size() < MAX_COUNT) check(_(L("Pause Print G-code")), config.pause_print_gcode.value); + if (ret.size() < MAX_COUNT) check(_(L("Template Custom G-code")), config.template_custom_gcode.value); + if (ret.size() < MAX_COUNT) { + for (const std::string& value : config.start_filament_gcode.values) { + check(_(L("Filament Start G-code")), value); + if (ret.size() == MAX_COUNT) + break; + } + } + if (ret.size() < MAX_COUNT) { + for (const std::string& value : config.end_filament_gcode.values) { + check(_(L("Filament End G-code")), value); + if (ret.size() == MAX_COUNT) + break; + } + } + + return ret; + } +#endif // ENABLE_VALIDATE_CUSTOM_GCODE } // namespace DoExport void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb) @@ -622,6 +683,22 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re print->set_started(psGCodeExport); +#if ENABLE_VALIDATE_CUSTOM_GCODE + // check if any custom gcode contains keywords used by the gcode processor to + // produce time estimation and gcode toolpaths + std::vector> validation_res = DoExport::validate_custom_gcode(*print); + if (!validation_res.empty()) { + std::string reports; + for (const auto& [source, keyword] : validation_res) { + reports += source + ": \"" + keyword + "\"\n"; + } + print->active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, + _(L("Found reserved keyword(s) into custom g-code:")) + "\n" + + reports + + _(L("This may cause problems in g-code visualization and printing time estimation."))); + } +#endif // ENABLE_VALIDATE_CUSTOM_GCODE + BOOST_LOG_TRIVIAL(info) << "Exporting G-code..." << log_memory_info(); // Remove the old g-code if it exists. @@ -1034,7 +1111,11 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // adds tags for time estimators if (print.config().remaining_times.value) +#if ENABLE_VALIDATE_CUSTOM_GCODE + _write_format(file, ";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::First_Line_M73_Placeholder).c_str()); +#else _writeln(file, GCodeProcessor::First_Line_M73_Placeholder_Tag); +#endif // ENABLE_VALIDATE_CUSTOM_GCODE // Prepare the helper object for replacing placeholders in custom G-code and output filename. m_placeholder_parser = print.placeholder_parser(); @@ -1140,7 +1221,11 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, false); // adds tag for processor +#if ENABLE_VALIDATE_CUSTOM_GCODE + _write_format(file, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str()); +#else _write_format(file, ";%s%s\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), ExtrusionEntity::role_to_string(erCustom).c_str()); +#endif // ENABLE_VALIDATE_CUSTOM_GCODE // Write the custom start G-code _writeln(file, start_gcode); @@ -1301,7 +1386,11 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu _write(file, m_writer.set_fan(false)); // adds tag for processor +#if ENABLE_VALIDATE_CUSTOM_GCODE + _write_format(file, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str()); +#else _write_format(file, ";%s%s\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), ExtrusionEntity::role_to_string(erCustom).c_str()); +#endif // ENABLE_VALIDATE_CUSTOM_GCODE // Process filament-specific gcode in extruder order. { @@ -1328,7 +1417,11 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // adds tags for time estimators if (print.config().remaining_times.value) +#if ENABLE_VALIDATE_CUSTOM_GCODE + _write_format(file, ";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Last_Line_M73_Placeholder).c_str()); +#else _writeln(file, GCodeProcessor::Last_Line_M73_Placeholder_Tag); +#endif // ENABLE_VALIDATE_CUSTOM_GCODE print.throw_if_canceled(); @@ -1344,7 +1437,11 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu _write_format(file, "; total filament cost = %.2lf\n", print.m_print_statistics.total_cost); if (print.m_print_statistics.total_toolchanges > 0) _write_format(file, "; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges); +#if ENABLE_VALIDATE_CUSTOM_GCODE + _write_format(file, ";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str()); +#else _writeln(file, GCodeProcessor::Estimated_Printing_Time_Placeholder_Tag); +#endif // ENABLE_VALIDATE_CUSTOM_GCODE // Append full config. _write(file, "\n"); @@ -1631,7 +1728,11 @@ namespace ProcessLayer assert(m600_extruder_before_layer >= 0); // Color Change or Tool Change as Color Change. // add tag for processor +#if ENABLE_VALIDATE_CUSTOM_GCODE + gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Color_Change) + ",T" + std::to_string(m600_extruder_before_layer) + "\n"; +#else gcode += ";" + GCodeProcessor::Color_Change_Tag + ",T" + std::to_string(m600_extruder_before_layer) + "\n"; +#endif // ENABLE_VALIDATE_CUSTOM_GCODE if (!single_extruder_printer && m600_extruder_before_layer >= 0 && first_extruder_id != (unsigned)m600_extruder_before_layer // && !MMU1 @@ -1646,21 +1747,27 @@ namespace ProcessLayer gcode += "\n"; } } - else - { + else { if (gcode_type == CustomGCode::PausePrint) // Pause print { // add tag for processor +#if ENABLE_VALIDATE_CUSTOM_GCODE + gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Pause_Print) + "\n"; +#else gcode += ";" + GCodeProcessor::Pause_Print_Tag + "\n"; +#endif // ENABLE_VALIDATE_CUSTOM_GCODE //! FIXME_in_fw show message during print pause if (!pause_print_msg.empty()) gcode += "M117 " + pause_print_msg + "\n"; gcode += config.pause_print_gcode; } - else - { + else { // add tag for processor +#if ENABLE_VALIDATE_CUSTOM_GCODE + gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Custom_Code) + "\n"; +#else gcode += ";" + GCodeProcessor::Custom_Code_Tag + "\n"; +#endif // ENABLE_VALIDATE_CUSTOM_GCODE if (gcode_type == CustomGCode::Template) // Template Cistom Gcode gcode += config.template_custom_gcode; else // custom Gcode @@ -1807,14 +1914,22 @@ void GCode::process_layer( std::string gcode; // add tag for processor +#if ENABLE_VALIDATE_CUSTOM_GCODE + gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Layer_Change) + "\n"; +#else gcode += ";" + GCodeProcessor::Layer_Change_Tag + "\n"; +#endif // ENABLE_VALIDATE_CUSTOM_GCODE // export layer z char buf[64]; sprintf(buf, ";Z:%g\n", print_z); gcode += buf; // export layer height float height = first_layer ? static_cast(print_z) : static_cast(print_z) - m_last_layer_z; +#if ENABLE_VALIDATE_CUSTOM_GCODE + sprintf(buf, ";%s%g\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height).c_str(), height); +#else sprintf(buf, ";%s%g\n", GCodeProcessor::Height_Tag.c_str(), height); +#endif // ENABLE_VALIDATE_CUSTOM_GCODE gcode += buf; // update caches m_last_layer_z = static_cast(print_z); @@ -2635,13 +2750,21 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, if (path.role() != m_last_processor_extrusion_role) { m_last_processor_extrusion_role = path.role(); +#if ENABLE_VALIDATE_CUSTOM_GCODE + sprintf(buf, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(m_last_processor_extrusion_role).c_str()); +#else sprintf(buf, ";%s%s\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), ExtrusionEntity::role_to_string(m_last_processor_extrusion_role).c_str()); +#endif // ENABLE_VALIDATE_CUSTOM_GCODE gcode += buf; } if (last_was_wipe_tower || m_last_width != path.width) { m_last_width = path.width; +#if ENABLE_VALIDATE_CUSTOM_GCODE + sprintf(buf, ";%s%g\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Width).c_str(), m_last_width); +#else sprintf(buf, ";%s%g\n", GCodeProcessor::Width_Tag.c_str(), m_last_width); +#endif // ENABLE_VALIDATE_CUSTOM_GCODE gcode += buf; } @@ -2655,7 +2778,11 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, if (last_was_wipe_tower || std::abs(m_last_height - path.height) > EPSILON) { m_last_height = path.height; +#if ENABLE_VALIDATE_CUSTOM_GCODE + sprintf(buf, ";%s%g\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height).c_str(), m_last_height); +#else sprintf(buf, ";%s%g\n", GCodeProcessor::Height_Tag.c_str(), m_last_height); +#endif // ENABLE_VALIDATE_CUSTOM_GCODE gcode += buf; } diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 4e60318e1..844135ee2 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -19,15 +19,31 @@ static const float INCHES_TO_MM = 25.4f; static const float MMMIN_TO_MMSEC = 1.0f / 60.0f; - static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2 namespace Slic3r { +#if ENABLE_VALIDATE_CUSTOM_GCODE +const std::vector GCodeProcessor::Reserved_Tags = { + "TYPE:", + "WIPE_START", + "WIPE_END", + "HEIGHT:", + "WIDTH:", + "LAYER_CHANGE", + "COLOR_CHANGE", + "PAUSE_PRINT", + "CUSTOM_GCODE", + "_GP_FIRST_LINE_M73_PLACEHOLDER", + "_GP_LAST_LINE_M73_PLACEHOLDER", + "_GP_ESTIMATED_PRINTING_TIME_PLACEHOLDER" +}; +#else const std::string GCodeProcessor::Extrusion_Role_Tag = "TYPE:"; const std::string GCodeProcessor::Wipe_Start_Tag = "WIPE_START"; const std::string GCodeProcessor::Wipe_End_Tag = "WIPE_END"; const std::string GCodeProcessor::Height_Tag = "HEIGHT:"; +const std::string GCodeProcessor::Width_Tag = "WIDTH:"; const std::string GCodeProcessor::Layer_Change_Tag = "LAYER_CHANGE"; const std::string GCodeProcessor::Color_Change_Tag = "COLOR_CHANGE"; const std::string GCodeProcessor::Pause_Print_Tag = "PAUSE_PRINT"; @@ -36,11 +52,11 @@ const std::string GCodeProcessor::Custom_Code_Tag = "CUSTOM_GCODE"; const std::string GCodeProcessor::First_Line_M73_Placeholder_Tag = "; _GP_FIRST_LINE_M73_PLACEHOLDER"; const std::string GCodeProcessor::Last_Line_M73_Placeholder_Tag = "; _GP_LAST_LINE_M73_PLACEHOLDER"; const std::string GCodeProcessor::Estimated_Printing_Time_Placeholder_Tag = "; _GP_ESTIMATED_PRINTING_TIME_PLACEHOLDER"; +#endif // ENABLE_VALIDATE_CUSTOM_GCODE const float GCodeProcessor::Wipe_Width = 0.05f; const float GCodeProcessor::Wipe_Height = 0.05f; -const std::string GCodeProcessor::Width_Tag = "WIDTH:"; #if ENABLE_GCODE_VIEWER_DATA_CHECKING const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "MM3_PER_MM:"; #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING @@ -365,7 +381,22 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) std::string line = gcode_line.substr(0, gcode_line.length() - 1); std::string ret; - +#if ENABLE_VALIDATE_CUSTOM_GCODE + if (line.length() > 1) { + line = line.substr(1); + if (export_remaining_time_enabled && + (line == reserved_tag(ETags::First_Line_M73_Placeholder) || line == reserved_tag(ETags::Last_Line_M73_Placeholder))) { + for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { + const TimeMachine& machine = machines[i]; + if (machine.enabled) { + ret += format_line_M73(machine.line_m73_mask.c_str(), + (line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? 0 : 100, + (line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? time_in_minutes(machine.time) : 0); + } + } + } + else if (line == reserved_tag(ETags::Estimated_Printing_Time_Placeholder)) { +#else if (export_remaining_time_enabled && (line == First_Line_M73_Placeholder_Tag || line == Last_Line_M73_Placeholder_Tag)) { for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { const TimeMachine& machine = machines[i]; @@ -377,18 +408,22 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) } } else if (line == Estimated_Printing_Time_Placeholder_Tag) { - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { - const TimeMachine& machine = machines[i]; - PrintEstimatedTimeStatistics::ETimeMode mode = static_cast(i); - if (mode == PrintEstimatedTimeStatistics::ETimeMode::Normal || machine.enabled) { - char buf[128]; - sprintf(buf, "; estimated printing time (%s mode) = %s\n", - (mode == PrintEstimatedTimeStatistics::ETimeMode::Normal) ? "normal" : "silent", - get_time_dhms(machine.time).c_str()); - ret += buf; +#endif // ENABLE_VALIDATE_CUSTOM_GCODE + for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { + const TimeMachine& machine = machines[i]; + PrintEstimatedTimeStatistics::ETimeMode mode = static_cast(i); + if (mode == PrintEstimatedTimeStatistics::ETimeMode::Normal || machine.enabled) { + char buf[128]; + sprintf(buf, "; estimated printing time (%s mode) = %s\n", + (mode == PrintEstimatedTimeStatistics::ETimeMode::Normal) ? "normal" : "silent", + get_time_dhms(machine.time).c_str()); + ret += buf; + } } } +#if ENABLE_VALIDATE_CUSTOM_GCODE } +#endif // ENABLE_VALIDATE_CUSTOM_GCODE return std::make_pair(!ret.empty(), ret.empty() ? gcode_line : ret); }; @@ -541,6 +576,64 @@ const std::vector> GCodeProces unsigned int GCodeProcessor::s_result_id = 0; +#if ENABLE_VALIDATE_CUSTOM_GCODE +static inline bool starts_with(const std::string_view comment, const std::string_view tag) +{ + size_t tag_len = tag.size(); + return comment.size() >= tag_len && comment.substr(0, tag_len) == tag; +} + +bool GCodeProcessor::contains_reserved_tag(const std::string& gcode, std::string& found_tag) +{ + bool ret = false; + + GCodeReader parser; + parser.parse_buffer(gcode, [&ret, &found_tag](GCodeReader& parser, const GCodeReader::GCodeLine& line) { + std::string comment = line.raw(); + if (comment.length() > 2 && comment.front() == ';') { + comment = comment.substr(1); + for (const std::string& s : Reserved_Tags) { + if (starts_with(comment, s)) { + ret = true; + found_tag = comment; + parser.quit_parsing(); + return; + } + } + } + }); + + return ret; +} + +bool GCodeProcessor::contains_reserved_tags(const std::string& gcode, unsigned int max_count, std::vector& found_tag) +{ + max_count = std::max(max_count, 1U); + + bool ret = false; + + GCodeReader parser; + parser.parse_buffer(gcode, [&ret, &found_tag, max_count](GCodeReader& parser, const GCodeReader::GCodeLine& line) { + std::string comment = line.raw(); + if (comment.length() > 2 && comment.front() == ';') { + comment = comment.substr(1); + for (const std::string& s : Reserved_Tags) { + if (starts_with(comment, s)) { + ret = true; + found_tag.push_back(comment); + if (found_tag.size() == max_count) { + parser.quit_parsing(); + return; + } + } + } + } + }); + + return ret; +} +#endif // ENABLE_VALIDATE_CUSTOM_GCODE + GCodeProcessor::GCodeProcessor() { reset(); @@ -847,7 +940,11 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr if (cmd.length() == 0) { const std::string_view comment = line.comment(); if (comment.length() > 1 && detect_producer(comment)) +#if ENABLE_VALIDATE_CUSTOM_GCODE + m_parser.quit_parsing(); +#else m_parser.quit_parsing_file(); +#endif // ENABLE_VALIDATE_CUSTOM_GCODE } }); @@ -1051,11 +1148,13 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) } } +#if !ENABLE_VALIDATE_CUSTOM_GCODE static inline bool starts_with(const std::string_view comment, const std::string_view tag) { size_t tag_len = tag.size(); return comment.size() >= tag_len && comment.substr(0, tag_len) == tag; } +#endif // !ENABLE_VALIDATE_CUSTOM_GCODE #if __has_include() template @@ -1108,6 +1207,25 @@ void GCodeProcessor::process_tags(const std::string_view comment) if (m_producers_enabled && process_producers_tags(comment)) return; +#if ENABLE_VALIDATE_CUSTOM_GCODE + // extrusion role tag + if (starts_with(comment, reserved_tag(ETags::Role))) { + m_extrusion_role = ExtrusionEntity::string_to_role(comment.substr(reserved_tag(ETags::Role).length())); + return; + } + + // wipe start tag + if (starts_with(comment, reserved_tag(ETags::Wipe_Start))) { + m_wiping = true; + return; + } + + // wipe end tag + if (starts_with(comment, reserved_tag(ETags::Wipe_End))) { + m_wiping = false; + return; + } +#else // extrusion role tag if (starts_with(comment, Extrusion_Role_Tag)) { m_extrusion_role = ExtrusionEntity::string_to_role(comment.substr(Extrusion_Role_Tag.length())); @@ -1125,8 +1243,23 @@ void GCodeProcessor::process_tags(const std::string_view comment) m_wiping = false; return; } +#endif // ENABLE_VALIDATE_CUSTOM_GCODE if (!m_producers_enabled || m_producer == EProducer::PrusaSlicer) { +#if ENABLE_VALIDATE_CUSTOM_GCODE + // height tag + if (starts_with(comment, reserved_tag(ETags::Height))) { + if (!parse_number(comment.substr(reserved_tag(ETags::Height).size()), m_forced_height)) + BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ")."; + return; + } + // width tag + if (starts_with(comment, reserved_tag(ETags::Width))) { + if (!parse_number(comment.substr(reserved_tag(ETags::Width).size()), m_forced_width)) + BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ")."; + return; + } +#else // height tag if (starts_with(comment, Height_Tag)) { if (!parse_number(comment.substr(Height_Tag.size()), m_forced_height)) @@ -1139,8 +1272,56 @@ void GCodeProcessor::process_tags(const std::string_view comment) BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ")."; return; } +#endif // ENABLE_VALIDATE_CUSTOM_GCODE } +#if ENABLE_VALIDATE_CUSTOM_GCODE + // color change tag + if (starts_with(comment, reserved_tag(ETags::Color_Change))) { + unsigned char extruder_id = 0; + if (starts_with(comment.substr(reserved_tag(ETags::Color_Change).size()), ",T")) { + int eid; + if (!parse_number(comment.substr(reserved_tag(ETags::Color_Change).size() + 2), eid) || eid < 0 || eid > 255) { + BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Color_Change (" << comment << ")."; + return; + } + extruder_id = static_cast(eid); + } + + m_extruder_colors[extruder_id] = static_cast(m_extruder_offsets.size()) + m_cp_color.counter; // color_change position in list of color for preview + ++m_cp_color.counter; + if (m_cp_color.counter == UCHAR_MAX) + m_cp_color.counter = 0; + + if (m_extruder_id == extruder_id) { + m_cp_color.current = m_extruder_colors[extruder_id]; + store_move_vertex(EMoveType::Color_change); + } + + process_custom_gcode_time(CustomGCode::ColorChange); + + return; + } + + // pause print tag + if (comment == reserved_tag(ETags::Pause_Print)) { + store_move_vertex(EMoveType::Pause_Print); + process_custom_gcode_time(CustomGCode::PausePrint); + return; + } + + // custom code tag + if (comment == reserved_tag(ETags::Custom_Code)) { + store_move_vertex(EMoveType::Custom_GCode); + return; + } + + // layer change tag + if (comment == reserved_tag(ETags::Layer_Change)) { + ++m_layer_id; + return; + } +#else // color change tag if (starts_with(comment, Color_Change_Tag)) { unsigned char extruder_id = 0; @@ -1181,6 +1362,13 @@ void GCodeProcessor::process_tags(const std::string_view comment) return; } + // layer change tag + if (comment == Layer_Change_Tag) { + ++m_layer_id; + return; + } +#endif // ENABLE_VALIDATE_CUSTOM_GCODE + #if ENABLE_GCODE_VIEWER_DATA_CHECKING // mm3_per_mm print tag if (starts_with(comment, Mm3_Per_Mm_Tag)) { @@ -1189,12 +1377,6 @@ void GCodeProcessor::process_tags(const std::string_view comment) return; } #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING - - // layer change tag - if (comment == Layer_Change_Tag) { - ++m_layer_id; - return; - } } bool GCodeProcessor::process_producers_tags(const std::string_view comment) diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 1ca93ce8f..05f9a2ce3 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -69,7 +69,34 @@ namespace Slic3r { class GCodeProcessor { +#if ENABLE_VALIDATE_CUSTOM_GCODE + static const std::vector Reserved_Tags; +#endif // ENABLE_VALIDATE_CUSTOM_GCODE public: +#if ENABLE_VALIDATE_CUSTOM_GCODE + enum class ETags : unsigned char + { + Role, + Wipe_Start, + Wipe_End, + Height, + Width, + Layer_Change, + Color_Change, + Pause_Print, + Custom_Code, + First_Line_M73_Placeholder, + Last_Line_M73_Placeholder, + Estimated_Printing_Time_Placeholder + }; + + static const std::string& reserved_tag(ETags tag) { return Reserved_Tags[static_cast(tag)]; } + // checks the given gcode for reserved tags and returns true when finding the 1st (which is returned into found_tag) + static bool contains_reserved_tag(const std::string& gcode, std::string& found_tag); + // checks the given gcode for reserved tags and returns true when finding any + // (the first max_count found tags are returned into found_tag) + static bool contains_reserved_tags(const std::string& gcode, unsigned int max_count, std::vector& found_tag); +#else static const std::string Extrusion_Role_Tag; static const std::string Wipe_Start_Tag; static const std::string Wipe_End_Tag; @@ -81,11 +108,12 @@ namespace Slic3r { static const std::string First_Line_M73_Placeholder_Tag; static const std::string Last_Line_M73_Placeholder_Tag; static const std::string Estimated_Printing_Time_Placeholder_Tag; + static const std::string Width_Tag; +#endif // ENABLE_VALIDATE_CUSTOM_GCODE static const float Wipe_Width; static const float Wipe_Height; - static const std::string Width_Tag; #if ENABLE_GCODE_VIEWER_DATA_CHECKING static const std::string Mm3_Per_Mm_Tag; #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 081e243ad..24248e16c 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -42,9 +42,15 @@ public: { // adds tag for analyzer: char buf[64]; +#if ENABLE_VALIDATE_CUSTOM_GCODE + sprintf(buf, ";%s%f\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height).c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming + m_gcode += buf; + sprintf(buf, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erWipeTower).c_str()); +#else sprintf(buf, ";%s%f\n", GCodeProcessor::Height_Tag.c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming m_gcode += buf; sprintf(buf, ";%s%s\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), ExtrusionEntity::role_to_string(erWipeTower).c_str()); +#endif // ENABLE_VALIDATE_CUSTOM_GCODE m_gcode += buf; change_analyzer_line_width(line_width); } @@ -52,7 +58,11 @@ public: WipeTowerWriter& change_analyzer_line_width(float line_width) { // adds tag for analyzer: char buf[64]; +#if ENABLE_VALIDATE_CUSTOM_GCODE + sprintf(buf, ";%s%f\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Width).c_str(), line_width); +#else sprintf(buf, ";%s%f\n", GCodeProcessor::Width_Tag.c_str(), line_width); +#endif // ENABLE_VALIDATE_CUSTOM_GCODE m_gcode += buf; return *this; } diff --git a/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp index 2ef238f10..fb493fcb7 100644 --- a/src/libslic3r/GCodeReader.cpp +++ b/src/libslic3r/GCodeReader.cpp @@ -116,8 +116,13 @@ void GCodeReader::parse_file(const std::string &file, callback_t callback) { boost::nowide::ifstream f(file); std::string line; +#if ENABLE_VALIDATE_CUSTOM_GCODE + m_parsing = true; + while (m_parsing && std::getline(f, line)) +#else m_parsing_file = true; while (m_parsing_file && std::getline(f, line)) +#endif // ENABLE_VALIDATE_CUSTOM_GCODE this->parse_line(line, callback); } diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp index d90a23160..54e193799 100644 --- a/src/libslic3r/GCodeReader.hpp +++ b/src/libslic3r/GCodeReader.hpp @@ -84,7 +84,12 @@ public: { const char *ptr = buffer.c_str(); GCodeLine gline; +#if ENABLE_VALIDATE_CUSTOM_GCODE + m_parsing = true; + while (m_parsing && *ptr != 0) { +#else while (*ptr != 0) { +#endif // ENABLE_VALIDATE_CUSTOM_GCODE gline.reset(); ptr = this->parse_line(ptr, gline, callback); } @@ -108,7 +113,11 @@ public: { GCodeLine gline; this->parse_line(line.c_str(), gline, callback); } void parse_file(const std::string &file, callback_t callback); +#if ENABLE_VALIDATE_CUSTOM_GCODE + void quit_parsing() { m_parsing = false; } +#else void quit_parsing_file() { m_parsing_file = false; } +#endif // ENABLE_VALIDATE_CUSTOM_GCODE float& x() { return m_position[X]; } float x() const { return m_position[X]; } @@ -147,7 +156,11 @@ private: char m_extrusion_axis; float m_position[NUM_AXES]; bool m_verbose; +#if ENABLE_VALIDATE_CUSTOM_GCODE + bool m_parsing{ false }; +#else bool m_parsing_file{ false }; +#endif // ENABLE_VALIDATE_CUSTOM_GCODE }; } /* namespace Slic3r */ diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 444cbd6f6..79355fae9 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -114,6 +114,7 @@ #define ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS (1 && ENABLE_SPLITTED_VERTEX_BUFFER) #define ENABLE_WARNING_TEXTURE_REMOVAL (1 && ENABLE_2_3_1_ALPHA1) #define ENABLE_GCODE_LINES_ID_IN_H_SLIDER (1 && ENABLE_2_3_1_ALPHA1) +#define ENABLE_VALIDATE_CUSTOM_GCODE (1 && ENABLE_2_3_1_ALPHA1) #endif // _prusaslicer_technologies_h_