feat: update happy hare parsing

This commit is contained in:
Ian Chua
2026-10-16 20:48:57 +08:00
parent 7ed4095b20
commit f68fbfed58
8 changed files with 768 additions and 77 deletions
+166 -17
View File
@@ -18,14 +18,80 @@
// TODO: remove this include
#include "slic3r/GUI/DeviceManager.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/GuiColor.hpp"
namespace Slic3r
{
// Bambu keeps its fixed external pair (255 main/right, 254 deputy/left) and its two
// left/right flags. Every other printer addresses one external virtual tray per live
// extruder, so ids 255, 254, 253, ... are extruders 0, 1, 2, ... .
static bool _is_bbl_vendor_printer()
{
return wxTheApp != nullptr && GUI::wxGetApp().preset_bundle != nullptr &&
GUI::wxGetApp().preset_bundle->is_bbl_vendor();
}
// Extruder a virtual (external spool) tray id feeds: VIRTUAL_TRAY_MAIN_ID - tray_id.
// Returns -1 for an empty, non-numeric or out-of-range id.
static int _virtual_tray_extruder_id(const std::string& tray_id)
{
if (tray_id.empty())
return -1;
for (char c : tray_id)
{
if (c < '0' || c > '9')
return -1;
}
try
{
const int vt_id = std::stoi(tray_id);
if (vt_id < 0 || vt_id > VIRTUAL_TRAY_MAIN_ID)
return -1;
return VIRTUAL_TRAY_MAIN_ID - vt_id;
}
catch (...)
{
return -1;
}
}
// True when obj->vt_slot actually carries this virtual tray id as a reported spool. A synthetic
// topology filler (is_slot_placeholder) only makes the slot addressable and does not count.
static bool _has_virtual_tray_slot(const MachineObject* obj, const std::string& tray_id)
{
if (obj == nullptr || tray_id.empty())
return false;
for (const auto& tray : obj->vt_slot)
{
if (!tray.is_slot_placeholder && tray.id == tray_id)
return true;
}
return false;
}
// A generic printer may expose one AMS unit / external slot per extruder. When both legacy
// flags of a category are enabled (the sidebar sync call uses both AMS and both external flags)
// every extruder participates; when only one side is enabled (SelectMachine's left/right split)
// keep to that side's legacy extruder (right = main = 0, left = deputy = 1), so the N slots do
// not leak across sides.
static bool _generic_extruder_in_selected_side(int extruder_id, bool use_right, bool use_left)
{
if (use_right && use_left)
return true;
if (use_right)
return extruder_id == MAIN_EXTRUDER_ID;
if (use_left)
return extruder_id == DEPUTY_EXTRUDER_ID;
return false;
}
bool DevMappingUtil::is_valid_mapping_result(const MachineObject* obj, std::vector<FilamentInfo>& result, bool check_empty_slot)
{
if (result.empty()) return false;
const bool is_bbl_vendor = _is_bbl_vendor_printer();
for (int i = 0; i < result.size(); i++)
{
// invalid mapping result
@@ -42,8 +108,13 @@ namespace Slic3r
auto ams_item = obj->GetFilaSystem()->GetAmsById(result[i].ams_id);
if (ams_item == nullptr)
{
if ((result[i].ams_id != std::to_string(VIRTUAL_TRAY_MAIN_ID)) &&
(result[i].ams_id != std::to_string(VIRTUAL_TRAY_DEPUTY_ID)))
// A virtual (external spool) tray is not an AMS unit. Bambu keeps its fixed pair;
// a generic printer accepts whichever per-extruder slots it actually reported.
const bool is_virtual_tray = is_bbl_vendor
? (result[i].ams_id == std::to_string(VIRTUAL_TRAY_MAIN_ID) ||
result[i].ams_id == std::to_string(VIRTUAL_TRAY_DEPUTY_ID))
: _has_virtual_tray_slot(obj, result[i].ams_id);
if (!is_virtual_tray)
{
result[i].tray_id = -1;
return false;
@@ -94,8 +165,11 @@ namespace Slic3r
result.ams_id = std::to_string(ams_id);
result.slot_id = std::to_string(slot_id);
if (ams_id == VIRTUAL_TRAY_MAIN_ID || ams_id == VIRTUAL_TRAY_DEPUTY_ID)
if (type == DevAms::EXT_SPOOL || ams_id == VIRTUAL_TRAY_MAIN_ID || ams_id == VIRTUAL_TRAY_DEPUTY_ID)
{
// An external spool is addressed by its virtual tray id (255 -> extruder 0, 254 -> 1,
// ...); the id is the tray's identity, so it must not fall through to the AMS
// ams_id * 4 + slot_id indexing.
result.tray_id = atoi(tray.id.c_str());
result.id = atoi(tray.id.c_str());
}
@@ -126,11 +200,25 @@ namespace Slic3r
std::map<int, FilamentInfo> tray_filaments; // tray_index : tray_color
bool left_nozzle_has_ams = false, right_nozzle_has_ams = false;
const bool is_bbl_vendor = _is_bbl_vendor_printer();
// Extruders whose AMS filament participates in this mapping. On a generic printer this
// carries the per-extruder "nozzle already has AMS" state that the legacy right/left flags
// express for Bambu, so the matching external spool can be suppressed.
std::set<int> generic_extruders_with_ams;
const auto& ams_list = obj->GetFilaSystem()->GetAmsList();
for (auto ams = ams_list.begin(); ams != ams_list.end(); ams++)
{
std::string ams_id = ams->second->GetAmsId();
auto ams_type = ams->second->GetAmsType();
const int ams_id_int = atoi(ams->first.c_str());
int generic_tray_base = 0;
if (!is_bbl_vendor) {
for (const auto& other : ams_list) {
if (atoi(other.first.c_str()) < ams_id_int)
generic_tray_base += static_cast<int>(other.second->GetTrays().size());
}
}
// GetAmsType() maps mixed -> AMS_LITE; recover the mixed type so N9 trays index at 24+slot.
if (ams_type == DevAms::AMS_LITE && ams->second->IsAmsLiteMixed())
{
@@ -138,10 +226,16 @@ namespace Slic3r
}
for (auto tray = ams->second->GetTrays().begin(); tray != ams->second->GetTrays().end(); tray++)
{
int ams_id = atoi(ams->first.c_str());
int ams_id = ams_id_int;
int tray_id = atoi(tray->first.c_str());
int tray_index = 0;
if (ams_type == DevAms::AMS || ams_type == DevAms::AMS_LITE || ams_type == DevAms::N3F)
if (!is_bbl_vendor)
{
// Generic units can have different lane counts; index all lanes cumulatively,
// matching the shared tray-index map and sidebar list builder.
tray_index = generic_tray_base + tray_id;
}
else if (ams_type == DevAms::AMS || ams_type == DevAms::AMS_LITE || ams_type == DevAms::N3F)
{
tray_index = ams_id * 4 + tray_id;
}
@@ -169,14 +263,18 @@ namespace Slic3r
if (tray->second->is_tray_info_ready())
{
_parse_tray_info(ams_id, tray_id, ams_type, *(tray->second), info);
// Generic unit lanes use one cumulative index across variable-width units;
// ams_id * 4 + slot_id is only valid for Bambu's fixed four-slot units.
if (!is_bbl_vendor)
info.id = tray_index;
}
//first: left,nozzle=1,map=1 second: right,nozzle=0,map=2
bool right_ams_valid = (ams->second->GetBindedExtruderSet().count(MAIN_EXTRUDER_ID) != 0) && map_opt[MappingOption::USE_RIGHT_AMS];
bool left_ams_valid = (ams->second->GetBindedExtruderSet().count(DEPUTY_EXTRUDER_ID) != 0) && map_opt[MappingOption::USE_LEFT_AMS];
if (right_ams_valid || left_ams_valid)
bool ams_valid = false;
if (is_bbl_vendor)
{
tray_filaments.emplace(std::make_pair(tray_index, info));
//first: left,nozzle=1,map=1 second: right,nozzle=0,map=2
bool right_ams_valid = (ams->second->GetBindedExtruderSet().count(MAIN_EXTRUDER_ID) != 0) && map_opt[MappingOption::USE_RIGHT_AMS];
bool left_ams_valid = (ams->second->GetBindedExtruderSet().count(DEPUTY_EXTRUDER_ID) != 0) && map_opt[MappingOption::USE_LEFT_AMS];
if (right_ams_valid)
{
right_nozzle_has_ams = true;
@@ -185,6 +283,25 @@ namespace Slic3r
{
left_nozzle_has_ams = true;
}
ams_valid = right_ams_valid || left_ams_valid;
}
else
{
// A generic unit is bound to its own extruder, so it takes part whenever the
// request covers that extruder.
for (int extruder_id : ams->second->GetBindedExtruderSet())
{
if (_generic_extruder_in_selected_side(extruder_id, map_opt[MappingOption::USE_RIGHT_AMS], map_opt[MappingOption::USE_LEFT_AMS]))
{
ams_valid = true;
generic_extruders_with_ams.insert(extruder_id);
}
}
}
if (ams_valid)
{
tray_filaments.emplace(std::make_pair(tray_index, info));
}
}
}
@@ -193,10 +310,21 @@ namespace Slic3r
{
for (auto tray : obj->vt_slot)
{
bool right_ext_valid = (tray.id == std::to_string(VIRTUAL_TRAY_MAIN_ID) && map_opt[MappingOption::USE_RIGHT_EXT]);
bool left_ext_valid = (tray.id == std::to_string(VIRTUAL_TRAY_DEPUTY_ID) && map_opt[MappingOption::USE_LEFT_EXT]);
if (right_ext_valid || left_ext_valid)
// A synthetic topology filler is not a reported external spool, so it is never a
// mapping candidate. Bambu keeps its fixed reported pair.
if (!is_bbl_vendor && tray.is_slot_placeholder)
{
continue;
}
if (is_bbl_vendor)
{
// Bambu keeps its fixed pair and the two left/right external flags.
const bool right_ext_valid = (tray.id == std::to_string(VIRTUAL_TRAY_MAIN_ID) && map_opt[MappingOption::USE_RIGHT_EXT]);
const bool left_ext_valid = (tray.id == std::to_string(VIRTUAL_TRAY_DEPUTY_ID) && map_opt[MappingOption::USE_LEFT_EXT]);
if (!right_ext_valid && !left_ext_valid)
{
continue;
}
if (nozzle_has_ams_then_ignore_ext)
{
if (right_ext_valid && right_nozzle_has_ams)
@@ -208,10 +336,31 @@ namespace Slic3r
continue;
}
}
FilamentInfo info;
_parse_tray_info(atoi(tray.id.c_str()), 0, DevAms::EXT_SPOOL, tray, info);
tray_filaments.emplace(std::make_pair(info.tray_id, info));
}
else
{
// Generic: every present virtual tray is the external spool of the extruder it
// addresses (VIRTUAL_TRAY_MAIN_ID - tray.id): 255 -> 0, 254 -> 1, ... . Keep to
// the selected side when only one legacy flag is enabled, and drop the spool when
// its extruder already has AMS filament in this mapping and suppression is on.
const int extruder_id = _virtual_tray_extruder_id(tray.id);
if (extruder_id < 0)
{
continue;
}
if (!_generic_extruder_in_selected_side(extruder_id, map_opt[MappingOption::USE_RIGHT_EXT], map_opt[MappingOption::USE_LEFT_EXT]))
{
continue;
}
if (nozzle_has_ams_then_ignore_ext && generic_extruders_with_ams.count(extruder_id) != 0)
{
continue;
}
}
FilamentInfo info;
_parse_tray_info(atoi(tray.id.c_str()), 0, DevAms::EXT_SPOOL, tray, info);
tray_filaments.emplace(std::make_pair(info.tray_id, info));
}
}
@@ -412,4 +561,4 @@ namespace Slic3r
return 0;
}
}
}