fix UI block issues

This commit is contained in:
SoftFever
2026-02-02 18:12:19 +08:00
parent d817a4d5d4
commit 431943408b
7 changed files with 124 additions and 65 deletions

View File

@@ -858,7 +858,10 @@ namespace Slic3r
{
if (MachineObject* obj_ = get_selected_machine()) {
GUI::wxGetApp().sidebar().update_sync_status(obj_);
GUI::wxGetApp().sidebar().load_ams_list(obj_);
if(m_agent->get_filament_sync_mode() == FilamentSyncMode::subscription)
{
GUI::wxGetApp().sidebar().load_ams_list(obj_);
}
};
}

View File

@@ -2591,6 +2591,11 @@ int GUI_App::OnExit()
m_user_manager = nullptr;
}
// Clear the printer agent cache before destroying the NetworkAgent.
// This disconnects all cached agents and releases their shared_ptrs,
// ensuring clean thread shutdown before the agent is deleted.
NetworkAgentFactory::clear_printer_agent_cache();
if (m_agent) {
// BBS avoid a crash on mac platform
#ifdef __WINDOWS__
@@ -3515,10 +3520,10 @@ void GUI_App::switch_printer_agent()
sidebar().update_all_preset_comboboxes();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": printer agent switched to " << effective_agent_id;
}
// Auto-switch MachineObject (runs even if agent IDs match)
select_machine(effective_agent_id);
// Auto-switch MachineObject
select_machine(effective_agent_id);
}
}
void GUI_App::select_machine(const std::string& agent_id)

View File

