fix(ElegooLink): pass printer SN to CC2 device panel URL (#13878)

* 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>
This commit is contained in:
DeathKhan
2026-05-28 06:45:03 -05:00
committed by GitHub
parent 9ac0cb16d4
commit 1eaf3f71d5
3 changed files with 130 additions and 57 deletions

File diff suppressed because one or more lines are too long

View File

@@ -98,8 +98,6 @@ public:
stop_upload = true;
if (upload_thread.joinable())
upload_thread.join();
if (sn_thread.joinable())
sn_thread.join();
}
void on_script_message(wxWebViewEvent &evt) override
@@ -287,35 +285,21 @@ private:
void handle_get_sn_request(const std::string& request_id, const std::string& method)
{
if (sn_request_in_progress.exchange(true)) {
send_ipc_message("response", request_id, method, 1, "SN request already in progress");
return;
// 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();
}
if (sn_thread.joinable())
sn_thread.join();
sn_thread = std::thread([this, request_id, method]() {
std::string sn;
DynamicPrintConfig* config = get_active_printer_config();
std::unique_ptr<PrintHost> print_host(config == nullptr ? nullptr : PrintHost::get_print_host(config));
if (print_host != nullptr)
sn = print_host->get_sn();
sn_request_in_progress = false;
json data = {
{"sn", sn}
};
send_ipc_message("response", request_id, method, 0, "success", dump_json(data));
});
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> sn_request_in_progress { false };
std::atomic<bool> stop_upload { false };
std::thread upload_thread;
std::thread sn_thread;
};
} // namespace

View File

@@ -1,6 +1,8 @@
#include "ElegooLink.hpp"
#include <algorithm>
#include <map>
#include <mutex>
#include <sstream>
#include <exception>
#include <boost/format.hpp>
@@ -60,6 +62,52 @@ namespace Slic3r {
namespace {
constexpr const char* ELEGOO_CC2_DEFAULT_TOKEN = "123456";
// AppConfig section for CC2 serial numbers, keyed by normalized print_host (host/IP).
constexpr const char* ELEGOO_DEV_SN_SECTION = "dev_sn";
static std::mutex s_sn_cache_mutex;
static std::map<std::string, std::string> s_sn_cache;
std::string sn_cache_key(const std::string& host_ip, const std::string& token)
{
return host_ip + ":" + token;
}
void cache_sn(const std::string& host_ip, const std::string& token, const std::string& sn)
{
if (host_ip.empty() || token.empty() || sn.empty())
return;
std::lock_guard<std::mutex> lock(s_sn_cache_mutex);
s_sn_cache[sn_cache_key(host_ip, token)] = sn;
}
std::string lookup_sn(const std::string& host_ip, const std::string& token)
{
std::lock_guard<std::mutex> lock(s_sn_cache_mutex);
auto it = s_sn_cache.find(sn_cache_key(host_ip, token));
return it != s_sn_cache.end() ? it->second : std::string{};
}
std::string load_sn_from_config(const std::string& host_ip)
{
if (host_ip.empty())
return {};
AppConfig* app_cfg = GUI::get_app_config();
if (app_cfg == nullptr)
return {};
return app_cfg->get(ELEGOO_DEV_SN_SECTION, host_ip);
}
void persist_sn(const std::string& host_ip, const std::string& token, const std::string& sn)
{
if (host_ip.empty() || sn.empty())
return;
cache_sn(host_ip, token, sn);
AppConfig* app_cfg = GUI::get_app_config();
if (app_cfg == nullptr)
return;
app_cfg->set_str(ELEGOO_DEV_SN_SECTION, host_ip, sn);
}
enum class ElegooPrinterType {
Other,
@@ -193,6 +241,30 @@ namespace Slic3r {
return out;
}
std::string lookup_cc2_serial_impl(const std::string& printer_model,
const std::string& print_host,
const std::string& apikey)
{
if (classify_printer_model(printer_model) != ElegooPrinterType::CC2)
return {};
const std::string host_ip = get_host_from_url(print_host);
const std::string token = get_cc2_token(apikey);
std::string sn = lookup_sn(host_ip, token);
if (sn.empty())
sn = load_sn_from_config(host_ip);
return sn;
}
std::string lookup_cc2_serial(DynamicPrintConfig* config)
{
if (config == nullptr)
return {};
return lookup_cc2_serial_impl(config->opt_string("printer_model"),
config->opt_string("print_host"),
config->opt_string("printhost_apikey"));
}
#ifdef WIN32
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
std::string substitute_host(const std::string& orig_addr, std::string sub_addr)
@@ -336,8 +408,30 @@ namespace Slic3r {
std::string web_path = resources_dir() + "/plugins/elegoolink/web/lan_service_web/index.html";
std::replace(web_path.begin(), web_path.end(), '\\', '/');
web_path = "file://" + web_path;
web_path += "?access_code=" + get_cc2_token(config->opt_string("printhost_apikey"));
web_path += "&ip=" + get_host_from_url(host) + "&id=elegoo_123456";
const std::string token = get_cc2_token(config->opt_string("printhost_apikey"));
const std::string host_ip = get_host_from_url(host);
// Pass sn= so the panel can subscribe to the correct MQTT topics.
std::string sn = lookup_cc2_serial(config);
if (sn.empty()) {
std::string error_msg;
auto http = Http::get("http://" + host_ip + "/system/info?X-Token=" + escape_string(token));
http.timeout_connect(3).timeout_max(5);
http.header("X-Token", token);
http.header("Accept", "application/json");
http.on_complete([&](std::string body, unsigned /*status*/) {
parse_cc2_response(body, error_msg, &sn);
}).perform_sync();
if (!sn.empty())
persist_sn(host_ip, token, sn);
}
web_path += "?access_code=" + token;
web_path += "&ip=" + host_ip;
if (!sn.empty())
web_path += "&sn=" + sn;
web_path += "&id=elegoo_123456";
const std::string lang = GUI::wxGetApp().current_language_code_safe().utf8_string();
if (!lang.empty())
@@ -376,33 +470,9 @@ namespace Slic3r {
std::string ElegooLink::get_sn() const
{
if (classify_printer_model(m_printerModel) != ElegooPrinterType::CC2)
return "";
const char* name = get_name();
std::string sn;
const auto token = cc2_token();
auto http = Http::get(make_cc2_info_url());
http.timeout_connect(10)
.timeout_max(15);
http.header("X-Token", token);
http.header("Accept", "application/json");
http.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting CC2 device info for SN: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
})
.on_complete([&](std::string body, unsigned status) {
std::string error_message;
if (!parse_cc2_response(body, error_message, &sn)) {
BOOST_LOG_TRIVIAL(warning) << boost::format("%1%: Failed to parse CC2 SN response, HTTP %2%, reason: %3%") % name % status % error_message;
sn.clear();
}
})
#ifdef WIN32
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
#endif // WIN32
.perform_sync();
return sn;
// Panel IPC calls this on every load with a 10s timeout. Never block on HTTP
// here — URL sn= and dev_sn must be enough; HTTP is only for get_print_host_webui.
return lookup_cc2_serial_impl(m_printerModel, m_host, m_apikey);
}
bool ElegooLink::elegoo_test(wxString& msg) const{
@@ -481,6 +551,7 @@ namespace Slic3r {
msg = format_error(body, error_message.empty() ? "CC2 device not detected" : error_message, status);
return;
}
persist_sn(get_host_from_url(m_host), token, serial_number);
res = true;
})
#ifdef WIN32