mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-16 20:31:14 +00:00
fix: nozzle & preset sync
This commit is contained in:
@@ -93,19 +93,33 @@ property of the unit kind and the reported trays:
|
||||
The tray ids are the payload's slot indices; they are expected to be unique, 0-based, and contiguous
|
||||
within a unit, because the generic tray-index mapping below is cumulative.
|
||||
|
||||
Bulk/extended spools are carried by `print.vir_slot`, which `DeviceManager` parses into
|
||||
`MachineObject::vt_slot`. Two ids are fixed: `VIRTUAL_TRAY_MAIN_ID = 255` (extruder 0) and
|
||||
`VIRTUAL_TRAY_DEPUTY_ID = 254` (extruder 1); the id convention is `vt_id = 255 - extruder_id`, so
|
||||
further extruders use 253, 252, … . The `vir_slot` parser recognises the 255/254 pair; the agent
|
||||
filament descriptor path `DevFilaSystemParser::ParseAgentFilament` is the entry that stores a fuller
|
||||
descending set in `vt_slot`.
|
||||
### External spool slots
|
||||
|
||||
External/manual spools are virtual trays in `MachineObject::vt_slot`, separate from the trays owned
|
||||
by `DevFilaSystem`. Their stable id is `vt_id = VIRTUAL_TRAY_MAIN_ID - extruder_id`: 255 for
|
||||
extruder 0, 254 for extruder 1, then 253, 252, and so on. The index in `vt_slot` is therefore
|
||||
`VIRTUAL_TRAY_MAIN_ID - vt_id`, not the order in which a printer happens to report trays.
|
||||
|
||||
For non-Bambu printers, slot topology comes from the live `DevExtderSystem` extruder count, not from
|
||||
external-spool telemetry. `MachineObject::ensure_external_virtual_tray_slots()` grows `vt_slot` to
|
||||
one entry per extruder and enables virtual-tray support even when the agent reports no `vir_slot`,
|
||||
`vt_tray`, or external-spool data. It only adds missing entries, so reported tray metadata is kept.
|
||||
The shared ensure runs after push-status parsing has updated the extruder count and after the generic
|
||||
AMS/agent-filament parsers. Thus agents may report external-spool metadata, but do not have to invent
|
||||
empty slots just to make each toolhead's external-spool row visible.
|
||||
|
||||
When a payload does report virtual trays, `vir_slot` entries are placed by their id and missing
|
||||
indices are filled with placeholders. Generic agent `external` entries are likewise placed by
|
||||
extruder id, so sparse or out-of-order reports do not shift a spool onto another toolhead. An empty
|
||||
placeholder represents the slot's existence, not a claim that the printer reported a loaded spool.
|
||||
Bambu keeps its fixed 255/254 virtual-tray pair and does not use generic slot synthesis.
|
||||
|
||||
## Mapping
|
||||
|
||||
Several subsystems relate AMS units, trays, and extruders, and more than one place derives a tray
|
||||
index. The Bambu mapping path (`DevMappingUtil`) computes `ams_id * 4 + slot_id` and knows only the
|
||||
Bambu unit kinds; `DevFilaSystem` provides the device model's own mapping below. The two agree for
|
||||
Bambu units and diverge for a non-Bambu unit whose slot count is not four.
|
||||
index. Bambu mapping computes `ams_id * 4 + slot_id` for its fixed-width units; generic mapping in
|
||||
`DevFilaSystem` and the sidebar matcher uses the count of lanes in preceding units plus the slot id.
|
||||
The two agree for Bambu units and differ for a non-Bambu unit whose slot count is not four.
|
||||
|
||||
### AMS id to extruder id
|
||||
|
||||
@@ -128,13 +142,25 @@ used for backup-slot translation, calibration addressing, and the mapping popups
|
||||
- Generic (non-Bambu): a running lane index — the count of trays in preceding units plus the slot
|
||||
id. This depends on each unit's tray ids being 0-based and contiguous.
|
||||
|
||||
The sidebar sync matcher uses the same identities. Bambu retains its fixed left/right AMS and
|
||||
external-spool choices. For a generic printer, a full sync considers AMS units according to their
|
||||
bound extruder ids and considers every external slot by `255 - vt_id`; a split left/right mapping
|
||||
still restricts itself to the corresponding legacy extruders 0 and 1. Generic AMS lanes use the
|
||||
cumulative tray index above, while their `ams_id`/`slot_id` pair remains the address used to update
|
||||
the matching sidebar entry. The runtime filament-list configs also carry a transient device-extruder
|
||||
association so generic entries are grouped under the correct toolhead after sync; that association
|
||||
is removed before the configs enter the slicing pipeline. Bambu keeps the existing key-based
|
||||
left/right grouping.
|
||||
|
||||
### Virtual-slot recognition
|
||||
|
||||
`devPrinterUtil::IsVirtualSlot()` decides whether an id denotes a virtual tray. On Bambu printers it
|
||||
is the fixed pair `{255, 254}`. On generic printers it is the descending range
|
||||
`[255 - count + 1, 255]`, where `count` is the selected printer preset's extruder count. The preset
|
||||
is expected to match the connected device; when the two disagree the virtual range is wrong, which
|
||||
is why a mismatch is reported rather than silently accepted.
|
||||
`[255 - count + 1, 255]`, where `count` is the selected printer preset's extruder count. Slot
|
||||
topology is grown from the live device extruder count, while this recognition range still uses the
|
||||
preset count; the preset is expected to match the connected device. A mismatch can therefore make
|
||||
the recognized virtual-id range disagree with `vt_slot`, and is reported by the existing count
|
||||
warning rather than silently accepted.
|
||||
|
||||
## Device-control UI
|
||||
|
||||
@@ -148,6 +174,9 @@ The device-control widgets follow the same device-reported counts.
|
||||
toolhead, `CreateAmsDoubleNozzle` for two, and `CreateAmsMultiNozzle` (one preview and one AMS
|
||||
simplebook per toolhead) for three or more. A two-toolhead generic printer therefore keeps the
|
||||
legacy left/right presentation; that is deliberate.
|
||||
- The status panel builds external-spool entries from `MachineObject::vt_slot`; generic slot
|
||||
synthesis keeps one empty entry available for each live toolhead even when the printer does not
|
||||
report external-spool state.
|
||||
- The status panel's `ExtruderImage` renders one per-toolhead state per nozzle and is resized to
|
||||
`GetTotalExtderCount()`. Non-Bambu printers choose the active toolhead through
|
||||
`m_generic_nozzle_selector` (shown when more than one toolhead exists); Bambu keeps its left/right
|
||||
@@ -157,8 +186,9 @@ The device-control widgets follow the same device-reported counts.
|
||||
|
||||
- **Single dialect.** The agent translates its protocol into this JSON; the model does not learn
|
||||
vendor protocols. Extending the model with a new unit kind or field means extending the dialect.
|
||||
- **Agent-produced counts.** Toolhead, AMS, and slot counts come from the device payload. Nothing
|
||||
caps them at two toolheads or four slots except the Bambu contract described above.
|
||||
- **Runtime counts.** The agent reports toolhead count and AMS unit/tray data. The shared model derives
|
||||
generic external-spool slot count from the parsed live toolhead count; nothing caps the number of
|
||||
toolheads or generic slots except the id range and the Bambu contract described above.
|
||||
- **Extruder dialect invariants.** `device.extruder.state`'s count field must equal the length of
|
||||
`info[]`, and `info[]` must be ordered by extruder id with `id` equal to the array index: the
|
||||
parser places toolheads by array position while `GetExtderById()` indexes by id.
|
||||
@@ -185,10 +215,14 @@ The device-control widgets follow the same device-reported counts.
|
||||
`device.extruder` parser
|
||||
- [`DevFilaSystem`](../../src/slic3r/GUI/DeviceCore/DevFilaSystem.h) — AMS/tray model, the AMS parser,
|
||||
and the mapping helpers
|
||||
- [`DevMapping.cpp`](../../src/slic3r/GUI/DeviceCore/DevMapping.cpp) — filament-to-device tray matching
|
||||
for Bambu and generic external/AMS slots
|
||||
- [`PresetBundle.cpp`](../../src/libslic3r/PresetBundle.cpp) — per-extruder grouping of the transient
|
||||
device filament list
|
||||
- [`DevDefs.h`](../../src/slic3r/GUI/DeviceCore/DevDefs.h) — `DevAmsType`, extruder ids, and virtual
|
||||
tray ids
|
||||
- [`DeviceManager.cpp`](../../src/slic3r/GUI/DeviceManager.cpp) — message dispatch, `vir_slot`
|
||||
parsing, and device state assembly
|
||||
parsing, external-slot topology, and device state assembly
|
||||
- [`AMSControl.cpp`](../../src/slic3r/GUI/Widgets/AMSControl.cpp) — per-toolhead AMS panes
|
||||
(`CreateAmsMultiNozzle`) and the generic layout
|
||||
- [`AMSItem.hpp`](../../src/slic3r/GUI/Widgets/AMSItem.hpp) — `AMSModel` and the variable-lane AMS
|
||||
|
||||
+149
-48
@@ -4511,6 +4511,53 @@ static void warn_ambiguous_filament_id_match(const PresetCollection &filaments,
|
||||
<< "\", also matches " << others;
|
||||
}
|
||||
|
||||
// Orca: resolve a compatible system preset from a printer-reported filament type. Prefers an exact
|
||||
// "Generic <type>" match, then the longest generic whose filament_type appears as a whole word in
|
||||
// the reported type (e.g. "ASA" in "ASA Sparkle"). On success `generic_name_out` holds the
|
||||
// "Generic <...>" name used for compatibility messages. Shared by the known-filament_id lookup and
|
||||
// the data-bearing-but-unmapped tray path so both pick the same preset.
|
||||
static PresetCollection::Iterator find_generic_preset_for_type(PresetCollection &filaments, const std::string &filament_type, std::string &generic_name_out)
|
||||
{
|
||||
if (filament_type.empty())
|
||||
return filaments.end();
|
||||
const std::string generic_name = "Generic " + filament_type;
|
||||
auto iter = std::find_if(filaments.begin(), filaments.end(), [&generic_name](const Preset &f) {
|
||||
return f.is_compatible && f.is_system && boost::algorithm::starts_with(f.name, generic_name);
|
||||
});
|
||||
if (iter != filaments.end()) {
|
||||
generic_name_out = generic_name;
|
||||
return iter;
|
||||
}
|
||||
// Similarity fallback: find a generic preset whose filament_type
|
||||
// appears as a whole word in the reported type (e.g. "ASA" in "ASA Sparkle").
|
||||
auto upper_type = boost::to_upper_copy(filament_type);
|
||||
auto contains_word = [](const std::string& haystack, const std::string& needle) {
|
||||
auto pos = haystack.find(needle);
|
||||
while (pos != std::string::npos) {
|
||||
bool start_ok = (pos == 0 || !std::isalnum(static_cast<unsigned char>(haystack[pos - 1])));
|
||||
bool end_ok = (pos + needle.size() >= haystack.size() ||
|
||||
!std::isalnum(static_cast<unsigned char>(haystack[pos + needle.size()])));
|
||||
if (start_ok && end_ok)
|
||||
return true;
|
||||
pos = haystack.find(needle, pos + 1);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
// Find the longest-matching preset type to prefer e.g. "PA-CF" over "PA".
|
||||
size_t best_len = 0;
|
||||
for (auto it = filaments.begin(); it != filaments.end(); ++it) {
|
||||
if (!it->is_compatible || !it->is_system || !boost::algorithm::starts_with(it->name, "Generic "))
|
||||
continue;
|
||||
auto preset_type = boost::to_upper_copy(it->config.opt_string("filament_type", 0u));
|
||||
if (preset_type.size() > best_len && contains_word(upper_type, preset_type)) {
|
||||
iter = it;
|
||||
best_len = preset_type.size();
|
||||
generic_name_out = "Generic " + it->config.opt_string("filament_type", 0u);
|
||||
}
|
||||
}
|
||||
return iter;
|
||||
}
|
||||
|
||||
void PresetBundle::get_ams_cobox_infos(AMSComboInfo& combox_info)
|
||||
{
|
||||
combox_info.clear();
|
||||
@@ -4566,7 +4613,9 @@ void PresetBundle::get_ams_cobox_infos(AMSComboInfo& combox_info)
|
||||
|
||||
unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfig *,std::string>> &unknowns, bool use_map, std::map<int, AMSMapInfo> &maps, bool enable_append, MergeFilamentInfo &merge_info, bool color_only)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "use_map:" << use_map << " enable_append:" << enable_append;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": use_map %1% enable_append %2% color_only %3% start_presets %4% start_colors %5%")
|
||||
% use_map % enable_append % color_only % this->filament_presets.size()
|
||||
% project_config.option<ConfigOptionStrings>("filament_colour")->values.size();
|
||||
std::vector<std::string> ams_filament_presets;
|
||||
std::vector<std::string> ams_filament_colors;
|
||||
std::vector<std::string> ams_filament_color_types;
|
||||
@@ -4578,6 +4627,10 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
||||
bool valid{false};
|
||||
bool is_map{false};
|
||||
bool is_placeholder{false};
|
||||
// Orca: set for a tray that reports filament data (type and/or loaded flag) but carries
|
||||
// no preset id. It is not `valid` (no id to map through), yet it still holds a real spool
|
||||
// and is eligible for the same append step as a mapped tray.
|
||||
bool has_reported_data{false};
|
||||
std::string filament_color = "";
|
||||
std::string filament_color_type = "";
|
||||
std::string filament_preset = "";
|
||||
@@ -4591,12 +4644,32 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
||||
auto filament_id = ams.opt_string("filament_id", 0u);
|
||||
auto filament_color = ams.opt_string("filament_colour", 0u);
|
||||
auto filament_color_type = ams.opt_string("filament_colour_type", 0u);
|
||||
auto filament_type = ams.opt_string("filament_type", 0u);
|
||||
auto filament_exist = ams.has("filament_exist") && ams.opt_bool("filament_exist", 0u);
|
||||
auto filament_changed = !ams.has("filament_changed") || ams.opt_bool("filament_changed");
|
||||
auto filament_multi_color = ams.opt<ConfigOptionStrings>("filament_multi_colour")->values;
|
||||
auto ams_id = ams.opt_string("ams_id", 0u);
|
||||
auto slot_id = ams.opt_string("slot_id", 0u);
|
||||
auto is_placeholder = ams.has("filament_slot_placeholder") && ams.opt_bool("filament_slot_placeholder", 0u);
|
||||
ams_infos.push_back({filament_id.empty() ? false : true, false, is_placeholder, filament_color});
|
||||
// Real reported filament data: the device named a type or flagged the slot loaded. A
|
||||
// topology-only synthetic virtual tray carries neither, so it must not become a filament.
|
||||
bool has_reported_data = !is_placeholder && filament_id.empty() && (!filament_type.empty() || filament_exist);
|
||||
AmsInfo ams_info;
|
||||
ams_info.valid = !filament_id.empty();
|
||||
ams_info.is_placeholder = is_placeholder;
|
||||
ams_info.has_reported_data = has_reported_data;
|
||||
ams_info.filament_color = filament_color;
|
||||
// Only the append path of a data-bearing, id-less tray reads these; the known-id path keeps
|
||||
// taking its color and color type from ams_filament_colors/ams_filament_color_types.
|
||||
if (has_reported_data) {
|
||||
if (ams_info.filament_color.empty())
|
||||
ams_info.filament_color = "#CECECE";
|
||||
ams_info.filament_color_type = filament_color_type.empty() ? "1" : filament_color_type;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": entry %1% ams_id %2% slot_id %3% filament_id %4% type %5% color %6% valid %7% placeholder %8% reported_data %9% changed %10%")
|
||||
% index % ams_id % slot_id % filament_id % filament_type % filament_color
|
||||
% (!filament_id.empty()) % is_placeholder % has_reported_data % filament_changed;
|
||||
ams_infos.push_back(std::move(ams_info));
|
||||
AMSMapInfo temp = {ams_id, slot_id};
|
||||
ams_array_maps.push_back(temp);
|
||||
index++;
|
||||
@@ -4617,14 +4690,45 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
||||
ams_filament_color_types.push_back("");
|
||||
ams_multi_color_filment.push_back({});
|
||||
} else if (use_map) {
|
||||
ams_filament_presets.push_back("Generic PLA");//for unknow matieral
|
||||
auto default_unknown_color = "#CECECE";
|
||||
ams_filament_colors.push_back(default_unknown_color);
|
||||
ams_filament_color_types.push_back("1");
|
||||
if (filament_multi_color.size() == 0) {
|
||||
filament_multi_color.push_back(default_unknown_color);
|
||||
if (has_reported_data) {
|
||||
// Orca: the tray reports a filament but no preset id, so it never reaches the
|
||||
// filament_id lookup. Resolve the same generic preset the id path would pick
|
||||
// from the reported type and keep the reported color; the append step below (if
|
||||
// enabled) then turns it into a sidebar filament. Without a generic match the
|
||||
// slot is dropped rather than invented, exactly like an empty tray.
|
||||
std::string resolved_generic;
|
||||
auto generic = find_generic_preset_for_type(filaments, filament_type, resolved_generic);
|
||||
if (generic == filaments.end()) {
|
||||
// Reported loaded but no usable type: fall back to the first compatible
|
||||
// generic, matching the id path's fallback for an unknown model.
|
||||
generic = std::find_if(filaments.begin(), filaments.end(), [](const Preset &f) {
|
||||
return f.is_compatible && f.is_system && boost::algorithm::starts_with(f.name, "Generic ");
|
||||
});
|
||||
}
|
||||
if (generic != filaments.end()) {
|
||||
auto append_color = filament_color.empty() ? std::string("#CECECE") : filament_color;
|
||||
ams_filament_presets.push_back(generic->name);
|
||||
ams_filament_colors.push_back(append_color);
|
||||
ams_filament_color_types.push_back(filament_color_type.empty() ? "1" : filament_color_type);
|
||||
if (filament_multi_color.size() == 0)
|
||||
filament_multi_color.push_back(append_color);
|
||||
ams_multi_color_filment.push_back(filament_multi_color);
|
||||
continue;
|
||||
}
|
||||
ams_filament_presets.push_back("");
|
||||
ams_filament_colors.push_back("");
|
||||
ams_filament_color_types.push_back("");
|
||||
ams_multi_color_filment.push_back({});
|
||||
} else {
|
||||
ams_filament_presets.push_back("Generic PLA");//for unknow matieral
|
||||
auto default_unknown_color = "#CECECE";
|
||||
ams_filament_colors.push_back(default_unknown_color);
|
||||
ams_filament_color_types.push_back("1");
|
||||
if (filament_multi_color.size() == 0) {
|
||||
filament_multi_color.push_back(default_unknown_color);
|
||||
}
|
||||
ams_multi_color_filment.push_back(filament_multi_color);
|
||||
}
|
||||
ams_multi_color_filment.push_back(filament_multi_color);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -4636,49 +4740,19 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
||||
continue;
|
||||
}
|
||||
bool has_type = false;
|
||||
auto filament_type = ams.opt_string("filament_type", 0u);
|
||||
auto iter = std::find_if(filaments.begin(), filaments.end(), [this, &filament_id, &has_type, filament_type](auto &f) {
|
||||
has_type |= f.config.opt_string("filament_type", 0u) == filament_type;
|
||||
return f.is_compatible && filaments.get_preset_base(f) == &f && f.filament_id == filament_id; });
|
||||
warn_ambiguous_filament_id_match(filaments, iter, filament_id);
|
||||
if (iter == filaments.end()) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": filament_id %1% not found or system or compatible") % filament_id;
|
||||
if (!filament_type.empty()) {
|
||||
auto original_type = filament_type;
|
||||
std::string resolved_generic;
|
||||
auto generic = find_generic_preset_for_type(filaments, filament_type, resolved_generic);
|
||||
if (!filament_type.empty())
|
||||
filament_type = "Generic " + filament_type;
|
||||
iter = std::find_if(filaments.begin(), filaments.end(), [&filament_type](auto &f) {
|
||||
return f.is_compatible && f.is_system
|
||||
&& boost::algorithm::starts_with(f.name, filament_type);
|
||||
});
|
||||
if (iter == filaments.end()) {
|
||||
// Similarity fallback: find a generic preset whose filament_type
|
||||
// appears as a whole word in the AMS type (e.g. "ASA" in "ASA Sparkle").
|
||||
auto upper_type = boost::to_upper_copy(original_type);
|
||||
auto contains_word = [](const std::string& haystack, const std::string& needle) {
|
||||
auto pos = haystack.find(needle);
|
||||
while (pos != std::string::npos) {
|
||||
bool start_ok = (pos == 0 || !std::isalnum(static_cast<unsigned char>(haystack[pos - 1])));
|
||||
bool end_ok = (pos + needle.size() >= haystack.size() ||
|
||||
!std::isalnum(static_cast<unsigned char>(haystack[pos + needle.size()])));
|
||||
if (start_ok && end_ok)
|
||||
return true;
|
||||
pos = haystack.find(needle, pos + 1);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
// Find the longest-matching preset type to prefer e.g. "PA-CF" over "PA".
|
||||
size_t best_len = 0;
|
||||
for (auto it = filaments.begin(); it != filaments.end(); ++it) {
|
||||
if (!it->is_compatible || !it->is_system || !boost::algorithm::starts_with(it->name, "Generic "))
|
||||
continue;
|
||||
auto preset_type = boost::to_upper_copy(it->config.opt_string("filament_type", 0u));
|
||||
if (preset_type.size() > best_len && contains_word(upper_type, preset_type)) {
|
||||
iter = it;
|
||||
best_len = preset_type.size();
|
||||
filament_type = "Generic " + it->config.opt_string("filament_type", 0u);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (generic != filaments.end()) {
|
||||
iter = generic;
|
||||
filament_type = resolved_generic;
|
||||
}
|
||||
if (iter == filaments.end()) {
|
||||
// Prefer old selection
|
||||
@@ -4838,6 +4912,8 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": color_only mapped_any %1% maps %2% entries %3%")
|
||||
% mapped_any % maps.size() % ams_infos.size();
|
||||
filament_color->values = exist_colors;
|
||||
ams_multi_color_filment = exist_multi_color_filment;
|
||||
merge_info.merges.clear();
|
||||
@@ -4897,13 +4973,18 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < ams_infos.size(); i++) {// check append
|
||||
if (ams_infos[i].valid) {
|
||||
// A mapped tray (valid id) and a data-bearing tray without an id both append the same
|
||||
// way; only a tray with neither contributes nothing.
|
||||
if (ams_infos[i].valid || ams_infos[i].has_reported_data) {
|
||||
if (i >= ams_filament_presets.size()) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "check error: array bound (check append)";
|
||||
continue;
|
||||
}
|
||||
ams_infos[i].filament_preset = ams_filament_presets[i];
|
||||
ams_infos[i].mutli_filament_color = ams_multi_color_filment[i];
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": entry %1% mapped %2% preset %3% color %4% valid %5% reported_data %6%")
|
||||
% i % ams_infos[i].is_map % ams_infos[i].filament_preset % ams_infos[i].filament_color
|
||||
% ams_infos[i].valid % ams_infos[i].has_reported_data;
|
||||
if (!ams_infos[i].is_map) {
|
||||
need_append_colors.emplace_back(ams_infos[i]);
|
||||
ams_filament_colors[i] = "";
|
||||
@@ -4913,6 +4994,7 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
||||
}
|
||||
}
|
||||
else {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": entry %1% dropped (no valid id or reported data)") % i;
|
||||
ams_filament_colors[i] = "";
|
||||
ams_filament_color_types[i] = "";
|
||||
ams_filament_presets[i] = "";
|
||||
@@ -4930,7 +5012,7 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
||||
[](std::vector<std::string> &value) { return value.empty(); }),
|
||||
ams_multi_color_filment.end());
|
||||
if (need_append_colors.size() > 0 && enable_append) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "need_append_colors.size() > 0 && enable_append";
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": append candidates %1% (start existing %2%)") % need_append_colors.size() % exist_filament_presets.size();
|
||||
auto get_idx_in_array = [](std::vector<std::string> &presets, std::vector<std::string> &colors, const std::string &preset, const std::string &color) -> int {
|
||||
for (size_t i = 0; i < presets.size(); i++) {
|
||||
if (presets[i] == preset && colors[i] == color) {
|
||||
@@ -4941,17 +5023,22 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
||||
};
|
||||
for (size_t i = 0; i < need_append_colors.size(); i++){
|
||||
if (exist_filament_presets.size() >= MAXIMUM_AMS_SYNC_FILAMENT_NUMBER){
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": append capped at %1%, %2% candidate(s) left") % MAXIMUM_AMS_SYNC_FILAMENT_NUMBER % (need_append_colors.size() - i);
|
||||
break;
|
||||
}
|
||||
auto idx = get_idx_in_array(exist_filament_presets, exist_colors, need_append_colors[i].filament_preset, need_append_colors[i].filament_color);
|
||||
if (idx >= 0) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": append dedup preset %1% color %2% already at %3%") % need_append_colors[i].filament_preset % need_append_colors[i].filament_color % idx;
|
||||
continue;
|
||||
}
|
||||
exist_filament_presets.push_back(need_append_colors[i].filament_preset);
|
||||
exist_colors.push_back(need_append_colors[i].filament_color);
|
||||
exist_color_types.push_back(need_append_colors[i].filament_color_type);
|
||||
exist_multi_color_filment.push_back(need_append_colors[i].mutli_filament_color);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": appended preset %1% color %2% type %3% -> slot %4%") % need_append_colors[i].filament_preset % need_append_colors[i].filament_color % need_append_colors[i].filament_color_type % (exist_filament_presets.size() - 1);
|
||||
}
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": append skipped candidates %1% enable_append %2%") % need_append_colors.size() % enable_append;
|
||||
}
|
||||
filament_color->values = exist_colors;
|
||||
filament_color_type->values = exist_color_types;
|
||||
@@ -5067,7 +5154,7 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
||||
// Update ams_multi_color_filment
|
||||
update_filament_multi_color();
|
||||
update_multi_material_filament_presets();
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "finish sync ams list";
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": finish sync ams list, end_presets %1% end_colors %2%") % this->filament_presets.size() % filament_color->values.size();
|
||||
return this->filament_presets.size();
|
||||
}
|
||||
|
||||
@@ -5146,6 +5233,20 @@ std::vector<std::vector<DynamicPrintConfig>> PresetBundle::get_extruder_filament
|
||||
if (extruder_nums > 1) {
|
||||
filament_infos.resize(extruder_nums, std::vector<DynamicPrintConfig>());
|
||||
for (auto ams_item : filament_ams_list) {
|
||||
// A generic slot carries the extruder it actually belongs to, so it lands in that
|
||||
// extruder's bucket instead of the legacy left/right pair. Bambu entries (and anything
|
||||
// without the field) keep the existing 0x10000 key convention.
|
||||
const bool has_extruder_id = ams_item.second.has("device_tray_extruder_id");
|
||||
const int entry_extruder = has_extruder_id ? ams_item.second.opt_int("device_tray_extruder_id") : -1;
|
||||
if (has_extruder_id) {
|
||||
if (entry_extruder >= 0 && entry_extruder < extruder_nums) {
|
||||
// Drop the transient association before the config reaches the slicing pipeline.
|
||||
DynamicPrintConfig entry = ams_item.second;
|
||||
entry.erase("device_tray_extruder_id");
|
||||
filament_infos[entry_extruder].push_back(std::move(entry));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (ams_item.first & 0x10000) { // right
|
||||
filament_infos[1].push_back(ams_item.second);
|
||||
} else { // left
|
||||
|
||||
+182
-31
@@ -20,6 +20,7 @@
|
||||
#include <clocale>
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <ios>
|
||||
#include <iomanip>
|
||||
#include <cstring>
|
||||
@@ -2361,13 +2362,18 @@ bool Sidebar::priv::sync_extruder_list(bool &only_external_material, bool is_man
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this->plater)
|
||||
return false;
|
||||
const Preset &selected_preset = preset_bundle->printers.get_selected_preset();
|
||||
const Preset *parent_preset = preset_bundle->printers.get_preset_parent(selected_preset);
|
||||
const bool inherits_system_preset = selected_preset.is_user() && parent_preset && parent_preset->is_system;
|
||||
if (!inherits_system_preset) {
|
||||
if (!this->plater)
|
||||
return false;
|
||||
|
||||
this->plater->update_objects_position_when_select_preset([machine_preset]() {
|
||||
Tab *printer_tab = GUI::wxGetApp().get_tab(Preset::Type::TYPE_PRINTER);
|
||||
printer_tab->select_preset(machine_preset->name);
|
||||
});
|
||||
this->plater->update_objects_position_when_select_preset([machine_preset]() {
|
||||
Tab *printer_tab = GUI::wxGetApp().get_tab(Preset::Type::TYPE_PRINTER);
|
||||
printer_tab->select_preset(machine_preset->name);
|
||||
});
|
||||
}
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " go on sync_extruder_list";
|
||||
const Preset &cur_preset = preset_bundle->printers.get_selected_preset();
|
||||
@@ -2379,7 +2385,19 @@ bool Sidebar::priv::sync_extruder_list(bool &only_external_material, bool is_man
|
||||
assert(physical_extruder_map->values.size() == extruder_nums);
|
||||
extruder_map = physical_extruder_map->values;
|
||||
}
|
||||
assert(obj->GetExtderSystem()->GetTotalExtderCount() == extruder_nums);
|
||||
const int device_extruder_count = obj->GetExtderSystem()->GetTotalExtderCount();
|
||||
if (device_extruder_count != extruder_nums) {
|
||||
MessageDialog dlg(
|
||||
this->plater,
|
||||
format_wxstr(
|
||||
_L("The connected printer reports %1% extruder(s), but the selected printer preset has %2%. "
|
||||
"Update the preset to match the printer before syncing."),
|
||||
device_extruder_count, extruder_nums),
|
||||
_L("Sync printer information"),
|
||||
wxOK | wxICON_WARNING);
|
||||
dlg.ShowModal();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Multi-nozzle: resolve the nozzle option (pops MultiNozzleSyncDialog when is_manual). No-op for every
|
||||
// existing printer — support_multi_nozzle is false unless some extruder_max_nozzle_count entry is a real
|
||||
@@ -2458,26 +2476,60 @@ bool Sidebar::priv::sync_extruder_list(bool &only_external_material, bool is_man
|
||||
int main_index = obj->is_main_extruder_on_left() ? 0 : 1;
|
||||
int deputy_index = obj->is_main_extruder_on_left() ? 1 : 0;
|
||||
|
||||
// Nozzle diameters are not changed by device sync. If the device reports a different or unavailable
|
||||
// diameter, warn and leave the preset and controls alone while syncing AMS and other printer info.
|
||||
const ConfigOptionFloats *preset_nozzle_diameter =
|
||||
preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter");
|
||||
bool nozzle_diameters_match = false;
|
||||
if (preset_nozzle_diameter != nullptr && preset_nozzle_diameter->values.size() == nozzle_diameters.size()) {
|
||||
nozzle_diameters_match = true;
|
||||
for (size_t index = 0; index < nozzle_diameters.size(); ++index) {
|
||||
// A diameter the device does not report (missing, zero, negative or not a finite number) never matches.
|
||||
const double device_diameter = nozzle_diameters[index];
|
||||
const double preset_diameter = preset_nozzle_diameter->values[index];
|
||||
if (!std::isfinite(device_diameter) || device_diameter <= 0.0 ||
|
||||
!std::isfinite(preset_diameter) || preset_diameter <= 0.0 ||
|
||||
std::fabs(device_diameter - preset_diameter) > EPSILON) {
|
||||
nozzle_diameters_match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!nozzle_diameters_match) {
|
||||
if (is_manual) {
|
||||
MessageDialog dlg(
|
||||
this->plater,
|
||||
_L("The nozzle diameter reported by the connected printer does not match the current printer preset. "
|
||||
"The nozzle diameter and the printer preset will not be synchronized. "
|
||||
"Select the printer preset that matches the nozzle installed on the printer, then sync again."),
|
||||
_L("Sync printer information"), wxICON_WARNING | wxOK);
|
||||
dlg.ShowModal();
|
||||
} else {
|
||||
// An automatic sync runs on every device update, so log the mismatch instead of popping a dialog.
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << __LINE__
|
||||
<< " nozzle diameter mismatch between connected printer and printer preset,"
|
||||
" skip syncing the nozzle diameter";
|
||||
}
|
||||
}
|
||||
|
||||
if (extruder_nums > 1) {
|
||||
int left_index = left_extruder->combo_diameter->FindString(get_diameter_string(nozzle_diameters[0]));
|
||||
int right_index = left_extruder->combo_diameter->FindString(get_diameter_string(nozzle_diameters[1]));
|
||||
assert(left_index != -1 && right_index != -1);
|
||||
left_extruder->combo_diameter->SetSelection(left_index);
|
||||
right_extruder->combo_diameter->SetSelection(right_index);
|
||||
is_switching_diameter = true;
|
||||
switch_diameter(false);
|
||||
is_switching_diameter = false;
|
||||
if (nozzle_diameters_match) {
|
||||
int left_index = left_extruder->combo_diameter->FindString(get_diameter_string(nozzle_diameters[0]));
|
||||
int right_index = left_extruder->combo_diameter->FindString(get_diameter_string(nozzle_diameters[1]));
|
||||
assert(left_index != -1 && right_index != -1);
|
||||
left_extruder->combo_diameter->SetSelection(left_index);
|
||||
right_extruder->combo_diameter->SetSelection(right_index);
|
||||
}
|
||||
AMSCountPopupWindow::SetAMSCount(deputy_index, deputy_4, deputy_1);
|
||||
AMSCountPopupWindow::SetAMSCount(main_index, main_4, main_1);
|
||||
AMSCountPopupWindow::UpdateAMSCount(0, left_extruder);
|
||||
AMSCountPopupWindow::UpdateAMSCount(1, right_extruder);
|
||||
} else {
|
||||
int index = single_extruder->combo_diameter->FindString(get_diameter_string(nozzle_diameters[0]));
|
||||
assert(index != -1);
|
||||
single_extruder->combo_diameter->SetSelection(index);
|
||||
is_switching_diameter = true;
|
||||
switch_diameter(true);
|
||||
is_switching_diameter = false;
|
||||
if (nozzle_diameters_match) {
|
||||
int index = single_extruder->combo_diameter->FindString(get_diameter_string(nozzle_diameters[0]));
|
||||
assert(index != -1);
|
||||
single_extruder->combo_diameter->SetSelection(index);
|
||||
}
|
||||
}
|
||||
|
||||
// set nozzle volume type after switching prset, so this value can override the old value stored in conf
|
||||
@@ -6069,7 +6121,9 @@ void Sidebar::on_bed_type_change(BedType bed_type)
|
||||
*
|
||||
* Return Value:
|
||||
* - Map key encoding:
|
||||
* - Virtual trays: 0x10000 + vt_tray.id (first/main extruder), or just vt_tray.id (secondary)
|
||||
* - Virtual trays: 0x10000 + vt_tray.id for the main extruder's slot, or just vt_tray.id for the
|
||||
* others. Bambu's fixed pair is 255 (main) / 254 (secondary); a generic printer identifies each
|
||||
* slot by its own id (extruder = VIRTUAL_TRAY_MAIN_ID - id: 255 -> 0, 254 -> 1, ...)
|
||||
* - AMS trays: 0x10000 + (ams_id * 4 + slot_id) (main extruder), or (ams_id * 4 + slot_id) (secondary)
|
||||
* - Generic (non-Bambu) units are not four slots wide: their trays index by a cumulative lane
|
||||
* count across units instead of ams_id * 4 + slot_id
|
||||
@@ -6092,7 +6146,13 @@ std::map<int, DynamicPrintConfig> Sidebar::build_filament_ams_list(MachineObject
|
||||
}
|
||||
}
|
||||
|
||||
auto build_tray_config = [](DevAmsTray const &tray, std::string const &name, std::string ams_id, std::string slot_id) {
|
||||
const bool is_bbl_vendor = wxTheApp != nullptr && wxGetApp().preset_bundle != nullptr && wxGetApp().preset_bundle->is_bbl_vendor();
|
||||
|
||||
// Diagnostic: keys produced from virtual (external) trays, so the final summary can label each
|
||||
// output entry's origin as virtual vs AMS.
|
||||
std::set<int> virtual_slot_keys;
|
||||
|
||||
auto build_tray_config = [is_bbl_vendor](DevAmsTray const &tray, std::string const &name, std::string ams_id, std::string slot_id, int extruder_id) {
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("build_filament_ams_list: name %1% setting_id %2% type %3% color %4%")
|
||||
% name % tray.setting_id % tray.m_fila_type % tray.color;
|
||||
DynamicPrintConfig tray_config;
|
||||
@@ -6107,6 +6167,12 @@ std::map<int, DynamicPrintConfig> Sidebar::build_filament_ams_list(MachineObject
|
||||
tray_config.set_key_value("filament_colour_type", new ConfigOptionStrings{std::to_string(tray.ctype)});
|
||||
tray_config.set_key_value("filament_exist", new ConfigOptionBools{tray.is_exists});
|
||||
tray_config.set_key_value("filament_slot_placeholder", new ConfigOptionBools{tray.is_slot_placeholder});
|
||||
// Transient (never persisted) per-extruder association of a generic slot, so the sync and
|
||||
// the slicing per-extruder grouping keep the real extruder instead of the legacy left/right
|
||||
// buckets. Bambu entries omit it and keep the legacy key-only convention.
|
||||
if (!is_bbl_vendor) {
|
||||
tray_config.set_key_value("device_tray_extruder_id", new ConfigOptionInt(extruder_id));
|
||||
}
|
||||
std::optional<FilamentBaseInfo> info;
|
||||
if (wxGetApp().preset_bundle) {
|
||||
info = wxGetApp().preset_bundle->get_filament_by_filament_id(tray.setting_id);
|
||||
@@ -6119,10 +6185,44 @@ std::map<int, DynamicPrintConfig> Sidebar::build_filament_ams_list(MachineObject
|
||||
};
|
||||
|
||||
if (obj->ams_support_virtual_tray) {
|
||||
int extruder = 0x10000; // Main (first) extruder at right
|
||||
for (auto & vt_tray : obj->vt_slot) {
|
||||
filament_ams_list.emplace(extruder + stoi(vt_tray.id), build_tray_config(vt_tray, "Ext",vt_tray.id, "0"));//254 or 255
|
||||
extruder = 0;
|
||||
for (size_t index = 0; index < obj->vt_slot.size(); ++index) {
|
||||
auto & vt_tray = obj->vt_slot[index];
|
||||
int vt_id = 0;
|
||||
if (vt_tray.id.empty()) continue;
|
||||
if (!std::all_of(vt_tray.id.begin(), vt_tray.id.end(), [](unsigned char c) { return std::isdigit(c) != 0; })) continue;
|
||||
try { vt_id = std::stoi(vt_tray.id); } catch (...) { continue; }
|
||||
if (vt_id < 0 || vt_id > VIRTUAL_TRAY_MAIN_ID) continue;
|
||||
// Enumerate the extruder ordinal (the vt_slot index) and validate the expected tray id
|
||||
// instead of trusting the vector size: a stray filler or a stale id for a non-live
|
||||
// extruder must never surface as a real tray. Bambu keeps its fixed pair untouched.
|
||||
if (!is_bbl_vendor) {
|
||||
const int expected_id = VIRTUAL_TRAY_MAIN_ID - static_cast<int>(index);
|
||||
if (vt_id != expected_id || !devPrinterUtil::IsVirtualSlot(vt_id)) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": skip vt_slot index %1% id %2% expected %3%") % index % vt_id % expected_id;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Bambu keeps its fixed pair with the leading (main/right) slot marked 0x10000. On a
|
||||
// generic printer every external slot is identified by its own virtual tray id
|
||||
// (extruder = VIRTUAL_TRAY_MAIN_ID - id: 255 -> 0, 254 -> 1, ...), so mark the main
|
||||
// extruder's slot by identity rather than by position in vt_slot. The map key then
|
||||
// keeps each slot bound to its extruder across refreshes and sparse reports.
|
||||
const bool is_main_slot = is_bbl_vendor ? (&vt_tray == &obj->vt_slot.front())
|
||||
: (vt_id == VIRTUAL_TRAY_MAIN_ID);
|
||||
const int key = (is_main_slot ? 0x10000 : 0) + vt_id;
|
||||
const auto inserted = filament_ams_list.emplace(key,
|
||||
build_tray_config(vt_tray, "Ext", vt_tray.id, "0", VIRTUAL_TRAY_MAIN_ID - vt_id));
|
||||
virtual_slot_keys.insert(key);
|
||||
if (!inserted.second) {
|
||||
const auto &existing = inserted.first->second;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": virtual insert collision key %1% incoming_id %2% existing_origin %3% existing_ams %4% existing_slot %5% existing_type %6% existing_id %7%")
|
||||
% key % vt_id
|
||||
% (virtual_slot_keys.count(key) ? "virtual" : "ams")
|
||||
% existing.opt_string("ams_id", 0u)
|
||||
% existing.opt_string("slot_id", 0u)
|
||||
% existing.opt_string("filament_type", 0u)
|
||||
% existing.opt_string("filament_id", 0u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6138,12 +6238,12 @@ std::map<int, DynamicPrintConfig> Sidebar::build_filament_ams_list(MachineObject
|
||||
return std::string();
|
||||
};
|
||||
|
||||
const bool is_bbl_vendor = wxTheApp != nullptr && wxGetApp().preset_bundle != nullptr && wxGetApp().preset_bundle->is_bbl_vendor();
|
||||
|
||||
auto list = obj->GetFilaSystem()->GetAmsList();
|
||||
for (auto ams : list) {
|
||||
int ams_id = std::stoi(ams.first);
|
||||
int extruder = ams.second->GetExtruderId() ? 0 : 0x10000; // Main (first) extruder at right
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": AMS unit %1% extruder %2% trays %3%")
|
||||
% ams_id % ams.second->GetExtruderId() % ams.second->GetTrays().size();
|
||||
// Bambu units are four slots wide; a generic unit reports its own lane count and its trays
|
||||
// carry a cumulative lane index. The AMS map is keyed by string id, so count preceding units
|
||||
// numerically to keep this the same index the parser and the agent use.
|
||||
@@ -6154,12 +6254,49 @@ std::map<int, DynamicPrintConfig> Sidebar::build_filament_ams_list(MachineObject
|
||||
generic_tray_base += static_cast<int>(other.second->GetTrays().size());
|
||||
}
|
||||
}
|
||||
size_t inserted_trays = 0;
|
||||
size_t rejected_trays = 0;
|
||||
for (auto tray : ams.second->GetTrays()) {
|
||||
int slot_id = std::stoi(tray.first);
|
||||
const int tray_index = is_bbl_vendor ? ams_id * 4 + slot_id : generic_tray_base + slot_id;
|
||||
filament_ams_list.emplace(extruder + tray_index,
|
||||
build_tray_config(*tray.second, get_ams_name(ams_id, slot_id), std::to_string(ams_id), std::to_string(slot_id)));
|
||||
const int key = extruder + tray_index;
|
||||
auto inserted = filament_ams_list.emplace(key,
|
||||
build_tray_config(*tray.second, get_ams_name(ams_id, slot_id), std::to_string(ams_id), std::to_string(slot_id),
|
||||
ams.second->GetExtruderId()));
|
||||
if (inserted.second) {
|
||||
++inserted_trays;
|
||||
} else {
|
||||
++rejected_trays;
|
||||
const auto &existing = inserted.first->second;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": AMS insert collision key %1% incoming_ams %2% incoming_slot %3% incoming_extruder %4% existing_origin %5% existing_ams %6% existing_slot %7% existing_extruder %8% existing_type %9% existing_id %10%")
|
||||
% key % ams_id % slot_id % ams.second->GetExtruderId()
|
||||
% (virtual_slot_keys.count(key) ? "virtual" : "ams")
|
||||
% existing.opt_string("ams_id", 0u)
|
||||
% existing.opt_string("slot_id", 0u)
|
||||
% (existing.has("device_tray_extruder_id") ? existing.opt_int("device_tray_extruder_id") : -1)
|
||||
% existing.opt_string("filament_type", 0u)
|
||||
% existing.opt_string("filament_id", 0u);
|
||||
}
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": AMS unit %1% tray inserts accepted %2% rejected %3%")
|
||||
% ams_id % inserted_trays % rejected_trays;
|
||||
}
|
||||
|
||||
// Diagnostic: dump the final output map (origin, key, identity/data flags) so parser output can
|
||||
// be correlated with the entries logged at the sync boundary.
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": output entries %1%") % filament_ams_list.size();
|
||||
for (const auto &entry : filament_ams_list) {
|
||||
const auto &cfg = entry.second;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": out %1% key %2% ams_id %3% slot_id %4% extruder %5% type %6% id %7% exist %8% placeholder %9%")
|
||||
% (virtual_slot_keys.count(entry.first) ? "virtual" : "ams")
|
||||
% entry.first
|
||||
% cfg.opt_string("ams_id", 0u)
|
||||
% cfg.opt_string("slot_id", 0u)
|
||||
% (cfg.has("device_tray_extruder_id") ? cfg.opt_int("device_tray_extruder_id") : -1)
|
||||
% cfg.opt_string("filament_type", 0u)
|
||||
% cfg.opt_string("filament_id", 0u)
|
||||
% cfg.opt_bool("filament_exist", 0u)
|
||||
% cfg.opt_bool("filament_slot_placeholder", 0u);
|
||||
}
|
||||
return filament_ams_list;
|
||||
}
|
||||
@@ -6396,6 +6533,20 @@ void Sidebar::sync_ams_list(bool is_from_big_sync_btn)
|
||||
std::vector<std::pair<DynamicPrintConfig *,std::string>> unknowns;
|
||||
auto enable_append = wxGetApp().app_config->get_bool("enable_append_color_by_sync_ams");
|
||||
auto sync_color_only = wxGetApp().app_config->get("sync_ams_filament_mode") == "1";
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": sync_color_only %1% enable_append %2% direct_sync %3% effective_use_map %4% filament_ams_list %5%")
|
||||
% sync_color_only % enable_append % sync_result.direct_sync % (!sync_result.direct_sync) % list.size();
|
||||
for (const auto &entry : list) {
|
||||
const auto &tray = entry.second;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": tray %1% ams_id %2% slot_id %3% filament_id %4% type %5% color %6% filament_exist %7% placeholder %8%")
|
||||
% entry.first
|
||||
% tray.opt_string("ams_id", 0u)
|
||||
% tray.opt_string("slot_id", 0u)
|
||||
% tray.opt_string("filament_id", 0u)
|
||||
% tray.opt_string("filament_type", 0u)
|
||||
% tray.opt_string("filament_colour", 0u)
|
||||
% (tray.has("filament_exist") && tray.opt_bool("filament_exist", 0u))
|
||||
% (tray.has("filament_slot_placeholder") && tray.opt_bool("filament_slot_placeholder", 0u));
|
||||
}
|
||||
auto n = wxGetApp().preset_bundle->sync_ams_list(unknowns, !sync_result.direct_sync, sync_result.sync_maps, enable_append, merge_info, sync_color_only);
|
||||
wxString detail;
|
||||
for (auto & uk : unknowns) {
|
||||
|
||||
@@ -1155,7 +1155,7 @@ void SelectMachineDialog::sync_ams_mapping_result(std::vector<FilamentInfo> &res
|
||||
wxString ams_id;
|
||||
wxColour ams_col;
|
||||
|
||||
if (f->tray_id == VIRTUAL_TRAY_MAIN_ID || f->tray_id == VIRTUAL_TRAY_DEPUTY_ID)
|
||||
if (devPrinterUtil::IsVirtualSlot(f->ams_id))
|
||||
{
|
||||
ams_id = _L("Ext");
|
||||
}else if (f->tray_id >= 0) {
|
||||
@@ -1347,6 +1347,8 @@ bool SelectMachineDialog::get_ams_mapping_result(std::string &mapping_array_str,
|
||||
if (m_ams_mapping_result.empty())
|
||||
return false;
|
||||
|
||||
const bool is_bbl_vendor = wxTheApp != nullptr && wxGetApp().preset_bundle != nullptr && wxGetApp().preset_bundle->is_bbl_vendor();
|
||||
|
||||
bool valid_mapping_result = true;
|
||||
int invalid_count = 0;
|
||||
for (int i = 0; i < m_ams_mapping_result.size(); i++) {
|
||||
@@ -1402,7 +1404,10 @@ bool SelectMachineDialog::get_ams_mapping_result(std::string &mapping_array_str,
|
||||
// convert #RRGGBB to RRGGBBAA
|
||||
mapping_item["sourceColor"] = m_filaments[k].color;
|
||||
mapping_item["targetColor"] = m_ams_mapping_result[k].color;
|
||||
if (tray_id == VIRTUAL_TRAY_MAIN_ID || tray_id == VIRTUAL_TRAY_DEPUTY_ID) { tray_id = -1; }
|
||||
// The legacy v0 mapping array encodes an external spool as -1, a BBL-firmware
|
||||
// contract. A generic printer keeps the virtual tray id (255 - extruder) so
|
||||
// the payload still says which extruder's external slot was mapped.
|
||||
if (is_bbl_vendor && (tray_id == VIRTUAL_TRAY_MAIN_ID || tray_id == VIRTUAL_TRAY_DEPUTY_ID)) { tray_id = -1; }
|
||||
|
||||
/*new ams mapping data*/
|
||||
try {
|
||||
|
||||
@@ -1245,7 +1245,7 @@ void SyncAmsInfoDialog::sync_ams_mapping_result(std::vector<FilamentInfo> &resul
|
||||
wxString ams_id;
|
||||
wxColour ams_col;
|
||||
|
||||
if (f->tray_id == VIRTUAL_TRAY_MAIN_ID || f->tray_id == VIRTUAL_TRAY_DEPUTY_ID) {
|
||||
if (devPrinterUtil::IsVirtualSlot(f->ams_id)) {
|
||||
ams_id = _L("Ext");
|
||||
}
|
||||
|
||||
@@ -1393,6 +1393,8 @@ bool SyncAmsInfoDialog::get_ams_mapping_result(std::string &mapping_array_str, s
|
||||
if (m_ams_mapping_result.empty())
|
||||
return false;
|
||||
|
||||
const bool is_bbl_vendor = wxTheApp != nullptr && wxGetApp().preset_bundle != nullptr && wxGetApp().preset_bundle->is_bbl_vendor();
|
||||
|
||||
bool valid_mapping_result = true;
|
||||
int invalid_count = 0;
|
||||
for (int i = 0; i < m_ams_mapping_result.size(); i++) {
|
||||
@@ -1449,7 +1451,10 @@ bool SyncAmsInfoDialog::get_ams_mapping_result(std::string &mapping_array_str, s
|
||||
// convert #RRGGBB to RRGGBBAA
|
||||
mapping_item["sourceColor"] = m_filaments[k].color;
|
||||
mapping_item["targetColor"] = m_ams_mapping_result[k].color;
|
||||
if (tray_id == VIRTUAL_TRAY_MAIN_ID || tray_id == VIRTUAL_TRAY_DEPUTY_ID) { tray_id = -1; }
|
||||
// The legacy v0 mapping array encodes an external spool as -1, a BBL-firmware
|
||||
// contract. A generic printer keeps the virtual tray id (255 - extruder) so the
|
||||
// payload still says which extruder's external slot was mapped.
|
||||
if (is_bbl_vendor && (tray_id == VIRTUAL_TRAY_MAIN_ID || tray_id == VIRTUAL_TRAY_DEPUTY_ID)) { tray_id = -1; }
|
||||
|
||||
/*new ams mapping data*/
|
||||
try {
|
||||
|
||||
@@ -2304,6 +2304,105 @@ TEST_CASE("set_num_filaments keeps mixed-color arrays in step with the filament
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// One system "Generic PLA" the sync can resolve a reported "PLA" tray to, with a real
|
||||
// filament_id so a tray that does report one still maps through the known-id path.
|
||||
void add_generic_pla(PresetBundle &bundle)
|
||||
{
|
||||
Preset &pla = add_inmemory_preset(bundle.filaments, "Generic PLA");
|
||||
pla.is_system = true;
|
||||
pla.is_compatible = true;
|
||||
pla.filament_id = "GFL99";
|
||||
}
|
||||
|
||||
// A device tray as build_filament_ams_list builds it: a reported type, color and loaded flag,
|
||||
// an optional preset id, and the placeholder flag pull-mode agents set on empty slots.
|
||||
DynamicPrintConfig ams_tray(const std::string &filament_id, const std::string &filament_type, bool exist, bool placeholder)
|
||||
{
|
||||
DynamicPrintConfig tray;
|
||||
tray.set_key_value("filament_id", new ConfigOptionStrings{filament_id});
|
||||
tray.set_key_value("filament_type", new ConfigOptionStrings{filament_type});
|
||||
tray.set_key_value("filament_colour", new ConfigOptionStrings{"#FF0000"});
|
||||
tray.set_key_value("filament_colour_type", new ConfigOptionStrings{"1"});
|
||||
tray.set_key_value("filament_multi_colour", new ConfigOptionStrings{});
|
||||
tray.set_key_value("filament_exist", new ConfigOptionBools{exist});
|
||||
tray.set_key_value("filament_slot_placeholder", new ConfigOptionBools{placeholder});
|
||||
tray.set_key_value("ams_id", new ConfigOptionStrings{"0"});
|
||||
tray.set_key_value("slot_id", new ConfigOptionStrings{"0"});
|
||||
return tray;
|
||||
}
|
||||
|
||||
// Run the mapped sync over the bundle's single tray and return the filament count it reports,
|
||||
// which is what the sidebar uses to rebuild the Prepare filament list.
|
||||
unsigned int sync_one_tray(PresetBundle &bundle, bool enable_append)
|
||||
{
|
||||
std::vector<std::pair<DynamicPrintConfig *, std::string>> unknowns;
|
||||
std::map<int, AMSMapInfo> maps;
|
||||
MergeFilamentInfo merge;
|
||||
return bundle.sync_ams_list(unknowns, /*use_map=*/true, maps, enable_append, merge, /*color_only=*/false);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// A generic device reports type/color/loaded for a spool but no preset id. The Device tab shows
|
||||
// that spool, so the Prepare sidebar has to count it: the sync appends the generic preset its
|
||||
// reported type resolves to. A tray that only carried topology (no type, not loaded) must not.
|
||||
TEST_CASE("A data-bearing AMS tray without a preset id appends a generic filament on sync", "[Preset][Bundle]")
|
||||
{
|
||||
PresetBundle bundle;
|
||||
add_generic_pla(bundle);
|
||||
bundle.set_num_filaments(1, std::string("#00FF00"));
|
||||
bundle.filament_ams_list[0] = ams_tray(/*filament_id=*/{}, "PLA", /*exist=*/true, /*placeholder=*/false);
|
||||
|
||||
const size_t before = bundle.filament_presets.size();
|
||||
const auto count = sync_one_tray(bundle, /*enable_append=*/true);
|
||||
|
||||
CHECK(count == before + 1);
|
||||
CHECK(bundle.filament_presets.back() == "Generic PLA");
|
||||
}
|
||||
|
||||
TEST_CASE("An empty synthetic AMS tray does not append a filament on sync", "[Preset][Bundle]")
|
||||
{
|
||||
PresetBundle bundle;
|
||||
add_generic_pla(bundle);
|
||||
bundle.set_num_filaments(1, std::string("#00FF00"));
|
||||
bundle.filament_ams_list[0] = ams_tray(/*filament_id=*/{}, /*filament_type=*/{}, /*exist=*/false, /*placeholder=*/false);
|
||||
|
||||
const size_t before = bundle.filament_presets.size();
|
||||
const auto count = sync_one_tray(bundle, /*enable_append=*/true);
|
||||
|
||||
CHECK(count == before);
|
||||
}
|
||||
|
||||
TEST_CASE("An empty AMS placeholder does not append a filament on sync", "[Preset][Bundle]")
|
||||
{
|
||||
PresetBundle bundle;
|
||||
add_generic_pla(bundle);
|
||||
bundle.set_num_filaments(1, std::string("#00FF00"));
|
||||
bundle.filament_ams_list[0] = ams_tray(/*filament_id=*/{}, /*filament_type=*/{}, /*exist=*/false, /*placeholder=*/true);
|
||||
|
||||
const size_t before = bundle.filament_presets.size();
|
||||
const auto count = sync_one_tray(bundle, /*enable_append=*/true);
|
||||
|
||||
CHECK(count == before);
|
||||
}
|
||||
|
||||
// The append preference still governs a data-bearing tray: with append off the sidebar count is
|
||||
// unchanged, exactly as for a mapped-but-unlisted tray.
|
||||
TEST_CASE("The append preference gates a data-bearing AMS tray without a preset id", "[Preset][Bundle]")
|
||||
{
|
||||
PresetBundle bundle;
|
||||
add_generic_pla(bundle);
|
||||
bundle.set_num_filaments(1, std::string("#00FF00"));
|
||||
bundle.filament_ams_list[0] = ams_tray(/*filament_id=*/{}, "PLA", /*exist=*/true, /*placeholder=*/false);
|
||||
|
||||
const size_t before = bundle.filament_presets.size();
|
||||
const auto count = sync_one_tray(bundle, /*enable_append=*/false);
|
||||
|
||||
CHECK(count == before);
|
||||
}
|
||||
|
||||
// A mix is described by 1-based indices into the project's filament list, which Orca rebuilds
|
||||
// from the selected printer's snapshot (filament_%02u / filament_colors) at startup and on every
|
||||
// printer selection. Held anywhere but that same per-printer snapshot, the mixed arrays end up
|
||||
|
||||
Reference in New Issue
Block a user