mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-09 08:41:14 +00:00
fix: harden generic N-extruder device model and status/AMS UI, add N-toolhead ExtruderImage sizing, tests, and HLSD
This commit is contained in:
@@ -144,10 +144,10 @@ The device-control widgets follow the same device-reported counts.
|
||||
`N3F_AMS`, `N3S_AMS`. A plain `AMS` maps to `GENERIC_AMS`.
|
||||
- On non-Bambu printers `use_generic_ams_layout()` routes `GENERIC_AMS` units through a variable-lane
|
||||
rendering that draws one lane per reported tray; Bambu keeps its fixed four-slot rendering.
|
||||
- `AMSControl` picks its layout by vendor: Bambu keeps the left/right presentation
|
||||
(`CreateAmsSingleNozzle` for one toolhead, `CreateAmsDoubleNozzle` for two), while non-Bambu
|
||||
printers use `CreateAmsMultiNozzle`, which builds one preview and one AMS simplebook per toolhead
|
||||
for any count.
|
||||
- `AMSControl` picks its layout by toolhead count, not vendor: `CreateAmsSingleNozzle` for one
|
||||
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'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
|
||||
@@ -162,10 +162,14 @@ The device-control widgets follow the same device-reported counts.
|
||||
- **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.
|
||||
- **One filament road per toolhead.** Each toolhead renders a single AMS filament road rather than an
|
||||
independent per-toolhead road state. A generic scaffold that allowed the latter was removed as
|
||||
unused; the one-extruder-per-toolhead road is the intended model.
|
||||
- **Preset/device agreement.** The generic layout and virtual-slot range derive their count from the
|
||||
selected printer preset. A printer preset must therefore declare the same number of
|
||||
`nozzle_diameter` entries as the device has toolheads; a mismatch is surfaced because the sidebar
|
||||
and extruder logic would otherwise disagree.
|
||||
`nozzle_diameter` entries as the device has toolheads. A mismatch is surfaced only as a log warning
|
||||
(`StatusPanel::check_extruder_count_mismatch()`), deliberately log-only, because the sidebar and
|
||||
extruder logic would otherwise silently disagree.
|
||||
- **Bambu compatibility.** Bambu-specific presentation (left/right pair, fixed 4-slot units, fixed
|
||||
virtual ids, physical/logical inversion) is preserved behind the vendor check. Generic paths do
|
||||
not alter it.
|
||||
|
||||
@@ -37,6 +37,9 @@ namespace Slic3r {
|
||||
|
||||
bool devPrinterUtil::IsVirtualSlot(int ams_id)
|
||||
{
|
||||
// When the app or preset bundle is unavailable we cannot know the vendor's
|
||||
// extruder count, so fall back deterministically to the fixed Bambu pair
|
||||
// {255, 254} rather than guessing from an unknown generic layout.
|
||||
if (wxTheApp == nullptr || GUI::wxGetApp().preset_bundle == nullptr || GUI::wxGetApp().preset_bundle->is_bbl_vendor())
|
||||
return ams_id == VIRTUAL_TRAY_MAIN_ID || ams_id == VIRTUAL_TRAY_DEPUTY_ID;
|
||||
|
||||
@@ -46,6 +49,14 @@ bool devPrinterUtil::IsVirtualSlot(int ams_id)
|
||||
|
||||
bool devPrinterUtil::IsVirtualSlot(const std::string& ams_id)
|
||||
{
|
||||
// Strict: only a fully numeric string is a valid slot id. std::stoi alone
|
||||
// would accept trailing garbage (e.g. "255abc" -> 255).
|
||||
if (ams_id.empty())
|
||||
return false;
|
||||
for (char c : ams_id) {
|
||||
if (c < '0' || c > '9')
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return IsVirtualSlot(std::stoi(ams_id));
|
||||
} catch (...) {
|
||||
@@ -220,6 +231,11 @@ wxString DevAms::GetDisplayName() const
|
||||
|
||||
int DevAms::GetSlotCount() const
|
||||
{
|
||||
// N3S is a 1-slot Bambu contract, so report 1 regardless of whether the
|
||||
// preset bundle is momentarily unavailable (m_trays.size() can be 0).
|
||||
if (GetAmsType() == N3S)
|
||||
return 1;
|
||||
|
||||
if (wxTheApp != nullptr && GUI::wxGetApp().preset_bundle != nullptr &&
|
||||
GUI::wxGetApp().preset_bundle->is_bbl_vendor()) {
|
||||
// GetAmsType() maps AMS_LITE_MIXED -> AMS_LITE, so N9 reports 4 slots like AMS-Lite.
|
||||
@@ -228,10 +244,6 @@ int DevAms::GetSlotCount() const
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
else if (ams_type == N3S)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<int>(m_trays.size());
|
||||
@@ -1057,7 +1069,7 @@ void DevFilaSystemParser::ParseAgentFilament(const json& data, MachineObject* ob
|
||||
if (!e.is_object())
|
||||
continue;
|
||||
const int ext = e.value("extruder", MAIN_EXTRUDER_ID);
|
||||
if (ext < MAIN_EXTRUDER_ID || ext > VIRTUAL_TRAY_MAIN_ID)
|
||||
if (ext < MAIN_EXTRUDER_ID || ext >= VIRTUAL_TRAY_MAIN_ID)
|
||||
continue;
|
||||
|
||||
const int vt_id = VIRTUAL_TRAY_MAIN_ID - ext;
|
||||
|
||||
@@ -808,8 +808,12 @@ bool MachineObject::is_extrusion_cali_finished()
|
||||
DevAmsTray *MachineObject::get_curr_tray()
|
||||
{
|
||||
const std::string& cur_ams_id = m_extder_system->GetCurrentAmsId();
|
||||
if (cur_ams_id.compare(std::to_string(VIRTUAL_TRAY_MAIN_ID)) == 0) {
|
||||
return &vt_slot[0];
|
||||
if (devPrinterUtil::IsVirtualSlot(cur_ams_id)) {
|
||||
for (auto& tray : vt_slot) {
|
||||
if (tray.id == cur_ams_id)
|
||||
return &tray;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DevAms* curr_ams = get_curr_Ams();
|
||||
@@ -2634,13 +2638,10 @@ void MachineObject::reset()
|
||||
json empty_j;
|
||||
print_json.diff2all_base_reset(empty_j);
|
||||
|
||||
for (auto i = 0; i < vt_slot.size(); i++) {
|
||||
vt_slot[i].reset();
|
||||
|
||||
if (i == 1) {
|
||||
vt_slot.erase(vt_slot.begin() + 1);
|
||||
}
|
||||
}
|
||||
for (auto& tray : vt_slot)
|
||||
tray.reset();
|
||||
if (vt_slot.size() > 1)
|
||||
vt_slot.erase(vt_slot.begin() + 1, vt_slot.end());
|
||||
// why: reset reuses MachineObject, so release its lazy subtask
|
||||
// before dropping the pointer to prevent reconnect leaks.
|
||||
if (subtask_) {
|
||||
|
||||
@@ -226,6 +226,8 @@ ExtruderImage::ExtruderImage(wxWindow* parent, wxWindowID id, int nozzle_num, co
|
||||
wxWindow::Create(parent, id, pos, wxSize(FromDIP(45), FromDIP(112)));
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
m_nozzle_num = nozzle_num;
|
||||
// Legacy Bambu single/dual page size. The generic (non-Bambu) grid grows
|
||||
// past this when it holds more than two toolheads; see updateGenericSize().
|
||||
SetSize(wxSize(FromDIP(45), FromDIP(112)));
|
||||
SetMinSize(wxSize(FromDIP(45), FromDIP(112)));
|
||||
SetMaxSize(wxSize(FromDIP(45), FromDIP(112)));
|
||||
@@ -298,6 +300,7 @@ void ExtruderImage::setExtruderCount(int nozzle_num)
|
||||
|
||||
m_nozzle_num = normalized_count;
|
||||
m_multi_extruder_states.resize(static_cast<size_t>(m_nozzle_num), ExtruderState::EMPTY_LOAD);
|
||||
updateGenericSize();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
@@ -307,9 +310,37 @@ void ExtruderImage::setGenericNozzleDisplay(bool enabled)
|
||||
return;
|
||||
|
||||
m_generic_nozzle_display = enabled;
|
||||
updateGenericSize();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void ExtruderImage::updateGenericSize()
|
||||
{
|
||||
// The generic (non-Bambu) grid must stay legible for any toolhead count, so
|
||||
// give it whole natural-size icons (24x62 DIP) and wrap into extra rows
|
||||
// instead of squeezing everything into the fixed 45x112 Bambu page. doRender()
|
||||
// balances the columns/rows inside the area we hand it, so sizing the page to
|
||||
// a grid of natural-size cells keeps every icon at its full size.
|
||||
// Bambu <=2 toolheads and generic <=2 toolheads keep the legacy page size.
|
||||
wxSize size(FromDIP(45), FromDIP(112));
|
||||
if (m_generic_nozzle_display && m_nozzle_num > 2) {
|
||||
const wxSize icon = m_left_extruder_active_filled->GetBmpSize();
|
||||
if (icon.x > 0 && icon.y > 0) {
|
||||
// Fit as many columns as the fixed-width extruder panel (143 DIP) allows.
|
||||
const int columns = std::max(1, std::min(m_nozzle_num, FromDIP(143) / icon.x));
|
||||
const int rows = (m_nozzle_num + columns - 1) / columns;
|
||||
size = wxSize(columns * icon.x, rows * icon.y);
|
||||
}
|
||||
}
|
||||
|
||||
if (GetMinSize() == size && GetMaxSize() == size)
|
||||
return;
|
||||
|
||||
SetMinSize(size);
|
||||
SetMaxSize(size);
|
||||
SetSize(size);
|
||||
}
|
||||
|
||||
void ExtruderImage::setExtruderUsed(std::string loc)
|
||||
{
|
||||
//current_nozzle_idx = nozzle_id;
|
||||
@@ -352,6 +383,7 @@ void ExtruderImage::update(ExtruderState state, int idx) {
|
||||
return;
|
||||
|
||||
m_multi_extruder_states[idx] = state;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void ExtruderImage::paintEvent(wxPaintEvent& evt)
|
||||
@@ -1954,6 +1986,8 @@ void StatusBasePanel::ensure_nozzle_temp_controls(size_t count)
|
||||
if (!m_temp_nozzle_parent || !m_temp_nozzle_sizer)
|
||||
return;
|
||||
|
||||
// Widgets beyond the current toolhead count are intentionally retained
|
||||
// (hidden, never destroyed) so a later count increase reuses them.
|
||||
bool changed = count != m_temp_nozzle_active_count;
|
||||
while (m_tempCtrl_nozzles.size() < count) {
|
||||
TempInput* temp_ctrl = create_nozzle_temp_control(m_temp_nozzle_parent, m_nozzle_temp_control_id);
|
||||
@@ -3715,6 +3749,9 @@ void StatusPanel::update_misc_ctrl(MachineObject *obj)
|
||||
ExtruderImage* image = m_extruderImage.front();
|
||||
image->setGenericNozzleDisplay(true);
|
||||
image->setExtruderCount(m_nozzle_num);
|
||||
// Re-apply every update so a DPI change (which the setters above skip on
|
||||
// steady state) still resizes the grid.
|
||||
image->updateGenericSize();
|
||||
|
||||
const int current_nozzle_id = extder_system->GetCurrentExtderId();
|
||||
const int selected_nozzle_id = current_nozzle_id >= 0 && current_nozzle_id < m_nozzle_num ? current_nozzle_id : -1;
|
||||
@@ -3740,7 +3777,31 @@ void StatusPanel::update_misc_ctrl(MachineObject *obj)
|
||||
m_generic_nozzle_selector->Enable();
|
||||
}
|
||||
|
||||
if (bbl_selector_was_shown != m_nozzle_btn_panel->IsShown() ||
|
||||
// The generic >2 toolhead page is larger than the legacy 45x112 Bambu page,
|
||||
// so drive the book's size from the page that is shown. The book is only
|
||||
// given an explicit size while a >2 generic grid is shown (or to clear a
|
||||
// previous one); a plain Bambu session leaves the book's natural 45x112
|
||||
// best size untouched.
|
||||
bool book_size_changed = false;
|
||||
{
|
||||
const bool generic_grid = !is_bbl_vendor && m_nozzle_num > 2;
|
||||
const bool book_has_explicit_size =
|
||||
m_extruder_book->GetMinSize() != wxDefaultSize ||
|
||||
m_extruder_book->GetMaxSize() != wxDefaultSize;
|
||||
if (generic_grid || book_has_explicit_size) {
|
||||
const wxSize legacy_size(FromDIP(45), FromDIP(112));
|
||||
const wxSize wanted_size = generic_grid ? m_extruderImage.front()->GetMinSize() : legacy_size;
|
||||
if (m_extruder_book->GetMinSize() != wanted_size ||
|
||||
m_extruder_book->GetMaxSize() != wanted_size) {
|
||||
m_extruder_book->SetMinSize(wanted_size);
|
||||
m_extruder_book->SetMaxSize(wanted_size);
|
||||
book_size_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (book_size_changed ||
|
||||
bbl_selector_was_shown != m_nozzle_btn_panel->IsShown() ||
|
||||
generic_selector_was_shown != m_generic_nozzle_selector->IsShown())
|
||||
m_nozzle_btn_panel->GetParent()->Layout();
|
||||
|
||||
@@ -3870,6 +3931,16 @@ void StatusPanel::update_ams(MachineObject *obj)
|
||||
AMSinfo info;
|
||||
info.ams_id = ams->first;
|
||||
if (ams->second->IsExist() && info.parse_ams_info(obj, ams->second, obj->GetFilaSystem()->IsDetectRemainEnabled(), obj->is_support_ams_humidity)) {
|
||||
// A non-Bambu unit reported as AMS_LITE but not matching the fixed
|
||||
// four-lane AMS_LITE grid (e.g. a generic unit advertising a
|
||||
// different slot count) has no fixed layout to render into and would
|
||||
// show an empty card. Treat it as GENERIC_AMS so it uses the same
|
||||
// variable-lane rendering. Bambu's fixed rendering is untouched.
|
||||
if (info.ams_type == AMSModel::AMS_LITE &&
|
||||
info.cans.size() != GENERIC_AMS_SLOT_NUM &&
|
||||
(!wxGetApp().preset_bundle || !wxGetApp().preset_bundle->is_bbl_vendor())) {
|
||||
info.ams_type = AMSModel::GENERIC_AMS;
|
||||
}
|
||||
ams_info.push_back(info);
|
||||
}
|
||||
}
|
||||
@@ -3889,6 +3960,7 @@ void StatusPanel::update_ams(MachineObject *obj)
|
||||
const bool show_generic_nozzle_switch =
|
||||
obj->GetExtderSystem()->GetTotalExtderCount() > 2 &&
|
||||
!wxGetApp().preset_bundle->is_bbl_vendor();
|
||||
const bool ams_nozzle_switch_was_shown = m_ams_nozzle_switch->IsShown();
|
||||
if (show_generic_nozzle_switch) {
|
||||
std::vector<wxString> nozzle_options;
|
||||
const int nozzle_count = obj->GetExtderSystem()->GetTotalExtderCount();
|
||||
@@ -3903,7 +3975,8 @@ void StatusPanel::update_ams(MachineObject *obj)
|
||||
} else {
|
||||
m_ams_nozzle_switch->Hide();
|
||||
}
|
||||
m_ams_nozzle_switch->GetParent()->Layout();
|
||||
if (ams_nozzle_switch_was_shown != m_ams_nozzle_switch->IsShown())
|
||||
m_ams_nozzle_switch->GetParent()->Layout();
|
||||
m_ams_control->UpdateAmsDryControl(obj);
|
||||
|
||||
last_tray_exist_bits = obj->tray_exist_bits;
|
||||
@@ -5607,6 +5680,12 @@ void StatusPanel::on_nozzle_selected(wxCommandEvent &event)
|
||||
m_generic_nozzle_selector->Disable();
|
||||
if (obj->GetCtrl()->command_select_extruder(nozzle_id) == 0)
|
||||
{
|
||||
if (!is_bbl_vendor) {
|
||||
// The command did not start; restore the previous selection and
|
||||
// re-enable the selector so the user is not stuck.
|
||||
m_generic_nozzle_selector->SetSelection(current_selection);
|
||||
m_generic_nozzle_selector->Enable();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -5735,11 +5814,16 @@ void StatusPanel::set_default()
|
||||
m_switch_cham_fan_timeout = 0;
|
||||
m_show_ams_group = false;
|
||||
m_show_filament_group = false;
|
||||
m_generic_nozzle_selector->Hide();
|
||||
if (m_generic_nozzle_selector)
|
||||
m_generic_nozzle_selector->Hide();
|
||||
m_nozzle_btn_panel->Hide();
|
||||
m_generic_nozzle_selector->Clear();
|
||||
if (m_generic_nozzle_selector)
|
||||
m_generic_nozzle_selector->Clear();
|
||||
m_generic_nozzle_selector_count = 0;
|
||||
m_extruder_book->SetSelection(0);
|
||||
// Clear any generic >2 toolhead grid growth from a previous device.
|
||||
m_extruder_book->SetMinSize(wxSize(FromDIP(45), FromDIP(112)));
|
||||
m_extruder_book->SetMaxSize(wxSize(FromDIP(45), FromDIP(112)));
|
||||
m_nozzle_btn_panel->GetParent()->Layout();
|
||||
reset_printing_values();
|
||||
|
||||
@@ -5765,7 +5849,8 @@ void StatusPanel::set_default()
|
||||
m_ams_control->Reset();
|
||||
m_ams_rack_switch->updateState("left");
|
||||
m_ams_rack_switch->Hide();
|
||||
m_ams_nozzle_switch->Hide();
|
||||
if (m_ams_nozzle_switch)
|
||||
m_ams_nozzle_switch->Hide();
|
||||
m_panel_nozzle_rack->Hide();
|
||||
m_scale_panel->Hide();
|
||||
m_filament_load_box->Hide();
|
||||
|
||||
@@ -168,6 +168,7 @@ public:
|
||||
void update(ExtruderState state, int idx);
|
||||
|
||||
void msw_rescale();
|
||||
void updateGenericSize();
|
||||
void setExtruderCount(int nozzle_num);
|
||||
void setGenericNozzleDisplay(bool enabled);
|
||||
void setExtruderUsed(std::string loc);
|
||||
|
||||
@@ -638,6 +638,7 @@ void AMSControl::ClearAms() {
|
||||
for (auto &pane : m_nozzle_panes) {
|
||||
if (pane.preview_panel) pane.preview_panel->Destroy();
|
||||
if (pane.ams_book) pane.ams_book->Destroy();
|
||||
if (pane.page) pane.page->Destroy();
|
||||
}
|
||||
}
|
||||
m_item_ids.assign(std::max(m_total_ext_count, 2), {});
|
||||
@@ -1329,17 +1330,25 @@ void AMSControl::createAms(wxSimplebook* parent, int& idx, AMSinfo info, AMSPane
|
||||
// Above eight lanes, AmsItem is a fixed-minimum viewport over a wider
|
||||
// lane strip. Expand the viewport through its page's sizers rather
|
||||
// than propagating the strip's virtual width into the card/book.
|
||||
// Reset back to the centered fixed layout for eight or fewer lanes so a
|
||||
// later small unit is not left in expand mode.
|
||||
const bool generic_lane_layout = !wxGetApp().preset_bundle || !wxGetApp().preset_bundle->is_bbl_vendor();
|
||||
if (generic_lane_layout && info.cans.size() > 8) {
|
||||
if (generic_lane_layout) {
|
||||
if (auto *page = parent->GetParent()) {
|
||||
auto pane = std::find_if(m_nozzle_panes.begin(), m_nozzle_panes.end(),
|
||||
[page](const NozzleAmsPane &candidate) { return candidate.page == page; });
|
||||
if (pane != m_nozzle_panes.end()) {
|
||||
pane->ams_area->GetItem(pane->ams_book)->SetProportion(1);
|
||||
pane->ams_area->GetItem(pane->ams_book)->SetFlag(wxEXPAND);
|
||||
pane->page_sizer->GetItem(pane->ams_area)->SetFlag(wxEXPAND | wxTOP);
|
||||
m_sizer_ams_items->GetItem(m_nozzle_book)->SetFlag(wxEXPAND);
|
||||
m_sizer_body->GetItem(m_sizer_ams_items)->SetFlag(wxEXPAND);
|
||||
if (pane != m_nozzle_panes.end() && m_nozzle_book) {
|
||||
const bool expand_viewport = info.cans.size() > 8;
|
||||
if (auto *book_item = pane->ams_area->GetItem(pane->ams_book)) {
|
||||
book_item->SetProportion(expand_viewport ? 1 : 0);
|
||||
book_item->SetFlag(expand_viewport ? wxEXPAND : wxALIGN_CENTER);
|
||||
}
|
||||
if (auto *area_item = pane->page_sizer->GetItem(pane->ams_area))
|
||||
area_item->SetFlag(expand_viewport ? (wxEXPAND | wxTOP) : (wxALIGN_CENTER | wxTOP));
|
||||
if (auto *nozzle_book_item = m_sizer_ams_items->GetItem(m_nozzle_book))
|
||||
nozzle_book_item->SetFlag(expand_viewport ? wxEXPAND : wxALIGN_CENTER);
|
||||
if (auto *items_item = m_sizer_body->GetItem(m_sizer_ams_items))
|
||||
items_item->SetFlag(expand_viewport ? wxEXPAND : wxALIGN_CENTER);
|
||||
m_amswin->Layout();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -877,7 +877,7 @@ void MultiNozzleStatusTable::UpdateRackInfo(std::weak_ptr<DevNozzleRack> rack)
|
||||
auto& nozzle = elem.second;
|
||||
|
||||
int extruder_id{};
|
||||
if (wxGetApp().preset_bundle->is_bbl_vendor()) {
|
||||
if (wxGetApp().preset_bundle && wxGetApp().preset_bundle->is_bbl_vendor()) {
|
||||
extruder_id = nozzle.AtLeftExtruder() ? 0 : 1;
|
||||
if (nozzle.AtRightExtruder())
|
||||
has_right = true;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// makes std::byte a competing candidate (otherwise the Windows COM headers pulled in via
|
||||
// DeviceManager.hpp error with an ambiguous `byte`). wx/timer.h must precede DeviceManager.hpp,
|
||||
// which includes DeviceErrorDialog.hpp (uses wxTimerEvent) before its own wx/timer.h include.
|
||||
#include "catch2/catch_message.hpp"
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -17,7 +16,6 @@
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
#include <wx/timer.h>
|
||||
@@ -84,3 +82,77 @@ TEST_CASE("Extruder state decodes a generic toolhead set", "[DevExtderSystem]")
|
||||
CHECK(ext->GetNozzleId() == id);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Legacy single-toolhead parser fills only the main extruder", "[DevExtderSystem]")
|
||||
{
|
||||
MachineObject obj(nullptr, nullptr, "test", "test_dev", "127.0.0.1");
|
||||
REQUIRE(obj.GetExtderSystem()->GetTotalExtderCount() == 1);
|
||||
|
||||
// ParseV1_0 is the legacy single/dual fallback. It returns unless the system reports exactly
|
||||
// one toolhead, so it only ever writes MAIN_EXTRUDER_ID's fields.
|
||||
const json legacy = {
|
||||
{"nozzle_temper", 210},
|
||||
{"nozzle_target_temper", 245},
|
||||
{"ams", {{"tray_now", "2"}, {"tray_tar", "3"}}}
|
||||
};
|
||||
ExtderSystemParser::ParseV1_0(legacy, obj.GetExtderSystem());
|
||||
|
||||
const auto ext = obj.GetExtderSystem()->GetExtderById(MAIN_EXTRUDER_ID);
|
||||
REQUIRE(ext.has_value());
|
||||
CHECK(ext->GetCurrentTemp() == 210);
|
||||
CHECK(ext->GetTargetTemp() == 245);
|
||||
|
||||
// Below 0x80 the tray pointer encodes ams_id = value / 4, slot_id = value % 4.
|
||||
CHECK(ext->GetSlotNow().ams_id == "0");
|
||||
CHECK(ext->GetSlotNow().slot_id == "2");
|
||||
CHECK(ext->GetSlotTarget().ams_id == "0");
|
||||
CHECK(ext->GetSlotTarget().slot_id == "3");
|
||||
CHECK(obj.GetExtderSystem()->IsBusyLoading());
|
||||
CHECK(obj.GetExtderSystem()->GetLoadingExtderId() == MAIN_EXTRUDER_ID);
|
||||
}
|
||||
|
||||
// ParseV2_0 does not enforce the documented extruder dialect invariants: the count in `state` and
|
||||
// the id ordering of `info[]` are trusted, not checked. This test pins the resulting behavior.
|
||||
TEST_CASE("Malformed extruder payload is stored by info array position", "[DevExtderSystem]")
|
||||
{
|
||||
const auto extder_info = [](int id) {
|
||||
// Every key ParseV2_0 reads must be present.
|
||||
return json{ {"id", id}, {"filam_bak", json::array()}, {"info", 0}, {"temp", 0},
|
||||
{"spre", 0}, {"snow", 0}, {"star", 0}, {"stat", 0}, {"hnow", 0} };
|
||||
};
|
||||
|
||||
SECTION("state count disagrees with info length") {
|
||||
MachineObject obj(nullptr, nullptr, "test", "test_dev", "127.0.0.1");
|
||||
|
||||
// `state` reports 2 toolheads but info[] carries 3.
|
||||
json info = json::array();
|
||||
for (int id = 0; id < 3; ++id)
|
||||
info.push_back(extder_info(id));
|
||||
ExtderSystemParser::ParseV2_0(json{ {"state", 2}, {"info", info} }, obj.GetExtderSystem());
|
||||
|
||||
// The info array wins: three toolheads are stored. m_total_extder_count still holds 2, so
|
||||
// GetTotalExtderCount() would trip its debug assert; only the stored vector is inspected.
|
||||
CHECK(obj.GetExtderSystem()->GetTotalExtderSize() == 3);
|
||||
for (int id = 0; id < 3; ++id) {
|
||||
const auto ext = obj.GetExtderSystem()->GetExtderById(id);
|
||||
REQUIRE(ext.has_value());
|
||||
CHECK(ext->GetExtId() == id);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("info id differs from the array index") {
|
||||
MachineObject obj(nullptr, nullptr, "test", "test_dev", "127.0.0.1");
|
||||
|
||||
// ids 1 and 2 sit at array positions 0 and 1.
|
||||
json info = json::array({extder_info(1), extder_info(2)});
|
||||
ExtderSystemParser::ParseV2_0(json{ {"state", 2}, {"info", info} }, obj.GetExtderSystem());
|
||||
|
||||
// Toolheads are stored by array position; the payload id is kept but is not the lookup key.
|
||||
const auto first = obj.GetExtderSystem()->GetExtderById(0);
|
||||
REQUIRE(first.has_value());
|
||||
CHECK(first->GetExtId() == 1);
|
||||
const auto second = obj.GetExtderSystem()->GetExtderById(1);
|
||||
REQUIRE(second.has_value());
|
||||
CHECK(second->GetExtId() == 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
// makes std::byte a competing candidate (otherwise the Windows COM headers pulled in via
|
||||
// DeviceManager.hpp error with an ambiguous `byte`). wx/timer.h must precede DeviceManager.hpp,
|
||||
// which includes DeviceErrorDialog.hpp (uses wxTimerEvent) before its own wx/timer.h include.
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#ifdef WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
@@ -15,11 +13,14 @@
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include "slic3r/GUI/DeviceCore/DevDefs.h"
|
||||
#include "libslic3r/ProjectTask.hpp"
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <catch2/catch_message.hpp>
|
||||
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
Reference in New Issue
Block a user