ENH: add PPS-CF/PPA-CF detection for multi-extruder printer

jira: STUDIO-9660

Change-Id: I1df024e178b8561569b493888d6057d8f96aea3c
This commit is contained in:
zhimin.zeng 2025-01-10 12:29:50 +08:00 committed by lane.wei
parent 0f37893bea
commit b68a7b3bd6
17 changed files with 167 additions and 4 deletions

View File

@ -1,7 +1,7 @@
{
"name": "Bambulab",
"url": "http://www.bambulab.com/Parameters/vendor/BBL.json",
"version": "02.00.00.12",
"version": "02.00.00.15",
"force_update": "0",
"description": "the initial version of BBL configurations",
"machine_model_list": [

View File

@ -28,6 +28,10 @@
"25x0,350x0,350x320,25x320"
],
"machine_load_filament_time": "24",
"unprintable_filament_types" : [
"TPU",
"PPS-CF,PPA-CF"
],
"machine_max_acceleration_x": [
"16000",
"16000",

View File

@ -39,6 +39,7 @@
],
"extruder_printable_area": [],
"extruder_printable_height": [],
"unprintable_filament_types" : [""],
"extruder_type": [
"Direct Drive"
],

View File

@ -48,6 +48,10 @@
"320",
"325"
],
"unprintable_filament_types" : [
"",
""
],
"extruder_type": [
"Direct Drive",
"Direct Drive"

View File

@ -54,6 +54,26 @@ bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const
return false;
}
bool check_filament_printable_after_group(const std::vector<unsigned int> &used_filaments, const std::vector<int> &filament_maps, const PrintConfig *print_config)
{
for (unsigned int filament_id : used_filaments) {
std::string filament_type = print_config->filament_type.get_at(filament_id);
for (size_t idx = 0; idx < print_config->unprintable_filament_types.values.size(); ++idx) {
if (filament_maps[filament_id] == idx) {
std::vector<std::string> limit_types = split_string(print_config->unprintable_filament_types.get_at(idx), ',');
auto iter = std::find(limit_types.begin(), limit_types.end(), filament_type);
if (iter != limit_types.end()) {
std::string error_msg;
std::string extruder_name = idx == 0 ? "left" : "right";
error_msg = "Grouping error: " + filament_type + " can not be placed in the " + extruder_name + " extruder";
throw Slic3r::RuntimeError(error_msg);
}
}
}
}
return true;
}
// Return a zero based extruder from the region, or extruder_override if overriden.
unsigned int LayerTools::wall_filament(const PrintRegion &region) const
{
@ -1024,6 +1044,14 @@ std::vector<int> ToolOrdering::get_recommended_filament_maps(const std::vector<s
fg.get_custom_seq = get_custom_seq;
ret = fg.calc_filament_group();
}
// todo: need calculated based on already grouped filaments
// PPS-CF/PPA-CF can only be placed on the left extruder
for (unsigned int filament_id : used_filaments) {
if (print_config.filament_type.get_at(filament_id) == "PPS-CF" || print_config.filament_type.get_at(filament_id) == "PPA-CF") {
ret[filament_id] = 0;
}
}
}
return ret;
@ -1102,6 +1130,8 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first
}
std::transform(filament_maps.begin(), filament_maps.end(), filament_maps.begin(), [](int value) { return value - 1; });
check_filament_printable_after_group(used_filaments, filament_maps, print_config);
if (!check_tpu_group(used_filaments, filament_maps, print_config)) {
if (map_mode == FilamentMapMode::fmmManual) {
throw Slic3r::RuntimeError(std::string("Manual grouping error: TPU can only be placed in a nozzle alone."));

View File

@ -949,7 +949,7 @@ static std::vector<std::string> s_Preset_printer_options {
"single_extruder_multi_material", "machine_start_gcode", "machine_end_gcode","printing_by_object_gcode","before_layer_change_gcode", "layer_change_gcode", "time_lapse_gcode", "change_filament_gcode",
"printer_model", "printer_variant", "printer_extruder_id", "printer_extruder_variant", "extruder_variant_list", "default_nozzle_volume_type",
"printable_height", "extruder_printable_height", "extruder_clearance_dist_to_rod", "extruder_clearance_max_radius","extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod",
"nozzle_height",
"nozzle_height", "unprintable_filament_types",
"default_print_profile", "inherits",
"silent_mode",
// BBS

View File

@ -565,6 +565,12 @@ void PrintConfigDef::init_common_params()
def->mode = comSimple;
def->set_default_value(new ConfigOptionFloatsNullable{});
def = this->add("unprintable_filament_types", coStrings);
def->label = L("Unprintable filament type");
def->tooltip = L("Unprintable filament type");
def->mode = comDevelop;
def->set_default_value(new ConfigOptionStrings{""});
// Options used by physical printers
def = this->add("preset_names", coStrings);

View File

@ -1113,6 +1113,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionPoints, bed_exclude_area))
((ConfigOptionPoints, head_wrap_detect_zone))
// BBS
((ConfigOptionStrings, unprintable_filament_types))
((ConfigOptionString, bed_custom_texture))
((ConfigOptionString, bed_custom_model))
((ConfigOptionEnum<BedType>, curr_bed_type))

View File

@ -172,6 +172,7 @@ typedef std::string local_encoded_string;
extern local_encoded_string encode_path(const char *src);
extern std::string decode_path(const char *src);
extern std::string normalize_utf8_nfc(const char *src);
extern std::vector<std::string> split_string(const std::string &str, char delimiter);
// Safely rename a file even if the target exists.
// On Windows, the file explorer (or anti-virus or whatever else) often locks the file

View File

@ -1089,6 +1089,18 @@ std::string normalize_utf8_nfc(const char *src)
return boost::locale::normalize(src, boost::locale::norm_nfc, locale_utf8);
}
std::vector<std::string> split_string(const std::string &str, char delimiter)
{
std::vector<std::string> result;
std::stringstream ss(str);
std::string substr;
while (std::getline(ss, substr, delimiter)) {
result.push_back(substr);
}
return result;
}
namespace PerlUtils {
// Get a file name including the extension.
std::string path_to_filename(const char *src) { return boost::filesystem::path(src).filename().string(); }

View File

@ -327,9 +327,23 @@ PrinterArch get_printer_arch_by_str(std::string arch_str)
void check_filaments_for_vt_slot(const std::string &tag_vendor, const std::string &tag_type, int ams_id, bool &in_blacklist, std::string &ac, std::string &info)
{
DeviceManager *dev = Slic3r::GUI::wxGetApp().getDeviceManager();
if (!dev)
return;
MachineObject *obj = dev->get_selected_machine();
if (obj == nullptr)
return;
if (tag_type == "TPU" && ams_id != VIRTUAL_TRAY_MAIN_ID) {
wxString extruder_name = _L("left");
if (obj->is_main_extruder_on_left()) {
extruder_name = _L("right");
}
wxString info_str = wxString::Format(_L("TPU is not supported by %s extruder for this printer."), extruder_name);
ac = "prohibition";
info = wxString(_L("TPU is not supported by deputy extruder.")).ToUTF8().data();
info = info_str.ToUTF8().data();
in_blacklist = true;
}
}
@ -7012,7 +7026,6 @@ void DeviceManager::load_last_machine()
json DeviceManager::filaments_blacklist = json::object();
std::string DeviceManager::parse_printer_type(std::string type_str)
{
return get_value_from_config<std::string>(type_str, "printer_type");
@ -7173,8 +7186,54 @@ bool DeviceManager::is_virtual_slot(int ams_id)
return false;
}
bool DeviceManager::check_filaments_printable(const std::string &tag_vendor, const std::string &tag_type, int ams_id, bool &in_blacklist, std::string &ac, std::string &info)
{
DeviceManager *dev = Slic3r::GUI::wxGetApp().getDeviceManager();
if (!dev) {
return true;
}
MachineObject *obj = dev->get_selected_machine();
if (obj == nullptr || !obj->is_multi_extruders()) {
return true;
}
Preset *printer_preset = GUI::get_printer_preset(obj);
if (!printer_preset)
return true;
ConfigOptionStrings *unprintable_filament_types_op = dynamic_cast<ConfigOptionStrings *>(printer_preset->config.option("unprintable_filament_types"));
if (!unprintable_filament_types_op)
return true;
ConfigOptionInts *physical_extruder_map_op = dynamic_cast<ConfigOptionInts *>(printer_preset->config.option("physical_extruder_map"));
if (!physical_extruder_map_op)
return true;
std::vector<int> physical_extruder_maps = physical_extruder_map_op->values;
for (size_t idx = 0; idx < unprintable_filament_types_op->values.size(); ++idx) {
if (physical_extruder_maps[idx] == obj->get_extruder_id_by_ams_id(std::to_string(ams_id))) {
std::vector<std::string> filament_types = split_string(unprintable_filament_types_op->values.at(idx), ',');
auto iter = std::find(filament_types.begin(), filament_types.end(), tag_type);
if (iter != filament_types.end()) {
wxString extruder_name = idx == 0 ? _L("left") : _L("right");
ac = "prohibition";
info = (wxString::Format(_L("%s is not supported by %s extruder."), tag_type, extruder_name)).ToUTF8().data();
in_blacklist = true;
return false;
}
}
}
return true;
}
void DeviceManager::check_filaments_in_blacklist(std::string tag_vendor, std::string tag_type, int ams_id, bool& in_blacklist, std::string& ac, std::string& info)
{
if (!check_filaments_printable(tag_vendor, tag_type, ams_id, in_blacklist, ac, info)) {
return;
}
if (DeviceManager::is_virtual_slot(ams_id)) {
check_filaments_for_vt_slot(tag_vendor, tag_type, ams_id, in_blacklist, ac, info);
return;

View File

@ -1294,6 +1294,7 @@ public:
static bool key_field_only;
static json function_table;
static json filaments_blacklist;
static json filaments_printable_blacklist;
template<typename T>
static T get_value_from_config(std::string type_str, std::string item){
@ -1329,6 +1330,7 @@ public:
static std::vector<std::string> get_resolution_supported(std::string type_str);
static std::vector<std::string> get_compatible_machine(std::string type_str);
static void check_filaments_in_blacklist(std::string tag_vendor, std::string tag_type, int ams_id, bool &in_blacklist, std::string &ac, std::string &info);
static bool check_filaments_printable(const std::string &tag_vendor, const std::string &tag_type, int ams_id, bool &in_blacklist, std::string &ac, std::string &info);
static boost::bimaps::bimap<std::string, std::string> get_all_model_id_with_name();
static std::string load_gcode(std::string type_str, std::string gcode_file);
static bool is_virtual_slot(int ams_id);

View File

@ -130,6 +130,8 @@ std::string object_limited_text = _u8L("An object is laid on the left/right extr
std::string object_clashed_text = _u8L("An object is laid over the boundary of plate or exceeds the height limit.\n"
"Please solve the problem by moving it totally on or off the plate, and confirming that the height is within the build volume.");
wxString filament_printable_error_msg;
GLCanvas3D::LayersEditing::~LayersEditing()
{
if (m_z_texture_id != 0) {
@ -2958,6 +2960,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
bool tpu_valid = cur_plate->check_tpu_printable_status(wxGetApp().preset_bundle->full_config(), wxGetApp().preset_bundle->get_used_tpu_filaments(cur_plate->get_extruders(true)));
_set_warning_notification(EWarning::TPUPrintableError, !tpu_valid);
bool filament_printable = cur_plate->check_filament_printable(wxGetApp().preset_bundle->full_config(), filament_printable_error_msg);
_set_warning_notification(EWarning::FilamentPrintableError, !filament_printable);
bool model_fits = contained_min_one && !m_model->objects.empty() && !partlyOut && object_results.filaments.empty() && tpu_valid;
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, model_fits));
ppl.get_curr_plate()->update_slice_ready_status(model_fits);
@ -2968,6 +2973,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
_set_warning_notification(EWarning::ObjectLimited, false);
//_set_warning_notification(EWarning::SlaSupportsOutside, false);
_set_warning_notification(EWarning::TPUPrintableError, false);
_set_warning_notification(EWarning::FilamentPrintableError, false);
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, false));
}
}
@ -9977,6 +9983,11 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
error = ErrorType::SLICING_ERROR;
break;
}
case EWarning::FilamentPrintableError: {
text = filament_printable_error_msg.ToUTF8();
error = ErrorType::SLICING_ERROR;
break;
}
case EWarning::MultiExtruderPrintableError: {
for (auto error_iter = m_gcode_viewer.m_gcode_check_result.print_area_error_infos.begin(); error_iter != m_gcode_viewer.m_gcode_check_result.print_area_error_infos.end(); ++error_iter) {
if (error_iter != m_gcode_viewer.m_gcode_check_result.print_area_error_infos.begin()) {
@ -10141,6 +10152,12 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
notification_manager.bbl_close_bed_filament_incompatible_notification();
}
}
if (warning == EWarning::FilamentPrintableError) {
if (state)
notification_manager.push_slicing_customize_error_notification(NotificationType::BBLFilamentPrintableError, NotificationManager::NotificationLevel::ErrorNotificationLevel, text);
else
notification_manager.close_slicing_customize_error_notification(NotificationType::BBLFilamentPrintableError, NotificationManager::NotificationLevel::ErrorNotificationLevel);
}
else {
if (state)
notification_manager.push_slicing_error_notification(text, conflictObj ? std::vector<ModelObject const*>{conflictObj} : std::vector<ModelObject const*>{});

View File

@ -382,6 +382,7 @@ class GLCanvas3D
GCodeConflict,
ToolHeightOutside,
TPUPrintableError,
FilamentPrintableError,
MultiExtruderPrintableError, // after slice
MultiExtruderHeightOutside, // after slice
FilamentUnPrintableOnFirstLayer

View File

@ -147,6 +147,7 @@ enum class NotificationType
BBLPreviewOnlyMode,
BBLPrinterConfigUpdateAvailable,
BBLUserPresetExceedLimit,
BBLFilamentPrintableError,
BBLSliceLimitError,
BBLBedFilamentIncompatible,
NotificationTypeCount

View File

@ -1307,6 +1307,29 @@ std::vector<int> PartPlate::get_used_filaments()
return std::vector(used_extruders_set.begin(), used_extruders_set.end());
}
bool PartPlate::check_filament_printable(const DynamicPrintConfig &config, wxString& error_message)
{
error_message.clear();
std::vector<int> used_filaments = get_extruders(true); // 1 base
if (!used_filaments.empty()) {
for (auto filament_idx : used_filaments) {
int filament_id = filament_idx - 1;
std::string filament_type = config.option<ConfigOptionStrings>("filament_type")->values.at(filament_id);
std::vector<int> filament_map = get_real_filament_maps(config);
int extruder_idx = filament_map[filament_id] - 1;
std::string filament_types_str = config.option<ConfigOptionStrings>("unprintable_filament_types")->values.at(extruder_idx);
std::vector<string> filament_types = split_string(filament_types_str, ',');
auto iter = std::find(filament_types.begin(), filament_types.end(), filament_type);
if (iter != filament_types.end()) {
wxString extruder_name = extruder_idx == 0 ? _L("left") : _L("right");
error_message = wxString::Format(_L("Filament %s cannot be placed in the %s extruder for printing."), filament_type, extruder_name);
return false;
}
}
}
return true;
}
bool PartPlate::check_tpu_printable_status(const DynamicPrintConfig & config, const std::vector<int> &tpu_filaments)
{
bool tpu_valid = true;

View File

@ -314,6 +314,7 @@ public:
// get used filaments from gcode result, 1 based idx
std::vector<int> get_used_filaments();
int get_physical_extruder_by_filament_id(const DynamicConfig& g_config, int idx) const;
bool check_filament_printable(const DynamicPrintConfig & config, wxString& error_message);
bool check_tpu_printable_status(const DynamicPrintConfig & config, const std::vector<int> &tpu_filaments);
/* instance related operations*/