mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-18 00:12:09 +00:00
Fix device popup, mapping, and status-parse defects
- Allow mapping to EMPTY trays only from the multi-machine send page and keep both panels pickable in the LEFT_AND_RIGHT view - Guard the error-dialog cloud snapshot against stale callbacks and fall back to the local illustration on timeout - Parse the ipcam storage-check ack and axis/chamber pushes defensively - Strip fan-control telemetry, initialize the upgrade error code, restore the .json filter in the model-id scan - Fix best-position popup tray lookup, gradient placement, and colour-list ownership - Cover switch binding sets and invalid-track transients in DevMapping tests
This commit is contained in:
@@ -1059,8 +1059,7 @@ void AmsMapingPopup::show_reset_button() {
|
||||
m_reset_btn->Show();
|
||||
}
|
||||
|
||||
// Orca: kept surface for the reverse-direction SelectMachine — this helper (dropped by the reference)
|
||||
// is still consumed by SelectMachine, so it stays as a permanent compatibility surface.
|
||||
// Orca: dropped by the reference but still consumed by SelectMachine.
|
||||
std::vector<TrayData> AmsMapingPopup::parse_ams_mapping(const std::map<std::string, DevAms*, NumericStrCompare>& amsList)
|
||||
{
|
||||
std::vector<TrayData> m_tray_data;
|
||||
@@ -1185,7 +1184,7 @@ void AmsMapingPopup::on_left_down(wxMouseEvent &evt)
|
||||
}
|
||||
}
|
||||
|
||||
if (item->m_tray_data.type == TrayType::EMPTY) return;
|
||||
if (item->m_tray_data.type == TrayType::EMPTY && !m_mapping_from_multi_machines) return;
|
||||
if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC && devPrinterUtil::IsVirtualSlot(item->m_ams_id)) return;
|
||||
|
||||
if ((m_show_type == ShowType::LEFT && item->GetParent()->GetName() == "left") ||
|
||||
|
||||
@@ -598,9 +598,11 @@ void AmsMapingPopup::add_ams_mapping(std::vector<TrayData> tray_data,
|
||||
}
|
||||
else {
|
||||
if (can_pick_the_item) {
|
||||
// LEFT_AND_RIGHT — the fallback when a filament has no entry in the
|
||||
// nozzle map — keeps both panels pickable.
|
||||
auto parent = container->GetParent();
|
||||
if (parent == m_left_marea_panel) {
|
||||
can_pick_the_item = (m_show_type == ShowType::LEFT);
|
||||
can_pick_the_item = (m_show_type == ShowType::LEFT || m_show_type == ShowType::LEFT_AND_RIGHT);
|
||||
} else if (parent == m_right_marea_panel) {
|
||||
if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) {
|
||||
can_pick_the_item = !devPrinterUtil::IsVirtualSlot(m_mapping_item->m_ams_id);
|
||||
@@ -608,7 +610,7 @@ void AmsMapingPopup::add_ams_mapping(std::vector<TrayData> tray_data,
|
||||
item_tooltip_msg = _L(
|
||||
"External spools is not supported since Filament Track Switch has been installed. If you want to use external spool, please uninstall it.");
|
||||
}
|
||||
} else if (m_show_type != ShowType::RIGHT) {
|
||||
} else if (m_show_type != ShowType::RIGHT && m_show_type != ShowType::LEFT_AND_RIGHT) {
|
||||
can_pick_the_item = false;
|
||||
}
|
||||
}
|
||||
@@ -620,7 +622,9 @@ void AmsMapingPopup::add_ams_mapping(std::vector<TrayData> tray_data,
|
||||
if (tray_data[i].type == NORMAL && !is_match_material(tray_data[i].filament_type)){
|
||||
can_pick_the_item = false;
|
||||
} else if(tray_data[i].type == EMPTY){
|
||||
can_pick_the_item = false;
|
||||
// Mapping to an empty slot is only allowed from the multi-machine send page,
|
||||
// which maps onto EMPTY placeholder trays.
|
||||
can_pick_the_item = m_mapping_from_multi_machines;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,8 @@ std::map<std::string, std::string> DevPrinterConfigUtil::get_all_model_id_with_n
|
||||
|
||||
for (wxString file : m_files)
|
||||
{
|
||||
if (!file.Lower().ends_with(".json")) continue;
|
||||
|
||||
std::string config_file = m_resource_file_path + "/printers/" + file.ToStdString();
|
||||
boost::nowide::ifstream json_file(config_file.c_str());
|
||||
|
||||
|
||||
@@ -46,29 +46,11 @@ void Slic3r::DevFan::converse_to_duct(bool is_suppt_part_fun, bool is_suppt_aux_
|
||||
}
|
||||
}
|
||||
|
||||
static std::string _get_string_from_fantype(int type)
|
||||
{
|
||||
switch (type) {
|
||||
case 1: return "cooling_fan";
|
||||
case 2: return "big_cooling_fan";
|
||||
case 3: return "chamber_fan";
|
||||
default: return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Old protocol
|
||||
int Slic3r::DevFan::command_control_fan(int fan_type, int val)
|
||||
{
|
||||
// Orca: strip analytics telemetry
|
||||
std::string gcode = (boost::format("M106 P%1% S%2% \n") % (int) fan_type % (val)).str();
|
||||
try {
|
||||
json j;
|
||||
j["ctrl_type"] = _get_string_from_fantype(fan_type);
|
||||
j["value"] = val;
|
||||
|
||||
NetworkAgent *agent = GUI::wxGetApp().getAgent();
|
||||
if (agent) agent->track_event("printer_control", j.dump());
|
||||
} catch (...) {}
|
||||
return m_owner->publish_gcode(gcode);
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ private:
|
||||
std::map<std::string, DevFirmwareVersionInfo> m_new_ver_list; // some protcols have version list
|
||||
DevFirmwareUpgradeState m_upgrade_display_state = DevFirmwareUpgradeState::DC;
|
||||
|
||||
int m_upgrade_err_code;
|
||||
int m_upgrade_err_code = 0;
|
||||
std::string m_upgrade_status;
|
||||
std::string m_upgrade_progress;
|
||||
std::string m_upgrade_message;
|
||||
|
||||
@@ -155,7 +155,10 @@ void DeviceErrorDialog::on_webrequest_state(wxWebRequestEvent& evt)
|
||||
void DeviceErrorDialog::on_request_timeout(wxTimerEvent& event)
|
||||
{
|
||||
if (m_request_cancelled.load()) { return; }
|
||||
m_error_picture->SetBitmap(make_placeholder_image(_L("Network unavailable")));
|
||||
// Same fallback order as the cloud callback: local/HTTP illustration first, placeholder last.
|
||||
if (!get_fail_snapshot_from_local(m_local_img_url)) {
|
||||
m_error_picture->SetBitmap(make_placeholder_image(_L("Network unavailable")));
|
||||
}
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
@@ -207,13 +210,16 @@ bool DeviceErrorDialog::get_fail_snapshot_from_cloud()
|
||||
// destruction with a liveness token (m_alive, cleared in the dtor) and marshal onto the UI
|
||||
// thread via the always-valid app handler instead of this->CallAfter. The raw bytes are
|
||||
// decoded on the UI thread so no wxImage (non-atomic refcount) is shared across threads.
|
||||
// Assumes a single in-flight request per dialog, which all current callers satisfy.
|
||||
// m_request_seq guards against a callback from a previous error painting over the
|
||||
// current one when the dialog is reused by a later show_error_code().
|
||||
auto alive = m_alive;
|
||||
const int seq = m_request_seq;
|
||||
int ret = agent->get_hms_snapshot(m_obj->get_dev_id(), m_obj->m_print_error_img_id,
|
||||
[this, alive](std::string body, int status) {
|
||||
[this, alive, seq](std::string body, int status) {
|
||||
if (!alive->load()) { return; }
|
||||
wxGetApp().CallAfter([this, alive, body = std::move(body), status]() {
|
||||
wxGetApp().CallAfter([this, alive, seq, body = std::move(body), status]() {
|
||||
if (!alive->load()) { return; }
|
||||
if (seq != m_request_seq) { return; }
|
||||
m_request_cancelled.store(true);
|
||||
clear_request_timer();
|
||||
|
||||
@@ -304,7 +310,7 @@ void DeviceErrorDialog::init_button_list()
|
||||
init_button(FILAMENT_LOAD_RESUME, _L("Filament Loaded, Resume"));
|
||||
init_button(JUMP_TO_LIVEVIEW, _L("View Liveview"));
|
||||
init_button(NO_REMINDER_NEXT_TIME, _L("No Reminder Next Time"));
|
||||
init_button(REFRESH_NOZZLE, _L("Recheck")); // Orca: ported REF error action
|
||||
init_button(REFRESH_NOZZLE, _L("Recheck"));
|
||||
init_button(IGNORE_NO_REMINDER_NEXT_TIME, _L("Ignore. Don't Remind Next Time"));
|
||||
init_button(IGNORE_RESUME, _L("Ignore this and Resume"));
|
||||
init_button(PROBLEM_SOLVED_RESUME, _L("Problem Solved and Resume"));
|
||||
@@ -313,8 +319,8 @@ void DeviceErrorDialog::init_button_list()
|
||||
init_button(CANCEL, _L("Cancel"));
|
||||
init_button(STOP_DRYING, _L("Stop Drying"));
|
||||
init_button(PROCEED, _L("Proceed"));
|
||||
init_button(OK_JUMP_RACK, _L("OK")); // Orca: ported REF error action
|
||||
init_button(ABORT, _L("Abort")); // Orca: ported REF error action
|
||||
init_button(OK_JUMP_RACK, _L("OK"));
|
||||
init_button(ABORT, _L("Abort"));
|
||||
init_button(DISABLE_PURIFICATION, _L("Disable Purification for This Print"));
|
||||
init_button(DONT_REMIND_NEXT_TIME, _L("Don't Remind Me"));
|
||||
init_button(DBL_CHECK_CANCEL, _L("Cancel"));
|
||||
@@ -452,6 +458,7 @@ void DeviceErrorDialog::update_contents(const wxString& title, const wxString& t
|
||||
// path keeps its existing behavior.
|
||||
m_local_img_url = image_url;
|
||||
m_request_cancelled.store(false);
|
||||
++m_request_seq; // invalidate any snapshot fetch still in flight for the previous error
|
||||
clear_request_timer();
|
||||
if (get_fail_snapshot_from_cloud())
|
||||
{
|
||||
@@ -489,9 +496,10 @@ void DeviceErrorDialog::update_contents(const wxString& title, const wxString& t
|
||||
auto text_size = m_error_msg_label->GetBestSize();
|
||||
if (text_size.y < FromDIP(360))
|
||||
{
|
||||
// Orca: also reserve the image area when only a cloud snapshot (m_print_error_img_id)
|
||||
// is available, so the scroll area sizes correctly even with an empty local image_url.
|
||||
if (!image_url.empty() || (m_obj && !m_obj->m_print_error_img_id.empty()))
|
||||
// Orca: reserve the image area exactly when an image is coming (cloud snapshot,
|
||||
// local illustration, or the loading placeholder) - m_error_picture's visibility
|
||||
// was decided above, so no blank slot is reserved when neither source is usable.
|
||||
if (m_error_picture->IsShown())
|
||||
{
|
||||
m_scroll_area->SetMinSize(wxSize(FromDIP(320), text_size.y + FromDIP(220)));
|
||||
}
|
||||
@@ -604,16 +612,16 @@ void DeviceErrorDialog::on_button_click(ActionButton btn_id)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::REFRESH_NOZZLE: { // Orca: ported REF error action
|
||||
case DeviceErrorDialog::REFRESH_NOZZLE: {
|
||||
m_obj->command_refresh_nozzle();
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::OK_JUMP_RACK: { // Orca: ported REF error action
|
||||
case DeviceErrorDialog::OK_JUMP_RACK: {
|
||||
Slic3r::GUI::wxGetApp().mainframe->jump_to_monitor();
|
||||
Slic3r::GUI::wxGetApp().mainframe->m_monitor->jump_to_Rack();
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::ABORT: { // Orca: ported REF error action
|
||||
case DeviceErrorDialog::ABORT: {
|
||||
m_obj->command_ams_control("abort");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
JUMP_TO_LIVEVIEW,
|
||||
|
||||
NO_REMINDER_NEXT_TIME = 23,
|
||||
REFRESH_NOZZLE = 24, // Orca: ported REF error action (recheck nozzle)
|
||||
REFRESH_NOZZLE = 24,
|
||||
IGNORE_NO_REMINDER_NEXT_TIME = 25,
|
||||
//LOAD_FILAMENT = 26*/
|
||||
IGNORE_RESUME = 27,
|
||||
@@ -49,8 +49,8 @@ public:
|
||||
CANCEL = 37,
|
||||
REMOVE_CLOSE_BTN = 39, // special case, do not show close button
|
||||
PROCEED = 41,
|
||||
OK_JUMP_RACK = 49, // Orca: ported REF error action (jump to the nozzle rack)
|
||||
ABORT = 51, // Orca: ported REF error action (abort)
|
||||
OK_JUMP_RACK = 49,
|
||||
ABORT = 51,
|
||||
DISABLE_PURIFICATION = 54,
|
||||
DONT_REMIND_NEXT_TIME = 57,
|
||||
|
||||
@@ -107,6 +107,9 @@ private:
|
||||
wxWebRequest web_request;
|
||||
wxTimer* m_request_timer{ nullptr };
|
||||
std::atomic<bool> m_request_cancelled{ false };
|
||||
// UI-thread-only generation counter: bumped on every update_contents() so a cloud
|
||||
// snapshot callback from a previous error cannot paint over the current one.
|
||||
int m_request_seq{ 0 };
|
||||
wxString m_local_img_url;
|
||||
// Orca: liveness token for the async cloud snapshot callback (the request has no cancel handle)
|
||||
std::shared_ptr<std::atomic_bool> m_alive{ std::make_shared<std::atomic_bool>(true) };
|
||||
|
||||
@@ -74,8 +74,6 @@ int get_tray_id_by_ams_id_and_slot_id(int ams_id, int slot_id)
|
||||
}
|
||||
}
|
||||
|
||||
// Orca: REF-additive stringing-prone filament chain, now ported (post-review).
|
||||
// Consumers arrive in resync clusters 5 (PrintOptionsDialog) and 7 (SelectMachine).
|
||||
namespace {
|
||||
|
||||
// Stringing-prone filament IDs per nozzle-diameter bucket.
|
||||
@@ -759,8 +757,6 @@ std::string MachineObject::get_filament_display_type(const std::string& ams_id,
|
||||
return this->get_tray(ams_id, tray_id).get_display_filament_type();
|
||||
}
|
||||
|
||||
// Orca: REF-additive, now ported (post-review). Adapted to Orca's kept fila model:
|
||||
// filament id resolved via MachineObject::get_filament_id (tray setting_id / tray_info_idx).
|
||||
bool MachineObject::any_loaded_filament_is_stringing_prone() const
|
||||
{
|
||||
if (print_job_filament_mapping.empty()) return false;
|
||||
@@ -806,7 +802,7 @@ void MachineObject::_parse_ams_status(int ams_status)
|
||||
ams_status_main = AmsStatusMain::AMS_STATUS_MAIN_SELF_CHECK;
|
||||
} else if (ams_status_main_int == (int) AmsStatusMain::AMS_STATUS_MAIN_DEBUG) {
|
||||
ams_status_main = AmsStatusMain::AMS_STATUS_MAIN_DEBUG;
|
||||
} else if (ams_status_main_int == (int) AmsStatusMain::AMS_STATUS_MAIN_COLD_PULL) { // Orca: REF-additive, now ported (post-review)
|
||||
} else if (ams_status_main_int == (int) AmsStatusMain::AMS_STATUS_MAIN_COLD_PULL) {
|
||||
ams_status_main = AmsStatusMain::AMS_STATUS_MAIN_COLD_PULL;
|
||||
} else {
|
||||
ams_status_main = AmsStatusMain::AMS_STATUS_MAIN_UNKNOWN;
|
||||
@@ -959,7 +955,6 @@ void MachineObject::clear_version_info()
|
||||
laser_version_info = DevFirmwareVersionInfo();
|
||||
cutting_module_version_info = DevFirmwareVersionInfo();
|
||||
extinguish_version_info = DevFirmwareVersionInfo();
|
||||
// Orca: REF-additive accessory firmware versions, now ported (post-review)
|
||||
rotary_version_info = DevFirmwareVersionInfo();
|
||||
exhaustfan_version_info = DevFirmwareVersionInfo();
|
||||
amshub_version_info = DevFirmwareVersionInfo();
|
||||
@@ -980,16 +975,16 @@ void MachineObject::store_version_info(const DevFirmwareVersionInfo& info)
|
||||
cutting_module_version_info = info;
|
||||
} else if (info.isExtinguishSystem()) {
|
||||
extinguish_version_info = info;
|
||||
} else if (info.isRotary()) { // Orca: REF-additive, now ported (post-review)
|
||||
} else if (info.isRotary()) {
|
||||
rotary_version_info = info;
|
||||
} else if (info.isWTM()) {
|
||||
// Route rack-hotend / extruder-nozzle firmware into the nozzle system so
|
||||
// the rack upgrade UI can read per-nozzle versions. isWTM() is false for every non-rack
|
||||
// printer's modules, so this branch never fires outside H2C.
|
||||
m_nozzle_system->AddFirmwareInfoWTM(info);
|
||||
} else if (info.isExhaustFan()) { // Orca: REF-additive, now ported (post-review)
|
||||
} else if (info.isExhaustFan()) {
|
||||
exhaustfan_version_info = info;
|
||||
} else if (info.isHmshub()) { // Orca: REF-additive, now ported (post-review)
|
||||
} else if (info.isHmshub()) {
|
||||
amshub_version_info = info;
|
||||
} else if (info.isFilaTrackSwitch()) {
|
||||
filatrack_version_info = info;
|
||||
@@ -3364,9 +3359,9 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
int flag3 = jj["flag3"].get<int>();
|
||||
is_support_filament_setting_inprinting = get_flag_bits(flag3, 3);
|
||||
is_enable_ams_np = get_flag_bits(flag3, 9);
|
||||
is_support_fila_change_abort = get_flag_bits(flag3, 13); // Orca: REF-additive, now ported (filament-change Stop button)
|
||||
is_support_ext_change_assist_old = get_flag_bits(flag3, 16); // Orca: REF-additive, now ported (A/P-series multi-color external change assist)
|
||||
is_support_filament_32_colors = get_flag_bits(flag3, 17); // Orca: REF-additive, now ported (post-review)
|
||||
is_support_fila_change_abort = get_flag_bits(flag3, 13); // filament-change Stop button
|
||||
is_support_ext_change_assist_old = get_flag_bits(flag3, 16); // A/P-series multi-color external change assist
|
||||
is_support_filament_32_colors = get_flag_bits(flag3, 17);
|
||||
}
|
||||
}
|
||||
if (!key_field_only) {
|
||||
@@ -3533,9 +3528,14 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
/* temperature */
|
||||
|
||||
// Orca: adopt DeviceCore split — populate DevAxis/DevChamber modules
|
||||
// alongside the inline handling (side-effect-free; inline stays authoritative)
|
||||
m_axis->ParseAxis(jj);
|
||||
m_chamber->ParseChamber(jj);
|
||||
// alongside the inline handling (side-effect-free; inline stays authoritative).
|
||||
// Contained locally so a malformed field cannot abort the whole status parse.
|
||||
try {
|
||||
m_axis->ParseAxis(jj);
|
||||
m_chamber->ParseChamber(jj);
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "parse_json: DevAxis/DevChamber parse failed";
|
||||
}
|
||||
|
||||
DevBed::ParseV1_0(jj,m_bed);
|
||||
|
||||
@@ -4548,7 +4548,7 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
} else if (j["camera"]["command"].get<std::string>() == "ipcam_get_media_info") {
|
||||
if (j["camera"].contains("sub_command") &&
|
||||
j["camera"]["sub_command"].get<std::string>() == "is_timelapse_storage_enough") {
|
||||
timelapse_storage_check_result = j["camera"]["result"].get<int>();
|
||||
timelapse_storage_check_result = j["camera"].value("result", -1);
|
||||
timelapse_storage_is_enough = j["camera"].value("is_enough", true);
|
||||
timelapse_storage_file_count = j["camera"].value("file_count", 0);
|
||||
timelapse_storage_check_done = true;
|
||||
@@ -5160,8 +5160,7 @@ bool MachineObject::check_enable_np(const json& print) const
|
||||
return false;
|
||||
}
|
||||
|
||||
// Orca: REF-additive, now ported (post-review). Consumer is the SelectMachine
|
||||
// color-quantity send gate (arrives in cluster 7). 0 = no explicit upper bound.
|
||||
// Max filament color count for the send gate; 0 = no explicit upper bound.
|
||||
int MachineObject::get_max_filament_color_count() const
|
||||
{
|
||||
if (is_support_filament_32_colors) return 32;
|
||||
@@ -5332,8 +5331,7 @@ void MachineObject::parse_new_info(json print)
|
||||
}
|
||||
}
|
||||
|
||||
/* Orca: REF-additive, now ported (post-review). Per-filament-index AMS slot mapping
|
||||
(task-level state). Consumers arrive in clusters 5/7. */
|
||||
/* Per-filament-index AMS slot mapping reported by the printer (task-level state) */
|
||||
if (print.contains("mapping") && print["mapping"].is_array()) {
|
||||
std::vector<uint16_t> new_mapping;
|
||||
new_mapping.reserve(print["mapping"].size());
|
||||
|
||||
@@ -79,7 +79,7 @@ void UiStyledAMSPanel::OnPaint(wxPaintEvent& event)
|
||||
|
||||
|
||||
int labelX = offset;
|
||||
int labelY = m_isTop ? offset : (height - offset - FromDIP(27)); // true=顶部, false=底部
|
||||
int labelY = m_isTop ? offset : (height - offset - FromDIP(27)); // true=top, false=bottom
|
||||
|
||||
|
||||
wxRect labelRect(labelX, labelY, width - 2 * offset, FromDIP(10));
|
||||
@@ -340,8 +340,7 @@ void UiAMSSlot::DrawRectangle(wxPaintDC& dc, const wxSize& cli)
|
||||
|
||||
for (int i = 0; i < m_bgColours.size(); i++)
|
||||
{
|
||||
baseX += step * i;
|
||||
wxRect rr(baseX, (cli.y - rectangleH) / 2, step, rectangleH);
|
||||
wxRect rr(baseX + step * i, (cli.y - rectangleH) / 2, step, rectangleH);
|
||||
|
||||
dc.SetBrush(wxBrush(LightenColour(m_bgColours[i])));
|
||||
dc.SetPen(wxPen(LightenColour(m_bgColours[i])));
|
||||
@@ -1010,8 +1009,9 @@ wxString ReselectMachineDialog::getTrayID(MachineObject* obj, const std::string&
|
||||
if (filaSys)
|
||||
{
|
||||
DevAms* ams = filaSys->GetAmsById(amsID);
|
||||
int ams_id_int = std::stoi(amsID);
|
||||
int slot_id_int = std::stoi(slotID);
|
||||
if (!ams) { return ""; }
|
||||
int ams_id_int = atoi(amsID.c_str());
|
||||
int slot_id_int = atoi(slotID.c_str());
|
||||
int tray_id = 0;
|
||||
if (ams->GetAmsType() == DevAmsType::AMS || ams->GetAmsType() == DevAmsType::AMS_LITE || ams->GetAmsType() == DevAmsType::N3F) {
|
||||
tray_id = ams_id_int * 4 + slot_id_int;
|
||||
|
||||
@@ -131,10 +131,12 @@ private:
|
||||
bool IsDark(const wxColour& c)
|
||||
{
|
||||
int brightness = (c.Red() * 299 + c.Green() * 587 + c.Blue() * 114) / 1000;
|
||||
return brightness < 128; // 0-255 range,128 mid
|
||||
return brightness < 128; // 0-255 range, 128 mid
|
||||
}
|
||||
private:
|
||||
const std::vector<wxColour>& m_bgColours;
|
||||
// Copied, not referenced: the slot repaints long after construction, so it must not
|
||||
// depend on the caller's vector staying alive.
|
||||
std::vector<wxColour> m_bgColours;
|
||||
wxString m_text;
|
||||
DataStatusType m_status;
|
||||
wxSize m_size;
|
||||
|
||||
@@ -79,3 +79,117 @@ TEST_CASE("Switch-bound AMS trays map to the left extruder", "[DevMapping]")
|
||||
CHECK(result[0].tray_id == 0);
|
||||
CHECK(result[0].ams_id == "0");
|
||||
}
|
||||
|
||||
TEST_CASE("Without a switch the binding set equals the single bound extruder", "[DevMapping]")
|
||||
{
|
||||
MachineObject obj(nullptr, nullptr, "test", "test_dev", "127.0.0.1");
|
||||
REQUIRE_FALSE(obj.GetFilaSwitch()->IsInstalled());
|
||||
|
||||
// AMS 0: info extruder nibble = MAIN (right). AMS 1: nibble = DEPUTY (left).
|
||||
// AMS 2: no "info" key at all (old X1/P1 firmware) -> must default to MAIN.
|
||||
// tray_exist_bits: bit ams_id*4+tray_id -> 0x111 marks tray 0 of each AMS.
|
||||
json print_push = json::parse(R"({
|
||||
"ams": {
|
||||
"tray_exist_bits": "111",
|
||||
"ams": [
|
||||
{ "id": "0", "info": "00000001", "tray": [ { "id": "0", "tray_color": "FF0000FF" } ] },
|
||||
{ "id": "1", "info": "00000101", "tray": [ { "id": "0", "tray_color": "00FF00FF" } ] },
|
||||
{ "id": "2", "tray": [ { "id": "0", "tray_color": "0000FFFF" } ] }
|
||||
]
|
||||
}
|
||||
})");
|
||||
DevFilaSystemParser::ParseV1_0(print_push, &obj, obj.GetFilaSystem().get(), false);
|
||||
|
||||
// The invariant that keeps the binding-set mapping filter behavior-preserving for
|
||||
// ordinary printers: GetBindedExtruderSet() == { GetExtruderId() }, info key or not.
|
||||
const auto& ams_list = obj.GetFilaSystem()->GetAmsList();
|
||||
REQUIRE(ams_list.count("0") == 1);
|
||||
REQUIRE(ams_list.count("1") == 1);
|
||||
REQUIRE(ams_list.count("2") == 1);
|
||||
for (const char* id : {"0", "1", "2"}) {
|
||||
const auto& ams = ams_list.at(id);
|
||||
INFO("ams " << id);
|
||||
REQUIRE(ams->GetBindedExtruderSet().size() == 1);
|
||||
REQUIRE(ams->GetBindedExtruderSet().count(ams->GetExtruderId()) == 1);
|
||||
}
|
||||
REQUIRE(ams_list.at("0")->GetExtruderId() == MAIN_EXTRUDER_ID);
|
||||
REQUIRE(ams_list.at("1")->GetExtruderId() == DEPUTY_EXTRUDER_ID);
|
||||
REQUIRE(ams_list.at("2")->GetExtruderId() == MAIN_EXTRUDER_ID);
|
||||
|
||||
for (const char* id : {"0", "1", "2"}) {
|
||||
DevAmsTray* tray = obj.GetFilaSystem()->GetAmsTray(id, "0");
|
||||
REQUIRE(tray != nullptr);
|
||||
tray->m_fila_type = "PLA";
|
||||
}
|
||||
|
||||
// A left-only request must exclude the MAIN-bound AMSes: the red filament exactly
|
||||
// matches AMS 0's red tray, so landing anywhere but AMS 1 (or unmapped) means the
|
||||
// exclusion is broken.
|
||||
FilamentInfo fila;
|
||||
fila.id = 0;
|
||||
fila.type = "PLA";
|
||||
fila.color = "FF0000FF";
|
||||
|
||||
std::vector<FilamentInfo> result;
|
||||
std::vector<bool> map_opt(4, false);
|
||||
map_opt[MappingOption::USE_LEFT_AMS] = true;
|
||||
DevMappingUtil::ams_filament_mapping(&obj, {fila}, result, map_opt, {}, false);
|
||||
REQUIRE(result.size() == 1);
|
||||
CHECK(result[0].ams_id != "0");
|
||||
CHECK(result[0].ams_id != "2");
|
||||
|
||||
// The mirrored right-only request maps to the exact-match MAIN-bound AMS.
|
||||
result.clear();
|
||||
map_opt[MappingOption::USE_LEFT_AMS] = false;
|
||||
map_opt[MappingOption::USE_RIGHT_AMS] = true;
|
||||
DevMappingUtil::ams_filament_mapping(&obj, {fila}, result, map_opt, {}, false);
|
||||
REQUIRE(result.size() == 1);
|
||||
CHECK(result[0].ams_id == "0");
|
||||
}
|
||||
|
||||
TEST_CASE("Switch-bound AMS with an invalid track is excluded from mapping", "[DevMapping]")
|
||||
{
|
||||
MachineObject obj(nullptr, nullptr, "test", "test_dev", "127.0.0.1");
|
||||
obj.GetFilaSwitch()->ParseFilaSwitchInfo(json::parse(R"({"aux":"20000000"})"));
|
||||
REQUIRE(obj.GetFilaSwitch()->IsInstalled());
|
||||
|
||||
// info bits 24-27 = 0xF: switch-bound (0xE) but the input track is not yet valid -
|
||||
// the transient while the device is still homing the switch. The AMS must survive
|
||||
// (display keeps working) with an EMPTY binding set that excludes it from mapping.
|
||||
json print_push = json::parse(R"({
|
||||
"ams": {
|
||||
"tray_exist_bits": "1",
|
||||
"ams": [ { "id": "0", "info": "0F000E01", "tray": [ { "id": "0", "tray_color": "FF0000FF" } ] } ]
|
||||
}
|
||||
})");
|
||||
DevFilaSystemParser::ParseV1_0(print_push, &obj, obj.GetFilaSystem().get(), false);
|
||||
|
||||
const auto& ams_list = obj.GetFilaSystem()->GetAmsList();
|
||||
REQUIRE(ams_list.count("0") == 1);
|
||||
REQUIRE(ams_list.at("0")->GetBindedExtruderSet().empty());
|
||||
REQUIRE_FALSE(ams_list.at("0")->GetSwitcherPos().has_value());
|
||||
REQUIRE_FALSE(obj.GetFilaSwitch()->IsReady());
|
||||
|
||||
DevAmsTray* tray = obj.GetFilaSystem()->GetAmsTray("0", "0");
|
||||
REQUIRE(tray != nullptr);
|
||||
tray->m_fila_type = "PLA";
|
||||
|
||||
FilamentInfo fila;
|
||||
fila.id = 0;
|
||||
fila.type = "PLA";
|
||||
fila.color = "FF0000FF";
|
||||
|
||||
std::vector<FilamentInfo> result;
|
||||
std::vector<bool> map_opt(4, false);
|
||||
map_opt[MappingOption::USE_LEFT_AMS] = true;
|
||||
map_opt[MappingOption::USE_RIGHT_AMS] = true;
|
||||
DevMappingUtil::ams_filament_mapping(&obj, {fila}, result, map_opt, {}, false);
|
||||
REQUIRE(result.size() == 1);
|
||||
CHECK(result[0].tray_id == -1);
|
||||
|
||||
// Without the switch, the same 0xE AMS is dropped from the list entirely.
|
||||
MachineObject obj_no_switch(nullptr, nullptr, "test", "test_dev", "127.0.0.1");
|
||||
REQUIRE_FALSE(obj_no_switch.GetFilaSwitch()->IsInstalled());
|
||||
DevFilaSystemParser::ParseV1_0(print_push, &obj_no_switch, obj_no_switch.GetFilaSystem().get(), false);
|
||||
REQUIRE(obj_no_switch.GetFilaSystem()->GetAmsList().count("0") == 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user