mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-16 07:22:10 +00:00
* fix(ElegooLink): pass printer SN to CC2 device panel URL The CC2 panel subscribes to MQTT topics keyed by the printer serial number. Without sn= in the URL it uses a wrong hardcoded fallback SN, subscribes to the wrong topics, and shows Offline permanently even though the printer is reachable. - Cache the SN in elegoo_cc2_test() (already fetches it, was discarding it) - Look up cache in get_print_host_webui(); fall back to a short LAN HTTP call on first use before the test has run - Append sn= to the panel URL - Clear the wrong hardcoded fallback SN/IP from the panel bundle - Add a small synchronous boot script to the panel that fetches the SN from the printer before the bundle reads URLSearchParams, as a fallback for unpatched binaries * fix(ElegooLink): persist CC2 serial number in AppConfig dev_sn section Store the printer SN under [dev_sn] keyed by normalized print_host after a successful connection test or system/info fetch. Reuse it on later sessions before hitting the network, matching how access_code is keyed by dev_id for other LAN printers. * fix(ElegooLink): answer get_sn IPC instantly from dev_sn cache The CC2 panel always calls get_sn with a 10s timeout. Remove the HTTP fallback from get_sn() and resolve IPC from dev_sn/memory only so Device tab load is not blocked after sn= is already in the URL. * fix(ElegooLink): skip get_sn IPC when URL already has sn The CC2 device panel calls get_sn with a 10s timeout on every MQTT connect even when Orca passes sn= in the query string. Use the URL serial immediately and only fall back to IPC when it is missing. * refactor(ElegooLink): resolve CC2 SN via PrintHost::get_sn in GUI Drop the ElegooLink.hpp include from PrinterWebViewHandler; the webview IPC handler uses the existing PrintHost virtual instead. Keep CC2 serial lookup helpers file-local in ElegooLink.cpp and share them between get_sn() and get_print_host_webui(). * chore: drop redundant <memory> include in PrinterWebViewHandler --------- Co-authored-by: SoftFever <softfeverever@gmail.com>
323 lines
11 KiB
C++
323 lines
11 KiB
C++
#include "PrinterWebViewHandler.hpp"
|
|
|
|
#include "I18N.hpp"
|
|
#include "PrinterWebView.hpp"
|
|
#include "slic3r/GUI/GUI_App.hpp"
|
|
#include "slic3r/GUI/Widgets/WebView.hpp"
|
|
#include "slic3r/Utils/PrintHost.hpp"
|
|
#include "libslic3r/Preset.hpp"
|
|
|
|
#include <nlohmann/json.hpp>
|
|
#include <atomic>
|
|
#include <boost/filesystem/path.hpp>
|
|
#include <thread>
|
|
#include <wx/filedlg.h>
|
|
#include <wx/string.h>
|
|
|
|
using json = nlohmann::json;
|
|
|
|
namespace Slic3r {
|
|
namespace GUI {
|
|
|
|
PrinterWebViewHandler::PrinterWebViewHandler(PrinterWebView& owner)
|
|
: m_owner(owner)
|
|
{
|
|
}
|
|
|
|
PrinterWebViewHandler::~PrinterWebViewHandler() = default;
|
|
|
|
void PrinterWebViewHandler::on_loaded(wxWebViewEvent &evt)
|
|
{
|
|
}
|
|
|
|
void PrinterWebViewHandler::on_script_message(wxWebViewEvent &evt)
|
|
{
|
|
}
|
|
|
|
PrinterWebView& PrinterWebViewHandler::owner() const
|
|
{
|
|
return m_owner;
|
|
}
|
|
|
|
wxWebView* PrinterWebViewHandler::browser() const
|
|
{
|
|
return m_owner.m_browser;
|
|
}
|
|
|
|
namespace {
|
|
|
|
DynamicPrintConfig* get_active_printer_config()
|
|
{
|
|
if (wxGetApp().preset_bundle == nullptr)
|
|
return nullptr;
|
|
|
|
return &wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
|
}
|
|
|
|
std::string json_string(const json& node, const char* key)
|
|
{
|
|
auto it = node.find(key);
|
|
return (it != node.end() && it->is_string()) ? it->get<std::string>() : std::string();
|
|
}
|
|
|
|
std::string dump_json(const json& node)
|
|
{
|
|
return node.dump(-1, ' ', false, json::error_handler_t::replace);
|
|
}
|
|
|
|
boost::filesystem::path path_from_utf8(const std::string& utf8_path)
|
|
{
|
|
#ifdef _WIN32
|
|
const wxString wide_path = wxString::FromUTF8(utf8_path.c_str());
|
|
return boost::filesystem::path(wide_path.ToStdWstring());
|
|
#else
|
|
return boost::filesystem::path(utf8_path);
|
|
#endif
|
|
}
|
|
|
|
std::string filename_to_utf8(const boost::filesystem::path& path)
|
|
{
|
|
#ifdef _WIN32
|
|
const wxString wx_filename(path.filename().c_str());
|
|
const wxScopedCharBuffer utf8 = wx_filename.ToUTF8();
|
|
return utf8.data() != nullptr ? std::string(utf8.data()) : std::string();
|
|
#else
|
|
return path.filename().string();
|
|
#endif
|
|
}
|
|
|
|
class ElegooPrinterWebViewHandler final : public PrinterWebViewHandler {
|
|
public:
|
|
explicit ElegooPrinterWebViewHandler(PrinterWebView& owner)
|
|
: PrinterWebViewHandler(owner)
|
|
{
|
|
}
|
|
|
|
~ElegooPrinterWebViewHandler() override
|
|
{
|
|
stop_upload = true;
|
|
if (upload_thread.joinable())
|
|
upload_thread.join();
|
|
}
|
|
|
|
void on_script_message(wxWebViewEvent &evt) override
|
|
{
|
|
const wxString message = evt.GetString();
|
|
if (message.empty())
|
|
return;
|
|
|
|
json root = json::parse(message.ToUTF8().data(), nullptr, false);
|
|
if (root.is_discarded() || !root.is_object())
|
|
return;
|
|
|
|
std::string request_id = json_string(root, "id");
|
|
std::string method = json_string(root, "method");
|
|
json params = root.contains("params") && root["params"].is_object() ? root["params"] : json::object();
|
|
|
|
if (method.empty()) {
|
|
method = json_string(root, "command");
|
|
if (params.empty() && root.contains("data") && root["data"].is_object())
|
|
params = root["data"];
|
|
}
|
|
|
|
if (method == "open" || method == "common_openurl") {
|
|
const std::string url = json_string(params, "url").empty() ? json_string(root, "url") : json_string(params, "url");
|
|
if (!url.empty())
|
|
wxLaunchDefaultBrowser(url);
|
|
if (!request_id.empty())
|
|
send_ipc_message("response", request_id, method, 0, "success");
|
|
return;
|
|
}
|
|
|
|
if (method == "upload_file") {
|
|
handle_upload_request(request_id, method, dump_json(params));
|
|
return;
|
|
}
|
|
|
|
if (method == "open_file_dialog") {
|
|
handle_open_file_dialog_request(request_id, method, dump_json(params));
|
|
return;
|
|
}
|
|
|
|
if (method == "get_sn") {
|
|
handle_get_sn_request(request_id, method);
|
|
return;
|
|
}
|
|
}
|
|
|
|
private:
|
|
void send_ipc_message(const char* type, const std::string& request_id, const std::string& method, int code,
|
|
const std::string& message, const std::string& data_json = "{}")
|
|
{
|
|
if (browser() == nullptr)
|
|
return;
|
|
|
|
json body = json::object();
|
|
body["type"] = type;
|
|
if (!request_id.empty())
|
|
body["id"] = request_id;
|
|
if (!method.empty())
|
|
body["method"] = method;
|
|
|
|
json data = json::parse(data_json, nullptr, false);
|
|
if (data.is_discarded())
|
|
data = json::object();
|
|
body["data"] = std::move(data);
|
|
|
|
if (std::string(type) == "response") {
|
|
body["code"] = code;
|
|
body["message"] = message;
|
|
}
|
|
|
|
const wxString payload = wxString::FromUTF8(dump_json(body));
|
|
const wxString script = "if (typeof HandleStudio === 'function') { HandleStudio(" + payload + "); } else { window.postMessage(" + payload + ", '*'); }";
|
|
wxGetApp().CallAfter([this, script]() {
|
|
if (browser() != nullptr)
|
|
WebView::RunScript(browser(), script);
|
|
});
|
|
}
|
|
|
|
void handle_upload_request(const std::string& request_id, const std::string& method, const std::string& params_json)
|
|
{
|
|
if (upload_in_progress.exchange(true)) {
|
|
send_ipc_message("response", request_id, method, 1, "Upload already in progress");
|
|
return;
|
|
}
|
|
|
|
if (upload_thread.joinable())
|
|
upload_thread.join();
|
|
|
|
json params = json::parse(params_json, nullptr, false);
|
|
if (params.is_discarded())
|
|
params = json::object();
|
|
|
|
std::string file_path = json_string(params, "filePath");
|
|
std::string file_name = json_string(params, "fileName");
|
|
|
|
if (file_path.empty()) {
|
|
upload_in_progress = false;
|
|
send_ipc_message("response", request_id, method, 1, "Missing filePath");
|
|
return;
|
|
}
|
|
|
|
// HTML IPC passes UTF-8 strings; decode explicitly to avoid Windows codepage issues.
|
|
boost::filesystem::path source_path = path_from_utf8(file_path);
|
|
if (file_name.empty())
|
|
file_name = filename_to_utf8(source_path);
|
|
|
|
DynamicPrintConfig* config = get_active_printer_config();
|
|
std::unique_ptr<PrintHost> print_host(config == nullptr ? nullptr : PrintHost::get_print_host(config));
|
|
if (print_host == nullptr) {
|
|
upload_in_progress = false;
|
|
send_ipc_message("response", request_id, method, 1, "Could not get a valid Printer Host reference");
|
|
return;
|
|
}
|
|
|
|
stop_upload = false;
|
|
upload_thread = std::thread([this, request_id, method, file_path, file_name, source_path, print_host = std::move(print_host)]() mutable {
|
|
std::string error_message;
|
|
|
|
PrintHostUpload upload_data;
|
|
upload_data.use_3mf = false;
|
|
upload_data.post_action = PrintHostPostUploadAction::None;
|
|
upload_data.source_path = source_path;
|
|
upload_data.upload_path = path_from_utf8(file_name);
|
|
|
|
const bool success = print_host->upload(
|
|
std::move(upload_data),
|
|
[this, request_id](Http::Progress progress, bool& cancel) {
|
|
cancel = stop_upload.load();
|
|
json data = {
|
|
{"uploadedBytes", static_cast<uint64_t>(progress.ulnow)},
|
|
{"totalBytes", static_cast<uint64_t>(progress.ultotal)}
|
|
};
|
|
send_ipc_message("event", request_id, "upload_progress", 0, "", dump_json(data));
|
|
},
|
|
[&error_message](wxString error) {
|
|
error_message = error.ToUTF8().data();
|
|
},
|
|
[this, request_id](wxString tag, wxString status) {
|
|
json data = {
|
|
{"tag", tag.ToUTF8().data()},
|
|
{"status", status.ToUTF8().data()}
|
|
};
|
|
send_ipc_message("event", request_id, "upload_info", 0, "", dump_json(data));
|
|
});
|
|
|
|
upload_in_progress = false;
|
|
|
|
if (success) {
|
|
json data = {
|
|
{"success", true},
|
|
{"filePath", file_path},
|
|
{"fileName", file_name}
|
|
};
|
|
send_ipc_message("response", request_id, method, 0, "success", dump_json(data));
|
|
} else {
|
|
if (error_message.empty())
|
|
error_message = "Upload failed";
|
|
send_ipc_message("response", request_id, method, 1, error_message);
|
|
}
|
|
});
|
|
}
|
|
|
|
void handle_open_file_dialog_request(const std::string& request_id, const std::string& method, const std::string& params_json)
|
|
{
|
|
json params = json::parse(params_json, nullptr, false);
|
|
if (params.is_discarded())
|
|
params = json::object();
|
|
|
|
const std::string filter = json_string(params, "filter").empty() ? "All files (*.*)|*.*" : json_string(params, "filter");
|
|
|
|
wxWindow* parent = owner().GetParent();
|
|
if (parent == nullptr)
|
|
parent = wxGetApp().GetTopWindow();
|
|
|
|
wxFileDialog open_file_dialog(parent, _L("Open File"), "", "", wxString::FromUTF8(filter), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
|
|
|
json data = json::object();
|
|
data["files"] = json::array();
|
|
if (open_file_dialog.ShowModal() != wxID_CANCEL)
|
|
data["files"].push_back(open_file_dialog.GetPath().ToUTF8().data());
|
|
|
|
send_ipc_message("response", request_id, method, 0, "success", dump_json(data));
|
|
}
|
|
|
|
void handle_get_sn_request(const std::string& request_id, const std::string& method)
|
|
{
|
|
// Panel always calls get_sn with a 10s IPC timeout. Answer immediately from
|
|
// dev_sn / cache — do not spawn a thread or perform HTTP (panel uses URL sn on miss).
|
|
std::string sn;
|
|
if (DynamicPrintConfig* config = get_active_printer_config()) {
|
|
const std::unique_ptr<PrintHost> host(PrintHost::get_print_host(config));
|
|
if (host)
|
|
sn = host->get_sn();
|
|
}
|
|
json data = { { "sn", sn } };
|
|
send_ipc_message("response", request_id, method, 0, "success", dump_json(data));
|
|
}
|
|
|
|
std::atomic<bool> upload_in_progress { false };
|
|
std::atomic<bool> stop_upload { false };
|
|
std::thread upload_thread;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
std::unique_ptr<PrinterWebViewHandler> create_printer_webview_handler(PrinterWebView& owner)
|
|
{
|
|
auto cfg = get_active_printer_config();
|
|
if(cfg == nullptr) return nullptr;
|
|
|
|
const auto host_type = cfg->option<ConfigOptionEnum<PrintHostType>>("host_type")->value;
|
|
switch (host_type)
|
|
{
|
|
case PrintHostType::htElegooLink:
|
|
return std::make_unique<ElegooPrinterWebViewHandler>(owner);
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
} // GUI
|
|
} // Slic3r
|