@@ -98,7 +98,15 @@ MoonrakerPrinterAgent::MoonrakerPrinterAgent(std::string log_dir) : m_cloud_agen
MoonrakerPrinterAgent::~MoonrakerPrinterAgent()
{
disconnect_printer(); // This will handle thread cleanup
{
std::lock_guard<std::recursive_mutex> lock(connect_mutex);
device_info = MoonrakerDeviceInfo{};
++connect_generation;
}
if (connect_thread.joinable()) {
connect_thread.join();
}
stop_status_stream();
}
AgentInfo MoonrakerPrinterAgent::get_agent_info_static()
@@ -133,31 +141,20 @@ int MoonrakerPrinterAgent::connect_printer(std::string dev_id, std::string dev_i
return BAMBU_NETWORK_ERR_INVALID_HANDLE;
}
// Check if connection already in progress
std::string base_url;
std::string api_key;
uint64_t gen;
{
std::lock_guard<std::recursive_mutex> lock(connect_mutex);
init_device_info(dev_id, dev_ip, username, password, use_ssl);
if (connect_in_progress.load()) {
// Don't reject - wait for previous connection to complete
// This can happen if MonitorPanel triggers connect while previous connect is still running
} else {
connect_in_progress.store(true);
connect_stop_requested.store(false);
gen = ++connect_generation;
base_url = device_info.base_url;
api_key = device_info.api_key;
if (connect_thread.joinable()) {
connect_thread.detach();
}
}
// Wait for previous connection thread to finish
if (connect_thread.joinable()) {
connect_thread.join();
}
// Now we can start a new connection
{
std::lock_guard<std::recursive_mutex> lock(connect_mutex);
connect_in_progress.store(true);
connect_stop_requested.store(false);
}
// Stop existing status stream and clear state
stop_status_stream();
{
@@ -168,30 +165,26 @@ int MoonrakerPrinterAgent::connect_printer(std::string dev_id, std::string dev_i
ws_last_dispatch_ms.store(0);
last_print_state.clear();
// Launch connection in background thread
connect_thread = std::thread([this, dev_id]() { perform_connection_async(dev_id, device_info.base_url, device_info.api_key); });
// Launch connection in background thread (capture by value to avoid data races)
{
std::lock_guard<std::recursive_mutex> lock(connect_mutex);
connect_thread = std::thread([this, dev_id, base_url, api_key, gen]() { perform_connection_async(dev_id, base_url, api_key, gen); });
}
return BAMBU_NETWORK_SUCCESS;
}
int MoonrakerPrinterAgent::disconnect_printer()
{
// Stop connection thread if running
{
std::lock_guard<std::recursive_mutex> lock(connect_mutex);
device_info = MoonrakerDeviceInfo{};
if (connect_in_progress.load()) {
connect_stop_requested.store(true);
// Wake up any sleeping
connect_cv.notify_all();
++connect_generation; // Invalidate any in-flight connection
if (connect_thread.joinable()) {
connect_thread.detach();
}
}
// Wait for connection thread to finish (with timeout)
if (connect_thread.joinable()) {
connect_thread.join();
}
stop_status_stream();
return BAMBU_NETWORK_SUCCESS;
}
@@ -1842,14 +1835,12 @@ int MoonrakerPrinterAgent::pause_print(const std::string& dev_id)
int MoonrakerPrinterAgent::resume_print(const std::string& dev_id)
{
std::string gcode = "RESUME";
return send_gcode(dev_id, gcode) ? BAMBU_NETWORK_SUCCESS : BAMBU_NETWORK_ERR_SEND_MSG_FAILED;
return send_gcode(dev_id, "RESUME") ? BAMBU_NETWORK_SUCCESS : BAMBU_NETWORK_ERR_SEND_MSG_FAILED;
}
int MoonrakerPrinterAgent::cancel_print(const std::string& dev_id)
{
std::string gcode = "CANCEL_PRINT";
return send_gcode(dev_id, gcode) ? BAMBU_NETWORK_SUCCESS : BAMBU_NETWORK_ERR_SEND_MSG_FAILED;
return send_gcode(dev_id, "CANCEL_PRINT") ? BAMBU_NETWORK_SUCCESS : BAMBU_NETWORK_ERR_SEND_MSG_FAILED;
}
bool MoonrakerPrinterAgent::send_jsonrpc_command(const std::string& base_url,
@@ -1889,20 +1880,38 @@ bool MoonrakerPrinterAgent::send_jsonrpc_command(const std::string& base_url,
return success;
}
void MoonrakerPrinterAgent::perform_connection_async(const std::string& dev_id, const std::string& base_url, const std::string& api_key)
void MoonrakerPrinterAgent::perform_connection_async(const std::string& dev_id, const std::string& base_url, const std::string& api_key, uint64_t generation)
{
auto is_stale = [&]() { return generation != connect_generation.load(); };
int result = BAMBU_NETWORK_ERR_CONNECTION_TO_PRINTER_FAILED;
std::string error_msg;
// Early exit if a newer connection was started before we begin
if (is_stale()) {
return;
}
try {
if (!fetch_device_info(base_url, api_key, device_info, error_msg)) {
MoonrakerDeviceInfo fetched_info;
if (!fetch_device_info(base_url, api_key, fetched_info, error_msg)) {
BOOST_LOG_TRIVIAL(error) << "MoonrakerPrinterAgent: Failed to fetch server info: " << error_msg;
// Orca todo: revist here, for now don't send error, this is set current MachineObject to null
// dispatch_local_connect(ConnectStatusFailed, dev_id, "server_info_failed");
finish_connection();
return;
}
// Commit fetched info back to device_info under lock, only if still current
{
std::lock_guard<std::recursive_mutex> lock(connect_mutex);
if (is_stale()) {
return;
}
device_info.dev_name = fetched_info.dev_name;
device_info.version = fetched_info.version;
device_info.klippy_state = fetched_info.klippy_state;
}
// Orca todo: disable websocket for now, as we don't use MonitorPanel for Moonraker printers yet
#if 0
// Query initial status
@@ -1929,23 +1938,15 @@ void MoonrakerPrinterAgent::perform_connection_async(const std::string& dev_id,
result = BAMBU_NETWORK_ERR_CONNECTION_TO_PRINTER_FAILED;
}
// Dispatch final result to UI
if (result == BAMBU_NETWORK_SUCCESS) {
// Only dispatch if this connection is still the current one
if (result == BAMBU_NETWORK_SUCCESS && !is_stale()) {
dispatch_local_connect(ConnectStatusOk, dev_id, "0");
dispatch_printer_connected(dev_id);
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent: connect_printer completed - dev_id=" << dev_id;
} else if (result != BAMBU_NETWORK_ERR_CANCELED) {
} else if (result != BAMBU_NETWORK_SUCCESS && result != BAMBU_NETWORK_ERR_CANCELED) {
// Orca todo: revist here, for now don't send error, this is set current MachineObject to null
// dispatch_local_connect(ConnectStatusFailed, dev_id, error_msg);
}
finish_connection();
}
void MoonrakerPrinterAgent::finish_connection()
{
std::lock_guard<std::recursive_mutex> lock(connect_mutex);
connect_in_progress.store(false);
}
bool MoonrakerPrinterAgent::is_numeric(const std::string& value)

View File

@@ -4,7 +4,6 @@
#include "IPrinterAgent.hpp"
#include "ICloudServiceAgent.hpp"
#include <condition_variable>
#include <memory>
#include <mutex>
#include <set>
@@ -158,8 +157,8 @@ private:
// Connection thread management
void perform_connection_async(const std::string& dev_id,
const std::string& base_url,
const std::string& api_key);
void finish_connection();
const std::string& api_key,
uint64_t generation);
std::string ssdp_announced_host;
std::string ssdp_announced_id;
@@ -194,11 +193,9 @@ private:
std::string last_print_state; // Track state for immediate dispatch on change
// Connection thread management
std::atomic<bool> connect_in_progress{false};
std::atomic<bool> connect_stop_requested{false};
std::thread connect_thread;
std::recursive_mutex connect_mutex;
std::condition_variable connect_cv;
std::atomic<uint64_t> connect_generation{0};
std::thread connect_thread;
std::recursive_mutex connect_mutex;
};
} // namespace Slic3r

View File

@@ -139,6 +139,7 @@ void NetworkAgent::set_printer_agent(std::shared_ptr<IPrinterAgent> printer_agen
// Local copies to allow safe access after releasing the lock.
// This pattern ensures the objects stay alive (via shared_ptr refcount) even if
// another thread modifies m_printer_agent or m_printer_callbacks after we unlock.
std::shared_ptr<IPrinterAgent> old_printer_agent;
std::shared_ptr<IPrinterAgent> new_printer_agent;
PrinterCallbacks callbacks;
@@ -152,6 +153,8 @@ void NetworkAgent::set_printer_agent(std::shared_ptr<IPrinterAgent> printer_agen
// Disconnect all callbacks from the old agent
apply_printer_callbacks(m_printer_agent, callbacks);
// Capture the old agent before overwriting so we can disconnect it outside the lock
old_printer_agent = m_printer_agent;
// Take ownership of the incoming agent and update the agent ID
m_printer_agent = std::move(printer_agent);
m_printer_agent_id = m_printer_agent->get_agent_info().id;
@@ -164,6 +167,11 @@ void NetworkAgent::set_printer_agent(std::shared_ptr<IPrinterAgent> printer_agen
}
// Lock released here - m_agent_mutex is now free for other threads
// Disconnect the old agent's connections/threads. The cache keeps it alive,
// but we release its network resources while it's not the active agent.
if (old_printer_agent && old_printer_agent != new_printer_agent)
old_printer_agent->disconnect_printer();
// Apply callbacks OUTSIDE the lock to avoid deadlock risk and minimize
// critical section duration. The local shared_ptr copy ensures the agent
// cannot be destroyed while we're using it.

View File

@@ -21,6 +21,12 @@ std::map<std::string, PrinterAgentInfo>& get_printer_agents()
return agents;
}
std::map<std::string, std::shared_ptr<IPrinterAgent>>& get_printer_agent_cache()
{
static std::map<std::string, std::shared_ptr<IPrinterAgent>> cache;
return cache;
}
// Helper to register a printer agent type with the standard factory pattern.
// AgentTypes that take a log_dir constructor arg use the default; BBLPrinterAgent
// (no log_dir) is registered separately.
@@ -82,15 +88,44 @@ std::shared_ptr<IPrinterAgent> NetworkAgentFactory::create_printer_agent_by_id(c
const std::string& log_dir)
{
std::lock_guard<std::mutex> lock(s_registry_mutex);
auto& agents = get_printer_agents();
auto it = agents.find(id);
// Check cache first
auto& cache = get_printer_agent_cache();
auto cache_it = cache.find(id);
if (cache_it != cache.end()) {
BOOST_LOG_TRIVIAL(info) << "Reusing cached printer agent: " << id;
if (cloud_agent)
cache_it->second->set_cloud_agent(cloud_agent);
return cache_it->second;
}
// Not cached — create via factory
auto& agents = get_printer_agents();
auto it = agents.find(id);
if (it == agents.end()) {
BOOST_LOG_TRIVIAL(warning) << "Unknown printer agent ID: " << id;
return nullptr;
}
return it->second.factory(cloud_agent, log_dir);
auto agent = it->second.factory(cloud_agent, log_dir);
if (agent) {
BOOST_LOG_TRIVIAL(info) << "Created and cached printer agent: " << id;
cache[id] = agent;
}
return agent;
}
void NetworkAgentFactory::clear_printer_agent_cache()
{
std::lock_guard<std::mutex> lock(s_registry_mutex);
auto& cache = get_printer_agent_cache();
for (auto& pair : cache) {
if (pair.second)
pair.second->disconnect_printer();
}
cache.clear();
BOOST_LOG_TRIVIAL(info) << "Printer agent cache cleared";
}
void NetworkAgentFactory::register_all_agents()

View File

@@ -106,6 +106,9 @@ public:
/**
* Create a printer agent by ID (using registry)
*
* Returns a cached instance if one exists for the given ID, otherwise
* creates a new agent via the registered factory and caches it.
*
* @param id Agent ID to create
* @param cloud_agent Cloud agent for token access
* @param log_dir Directory for log files
@@ -115,6 +118,13 @@ public:
std::shared_ptr<ICloudServiceAgent> cloud_agent,
const std::string& log_dir);
/**
* Clear the printer agent cache.
* Calls disconnect_printer() on each cached agent and releases all shared_ptrs.
* Should be called during application shutdown before destroying the NetworkAgent.
*/
static void clear_printer_agent_cache();
// ========================================================================
// Cloud Agent Factory
// ========================================================================