This commit is contained in:
SoftFever
2026-01-27 00:42:23 +08:00
parent 997b2154c8
commit 722d5f8aa7
8 changed files with 182 additions and 325 deletions

View File

@@ -3208,7 +3208,9 @@ std::map<int, DynamicPrintConfig> Sidebar::build_filament_ams_list(MachineObject
// For pull-mode agents (e.g., HTTP REST API), refresh DevFilaSystem first // For pull-mode agents (e.g., HTTP REST API), refresh DevFilaSystem first
auto* agent = wxGetApp().getDeviceManager()->get_agent(); auto* agent = wxGetApp().getDeviceManager()->get_agent();
if (agent && agent->get_filament_sync_mode() == FilamentSyncMode::pull) { if (agent && agent->get_filament_sync_mode() == FilamentSyncMode::pull) {
agent->fetch_filament_info(obj->get_dev_id()); if (!agent->fetch_filament_info(obj->get_dev_id())) {
return filament_ams_list;
}
} }
auto build_tray_config = [](DevAmsTray const &tray, std::string const &name, std::string ams_id, std::string slot_id) { auto build_tray_config = [](DevAmsTray const &tray, std::string const &name, std::string ams_id, std::string slot_id) {
@@ -16631,8 +16633,15 @@ void Plater::pop_warning_and_go_to_device_page(wxString printer_name, PrinterWar
{ {
printer_name.Replace("Bambu Lab", "", false); printer_name.Replace("Bambu Lab", "", false);
wxString content; wxString content;
bool device_page = (wxGetApp().mainframe == nullptr) && (wxGetApp().mainframe->m_monitor->IsShown());
if (type == PrinterWarningType::NOT_CONNECTED) { if (type == PrinterWarningType::NOT_CONNECTED) {
content = wxString::Format(_L("Printer not connected. Please go to the device page to connect %s before syncing."), printer_name); if (device_page) {
content = wxString::Format(_L("Printer not connected. Please go to the device page to connect %s before syncing."),
printer_name);
} else {
content = wxString::Format(
_L("OrcaSlicer can't connect to %s. Please check if the printer is powered on and connected to the network."), printer_name);
}
} else if (type == PrinterWarningType::INCONSISTENT) { } else if (type == PrinterWarningType::INCONSISTENT) {
content = wxString::Format(_L("The currently connected printer on the device page is not %s. Please switch to %s before syncing."), printer_name, printer_name); content = wxString::Format(_L("The currently connected printer on the device page is not %s. Please switch to %s before syncing."), printer_name, printer_name);
} else if (type == PrinterWarningType::UNINSTALL_FILAMENT) { } else if (type == PrinterWarningType::UNINSTALL_FILAMENT) {

View File

@@ -252,7 +252,7 @@ public:
* Should only be called when get_filament_sync_mode() returns FilamentSyncMode::pull. * Should only be called when get_filament_sync_mode() returns FilamentSyncMode::pull.
* Populates the MachineObject's DevFilaSystem with fetched filament data. * Populates the MachineObject's DevFilaSystem with fetched filament data.
*/ */
virtual void fetch_filament_info(std::string dev_id) {} virtual bool fetch_filament_info(std::string dev_id) { return false; }
}; };
} // namespace Slic3r } // namespace Slic3r

View File

@@ -3,8 +3,6 @@
#include "libslic3r/Preset.hpp" #include "libslic3r/Preset.hpp"
#include "libslic3r/PresetBundle.hpp" #include "libslic3r/PresetBundle.hpp"
#include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/DeviceManager.hpp"
#include "nlohmann/json.hpp" #include "nlohmann/json.hpp"
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/asio/connect.hpp> #include <boost/asio/connect.hpp>
@@ -17,7 +15,6 @@
#include <chrono> #include <chrono>
#include <cstdint> #include <cstdint>
#include <cctype> #include <cctype>
#include <sstream>
#include <thread> #include <thread>
namespace { namespace {
@@ -28,7 +25,6 @@ namespace websocket = beast::websocket;
namespace net = boost::asio; namespace net = boost::asio;
using tcp = net::ip::tcp; using tcp = net::ip::tcp;
struct WsEndpoint struct WsEndpoint
{ {
std::string host; std::string host;
@@ -94,10 +90,7 @@ namespace Slic3r {
const std::string MoonrakerPrinterAgent_VERSION = "1.0.0"; const std::string MoonrakerPrinterAgent_VERSION = "1.0.0";
MoonrakerPrinterAgent::MoonrakerPrinterAgent(std::string log_dir) : m_cloud_agent(nullptr) MoonrakerPrinterAgent::MoonrakerPrinterAgent(std::string log_dir) : m_cloud_agent(nullptr) { (void) log_dir; }
{
(void) log_dir;
}
MoonrakerPrinterAgent::~MoonrakerPrinterAgent() MoonrakerPrinterAgent::~MoonrakerPrinterAgent()
{ {
@@ -106,7 +99,10 @@ MoonrakerPrinterAgent::~MoonrakerPrinterAgent()
AgentInfo MoonrakerPrinterAgent::get_agent_info_static() AgentInfo MoonrakerPrinterAgent::get_agent_info_static()
{ {
return AgentInfo{.id = "moonraker", .name = "Moonraker Printer Agent", .version = MoonrakerPrinterAgent_VERSION, .description = "Klipper/Moonraker printer agent"}; return AgentInfo{.id = "moonraker",
.name = "Moonraker Printer Agent",
.version = MoonrakerPrinterAgent_VERSION,
.description = "Klipper/Moonraker printer agent"};
} }
void MoonrakerPrinterAgent::set_cloud_agent(std::shared_ptr<ICloudServiceAgent> cloud) void MoonrakerPrinterAgent::set_cloud_agent(std::shared_ptr<ICloudServiceAgent> cloud)
@@ -131,23 +127,15 @@ int MoonrakerPrinterAgent::send_message_to_printer(std::string dev_id, std::stri
int MoonrakerPrinterAgent::connect_printer(std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl) int MoonrakerPrinterAgent::connect_printer(std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl)
{ {
if (dev_id.empty() || dev_ip.empty()) { if (dev_id.empty() || dev_ip.empty()) {
BOOST_LOG_TRIVIAL(error) << "MoonrakerPrinterAgent: connect_printer missing dev_id or dev_ip"; BOOST_LOG_TRIVIAL(error) << "MoonrakerPrinterAgent: connect_printer missing dev_id or dev_ip";
return BAMBU_NETWORK_ERR_INVALID_HANDLE; return BAMBU_NETWORK_ERR_INVALID_HANDLE;
} }
init_device_info(dev_id, dev_ip, username, password, use_ssl);
if (device_info.dev_id != dev_id) {
BOOST_LOG_TRIVIAL(error) << "MoonrakerPrinterAgent: connect_printer dev_id mismatch: expected " << device_info.dev_id << ", got " << dev_id;
dispatch_local_connect(ConnectStatusFailed, dev_id, "dev_id_mismatch");
return BAMBU_NETWORK_ERR_INVALID_HANDLE;
}
// Check if connection already in progress // Check if connection already in progress
{ {
std::lock_guard<std::recursive_mutex> lock(connect_mutex); 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()) { if (connect_in_progress.load()) {
// Don't reject - wait for previous connection to complete // Don't reject - wait for previous connection to complete
// This can happen if MonitorPanel triggers connect while previous connect is still running // This can happen if MonitorPanel triggers connect while previous connect is still running
@@ -180,9 +168,7 @@ int MoonrakerPrinterAgent::connect_printer(std::string dev_id, std::string dev_i
last_print_state.clear(); last_print_state.clear();
// Launch connection in background thread // Launch connection in background thread
connect_thread = std::thread([this, dev_id]() { connect_thread = std::thread([this, dev_id]() { perform_connection_async(dev_id, device_info.base_url, device_info.api_key); });
perform_connection_async(dev_id, device_info.base_url, device_info.api_key);
});
return BAMBU_NETWORK_SUCCESS; return BAMBU_NETWORK_SUCCESS;
} }
@@ -192,6 +178,7 @@ int MoonrakerPrinterAgent::disconnect_printer()
// Stop connection thread if running // Stop connection thread if running
{ {
std::lock_guard<std::recursive_mutex> lock(connect_mutex); std::lock_guard<std::recursive_mutex> lock(connect_mutex);
device_info = MoonrakerDeviceInfo{};
if (connect_in_progress.load()) { if (connect_in_progress.load()) {
connect_stop_requested.store(true); connect_stop_requested.store(true);
// Wake up any sleeping // Wake up any sleeping
@@ -208,10 +195,7 @@ int MoonrakerPrinterAgent::disconnect_printer()
return BAMBU_NETWORK_SUCCESS; return BAMBU_NETWORK_SUCCESS;
} }
int MoonrakerPrinterAgent::check_cert() int MoonrakerPrinterAgent::check_cert() { return BAMBU_NETWORK_SUCCESS; }
{
return BAMBU_NETWORK_SUCCESS;
}
void MoonrakerPrinterAgent::install_device_cert(std::string dev_id, bool lan_only) void MoonrakerPrinterAgent::install_device_cert(std::string dev_id, bool lan_only)
{ {
@@ -238,17 +222,6 @@ int MoonrakerPrinterAgent::bind_detect(std::string dev_ip, std::string sec_link,
{ {
(void) sec_link; (void) sec_link;
std::string base_url = normalize_base_url(dev_ip, "");
if (base_url.empty()) {
return BAMBU_NETWORK_ERR_INVALID_HANDLE;
}
std::string error;
if (!fetch_device_info(base_url, device_info.api_key, device_info, error)) {
BOOST_LOG_TRIVIAL(error) << "MoonrakerPrinterAgent: bind_detect failed: " << error;
return BAMBU_NETWORK_ERR_CONNECTION_TO_PRINTER_FAILED;
}
detect.dev_id = device_info.dev_id.empty() ? dev_ip : device_info.dev_id; detect.dev_id = device_info.dev_id.empty() ? dev_ip : device_info.dev_id;
detect.model_id = device_info.model_id.empty() ? device_info.model_name : device_info.model_id; detect.model_id = device_info.model_id.empty() ? device_info.model_name : device_info.model_id;
// Prefer fetched hostname, then preset model name, then generic fallback // Prefer fetched hostname, then preset model name, then generic fallback
@@ -315,7 +288,10 @@ int MoonrakerPrinterAgent::start_print(PrintParams params, OnUpdateStatusFn upda
return BAMBU_NETWORK_SUCCESS; return BAMBU_NETWORK_SUCCESS;
} }
int MoonrakerPrinterAgent::start_local_print_with_record(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) int MoonrakerPrinterAgent::start_local_print_with_record(PrintParams params,
OnUpdateStatusFn update_fn,
WasCancelledFn cancel_fn,
OnWaitFn wait_fn)
{ {
(void) params; (void) params;
(void) update_fn; (void) update_fn;
@@ -324,11 +300,15 @@ int MoonrakerPrinterAgent::start_local_print_with_record(PrintParams params, OnU
return BAMBU_NETWORK_SUCCESS; return BAMBU_NETWORK_SUCCESS;
} }
int MoonrakerPrinterAgent::start_send_gcode_to_sdcard(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) int MoonrakerPrinterAgent::start_send_gcode_to_sdcard(PrintParams params,
OnUpdateStatusFn update_fn,
WasCancelledFn cancel_fn,
OnWaitFn wait_fn)
{ {
(void) wait_fn; (void) wait_fn;
if (update_fn) update_fn(PrintingStageCreate, 0, "Preparing..."); if (update_fn)
update_fn(PrintingStageCreate, 0, "Preparing...");
std::string filename = params.filename; std::string filename = params.filename;
if (filename.empty()) { if (filename.empty()) {
@@ -346,13 +326,15 @@ int MoonrakerPrinterAgent::start_send_gcode_to_sdcard(PrintParams params, OnUpda
return BAMBU_NETWORK_ERR_PRINT_SG_UPLOAD_FTP_FAILED; return BAMBU_NETWORK_ERR_PRINT_SG_UPLOAD_FTP_FAILED;
} }
if (update_fn) update_fn(PrintingStageFinished, 100, "File uploaded"); if (update_fn)
update_fn(PrintingStageFinished, 100, "File uploaded");
return BAMBU_NETWORK_SUCCESS; return BAMBU_NETWORK_SUCCESS;
} }
int MoonrakerPrinterAgent::start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) int MoonrakerPrinterAgent::start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn)
{ {
if (update_fn) update_fn(PrintingStageCreate, 0, "Preparing..."); if (update_fn)
update_fn(PrintingStageCreate, 0, "Preparing...");
// Check cancellation // Check cancellation
if (cancel_fn && cancel_fn()) { if (cancel_fn && cancel_fn()) {
@@ -382,7 +364,8 @@ int MoonrakerPrinterAgent::start_local_print(PrintParams params, OnUpdateStatusF
upload_filename = sanitize_filename(upload_filename); upload_filename = sanitize_filename(upload_filename);
// Upload file // Upload file
if (update_fn) update_fn(PrintingStageUpload, 0, "Uploading G-code..."); if (update_fn)
update_fn(PrintingStageUpload, 0, "Uploading G-code...");
if (!upload_gcode(gcode_path, upload_filename, device_info.base_url, device_info.api_key, update_fn, cancel_fn)) { if (!upload_gcode(gcode_path, upload_filename, device_info.base_url, device_info.api_key, update_fn, cancel_fn)) {
return BAMBU_NETWORK_ERR_PRINT_LP_UPLOAD_FTP_FAILED; return BAMBU_NETWORK_ERR_PRINT_LP_UPLOAD_FTP_FAILED;
} }
@@ -393,13 +376,15 @@ int MoonrakerPrinterAgent::start_local_print(PrintParams params, OnUpdateStatusF
} }
// Start print via gcode script (simpler than JSON-RPC) // Start print via gcode script (simpler than JSON-RPC)
if (update_fn) update_fn(PrintingStageSending, 0, "Starting print..."); if (update_fn)
update_fn(PrintingStageSending, 0, "Starting print...");
std::string gcode = "SDCARD_PRINT_FILE FILENAME=" + upload_filename; std::string gcode = "SDCARD_PRINT_FILE FILENAME=" + upload_filename;
if (!send_gcode(device_info.dev_id, gcode)) { if (!send_gcode(device_info.dev_id, gcode)) {
return BAMBU_NETWORK_ERR_PRINT_LP_PUBLISH_MSG_FAILED; return BAMBU_NETWORK_ERR_PRINT_LP_PUBLISH_MSG_FAILED;
} }
if (update_fn) update_fn(PrintingStageFinished, 100, "Print started"); if (update_fn)
update_fn(PrintingStageFinished, 100, "Print started");
return BAMBU_NETWORK_SUCCESS; return BAMBU_NETWORK_SUCCESS;
} }
@@ -469,10 +454,7 @@ int MoonrakerPrinterAgent::set_queue_on_main_fn(QueueOnMainFn fn)
return BAMBU_NETWORK_SUCCESS; return BAMBU_NETWORK_SUCCESS;
} }
void MoonrakerPrinterAgent::fetch_filament_info(std::string dev_id) bool MoonrakerPrinterAgent::fetch_filament_info(std::string dev_id) { return false; }
{
(void) dev_id;
}
int MoonrakerPrinterAgent::handle_request(const std::string& dev_id, const std::string& json_str) int MoonrakerPrinterAgent::handle_request(const std::string& dev_id, const std::string& json_str)
{ {
@@ -659,104 +641,8 @@ bool MoonrakerPrinterAgent::fetch_device_info(const std::string& base_url,
nlohmann::json result = json.contains("result") ? json["result"] : json; nlohmann::json result = json.contains("result") ? json["result"] : json;
info.dev_name = result.value("machine_name", result.value("hostname", "")); info.dev_name = result.value("machine_name", result.value("hostname", ""));
info.version = result.value("software_version", result.value("firmware_version", "")); info.version = result.value("moonraker_version", "");
info.klippy_state = result.value("klippy_state", "");
return true;
}
bool MoonrakerPrinterAgent::fetch_server_info(const std::string& base_url,
const std::string& api_key,
std::string& version,
std::string& error) const
{
std::string response_body;
bool success = false;
std::string http_error;
auto http = Http::get(join_url(base_url, "/server/info"));
if (!api_key.empty()) {
http.header("X-Api-Key", api_key);
}
http.timeout_connect(5)
.timeout_max(10)
.on_complete([&](std::string body, unsigned status) {
if (status == 200) {
response_body = body;
success = true;
} else {
http_error = "HTTP error: " + std::to_string(status);
}
})
.on_error([&](std::string body, std::string err, unsigned status) {
http_error = err;
if (status > 0) {
http_error += " (HTTP " + std::to_string(status) + ")";
}
})
.perform_sync();
if (!success) {
error = http_error.empty() ? "Connection failed" : http_error;
return false;
}
auto json = nlohmann::json::parse(response_body, nullptr, false, true);
if (json.is_discarded()) {
error = "Invalid JSON response";
return false;
}
nlohmann::json result = json.contains("result") ? json["result"] : json;
if (result.contains("moonraker_version") && result["moonraker_version"].is_string()) {
version = result["moonraker_version"].get<std::string>();
} else if (result.contains("version") && result["version"].is_string()) {
version = result["version"].get<std::string>();
}
return true;
}
bool MoonrakerPrinterAgent::fetch_server_info_json(const std::string& base_url,
const std::string& api_key,
nlohmann::json& info,
std::string& error) const
{
std::string response_body;
bool success = false;
std::string http_error;
auto http = Http::get(join_url(base_url, "/server/info"));
if (!api_key.empty()) {
http.header("X-Api-Key", api_key);
}
http.timeout_connect(5)
.timeout_max(10)
.on_complete([&](std::string body, unsigned status) {
if (status == 200) {
response_body = body;
success = true;
} else {
http_error = "HTTP error: " + std::to_string(status);
}
})
.on_error([&](std::string body, std::string err, unsigned status) {
http_error = err;
if (status > 0) {
http_error += " (HTTP " + std::to_string(status) + ")";
}
})
.perform_sync();
if (!success) {
error = http_error.empty() ? "Connection failed" : http_error;
return false;
}
info = nlohmann::json::parse(response_body, nullptr, false, true);
if (info.is_discarded()) {
error = "Invalid JSON response";
return false;
}
return true; return true;
} }
@@ -916,15 +802,6 @@ bool MoonrakerPrinterAgent::fetch_object_list(const std::string& base_url,
int MoonrakerPrinterAgent::send_version_info(const std::string& dev_id) int MoonrakerPrinterAgent::send_version_info(const std::string& dev_id)
{ {
std::string version;
std::string error;
if (!fetch_server_info(device_info.base_url, device_info.api_key, version, error)) {
BOOST_LOG_TRIVIAL(warning) << "MoonrakerPrinterAgent: Failed to fetch server info: " << error;
}
if (version.empty()) {
version = "moonraker";
}
nlohmann::json payload; nlohmann::json payload;
payload["info"]["command"] = "get_version"; payload["info"]["command"] = "get_version";
payload["info"]["result"] = "success"; payload["info"]["result"] = "success";
@@ -932,7 +809,7 @@ int MoonrakerPrinterAgent::send_version_info(const std::string& dev_id)
nlohmann::json module; nlohmann::json module;
module["name"] = "ota"; module["name"] = "ota";
module["sw_ver"] = version; module["sw_ver"] = device_info.version;
module["product_name"] = "Moonraker"; module["product_name"] = "Moonraker";
payload["info"]["module"].push_back(module); payload["info"]["module"].push_back(module);
@@ -1057,9 +934,7 @@ void MoonrakerPrinterAgent::start_status_stream(const std::string& dev_id, const
} }
ws_stop.store(false); ws_stop.store(false);
ws_thread = std::thread([this, dev_id, base_url, api_key]() { ws_thread = std::thread([this, dev_id, base_url, api_key]() { run_status_stream(dev_id, base_url, api_key); });
run_status_stream(dev_id, base_url, api_key);
});
} }
void MoonrakerPrinterAgent::stop_status_stream() void MoonrakerPrinterAgent::stop_status_stream()
@@ -1186,8 +1061,7 @@ void MoonrakerPrinterAgent::run_status_stream(std::string dev_id, std::string ba
ws.read(buffer, ec); ws.read(buffer, ec);
if (ec == beast::error::timeout) { if (ec == beast::error::timeout) {
const auto now_ms = static_cast<uint64_t>( const auto now_ms = static_cast<uint64_t>(
std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count());
std::chrono::steady_clock::now().time_since_epoch()).count());
const auto last_ms = ws_last_emit_ms.load(); const auto last_ms = ws_last_emit_ms.load();
if (last_ms == 0 || now_ms - last_ms >= 10000) { if (last_ms == 0 || now_ms - last_ms >= 10000) {
nlohmann::json message; nlohmann::json message;
@@ -1265,8 +1139,7 @@ void MoonrakerPrinterAgent::handle_ws_message(const std::string& dev_id, const s
bool is_critical = false; // Track if this is a critical update that bypasses throttle bool is_critical = false; // Track if this is a critical update that bypasses throttle
// Check for subscription response (has "result.status") - initial subscription is critical // Check for subscription response (has "result.status") - initial subscription is critical
if (json.contains("result") && json["result"].contains("status") && if (json.contains("result") && json["result"].contains("status") && json["result"]["status"].is_object()) {
json["result"]["status"].is_object()) {
update_status_cache(json["result"]["status"]); update_status_cache(json["result"]["status"]);
updated = true; updated = true;
is_critical = true; // Initial subscription response - dispatch immediately is_critical = true; // Initial subscription response - dispatch immediately
@@ -1275,8 +1148,7 @@ void MoonrakerPrinterAgent::handle_ws_message(const std::string& dev_id, const s
// Check for status update notifications // Check for status update notifications
if (json.contains("method") && json["method"].is_string()) { if (json.contains("method") && json["method"].is_string()) {
const std::string method = json["method"].get<std::string>(); const std::string method = json["method"].get<std::string>();
if (method == "notify_status_update" && json.contains("params") && if (method == "notify_status_update" && json.contains("params") && json["params"].is_array() && !json["params"].empty() &&
json["params"].is_array() && !json["params"].empty() &&
json["params"][0].is_object()) { json["params"][0].is_object()) {
update_status_cache(json["params"][0]); update_status_cache(json["params"][0]);
updated = true; updated = true;
@@ -1313,8 +1185,7 @@ void MoonrakerPrinterAgent::handle_ws_message(const std::string& dev_id, const s
std::string current_state; std::string current_state;
{ {
std::lock_guard<std::recursive_mutex> lock(payload_mutex); std::lock_guard<std::recursive_mutex> lock(payload_mutex);
if (status_cache.contains("print_stats") && if (status_cache.contains("print_stats") && status_cache["print_stats"].contains("state") &&
status_cache["print_stats"].contains("state") &&
status_cache["print_stats"]["state"].is_string()) { status_cache["print_stats"]["state"].is_string()) {
current_state = status_cache["print_stats"]["state"].get<std::string>(); current_state = status_cache["print_stats"]["state"].get<std::string>();
} }
@@ -1328,14 +1199,11 @@ void MoonrakerPrinterAgent::handle_ws_message(const std::string& dev_id, const s
if (updated) { if (updated) {
const auto now_ms = static_cast<uint64_t>( const auto now_ms = static_cast<uint64_t>(
std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count());
std::chrono::steady_clock::now().time_since_epoch()).count());
const auto last_dispatch_ms = ws_last_dispatch_ms.load(); const auto last_dispatch_ms = ws_last_dispatch_ms.load();
// Dispatch if: critical change OR throttle interval elapsed // Dispatch if: critical change OR throttle interval elapsed
const bool should_dispatch = is_critical || const bool should_dispatch = is_critical || last_dispatch_ms == 0 || now_ms - last_dispatch_ms >= STATUS_UPDATE_INTERVAL_MS;
last_dispatch_ms == 0 ||
now_ms - last_dispatch_ms >= STATUS_UPDATE_INTERVAL_MS;
if (should_dispatch) { if (should_dispatch) {
nlohmann::json message; nlohmann::json message;
@@ -1396,10 +1264,14 @@ nlohmann::json MoonrakerPrinterAgent::build_print_payload_locked() const
int mc_print_stage = 0; int mc_print_stage = 0;
if (status_cache.contains("print_stats") && status_cache["print_stats"].contains("state")) { if (status_cache.contains("print_stats") && status_cache["print_stats"].contains("state")) {
std::string mr_state = status_cache["print_stats"]["state"].get<std::string>(); std::string mr_state = status_cache["print_stats"]["state"].get<std::string>();
if (mr_state == "printing") mc_print_stage = 1; if (mr_state == "printing")
else if (mr_state == "paused") mc_print_stage = 2; mc_print_stage = 1;
else if (mr_state == "complete") mc_print_stage = 3; else if (mr_state == "paused")
else if (mr_state == "error") mc_print_stage = 4; mc_print_stage = 2;
else if (mr_state == "complete")
mc_print_stage = 3;
else if (mr_state == "error")
mc_print_stage = 4;
} }
payload["print"]["mc_print_stage"] = mc_print_stage; payload["print"]["mc_print_stage"] = mc_print_stage;
@@ -1416,9 +1288,12 @@ nlohmann::json MoonrakerPrinterAgent::build_print_payload_locked() const
int home_flag = 0; int home_flag = 0;
if (status_cache.contains("toolhead") && status_cache["toolhead"].contains("homed_axes")) { if (status_cache.contains("toolhead") && status_cache["toolhead"].contains("homed_axes")) {
std::string homed = status_cache["toolhead"]["homed_axes"].get<std::string>(); std::string homed = status_cache["toolhead"]["homed_axes"].get<std::string>();
if (homed.find('X') != std::string::npos) home_flag |= 1; // bit 0 if (homed.find('X') != std::string::npos)
if (homed.find('Y') != std::string::npos) home_flag |= 2; // bit 1 home_flag |= 1; // bit 0
if (homed.find('Z') != std::string::npos) home_flag |= 4; // bit 2 if (homed.find('Y') != std::string::npos)
home_flag |= 2; // bit 1
if (homed.find('Z') != std::string::npos)
home_flag |= 4; // bit 2
} }
payload["print"]["home_flag"] = home_flag; payload["print"]["home_flag"] = home_flag;
@@ -1429,8 +1304,7 @@ nlohmann::json MoonrakerPrinterAgent::build_print_payload_locked() const
payload["print"]["support_send_to_sd"] = true; payload["print"]["support_send_to_sd"] = true;
// Detect bed_leveling support from available objects (bed_mesh or probe) // Detect bed_leveling support from available objects (bed_mesh or probe)
// Default to 0 (not supported) if neither object exists // Default to 0 (not supported) if neither object exists
bool has_bed_leveling = (available_objects.count("bed_mesh") != 0 || bool has_bed_leveling = (available_objects.count("bed_mesh") != 0 || available_objects.count("probe") != 0);
available_objects.count("probe") != 0);
payload["print"]["support_bed_leveling"] = has_bed_leveling ? 1 : 0; payload["print"]["support_bed_leveling"] = has_bed_leveling ? 1 : 0;
const nlohmann::json* extruder = nullptr; const nlohmann::json* extruder = nullptr;
@@ -1496,8 +1370,7 @@ nlohmann::json MoonrakerPrinterAgent::build_print_payload_locked() const
} }
int mc_percent = -1; int mc_percent = -1;
if (status_cache.contains("virtual_sdcard") && if (status_cache.contains("virtual_sdcard") && status_cache["virtual_sdcard"].contains("progress") &&
status_cache["virtual_sdcard"].contains("progress") &&
status_cache["virtual_sdcard"]["progress"].is_number()) { status_cache["virtual_sdcard"]["progress"].is_number()) {
const double progress = status_cache["virtual_sdcard"]["progress"].get<double>(); const double progress = status_cache["virtual_sdcard"]["progress"].get<double>();
if (progress >= 0.0) { if (progress >= 0.0) {
@@ -1508,10 +1381,8 @@ nlohmann::json MoonrakerPrinterAgent::build_print_payload_locked() const
payload["print"]["mc_percent"] = mc_percent; payload["print"]["mc_percent"] = mc_percent;
} }
if (status_cache.contains("print_stats") && if (status_cache.contains("print_stats") && status_cache["print_stats"].contains("total_duration") &&
status_cache["print_stats"].contains("total_duration") && status_cache["print_stats"].contains("print_duration") && status_cache["print_stats"]["total_duration"].is_number() &&
status_cache["print_stats"].contains("print_duration") &&
status_cache["print_stats"]["total_duration"].is_number() &&
status_cache["print_stats"]["print_duration"].is_number()) { status_cache["print_stats"]["print_duration"].is_number()) {
const double total = status_cache["print_stats"]["total_duration"].get<double>(); const double total = status_cache["print_stats"]["total_duration"].get<double>();
const double elapsed = status_cache["print_stats"]["print_duration"].get<double>(); const double elapsed = status_cache["print_stats"]["print_duration"].get<double>();
@@ -1522,8 +1393,7 @@ nlohmann::json MoonrakerPrinterAgent::build_print_payload_locked() const
} }
const auto now_ms = static_cast<uint64_t>( const auto now_ms = static_cast<uint64_t>(
std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
std::chrono::system_clock::now().time_since_epoch()).count());
payload["t_utc"] = now_ms; payload["t_utc"] = now_ms;
return payload; return payload;
@@ -1563,8 +1433,7 @@ void MoonrakerPrinterAgent::dispatch_message(const std::string& dev_id, const st
} }
} }
bool MoonrakerPrinterAgent::upload_gcode( bool MoonrakerPrinterAgent::upload_gcode(const std::string& local_path,
const std::string& local_path,
const std::string& filename, const std::string& filename,
const std::string& base_url, const std::string& base_url,
const std::string& api_key, const std::string& api_key,
@@ -1652,8 +1521,7 @@ int MoonrakerPrinterAgent::cancel_print(const std::string& dev_id)
return send_gcode(dev_id, gcode) ? BAMBU_NETWORK_SUCCESS : BAMBU_NETWORK_ERR_SEND_MSG_FAILED; return send_gcode(dev_id, gcode) ? BAMBU_NETWORK_SUCCESS : BAMBU_NETWORK_ERR_SEND_MSG_FAILED;
} }
bool MoonrakerPrinterAgent::send_jsonrpc_command( bool MoonrakerPrinterAgent::send_jsonrpc_command(const std::string& base_url,
const std::string& base_url,
const std::string& api_key, const std::string& api_key,
const nlohmann::json& request, const nlohmann::json& request,
std::string& response) const std::string& response) const
@@ -1680,9 +1548,7 @@ bool MoonrakerPrinterAgent::send_jsonrpc_command(
http_error = "HTTP " + std::to_string(status); http_error = "HTTP " + std::to_string(status);
} }
}) })
.on_error([&](std::string body, std::string err, unsigned status) { .on_error([&](std::string body, std::string err, unsigned status) { http_error = err; })
http_error = err;
})
.perform_sync(); .perform_sync();
if (!success) { if (!success) {
@@ -1692,39 +1558,22 @@ bool MoonrakerPrinterAgent::send_jsonrpc_command(
return success; return success;
} }
void MoonrakerPrinterAgent::perform_connection_async( void MoonrakerPrinterAgent::perform_connection_async(const std::string& dev_id, const std::string& base_url, const std::string& api_key)
const std::string& dev_id,
const std::string& base_url,
const std::string& api_key)
{ {
int result = BAMBU_NETWORK_ERR_CONNECTION_TO_PRINTER_FAILED; int result = BAMBU_NETWORK_ERR_CONNECTION_TO_PRINTER_FAILED;
std::string error_msg; std::string error_msg;
try { try {
// Check Klippy state if (!fetch_device_info(base_url, api_key, device_info, error_msg)) {
nlohmann::json server_info;
if (!fetch_server_info_json(base_url, api_key, server_info, error_msg)) {
BOOST_LOG_TRIVIAL(error) << "MoonrakerPrinterAgent: Failed to fetch server info: " << error_msg; BOOST_LOG_TRIVIAL(error) << "MoonrakerPrinterAgent: Failed to fetch server info: " << error_msg;
dispatch_local_connect(ConnectStatusFailed, dev_id, "server_info_failed"); // Orca todo: revist here, for now don't send error, this is set current MachineObject to null
finish_connection(); // dispatch_local_connect(ConnectStatusFailed, dev_id, "server_info_failed");
return;
}
nlohmann::json result_json = server_info.contains("result")
? server_info["result"] : server_info;
std::string klippy_state = result_json.value("klippy_state", "");
// Check state
if (klippy_state != "ready" && result == BAMBU_NETWORK_ERR_CONNECTION_TO_PRINTER_FAILED) {
std::string state_message = result_json.value("state_message", "Unknown error");
BOOST_LOG_TRIVIAL(error) << "MoonrakerPrinterAgent: Klippy not ready: " << klippy_state
<< " - " << state_message;
error_msg = "klippy_not_ready:" + klippy_state;
dispatch_local_connect(ConnectStatusFailed, dev_id, error_msg);
finish_connection(); finish_connection();
return; return;
} }
// Orca todo: disable websocket for now, as we don't use MonitorPanel for Moonraker printers yet
#if 0
// Query initial status // Query initial status
nlohmann::json initial_status; nlohmann::json initial_status;
if (query_printer_status(base_url, api_key, initial_status, error_msg)) { if (query_printer_status(base_url, api_key, initial_status, error_msg)) {
@@ -1738,6 +1587,7 @@ void MoonrakerPrinterAgent::perform_connection_async(
// Start WebSocket status stream // Start WebSocket status stream
start_status_stream(dev_id, base_url, api_key); start_status_stream(dev_id, base_url, api_key);
#endif
// Success! // Success!
result = BAMBU_NETWORK_SUCCESS; result = BAMBU_NETWORK_SUCCESS;
@@ -1754,7 +1604,8 @@ void MoonrakerPrinterAgent::perform_connection_async(
dispatch_printer_connected(dev_id); dispatch_printer_connected(dev_id);
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent: connect_printer completed - dev_id=" << 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_ERR_CANCELED) {
dispatch_local_connect(ConnectStatusFailed, dev_id, 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, error_msg);
} }
finish_connection(); finish_connection();
@@ -1811,7 +1662,6 @@ std::string MoonrakerPrinterAgent::join_url(const std::string& base_url, const s
return base_url + path; return base_url + path;
} }
// Sanitize filename to prevent path traversal attacks // Sanitize filename to prevent path traversal attacks
// Extracts only the basename, removing any path components // Extracts only the basename, removing any path components
std::string MoonrakerPrinterAgent::sanitize_filename(const std::string& filename) std::string MoonrakerPrinterAgent::sanitize_filename(const std::string& filename)

View File

@@ -71,7 +71,7 @@ public:
// Pull-mode agent (on-demand filament sync) // Pull-mode agent (on-demand filament sync)
virtual FilamentSyncMode get_filament_sync_mode() const override { return FilamentSyncMode::pull; } virtual FilamentSyncMode get_filament_sync_mode() const override { return FilamentSyncMode::pull; }
virtual void fetch_filament_info(std::string dev_id) override; virtual bool fetch_filament_info(std::string dev_id) override;
protected: protected:
struct MoonrakerDeviceInfo struct MoonrakerDeviceInfo
@@ -84,6 +84,7 @@ protected:
std::string model_name; std::string model_name;
std::string dev_name; std::string dev_name;
std::string version; std::string version;
std::string klippy_state;
bool use_ssl = false; bool use_ssl = false;
} device_info; } device_info;
@@ -105,7 +106,6 @@ private:
int send_version_info(const std::string& dev_id); int send_version_info(const std::string& dev_id);
int send_access_code(const std::string& dev_id); int send_access_code(const std::string& dev_id);
bool fetch_server_info(const std::string& base_url, const std::string& api_key, std::string& version, std::string& error) const;
bool fetch_object_list(const std::string& base_url, const std::string& api_key, std::set<std::string>& objects, std::string& error) const; bool fetch_object_list(const std::string& base_url, const std::string& api_key, std::set<std::string>& objects, std::string& error) const;
bool query_printer_status(const std::string& base_url, const std::string& api_key, nlohmann::json& status, std::string& error) const; bool query_printer_status(const std::string& base_url, const std::string& api_key, nlohmann::json& status, std::string& error) const;
bool send_gcode(const std::string& dev_id, const std::string& gcode) const; bool send_gcode(const std::string& dev_id, const std::string& gcode) const;
@@ -135,10 +135,6 @@ private:
bool send_jsonrpc_command(const std::string& base_url, const std::string& api_key, bool send_jsonrpc_command(const std::string& base_url, const std::string& api_key,
const nlohmann::json& request, std::string& response) const; const nlohmann::json& request, std::string& response) const;
// Server info (returns JSON, not just version string)
bool fetch_server_info_json(const std::string& base_url, const std::string& api_key,
nlohmann::json& info, std::string& error) const;
// Connection thread management // Connection thread management
void perform_connection_async(const std::string& dev_id, void perform_connection_async(const std::string& dev_id,
const std::string& base_url, const std::string& base_url,

View File

@@ -597,11 +597,12 @@ FilamentSyncMode NetworkAgent::get_filament_sync_mode() const
return FilamentSyncMode::none; // Default when no agent return FilamentSyncMode::none; // Default when no agent
} }
void NetworkAgent::fetch_filament_info(std::string dev_id) bool NetworkAgent::fetch_filament_info(std::string dev_id)
{ {
if (m_printer_agent) { if (m_printer_agent) {
m_printer_agent->fetch_filament_info(dev_id); return m_printer_agent->fetch_filament_info(dev_id);
} }
return false;
} }
int NetworkAgent::get_user_presets(std::map<std::string, std::map<std::string, std::string>>* user_presets) int NetworkAgent::get_user_presets(std::map<std::string, std::map<std::string, std::string>>* user_presets)

View File

@@ -115,7 +115,7 @@ public:
int start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn); int start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn);
int start_sdcard_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn); int start_sdcard_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn);
FilamentSyncMode get_filament_sync_mode() const; FilamentSyncMode get_filament_sync_mode() const;
void fetch_filament_info(std::string dev_id); bool fetch_filament_info(std::string dev_id);
int get_user_presets(std::map<std::string, std::map<std::string, std::string>>* user_presets); int get_user_presets(std::map<std::string, std::map<std::string, std::string>>* user_presets);
std::string request_setting_id(std::string name, std::map<std::string, std::string>* values_map, unsigned int* http_code); std::string request_setting_id(std::string name, std::map<std::string, std::string>* values_map, unsigned int* http_code);
int put_setting(std::string setting_id, std::string name, std::map<std::string, std::string>* values_map, unsigned int* http_code); int put_setting(std::string setting_id, std::string name, std::map<std::string, std::string>* values_map, unsigned int* http_code);

View File

@@ -24,18 +24,18 @@ AgentInfo QidiPrinterAgent::get_agent_info_static()
return AgentInfo{.id = "qidi", .name = "Qidi Printer Agent", .version = QidiPrinterAgent_VERSION, .description = "Qidi printer agent"}; return AgentInfo{.id = "qidi", .name = "Qidi Printer Agent", .version = QidiPrinterAgent_VERSION, .description = "Qidi printer agent"};
} }
void QidiPrinterAgent::fetch_filament_info(std::string dev_id) bool QidiPrinterAgent::fetch_filament_info(std::string dev_id)
{ {
// Look up MachineObject via DeviceManager // Look up MachineObject via DeviceManager
auto* dev_manager = GUI::wxGetApp().getDeviceManager(); auto* dev_manager = GUI::wxGetApp().getDeviceManager();
if (!dev_manager) { if (!dev_manager) {
BOOST_LOG_TRIVIAL(error) << "QidiPrinterAgent::fetch_filament_info: DeviceManager is null"; BOOST_LOG_TRIVIAL(error) << "QidiPrinterAgent::fetch_filament_info: DeviceManager is null";
return; return false;
} }
MachineObject* obj = dev_manager->get_my_machine(dev_id); MachineObject* obj = dev_manager->get_my_machine(dev_id);
if (!obj) { if (!obj) {
BOOST_LOG_TRIVIAL(error) << "QidiPrinterAgent::fetch_filament_info: MachineObject not found for dev_id=" << dev_id; BOOST_LOG_TRIVIAL(error) << "QidiPrinterAgent::fetch_filament_info: MachineObject not found for dev_id=" << dev_id;
return; return false;
} }
std::vector<QidiSlotInfo> slots; std::vector<QidiSlotInfo> slots;
@@ -43,7 +43,7 @@ void QidiPrinterAgent::fetch_filament_info(std::string dev_id)
std::string error; std::string error;
if (!fetch_slot_info(device_info.base_url, device_info.api_key, slots, box_count, error)) { if (!fetch_slot_info(device_info.base_url, device_info.api_key, slots, box_count, error)) {
BOOST_LOG_TRIVIAL(error) << "QidiPrinterAgent::fetch_filament_info: Failed to fetch slot info: " << error; BOOST_LOG_TRIVIAL(error) << "QidiPrinterAgent::fetch_filament_info: Failed to fetch slot info: " << error;
return; return false;
} }
QidiFilamentDict dict; QidiFilamentDict dict;
@@ -141,6 +141,7 @@ void QidiPrinterAgent::fetch_filament_info(std::string dev_id)
// Call the parser to populate DevFilaSystem // Call the parser to populate DevFilaSystem
DevFilaSystemParser::ParseV1_0(print_json, obj, obj->GetFilaSystem(), false); DevFilaSystemParser::ParseV1_0(print_json, obj, obj->GetFilaSystem(), false);
return true;
} }
bool QidiPrinterAgent::fetch_slot_info(const std::string& base_url, bool QidiPrinterAgent::fetch_slot_info(const std::string& base_url,
@@ -162,8 +163,8 @@ bool QidiPrinterAgent::fetch_slot_info(const std::string& base_url,
if (!api_key.empty()) { if (!api_key.empty()) {
http.header("X-Api-Key", api_key); http.header("X-Api-Key", api_key);
} }
http.timeout_connect(10) http.timeout_connect(5)
.timeout_max(30) .timeout_max(10)
.on_complete([&](std::string body, unsigned status) { .on_complete([&](std::string body, unsigned status) {
if (status == 200) { if (status == 200) {
response_body = body; response_body = body;
@@ -246,8 +247,8 @@ bool QidiPrinterAgent::fetch_filament_dict(const std::string& base_url,
if (!api_key.empty()) { if (!api_key.empty()) {
http.header("X-Api-Key", api_key); http.header("X-Api-Key", api_key);
} }
http.timeout_connect(10) http.timeout_connect(5)
.timeout_max(30) .timeout_max(10)
.on_complete([&](std::string body, unsigned status) { .on_complete([&](std::string body, unsigned status) {
if (status == 200) { if (status == 200) {
response_body = body; response_body = body;

View File

@@ -19,7 +19,7 @@ public:
AgentInfo get_agent_info() override { return get_agent_info_static(); } AgentInfo get_agent_info() override { return get_agent_info_static(); }
// Override filament sync (Qidi-specific implementation) // Override filament sync (Qidi-specific implementation)
void fetch_filament_info(std::string dev_id) override; bool fetch_filament_info(std::string dev_id) override;
private: private:
// Qidi-specific device info (extends base MoonrakerDeviceInfo with model_id) // Qidi-specific device info (extends base MoonrakerDeviceInfo with model_id)