mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-10 02:37:04 +00:00
fix: ams filament mapping workflow
This commit is contained in:
@@ -752,9 +752,9 @@ void DevFilaSystemParser::ParseV1_0(const json& jj, MachineObject* obj, DevFilaS
|
|||||||
{
|
{
|
||||||
curr_tray->remain = -1;
|
curr_tray->remain = -1;
|
||||||
}
|
}
|
||||||
if (tray_it->contains("tray_slot_placeholder")) {
|
// The tray objects are reused across status updates. Reset this
|
||||||
curr_tray->is_slot_placeholder = true;
|
// state when a previously empty slot receives a filament again.
|
||||||
}
|
curr_tray->is_slot_placeholder = tray_it->contains("tray_slot_placeholder");
|
||||||
int ams_id_int = 0;
|
int ams_id_int = 0;
|
||||||
int tray_id_int = 0;
|
int tray_id_int = 0;
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -4336,11 +4336,30 @@ void Sidebar::load_ams_list(MachineObject* obj)
|
|||||||
filament_ams_list = build_filament_ams_list(obj);
|
filament_ams_list = build_filament_ams_list(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool device_change = false;
|
|
||||||
const std::string& device = obj ? obj->get_dev_id() : "";
|
const std::string& device = obj ? obj->get_dev_id() : "";
|
||||||
if (p->ams_list_device != device) {
|
const bool same_device = p->ams_list_device == device;
|
||||||
|
|
||||||
|
// Keep sync metadata out of the device payload, but preserve it across a
|
||||||
|
// subscription refresh when the physical filament in a slot is unchanged.
|
||||||
|
// Otherwise the refreshed configs differ only by the missing
|
||||||
|
// filament_changed key, causing combo boxes to rebuild and lose their
|
||||||
|
// transient post-sync badges.
|
||||||
|
auto &previous_filament_ams_list = wxGetApp().preset_bundle->filament_ams_list;
|
||||||
|
for (auto &entry : filament_ams_list) {
|
||||||
|
auto previous = previous_filament_ams_list.find(entry.first);
|
||||||
|
const auto *previous_changed = previous == previous_filament_ams_list.end() ? nullptr :
|
||||||
|
dynamic_cast<const ConfigOptionBool *>(previous->second.option("filament_changed"));
|
||||||
|
if (!same_device || previous_changed == nullptr ||
|
||||||
|
previous->second.opt_string("filament_id", 0u) != entry.second.opt_string("filament_id", 0u)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
entry.second.set_key_value("filament_changed",
|
||||||
|
new ConfigOptionBool{previous_changed->value});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool device_change = !same_device;
|
||||||
|
if (device_change) {
|
||||||
p->ams_list_device = device;
|
p->ams_list_device = device;
|
||||||
device_change = true;
|
|
||||||
}
|
}
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": %1% items") % filament_ams_list.size();
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": %1% items") % filament_ams_list.size();
|
||||||
if (wxGetApp().preset_bundle->filament_ams_list == filament_ams_list && !device_change)
|
if (wxGetApp().preset_bundle->filament_ams_list == filament_ams_list && !device_change)
|
||||||
@@ -4350,9 +4369,27 @@ void Sidebar::load_ams_list(MachineObject* obj)
|
|||||||
wxGetApp().preset_bundle->filament_ams_list = filament_ams_list;
|
wxGetApp().preset_bundle->filament_ams_list = filament_ams_list;
|
||||||
|
|
||||||
for (auto c : p->combos_filament){
|
for (auto c : p->combos_filament){
|
||||||
|
c->set_sync_badge(false);
|
||||||
c->update();
|
c->update();
|
||||||
if (device_change) {
|
}
|
||||||
c->ShowBadge(false);//change printer,then clear badge
|
|
||||||
|
if (!device_change) {
|
||||||
|
size_t combo_index = 0;
|
||||||
|
for (const auto &entry : filament_ams_list) {
|
||||||
|
const auto &tray = entry.second;
|
||||||
|
const bool has_filament = !tray.opt_string("filament_id", 0u).empty();
|
||||||
|
const bool is_placeholder = tray.has("filament_slot_placeholder") &&
|
||||||
|
tray.opt_bool("filament_slot_placeholder", 0u);
|
||||||
|
if (!has_filament && !is_placeholder) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (combo_index >= p->combos_filament.size()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const auto *filament_changed = dynamic_cast<const ConfigOptionBool *>(tray.option("filament_changed"));
|
||||||
|
p->combos_filament[combo_index]->set_sync_badge(
|
||||||
|
has_filament && !is_placeholder && filament_changed != nullptr && filament_changed->value);
|
||||||
|
++combo_index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4526,18 +4563,32 @@ void Sidebar::sync_ams_list(bool is_from_big_sync_btn)
|
|||||||
auto tip = sync_color_only ? _L("Only filament color information has been synchronized from printer.") :
|
auto tip = sync_color_only ? _L("Only filament color information has been synchronized from printer.") :
|
||||||
_L("Filament type and color information have been synchronized, but slot information is not included.");
|
_L("Filament type and color information have been synchronized, but slot information is not included.");
|
||||||
c->SetToolTip(tip);
|
c->SetToolTip(tip);
|
||||||
c->ShowBadge(true);
|
c->set_sync_badge(true);
|
||||||
};
|
};
|
||||||
{ // badge ams filament
|
{ // badge ams filament
|
||||||
clear_combos_filament_badge();
|
clear_combos_filament_badge();
|
||||||
if (sync_result.direct_sync) {
|
if (sync_result.direct_sync) {
|
||||||
// Orca: PresetBundle::sync_ams_list rebuilds combos_filament
|
// A placeholder contributes a preserved project filament to the
|
||||||
// 1:1 from the AMS trays that produce a combo (loaded trays + placeholders; non-placeholder
|
// overwrite result, but it is not AMS-sourced and must not get a
|
||||||
// empty trays are skipped), so every resulting combo is AMS-sourced and gets a badge. The
|
// sync badge. Non-placeholder empty trays are omitted entirely.
|
||||||
// previous per-tray index walked the full filament_ams_list (including the skipped empties),
|
size_t combo_index = 0;
|
||||||
// so an empty slot before a loaded one dropped the badge for the trailing filaments.
|
for (const auto &entry : wxGetApp().preset_bundle->filament_ams_list) {
|
||||||
for (auto &c : p->combos_filament) {
|
const auto &tray = entry.second;
|
||||||
badge_combox_filament(c);
|
const bool has_filament = !tray.opt_string("filament_id", 0u).empty();
|
||||||
|
const bool is_placeholder = tray.has("filament_slot_placeholder") &&
|
||||||
|
tray.opt_bool("filament_slot_placeholder", 0u);
|
||||||
|
if (!has_filament && !is_placeholder) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (combo_index >= p->combos_filament.size()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (is_placeholder) {
|
||||||
|
p->combos_filament[combo_index]->set_sync_badge(false);
|
||||||
|
} else {
|
||||||
|
badge_combox_filament(p->combos_filament[combo_index]);
|
||||||
|
}
|
||||||
|
++combo_index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4935,17 +4986,14 @@ void Sidebar::clear_combos_filament_badge()
|
|||||||
{
|
{
|
||||||
auto &combos_filament = p->combos_filament;
|
auto &combos_filament = p->combos_filament;
|
||||||
for (auto &c : combos_filament) { // clear flag
|
for (auto &c : combos_filament) { // clear flag
|
||||||
c->ShowBadge(false);
|
c->set_sync_badge(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sidebar::udpate_combos_filament_badge() {
|
void Sidebar::udpate_combos_filament_badge() {
|
||||||
auto &combos_filament = p->combos_filament;
|
auto &combos_filament = p->combos_filament;
|
||||||
for (auto &c : combos_filament) {
|
for (auto &c : combos_filament) {
|
||||||
auto selection = c->GetSelection();
|
c->update_badge_according_flag();
|
||||||
auto select_flag = c->GetFlag(selection);
|
|
||||||
auto ok = select_flag == (int) PresetComboBox::FilamentAMSType::FROM_AMS;
|
|
||||||
c->ShowBadge(ok);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -10485,7 +10533,7 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
|
|||||||
sidebar->auto_calc_flushing_volumes(idx);
|
sidebar->auto_calc_flushing_volumes(idx);
|
||||||
}
|
}
|
||||||
auto select_flag = combo->GetFlag(selection);
|
auto select_flag = combo->GetFlag(selection);
|
||||||
combo->ShowBadge(select_flag == (int)PresetComboBox::FilamentAMSType::FROM_AMS);
|
combo->set_sync_badge(select_flag == (int)PresetComboBox::FilamentAMSType::FROM_AMS);
|
||||||
q->on_filament_change(idx);
|
q->on_filament_change(idx);
|
||||||
}
|
}
|
||||||
bool select_preset = !combo->selection_is_changed_according_to_physical_printers();
|
bool select_preset = !combo->selection_is_changed_according_to_physical_printers();
|
||||||
|
|||||||
@@ -994,7 +994,13 @@ void PlaterPresetComboBox::update_badge_according_flag() {
|
|||||||
auto selection = GetSelection();
|
auto selection = GetSelection();
|
||||||
auto select_flag = GetFlag(selection);
|
auto select_flag = GetFlag(selection);
|
||||||
auto ok = select_flag == (int) PresetComboBox::FilamentAMSType::FROM_AMS;
|
auto ok = select_flag == (int) PresetComboBox::FilamentAMSType::FROM_AMS;
|
||||||
ShowBadge(ok);
|
ShowBadge(m_sync_badge || ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlaterPresetComboBox::set_sync_badge(bool show)
|
||||||
|
{
|
||||||
|
m_sync_badge = show;
|
||||||
|
ShowBadge(show);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlaterPresetComboBox::switch_to_tab()
|
bool PlaterPresetComboBox::switch_to_tab()
|
||||||
|
|||||||
@@ -205,6 +205,7 @@ public:
|
|||||||
void msw_rescale() override;
|
void msw_rescale() override;
|
||||||
void OnSelect(wxCommandEvent& evt) override;
|
void OnSelect(wxCommandEvent& evt) override;
|
||||||
void update_badge_according_flag();
|
void update_badge_according_flag();
|
||||||
|
void set_sync_badge(bool show);
|
||||||
|
|
||||||
FilamentColor get_cur_color_info();
|
FilamentColor get_cur_color_info();
|
||||||
void show_default_color_picker();
|
void show_default_color_picker();
|
||||||
@@ -214,6 +215,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
// BBS
|
// BBS
|
||||||
wxColor m_color;
|
wxColor m_color;
|
||||||
|
bool m_sync_badge{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,37 @@
|
|||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
|
|
||||||
|
TEST_CASE("AMS tray placeholder state follows the latest status", "[DevFilaSystem]")
|
||||||
|
{
|
||||||
|
MachineObject obj(nullptr, nullptr, "test", "test_dev", "127.0.0.1");
|
||||||
|
|
||||||
|
const json empty_slot = json::parse(R"({
|
||||||
|
"ams": {
|
||||||
|
"tray_exist_bits": "0",
|
||||||
|
"ams": [ { "id": "0", "info": "00000001", "tray": [
|
||||||
|
{ "id": "0", "tray_slot_placeholder": "1", "tray_color": "00000000" }
|
||||||
|
] } ]
|
||||||
|
}
|
||||||
|
})");
|
||||||
|
DevFilaSystemParser::ParseV1_0(empty_slot, &obj, obj.GetFilaSystem().get(), false);
|
||||||
|
|
||||||
|
DevAmsTray* tray = obj.GetFilaSystem()->GetAmsTray("0", "0");
|
||||||
|
REQUIRE(tray != nullptr);
|
||||||
|
REQUIRE(tray->is_slot_placeholder);
|
||||||
|
|
||||||
|
const json loaded_slot = json::parse(R"({
|
||||||
|
"ams": {
|
||||||
|
"tray_exist_bits": "1",
|
||||||
|
"ams": [ { "id": "0", "info": "00000001", "tray": [
|
||||||
|
{ "id": "0", "tray_color": "FF0000FF" }
|
||||||
|
] } ]
|
||||||
|
}
|
||||||
|
})");
|
||||||
|
DevFilaSystemParser::ParseV1_0(loaded_slot, &obj, obj.GetFilaSystem().get(), false);
|
||||||
|
|
||||||
|
CHECK_FALSE(tray->is_slot_placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Switch-bound AMS trays map to the left extruder", "[DevMapping]")
|
TEST_CASE("Switch-bound AMS trays map to the left extruder", "[DevMapping]")
|
||||||
{
|
{
|
||||||
MachineObject obj(nullptr, nullptr, "test", "test_dev", "127.0.0.1");
|
MachineObject obj(nullptr, nullptr, "test", "test_dev", "127.0.0.1");
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
#include <pybind11/embed.h>
|
#include <pybind11/embed.h>
|
||||||
#include <pybind11/pybind11.h>
|
#include <pybind11/pybind11.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <future>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -69,6 +72,45 @@ TEST_CASE("unit: Moonraker reports untranslated commands as not supported", "[un
|
|||||||
CHECK(agent.send_message("dev", "{not json", 0, 0) == BAMBU_NETWORK_ERR_INVALID_RESULT);
|
CHECK(agent.send_message("dev", "{not json", 0, 0) == BAMBU_NETWORK_ERR_INVALID_RESULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("unit: MoonrakerPrinterAgent::fetch_filament_info is fire-and-forget and dispatches to the derived override",
|
||||||
|
"[unit][moonraker]")
|
||||||
|
{
|
||||||
|
class RecordingAgent : public Slic3r::MoonrakerPrinterAgent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit RecordingAgent(std::string log_dir) : MoonrakerPrinterAgent(std::move(log_dir)) {}
|
||||||
|
|
||||||
|
std::atomic<bool> invoked{false};
|
||||||
|
std::promise<void> release_gate;
|
||||||
|
std::promise<void> done_promise;
|
||||||
|
|
||||||
|
bool do_fetch_filament_info(std::string /*dev_id*/) override
|
||||||
|
{
|
||||||
|
invoked.store(true);
|
||||||
|
// Block here until the test explicitly releases us, proving the caller
|
||||||
|
// (fetch_filament_info) does not wait for this to run.
|
||||||
|
release_gate.get_future().wait();
|
||||||
|
done_promise.set_value();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto agent = std::make_shared<RecordingAgent>(std::string{});
|
||||||
|
auto done_future = agent->done_promise.get_future();
|
||||||
|
|
||||||
|
bool immediate_result = agent->fetch_filament_info("test-dev");
|
||||||
|
|
||||||
|
// fetch_filament_info must return before do_fetch_filament_info completes — prove
|
||||||
|
// it by confirming the background call is still blocked on the gate right now.
|
||||||
|
REQUIRE(immediate_result == true);
|
||||||
|
REQUIRE(done_future.wait_for(std::chrono::milliseconds(100)) == std::future_status::timeout);
|
||||||
|
|
||||||
|
// Now let the background call finish and confirm it actually ran (polymorphic dispatch).
|
||||||
|
agent->release_gate.set_value();
|
||||||
|
REQUIRE(done_future.wait_for(std::chrono::seconds(2)) == std::future_status::ready);
|
||||||
|
REQUIRE(agent->invoked.load() == true);
|
||||||
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// UNIT - printer-agent registry duplicate handling.
|
// UNIT - printer-agent registry duplicate handling.
|
||||||
// Confirms a duplicate agent id is rejected so a plugin cannot shadow a built-in
|
// Confirms a duplicate agent id is rejected so a plugin cannot shadow a built-in
|
||||||
|
|||||||
Reference in New Issue
Block a user