feat: Isolate devices across different printer agents

This commit is contained in:
Ian Chua
2026-08-06 16:11:44 +08:00
parent b2e05d0683
commit 159e577543
9 changed files with 157 additions and 32 deletions

View File

@@ -853,6 +853,10 @@ std::string AppConfig::load()
local_machine.dev_ip = p["dev_ip"].get<std::string>();
if (p.contains("printer_type"))
local_machine.printer_type = p["printer_type"].get<std::string>();
if (p.contains("printer_agent_id"))
local_machine.printer_agent_id = p["printer_agent_id"].get<std::string>();
if (p.contains("access_code"))
local_machine.access_code = p["access_code"].get<std::string>();
m_local_machines[local_machine.dev_id] = local_machine;
}
} else {
@@ -1065,6 +1069,8 @@ void AppConfig::save()
m_json["dev_name"] = local_machine.second.dev_name;
m_json["dev_ip"] = local_machine.second.dev_ip;
m_json["printer_type"] = local_machine.second.printer_type;
m_json["printer_agent_id"] = local_machine.second.printer_agent_id;
m_json["access_code"] = local_machine.second.access_code;
j["local_machines"][local_machine.first] = m_json;
}

View File

@@ -61,10 +61,19 @@ struct BBLocalMachine
std::string dev_ip;
std::string dev_id; /* serial number */
std::string printer_type; /* model_id */
std::string printer_agent_id; /* id of the IPrinterAgent that discovered/bound this device, e.g. "bbl"; empty for entries persisted before this field existed */
// Access code, scoped to printer_agent_id above - so a code saved while bound under one
// printer agent isn't treated as valid for a different, independent agent talking to the
// same physical dev_id. Empty for entries persisted before this field existed; those fall
// back to the legacy flat "access_code"/"user_access_code" AppConfig sections (BBL-only,
// since BBL was the only agent when they were saved) - see
// get_access_code_with_legacy_fallback() in DevManager.cpp.
std::string access_code;
bool operator==(const BBLocalMachine& other) const
{
return dev_name == other.dev_name && dev_ip == other.dev_ip && dev_id == other.dev_id && printer_type == other.printer_type;
return dev_name == other.dev_name && dev_ip == other.dev_ip && dev_id == other.dev_id && printer_type == other.printer_type &&
printer_agent_id == other.printer_agent_id && access_code == other.access_code;
}
bool operator!=(const BBLocalMachine& other) const { return !operator==(other); }
};

View File

