mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-25 18:00:57 +00:00
Fixes issue where it would display a question mark instead of empty when there's an empty filament slot in the AMS or external slot
This commit is contained in:
@@ -169,9 +169,12 @@ Orca agent does not poll the Moonraker `lane_data` namespace, which exists for
|
||||
Moonraker-channel consumers. Slot presence is the user's declaration, not sensed material:
|
||||
`tray_exist_bits` and the presence of a `vir_slot` entry mark a slot as present even with an
|
||||
empty `tray_type`, which is the `is_exists` state the filament UI reads. A full status frame
|
||||
(`msg=0`, or a LAN frame with no `msg`) is authoritative for removals, so a virtual tray id
|
||||
absent from a populated `vir_slot` is dropped; a delta frame (`msg=1`) only updates the
|
||||
entries it names and leaves omitted entries held.
|
||||
(`msg=0`, or a LAN frame with no `msg`) updates each virtual-tray ID it contains but does not
|
||||
remove IDs already observed during the current connection. An observed ID remains if omitted
|
||||
from a populated `vir_slot`, if `vir_slot` is empty, or if the field is absent. Before any
|
||||
virtual tray has been observed, a full frame with no virtual-tray entries clears the initial
|
||||
placeholder. `MachineObject::reset()` clears retained IDs on reconnect. Delta frames update
|
||||
named entries only. This retention rule is specific to the native Orca agent.
|
||||
|
||||
Writes follow the same edge-translation rule as the rest of the agent. The shared
|
||||
`MachineObject` command builders emit Bambu-shaped `print.ams_*` payloads, and the agent's
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include "DevUtil.h"
|
||||
#include "DevUtilBackend.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace nlohmann;
|
||||
|
||||
namespace Slic3r {
|
||||
@@ -42,6 +44,15 @@ void DevAmsTray::UpdateColorFromStr(const std::string& color)
|
||||
}
|
||||
}
|
||||
|
||||
void DevAmsTray::UpdateEmptyState(bool material_fields_present)
|
||||
{
|
||||
const auto is_zero_or_empty = [](const std::string& value) {
|
||||
return value.empty() || std::all_of(value.begin(), value.end(), [](char c) { return c == '0'; });
|
||||
};
|
||||
is_empty = material_fields_present && setting_id.empty() && m_fila_type.empty() &&
|
||||
is_zero_or_empty(color) && is_zero_or_empty(tag_uid);
|
||||
}
|
||||
|
||||
void DevAmsTray::reset()
|
||||
{
|
||||
tag_uid = "";
|
||||
@@ -63,6 +74,7 @@ void DevAmsTray::reset()
|
||||
k = 0.0f;
|
||||
n = 0.0f;
|
||||
is_bbl = false;
|
||||
is_empty = false;
|
||||
hold_count = 0;
|
||||
remain = 0;
|
||||
}
|
||||
@@ -662,7 +674,7 @@ void DevFilaSystemParser::ParseV1_0(const json& jj, MachineObject* obj, DevFilaS
|
||||
//std::string type = (*tray_it)["tray_type"].get<std::string>();
|
||||
std::string type = MachineObject::setting_id_to_type(curr_tray->setting_id, (*tray_it)["tray_type"].get<std::string>());
|
||||
// curr_tray->setting_id is our OF id; GFS00/GFS01 are the printer's own support-filament ids.
|
||||
auto* agent = GUI::wxGetApp().getAgent();
|
||||
auto* agent = wxTheApp != nullptr ? GUI::wxGetApp().getAgent() : nullptr;
|
||||
const std::string printer_filament_id = agent ? agent->from_orca_filament_id(curr_tray->setting_id) : curr_tray->setting_id;
|
||||
if (printer_filament_id == "GFS00")
|
||||
{
|
||||
@@ -762,6 +774,7 @@ void DevFilaSystemParser::ParseV1_0(const json& jj, MachineObject* obj, DevFilaS
|
||||
{
|
||||
curr_tray->remain = -1;
|
||||
}
|
||||
curr_tray->UpdateEmptyState(tray_it->contains("tray_info_idx") && tray_it->contains("tray_type"));
|
||||
// The tray objects are reused across status updates. Reset this
|
||||
// state when a previously empty slot receives a filament again.
|
||||
curr_tray->is_slot_placeholder = tray_it->contains("tray_slot_placeholder");
|
||||
|
||||
@@ -83,6 +83,7 @@ public:
|
||||
wxColour wx_color;
|
||||
bool is_bbl;
|
||||
bool is_exists = false;
|
||||
bool is_empty = false; // Explicitly reported as having no filament.
|
||||
bool is_slot_placeholder = false; // Orca: True for empty tray slots from pull-mode agents
|
||||
int hold_count = 0;
|
||||
int remain = 0; // filament remain: 0 ~ 100
|
||||
@@ -91,13 +92,15 @@ public:
|
||||
// operators
|
||||
bool operator==(DevAmsTray const& o) const
|
||||
{
|
||||
return id == o.id && m_fila_type == o.m_fila_type && filament_setting_id == o.filament_setting_id && color == o.color;
|
||||
return id == o.id && m_fila_type == o.m_fila_type && filament_setting_id == o.filament_setting_id &&
|
||||
color == o.color && is_empty == o.is_empty;
|
||||
}
|
||||
bool operator!=(DevAmsTray const& o) const { return !operator==(o); }
|
||||
|
||||
// setters
|
||||
void reset();
|
||||
void UpdateColorFromStr(const std::string& color);
|
||||
void UpdateEmptyState(bool material_fields_present);
|
||||
void set_hold_count() { hold_count = HOLD_COUNT_MAX; }
|
||||
|
||||
// getter
|
||||
@@ -432,4 +435,4 @@ struct DevFilamentDryingPreset
|
||||
float filament_dev_ams_drying_heat_distortion_temperature = 0.0f;
|
||||
};
|
||||
|
||||
}// namespace Slic3r
|
||||
}// namespace Slic3r
|
||||
|
||||
@@ -5247,7 +5247,7 @@ DevAmsTray MachineObject::parse_vt_tray(json vtray)
|
||||
//std::string type = vtray["tray_type"].get<std::string>();
|
||||
std::string type = setting_id_to_type(vt_tray.setting_id, vtray["tray_type"].get<std::string>());
|
||||
// vt_tray.setting_id is our OF id (translated on the way in); the two support ids below are the printer's own.
|
||||
auto* agent = GUI::wxGetApp().getAgent();
|
||||
auto* agent = wxTheApp != nullptr ? GUI::wxGetApp().getAgent() : nullptr;
|
||||
const std::string printer_filament_id = agent ? agent->from_orca_filament_id(vt_tray.setting_id) : vt_tray.setting_id;
|
||||
if (printer_filament_id == "GFS00") {
|
||||
vt_tray.m_fila_type = "PLA-S";
|
||||
@@ -5342,6 +5342,7 @@ DevAmsTray MachineObject::parse_vt_tray(json vtray)
|
||||
else {
|
||||
vt_tray.remain = -1;
|
||||
}
|
||||
vt_tray.UpdateEmptyState(vtray.contains("tray_info_idx") && vtray.contains("tray_type"));
|
||||
}
|
||||
|
||||
return vt_tray;
|
||||
|
||||
@@ -943,7 +943,11 @@ int ReselectMachineDialog::CaculateSwitcherDistribution(MachineObject* obj, cons
|
||||
const auto& can = ams.cans[j];
|
||||
auto id = getTrayID(obj, ams.ams_id, can.can_id);
|
||||
auto material = can.material_name;
|
||||
if (can.material_state == AMSCanType::AMS_CAN_TYPE_THIRDBRAND ||
|
||||
if (can.is_empty || can.material_state == AMSCanType::AMS_CAN_TYPE_EMPTY)
|
||||
{
|
||||
material = L("Empty");
|
||||
}
|
||||
else if (can.material_state == AMSCanType::AMS_CAN_TYPE_THIRDBRAND ||
|
||||
can.material_state == AMSCanType::AMS_CAN_TYPE_BRAND ||
|
||||
can.material_state == AMSCanType::AMS_CAN_TYPE_VIRTUAL)
|
||||
{
|
||||
@@ -952,11 +956,6 @@ int ReselectMachineDialog::CaculateSwitcherDistribution(MachineObject* obj, cons
|
||||
material = L("?");
|
||||
}
|
||||
}
|
||||
if (can.material_state == AMSCanType::AMS_CAN_TYPE_EMPTY)
|
||||
{
|
||||
material = "Empty";
|
||||
}
|
||||
|
||||
auto itOK = std::find_if(posOK.begin(), posOK.end(), [&](const trayHelper& tray){
|
||||
auto amsID = std::get<0>(tray);
|
||||
auto slotID = std::get<1>(tray);
|
||||
@@ -1043,4 +1042,4 @@ ReselectMachineDialog::~ReselectMachineDialog()
|
||||
|
||||
} // namespace GUI
|
||||
|
||||
} // namespace Slic3r
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -72,6 +72,7 @@ bool AMSinfo::parse_ams_info(MachineObject *obj, DevAms *ams, bool remain_flag,
|
||||
Caninfo info;
|
||||
// tray is exists
|
||||
if (it != ams->GetTrays().end() && it->second->is_exists) {
|
||||
info.is_empty = it->second->is_empty;
|
||||
if (it->second->is_tray_info_ready()) {
|
||||
info.can_id = it->second->id;
|
||||
info.ctype = it->second->ctype;
|
||||
@@ -139,6 +140,7 @@ void AMSinfo::parse_ext_info(MachineObject* obj, DevAmsTray tray) {
|
||||
this->ams_type = AMSModel::EXT_AMS;
|
||||
Caninfo info;
|
||||
info.can_id = std::to_string(0);
|
||||
info.is_empty = tray.is_empty;
|
||||
this->cans.clear();
|
||||
|
||||
if (tray.id == std::to_string(VIRTUAL_TRAY_MAIN_ID))
|
||||
@@ -1101,9 +1103,9 @@ void AMSLib::render_lite_text(wxDC& dc)
|
||||
dc.SetTextForeground(temp_text_colour);
|
||||
|
||||
auto libsize = GetSize();
|
||||
if (m_info.material_state == AMSCanType::AMS_CAN_TYPE_THIRDBRAND
|
||||
if (!m_info.is_empty && (m_info.material_state == AMSCanType::AMS_CAN_TYPE_THIRDBRAND
|
||||
|| m_info.material_state == AMSCanType::AMS_CAN_TYPE_BRAND
|
||||
|| m_info.material_state == AMSCanType::AMS_CAN_TYPE_VIRTUAL) {
|
||||
|| m_info.material_state == AMSCanType::AMS_CAN_TYPE_VIRTUAL)) {
|
||||
|
||||
if (m_info.material_name.empty()) {
|
||||
auto tsize = dc.GetMultiLineTextExtent("?");
|
||||
@@ -1151,7 +1153,7 @@ void AMSLib::render_lite_text(wxDC& dc)
|
||||
}
|
||||
}
|
||||
|
||||
if (m_info.material_state == AMSCanType::AMS_CAN_TYPE_EMPTY) {
|
||||
if (m_info.material_state == AMSCanType::AMS_CAN_TYPE_EMPTY || m_info.is_empty) {
|
||||
auto tsize = dc.GetMultiLineTextExtent(_L("/"));
|
||||
auto pot = wxPoint((libsize.x - tsize.x) / 2 + FromDIP(2), (libsize.y - tsize.y) / 2 + FromDIP(3));
|
||||
dc.DrawText(_L("/"), pot);
|
||||
@@ -1202,9 +1204,9 @@ void AMSLib::render_generic_text(wxDC &dc)
|
||||
}
|
||||
|
||||
auto libsize = GetSize();
|
||||
if (m_info.material_state == AMSCanType::AMS_CAN_TYPE_THIRDBRAND
|
||||
if (!m_info.is_empty && (m_info.material_state == AMSCanType::AMS_CAN_TYPE_THIRDBRAND
|
||||
|| m_info.material_state == AMSCanType::AMS_CAN_TYPE_BRAND
|
||||
|| m_info.material_state == AMSCanType::AMS_CAN_TYPE_VIRTUAL) {
|
||||
|| m_info.material_state == AMSCanType::AMS_CAN_TYPE_VIRTUAL)) {
|
||||
|
||||
if (m_info.material_name.empty() /*&& m_info.material_state != AMSCanType::AMS_CAN_TYPE_VIRTUAL*/) {
|
||||
auto tsize = dc.GetMultiLineTextExtent("?");
|
||||
@@ -1284,7 +1286,7 @@ void AMSLib::render_generic_text(wxDC &dc)
|
||||
}
|
||||
}
|
||||
|
||||
if (m_info.material_state == AMSCanType::AMS_CAN_TYPE_EMPTY) {
|
||||
if (m_info.material_state == AMSCanType::AMS_CAN_TYPE_EMPTY || m_info.is_empty) {
|
||||
auto tsize = dc.GetMultiLineTextExtent(_L("Empty"));
|
||||
auto pot = wxPoint((libsize.x - tsize.x) / 2, (libsize.y - tsize.y) / 2 + FromDIP(3));
|
||||
dc.DrawText(_L("Empty"), pot);
|
||||
@@ -2781,7 +2783,7 @@ void AMSPreview::doRender(wxDC &dc)
|
||||
}
|
||||
else {
|
||||
wxRect rect(left, (size.y - AMS_ITEM_CUBE_SIZE.y) / 2, AMS_ITEM_CUBE_SIZE.x, AMS_ITEM_CUBE_SIZE.y);
|
||||
if (iter->material_state == AMSCanType::AMS_CAN_TYPE_EMPTY) {
|
||||
if (iter->material_state == AMSCanType::AMS_CAN_TYPE_EMPTY || iter->is_empty) {
|
||||
dc.SetPen(wxPen(wxColor(0, 0, 0)));
|
||||
dc.DrawLine(rect.GetRight() - FromDIP(1), rect.GetTop() + FromDIP(1), rect.GetLeft() + FromDIP(1), rect.GetBottom() - FromDIP(1));
|
||||
}
|
||||
|
||||
@@ -183,6 +183,7 @@ struct Caninfo
|
||||
wxString material_name;
|
||||
wxColour material_colour = {*wxWHITE};
|
||||
AMSCanType material_state;
|
||||
bool is_empty = false;
|
||||
int ctype=0;
|
||||
int material_remain = 100;
|
||||
int cali_idx = -1;
|
||||
@@ -198,6 +199,7 @@ public:
|
||||
material_name == other.material_name &&
|
||||
material_colour == other.material_colour &&
|
||||
material_state == other.material_state &&
|
||||
is_empty == other.is_empty &&
|
||||
ctype == other.ctype &&
|
||||
material_remain == other.material_remain &&
|
||||
cali_idx == other.cali_idx &&
|
||||
|
||||
@@ -7,6 +7,7 @@ add_executable(${_TEST_NAME}_tests
|
||||
test_filament_bitmap_utils.cpp
|
||||
test_device_progress.cpp
|
||||
test_device_manager.cpp
|
||||
test_ams_item.cpp
|
||||
test_device_manager_integration.cpp
|
||||
test_web_media_controller.cpp
|
||||
test_network_versions.cpp
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
// why: match the GUI include order to avoid rpcndr.h byte/std::byte
|
||||
// ambiguity in the Windows COM headers.
|
||||
#ifdef WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
#include <wx/timer.h>
|
||||
|
||||
#include "slic3r/GUI/Widgets/AMSItem.hpp"
|
||||
#include "slic3r/GUI/DeviceCore/DevFilaSystem.h"
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Slic3r::GUI;
|
||||
|
||||
TEST_CASE("Configured empty AMS trays remain distinct from unknown trays", "[AMSItem]")
|
||||
{
|
||||
MachineObject machine(nullptr, nullptr, "test", "test-device", "127.0.0.1");
|
||||
machine.printer_agent_id = "orca";
|
||||
|
||||
machine.parse_json("lan", R"({"print":{"command":"push_status","msg":0,"ams":{
|
||||
"ams_exist_bits":"1","tray_exist_bits":"3","ams":[
|
||||
{"id":"0","info":"0001","tray":[
|
||||
{"id":"0","tag_uid":"0000000000000000","tray_info_idx":"","tray_type":"","tray_color":"00000000"},
|
||||
{"id":"1"}
|
||||
]}
|
||||
]
|
||||
}}})", false);
|
||||
|
||||
const auto& ams_list = machine.GetFilaSystem()->GetAmsList();
|
||||
const auto ams_it = ams_list.find("0");
|
||||
REQUIRE(ams_it != ams_list.end());
|
||||
auto* ams = ams_it->second;
|
||||
REQUIRE(ams != nullptr);
|
||||
REQUIRE(ams->GetTray("0") != nullptr);
|
||||
CHECK(ams->GetTray("0")->is_exists);
|
||||
AMSinfo info;
|
||||
REQUIRE(info.parse_ams_info(&machine, ams));
|
||||
REQUIRE(info.cans.size() == 2);
|
||||
|
||||
CHECK(info.cans[0].is_empty);
|
||||
CHECK(info.cans[0].material_state == AMSCanType::AMS_CAN_TYPE_THIRDBRAND);
|
||||
CHECK_FALSE(info.cans[1].is_empty);
|
||||
CHECK(info.cans[1].material_state == AMSCanType::AMS_CAN_TYPE_THIRDBRAND);
|
||||
}
|
||||
|
||||
TEST_CASE("Empty external slots remain distinct from unknown slots", "[AMSItem]")
|
||||
{
|
||||
MachineObject machine(nullptr, nullptr, "test", "test-device", "127.0.0.1");
|
||||
machine.printer_agent_id = "orca";
|
||||
|
||||
machine.parse_json("lan", R"({"print":{"command":"push_status","msg":0,"vir_slot":[
|
||||
{"id":"255","tag_uid":"0000000000000000","tray_info_idx":"","tray_type":"","tray_color":"00000000"},
|
||||
{"id":"254"}
|
||||
]}})", false);
|
||||
|
||||
REQUIRE(machine.vt_slot.size() == 2);
|
||||
CHECK(machine.vt_slot[0].is_exists);
|
||||
AMSinfo empty_info;
|
||||
empty_info.parse_ext_info(&machine, machine.vt_slot[0]);
|
||||
CHECK(empty_info.cans[0].is_empty);
|
||||
CHECK(empty_info.cans[0].material_state == AMSCanType::AMS_CAN_TYPE_VIRTUAL);
|
||||
|
||||
AMSinfo unknown_info;
|
||||
unknown_info.parse_ext_info(&machine, machine.vt_slot[1]);
|
||||
CHECK_FALSE(unknown_info.cans[0].is_empty);
|
||||
CHECK(unknown_info.cans[0].material_state == AMSCanType::AMS_CAN_TYPE_VIRTUAL);
|
||||
}
|
||||
Reference in New Issue
Block a user