// Implementation of web communication protocol for Slicer Studio #include "SSWCP.hpp" #include "GUI_App.hpp" #include "MainFrame.hpp" #include "DownloadManager.hpp" #include "nlohmann/json.hpp" #include "slic3r/GUI/Tab.hpp" #include "sentry_wrapper/SentryWrapper.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "NetworkTestDialog.hpp" #include "MoonRaker.hpp" #include "slic3r/GUI/WebPresetDialog.hpp" #include #include "slic3r/GUI/SMPhysicalPrinterDialog.hpp" #include "slic3r/GUI/WebUrlDialog.hpp" #include "miniz/miniz.h" #include "slic3r/Utils/MQTT.hpp" namespace pt = boost::property_tree; using namespace nlohmann; namespace Slic3r { namespace GUI { // WCP_Logger WCP_Logger::WCP_Logger() { } bool WCP_Logger::run() { if (inited) { return true; // Already initialized } // 创建IO上下文和TCP套接字 socket = new tcp::socket(io_ctx); // 解析服务器地址(本地回环) resolver = new tcp::resolver(io_ctx); auto endpoints = resolver->resolve("127.0.0.1", "50000"); // 端口与Python服务端一致 // 连接服务器(同步连接,阻塞直到成功或失败) try { asio::connect(*socket, endpoints); } catch (std::exception& e) { return false; } m_work_thread = std::thread(&WCP_Logger::worker, this); inited = true; return inited; } WCP_Logger& WCP_Logger::getInstance() { static WCP_Logger instance; return instance; } bool WCP_Logger::set_level(wxString& level) { if (m_log_level_map.count(level)) { m_log_level = m_log_level_map[level]; return true; } else { m_log_level = 0; return false; } } // Add a log message to the queue void WCP_Logger::add_log(const wxString& content, bool is_web = false, wxString time = "", wxString module = "Default", wxString level = "debug") { if (!inited) { return; } if (time == "") { // 获取当前时间 wxDateTime now = wxDateTime::Now(); // 格式化日期时间部分(年-月-日 时:分:秒) wxString dateTimePart = now.Format(_T("%Y-%m-%d %H:%M:%S")); // 获取毫秒并格式化为三位字符串(补零) int milliseconds = now.GetMillisecond(); wxString msPart = wxString::Format(_T("%03d"), milliseconds); // 拼接完整时间字符串 time = dateTimePart + _T(":") + msPart; } std::lock_guard lock(m_log_mtx); m_log_que.push((time + " [ " + (is_web ? "Flutter" : "Native") + " ] [ " + level + " ] [ " + module + "] " + content + "\n").ToUTF8()); } void WCP_Logger::worker() { while (true) { m_log_mtx.lock(); if (!m_log_que.empty()) { wxString log = m_log_que.front(); m_log_que.pop(); m_log_mtx.unlock(); log += "\n"; try { asio::write(*socket, asio::buffer(log.ToUTF8().data(), log.length() + 1)); } catch (std::exception& e) { } } else { m_log_mtx.unlock(); std::this_thread::sleep_for(std::chrono::seconds(1)); } bool end_flag = false; m_end_mtx.lock(); end_flag = m_end; m_end_mtx.unlock(); if (end_flag) break; } } WCP_Logger::~WCP_Logger() { m_end_mtx.lock(); m_end = true; m_end_mtx.unlock(); if (m_work_thread.joinable()) m_work_thread.join(); try { if (socket != nullptr && socket->is_open()) { socket->close(); } } catch (std::exception& e) { if (resolver) delete resolver; if (socket) delete socket; return; } if (resolver) delete resolver; if (socket) delete socket; } extern json m_ProfileJson; extern std::mutex m_ProfileJson_mutex; std::vector load_thumbnails(const std::string& file, size_t image_count) { std::vector res; // Read a 64k block from the end of the G-code. boost::nowide::ifstream ifs(file); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": before parse_file %1%") % file.c_str(); bool begin_found = false; bool end_found = false; std::string line; int thumbnail_id = 0; while (std::getline(ifs, line)) { if (thumbnail_id == image_count) { break; } // 找到缩略图开始标记 if (line.find("; THUMBNAIL_BLOCK_START") != std::string::npos) { std::string thumb_content = ""; int width = 0; int height = 0; int data_size = 0; // 跳过空行 std::getline(ifs, line); std::getline(ifs, line); // 读取缩略图信息行 std::getline(ifs, line); if (line.find("; thumbnail begin") != std::string::npos) { // 解析宽度、高度和数据大小 // 格式: "; thumbnail begin 48x48 1144" sscanf(line.c_str(), "; thumbnail begin %dx%d %d", &width, &height, &data_size); // 读取Base64编码的数据 std::string base64_data; while (std::getline(ifs, line)) { if (line.find("; thumbnail end") != std::string::npos) { break; } // 移除行首的 "; " if (line.substr(0, 2) == "; ") { base64_data += line.substr(2); } } thumb_content = base64_data; res.emplace_back(thumb_content); ++thumbnail_id; } // 读取到块结束标记 while (std::getline(ifs, line)) { if (line.find("; THUMBNAIL_BLOCK_END") != std::string::npos) { break; } } } } ifs.clear(); // 清除可能的 EOF 标志 ifs.seekg(0); return std::move(res); } // Util std::vector create_zip_with_miniz(const std::string& name1, // 原文件路径(如 "c:/xxx/1.gcode") const std::string& name2 // ZIP 内文件名(如 "target.gcode") ) { // 1. 读取原文件内容 std::ifstream file(name1, std::ios::binary); if (!file.is_open()) { throw std::runtime_error("Failed to open source file: " + name1); } std::vector file_content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); SSWCP::m_file_size_mutex.lock(); SSWCP::m_active_file_size = file_content.size(); SSWCP::m_file_size_mutex.unlock(); // 2. 初始化 ZIP 写入器(内存模式) mz_zip_archive zip_archive; memset(&zip_archive, 0, sizeof(zip_archive)); // 初始化 ZIP 写入到堆内存 if (!mz_zip_writer_init_heap(&zip_archive, 0, 0)) { throw std::runtime_error("Failed to initialize ZIP writer"); } // 3. 将文件内容添加到 ZIP(使用 name2 作为内部文件名) if (!mz_zip_writer_add_mem(&zip_archive, name2.c_str(), // ZIP 内文件名 file_content.data(), // 文件内容指针 file_content.size(), // 文件内容大小 MZ_DEFAULT_COMPRESSION // 压缩级别 )) { mz_zip_writer_end(&zip_archive); throw std::runtime_error("Failed to add file to ZIP"); } // 4. 完成 ZIP 写入并获取内存数据 void* zip_data = nullptr; size_t zip_size = 0; if (!mz_zip_writer_finalize_heap_archive(&zip_archive, &zip_data, &zip_size)) { mz_zip_writer_end(&zip_archive); throw std::runtime_error("Failed to finalize ZIP archive"); } // 将 ZIP 数据复制到 vector(方便后续操作) std::vector zip_stream(static_cast(zip_data), static_cast(zip_data) + zip_size); // 5. 清理资源 mz_zip_writer_end(&zip_archive); mz_free(zip_data); return zip_stream; } static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; std::string base64_encode(const char* data, size_t len) { std::string encoded; int i = 0, j = 0; uint8_t byte3[3], byte4[4]; while (len--) { byte3[i++] = *(data++); if (i == 3) { byte4[0] = (byte3[0] & 0xfc) >> 2; byte4[1] = ((byte3[0] & 0x03) << 4) | ((byte3[1] & 0xf0) >> 4); byte4[2] = ((byte3[1] & 0x0f) << 2) | ((byte3[2] & 0xc0) >> 6); byte4[3] = byte3[2] & 0x3f; for (i = 0; i < 4; i++) encoded += base64_chars[byte4[i]]; i = 0; } } if (i) { for (j = i; j < 3; j++) byte3[j] = 0; byte4[0] = (byte3[0] & 0xfc) >> 2; byte4[1] = ((byte3[0] & 0x03) << 4) | ((byte3[1] & 0xf0) >> 4); byte4[2] = ((byte3[1] & 0x0f) << 2) | ((byte3[2] & 0xc0) >> 6); byte4[3] = byte3[2] & 0x3f; for (j = 0; j < i + 1; j++) encoded += base64_chars[byte4[j]]; while (i++ < 3) encoded += '='; } return encoded; } std::string generate_zip_path(const std::string& oriname, const std::string& targetname) { // 解析 name1 的路径 fs::path path1 = oriname; // 获取父目录(例如 "c:/xxx/xxx/xxx") fs::path parent_dir = path1.parent_path(); // 将 name2 作为基础文件名,追加 ".zip"(例如 "target.gcode" -> "target.gcode.zip") fs::path new_filename = fs::path(targetname); new_filename += ".zip"; // 直接追加扩展名 // 组合完整路径 fs::path zip_path = parent_dir / new_filename; return zip_path.string(); } // 检查文件是否存在并读取内容 bool read_existing_zip(const std::string& zip_path, std::vector& out_data) { if (!fs::exists(zip_path)) { return false; } std::ifstream file(zip_path, std::ios::binary); if (!file.is_open()) { throw std::runtime_error("Failed to open existing ZIP file: " + zip_path); } out_data.assign(std::istreambuf_iterator(file), std::istreambuf_iterator()); return true; } // 主逻辑函数 json get_or_create_zip_json(const std::string& name1, // 原文件路径(如 "1.gcode") const std::string& name2, // 目标 ZIP 文件名(如 "target.gcode") const std::string& zip_path // 要检查的 ZIP 文件路径(如 "output.zip") ) { std::vector zip_stream; // 1. 检查同名 ZIP 是否存在 if (read_existing_zip(zip_path, zip_stream)) { std::cout << "Reusing existing ZIP file: " << zip_path << std::endl; } else { // 2. 若不存在,创建新 ZIP 并写入文件 std::cout << "Creating new ZIP file: " << zip_path << std::endl; zip_stream = create_zip_with_miniz(name1, name2); // 将新生成的 ZIP 写入文件(可选持久化) std::ofstream out_file(zip_path, std::ios::binary); out_file.write(zip_stream.data(), zip_stream.size()); if (!out_file.good()) { throw std::runtime_error("Failed to write ZIP to: " + zip_path); } } //// 3. 编码为 Base64 并存入 JSON //std::string base64_str = base64_encode(zip_stream.data(), zip_stream.size()); json j; j["zip_data"] = zip_stream; j["zip_name"] = name2 + ".zip"; return j; } bool SSWCP_Instance::m_first_connected = true; // Base SSWCP_Instance implementation void SSWCP_Instance::process() { if (m_event_id != "") { json header; send_to_js(); m_header.clear(); m_header["event_id"] = m_event_id; } if (m_cmd == "test") { sync_test(); } else if (m_cmd == "test_async"){ async_test(); } else if (m_cmd == "sw_test_mqtt_moonraker") { test_mqtt_request(); } else if (m_cmd == "sw_SetCache") { sw_SetCache(); } else if (m_cmd == "sw_GetCache") { sw_GetCache(); } else if (m_cmd == "sw_RemoveCache") { sw_RemoveCache(); } else if (m_cmd == "sw_SwitchTab") { sw_SwitchTab(); } else if (m_cmd == "sw_Webview_Unsubscribe") { sw_Webview_Unsubscribe(); } else if (m_cmd == "sw_UnsubscribeAll") { sw_UnsubscribeAll(); } else if (m_cmd == "sw_Unsubscribe_Filter") { sw_Unsubscribe_Filter(); } else if (m_cmd == "sw_GetFileStream") { sw_GetFileStream(); } else if (m_cmd == "sw_GetActiveFile") { sw_GetActiveFile(); } else if (m_cmd == "sw_Log") { sw_Log(); } else if (m_cmd == "sw_SetLogLevel") { sw_SetLogLevel(); } else if (m_cmd == "sw_LaunchConsole") { sw_LaunchConsole(); } else if (m_cmd == "sw_Exit") { sw_Exit(); } else if(m_cmd == "sw_FileLog") { sw_FileLog(); } else if (m_cmd == "sw_SubscribeCacheKey") { sw_SubscribeCacheKey(); } else if (m_cmd == "sw_UnsubscribeCacheKeys") { sw_UnsubscribeCacheKeys(); } else if (m_cmd == "sw_UploadEvent") { sw_UploadEvent(); } else if (m_cmd == "sw_OpenOrcaWebview") { sw_OpenOrcaWebview(); } else if (m_cmd == "sw_OpenBrowser") { sw_OpenBrowser(); } else if (m_cmd == "sw_OpenNetworkDialog"){ sw_OpenNetworkDialog(); } else { handle_general_fail(); } } void SSWCP_Instance::sw_UploadEvent() { try { if(!m_param_data.count("traceId")){ handle_general_fail(-1, "param [traceId] required!"); return; } if(!m_param_data.count("level")){ handle_general_fail(-1, "param [level] required!"); return; } if(!m_param_data.count("content")){ handle_general_fail(-1, "param [content] required!"); return; } std::string traceId = m_param_data["traceId"].get(); int level = m_param_data["level"].get(); std::string content = m_param_data["content"].get(); std::string funcModule = m_param_data.count("funcModule") ? m_param_data["funcModule"].get() : ""; std::string tagKey = m_param_data.count("tagKey") ? m_param_data["tagKey"].get() : ""; std::string tagValue = m_param_data.count("tagValue") ? m_param_data["tagValue"].get() : ""; sentryReportLog(SENTRY_LOG_LEVEL(level), content, funcModule, tagKey, tagValue, traceId); send_to_js(); finish_job(); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_Instance::sw_OpenNetworkDialog() { try { send_to_js(); finish_job(); wxGetApp().CallAfter([]() { // Use shared_ptr to manage dialog lifetime auto dlg = std::make_shared(wxGetApp().mainframe); dlg->ShowModal(); // Keep dialog alive for 2 seconds after closing to allow background threads to finish // Use a timer to delay the destruction class DelayedReleaseTimer : public wxTimer { std::shared_ptr m_dialog; public: DelayedReleaseTimer(std::shared_ptr dlg) : m_dialog(std::move(dlg)) { StartOnce(5000); // 5 seconds delay } void Notify() override { m_dialog.reset(); // Release the dialog delete this; // Delete the timer itself } }; new DelayedReleaseTimer(dlg); }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_Instance::sw_OpenBrowser() { try { std::string url = m_param_data.count("url") ? m_param_data["url"].get() : ""; wxString wx_url = wxString::FromUTF8(url); std::weak_ptr weak_self = shared_from_this(); wxGetApp().CallAfter([wx_url, weak_self]() { auto self = weak_self.lock(); if (!self) { return; } bool res = wxLaunchDefaultBrowser(wx_url); if (!res) { self->handle_general_fail(-1, "Open browser failed"); } else { self->send_to_js(); self->finish_job(); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_Instance::sw_OpenOrcaWebview() { try { std::string url = m_param_data.count("url") ? m_param_data["url"].get() : ""; wxString wx_url = wxString::FromUTF8(url); std::weak_ptr weak_self = shared_from_this(); wxGetApp().CallAfter([wx_url, weak_self]() { auto self = weak_self.lock(); if (!self) { return; } auto dialog = new WebUrlDialog(); dialog->load_url(wx_url); self->send_to_js(); self->finish_job(); dialog->Show(); }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_Instance::sw_FileLog() { try { std::string level = m_param_data.count("level") ? m_param_data["level"].get() : "debug"; std::string content = m_param_data.count("content") ? m_param_data["content"].get() : ""; if(level == "debug") { BOOST_LOG_TRIVIAL(debug) << "[WCP] " << content; } else if(level == "info") { BOOST_LOG_TRIVIAL(info) << "[WCP] " << content; } else if(level == "warning") { BOOST_LOG_TRIVIAL(warning) << "[WCP] " << content; } else if(level == "error") { BOOST_LOG_TRIVIAL(error) << "[WCP] " << content; } else if(level == "fatal") { BOOST_LOG_TRIVIAL(fatal) << "[WCP] " << content; } send_to_js(); finish_job(); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_Instance::sw_Exit() { try { wxGetApp().Exit(); } catch (std::exception& e) { } } void SSWCP_Instance::sw_GetActiveFile() { try { std::string file_path = SSWCP::get_active_filename(); std::string file_name = SSWCP::get_display_filename(); if (file_path == "" || file_name == "") { handle_general_fail(); return; } bool iszip = false; if (m_param_data.count("is_zip")) { iszip = m_param_data["is_zip"].get(); } if (iszip) { std::weak_ptr weak_self = shared_from_this(); if (m_work_thread.joinable()) m_work_thread.join(); m_work_thread = std::thread([file_path, file_name, weak_self]() { auto self = weak_self.lock(); std::string zipname = generate_zip_path(file_path, file_name); json res = get_or_create_zip_json(file_path, file_name, zipname); size_t name_index = file_name.find_last_of("."); size_t path_index = file_path.find_last_of("."); if (!(name_index == std::string::npos || path_index == std::string::npos)) { self->m_res_data["file_name"] = file_name.substr(0, name_index) + ".zip"; self->m_res_data["file_path"] = wxString(zipname).ToUTF8(); SSWCP::m_file_size_mutex.lock(); self->m_res_data["origin_size"] = SSWCP::m_active_file_size; SSWCP::m_file_size_mutex.unlock(); wxGetApp().CallAfter([weak_self]() { if (weak_self.lock()) { weak_self.lock()->send_to_js(); weak_self.lock()->finish_job(); } }); } else { wxGetApp().CallAfter([weak_self]() { if (weak_self.lock()) { weak_self.lock()->handle_general_fail(); } }); return; } }); } else { m_res_data["file_name"] = file_name; m_res_data["file_path"] = file_path; send_to_js(); finish_job(); } } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_Instance::sw_LaunchConsole() { try { bool res = WCP_Logger::getInstance().run(); if (res) { m_msg = "Orca Console has been launched"; send_to_js(); finish_job(); } else { handle_general_fail(-1, "Orca Console launched failed"); } } catch (std::exception& e) { wxString reason = e.what(); handle_general_fail(-1, "Exception caught: " + reason); } } void SSWCP_Instance::sw_SetLogLevel() { try { if (m_param_data.count("level")) { wxString level = m_param_data["level"].get(); bool res = WCP_Logger::getInstance().set_level(level); if (res) { m_msg = ("The log level has been set to " + level).ToUTF8(); send_to_js(); finish_job(); } else { handle_general_fail(-1, "The param [level] is not legal, the log level will be set to debug"); } } else { handle_general_fail(-1, "param [level] required!"); } } catch (std::exception& e) { wxString reason = e.what(); handle_general_fail(-1, "Exception caught: " + reason); } } void SSWCP_Instance::sw_Log() { try { wxString time = m_param_data.count("time") ? m_param_data["time"].get() : ""; wxString level = m_param_data.count("level") ? m_param_data["level"].get() : ""; wxString module = m_param_data.count("module") ? m_param_data["module"].get() : ""; wxString content = m_param_data.count("content") ? m_param_data["content"].get() : ""; auto& logger = WCP_Logger::getInstance(); if (!logger.m_log_level_map.count(level)) { // todo log:级别不对,转成debug,打原生log level = "debug"; } if (logger.m_log_level_map[level] >= logger.get_level()) { logger.add_log(content, true, time, module, level); } finish_job(); } catch (std::exception& e) { finish_job(); } } void SSWCP_Instance::sw_GetFileStream() { try { bool isZip = false; if (m_param_data.count("is_zip")) { isZip = m_param_data["is_zip"].get(); } std::string file_path = SSWCP::get_active_filename(); std::weak_ptr weak_self = shared_from_this(); if (isZip) { auto oriname = SSWCP::get_active_filename(); auto targetname = SSWCP::get_display_filename(); std::weak_ptr weak_self = shared_from_this(); if (m_work_thread.joinable()) m_work_thread.join(); m_work_thread = std::thread([oriname, targetname, weak_self]() { auto self = weak_self.lock(); if (self) { std::string zipname = generate_zip_path(oriname, targetname); json res = get_or_create_zip_json(oriname, targetname, zipname); wxGetApp().CallAfter([weak_self, res]() { auto self = weak_self.lock(); if (self) { self->m_res_data["name"] = res["zip_name"]; self->m_res_data["content"] = res["zip_data"]; self->send_to_js(); self->finish_job(); } }); } }); } else { if (m_work_thread.joinable()) m_work_thread.join(); m_work_thread = std::thread([file_path, weak_self]() { auto self = weak_self.lock(); if (self) { // 1. 读取原文件内容 std::ifstream file(file_path, std::ios::binary); if (!file.is_open()) { self->handle_general_fail(); return; } // 获取文件大小 file.seekg(0, std::ios::end); std::streamsize file_size = file.tellg(); file.seekg(0, std::ios::beg); // 预分配 std::string 空间 std::string content; content.resize(file_size); // 一次性读取整个文件 if (!file.read(&content[0], file_size)) { std::cerr << "读取文件失败" << std::endl; self->handle_general_fail(); return; } self->m_res_data["content"] = wxString(content).ToUTF8(); wxGetApp().CallAfter([weak_self]() { auto self = weak_self.lock(); if (self) { self->send_to_js(); self->finish_job(); } }); } }); } } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_Instance::handle_general_fail(int code, const wxString& msg) { try { m_status = code; m_msg = msg.ToUTF8(); send_to_js(); finish_job(); } catch (std::exception& e) {} } // Mark instance as invalid void SSWCP_Instance::set_Instance_illegal() { m_illegal_mtx.lock(); m_illegal = true; m_illegal_mtx.unlock(); } // Check if instance is invalid bool SSWCP_Instance::is_Instance_illegal() { m_illegal_mtx.lock(); bool res = m_illegal; m_illegal_mtx.unlock(); return res; } // Get associated webview wxWebView* SSWCP_Instance::get_web_view() const { return m_webview; } void SSWCP_Instance::set_web_view(wxWebView* view) { m_webview = view; } // Send response to JavaScript void SSWCP_Instance::send_to_js() { try { if (is_Instance_illegal()) { return; } json response, payload; response["header"] = m_header; payload["code"] = m_status; payload["message"] = m_msg; payload["data"] = m_res_data; response["payload"] = payload; std::string json_str = response.dump(4, ' ', true); std::string str_res = "window.postMessage(JSON.stringify(" + json_str + "), '*');"; WCP_Logger::getInstance().add_log(str_res, false, "", "WCP", "info"); auto weak_self = std::weak_ptr(shared_from_this()); wxGetApp().CallAfter([weak_self, str_res]() { try { auto self = weak_self.lock(); if (self && self->m_webview && self->m_webview->GetRefData()) { WebView::RunScript(self->m_webview, str_res); } } catch (std::exception& e) { WCP_Logger::getInstance().add_log(e.what(), false, "", "WCP", "info"); } }); } catch (std::exception& e) {} } // Clean up instance void SSWCP_Instance::finish_job() { SSWCP::delete_target(this); } // Asynchronous test implementation void SSWCP_Instance::async_test() { auto http = Http::get("http://172.18.1.69/"); http.on_error([&](std::string body, std::string error, unsigned status) { }) .on_complete([=](std::string body, unsigned) { json data; data["str"] = body; this->m_res_data = data; this->send_to_js(); finish_job(); }) .perform(); } // Synchronous test implementation std::unordered_map SSWCP_Instance::m_wcp_cache; void SSWCP_Instance::sync_test() { m_res_data = m_param_data; send_to_js(); finish_job(); } void SSWCP_Instance::test_mqtt_request() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->test_async_wcp_mqtt_moonraker(m_param_data, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); // host->async_get_printer_info([self](const json& response) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_Instance::sw_SwitchTab() { try { if (m_param_data.count("target")) { std::string target_tab = m_param_data["target"].get(); if (SSWCP::m_tab_map.count(target_tab)) { wxGetApp().mainframe->request_select_tab(MainFrame::TabPosition(SSWCP::m_tab_map[target_tab])); send_to_js(); finish_job(); return; } } handle_general_fail(); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_Instance::sw_SetCache() { try { if (m_param_data.count("objects") && m_param_data["objects"].is_array() && m_param_data["objects"].size() > 0) { json objects = m_param_data["objects"]; for (size_t i = 0; i < objects.size(); ++i) { m_wcp_cache.insert({objects[i]["key"].get(), objects[i]["value"]}); wxGetApp().cache_notify(objects[i]["key"].get(), objects[i]["value"]); } send_to_js(); finish_job(); } else { handle_general_fail(); } } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_Instance::sw_GetCache() { try { if (m_param_data.count("keys") && m_param_data["keys"].is_array() && m_param_data["keys"].size() > 0) { json res_data; json keys = m_param_data["keys"]; for (size_t i = 0; i < keys.size(); ++i) { std::string key = keys[i].get(); if (m_wcp_cache.count(key)) { res_data.push_back(m_wcp_cache[key]); } else { res_data.push_back(json::object()); } } m_res_data = res_data; send_to_js(); finish_job(); } else { handle_general_fail(); } } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_Instance::sw_RemoveCache() { try { if (m_param_data.count("keys") && m_param_data["keys"].is_array()) { json keys = m_param_data["keys"]; if (keys.size() == 0) { for (const auto& item : m_wcp_cache) { wxGetApp().cache_notify(item.first, json::value_t::null); } m_wcp_cache.clear(); } else { for (size_t i = 0; i < keys.size(); ++i) { std::string key = keys[i].get(); if (m_wcp_cache.count(key)) { wxGetApp().cache_notify(key, json::value_t::null); m_wcp_cache.erase(key); } } } send_to_js(); finish_job(); } else { handle_general_fail(); } } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_Instance::sw_SubscribeCacheKey() { try { if (!m_param_data.count("key") || !m_param_data["key"].is_string()) { handle_general_fail(-1, "param [keys] required or wrong type"); return; } std::string key = m_param_data["key"].get(); auto& cache_map = wxGetApp().m_cache_subscribers; for (auto iter = cache_map.begin(); iter != cache_map.end();) { if (iter->first.first == m_webview && iter->second == key) { // 删除之前的订阅 iter = cache_map.erase(iter); } else { ++iter; } } std::weak_ptr weak_self = shared_from_this(); cache_map[{m_webview, weak_self}] = key; } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_Instance::sw_UnsubscribeCacheKeys() { try { if (!m_param_data.count("keys") || !m_param_data["keys"].is_array()) { handle_general_fail(-1, "param [keys] required or wrong type!"); return; } json keys = m_param_data["keys"]; auto& cache_map = wxGetApp().m_cache_subscribers; if (keys.size() == 0) { for (auto iter = cache_map.begin(); iter != cache_map.end();) { if (iter->first.first == m_webview) { iter = cache_map.erase(iter); } else { iter++; } } } else { for (size_t i = 0; i < keys.size(); ++i) { std::string delete_key = keys[i].get(); for (auto iter = cache_map.begin(); iter != cache_map.end();) { if (iter->first.first == m_webview && iter->second == delete_key) { iter = cache_map.erase(iter); } else { iter++; } } } } } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_Instance::on_mqtt_status_msg_arrived(std::shared_ptr obj, const json& response) { if (!obj) { return; } if (response.is_null()) { obj->m_status = -1; obj->m_msg = "failure"; obj->send_to_js(); } else if (response.count("error")) { if (response["error"].is_string() && "timeout" == response["error"].get()) { obj->on_timeout(); } else { obj->m_res_data = response; obj->send_to_js(); } } else { obj->m_res_data = response; obj->send_to_js(); } } void SSWCP_Instance::on_mqtt_msg_arrived(std::shared_ptr obj, const json& response) { if (!obj) { return; } if (response.is_null()) { obj->m_status = -1; obj->m_msg = "failure"; obj->send_to_js(); } else if (response.count("error")) { if (response["error"].is_string() && "timeout" == response["error"].get()) { obj->on_timeout(); } else { obj->m_res_data = response; obj->send_to_js(); } } else { obj->m_res_data = response; obj->send_to_js(); } obj->finish_job(); } void SSWCP_Instance::sw_UnsubscribeAll() { wxGetApp().m_device_card_subscribers.clear(); wxGetApp().m_recent_file_subscribers.clear(); wxGetApp().m_user_login_subscribers.clear(); wxGetApp().m_cache_subscribers.clear(); wxGetApp().m_user_update_privacy_subscribers.clear(); send_to_js(); finish_job(); } void SSWCP_Instance::sw_Webview_Unsubscribe() { auto& device_map = wxGetApp().m_device_card_subscribers; for (auto iter = device_map.begin(); iter != device_map.end();) { if (iter->first == m_webview) { iter = device_map.erase(iter); } else { iter++; } } auto& login_map = wxGetApp().m_user_login_subscribers; for (auto iter = login_map.begin(); iter != login_map.end();) { if (iter->first == m_webview) { iter = login_map.erase(iter); } else { iter++; } } auto& recent_file_map = wxGetApp().m_recent_file_subscribers; for (auto iter = recent_file_map.begin(); iter != recent_file_map.end();) { if (iter->first == m_webview) { iter = recent_file_map.erase(iter); } else { iter++; } } auto& cache_map = wxGetApp().m_cache_subscribers; for (auto iter = cache_map.begin(); iter != cache_map.end();) { if (iter->first.first == m_webview) { iter = cache_map.erase(iter); } else { iter++; } } auto& privacy_map = wxGetApp().m_user_update_privacy_subscribers; for (auto iter = privacy_map.begin(); iter != privacy_map.end();) { if (iter->first == m_webview) { iter = privacy_map.erase(iter); } else { iter++; } } send_to_js(); finish_job(); } void SSWCP_Instance::sw_Unsubscribe_Filter() { try { std::string cmd = m_param_data.count("cmd") ? m_param_data["cmd"].get() : ""; std::string event_id = m_param_data.count("event_id") ? m_param_data["event_id"].get() : ""; if (cmd == "" || event_id == "") { send_to_js(); finish_job(); return; } auto& device_map = wxGetApp().m_device_card_subscribers; auto& login_map = wxGetApp().m_user_login_subscribers; auto& privacy_map = wxGetApp().m_user_update_privacy_subscribers; auto& recent_file_map = wxGetApp().m_recent_file_subscribers; auto& cache_map = wxGetApp().m_cache_subscribers; if (cmd == "") { for (auto iter = device_map.begin(); iter != device_map.end();) { if (iter->first == m_webview) { auto ptr = iter->second.lock(); if (ptr) { if (ptr->m_event_id == event_id) { iter = device_map.erase(iter); } else { iter++; } } else { iter = device_map.erase(iter); } } else { iter++; } } for (auto iter = login_map.begin(); iter != login_map.end();) { if (iter->first == m_webview) { auto ptr = iter->second.lock(); if (ptr) { if (ptr->m_event_id == event_id) { iter = login_map.erase(iter); } else { iter++; } } else { iter = login_map.erase(iter); } } else { iter++; } } for (auto iter = privacy_map.begin(); iter != privacy_map.end();) { if (iter->first == m_webview) { auto ptr = iter->second.lock(); if (ptr) { if (ptr->m_event_id == event_id) { iter = privacy_map.erase(iter); } else { iter++; } } else { iter = privacy_map.erase(iter); } } else { iter++; } } for (auto iter = recent_file_map.begin(); iter != recent_file_map.end();) { if (iter->first == m_webview) { auto ptr = iter->second.lock(); if (ptr) { if (ptr->m_event_id == event_id) { iter = recent_file_map.erase(iter); } else { iter++; } } else { iter = recent_file_map.erase(iter); } } else { iter++; } } for (auto iter = cache_map.begin(); iter != cache_map.end();) { if (iter->first.first == m_webview) { auto ptr = iter->first.second.lock(); if (ptr) { if (ptr->m_event_id == event_id) { iter = cache_map.erase(iter); } else { iter++; } } else { iter = cache_map.erase(iter); } } else { iter++; } } } else if (cmd == "sw_SubscribeRecentFiles") { for (auto iter = recent_file_map.begin(); iter != recent_file_map.end();) { if (iter->first == m_webview) { auto ptr = iter->second.lock(); if (ptr) { if (event_id == "" || (event_id != "" && event_id == ptr->m_event_id)) { iter = recent_file_map.erase(iter); } else { iter++; } } else { iter = recent_file_map.erase(iter); } } else { iter++; } } } else if (cmd == "sw_SubscribeUserLoginState") { for (auto iter = login_map.begin(); iter != login_map.end();) { if (iter->first == m_webview) { auto ptr = iter->second.lock(); if (ptr) { if (event_id == "" || (event_id != "" && event_id == ptr->m_event_id)) { iter = login_map.erase(iter); } else { iter++; } } else { iter = login_map.erase(iter); } } else { iter++; } } } else if (cmd == UPDATE_PRIVACY_STATUS) { for (auto iter = privacy_map.begin(); iter != privacy_map.end();) { if (iter->first == m_webview) { auto ptr = iter->second.lock(); if (ptr) { if (event_id == "" || (event_id != "" && event_id == ptr->m_event_id)) { iter = privacy_map.erase(iter); } else { iter++; } } else { iter = privacy_map.erase(iter); } } else { iter++; } } }else if (cmd == "sw_SubscribeLocalDevices") { for (auto iter = device_map.begin(); iter != device_map.end();) { if (iter->first == m_webview) { auto ptr = iter->second.lock(); if (ptr) { if (event_id == "" || (event_id != "" && event_id == ptr->m_event_id)) { iter = device_map.erase(iter); } else { iter++; } } else { iter = device_map.erase(iter); } } else { iter++; } } } else if (cmd == "sw_SubscribeCacheKey") { for (auto iter = cache_map.begin(); iter != cache_map.end();) { if (iter->first.first == m_webview) { auto ptr = iter->first.second.lock(); if (ptr) { if (event_id == "" || (event_id != "" && event_id == ptr->m_event_id)) { iter = cache_map.erase(iter); } else { iter++; } } else { iter = cache_map.erase(iter); } } else { iter++; } } } else { BOOST_LOG_TRIVIAL(warning) << "no this cmd for:" << cmd; } send_to_js(); finish_job(); } catch (std::exception& e) { handle_general_fail(); } } // Handle timeout event void SSWCP_Instance::on_timeout() { handle_general_fail(-2, "time out"); finish_job(); } void SSWCP_Instance::update_filament_info(const json& objects, bool send_message) { if (objects.size() < 1) { if (send_message) handle_general_fail(-1, "objects is empty!"); return; } if (!objects[0].count("key") || !objects[0]["key"].is_string()) { if (send_message) handle_general_fail(-1, "param [key] required or wrong type!"); return; } if (!objects[0].count("value") || !objects[0]["value"].is_string()) { if (send_message) handle_general_fail(-1, "param [value] required or wrong type!"); return; } std::string sn = objects[0]["key"].get(); std::string value = objects[0]["value"].get(); DeviceInfo info; if (!wxGetApp().app_config->get_device_info(sn, info)) { m_msg = "sn does not exist!"; if (send_message) { handle_general_fail(-1, m_msg); } return; } else { if (!info.connected) { m_msg = "The machine is not connected!"; if (send_message) { handle_general_fail(-1, m_msg); } return; } else { json j_value; try { j_value = json::parse(value); } catch (std::exception& e) { if (send_message) handle_general_fail(-1, "value parse failed"); return; } if (!j_value.count("filament_vendor") || !j_value["filament_vendor"].is_array() || !j_value.count("filament_type") || !j_value["filament_type"].is_array() || !j_value.count("filament_sub_type") || !j_value["filament_sub_type"].is_array() || ((!j_value.count("filament_color") || !j_value["filament_color"].is_array()) && (!j_value.count("filament_color_rgba") || !j_value["filament_color_rgba"].is_array())) || !j_value.count("extruder_map_table") || !j_value["extruder_map_table"].is_array() || !j_value.count("filament_official") || !j_value["filament_official"].is_array()) { if (send_message) { handle_general_fail(-1, "value parse failed"); } return; } // 存储耗材,并触发更新 auto& filaments = wxGetApp().preset_bundle->machine_filaments; static auto tmp_filaments = filaments; if (m_first_connected) { tmp_filaments = filaments; m_first_connected = false; } filaments.clear(); size_t count = 0; for (size_t i = 0; i < j_value["filament_official"].size(); ++i) { bool is_official = j_value["filament_official"][i].get(); if (/*is_official*/ true) { std::string vendor = j_value["filament_vendor"][i].get(); std::string type = j_value["filament_type"][i].get(); std::string sub_type = j_value["filament_sub_type"][i].get(); std::string name = ""; // 名称特殊处理 if (type == "TPU") { name = vendor + " " + type; } else if (sub_type == "Support") { name = vendor + " Support" + " For " + type; } else { name = vendor + " " + type + ((sub_type != "NONE" && sub_type != "") ? " " + sub_type : ""); } int extruder = j_value["extruder_map_table"][i].get(); if (j_value.count("filament_color_rgba") && j_value["filament_color_rgba"].is_array() && j_value["filament_color_rgba"].size() != 0) { std::string str_color = "#" + j_value["filament_color_rgba"][i].get(); filaments.insert({int(i), {name, str_color}}); } else { if (j_value["filament_color"][i].is_number()) { int color = j_value["filament_color"][i].get(); std::ostringstream oss; oss << "#" << std::uppercase << std::setfill('0') << std::setw(6) << std::hex << (color & 0x00FFFFFF); // 仅取低24位 std::string str_color = oss.str(); filaments.insert({int(i), {name, str_color}}); } else { std::string str_color = "#" + j_value["filament_color"][i].get(); filaments.insert({int(i), {name, str_color}}); } } } } bool need_load_preset = false; if (filaments.size() == 0 && tmp_filaments.size() != 0) need_load_preset = true; for (auto iter = filaments.begin(); iter != filaments.end(); iter++) { if (tmp_filaments.count(iter->first)) { auto pair = iter->second; auto tmp_pair = tmp_filaments[iter->first]; if (pair.first == tmp_pair.first && pair.second == pair.second) { continue; } else { need_load_preset = true; break; } } else { auto pair = iter->second; if (pair.first.find("NONE") != std::string::npos) { continue; } need_load_preset = true; break; } } if (need_load_preset) { tmp_filaments = filaments; wxGetApp().load_current_presets(); } if (send_message) { send_to_js(); finish_job(); } } } } // SSWCP_MachineFind_Instance implementation // Process machine find commands void SSWCP_MachineFind_Instance::process() { if (m_event_id != "") { json header; send_to_js(); m_header.clear(); m_header["event_id"] = m_event_id; } if (m_cmd == "sw_StartMachineFind") { sw_StartMachineFind(); } else if (m_cmd == "sw_GetMachineFindSupportInfo") { sw_GetMachineFindSupportInfo(); } else if (m_cmd == "sw_StopMachineFind") { sw_StopMachineFind(); } else if (m_cmd == "sw_WakeupFind") { sw_WakeupFind(); } else { handle_general_fail(); } } // Set stop flag for machine discovery void SSWCP_MachineFind_Instance::set_stop(bool stop) { m_stop_mtx.lock(); m_stop = stop; m_stop_mtx.unlock(); } // Get machine discovery support info void SSWCP_MachineFind_Instance::sw_GetMachineFindSupportInfo() { // 2.0.0 只支持 mdns - snapmaker json protocols = json::array(); protocols.push_back("mdns"); json mdns_service_names = json::array(); mdns_service_names.push_back("snapmaker"); json res; res["protocols"] = protocols; res["mdns_service_names"] = mdns_service_names; m_res_data = res; send_to_js(); } void SSWCP_MachineFind_Instance::sw_WakeupFind() { try { // 1) 广域服务浏览:触发 _services._dns-sd._udp.local.,维持约8秒 // 此查询促使 AP/交换机为本机建立 224.0.0.251:5353 的组播转发表,并促使设备进行通告 try { Bonjour::TxtKeys warmup_txt_keys = {}; auto warmup = Bonjour("services._dns-sd") .set_protocol("udp") .set_txt_keys(std::move(warmup_txt_keys)) .set_retries(2) .set_timeout(4) // 2*4 = ~8s .on_reply([](BonjourReply&&){}) .on_complete([](){}) .lookup(); (void)warmup; } catch (...) { // 预热失败不影响后续短查 } // 2) 目标服务短查:对 snapmaker 做一次短查询(2 秒),帮助休眠设备快速回应 try { auto kick = Bonjour("snapmaker") .set_retries(1) .set_timeout(2) .on_reply([](BonjourReply&&){}) .on_complete([](){}) .lookup(); (void)kick; } catch (...) { } // 立刻返回成功,查询在线程中进行 m_status = 200; m_msg = "OK"; m_res_data["result"] = "wakeup_started"; send_to_js(); finish_job(); } catch (std::exception& e) { handle_general_fail(); } } // Start machine discovery void SSWCP_MachineFind_Instance::sw_StartMachineFind() { try { std::vector protocols; float last_time = -1; if (m_param_data.count("last_time") && m_param_data["last_time"].is_number()) { last_time = m_param_data["last_time"].get(); } last_time = -1; // 目前只支持通过mdns协议搜索snapmaker,prusalink,之后可以再扩充 protocols.push_back("mdns"); for (size_t i = 0; i < protocols.size(); ++i) { if (protocols[i] == "mdns") { std::vector mdns_service_names; mdns_service_names.push_back("snapmaker"); //mdns_service_names.push_back("prusalink"); //mdns_service_names.push_back("rdlink"); //mdns_service_names.push_back("raop"); m_engines.clear(); for (size_t i = 0; i < mdns_service_names.size(); ++i) { m_engines.push_back(nullptr); } //// 预热扫描:发送一轮短超时查询以激活休眠后的mDNS响应 //for (const auto& svc : mdns_service_names) { // Bonjour(svc) // .set_retries(1) // .set_timeout(2) // .on_reply([](BonjourReply&&){}) // .on_complete([]{}) // .lookup(); //} //std::this_thread::sleep_for(std::chrono::milliseconds(300)); Bonjour::TxtKeys txt_keys = {"sn", "version", "machine_type", "link_mode", "userid", "device_name", "ip", "region"}; std::string unique_key = "sn"; for (size_t i = 0; i < m_engines.size(); ++i) { auto self = std::dynamic_pointer_cast(shared_from_this()); if(!self){ continue; } std::weak_ptr weak_self(self); m_engines[i] = Bonjour(mdns_service_names[i]) .set_txt_keys(std::move(txt_keys)) .set_retries(3) .set_timeout(last_time >= 0.0 ? last_time/1000 : 20) .on_reply([weak_self, unique_key](BonjourReply&& reply) { // Check if application is still alive before processing if (!GUI_App::m_app_alive.load()) { return; } auto self = weak_self.lock(); if(!self || self->is_stop()){ return; } // Double check application is still alive after locking if (!GUI_App::m_app_alive.load()) { return; } json machine_data; std::string hostname = reply.hostname; size_t pos = hostname.find(".local"); if (pos != std::string::npos) { hostname = hostname.substr(0, pos); } machine_data["name"] = hostname != "" ? hostname : reply.service_name; // machine_data["hostname"] = reply.hostname; if (!reply.ip.is_v4()) { return; } machine_data["ip"] = reply.ip.to_string(); machine_data["port"] = reply.port; if (reply.txt_data.count("protocol")) { machine_data["protocol"] = reply.txt_data["protocol"]; } if (reply.txt_data.count("version")) { machine_data["pro_version"] = reply.txt_data["version"]; } if (reply.txt_data.count(unique_key)) { machine_data["unique_key"] = unique_key; machine_data["unique_value"] = reply.txt_data[unique_key]; machine_data[unique_key] = machine_data["unique_value"]; } if (reply.txt_data.count("link_mode")) { machine_data["link_mode"] = reply.txt_data["link_mode"]; } if (reply.txt_data.count("userid")) { machine_data["userid"] = reply.txt_data["userid"]; } if (reply.txt_data.count("device_name")) { machine_data["device_name"] = reply.txt_data["device_name"]; } // 模拟一下 /* machine_data["cover"] = LOCALHOST_URL + std::to_string(PAGE_HTTP_PORT) + "/profiles/Snapmaker/Snapmaker A350 Dual BKit_cover.png";*/ if (reply.txt_data.count("machine_type")) { std::string machine_type = reply.txt_data["machine_type"]; machine_data["machine_type"] = machine_type; auto machine_ip_type = MachineIPType::getInstance(); if (machine_ip_type) { machine_ip_type->add_instance(reply.ip.to_string(), machine_type); } size_t vendor_pos = machine_type.find_first_of(" "); if (vendor_pos != std::string::npos) { std::string vendor = machine_type.substr(0, vendor_pos); // Check application is still alive before accessing wxGetApp() if (GUI_App::m_app_alive.load()) { try { std::string machine_cover = LOCALHOST_URL + std::to_string(wxGetApp().m_page_http_server.get_port()) + "/profiles/" + vendor + "/" + machine_type + "_cover.png"; machine_data["cover"] = machine_cover; } catch (...) { // Application is shutting down, skip setting cover } } } } else { // test /*auto machine_ip_type = MachineIPType::getInstance(); if (machine_ip_type) { machine_ip_type->add_instance(reply.ip.to_string(), "unknown"); }*/ } if (reply.txt_data.count("ip") && machine_data["ip"] == "") { BOOST_LOG_TRIVIAL(warning) << "[Machine Find] Can't find the endpoint's ip, use the txtData: " << reply.txt_data["ip"]; machine_data["ip"] = reply.txt_data["ip"]; } machine_data["txt_ip"] = reply.txt_data["ip"]; if (reply.txt_data.count("region")) { machine_data["region"] = reply.txt_data["region"]; } // Final check before adding to list if (!GUI_App::m_app_alive.load() || !self || self->is_stop()) { return; } json machine_object; if (machine_data.count("unique_value")) { self->add_machine_to_list(machine_object); machine_object[reply.txt_data[unique_key]] = machine_data; } else { machine_object[reply.ip.to_string()] = machine_data; } self->add_machine_to_list(machine_object); }) .on_complete([weak_self]() { // Check if application is still alive before scheduling callback if (!GUI_App::m_app_alive.load()) { return; } try { wxGetApp().CallAfter([weak_self]() { // Check again inside the callback if (!GUI_App::m_app_alive.load()) { return; } auto self = weak_self.lock(); if (self && !self->is_stop()) { self->onOneEngineEnd(); } }); } catch (...) { // Application is shutting down, ignore the callback } }) .lookup(); } } else { // 支持其他机器发现协议 } } } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineFind_Instance::sw_StopMachineFind() { try { SSWCP::stop_machine_find(); send_to_js(); finish_job(); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineFind_Instance::add_machine_to_list(const json& machine_info) { try { BOOST_LOG_TRIVIAL(info) << "check the machine list on json: " << machine_info.dump(); for (const auto& [key, value] : machine_info.items()) { std::string sn = value["sn"].get(); bool need_send = false; m_machine_list_mtx.lock(); if (!m_machine_list.count(sn)) { m_machine_list[sn] = machine_info; m_machine_list_mtx.unlock(); m_res_data[key] = value; need_send = true; } else { m_machine_list_mtx.unlock(); } m_res_data[key]["connected"] = false; DeviceInfo info; if (wxGetApp().app_config->get_device_info(sn, info)) { if (info.connected) { m_res_data[key]["connected"] = true; } std::string ip = value["ip"].get(); if (0) { info.ip = ip; wxGetApp().app_config->save_device_info(info); wxGetApp().CallAfter([]() { auto devices = wxGetApp().app_config->get_devices(); json param; param["command"] = "local_devices_arrived"; param["sequece_id"] = "10001"; param["data"] = devices; std::string logout_cmd = param.dump(); wxString strJS = wxString::Format("window.postMessage(%s)", logout_cmd); GUI::wxGetApp().run_script(strJS); // wcp订阅 json data = devices; wxGetApp().device_card_notify(data); }); } } if (need_send) { send_to_js(); } } } catch (std::exception& e) { } } void SSWCP_MachineFind_Instance::onOneEngineEnd() { ++m_engine_end_count; if (m_engine_end_count == m_engines.size()) { this->finish_job(); } } // SSWCP_MachineOption_Instance void SSWCP_MachineOption_Instance::process() { if (m_event_id != "") { json header; send_to_js(); m_header.clear(); m_header["event_id"] = m_event_id; } if (m_cmd == "sw_SendGCodes") { sw_SendGCodes(); } else if (m_cmd == "sw_FileGetStatus") { sw_FileGetStatus(); } else if (m_cmd == "sw_SystemGetDeviceInfo") { sw_SystemGetDeviceInfo(); } else if (m_cmd == "sw_GetMachineState") { sw_GetMachineState(); } else if (m_cmd == "sw_SubscribeMachineState") { sw_SubscribeMachineState(); } else if (m_cmd == "sw_GetMachineObjects") { sw_GetMachineObjects(); } else if (m_cmd == "sw_SetSubscribeFilter") { sw_SetMachineSubscribeFilter(); } else if (m_cmd == "sw_StopMachineStateSubscription") { sw_UnSubscribeMachineState(); } else if (m_cmd == "sw_GetPrinterInfo") { sw_GetPrintInfo(); } else if (m_cmd == "sw_GetMachineSystemInfo") { sw_GetSystemInfo(); } else if (m_cmd == "sw_MachinePrintStart") { sw_MachinePrintStart(); } else if (m_cmd == "sw_MachinePrintPause") { sw_MachinePrintPause(); } else if (m_cmd == "sw_MachinePrintResume") { sw_MachinePrintResume(); } else if (m_cmd == "sw_MachinePrintCancel") { sw_MachinePrintCancel(); } else if (m_cmd == "sw_MachineFilesRoots") { sw_MachineFilesRoots(); } else if (m_cmd == "sw_MachineFilesMetadata") { sw_MachineFilesMetadata(); } else if (m_cmd == "sw_MachineFilesThumbnails") { sw_MachineFilesThumbnails(); } else if (m_cmd == "sw_MachineFilesGetDirectory") { sw_MachineFilesGetDirectory(); } else if (m_cmd == "sw_CameraStartMonitor") { sw_CameraStartMonitor(); } else if (m_cmd == "sw_CameraStopMonitor") { sw_CameraStopMonitor(); } else if (m_cmd == "sw_DeleteMachineFile") { sw_DeleteMachineFile(); } else if (m_cmd == "sw_SetFilamentMappingComplete") { sw_SetFilamentMappingComplete(); } else if (m_cmd == "sw_GetFileFilamentMapping") { sw_GetFileFilamentMapping(); } else if (m_cmd == "sw_FinishFilamentMapping") { sw_FinishFilamentMapping(); } else if(m_cmd == "sw_DownloadMachineFile"){ sw_DownloadMachineFile(); } else if (m_cmd == "sw_UploadFiletoMachine") { sw_UploadFiletoMachine(); } else if (m_cmd == "sw_GetPrintLegal") { sw_GetPrintLegal(); } else if (m_cmd == "sw_GetPrintZip") { sw_GetPrintZip(); } else if (m_cmd == "sw_FinishPreprint") { sw_FinishPreprint(); } else if (m_cmd == "sw_PullCloudFile") { sw_PullCloudFile(); } else if (m_cmd == "sw_CancelPullCloudFile") { sw_CancelPullCloudFile(); } else if (m_cmd == "sw_StartCloudPrint") { sw_StartCloudPrint(); } else if (m_cmd == "sw_SetDeviceName") { sw_SetDeviceName(); } else if (m_cmd == "sw_ControlLed") { sw_ControlLed(); } else if (m_cmd == "sw_ControlPrintSpeed") { sw_ControlPrintSpeed(); } else if (m_cmd == "sw_BedMesh_AbortProbeMesh") { sw_BedMesh_AbortProbeMesh(); } else if (m_cmd == "sw_ControlPurifier") { sw_ControlPurifier(); } else if (m_cmd == "sw_ControlMainFan") { sw_ControlMainFan(); } else if (m_cmd == "sw_ControlGenericFan") { sw_ControlGenericFan(); } else if (m_cmd == "sw_ControlBedTemp") { sw_ControlBedTemp(); } else if (m_cmd == "sw_ControlExtruderTemp") { sw_ControlExtruderTemp(); } else if (m_cmd == "sw_FilesThumbnailsBase64") { sw_FilesThumbnailsBase64(); } else if (m_cmd == "sw_exception_query") { sw_exception_query(); } else if (m_cmd == "sw_GetFileListPage") { sw_GetFileListPage(); } else if (m_cmd == "sw_UpdateMachineFilamentInfo") { sw_UpdateMachineFilamentInfo(); } else if (m_cmd == "sw_UploadCameraTimelapse") { sw_UploadCameraTimelapse(); } else if (m_cmd == "sw_DeleteCameraTimelapse") { sw_DeleteCameraTimelapse(); } else if (m_cmd == "sw_GetCameraTimelapseInstance") { sw_GetCameraTimelapseInstance(); } else if (m_cmd == "sw_ServerClientManagerSetUserinfo") { sw_ServerClientManagerSetUserinfo(); } else if (m_cmd == "sw_DefectDetactionConfig"){ sw_DefectDetactionConfig(); } else if (m_cmd == GET_DEVICEDATA_STORAGESPACE) { sw_GetDeviceDataStorageSpace(); } else { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_UnSubscribeMachineState() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { m_status = 1; m_msg = "failure"; send_to_js(); finish_job(); } auto weak_self = std::weak_ptr(shared_from_this()); std::string key = m_event_id + std::to_string(int64_t(m_webview)); host->async_unsubscribe_machine_info(key, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); SSWCP::stop_subscribe_machine(); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_SubscribeMachineState() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { m_status = 1; m_msg = "failure"; send_to_js(); finish_job(); return; } auto weak_self = std::weak_ptr(shared_from_this()); std::string key = m_event_id + std::to_string(int64_t(m_webview)); host->async_subscribe_machine_info(key, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_status_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_GetPrintInfo() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_get_printer_info([weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_GetMachineState() { try { if (m_param_data.count("objects")) { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); std::vector>> targets; json items = m_param_data["objects"]; for (auto& [key, value] : items.items()) { if (value.is_null()) { targets.push_back({key, {}}); } else { std::vector items; if (value.is_array()) { for (size_t i = 0; i < value.size(); ++i) { items.push_back(value[i].get()); } } else { items.push_back(value.get()); } targets.push_back({key, items}); } } if (!host) { handle_general_fail(); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_get_machine_info(targets, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } else { handle_general_fail(); } } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_SystemGetDeviceInfo() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_get_device_info([weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (const std::exception&) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_FileGetStatus() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_server_files_get_status ([weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_SendGCodes() { try { if (m_param_data.count("script")) { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); std::vector str_codes; if (m_param_data["script"].is_array()) { json codes = m_param_data["script"]; for (size_t i = 0; i < codes.size(); ++i) { str_codes.push_back(codes[i].get()); } } else if (m_param_data["script"].is_string()) { str_codes.push_back(m_param_data["script"].get()); } if (!host) { handle_general_fail(); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_send_gcodes(str_codes, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } } catch (const std::exception&) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_MachinePrintStart() { try { if (m_param_data.count("filename")) { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); std::string filename = m_param_data["filename"].get(); if (!host) { handle_general_fail(); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_start_print_job(filename, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } } catch(std::exception& e){ handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_MachinePrintPause() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_pause_print_job([weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_MachinePrintResume() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_resume_print_job([weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_MachinePrintCancel() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_cancel_print_job([weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_GetSystemInfo() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_get_system_info([weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch(std::exception& e){ handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_SetMachineSubscribeFilter() { try { if (m_param_data.count("objects")) { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); std::vector>> targets; json items = m_param_data["objects"]; for (auto& [key, value] : items.items()) { if (value.is_null()) { targets.push_back({key, {}}); } else { std::vector items; if (value.is_array()) { for (size_t i = 0; i < value.size(); ++i) { items.push_back(value[i].get()); } } else { items.push_back(value.get()); } targets.push_back({key, items}); } } if (!host) { // 错误处理 handle_general_fail(); } else { auto weak_self = std::weak_ptr(shared_from_this()); host->async_set_machine_subscribe_filter(targets, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } } else { handle_general_fail(); } } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_GetMachineObjects() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Can't find the active machine"); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_get_machine_objects([weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_PullCloudFile() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Can't find the active machine"); return; } json items = m_param_data; auto weak_self = std::weak_ptr(shared_from_this()); host->async_pull_cloud_file(items, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_UpdateMachineFilamentInfo() { try { if (!m_param_data.count("objects") || !m_param_data["objects"].is_array()) { handle_general_fail(-1, "param [objects] required or wrong type!"); return; } update_filament_info(m_param_data["objects"], true); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_CancelPullCloudFile() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Can't find the active machine"); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_cancel_pull_cloud_file([weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_StartCloudPrint() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Can't find the active machine"); return; } json items = m_param_data; auto weak_self = std::weak_ptr(shared_from_this()); host->async_start_cloud_print(items, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_MachineFilesRoots() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_machine_files_roots([weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_MachineFilesMetadata() { try { if (m_param_data.count("filename")) { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(); return; } std::string filename = m_param_data["filename"].get(); auto weak_self = std::weak_ptr(shared_from_this()); host->async_machine_files_metadata(filename, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } else { handle_general_fail(); } } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_MachineFilesThumbnails() { try { if (m_param_data.count("filename")) { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(); return; } std::string filename = m_param_data["filename"].get(); auto weak_self = std::weak_ptr(shared_from_this()); host->async_machine_files_thumbnails(filename, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } else { handle_general_fail(); } } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_MachineFilesGetDirectory() { try { if (m_param_data.count("path") && m_param_data.count("extended")) { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(); return; } std::string path = m_param_data["path"].get(); bool extend = m_param_data["extended"].get(); auto weak_self = std::weak_ptr(shared_from_this()); host->async_machine_files_directory(path, extend, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } else { handle_general_fail(); } } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_ServerClientManagerSetUserinfo() { try { if (!m_param_data.count("auther")) { handle_general_fail(-1, "param [auther] is required"); return; } json auther = m_param_data["auther"]; if(!auther.count("id") || !auther.count("nickname")){ handle_general_fail(-1, "param [id] and [nickname] is required"); return; } std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "can't find the active machine"); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_server_client_manager_set_userinfo(m_param_data, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_FinishPreprint() { try { if (m_param_data.count("status")) { std::string status = m_param_data["status"].get(); auto p_dialog = dynamic_cast(wxGetApp().get_web_preprint_dialog()); if (p_dialog) { if (status != "success") { p_dialog->set_swtich_to_device(false); } } send_to_js(); finish_job(); return; } handle_general_fail(); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_GetPrintZip() { try { auto oriname = SSWCP::get_active_filename(); auto targetname = SSWCP::get_display_filename(); std::weak_ptr weak_self = shared_from_this(); if (m_work_thread.joinable()) m_work_thread.join(); m_work_thread = std::thread([oriname, targetname, weak_self]() { auto self = weak_self.lock(); if (self) { std::string zipname = generate_zip_path(oriname, targetname); json res = get_or_create_zip_json(oriname, targetname, zipname); wxGetApp().CallAfter([weak_self, res]() { auto self = weak_self.lock(); if (self) { self->m_res_data["name"] = res["zip_name"]; self->m_res_data["content"] = res["zip_data"]; self->send_to_js(); self->finish_job(); } }); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_GetPrintLegal() { try { if (m_param_data.count("connected_model")) { std::string connected_model = m_param_data["connected_model"].get(); const auto& edit_preset = wxGetApp().preset_bundle->printers.get_edited_preset(); std::string local_name = ""; if (edit_preset.is_system) { local_name = edit_preset.name; } else { const auto& base_preset = wxGetApp().preset_bundle->printers.get_preset_base(edit_preset); local_name = base_preset->name; } local_name.erase(std::remove(local_name.begin(), local_name.end(), '('), local_name.end()); local_name.erase(std::remove(local_name.begin(), local_name.end(), ')'), local_name.end()); m_res_data["preset_model"] = local_name; m_res_data["legal"] = (local_name == connected_model); send_to_js(); finish_job(); return; } handle_general_fail(); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_UploadFiletoMachine() { try { if (!m_param_data.count("url")) { handle_general_fail(); return; } std::string upload_url = std::string(wxString(m_param_data["url"].get()).ToUTF8()); wxString wildcard = "All files (*.*)|*.*"; wxGetApp().CallAfter([wildcard, this, upload_url]() { // 创建选择文件对话框 wxFileDialog picFileDialog(nullptr, L("select file"), // 标题 "", // 默认路径 "", // 默认文件名 wildcard, // 文件类型过滤器 wxFD_OPEN | wxFD_OVERWRITE_PROMPT); // 样式 if (picFileDialog.ShowModal() == wxID_CANCEL) { // 用户取消上传 handle_general_fail(); return; } // 获取选择的保存路径 wxString filepath = picFileDialog.GetPath(); wxString filename = picFileDialog.GetFilename(); std::string tmp_url = upload_url; auto final_url = Http::encode_url_path(tmp_url); Http http_object = Http::post(final_url); http_object .form_add("print", "false") .form_add_file("file", std::string(filepath.ToUTF8()), std::string(filename.ToUTF8())) .on_error([=](std::string body, std::string error, unsigned status) { handle_general_fail(); wxGetApp().CallAfter([filename]() { MessageDialog msg_window(nullptr, " " + filename + _L(" upload failed") + "\n", _L("UpLoad Failed"), wxICON_QUESTION | wxOK); msg_window.ShowModal(); }); }) .on_complete([=](std::string body, unsigned) { try { wxGetApp().CallAfter([=]() { json response; response["filename"] = std::string(filename.ToUTF8()); m_res_data = response; send_to_js(); MessageDialog msg_window(nullptr, " " + filename + _L(" has already been uploaded") + "\n", _L("UpLoad Successfully"), wxICON_QUESTION | wxOK); msg_window.ShowModal(); finish_job(); }); } catch (std::exception& e) { handle_general_fail(); } }) .on_progress([&](Http::Progress progress, bool& cancel) { }) .perform(); }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_DownloadMachineFile() { try { if (!m_param_data.count("url")) { handle_general_fail(); return; } wxString download_url = wxString::FromUTF8(m_param_data["url"].get()); // 从 URL 获取默认文件名(如果没有提供) wxString filename = ""; if (!m_param_data.count("filename")) { size_t last_slash = download_url.find_last_of("/"); if (last_slash != std::string::npos) { filename = download_url.substr(last_slash + 1); } } else { filename = wxString::FromUTF8(m_param_data["filename"].get()); } // 获取文件扩展名 wxString extension; size_t dot_pos = filename.find_last_of("."); if (dot_pos != std::string::npos) { extension = filename.substr(dot_pos + 1); } // 构建文件类型过滤器 wxString wildcard; if (!extension.empty()) { // 例如: "PNG files (*.png)|*.png|All files (*.*)|*.*" wildcard = wxString::Format("%s files (*.%s)|*.%s|All files (*.*)|*.*", extension, extension, extension); } else { wildcard = "All files (*.*)|*.*"; } wxGetApp().CallAfter([filename, extension, wildcard, this, download_url]() { // 创建保存文件对话框 wxFileDialog saveFileDialog(nullptr, L("Save file"), // 标题 "", // 默认路径 filename, // 默认文件名 wildcard, // 文件类型过滤器 wxFD_SAVE | wxFD_OVERWRITE_PROMPT); // 样式 if (saveFileDialog.ShowModal() == wxID_CANCEL) { // 用户取消下载 handle_general_fail(); return; } // 获取选择的保存路径 wxString path = saveFileDialog.GetPath(); auto final_url = Http::encode_url_path(download_url.ToStdString(wxConvUTF8)); Http http_object = Http::get(final_url); http_object .on_error([=](std::string body, std::string error, unsigned status) { handle_general_fail(); wxGetApp().CallAfter([filename]() { MessageDialog msg_window(nullptr, " " + filename + _L(" download failed") + "\n", _L("DownLoad Failed"), wxICON_QUESTION | wxOK); msg_window.ShowModal(); }); }) .on_complete([=](std::string body, unsigned) { try { boost::nowide::ofstream file(path.ToStdString(wxConvUTF8), std::ios::binary); if (!file.is_open()) { BOOST_LOG_TRIVIAL(error) << "Failed to open file for writing: " << path; return; } file.write(body.c_str(), body.size()); file.close(); wxGetApp().CallAfter([=]() { MessageDialog msg_window(nullptr, " " + filename + _L(" has already been downloaded") + "\n", _L("DownLoad Successfully"), wxICON_QUESTION | wxOK); msg_window.ShowModal(); }); } catch (std::exception& e) { handle_general_fail(); } }) .on_progress([&](Http::Progress progress, bool& cancel) { }) .perform(); }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_FinishFilamentMapping() { try { if (wxGetApp().get_web_preprint_dialog()) { WebPreprintDialog* dialog = dynamic_cast(wxGetApp().get_web_preprint_dialog()); if (dialog) { // BBS: Use SafeEndModal to prevent duplicate EndModal calls if(dialog->is_finish()){ dialog->SafeEndModal(wxID_OK); }else{ dialog->SafeEndModal(wxID_CANCEL); } } } } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_GetFileFilamentMapping() { try { std::string filename = m_param_data.count("filename") ? m_param_data["filename"].get() : ""; if (filename == "") { filename = SSWCP::get_active_filename(); } if (filename == "") { handle_general_fail(); return; } json response = json::object(); // 检查文件是否存在且可读 if (!boost::filesystem::exists(filename) || !boost::filesystem::is_regular_file(filename)) { handle_general_fail(); return; } auto& config = wxGetApp().plater()->get_partplate_list().get_curr_plate()->fff_print()->config(); auto& result = *(wxGetApp().plater()->get_partplate_list().get_curr_plate()->get_slice_result()); /*GCodeProcessor processor; processor.process_file(filename.data()); auto& result = processor.result(); auto& config = processor.current_dynamic_config();*/ auto time = wxGetApp() .mainframe->plater() ->get_partplate_list() .get_curr_plate() ->get_slice_result() ->print_statistics.modes[static_cast(PrintEstimatedStatistics::ETimeMode::Normal)] .time; response["estimated_time"] = time; auto color_to_int = [](const std::string& oriclr) -> long long { long long res = 0; if ((oriclr.size() != 7 && oriclr.size() != 9) || oriclr[0] != '#') { return -1; } if (oriclr.size() == 7) { for (int i = 1; i <= 6; ++i) { if (oriclr[7 - i] - '0' >= 0 && oriclr[7 - i] - '0' <= 9) { res += std::pow(16, i - 1) * (oriclr[7 - i] - '0'); } else { res += std::pow(16, i - 1) * (oriclr[7 - i] - 'A' + 10); } } } else { for (int i = 1; i <= 8; ++i) { if (oriclr[7 - i] - '0' >= 0 && oriclr[7 - i] - '0' <= 9) { res += std::pow(16, i - 1) * (oriclr[7 - i] - '0'); } else { res += std::pow(16, i - 1) * (oriclr[7 - i] - 'A' + 10); } } } return res; }; // filament colour if (config.has("filament_colour")) { auto filament_color = config.option("filament_colour")->values; std::vector number_res(filament_color.size(), 0); std::vector str_res(filament_color.size()); for (int i = 0; i < filament_color.size(); ++i) { number_res[i] = color_to_int(filament_color[i]); str_res[i] = filament_color[i]; } response["filament_color"] = number_res; response["filament_color_rgba"] = str_res; } // filament type if (config.has("filament_type")) { auto filament_type = config.option("filament_type")->values; response["filament_type"] = filament_type; } // filament used if (config.has("filament_density")) { auto filament_density = config.option("filament_density")->values; std::vector filament_used_g(filament_density.size(), 0); double total_weight = 0; for (const auto& pr : result.print_statistics.total_volumes_per_extruder) { filament_used_g[pr.first] = filament_density[pr.first] * pr.second * 0.001; total_weight += filament_used_g[pr.first]; } response["filament_weight"] = filament_used_g; response["filament_weight_total"] = total_weight; } // filament extruder auto& filament_extruder_map = wxGetApp().app_config->get_filament_extruder_map_ref(); if (!filament_extruder_map.empty()) { json object; for (const auto& item : filament_extruder_map) { object[std::to_string(item.first)] = std::to_string(item.second); } response["filament_extruder_map"] = object; } // printer model auto current_preset = wxGetApp().preset_bundle->printers.get_edited_preset(); std::string c_preset = ""; if (current_preset.is_system) { c_preset = current_preset.name; } else { auto base_preset = wxGetApp().preset_bundle->printers.get_preset_base(current_preset); c_preset = base_preset->name; } response["machine_model"] = c_preset; // file cover json thumbnails = json::array(); int thumbnail_count = 0; if (config.has("thumbnails")) { std::string thumbnails_describe = config.option("thumbnails")->value; std::vector> thumbnails_size; std::vector temp; do{ size_t pos = thumbnails_describe.find(", "); std::string tmp = ""; if (pos != std::string::npos) { tmp = thumbnails_describe.substr(0, pos); thumbnails_describe = thumbnails_describe.substr(pos + 2); } else { tmp = thumbnails_describe; thumbnails_describe = ""; } size_t end_tail_pos = tmp.find("/"); if (end_tail_pos == std::string::npos) { break; } tmp = tmp.substr(0, end_tail_pos); std::string str_width = tmp.substr(0, tmp.find("x")); std::string str_height = tmp.substr(tmp.find("x") + 1); thumbnails_size.push_back({atof(str_width.c_str()), atof(str_height.c_str())}); } while (thumbnails_describe != ""); thumbnail_count = thumbnails_size.size(); auto thumbnail_list = load_thumbnails(filename, thumbnail_count); for (int i = 0; i < thumbnail_list.size(); ++i) { json thumbnail = json::object(); auto thumbnail_string = "data:image/png;base64," + thumbnail_list[i]; thumbnail["url"] = thumbnail_string; thumbnail["width"] = thumbnails_size[i].first; thumbnail["height"] = thumbnails_size[i].second; thumbnails.push_back(thumbnail); } } response["thumbnails"] = thumbnails; // file name response["filename"] = SSWCP::get_display_filename(); response["filepath"] = SSWCP::get_active_filename(); m_res_data = response; send_to_js(); finish_job(); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_SetFilamentMappingComplete() { try { if (!m_param_data.count("status")) { handle_general_fail(); } std::string status = m_param_data["status"].get(); if (status == "success" || status == "canceled") { int flag = -1; if (status == "success") { flag = wxID_OK; } //// 耗材绑定成功 //if (status == "success") { // MessageDialog msg_window(nullptr, " " + _L("setting successfully, continue to print?") + "\n", _L("Print Job Setting"), // wxICON_QUESTION | wxOK | wxCANCEL); // flag = msg_window.ShowModal(); //} else { // MessageDialog msg_window(nullptr, " " + _L("cancel the setting, continue to print?") + "\n", _L("Print Job Setting"), // wxICON_QUESTION | wxOK | wxCANCEL); // flag = msg_window.ShowModal(); //} WebPreprintDialog* dialog = dynamic_cast(wxGetApp().get_web_preprint_dialog()); if (dialog) { if(flag == wxID_OK){ dialog->set_finish(true); }else{ dialog->set_finish(false); } } } else { MessageDialog msg_window(nullptr, " " + _L("setting failed") + "\n", _L("Print Job Setting"), wxICON_QUESTION | wxOK); msg_window.ShowModal(); } send_to_js(); finish_job(); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_CameraStartMonitor() { try { if (m_param_data.count("domain")) { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(); return; } std::string domain = m_param_data["domain"].get(); int interval = m_param_data.count("interval") ? m_param_data["interval"].get() : 2; bool expect_pw = m_param_data.count("expect_pw") ? m_param_data["expect_pw"].get() : false; auto weak_self = std::weak_ptr(shared_from_this()); host->async_camera_start(domain, interval, expect_pw, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } else { handle_general_fail(); } } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_DeleteMachineFile() { try { if(!m_param_data.count("path")){ handle_general_fail(-1, "param [path] required!"); return; } std::string path = m_param_data["path"].get(); std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Connection lost!"); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_delete_machine_file(path, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_CameraStopMonitor() { try { if (m_param_data.count("domain")) { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(); return; } std::string domain = m_param_data["domain"].get(); auto weak_self = std::weak_ptr(shared_from_this()); host->async_canmera_stop(domain, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } else { handle_general_fail(); } } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_SetDeviceName() { try { if (m_param_data.count("name")) { std::string name = m_param_data["name"].get(); std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Connection lost!"); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_set_device_name(name, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } else { handle_general_fail(-1, "param [name] required!"); } } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_ControlLed() { try { if (!m_param_data.count("name")) { handle_general_fail(-1, "param [name] required!"); return; } if (!m_param_data.count("white")) { handle_general_fail(-1, "param [white] required!"); return; } std::string name = m_param_data["name"].get(); int white = m_param_data["white"].get(); std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Connection lost!"); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_control_led(name, white, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_ControlPrintSpeed() { try { if (!m_param_data.count("percentage")) { handle_general_fail(-1, "param [percentage] required!"); return; } int percentage = m_param_data["percentage"].get(); std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Connection lost!"); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_control_print_speed(percentage, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_BedMesh_AbortProbeMesh() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Connection lost!"); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_bedmesh_abort_probe_mesh([weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_ControlPurifier() { try { int fan_speed = m_param_data.count("fan_speed") ? m_param_data["fan_speed"].get() : -1; int delay_time = m_param_data.count("delay_time") ? m_param_data["delay_time"].get() : -1; int work_time = m_param_data.count("work_time") ? m_param_data["work_time"].get() : -1; std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Connection lost!"); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_controlPurifier(fan_speed, delay_time, work_time, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_ControlMainFan() { try { if (!m_param_data.count("speed")) { handle_general_fail(-1, "param [speed] required!"); return; } int speed = m_param_data["speed"].get(); std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Connection lost!"); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_control_main_fan(speed, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_ControlGenericFan() { try { if (!m_param_data.count("name")) { handle_general_fail(-1, "param [fan_id] required!"); return; } if (!m_param_data.count("speed")) { handle_general_fail(-1, "param [speed] required!"); return; } std::string name = m_param_data["name"].get(); int speed = m_param_data["speed"].get(); std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Connection lost!"); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_control_generic_fan(name, speed, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_ControlBedTemp() { try { if (!m_param_data.count("temp")) { handle_general_fail(-1, "param [temp] required!"); return; } int temp = m_param_data["temp"].get(); std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Connection lost!"); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_control_bed_temp(temp, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_ControlExtruderTemp() { try { if (!m_param_data.count("temp")) { handle_general_fail(-1, "param [temp] required!"); return; } int index = m_param_data.count("index") ? m_param_data["index"].get() : -1; int map = m_param_data.count("map") ? m_param_data["map"].get() : -1; int temp = m_param_data["temp"].get(); std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Connection lost!"); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_control_extruder_temp(temp, index, map, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_FilesThumbnailsBase64() { try { if (!m_param_data.count("path")) { handle_general_fail(-1, "param [path] required"); return; } std::string path = m_param_data["path"].get(); std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Connection lost!"); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_files_thumbnails_base64(path, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_UploadCameraTimelapse() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Connection lost!"); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_upload_camera_timelapse(m_param_data, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::CmdForwarding() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Connection lost!"); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->test_async_wcp_mqtt_moonraker(m_param_data, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_GetCameraTimelapseInstance() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Connection lost!"); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_get_timelapse_instance(m_param_data, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_GetDeviceDataStorageSpace() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Connection lost!"); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_get_userdata_space(m_param_data, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_DeleteCameraTimelapse() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Connection lost!"); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_delete_camera_timelapse(m_param_data, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_DefectDetactionConfig() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Connection lost!"); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_defect_detaction_config(m_param_data, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_GetFileListPage() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Connection lost!"); return; } if (!m_param_data.count("root") || !m_param_data["root"].is_string()) { handle_general_fail(-1, "param [root] required or wrong type!"); return; } if (!m_param_data.count("files_per_page") || !m_param_data["files_per_page"].is_number()) { handle_general_fail(-1, "param [files_per_page] required or wrong type"); return; } if (!m_param_data.count("page_number") || !m_param_data["page_number"].is_number()) { handle_general_fail(-1, "param [page_number] required or wrong type"); return; } std::string root = m_param_data["root"].get(); int files_per_page = m_param_data["files_per_page"].get(); int page_number = m_param_data["page_number"].get(); auto weak_self = std::weak_ptr(shared_from_this()); host->async_get_file_page_list(root, files_per_page, page_number, [weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineOption_Instance::sw_exception_query() { try { std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); if (!host) { handle_general_fail(-1, "Connection lost!"); return; } auto weak_self = std::weak_ptr(shared_from_this()); host->async_exception_query([weak_self](const json& response) { auto self = weak_self.lock(); if (self) { SSWCP_Instance::on_mqtt_msg_arrived(self, response); } }); } catch (std::exception& e) { handle_general_fail(); } } // SSWCP_MachineConnect_Instance void SSWCP_MachineConnect_Instance::process() { if (m_event_id != "") { json header; send_to_js(); m_header.clear(); m_header["event_id"] = m_event_id; } if (m_cmd == "sw_Test_connect") { sw_test_connect(); } else if (m_cmd == "sw_Connect") { sw_connect(); } else if (m_cmd == "sw_Disconnect") { sw_disconnect(); } else if (m_cmd == "sw_GetConnectedMachine") { sw_get_connect_machine(); } else if (m_cmd == "sw_ConnectOtherMachine"){ sw_connect_other_device(); } else if (m_cmd == "sw_GetPincode") { sw_get_pin_code(); } else { handle_general_fail(); } } void SSWCP_MachineConnect_Instance::sw_get_pin_code() { try { if (m_param_data.count("ip") && m_param_data.count("userid") && m_param_data.count("nickname")) { std::string ip = m_param_data["ip"].get(); std::string userid = m_param_data["userid"].get(); std::string nickname = m_param_data["nickname"].get(); int port = m_param_data.count("port") ? m_param_data["port"].get() : 1884; auto weak_self = std::weak_ptr(shared_from_this()); wxGetApp().CallAfter([=]() { MqttClient* mqtt_client = new MqttClient("mqtt://" + ip + ":" + std::to_string(port), "Snapmaker Orca"); std::string connect_msg = ""; if (mqtt_client->Connect(connect_msg)) { std::string sub_msg = "success"; if (mqtt_client->Subscribe("cloud/config/response", 1, sub_msg)) { mqtt_client->SetMessageCallback([weak_self, mqtt_client](const std::string& topic, const std::string& message) { auto self = weak_self.lock(); if (self) { if (topic == "cloud/config/response") { json response = json::parse(message); if (response.count("result")) { self->m_res_data = response["result"]; self->send_to_js(); self->finish_job(); std::string dc_msg = "success"; bool flag = mqtt_client->Disconnect(dc_msg); wxGetApp().CallAfter([mqtt_client]() { delete mqtt_client; }); return; } self->handle_general_fail(); } } }); // 构建请求消息 json req_body; req_body["jsonrpc"] = "2.0", req_body["method"] = "server.client_manager.request_pin_code"; req_body["params"] = json::object(); req_body["params"]["userid"] = userid; req_body["params"]["nickname"] = nickname; Moonraker_Mqtt::SequenceGenerator generator; req_body["id"] = generator.generate_seq_id(); // 发送请求 std::string pub_msg = "success"; if (mqtt_client->Publish("cloud/config/request", req_body.dump(), 1, pub_msg)) { return; } } return; } auto self = weak_self.lock(); if (self) { self->handle_general_fail(); } }); } else { handle_general_fail(); } } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineConnect_Instance::sw_connect_other_device() { try { auto weak_self = std::weak_ptr(shared_from_this()); wxGetApp().CallAfter([weak_self](){ auto config = &wxGetApp().preset_bundle->printers.get_edited_preset().config; config->set("print_host", ""); config->set("printhost_apikey", ""); auto dialog = SMPhysicalPrinterDialog(wxGetApp().mainframe->plater()->GetParent()); // todo int res = dialog.ShowModal(); dialog.EndModal(1); if (dialog.m_connected) { auto device_dialog = wxGetApp().get_web_device_dialog(); if (device_dialog) { device_dialog->EndModal(1); } } else { auto self = weak_self.lock(); if (self) { self->handle_general_fail(); } } }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineConnect_Instance::sw_test_connect() { try { if (m_param_data.count("ip")) { std::string protocol = "moonraker"; if (m_param_data.count("protcol")) { protocol = m_param_data["protocol"].get(); } std::string ip = m_param_data["ip"].get(); int port = -1; if (m_param_data.count("port") && m_param_data["port"].is_number_integer()) { port = m_param_data["port"].get(); } auto p_config = &(wxGetApp().preset_bundle->printers.get_edited_preset().config); PrintHostType type = PrintHostType::htMoonRaker; // todo : 增加输入与type的映射 p_config->option>("host_type")->value = type; p_config->set("print_host", ip + (port == -1 ? "" : std::to_string(port))); std::shared_ptr host(PrintHost::get_print_host(&wxGetApp().preset_bundle->printers.get_edited_preset().config)); if (!host) { // 错误处理 finish_job(); } else { if (m_work_thread.joinable()) m_work_thread.join(); m_work_thread = std::thread([this, host] { wxString msg; bool res = host->test(msg); if (res) { send_to_js(); } else { // 错误处理 m_status = 1; m_msg = msg.c_str(); send_to_js(); } finish_job(); }); } } else { // 错误处理 finish_job(); } } catch (const std::exception&) { handle_general_fail(); } } void SSWCP_MachineConnect_Instance::sw_connect() { } void SSWCP_MachineConnect_Instance::sw_get_connect_machine() { try { auto devices = wxGetApp().app_config->get_devices(); for (const auto& device : devices) { if (device.connected) { m_res_data = device; break; } } send_to_js(); finish_job(); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineConnect_Instance::sw_disconnect() { bool need_reload = m_param_data.count("need_reload") ? m_param_data["need_reload"].get() : true; std::string dev_id = m_param_data.count("dev_id") ? m_param_data["dev_id"] : ""; auto weak_self = std::weak_ptr(shared_from_this()); if (m_work_thread.joinable()) m_work_thread.join(); m_work_thread = std::thread([weak_self, need_reload, dev_id](){ auto self = weak_self.lock(); if (dev_id != "") { DeviceInfo info; if (wxGetApp().app_config->get_device_info(dev_id, info)) { if (info.connected == false) { if (self) { self->handle_general_fail(-3, dev_id + " is not connected before disconnect!"); return; } } } else { if (self) { self->handle_general_fail(-1, dev_id + " is not exist!"); return; } } } bool res = wxGetApp().sm_disconnect_current_machine(need_reload); m_first_connected = true; if (!res) { if (self) { self->m_status = 1; self->m_msg = "disconnected failed"; } } wxGetApp().CallAfter([]() { wxGetApp().app_config->clear_filament_extruder_map(); wxGetApp().preset_bundle->machine_filaments.clear(); wxGetApp().load_current_presets(); }); if (self) { self->send_to_js(); self->finish_job(); } }); } // SSWCP_SliceProject_Instance // Process machine find commands void SSWCP_SliceProject_Instance::process() { if (m_event_id != "") { json header; send_to_js(); m_header.clear(); m_header["event_id"] = m_event_id; } if (m_cmd == "sw_NewProject") { sw_NewProject(); } else if (m_cmd == "sw_OpenProject") { sw_OpenProject(); } else if (m_cmd == "sw_GetRecentProjects") { sw_GetRecentProjects(); } else if (m_cmd == "sw_OpenRecentFile") { sw_OpenRecentFile(); } else if (m_cmd == "sw_DeleteRecentFiles") { sw_DeleteRecentFiles(); } else if (m_cmd == "sw_SubscribeRecentFiles") { sw_SubscribeRecentFiles(); } else { handle_general_fail(); } } void SSWCP_SliceProject_Instance::sw_NewProject() { try { if (!m_param_data.count("preset_name") || m_param_data["preset_name"].get() == "") wxGetApp().request_open_project(""); else { std::string preset_name = m_param_data["preset_name"].get(); wxGetApp().CallAfter([this, preset_name]() { try { if (wxGetApp().get_tab(Preset::TYPE_PRINTER)->select_preset(preset_name)) { wxGetApp().plater()->new_project(); } else { MessageDialog msg_window(nullptr, "The machine model has not been installed!", _L("Create Failed"), wxICON_QUESTION | wxOK); msg_window.ShowModal(); } } catch (std::exception& e) { // 异常处理 } }); } send_to_js(); finish_job(); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_SliceProject_Instance::sw_OpenProject() { try { wxGetApp().request_open_project({}); send_to_js(); finish_job(); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_SliceProject_Instance::sw_GetRecentProjects() { try { json data; wxGetApp().mainframe->get_recent_projects(data, INT_MAX); m_res_data = data; send_to_js(); finish_job(); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_SliceProject_Instance::sw_OpenRecentFile() { try { if (m_param_data.count("path")) { std::string path = m_param_data["path"].get(); if (path != "") { wxGetApp().request_open_project(path); } else { handle_general_fail(); return; } } else { handle_general_fail(); return; } send_to_js(); finish_job(); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_SliceProject_Instance::sw_DeleteRecentFiles() { try { if (m_param_data.count("paths") && m_param_data["paths"].is_array()) { auto paths = m_param_data["paths"]; send_to_js(); if (paths.size() == 0) { wxGetApp().sm_request_remove_project(""); } else { for (size_t i = 0; i < paths.size(); ++i) { wxGetApp().sm_request_remove_project(paths[i].get()); } } } else { handle_general_fail(); return; } finish_job(); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_SliceProject_Instance::sw_SubscribeRecentFiles() { try { auto weak_self = std::weak_ptr(shared_from_this()); wxGetApp().m_recent_file_subscribers[m_webview] = weak_self; } catch (std::exception& e) { handle_general_fail(); } } // SSWCP_UserLogin_Instance void SSWCP_UserLogin_Instance::process() { if (m_event_id != "") { json header; send_to_js(); m_header.clear(); m_header["event_id"] = m_event_id; } if (m_cmd == "sw_UserLogin") { sw_UserLogin(); } else if (m_cmd == "sw_UserLogout") { sw_UserLogout(); } else if (m_cmd == "sw_GetUserLoginState") { sw_GetUserLoginState(); } else if (m_cmd == "sw_SubscribeUserLoginState") { sw_SubscribeUserLoginState(); } else if (m_cmd == UPDATE_PRIVACY_STATUS) { sw_SubUserUpdatePrivacy(); } else if (m_cmd == GET_PRIVACY_STATUS) { sw_GetUserUpdatePrivacy(); } else if (m_cmd == DOWNLOAD_FILE) { sw_DownloadFile(); } else if (m_cmd == CANCEL_DOWNLOAD) { sw_CancelDownload(); } else if (m_cmd == FILE_VIEW) { sw_FileView(); } else { handle_general_fail(); } } void SSWCP_UserLogin_Instance::sw_UserLogin() { try { send_to_js(); finish_job(); bool show = m_param_data.count("show") ? m_param_data["show"].get() : true; wxGetApp().CallAfter([show]() { wxGetApp().sm_request_login(show); }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_UserLogin_Instance::sw_UserLogout() { try { send_to_js(); wxGetApp().sm_request_user_logout(); finish_job(); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_UserLogin_Instance::sw_GetUserLoginState() { try { json data; auto pInfo = wxGetApp().sm_get_userinfo(); if (pInfo) { bool islogin = pInfo->is_user_login(); if (islogin) { data["status"] = "online"; data["nickname"] = pInfo->get_user_name(); data["icon"] = pInfo->get_user_icon_url(); data["token"] = pInfo->get_user_token(); data["userid"] = pInfo->get_user_id(); data["account"] = pInfo->get_user_account(); } else { data["status"] = "offline"; } m_res_data = data; send_to_js(); finish_job(); } else { handle_general_fail(); } } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_UserLogin_Instance::sw_GetUserUpdatePrivacy() { json data; auto isAgree = wxGetApp().app_config->get("app", PRIVACY_POLICY_FLAGS); bool isUserAgree = false; if (isAgree == "true") isUserAgree = true; data[PRIVACY_POLICY_FLAGS] = isUserAgree; m_res_data = data; send_to_js(); finish_job(); } void SSWCP_UserLogin_Instance::sw_DownloadFile() { try { std::string fileName = m_param_data.count("file_name") ? m_param_data["file_name"].get() : ""; std::string fileUrl = m_param_data.count("file_url") ? m_param_data["file_url"].get() : ""; if (fileUrl.empty() || fileName.empty()) { handle_general_fail(-1, "file_url and file_name are required"); return; } // Use Download Manager DownloadManager* download_mgr = wxGetApp().download_manager(); if (!download_mgr) { handle_general_fail(-1, "Download Manager not available"); return; } wxGetApp().mainframe->downloadOpenProject(fileUrl, fileName, ""); m_status = 0; m_msg = "Download started"; send_to_js(); finish_job(); } catch (std::exception& e) { handle_general_fail(-1, e.what()); } } void SSWCP_UserLogin_Instance::sw_DownloadFileEx() { try { std::string fileName = m_param_data.count("file_name") ? m_param_data["file_name"].get() : ""; std::string fileUrl = m_param_data.count("file_url") ? m_param_data["file_url"].get() : ""; if (fileUrl.empty() || fileName.empty()) { handle_general_fail(-1, "file_url and file_name are required"); return; } // Use Download Manager DownloadManager* download_mgr = wxGetApp().download_manager(); if (!download_mgr) { handle_general_fail(-1, "Download Manager not available"); return; } size_t task_id = download_mgr->start_wcp_download(fileUrl, fileName, shared_from_this(), true); json response; response["task_id"] = task_id; response["file_name"] = fileName; response["file_url"] = fileUrl; m_res_data = response; m_status = 0; m_msg = "Download started"; send_to_js(); } catch (std::exception& e) { handle_general_fail(-1, e.what()); } } void SSWCP_UserLogin_Instance::sw_CancelDownload() { try { size_t task_id = m_param_data.count("task_id") ? m_param_data["task_id"].get() : 0; if (task_id == 0) { handle_general_fail(-1, "task_id is required"); return; } DownloadManager* download_mgr = wxGetApp().download_manager(); if (!download_mgr) { handle_general_fail(-1, "WCP Download Manager not available"); return; } bool success = download_mgr->cancel_download(task_id); if (success) { json response; response["task_id"] = task_id; response["canceled"] = true; m_res_data = response; m_status = 0; m_msg = "Download canceled"; } else { handle_general_fail(-1, "Failed to cancel download or task not found"); return; } send_to_js(); finish_job(); } catch (std::exception& e) { handle_general_fail(-1, e.what()); } } void SSWCP_UserLogin_Instance::sw_FileView() { try { std::string file_path = m_param_data.count("file_path") ? m_param_data["file_path"].get() : ""; wxFileName file(file_path); if (!file.FileExists()) { handle_general_fail(); //wxMessageBox(wxT("file not exsit"), wxT("tips"), wxOK | wxICON_WARNING); return; } std::weak_ptr weak_self = shared_from_this(); wxGetApp().CallAfter([file_path, weak_self]() { auto self = weak_self.lock(); if (!self) { return; } //open file in folder desktop_open_any_folderEx(file_path); self->send_to_js(); self->finish_job(); }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_UserLogin_Instance::sw_SubUserUpdatePrivacy() { try { std::weak_ptr weak_ptr = shared_from_this(); wxGetApp().m_user_update_privacy_subscribers[m_webview] = weak_ptr; } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_UserLogin_Instance::sw_SubscribeUserLoginState() { try { std::weak_ptr weak_ptr = shared_from_this(); wxGetApp().m_user_login_subscribers[m_webview] = weak_ptr; } catch (std::exception& e) { handle_general_fail(); } } // SSWCP_MachineManage_Instance void SSWCP_MachineManage_Instance::process() { if (m_event_id != "") { json header; send_to_js(); m_header.clear(); m_header["event_id"] = m_event_id; } if (m_cmd == "sw_GetLocalDevices") { sw_GetLocalDevices(); } else if (m_cmd == "sw_AddDevice") { sw_AddDevice(); } else if (m_cmd == "sw_SubscribeLocalDevices") { sw_SubscribeLocalDevices(); } else if (m_cmd == "sw_RenameDevice") { sw_RenameDevice(); } else if (m_cmd == "sw_SwitchModel") { sw_SwitchModel(); } else if (m_cmd == "sw_DeleteDevices") { sw_DeleteDevices(); } else { handle_general_fail(); } } void SSWCP_MachineManage_Instance::sw_GetLocalDevices() { try { auto devices = wxGetApp().app_config->get_devices(); m_res_data = devices; send_to_js(); finish_job(); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineManage_Instance::sw_SubscribeLocalDevices() { try { auto self = shared_from_this(); wxGetApp().m_device_card_subscribers[m_webview] = self; } catch (std::exception& e) { handle_general_fail(); } } // SSWCP_PageStateChange_Instance void SSWCP_PageStateChange_Instance::process() { if (m_event_id != "") { json header; send_to_js(); m_header.clear(); m_header["event_id"] = m_event_id; } if (m_cmd == "sw_SubscribePageStateChange") { sw_SubscribePageStateChange(); } else if (m_cmd == "sw_UnsubscribePageStateChange") { sw_UnsubscribePageStateChange(); } else { handle_general_fail(); } } void SSWCP_PageStateChange_Instance::sw_SubscribePageStateChange() { try { auto self = shared_from_this(); wxGetApp().m_page_state_subscribers[m_webview] = self; } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_PageStateChange_Instance::sw_UnsubscribePageStateChange() { try { auto& page_state_map = wxGetApp().m_page_state_subscribers; std::string event_id = m_param_data.count("event_id") ? m_param_data["event_id"].get() : ""; for (auto iter = page_state_map.begin(); iter != page_state_map.end();) { if (iter->first == m_webview) { auto ptr = iter->second.lock(); if (ptr) { if (event_id == "" || (event_id != "" && event_id == ptr->m_event_id)) { iter = page_state_map.erase(iter); } else { iter++; } } else { iter = page_state_map.erase(iter); } } else { iter++; } } send_to_js(); finish_job(); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineManage_Instance::sw_AddDevice() { try { wxGetApp().CallAfter([] { if (wxGetApp().web_device_dialog) delete wxGetApp().web_device_dialog; wxGetApp().web_device_dialog = new WebDeviceDialog; wxGetApp().web_device_dialog->run(); }); send_to_js(); finish_job(); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineManage_Instance::sw_RenameDevice() { try { if (m_param_data.count("dev_id") && m_param_data.count("dev_name")) { std::string dev_id = m_param_data["dev_id"].get(); std::string dev_name = m_param_data["dev_name"].get(); DeviceInfo info; wxGetApp().app_config->get_device_info(dev_id, info); info.dev_name = dev_name; wxGetApp().app_config->save_device_info(info); wxGetApp().CallAfter([] { // wcp订阅 json data = wxGetApp().app_config->get_devices(); wxGetApp().device_card_notify(data); }); send_to_js(); finish_job(); } else { handle_general_fail(); } } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineManage_Instance::sw_DeleteDevices() { try { if (m_param_data.count("dev_ids") && m_param_data["dev_ids"].is_array()) { auto ids = m_param_data["dev_ids"]; if (ids.size() == 0) { auto devices = wxGetApp().app_config->get_devices(); for (size_t i = 0; i < devices.size(); ++i) { if (devices[i].connected) { std::shared_ptr current_host; wxGetApp().get_connect_host(current_host); if (current_host && current_host->get_sn() == devices[i].sn) { wxString msg = ""; json param; current_host->disconnect(msg, param); } } } wxGetApp().app_config->clear_device_info(); } else { for (size_t i = 0; i < ids.size(); ++i) { std::string dev_id = ids[i].get(); DeviceInfo info; if (wxGetApp().app_config->get_device_info(dev_id, info)) { if (info.connected) { std::shared_ptr current_host; wxGetApp().get_connect_host(current_host); wxString msg = ""; json param; current_host->disconnect(msg, param); } } wxGetApp().app_config->remove_device_info(dev_id); } } wxGetApp().CallAfter([] { // wcp订阅 json data = wxGetApp().app_config->get_devices(); wxGetApp().device_card_notify(data); }); send_to_js(); finish_job(); } else { handle_general_fail(); } } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MachineManage_Instance::sw_SwitchModel() { try { if (m_param_data.count("dev_id")) { std::string dev_id = m_param_data["dev_id"].get(); send_to_js(); wxGetApp().CallAfter([dev_id]() { WebPresetDialog dialog(&wxGetApp()); dialog.m_device_id = dev_id; dialog.run(); }); finish_job(); } else { handle_general_fail(); } } catch (std::exception& e) { handle_general_fail(); } } // SSWCP_MqttAgent_Instance std::unordered_map>> SSWCP_MqttAgent_Instance::m_mqtt_engine_map; std::mutex SSWCP_MqttAgent_Instance::m_engine_map_mtx; std::map, std::string> SSWCP_MqttAgent_Instance::m_subscribe_map; std::map, std::weak_ptr> SSWCP_MqttAgent_Instance::m_subscribe_instance_map; WebPresetDialog* SSWCP_MqttAgent_Instance::m_dialog = nullptr; void SSWCP_MqttAgent_Instance::process() { if (m_cmd == "sw_create_mqtt_client") { sw_create_mqtt_client(); } else if (m_cmd == "sw_mqtt_connect") { sw_mqtt_connect(); } else if (m_cmd == "sw_mqtt_disconnect") { sw_mqtt_disconnect(); } else if (m_cmd == "sw_mqtt_subscribe") { sw_mqtt_subscribe(); } else if (m_cmd == "sw_mqtt_unsubscribe") { sw_mqtt_unsubscribe(); } else if (m_cmd == "sw_mqtt_publish") { sw_mqtt_publish(); } else if (m_cmd == "sw_mqtt_set_engine") { sw_mqtt_set_engine(); } else { handle_general_fail(); } } // 校验mqtt引擎id bool SSWCP_MqttAgent_Instance::validate_id(const std::string& id) { bool flag = true; m_engine_map_mtx.lock(); if (!m_mqtt_engine_map.count(m_webview)) { flag = false; } else { flag = m_mqtt_engine_map[m_webview].first == id; } m_engine_map_mtx.unlock(); return flag; } // webview析构回调 void SSWCP_MqttAgent_Instance::set_Instance_illegal() { SSWCP_Instance::set_Instance_illegal(); clean_current_engine(); } // 清空当前mqtt实例 void SSWCP_MqttAgent_Instance::clean_current_engine() { //清除该实例的所有订阅 for (auto iter = m_subscribe_map.begin(); iter != m_subscribe_map.end();) { if (iter->first.second == m_webview) { iter = m_subscribe_map.erase(iter); } else { iter++; } } for (auto iter = m_subscribe_instance_map.begin(); iter != m_subscribe_instance_map.end();) { if (iter->first.second == m_webview) { iter = m_subscribe_instance_map.erase(iter); } else { iter++; } } m_engine_map_mtx.lock(); m_mqtt_engine_map.erase(m_webview); m_engine_map_mtx.unlock(); } // mqtt静态消息回调 void SSWCP_MqttAgent_Instance::mqtt_msg_cb(const std::string& topic, const std::string& payload, void* client) { auto& wcp_loger = GUI::WCP_Logger::getInstance(); BOOST_LOG_TRIVIAL(info) << "[Mqtt_Agent] 收到MQTT消息,主题: " << topic << ", 载荷长度: " << payload.length(); wcp_loger.add_log("收到MQTTS消息,主题: " + topic + ", 载荷长度: " + std::to_string(payload.length()), false, "", "Mqtt_Agent", "info"); try { wxGetApp().CallAfter([topic, payload, client]() { for (const auto& item : SSWCP_MqttAgent_Instance::m_subscribe_map) { std::string id_topic = item.second; std::string target_topic = topic; if (id_topic.find("+") != std::string::npos) { id_topic = id_topic.substr(id_topic.find_first_of("/")); target_topic = topic.substr(topic.find_first_of("/")); } if (id_topic == target_topic) { if (SSWCP_MqttAgent_Instance::m_subscribe_instance_map.count(item.first)) { auto& instance = SSWCP_MqttAgent_Instance::m_subscribe_instance_map[item.first]; if (auto self = instance.lock()) { auto mqtt_self = dynamic_pointer_cast(self); if (mqtt_self && (void*)(mqtt_self->get_current_engine().get()) == client) { self->m_res_data["topic"] = topic; self->m_res_data["data"] = payload; self->send_to_js(); } } } else { return; } } } }); } catch (std::exception& e) { BOOST_LOG_TRIVIAL(error) << "[Moonraker_Mqtt] 处理MQTT消息异常: " << e.what(); wcp_loger.add_log("处理MQTTS消息异常: " + std::string(e.what()), false, "", "Moonraker_Mqtt", "error"); } } // 创建mqtt实例 void SSWCP_MqttAgent_Instance::sw_create_mqtt_client() { try { // 解析参数 std::string server_address = ""; std::string clientId = ""; std::string ca = ""; std::string cert = ""; std::string key = ""; std::string username = ""; std::string password = ""; bool clean_session = false; if (m_param_data.count("server_address") || !m_param_data["server_address"].is_string()) { server_address = m_param_data["server_address"].get(); if (server_address == "") { handle_general_fail(-1, "the value of param [server_address] is illegal"); return; } } else { handle_general_fail(-1, "param [server_address] is required or wrong type"); return; } if (m_param_data.count("clientId") || !m_param_data["clientId"].is_string()) { clientId = m_param_data["clientId"].get(); if (clientId == "") { handle_general_fail(-1, "the value of param [clientId] is illegal"); return; } } else { handle_general_fail(-1, "param [clientId] is required or wroing type"); return; } ca = m_param_data.count("ca") ? m_param_data["ca"].get() : ""; cert = m_param_data.count("cert") ? m_param_data["cert"].get() : ""; key = m_param_data.count("key") ? m_param_data["key"].get() : ""; clean_session = m_param_data.count("clean_session") ? m_param_data["clean_session"].get() : false; username = m_param_data.count("username") ? m_param_data["username"].get() : ""; password = m_param_data.count("password") ? m_param_data["password"].get() : ""; // 确认mqtt连接类型,并创建实例 std::shared_ptr client = nullptr; std::string type = "mqtt"; if (ca != "" && cert != "" && key != "") { type = "mqtts"; client.reset(new MqttClient(server_address, clientId, ca, cert, key, username, password, clean_session)); }else{ client.reset(new MqttClient(server_address, clientId, username, password, clean_session)); } if (client == nullptr) { // 创建失败 handle_general_fail(-1, "create instance failed"); return; } // 清空当前m_clinet的订阅列表 auto ptr = get_current_engine(); if (!ptr) { ptr.reset(); } clean_current_engine(); // 替换新引擎 bool flag = set_current_engine({std::to_string(int64_t(client.get())), client}); if (!flag) { handle_general_fail(-1, "create failed"); return; } // 绑定静态回调 client->SetMessageCallback(SSWCP_MqttAgent_Instance::mqtt_msg_cb); m_res_data["type"] = type; m_res_data["id"] = std::to_string(int64_t(get_current_engine().get())); send_to_js(); finish_job(); } catch (std::exception& e) { handle_general_fail(); } } // mqtt引擎建立连接 void SSWCP_MqttAgent_Instance::sw_mqtt_connect() { try { if (!m_param_data.count("id") || !m_param_data["id"].is_string()) { handle_general_fail(-1, "param [id] is required or wrong type"); Slic3r::sentryReportLog(Slic3r::SENTRY_LOG_ERROR, std::string("device_connect param [id] is required or wrong type"), DEVICE_CONNECT_ERR); return; } std::string id = m_param_data["id"].get(); if (!validate_id(id)) { handle_general_fail(-1, "id is illegal"); Slic3r::sentryReportLog(Slic3r::SENTRY_LOG_ERROR, std::string("device_connect id is illegal"),DEVICE_CONNECT_ERR); return; } std::weak_ptr weak_ptr = shared_from_this(); auto engine = get_current_engine(); if (m_work_thread.joinable()) m_work_thread.join(); m_work_thread = std::thread([weak_ptr, engine]() { if (!weak_ptr.lock()) { return; } auto self = std::dynamic_pointer_cast(weak_ptr.lock()); engine->SetConnectionFailureCallback([engine]() { std::string msg = ""; engine->Disconnect(msg); }); std::string msg; bool flag = engine->Connect(msg); wxGetApp().CallAfter([weak_ptr, msg, flag]() { auto self = weak_ptr.lock(); if (self) { if (flag) { self->m_msg = msg; self->send_to_js(); self->finish_job(); } else { self->handle_general_fail(-1, msg); } } }); }); } catch (std::exception& e) { handle_general_fail(); } } // mqtt引擎断开连接 void SSWCP_MqttAgent_Instance::sw_mqtt_disconnect() { try { if (!m_param_data.count("id") || !m_param_data["id"].is_string()) { handle_general_fail(-1, "param [id] is required or wrong type"); return; } std::string id = m_param_data["id"].get(); if (!validate_id(id)) { handle_general_fail(-1, "id is illegal"); return; } std::weak_ptr weak_ptr = shared_from_this(); auto engine = get_current_engine(); if (m_work_thread.joinable()) m_work_thread.join(); m_work_thread = std::thread([weak_ptr, engine]() { if (!weak_ptr.lock()) { return; } auto self = std::dynamic_pointer_cast(weak_ptr.lock()); std::string msg = "success"; bool flag = engine->Disconnect(msg); wxGetApp().CallAfter([weak_ptr, msg, flag]() { auto self = weak_ptr.lock(); if (self) { if (flag) { self->m_msg = msg; self->send_to_js(); self->finish_job(); } else { self->handle_general_fail(-1, msg); } } }); }); } catch (std::exception& e) { handle_general_fail(); } } // 订阅topic void SSWCP_MqttAgent_Instance::sw_mqtt_subscribe() { try { if (!m_param_data.count("id") || !m_param_data["id"].is_string()) { handle_general_fail(-1, "param [id] is required or wrong type"); Slic3r::sentryReportLog(Slic3r::SENTRY_LOG_ERROR, std::string("device_subscribe param [id] is required or wrong type"), DEVICE_SUBSCRIBE_ERR); return; } std::string id = m_param_data["id"].get(); if (!validate_id(id)) { handle_general_fail(-1, "id is illegal"); Slic3r::sentryReportLog(Slic3r::SENTRY_LOG_ERROR, std::string("device_subscribe id is illegal with:")+id,DEVICE_SUBSCRIBE_ERR); return; } if (m_event_id == "") { handle_general_fail(-1, "event_id is required or wrong type"); Slic3r::sentryReportLog(Slic3r::SENTRY_LOG_ERROR, std::string("device_subscribe event_id is required or wrong type with:") + id,DEVICE_SUBSCRIBE_ERR); return; } std::string event_id = m_event_id; if (!m_param_data.count("topic") || !m_param_data["topic"].is_string()) { handle_general_fail(-1, "param [topic] is required or wrong type"); Slic3r::sentryReportLog(Slic3r::SENTRY_LOG_ERROR, std::string("device_subscribe param [topic] is required or wrong type"),DEVICE_SUBSCRIBE_ERR); return; } std::string topic = m_param_data["topic"].get(); m_subscribe_map[{event_id, m_webview}] = topic; m_subscribe_instance_map[{event_id, m_webview}] = shared_from_this(); if (!m_param_data.count("qos") || !m_param_data["qos"].is_number()) { handle_general_fail(-1, "param [qos] is required or wrong type"); Slic3r::sentryReportLog(Slic3r::SENTRY_LOG_ERROR, std::string("device_subscribe param [qos] is required or wrong type"), DEVICE_SUBSCRIBE_ERR); return; } int qos = m_param_data["qos"].get(); std::weak_ptr weak_ptr = shared_from_this(); auto engine = get_current_engine(); if (m_work_thread.joinable()) m_work_thread.join(); m_work_thread = std::thread([weak_ptr, engine, topic, qos]() { if (!weak_ptr.lock()) { return; } auto self = std::dynamic_pointer_cast(weak_ptr.lock()); std::string msg = "success"; bool flag = engine->Subscribe(topic, qos, msg); wxGetApp().CallAfter([weak_ptr, msg, flag]() { auto self = weak_ptr.lock(); if (self) { if (flag) { // 回复后, 设置event_id, 长期保留对象 if (self->m_event_id != "") { self->m_msg = msg; self->send_to_js(); json header; self->m_header.clear(); self->m_header["event_id"] = self->m_event_id; } else { self->handle_general_fail(-1, "event_id is null"); } } else { self->handle_general_fail(-1, msg); } } }); }); } catch (std::exception& e) { handle_general_fail(); } } // 取消订阅 void SSWCP_MqttAgent_Instance::sw_mqtt_unsubscribe() { try { if (!m_param_data.count("id") || !m_param_data["id"].is_string()) { handle_general_fail(-1, "param [id] is required or wrong type"); return; } std::string id = m_param_data["id"].get(); if (!validate_id(id)) { handle_general_fail(-1, "id is illegal"); return; } if (!m_param_data.count("topic") || !m_param_data["topic"].is_string()) { handle_general_fail(-1, "param [topic] is required or wrong type"); return; } std::string topic = m_param_data["topic"].get(); // 维护订阅topic表和eventid实例表 for (auto iter = m_subscribe_map.begin(); iter != m_subscribe_map.end(); ) { if (iter->second == topic) { if (m_subscribe_instance_map.count(iter->first)) { m_subscribe_instance_map.erase(iter->first); } iter = m_subscribe_map.erase(iter); } else { ++iter; } } std::weak_ptr weak_ptr = shared_from_this(); auto engine = get_current_engine(); if (m_work_thread.joinable()) m_work_thread.join(); m_work_thread = std::thread([weak_ptr, engine, topic]() { if (!weak_ptr.lock()) { return; } auto self = std::dynamic_pointer_cast(weak_ptr.lock()); std::string msg = "success"; bool flag = engine->Unsubscribe(topic, msg); wxGetApp().CallAfter([weak_ptr, msg, flag]() { auto self = weak_ptr.lock(); if (self) { if (flag) { self->m_msg = msg; self->send_to_js(); self->finish_job(); } else { self->handle_general_fail(-1, msg); } } }); }); } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MqttAgent_Instance::sw_mqtt_set_engine() { try { if (!m_param_data.count("engine_id") || !m_param_data["engine_id"].is_string()) { handle_general_fail(-1, "param [engine_id] is required or wrong type"); Slic3r::sentryReportLog(Slic3r::SENTRY_LOG_ERROR, std::string("device_set_engine param [engine_id] is required or wrong type"),DEVICE_SET_ENGINE_ERR); return; } std::string engine_id = m_param_data["engine_id"].get(); if (!validate_id(engine_id)) { handle_general_fail(-1, "id is illegal"); Slic3r::sentryReportLog(Slic3r::SENTRY_LOG_ERROR, std::string("device_set_engine id is illegal with:") + engine_id, DEVICE_SET_ENGINE_ERR); return; } if (!m_param_data.count("ip") || !m_param_data["ip"].is_string()) { handle_general_fail(-1, "param [ip] is required or wrong type"); Slic3r::sentryReportLog(Slic3r::SENTRY_LOG_ERROR, std::string("device_set_engine param [ip] is required or wrong type"),DEVICE_SET_ENGINE_ERR); return; } std::string ip = m_param_data["ip"].get(); if (!m_param_data.count("port") || !m_param_data["port"].is_number()) { handle_general_fail(-1, "param [port] is required or wrong type"); Slic3r::sentryReportLog(Slic3r::SENTRY_LOG_ERROR, std::string("device_set_engine param [port] is required or wrong type"), DEVICE_SET_ENGINE_ERR); return; } bool reload_device_view = m_param_data.count("need_reload") ? m_param_data["need_reload"].get() : true; int port = m_param_data["port"].get(); auto config = wxGetApp().preset_bundle->printers.get_edited_preset().config; PrintHostType type = PrintHostType::htMoonRaker_mqtt; // todo : 增加输入与type的映射 config.option>("host_type")->value = type; config.set("print_host", ip + (port == -1 ? "" : ":" + std::to_string(port))); std::shared_ptr tmp_host(PrintHost::get_print_host(&config)); wxGetApp().set_connect_host(tmp_host); wxGetApp().set_host_config(config); std::shared_ptr host = dynamic_pointer_cast(tmp_host); if (host) { auto engine = get_current_engine(); if (engine == nullptr) { handle_general_fail(-1, "invalid engine"); Slic3r::sentryReportLog(Slic3r::SENTRY_LOG_ERROR, std::string("device_set_engine invalid engine"),DEVICE_SET_ENGINE_ERR); return; } BOOST_LOG_TRIVIAL(info) << "[SSWCP_MqttAgent_Instance] 检查引擎连接状态..."; if (!engine->CheckConnected()) { BOOST_LOG_TRIVIAL(error) << "[SSWCP_MqttAgent_Instance] 引擎连接状态异常"; handle_general_fail(-1, "engine connection lost"); Slic3r::sentryReportLog(Slic3r::SENTRY_LOG_ERROR, std::string("device_set_engine engine connection lost"), DEVICE_SET_ENGINE_ERR); return; } BOOST_LOG_TRIVIAL(info) << "[SSWCP_MqttAgent_Instance] 引擎连接状态正常"; std::string msg = "success"; BOOST_LOG_TRIVIAL(info) << "[SSWCP_MqttAgent_Instance] 设置引擎..."; bool flag = host->set_engine(engine, msg); BOOST_LOG_TRIVIAL(info) << "[SSWCP_MqttAgent_Instance] 引擎设置完成, flag: " << flag; if (flag) { if (m_param_data.count("ip")) { std::string ip = m_param_data["ip"].get(); int port = -1; if (m_param_data.count("port") && m_param_data["port"].is_number_integer()) { port = m_param_data["port"].get(); } // test if (port == -1 || port == 1883) { port = 1884; } json connect_params; if (m_param_data.count("sn") && m_param_data["sn"].is_string()) { connect_params["sn"] = m_param_data["sn"].get(); host->m_sn_mtx.lock(); host->m_sn = m_param_data["sn"].get(); host->m_sn_mtx.unlock(); } else { handle_general_fail(-1, "param [sn] is required or wrong type"); return; } // 序列化参数 if (m_param_data.count("code")){ connect_params["code"] = m_param_data["code"]; } if (m_param_data.count("ca")) { connect_params["ca"] = m_param_data["ca"]; host->m_ca = m_param_data["ca"].get(); } if (m_param_data.count("cert")) { connect_params["cert"] = m_param_data["cert"]; host->m_cert = m_param_data["cert"].get(); } if (m_param_data.count("key")) { connect_params["key"] = m_param_data["key"]; host->m_key = m_param_data["key"].get(); } if (m_param_data.count("user")) { connect_params["user"] = m_param_data["user"]; host->m_user_name = m_param_data["user"].get(); } if (m_param_data.count("password")) { connect_params["password"] = m_param_data["password"]; host->m_password = m_param_data["password"].get(); } if (m_param_data.count("port")) { connect_params["port"] = m_param_data["port"]; host->m_port = m_param_data["port"].get(); } if (m_param_data.count("clientId")) { connect_params["clientId"] = m_param_data["clientId"]; host->m_client_id = m_param_data["clientId"].get(); } std::string link_mode = m_param_data.count("link_mode") ? m_param_data["link_mode"] : "lan"; connect_params["link_mode"] = link_mode; std::string id = m_param_data.count("id") ? m_param_data["id"].get() : ""; std::string userid = m_param_data.count("userid") ? m_param_data["userid"].get() : ""; if (!host) { handle_general_fail(-1, "host created failed"); Slic3r::sentryReportLog(Slic3r::SENTRY_LOG_ERROR, std::string("device_set_engine host created failed"), DEVICE_SET_ENGINE_ERR); return; } else { auto weak_self = std::weak_ptr(shared_from_this()); // 设置断联回调 if (m_work_thread.joinable()) m_work_thread.join(); m_work_thread = std::thread([weak_self, host, connect_params, link_mode, id, userid, reload_device_view] { auto self = weak_self.lock(); wxString msg = ""; json params; host->set_connection_lost([]() { wxGetApp().CallAfter([]() { SSWCP_Instance::m_first_connected = true; wxGetApp().app_config->clear_filament_extruder_map(); wxGetApp().preset_bundle->machine_filaments.clear(); wxGetApp().load_current_presets(); }); wxGetApp().CallAfter([]() { wxGetApp().app_config->set("use_new_connect", "false"); auto p_config = &(wxGetApp().preset_bundle->printers.get_edited_preset().config); p_config->set("print_host", ""); std::shared_ptr ptr = nullptr; wxGetApp().get_connect_host(ptr); if (ptr) { wxString disconn_msg = ""; json disconn_param; ptr->disconnect(disconn_msg, disconn_param); } wxGetApp().set_connect_host(nullptr); auto devices = wxGetApp().app_config->get_devices(); for (size_t i = 0; i < devices.size(); ++i) { if (devices[i].connected) { devices[i].connected = false; wxGetApp().app_config->save_device_info(devices[i]); break; } } // 更新卡片 json param; param["command"] = "local_devices_arrived"; param["sequece_id"] = "10001"; param["data"] = devices; std::string logout_cmd = param.dump(); wxString strJS = wxString::Format("window.postMessage(%s)", logout_cmd); GUI::wxGetApp().run_script(strJS); // wcp订阅 json data = devices; wxGetApp().device_card_notify(data); MessageDialog msg_window(nullptr, " " + _L("Connection has been disconnected and recovery attempt failed. Please reconnect.") + "\n", _L("Machine Disconnected"), wxICON_QUESTION | wxOK); msg_window.ShowModal(); wxGetApp().set_connect_host(nullptr); wxGetApp().mainframe->plater()->sidebar().update_all_preset_comboboxes(); }); }); bool res = true; std::string ip_port = host->get_host(); if (res) { int pos = ip_port.find(':'); std::string ip = ip_port; if (pos != std::string::npos) { ip = ip_port.substr(0, pos); } // 更新其他设备连接状态为断开 auto devices = wxGetApp().app_config->get_devices(); for (size_t i = 0; i < devices.size(); ++i) { if (devices[i].connected) { devices[i].connected = false; wxGetApp().app_config->save_device_info(devices[i]); break; } } // 查询机器的机型和喷嘴信息 std::string machine_type = ""; std::vector nozzle_diameters; std::string device_name = ""; std::shared_ptr host = nullptr; wxGetApp().get_connect_host(host); // 设置sn if (SSWCP::query_machine_info(host, machine_type, nozzle_diameters, device_name) && machine_type != "") { wxGetApp().CallAfter([ip, host, link_mode, machine_type, connect_params, nozzle_diameters, device_name, id, userid, reload_device_view]() { // 查询成功 DeviceInfo info; info.ip = ip; info.dev_id = host->get_sn() != "" ? host->get_sn() : ip; info.dev_name = ip; info.connected = true; info.link_mode = link_mode; info.id = id; info.userid = userid; ; info.model_name = machine_type; info.protocol = int(PrintHostType::htMoonRaker_mqtt); if (connect_params.count("sn") && connect_params["sn"].is_string()) { std::string sn = host->get_sn(); info.sn = connect_params["sn"].get(); if (sn != "" && sn != info.sn) { info.sn = sn; } info.dev_name = info.sn != "" ? info.sn : info.dev_name; info.dev_id = info.sn != "" ? info.sn : info.ip; } if (device_name != "") { info.dev_name = device_name; } size_t vendor_pos = machine_type.find_first_of(" "); if (vendor_pos != std::string::npos) { std::string vendor = machine_type.substr(0, vendor_pos); std::string machine_cover = LOCALHOST_URL + std::to_string(wxGetApp().m_page_http_server.get_port()) + "/profiles/" + vendor + "/" + machine_type + "_cover.png"; info.img = machine_cover; } auto auth_info = host->get_auth_info(); try { info.ca = /* auth_info["ca"]*/ ""; info.cert = /* auth_info["cert"]*/ ""; info.key = /* auth_info["key"]*/ ""; info.user = auth_info["user"]; info.password = auth_info["password"]; info.port = auth_info["port"]; info.clientId = auth_info["clientId"]; } catch (std::exception& e) {} DeviceInfo query_info; bool exist = wxGetApp().app_config->get_device_info(info.dev_id, query_info); if (nozzle_diameters.empty()) { if (exist) { query_info.connected = true; wxGetApp().app_config->save_device_info(query_info); } else { wxGetApp().app_config->save_device_info(info); MessageDialog msg_window(nullptr, ip + " " + _L("The target machine model has been detected as") + "" + machine_type + "\n" + _L("Please bind the nozzle information") + "\n", _L("Nozzle Bind"), wxICON_QUESTION | wxOK); msg_window.ShowModal(); m_dialog->m_bind_nozzle = true; m_dialog->m_device_id = ip; m_dialog->run(); } } else { info.nozzle_sizes = nozzle_diameters; info.preset_name = machine_type + " (" + nozzle_diameters[0] + " nozzle)"; wxGetApp().app_config->save_device_info(info); m_dialog->m_device_id = ip; // 检查是否该预设已经选入系统 { std::lock_guard lock(m_ProfileJson_mutex); int nModel = m_ProfileJson["model"].size(); bool isFind = false; for (int m = 0; m < nModel; m++) { if (m_ProfileJson["model"][m]["model"].get() == info.model_name) { // 绑定的预设已被选入系统 isFind = true; std::string nozzle_selected = m_ProfileJson["model"][m]["nozzle_selected"] .get(); std::string se_nozz_selected = nozzle_diameters[0]; if (nozzle_selected.find(se_nozz_selected) == std::string::npos) { nozzle_selected += ";" + se_nozz_selected; m_ProfileJson["model"][m]["nozzle_selected"] = nozzle_selected; } break; } } if (!isFind) { json new_item; new_item["vendor"] = "Snapmaker"; new_item["model"] = info.model_name; new_item["nozzle_selected"] = nozzle_diameters[0]; m_ProfileJson["model"].push_back(new_item); } } wxGetApp().mainframe->plater()->sidebar().update_all_preset_comboboxes(false); m_dialog->SaveProfile(); bool flag = false; m_dialog->apply_config(wxGetApp().app_config, wxGetApp().preset_bundle, wxGetApp().preset_updater, flag); wxGetApp().update_mode(); } }); } else { wxGetApp().CallAfter([connect_params, ip, host, link_mode, id, userid]() { // 是否为连接过的设备 DeviceInfo query_info; std::string dev_id = connect_params.count("sn") ? connect_params["sn"].get() : ip; if (wxGetApp().app_config->get_device_info(dev_id, query_info)) { query_info.connected = true; wxGetApp().app_config->save_device_info(query_info); } else { auto machine_ip_type = MachineIPType::getInstance(); if (machine_ip_type) { std::string machine_type = ""; if (machine_ip_type->get_machine_type(ip, machine_type)) { // 已经发现过的机型信息 // test if (machine_type == "lava" || machine_type == "Snapmaker test") { machine_type = "Snapmaker U1"; } DeviceInfo info; host->get_auth_info(); auto auth_info = host->get_auth_info(); try { info.ca = auth_info["ca"]; info.cert = auth_info["cert"]; info.key = auth_info["key"]; info.user = auth_info["user"]; info.password = auth_info["password"]; info.port = auth_info["port"]; info.clientId = auth_info["clientId"]; } catch (std::exception& e) {} info.ip = ip; info.dev_id = dev_id; info.dev_name = ip; info.connected = true; info.model_name = machine_type; info.protocol = int(PrintHostType::htMoonRaker_mqtt); info.link_mode = link_mode; info.id = id; info.userid = userid; if (connect_params.count("sn") && connect_params["sn"].is_string()) { info.sn = connect_params["sn"].get(); info.dev_name = info.sn != "" ? info.sn : info.dev_name; info.dev_id = info.sn != "" ? info.sn : info.dev_name; } size_t vendor_pos = machine_type.find_first_of(" "); if (vendor_pos != std::string::npos) { std::string vendor = machine_type.substr(0, vendor_pos); std::string machine_cover = LOCALHOST_URL + std::to_string( wxGetApp().m_page_http_server.get_port()) + "/profiles/" + vendor + "/" + machine_type + "_cover.png"; info.img = machine_cover; } wxGetApp().app_config->save_device_info(info); // todo 绑定喷嘴 MessageDialog msg_window(nullptr, ip + " " + _L("The target machine model has been detected as") + " " + machine_type + "\n" + _L("Please bind the nozzle information") + "\n", _L("Nozzle Bind"), wxICON_QUESTION | wxOK); msg_window.ShowModal(); m_dialog->m_bind_nozzle = true; m_dialog->m_device_id = ip; m_dialog->run(); if (info.nozzle_sizes.empty()) info.nozzle_sizes.push_back("0.4"); info.preset_name = machine_type + " (" + info.nozzle_sizes[0] + " nozzle)"; wxGetApp().app_config->save_device_info(info); } else { DeviceInfo info; auto auth_info = host->get_auth_info(); try { info.ca = auth_info["ca"]; info.cert = auth_info["cert"]; info.key = auth_info["key"]; info.user = auth_info["user"]; info.password = auth_info["password"]; info.port = auth_info["port"]; info.clientId = auth_info["clientId"]; } catch (std::exception& e) {} info.ip = ip; info.dev_id = dev_id; info.dev_name = ip; info.connected = true; info.link_mode = link_mode; info.id = id; info.userid = id; info.protocol = int(PrintHostType::htMoonRaker_mqtt); if (connect_params.count("sn") && connect_params["sn"].is_string()) { info.sn = connect_params["sn"].get(); info.dev_name = info.sn != "" ? info.sn : info.dev_name; info.dev_id = info.sn != "" ? info.sn : info.dev_name; } wxGetApp().app_config->save_device_info(info); MessageDialog msg_window( nullptr, ip + " " + _L("The target machine model has not been detected. Please bind manually."), _L("Machine Bind"), wxICON_QUESTION | wxOK); msg_window.ShowModal(); m_dialog->m_device_id = info.dev_id; m_dialog->run(); } } } }); } wxGetApp().CallAfter([weak_self, reload_device_view]() { // 更新首页设备卡片 auto devices = wxGetApp().app_config->get_devices(); json param; param["command"] = "local_devices_arrived"; param["sequece_id"] = "10001"; param["data"] = devices; std::string logout_cmd = param.dump(); wxString strJS = wxString::Format("window.postMessage(%s)", logout_cmd); GUI::wxGetApp().run_script(strJS); // wcp订阅 json data = devices; wxGetApp().device_card_notify(data); /*MessageDialog msg_window(nullptr, ip + " " + _L("connected sucessfully !") + "\n", _L("Machine Connected"), wxICON_QUESTION | wxOK); msg_window.ShowModal();*/ auto dialog = wxGetApp().get_web_device_dialog(); if (dialog) { dialog->EndModal(1); } wxGetApp().app_config->set("use_new_connect", "true"); wxGetApp().mainframe->plater()->sidebar().update_all_preset_comboboxes(reload_device_view); wxGetApp().mainframe->m_print_enable = true; wxGetApp().mainframe->update_slice_print_status(MainFrame::eEventPlateUpdate); // wxGetApp().mainframe->load_printer_url("http://" + ip); //到时全部加载本地交互页面 if (!wxGetApp().mainframe->m_printer_view->isSnapmakerPage()) { wxString url = wxString::FromUTF8(LOCALHOST_URL + std::to_string(PAGE_HTTP_PORT) + "/web/flutter_web/index.html?path=2"); auto real_url = wxGetApp().get_international_url(url); wxGetApp().mainframe->load_printer_url(real_url); // 到时全部加载本地交互页面 } else { if (reload_device_view) { wxString url = wxString::FromUTF8(LOCALHOST_URL + std::to_string(PAGE_HTTP_PORT) + "/web/flutter_web/index.html?path=2"); auto real_url = wxGetApp().get_international_url(url); wxGetApp().mainframe->load_printer_url(real_url); // 到时全部加载本地交互页面 } } auto self = weak_self.lock(); if (!self) { return; } // 清除耗材喷嘴映射信息 wxGetApp().app_config->clear_filament_extruder_map(); // 尝试获取新的耗材喷嘴映射信息 if (self->m_wcp_cache.count("deviceFilamentInfo")) { std::string value_str = m_wcp_cache["deviceFilamentInfo"].get(); json value = json::parse(value_str); json value_item = value["value"]; auto machines = wxGetApp().app_config->get_devices(); bool find = false; for (auto& [key, value] : value_item.items()) { if (find) { break; } for (const auto& machine : machines) { if (machine.sn == key && machine.connected) { find = true; json target = json::array(); json object = json::object(); object["key"] = key; object["value"] = value.dump(); target.push_back(object); self->update_filament_info(target, false); break; } } } } // 整理订阅列表,取消权限,但是保留真正的底层订阅 // 维护订阅topic表和eventid实例表 auto mqtt_self = dynamic_pointer_cast(self); mqtt_self->clean_current_engine(); self->send_to_js(); self->finish_job(); }); } }); } } else { handle_general_fail(-1, "param [ip] required"); } } else { handle_general_fail(); } } else { handle_general_fail(); } } catch (std::exception& e) { handle_general_fail(); } } void SSWCP_MqttAgent_Instance::sw_mqtt_publish() { try { if (!m_param_data.count("id") || !m_param_data["id"].is_string()) { handle_general_fail(-1, "param [id] is required or wrong type"); Slic3r::sentryReportLog(Slic3r::SENTRY_LOG_ERROR, std::string("device_publish host created failed"), DEVICE_PBLISH_ERR); return; } std::string id = m_param_data["id"].get(); if (!validate_id(id)) { handle_general_fail(-1, "id is illegal"); Slic3r::sentryReportLog(Slic3r::SENTRY_LOG_ERROR, std::string("device_publish id is illegal"), DEVICE_PBLISH_ERR); return; } if (!m_param_data.count("topic") || !m_param_data["topic"].is_string()) { handle_general_fail(-1, "param [topic] is required or wrong type"); Slic3r::sentryReportLog(Slic3r::SENTRY_LOG_ERROR, std::string("device_publish param [topic] is required or wrong type"), DEVICE_PBLISH_ERR); return; } std::string topic = m_param_data["topic"].get(); if (!m_param_data.count("qos") || !m_param_data["qos"].is_number()) { handle_general_fail(-1, "param [qos] is required or wrong type"); Slic3r::sentryReportLog(Slic3r::SENTRY_LOG_ERROR, std::string("device_publish param [qos] is required or wrong type"), DEVICE_PBLISH_ERR); return; } int qos = m_param_data["qos"].get(); if (!m_param_data.count("payload") || !m_param_data["payload"].is_string()) { handle_general_fail(-1, "param [payload] required"); Slic3r::sentryReportLog(Slic3r::SENTRY_LOG_ERROR, std::string("device_publish param [payload] required"), DEVICE_PBLISH_ERR); return; } std::string payload = m_param_data["payload"].get(); std::weak_ptr weak_ptr = shared_from_this(); auto engine = get_current_engine(); if (m_work_thread.joinable()) m_work_thread.join(); m_work_thread = std::thread([weak_ptr, engine, topic, payload, qos]() { if (!weak_ptr.lock()) { return; } auto self = std::dynamic_pointer_cast(weak_ptr.lock()); std::string msg = "success"; bool flag = engine->Publish(topic, payload, qos, msg); wxGetApp().CallAfter([weak_ptr, msg, flag]() { auto self = weak_ptr.lock(); if (self) { if (flag) { self->m_msg = msg; self->send_to_js(); self->finish_job(); } else { self->handle_general_fail(-1, msg); } } }); }); } catch (std::exception& e) { handle_general_fail(); } } // SSWCP TimeoutMap> SSWCP::m_instance_list; constexpr std::chrono::milliseconds SSWCP::DEFAULT_INSTANCE_TIMEOUT; std::string SSWCP::m_active_gcode_filename = ""; std::string SSWCP::m_display_gcode_filename = ""; long long SSWCP::m_active_file_size = 0; std::mutex SSWCP::m_file_size_mutex; std::unordered_map SSWCP::m_tab_map = { {"Home", MainFrame::TabPosition::tpHome}, {"3DEditor", MainFrame::TabPosition::tp3DEditor}, {"Preview", MainFrame::TabPosition::tpPreview}, {"Monitor", MainFrame::TabPosition::tpMonitor}, {"MultiDevice", MainFrame::TabPosition::tpMultiDevice}, {"Project", MainFrame::TabPosition::tpProject}, {"Calibration", MainFrame::TabPosition::tpCalibration}, {"Auxiliary", MainFrame::TabPosition::tpAuxiliary}, {"DebugTool", MainFrame::TabPosition::toDebugTool} }; std::unordered_set SSWCP::m_machine_find_cmd_list = { "sw_GetMachineFindSupportInfo", "sw_StartMachineFind", "sw_StopMachineFind", "sw_WakeupFind", }; std::unordered_set SSWCP::m_machine_option_cmd_list = { "system.get_device_info", "sw_SendGCodes", "sw_FileGetStatus", "sw_SystemGetDeviceInfo", "sw_GetMachineState", "sw_SubscribeMachineState", "sw_GetMachineObjects", "sw_SetSubscribeFilter", "sw_StopMachineStateSubscription", "sw_GetPrinterInfo", "sw_MachinePrintStart", "sw_MachinePrintPause", "sw_MachinePrintResume", "sw_MachinePrintCancel", "sw_GetMachineSystemInfo", "sw_MachineFilesRoots", "sw_MachineFilesMetadata", "sw_MachineFilesThumbnails", "sw_MachineFilesGetDirectory", "sw_CameraStartMonitor", "sw_CameraStopMonitor", "sw_DeleteMachineFile", "sw_GetFileFilamentMapping", "sw_SetFilamentMappingComplete", "sw_FinishFilamentMapping", "sw_DownloadMachineFile", "sw_UploadFiletoMachine", "sw_GetPrintLegal", "sw_GetPrintZip", "sw_FinishPreprint", "sw_PullCloudFile", "sw_CancelPullCloudFile", "sw_StartCloudPrint", "sw_SetDeviceName", "sw_ControlLed", "sw_ControlPrintSpeed", "sw_BedMesh_AbortProbeMesh", "sw_ControlPurifier", "sw_ControlMainFan", "sw_ControlGenericFan", "sw_ControlBedTemp", "sw_ControlExtruderTemp", "sw_FilesThumbnailsBase64", "sw_exception_query", "sw_GetFileListPage", "sw_UpdateMachineFilamentInfo", "sw_UploadCameraTimelapse", "sw_DeleteCameraTimelapse", "sw_GetCameraTimelapseInstance", "sw_ServerClientManagerSetUserinfo", "sw_DefectDetactionConfig", GET_DEVICEDATA_STORAGESPACE }; std::unordered_set SSWCP::m_machine_connect_cmd_list = { "sw_Test_connect", "sw_Connect", "sw_Disconnect", "sw_GetConnectedMachine", "sw_ConnectOtherMachine", "sw_GetPincode" }; std::unordered_set SSWCP::m_project_cmd_list = { "sw_NewProject", "sw_OpenProject", "sw_GetRecentProjects", "sw_OpenRecentFile", "sw_DeleteRecentFiles", "sw_SubscribeRecentFiles", }; std::unordered_set SSWCP::m_login_cmd_list = {"sw_UserLogin", "sw_UserLogout", "sw_GetUserLoginState", "sw_SubscribeUserLoginState", UPDATE_PRIVACY_STATUS, GET_PRIVACY_STATUS}; std::unordered_set SSWCP::m_machine_manage_cmd_list = { "sw_GetLocalDevices", "sw_AddDevice", "sw_SubscribeLocalDevices", "sw_RenameDevice", "sw_SwitchModel", "sw_DeleteDevices" }; std::unordered_set SSWCP::m_page_state_cmd_list = { "sw_SubscribePageStateChange", "sw_UnsubscribePageStateChange" }; std::unordered_set SSWCP::m_mqtt_agent_cmd_list = { "sw_create_mqtt_client", "sw_mqtt_connect", "sw_mqtt_disconnect", "sw_mqtt_subscribe", "sw_mqtt_unpublish", "sw_mqtt_publish", "sw_mqtt_set_engine" }; std::shared_ptr SSWCP::create_sswcp_instance(std::string cmd, const json& header, const json& data, std::string event_id, wxWebView* webview) { std::shared_ptr instance; if (m_machine_find_cmd_list.find(cmd) != m_machine_find_cmd_list.end()) { instance = std::make_shared(cmd, header, data, event_id, webview); } else if (m_machine_connect_cmd_list.find(cmd) != m_machine_connect_cmd_list.end()) { instance = std::make_shared(cmd, header, data, event_id, webview); } else if (m_machine_option_cmd_list.find(cmd) != m_machine_option_cmd_list.end()) { instance = std::make_shared(cmd, header, data, event_id, webview); } else if (m_project_cmd_list.find(cmd) != m_project_cmd_list.end()) { instance = std::make_shared(cmd, header, data, event_id, webview); } else if (m_login_cmd_list.find(cmd) != m_login_cmd_list.end()) { instance = std::make_shared(cmd, header, data, event_id, webview); } else if (m_machine_manage_cmd_list.find(cmd) != m_machine_manage_cmd_list.end()) { instance = std::make_shared(cmd, header, data, event_id, webview); } else if (m_page_state_cmd_list.find(cmd) != m_page_state_cmd_list.end()) { instance = std::make_shared(cmd, header, data, event_id, webview); } else if(m_mqtt_agent_cmd_list.find(cmd) != m_mqtt_agent_cmd_list.end()) { instance = std::make_shared(cmd, header, data, event_id, webview); } else { instance = std::make_shared(cmd, header, data, event_id, webview); } return instance; } // Handle incoming web messages void SSWCP::handle_web_message(std::string message, wxWebView* webview) { try { if (!webview) { return; } WCP_Logger::getInstance().add_log(message, false, "", "WCP", "info"); json j_message = json::parse(message); if (j_message.empty() || !j_message.count("header") || !j_message.count("payload") || !j_message["payload"].count("cmd")) { return; } json header = j_message["header"]; json payload = j_message["payload"]; std::string cmd = ""; std::string event_id = ""; json params; if (payload.count("cmd")) { cmd = payload["cmd"].get(); } if (payload.count("params")) { params = payload["params"]; } if (payload.count("event_id") && !payload["event_id"].is_null()) { event_id = payload["event_id"].get(); } std::shared_ptr instance = create_sswcp_instance(cmd, header, params, event_id, webview); if (instance) { if (event_id != "") { m_instance_list.add_infinite(instance.get(), instance); } else { m_instance_list.add(instance.get(), instance, DEFAULT_INSTANCE_TIMEOUT); } instance->process(); } //if (!m_func_map.count(cmd)) { // // todo:返回不支持处理 //} //m_func_map[cmd](sequenceId, data, callback_name, webview); } catch (std::exception& e) { } } // Delete instance from list void SSWCP::delete_target(SSWCP_Instance* target) { wxGetApp().CallAfter([target]() { m_instance_list.remove(target); }); } // Stop all machine subscriptions void SSWCP::stop_subscribe_machine() { wxGetApp().CallAfter([]() { std::vector instances_to_stop; auto snapshot = m_instance_list.get_snapshot(); // Get all subscription instances to stop for (const auto& instance : snapshot) { if (instance.second->getType() == SSWCP_MachineFind_Instance::MACHINE_OPTION && instance.second->m_cmd == "sw_SubscribeMachineState") { instances_to_stop.push_back(instance.first); } } // Stop each instance for (auto* instance : instances_to_stop) { auto instance_ptr = m_instance_list.get(instance); if (instance_ptr) { (*instance_ptr)->finish_job(); } } }); } // Stop all machine discovery instances void SSWCP::stop_machine_find() { wxGetApp().CallAfter([]() { std::vector instances_to_stop; auto snapshot = m_instance_list.get_snapshot(); // Get all discovery instances to stop for (const auto& instance : snapshot) { if (instance.second->getType() == SSWCP_MachineFind_Instance::MACHINE_FIND) { instances_to_stop.push_back(instance.first); } } // Set stop flag for each instance for (auto* instance : instances_to_stop) { auto instance_ptr = m_instance_list.get(instance); if (instance_ptr) { (*instance_ptr)->set_stop(true); } } }); } // Handle webview deletion void SSWCP::on_webview_delete(wxWebView* view) { // Mark all instances associated with this webview as invalid std::vector instances_to_invalidate; // Get all instances using this webview for (const auto& instance : m_instance_list) { if (instance.second->value->get_web_view() == view) { instances_to_invalidate.push_back(instance.first); instance.second->value->set_web_view(nullptr); } } // Mark each instance as invalid for (auto* instance : instances_to_invalidate) { auto instance_ptr = m_instance_list.get(instance); if (instance_ptr) { (*instance_ptr)->set_Instance_illegal(); } } auto& device_map = wxGetApp().m_device_card_subscribers; for (auto iter = device_map.begin(); iter != device_map.end();) { if (iter->first == view) { iter = device_map.erase(iter); } else { iter++; } } auto& login_map = wxGetApp().m_user_login_subscribers; for (auto iter = login_map.begin(); iter != login_map.end();) { if (iter->first == view) { iter = login_map.erase(iter); } else { iter++; } } auto& privacy_map = wxGetApp().m_user_update_privacy_subscribers; for (auto iter = privacy_map.begin(); iter != privacy_map.end();) { if (iter->first == view) { iter = privacy_map.erase(iter); } else { iter++; } } auto& recent_file_map = wxGetApp().m_recent_file_subscribers; for (auto iter = recent_file_map.begin(); iter != recent_file_map.end();) { if (iter->first == view) { iter = recent_file_map.erase(iter); } else { iter++; } } auto& cache_map = wxGetApp().m_cache_subscribers; for (auto iter = cache_map.begin(); iter != cache_map.end();) { if (iter->first.first == view) { iter = cache_map.erase(iter); } else { iter++; } } auto& page_state_map = wxGetApp().m_page_state_subscribers; for (auto iter = page_state_map.begin(); iter != page_state_map.end();) { if (iter->first == view) { iter = page_state_map.erase(iter); } else { iter++; } } } std::string SSWCP::get_display_filename() { return m_display_gcode_filename; } // get the active filename std::string SSWCP::get_active_filename() { return m_active_gcode_filename; } // set the display name void SSWCP::update_display_filename(const std::string& filename) { m_display_gcode_filename = filename; } // set the active filename void SSWCP::update_active_filename(const std::string& filename) { m_active_gcode_filename = filename; } // query the info of the machine bool SSWCP::query_machine_info(std::shared_ptr& host, std::string& out_model, std::vector& out_nozzle_diameters, std::string& device_name, int timeout_second) { if (!host) return false; // 创建同步等待的条件变量和互斥锁 std::condition_variable cv; std::shared_ptr mutex(new std::mutex); std::weak_ptr cb_mutex = mutex; bool received = false; bool timeout = false; json system_info; // 发送查询请求 host->async_get_system_info( [&, cb_mutex](const json& response) { if (cb_mutex.expired()) { return; } std::lock_guard lock(*mutex); if (!response.is_null() && !response.count("error")) { system_info = response; } received = true; cv.notify_one(); } ); // 等待响应 { std::unique_lock lock(*mutex); auto predicate = [&received]() { return received; }; timeout = !cv.wait_for(lock, std::chrono::seconds(timeout_second), predicate); } if (!timeout && !system_info.is_null()) { // 成功获取到信息 if (system_info.count("data")) { system_info = system_info["data"]; } if (system_info.contains("system_info")) { auto& system_data = system_info["system_info"]; if(system_data.contains("product_info")){ auto& product_info = system_data["product_info"]; // 获取机型 if(product_info.contains("machine_type")){ out_model = product_info["machine_type"].get(); } // 获取喷嘴信息 if(product_info.contains("nozzle_diameter")){ try { if (product_info["nozzle_diameter"].is_array()) { for (const auto& nozzle : product_info["nozzle_diameter"]) { // todo 不一定是string if (nozzle.is_number()) { double temp = nozzle.get(); if (fabs(temp - 0.2) < 1e-6) { out_nozzle_diameters.push_back("0.2"); } else if (fabs(temp - 0.4) < 1e-6) { out_nozzle_diameters.push_back("0.4"); } else if (fabs(temp - 0.6) < 1e-6) { out_nozzle_diameters.push_back("0.6"); } else if (fabs(temp - 0.8) < 1e-6) { out_nozzle_diameters.push_back("0.8"); } } else { std::string temp = nozzle.get(); if (temp == "0.2" || temp == "0.4" || temp == "0.6" || temp == "0.8") { out_nozzle_diameters.push_back(temp); } } } } else { // 如果是单个值 if (product_info["nozzle_diameter"].is_number()) { double temp = product_info["nozzle_diameter"].get(); if (fabs(temp - 0.2) < 1e-6) { out_nozzle_diameters.push_back("0.2"); } else if (fabs(temp - 0.4) < 1e-6) { out_nozzle_diameters.push_back("0.2"); } else if (fabs(temp - 0.6) < 1e-6) { out_nozzle_diameters.push_back("0.2"); } else if (fabs(temp - 0.8) < 1e-6) { out_nozzle_diameters.push_back("0.2"); } } else { std::string temp = product_info["nozzle_diameter"].get(); if (temp == "0.2" || temp == "0.4" || temp == "0.6" || temp == "0.8") { out_nozzle_diameters.push_back(temp); } } } } catch (std::exception& e) { return false; } } if (product_info.contains("device_name")) { device_name = product_info["device_name"].get(); } } return true; } } return false; } MachineIPType* MachineIPType::getInstance() { static MachineIPType mipt_instance; return &mipt_instance; } }}; // namespace Slic3r::GUI