@@ -10,20 +10,36 @@
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/Utils/NetworkAgentFactory.hpp"
#include "libslic3r/Time.hpp"
using namespace nlohmann;
namespace {
// Orca: access_code and user_access_code used to be separate AppConfig keys before the two
// fields were merged; fall back to the legacy key so existing users' saved codes aren't lost.
std::string get_access_code_with_legacy_fallback(Slic3r::AppConfig* config, const std::string& dev_id)
// Orca: access_code lives on BBLocalMachine::access_code (keyed by dev_id via
// get_local_machines(), scoped by the record's own printer_agent_id field) - so binding a
// printer under one agent doesn't silently appear as already-bound under a different,
// independent agent. This only covers LAN devices (BBLocalMachine's own scope); access_code
// and user_access_code used to be the only, flat dev_id-only AppConfig keys before
// BBLocalMachine::access_code existed, and codes saved back then are still stored flat (no
// agent association at all). Since BBL was the only agent that existed at the time, honor
// those flat legacy keys as implicitly BBL's - but only for the BBL agent, so they aren't
// leaked to other agents that never bound the device themselves.
std::string get_access_code_with_legacy_fallback(Slic3r::AppConfig* config, const std::string& dev_id, const std::string& agent_id)
{
std::string code = config->get("access_code", dev_id);
if (code.empty())
code = config->get("user_access_code", dev_id);
return code;
const auto& machines = config->get_local_machines();
auto it = machines.find(dev_id);
if (it != machines.end() && it->second.printer_agent_id == agent_id && !it->second.access_code.empty())
return it->second.access_code;
if (agent_id == Slic3r::BBL_PRINTER_AGENT_ID || agent_id.empty()) {
std::string code = config->get("access_code", dev_id);
if (code.empty())
code = config->get("user_access_code", dev_id);
return code;
}
return "";
}
}
@@ -55,12 +71,13 @@ namespace Slic3r
continue;
MachineObject* obj = new MachineObject(this, m_agent, m.dev_name, m.dev_id, m.dev_ip);
obj->printer_type = m.printer_type;
obj->printer_agent_id = m.printer_agent_id;
obj->dev_connection_type = "lan";
obj->bind_state = "free";
obj->bind_sec_link = "secure";
obj->m_is_online = true;
obj->last_alive = Slic3r::Utils::get_current_time_utc();
obj->set_access_code(get_access_code_with_legacy_fallback(config, m.dev_id), false);
obj->set_access_code(get_access_code_with_legacy_fallback(config, m.dev_id, obj->printer_agent_id), false);
if (obj->has_access_right()) {
localMachineList.insert(std::make_pair(m.dev_id, obj));
} else {
@@ -77,10 +94,12 @@ namespace Slic3r
if (m.is_lan_mode_printer()) {
if (m.has_access_right()) {
BBLocalMachine local_machine;
local_machine.dev_id = m.get_dev_id();
local_machine.dev_name = m.get_dev_name();
local_machine.dev_ip = m.get_dev_ip();
local_machine.printer_type = m.printer_type;
local_machine.dev_id = m.get_dev_id();
local_machine.dev_name = m.get_dev_name();
local_machine.dev_ip = m.get_dev_ip();
local_machine.printer_type = m.printer_type;
local_machine.printer_agent_id = m.printer_agent_id;
local_machine.access_code = m.get_access_code();
config->update_local_machine(local_machine);
}
} else {
@@ -143,6 +162,14 @@ namespace Slic3r
}
}
std::string DeviceManager::get_current_printer_agent_id() const
{
if (!m_agent)
return "";
auto printer_agent = m_agent->get_printer_agent();
return printer_agent ? printer_agent->get_agent_info().id : "";
}
void DeviceManager::EnableMultiMachine(bool enable)
{
m_agent->enable_multi_machine(enable);
@@ -339,6 +366,7 @@ namespace Slic3r
/* insert a new machine */
obj = new MachineObject(this, m_agent, dev_name, dev_id, dev_ip);
obj->printer_type = _parse_printer_type(printer_type_str);
obj->printer_agent_id = get_current_printer_agent_id();
obj->wifi_signal = printer_signal;
obj->dev_connection_type = connect_type;
obj->bind_state = bind_state;
@@ -350,7 +378,7 @@ namespace Slic3r
//load access code
AppConfig* config = Slic3r::GUI::wxGetApp().app_config;
if (config) {
obj->set_access_code(get_access_code_with_legacy_fallback(config, dev_id), false);
obj->set_access_code(get_access_code_with_legacy_fallback(config, dev_id, obj->printer_agent_id), false);
}
localMachineList.insert(std::make_pair(dev_id, obj));
@@ -379,6 +407,7 @@ namespace Slic3r
obj = it->second;
} else {
obj = new MachineObject(this, m_agent, machine.dev_name, machine.dev_id, machine.dev_ip);
obj->printer_agent_id = get_current_printer_agent_id();
localMachineList.insert(std::make_pair(machine.dev_id, obj));
}
if (machine.printer_type.empty())
@@ -505,16 +534,26 @@ namespace Slic3r
OnSelectedMachineChanged(previous_selected_machine, selected_machine);
}
void DeviceManager::clear_other_devices()
void DeviceManager::clear_other_devices(const std::string& target_agent_id)
{
// why: on agent swap, keep "My Devices" but drop the transient "Other Devices"
// Those belong to the previous agent's network scan; the new agent's start_discovery re-populates its own.
//
// Also drop "My Devices" stamped by a different agent than the one we're swapping to
// (target_agent_id, passed by the caller since the live agent hasn't been repointed yet
// at this point): otherwise a device first discovered under agent A survives every swap
// with a stale printer_agent_id, stays hidden from every agent's filtered list, and only
// gets re-tagged if something happens to delete and re-create it (e.g. account logout).
// Dropping it here instead lets the new agent's start_discovery re-insert and re-stamp it
// like any other fresh device.
const auto my = get_my_machine_list();
for (auto it = localMachineList.begin(); it != localMachineList.end();)
{
if (my.find(it->first) == my.end())
const bool is_my_device = my.find(it->first) != my.end();
const bool agent_mismatch = !target_agent_id.empty() && it->second &&
it->second->printer_agent_id != target_agent_id;
if (!is_my_device || agent_mismatch)
{
// not a "My Device" -> an "Other Device"
delete it->second;
it = localMachineList.erase(it);
}
@@ -697,13 +736,16 @@ namespace Slic3r
m_agent->add_subscribe(subscribe_list_cache);
}
std::map<std::string, MachineObject*> DeviceManager::get_my_machine_list()
std::map<std::string, MachineObject*> DeviceManager::get_my_machine_list(const std::string& agent_id)
{
std::map<std::string, MachineObject*> result;
for (auto it = userMachineList.begin(); it != userMachineList.end(); it++)
{
if (it->second && !it->second->is_lan_mode_printer())
if (!it->second || (!agent_id.empty() && it->second->printer_agent_id != agent_id))
continue;
if (!it->second->is_lan_mode_printer())
{
result.insert(std::make_pair(it->first, it->second));
}
@@ -711,7 +753,10 @@ namespace Slic3r
for (auto it = localMachineList.begin(); it != localMachineList.end(); it++)
{
if (it->second && it->second->has_access_right() && it->second->is_avaliable() && it->second->is_lan_mode_printer())
if (!it->second || (!agent_id.empty() && it->second->printer_agent_id != agent_id))
continue;
if (it->second->has_access_right() && it->second->is_avaliable() && it->second->is_lan_mode_printer())
{
// remove redundant in userMachineList
if (result.find(it->first) == result.end())
@@ -723,12 +768,15 @@ namespace Slic3r
return result;
}
std::map<std::string, MachineObject*> DeviceManager::get_my_cloud_machine_list()
std::map<std::string, MachineObject*> DeviceManager::get_my_cloud_machine_list(const std::string& agent_id)
{
std::map<std::string, MachineObject*> result;
for (auto it = userMachineList.begin(); it != userMachineList.end(); it++)
{
if (it->second && !it->second->is_lan_mode_printer()) { result.emplace(*it); }
if (!it->second || (!agent_id.empty() && it->second->printer_agent_id != agent_id))
continue;
if (!it->second->is_lan_mode_printer()) { result.emplace(*it); }
}
return result;
}
@@ -801,6 +849,7 @@ namespace Slic3r
else
{
obj = new MachineObject(this, m_agent, "", "", "");
obj->printer_agent_id = get_current_printer_agent_id();
if (m_agent)
{
obj->set_bind_status(m_agent->get_user_name(provider));

View File

@@ -74,7 +74,10 @@ public:
void erase_user_machine(std::string dev_id) { userMachineList.erase(dev_id); }
void clean_user_info(bool keep_local_selection = false);
void clear_other_devices();
// target_agent_id: id of the agent being swapped to (empty = no agent-mismatch check,
// just the original "drop Other Devices" behavior). Pass the incoming agent's id, not the
// live one - this runs before the live agent is repointed.
void clear_other_devices(const std::string& target_agent_id = "");
void load_last_machine();
void update_user_machine_list_info(const std::string& provider);
@@ -90,10 +93,15 @@ public:
/* my machine*/
MachineObject* get_my_machine(std::string dev_id);
std::map<std::string, MachineObject*> get_my_machine_list();
std::map<std::string, MachineObject*> get_my_cloud_machine_list();
std::map<std::string, MachineObject*> get_my_machine_list(const std::string& agent_id = "");
std::map<std::string, MachineObject*> get_my_cloud_machine_list(const std::string& agent_id = "");
void modify_device_name(std::string dev_id, std::string dev_name, const std::string& provider);
// id of the currently live IPrinterAgent (IPrinterAgent::get_agent_info().id), or empty if
// m_agent has no printer agent set yet. Pass to get_my_machine_list()/get_my_cloud_machine_list()
// to scope results to the active agent.
std::string get_current_printer_agent_id() const;
/* create machine or update machine properties */
void on_machine_alive(std::string json_str);
int query_bind_status(std::string& msg, const std::string& provider);

View File

@@ -3,6 +3,7 @@
#include "libslic3r/Time.hpp"
#include "libslic3r/Thread.hpp"
#include "slic3r/Utils/NetworkAgent.hpp"
#include "slic3r/Utils/NetworkAgentFactory.hpp"
#include "GuiColor.hpp"
#include "GUI_App.hpp"
@@ -458,11 +459,41 @@ void MachineObject::set_access_code(std::string code, bool only_refresh)
if (only_refresh) {
AppConfig* config = GUI::wxGetApp().app_config;
if (config) {
if (!code.empty()) {
GUI::wxGetApp().app_config->set_str("access_code", get_dev_id(), code);
DeviceManager::update_local_machine(*this);
if (is_lan_mode_printer()) {
// why: LAN codes are scoped via BBLocalMachine::access_code, keyed by dev_id and
// scoped by that record's own printer_agent_id field - see the matching comment
// on get_access_code_with_legacy_fallback() in DevManager.cpp - so binding this
// device under one printer agent doesn't silently read as already-bound under a
// different, independent one. Cloud devices (the else branch below) aren't
// scoped this way: they're never recalled from a stale local cache across a
// session boundary, since parse_user_print_info() always overwrites their code
// fresh from the cloud API's current response, so there's no cross-agent leakage
// risk to guard against there.
if (!code.empty()) {
DeviceManager::update_local_machine(*this);
} else {
// Only patch an existing record's code - don't persist a brand-new
// never-bound entry just because set_access_code("") was called on it.
const auto& machines = config->get_local_machines();
auto it = machines.find(get_dev_id());
if (it != machines.end()) {
BBLocalMachine local_machine = it->second;
local_machine.access_code = "";
config->update_local_machine(local_machine);
}
// Also clear the pre-scoping flat legacy key when unbinding under BBL, so an
// old BBL-era code can't silently "re-bind" this device again via
// get_access_code_with_legacy_fallback()'s legacy fallback.
if (printer_agent_id == BBL_PRINTER_AGENT_ID || printer_agent_id.empty()) {
config->erase("access_code", get_dev_id());
config->erase("user_access_code", get_dev_id());
}
}
} else {
GUI::wxGetApp().app_config->erase("access_code", get_dev_id());
if (!code.empty())
config->set_str("access_code", get_dev_id(), code);
else
config->erase("access_code", get_dev_id());
}
}
}

View File

@@ -229,6 +229,16 @@ public:
//PRINTER_TYPE printer_type = PRINTER_3DPrinter_UKNOWN;
std::string printer_type; /* model_id */
// id of the IPrinterAgent that was used to discover or bind this device (IPrinterAgent::get_agent_info().id,
// e.g. "bbl"), stamped at creation time — not derived from get_agent(), since m_agent is a single
// process-wide NetworkAgent shared by every MachineObject and gets repointed on agent swap
// (see DeviceManager::set_agent()), so it can't tell which agent originally found this device.
// We persist this as well so that when the printer agent is swapped, we don't show unrelated devices,
// e.g. if the current printer agent is elegoo, we shouldn't show printers connected by BBL printer agent
// under local machines.
std::string printer_agent_id;
std::string get_show_printer_type() const;
PrinterSeries get_printer_series() const;
PrinterArch get_printer_arch() const;

View File

@@ -3937,7 +3937,13 @@ void GUI_App::set_live_printer_agent(std::shared_ptr<IPrinterAgent> agent)
m_agent->set_user_selected_machine("");
// note: belt-and-suspenders (precedent: DeviceManagerRefresher::on_timer)
dev->OnSelectedMachineLost(); // why: clear stale sidebar sync-status / AMS
dev->clear_other_devices(); // why: drop stale LAN discoveries; keep My Devices
// why: drop stale LAN discoveries; keep My Devices, but only those belonging to the
// agent we're about to swap to, so a device stamped by the outgoing agent doesn't
// linger hidden - the new agent's start_discovery re-inserts and re-stamps it fresh.
// agent is null when clearing the live agent entirely (e.g. plugin unload); there's no
// target to filter against then, so fall back to the original "keep all My Devices"
// behavior rather than guessing.
dev->clear_other_devices(agent ? agent->get_agent_info().id : std::string());
}
m_agent->set_printer_agent(agent);

View File

@@ -3913,7 +3913,8 @@ _collect_sorted_machines(Slic3r::DeviceManager* dev_manager,
};
// collect from user machine list
const auto& user_machine_list = dev_manager->get_my_machine_list();// user machine list
const std::string agent_id = wxGetApp().preset_bundle->printers.get_edited_preset().config.opt_string("printer_agent");
const auto& user_machine_list = dev_manager->get_my_machine_list(agent_id);// user machine list
for (const auto& elem : user_machine_list)
{
MachineObject* mobj = elem.second;

View File

@@ -501,6 +501,7 @@ void SelectMachinePopup::update_other_devices()
DeviceManager* dev = wxGetApp().getDeviceManager();
if (!dev) return;
m_free_machine_list = dev->get_local_machinelist();
const std::string current_agent_id = dev->get_current_printer_agent_id();
BOOST_LOG_TRIVIAL(trace) << "SelectMachinePopup update_other_devices start";
this->Freeze();
@@ -512,6 +513,10 @@ void SelectMachinePopup::update_other_devices()
/* do not show printer bind state is empty */
if (!mobj->is_avaliable()) continue;
/* do not show devices discovered/bound by a different printer agent */
if (mobj->printer_agent_id != current_agent_id)
continue;
if (!wxGetApp().is_user_login(wxGetApp().get_printer_cloud_provider()) && !mobj->is_lan_mode_printer())
continue;
@@ -634,7 +639,7 @@ void SelectMachinePopup::update_user_devices()
}
m_bind_machine_list.clear();
m_bind_machine_list = dev->get_my_machine_list();
m_bind_machine_list = dev->get_my_machine_list(dev->get_current_printer_agent_id());
//sort list
std::vector<std::pair<std::string, MachineObject*>> user_machine_list;