diff --git a/src/slic3r/GUI/Jobs/PrintJob.cpp b/src/slic3r/GUI/Jobs/PrintJob.cpp index 59cc7912ad..79bdecc1dd 100644 --- a/src/slic3r/GUI/Jobs/PrintJob.cpp +++ b/src/slic3r/GUI/Jobs/PrintJob.cpp @@ -12,7 +12,9 @@ #include "slic3r/GUI/DeviceCore/DevManager.h" #include "slic3r/GUI/DeviceCore/DevUtil.h" -#include "IPrinterAgent.hpp" +#include "slic3r/Utils/FileTransferUtils.hpp" +#include "slic3r/Utils/BBLNetworkPlugin.hpp" +#include "NetworkAgent.hpp" namespace Slic3r { namespace GUI { @@ -206,6 +208,51 @@ void PrintJob::process(Ctl &ctl) params.username = m_agent->default_lan_username(); params.password = m_access_code; + // check access code and ip address + if (this->connection_type == "lan" && m_print_type == "from_normal") { + bool emmc_ok = false; + bool ftp_ok = false; + if (could_emmc_print) { + std::string devIP = m_dev_ip; + std::string accessCode = m_access_code; + std::string url = "bambu:///local/" + devIP + "?port=6000&user=" + "bblp" + "&passwd=" + accessCode; + try { + std::unique_ptr tunnel = std::make_unique(module(), url); + emmc_ok = tunnel->sync_start_connect(); + } catch (const std::exception &e) { + BOOST_LOG_TRIVIAL(warning) << "eMMC tunnel unavailable, falling back to FTP: " << e.what(); + emmc_ok = false; + } + } + { + params.dev_id = m_dev_id; + params.project_name = "verify_job"; + params.filename = job_data._temp_path.string(); + params.connection_type = this->connection_type; + + result = m_agent->start_send_gcode_to_sdcard(params, nullptr, nullptr, nullptr); + + ftp_ok = result == 0; + } + if (!emmc_ok && !ftp_ok) { + bool legacy_mode = BBLNetworkPlugin::instance().use_legacy_network(); + BOOST_LOG_TRIVIAL(error) << "LAN connection verification failed:" + << " emmc_ok=" << emmc_ok + << ", ftp_ok=" << ftp_ok + << ", ftp_result=" << result + << ", dev_ip=" << m_dev_ip + << ", dev_id=" << m_dev_id + << ", password_length=" << m_access_code.size() + << ", legacy_mode=" << (legacy_mode ? "true" : "false"); + m_enter_ip_address_fun_fail(); + m_job_finished = true; + return; + } + + params.project_name = ""; + params.filename = ""; + } + params.dev_id = m_dev_id; params.ftp_folder = m_ftp_folder; params.filename = job_data._3mf_path.string(); @@ -596,13 +643,6 @@ void PrintJob::process(Ctl &ctl) } if (result < 0) { - if (result == ORCA_NETWORK_ERR_ACCESS_VERIFICATION_FAILED) { - if (m_enter_ip_address_fun_fail) - m_enter_ip_address_fun_fail(); - m_job_finished = true; - return; - } - curr_percent = -1; // The printer is still fetching its encryption flag (a transient state), so ask the diff --git a/src/slic3r/GUI/PartSkipDialog.hpp b/src/slic3r/GUI/PartSkipDialog.hpp index 4137a476fc..d204ba2043 100644 --- a/src/slic3r/GUI/PartSkipDialog.hpp +++ b/src/slic3r/GUI/PartSkipDialog.hpp @@ -14,6 +14,7 @@ #include #include +#include "NetworkAgent.hpp" #include "Widgets/Label.hpp" #include "Widgets/CheckBox.hpp" #include "Widgets/Button.hpp" @@ -117,7 +118,7 @@ private: std::map m_parts_name; std::vector m_partskip_ids; - enum URL_STATE m_url_state = URL_STATE::URL_TCP; + URL_STATE m_url_state = URL_STATE::URL_TCP; PartsInfo GetPartsInfo(); bool is_drag_mode(); diff --git a/src/slic3r/GUI/SendToPrinter.cpp b/src/slic3r/GUI/SendToPrinter.cpp index 80167850e6..b970447cd8 100644 --- a/src/slic3r/GUI/SendToPrinter.cpp +++ b/src/slic3r/GUI/SendToPrinter.cpp @@ -1,7 +1,6 @@ #include "SendToPrinter.hpp" #include "I18N.hpp" -#include "IPrinterAgent.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/Thread.hpp" #include "GUI.hpp" @@ -26,6 +25,7 @@ #include "DeviceCore/DevManager.h" #include "DeviceCore/DevStorage.h" +#include "slic3r/Utils/FileTransferUtils.hpp" namespace Slic3r { @@ -845,8 +845,11 @@ void SendToPrinterDialog::on_ok(wxCommandEvent &event) m_task_timer.reset(); } - if (auto agent = wxGetApp().getAgent(); agent && agent->get_printer_agent()) - agent->get_printer_agent()->cancel_file_transfer(); + if (m_filetransfer_uploadfile_job) { + m_filetransfer_uploadfile_job->cancel(); + m_filetransfer_uploadfile_job.reset(); + m_filetransfer_uploadfile_job = nullptr; + } m_is_canceled = true; wxCommandEvent* event = new wxCommandEvent(EVT_PRINT_JOB_CANCEL); @@ -871,10 +874,10 @@ void SendToPrinterDialog::on_ok(wxCommandEvent &event) return; bool cancelled = false; wxString msg = _L("Preparing print job"); - m_status_bar->update_status(msg, cancelled, 10, true); - m_export_3mf_cancel = cancel = cancelled; - }); - } + m_status_bar->update_status(msg, cancelled, 10, true); + m_export_3mf_cancel = cancel = cancelled; + }); + } if (m_is_canceled || m_export_3mf_cancel) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": send progress 10"; @@ -938,8 +941,7 @@ void SendToPrinterDialog::on_ok(wxCommandEvent &event) this->Bind(wxEVT_TIMER, [this](auto e){ show_status(PrintDialogStatus::PrintStatusPublicUploadFiled); - if (auto agent = wxGetApp().getAgent(); agent && agent->get_printer_agent()) - agent->get_printer_agent()->cancel_file_transfer(); + m_filetransfer_uploadfile_job->cancel(); update_print_status_msg(_L("Upload file timeout, please check if the firmware version supports it."), false, true); },m_task_timer->GetId()); m_task_timer->StartOnce(timeout_period); @@ -1295,8 +1297,10 @@ void SendToPrinterDialog::update_show_status() else m_if_has_sdcard = true; - if (auto agent = wxGetApp().getAgent(); agent && agent->get_printer_agent()) - agent->get_printer_agent()->cancel_file_transfer(); + if (m_filetransfer_tunnel) { + m_filetransfer_tunnel.reset(); + m_filetransfer_tunnel = nullptr; + } GetConnection(); } @@ -1655,6 +1659,7 @@ bool SendToPrinterDialog::Show(bool show) return DPIDialog::Show(show); } +extern wxString hide_passwd(wxString url, std::vector const &passwords); extern void refresh_agora_url(char const *device, char const *dev_ver, char const *channel, void *context, void (*callback)(void *context, char const *url)); void SendToPrinterDialog::GetConnection() @@ -1665,80 +1670,102 @@ void SendToPrinterDialog::GetConnection() if (obj == nullptr) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " : obj is empty"; m_connection_status = ConnectionStatus::NOT_START; - return; } int remote_proto = obj->get_file_remote(); if (!remote_proto) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " : remote_proto is not support"; m_connection_status = ConnectionStatus::NOT_START; - return; } if (obj->is_camera_busy_off()) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " : camera is busy"; m_connection_status = ConnectionStatus::NOT_START; - return; } - NetworkAgent *agent = wxGetApp().getAgent(); - if (!agent || !agent->get_printer_agent()) { - show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, - _L("The selected printer does not support file transfer.")); - return; + NetworkAgent *agent = wxGetApp().getAgent(); + std::string agent_version = agent ? agent->get_version() : ""; + std::string dev_ver = obj->get_ota_version(); + std::string dev_id = obj->get_dev_id(); + + if (m_url_timer && m_url_timer->IsRunning()) + { + m_url_timer->Stop(); + } + + m_url_timer.reset(new wxTimer()); + m_url_timer->SetOwner(this); + this->Bind( + wxEVT_TIMER, + [this](wxTimerEvent &e) { + BOOST_LOG_TRIVIAL(info) << "Timer callback triggered!"; + m_connection_status = ConnectionStatus::CONNECTION_FAILED; + m_ftp_try_connect = true; + if (m_filetransfer_tunnel) + { + m_filetransfer_tunnel.reset(); + m_filetransfer_tunnel = nullptr; + } + + }, + m_url_timer->GetId()); + m_url_timer->StartOnce(8000); + + if (agent) { + if (m_tcp_try_connect) { + std::string devIP = obj->get_dev_ip(); + std::string accessCode = obj->get_access_code(); + std::string url = "bambu:///local/" + devIP + "?port=6000&user=" + "bblp" + "&passwd=" + accessCode; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Connect method tcp"; + m_filetransfer_tunnel = std::make_unique(module(), url); + m_filetransfer_tunnel->on_connection([this](bool is_success, int err_code, std::string error_msg) { + CallAfter([this, is_success, err_code, error_msg]() { + OnConnection(is_success, err_code, error_msg); + }); + }); + m_filetransfer_tunnel->start_connect(); + } + else if (m_tutk_try_connect) + { + std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; + agent->get_camera_url(obj->get_dev_id() + "|" + dev_ver + "|" + protocols[1], [this, m = dev_id](CameraURLResult result) { + std::string url = std::move(result.url); + + if (m_url_timer && m_url_timer->IsRunning()) + { + m_url_timer->Stop(); + } + + #if !BBL_RELEASE_TO_PUBLIC + BOOST_LOG_TRIVIAL(info) << "SendToPrinter::camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="}); + #endif + + + if (result.is_success) + { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Connect method tutk"; + m_filetransfer_tunnel = std::make_unique(module(), url); + m_filetransfer_tunnel->on_connection([this](bool is_success, int err_code, std::string error_msg) { + CallAfter([this, is_success, err_code, error_msg]() { OnConnection(is_success, err_code, error_msg); }); + }); + m_filetransfer_tunnel->start_connect(); + } + else + { + std::string res = ""; + if (!url.empty() && boost::ends_with(url, "]")) + { + size_t n = url.find_last_of('['); + if (n != std::string::npos) + res = url.substr(n + 1, url.length() - n - 2); + } + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " : Tutk url error: ress = " << res; + } + }, wxGetApp().get_printer_cloud_provider(), + CameraURLParams{"", "", "", LVL_None, dev_id, agent->get_version(), dev_ver, + boost::lexical_cast(&refresh_agora_url), wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION, true}); + } } - - if (m_url_timer && m_url_timer->IsRunning()) { - m_url_timer->Stop(); - } - - m_url_timer.reset(new wxTimer()); - m_url_timer->SetOwner(this); - this->Bind( - wxEVT_TIMER, - [this](wxTimerEvent& e) { - BOOST_LOG_TRIVIAL(info) << "Timer callback triggered!"; - m_connection_status = ConnectionStatus::CONNECTION_FAILED; - m_ftp_try_connect = true; - if (auto agent = wxGetApp().getAgent(); agent && agent->get_printer_agent()) - agent->get_printer_agent()->cancel_file_transfer(); - }, - m_url_timer->GetId()); - m_url_timer->StartOnce(8000); - - IPrinterAgent::FileTransferRequest request; - request.device_id = obj->get_dev_id(); - request.device_ip = obj->get_dev_ip(); - request.access_code = obj->get_access_code(); - request.network_version = agent->get_version(); - request.device_version = obj->get_ota_version(); - request.refresh_url = boost::lexical_cast(&refresh_agora_url); - request.client_id = wxGetApp().app_config->get("slicer_uuid"); - request.client_version = SLIC3R_VERSION; - request.lan_mode = obj->connection_type() == "lan"; - - IPrinterAgent::FileTransferCallbacks callbacks; - callbacks.on_connection = [this](bool is_success, int error_code, std::string error_msg) { - CallAfter([this, is_success, error_code, error_msg = std::move(error_msg)] { - OnConnection(is_success, error_code, std::move(error_msg)); - }); - }; - callbacks.file_transfer_error = [this] { - CallAfter([this] { - show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, - _L("The selected printer does not support file transfer.")); - }); - }; - agent->get_printer_agent()->prepare_file_transfer(request, std::move(callbacks)); -} - -void SendToPrinterDialog::show_file_transfer_error(PrintDialogStatus status, wxString message) -{ - if (m_url_timer && m_url_timer->IsRunning()) - m_url_timer->Stop(); - m_connection_status = ConnectionStatus::CONNECTION_FAILED; - show_status(status); - update_print_status_msg(message, false, true); } void SendToPrinterDialog::OnConnection(bool is_success, int error_code, std::string error_msg) { @@ -1753,13 +1780,43 @@ void SendToPrinterDialog::OnConnection(bool is_success, int error_code, std::str { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Connect failed, error_code is:" << error_code << "error_msg is :" << error_msg; m_connection_status = ConnectionStatus::CONNECTION_FAILED; - m_tcp_try_connect = false; - m_tutk_try_connect = false; - m_ftp_try_connect = true; - show_status(PrintDialogStatus::PrintStatusPublicInitFailed); + ChangeConnectMethod(); + if (!m_tcp_try_connect && !m_tutk_try_connect) { + show_status(PrintDialogStatus::PrintStatusPublicInitFailed); + return; + } + m_filetransfer_tunnel.reset(); + m_filetransfer_tunnel = nullptr; + GetConnection(); } } +void SendToPrinterDialog::ChangeConnectMethod() +{ + DeviceManager *dev = Slic3r::GUI::wxGetApp().getDeviceManager(); + if (!dev) return; + MachineObject *obj = dev->get_my_machine(m_printer_last_select); + if (!obj) return; + + bool is_lan = (obj->connection_type() == "lan"); + + m_tcp_try_connect = false; + + if (is_lan) { + m_ftp_try_connect = true; + m_tutk_try_connect = false; + } else { + if (m_connect_try_times == 0) { + m_ftp_try_connect = false; + m_tutk_try_connect = true; + } else { + m_ftp_try_connect = true; + m_tutk_try_connect = false; + } + } + m_connect_try_times++; +} + void SendToPrinterDialog::ResetConnectMethod() { m_tcp_try_connect = true; @@ -1771,21 +1828,32 @@ void SendToPrinterDialog::ResetConnectMethod() void SendToPrinterDialog::ResetTunnelAndJob() { - if (auto agent = wxGetApp().getAgent(); agent && agent->get_printer_agent()) - agent->get_printer_agent()->cancel_file_transfer(); + if (m_filetransfer_uploadfile_job) + { + m_filetransfer_uploadfile_job->cancel(); + m_filetransfer_uploadfile_job.reset(); + m_filetransfer_uploadfile_job = nullptr; + } + if (m_filetransfer_mediability_job) + { + m_filetransfer_mediability_job->cancel(); + m_filetransfer_mediability_job.reset(); + m_filetransfer_mediability_job = nullptr; + } + if (m_filetransfer_tunnel) + { + m_filetransfer_tunnel.reset(); + m_filetransfer_tunnel = nullptr; + } } void SendToPrinterDialog::CreateMediaAbilityJob() { - NetworkAgent *agent = wxGetApp().getAgent(); - if (!agent || !agent->get_printer_agent()) { - show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, - _L("The selected printer does not support file transfer.")); - return; - } - IPrinterAgent::FileTransferCallbacks callbacks; - callbacks.on_destinations = [this](int res, int resp_ec, std::string json_res) { - CallAfter([this, res, resp_ec, json_res = std::move(json_res)] { + nlohmann::json media_ability = {{"cmd_type", 7}}; + m_filetransfer_mediability_job = std::make_unique(module(), std::string(media_ability.dump())); + m_filetransfer_mediability_job->on_result([this](int res, int resp_ec, std::string json_res, std::vector bin_res) { + //this pl + CallAfter([this, res, resp_ec, json_res] { if (res == 0) // 0 is success { show_status(PrintDialogStatus::PrintStatusReadingFinished); @@ -1821,41 +1889,59 @@ void SendToPrinterDialog::CreateMediaAbilityJob() show_status(PrintDialogStatus::PrintStatusPublicInitFailed); update_print_status_msg(ParseErrorCode(resp_ec), false, true); } - }); - }; - callbacks.file_transfer_error = [this] { - CallAfter([this] { - show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, - _L("The selected printer does not support file transfer.")); - }); - }; - agent->get_printer_agent()->get_file_destinations(std::move(callbacks)); + }); + }); + // Guard against a null transfer tunnel before dereferencing. + if (m_filetransfer_tunnel) { + m_filetransfer_mediability_job->start_on(*m_filetransfer_tunnel); + } else { + BOOST_LOG_TRIVIAL(info) << "CreateMediaAbilityJob: file transfer tunnel is null"; + } } void SendToPrinterDialog::CreateUploadFileJob(const std::string &path, const std::string &name) { - NetworkAgent *agent = wxGetApp().getAgent(); - if (!agent || !agent->get_printer_agent()) { - show_file_transfer_error(PrintDialogStatus::PrintStatusPublicUploadFiled, - _L("The selected printer does not support file transfer.")); - return; + nlohmann::json upload_params = { + {"cmd_type", 5}, + }; + upload_params["dest_storage"] = m_selected_storage; + upload_params["dest_name"] = name; // filenme no path + upload_params["file_path"] = path; + + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Begin CreateUploadFileJob"; + m_filetransfer_uploadfile_job = std::make_unique(module(), std::string(upload_params.dump())); + m_filetransfer_uploadfile_job->on_result([this](int res, int resp_ec, std::string json_res, std::vector bin_res) { // + CallAfter([this, res, resp_ec, json_res, bin_res] { + UploadFileRessultCallback(res, resp_ec,json_res, bin_res); + }); + }); + + m_filetransfer_uploadfile_job->on_msg([this](int kind, std::string json_res) { + CallAfter([this, kind, json_res] { + if (kind == 0) { + try + { + auto js = nlohmann::json::parse(json_res); + int progress = js["progress"].get(); + UploadFileProgressCallback(progress); + } + catch (const nlohmann::json::exception& e) + { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": " << e.what(); + } + catch (...) + { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": " << "parse_json failed! "; + } + } + }); + }); + // Guard against a null transfer tunnel before dereferencing. + if (m_filetransfer_tunnel) { + m_filetransfer_uploadfile_job->start_on(*m_filetransfer_tunnel); + } else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": file transfer tunnel is null"; } - IPrinterAgent::FileTransferCallbacks callbacks; - callbacks.on_progress = [this](int progress) { - CallAfter([this, progress] { UploadFileProgressCallback(progress); }); - }; - callbacks.on_result = [this](int res, int resp_ec, std::string json_res, std::vector bin_res) { - CallAfter([this, res, resp_ec, json_res = std::move(json_res), bin_res = std::move(bin_res)] { - UploadFileRessultCallback(res, resp_ec, std::move(json_res), std::move(bin_res)); - }); - }; - callbacks.file_transfer_error = [this] { - CallAfter([this] { - show_file_transfer_error(PrintDialogStatus::PrintStatusPublicUploadFiled, - _L("The selected printer does not support file transfer.")); - }); - }; - agent->get_printer_agent()->upload_file(path, name, m_selected_storage, std::move(callbacks)); } void SendToPrinterDialog::UploadFileProgressCallback(int progress) @@ -1874,8 +1960,6 @@ void SendToPrinterDialog::UploadFileProgressCallback(int progress) wxEVT_TIMER, [this](auto e) { show_status(PrintDialogStatus::PrintStatusPublicUploadFiled); - if (auto agent = wxGetApp().getAgent(); agent && agent->get_printer_agent()) - agent->get_printer_agent()->cancel_file_transfer(); update_print_status_msg( _L("File upload timed out. Please check if the firmware version supports this operation or verify if the printer is functioning properly."), false, true); }, @@ -1904,6 +1988,8 @@ void SendToPrinterDialog::UploadFileRessultCallback(int res, int resp_ec, std::s update_print_status_msg(ParseErrorCode(resp_ec), false, true); else update_print_status_msg(_L("Sending failed, please try again!"), false, true); + m_filetransfer_uploadfile_job.reset(); + m_filetransfer_uploadfile_job = nullptr; } } diff --git a/src/slic3r/GUI/SendToPrinter.hpp b/src/slic3r/GUI/SendToPrinter.hpp index 2d0073ce59..14493a1f20 100644 --- a/src/slic3r/GUI/SendToPrinter.hpp +++ b/src/slic3r/GUI/SendToPrinter.hpp @@ -24,7 +24,6 @@ #include #include -#include "IPrinterAgent.hpp" #include "SelectMachine.hpp" #include "GUI_Utils.hpp" #include "wxExtensions.hpp" @@ -44,6 +43,8 @@ namespace Slic3r { +class FileTransferTunnel; +class FileTransferJob; namespace GUI { @@ -170,6 +171,9 @@ private: enum ConnectionStatus { NOT_START, CONNECTING, CONNECTED, CONNECTION_FAILED, DISCONNECTED }; ConnectionStatus m_connection_status{ConnectionStatus::NOT_START}; + std::unique_ptr m_filetransfer_tunnel; + std::unique_ptr m_filetransfer_mediability_job; + std::unique_ptr m_filetransfer_uploadfile_job; wxDateTime m_last_refresh_time; public: @@ -219,10 +223,10 @@ public: private: void ResetConnectMethod(); void ResetTunnelAndJob(); - void show_file_transfer_error(PrintDialogStatus status, wxString message); void OnConnection(bool is_success, int error_code, std::string error_msg); void CreateMediaAbilityJob(); void CreateUploadFileJob(const std::string &path, const std::string &name); + void ChangeConnectMethod(); void UploadFileProgressCallback(int progress); void UploadFileRessultCallback(int res, int resp_ec, std::string json_res, std::vector bin_res); void Reset(); diff --git a/src/slic3r/Utils/BBLPrinterAgent.cpp b/src/slic3r/Utils/BBLPrinterAgent.cpp index 3b81a6fbc5..c1f5b5666a 100644 --- a/src/slic3r/Utils/BBLPrinterAgent.cpp +++ b/src/slic3r/Utils/BBLPrinterAgent.cpp @@ -1,6 +1,5 @@ #include "BBLPrinterAgent.hpp" #include "BBLNetworkPlugin.hpp" -#include "FileTransferUtils.hpp" #include "IPrinterAgent.hpp" #include "NetworkAgentFactory.hpp" #include "NetworkAgent.hpp" @@ -11,395 +10,13 @@ #include #include #include -#include namespace Slic3r { -// ============================================================================ -// File Transfer (Bambu eMMC tunnel ABI) -// ============================================================================ - -BBLFileTransferTunnel::BBLFileTransferTunnel(const std::string &url) : IFileTransferTunnel(url) -{ - FileTransferModule &m = module(); - m_ = &m; - // Guard against missing symbols in older Bambu networking plugins. - // These symbols were added in a newer plugin ABI; if the installed - // plugin predates them, ft_tunnel_create/ft_tunnel_set_status_cb - // will be null and calling them crashes. - if (!m_->ft_tunnel_create || !m_->ft_tunnel_set_status_cb) { - throw std::runtime_error("Bambu networking plugin is too old: missing ft_tunnel_* symbols. " - "Please update the networking plugin."); - } - FT_TunnelHandle *h{}; - if (m_->ft_tunnel_create(url.c_str(), &h) != 0 || !h) { - throw std::runtime_error("ft_tunnel_create failed"); - } - h_ = h; - - // C API: ft_status_cb(void* user, int old_status, int new_status, int err, const char* msg) - auto tramp = [](void *user, int old_status, int new_status, int err_code, const char *msg) noexcept { - auto *self = reinterpret_cast(user); - self->status_ = new_status; - if (!self->status_cb_) return; - try { - self->status_cb_(old_status, new_status, err_code, std::string(msg ? msg : "")); - } catch (...) {} - }; - if (m_->ft_tunnel_set_status_cb(h_, tramp, this) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_tunnel_set_status_cb failed"); } -} - -void BBLFileTransferTunnel::start_connect() -{ - // C API: ft_conn_cb(void* user, int ok, int err, const char* msg) - auto tramp = [](void *user, int ok, int ec, const char *msg) noexcept { - auto *pcb = reinterpret_cast(user); - if (!pcb) return; - try { - (*pcb)(ok == 0, ec, std::string(msg ? msg : "")); - } catch (...) {} - }; - if (m_->ft_tunnel_start_connect(h_, tramp, &conn_cb_) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_tunnel_start_connect failed"); } -} - -bool BBLFileTransferTunnel::sync_start_connect() -{ - return m_->ft_tunnel_sync_connect(h_) == FT_OK; -} - -void BBLFileTransferTunnel::shutdown() -{ - if (m_->ft_tunnel_shutdown) (void) m_->ft_tunnel_shutdown(h_); -} - -BBLFileTransferJob::BBLFileTransferJob(const std::string ¶ms_json) : IFileTransferJob(params_json) -{ - m_ = &module(); - FT_JobHandle *h{}; - if (m_->ft_job_create(params_json.c_str(), &h) != 0 || !h) { - throw std::runtime_error("ft_job_create failed"); - } - h_ = h; - - // C API: ft_job_result_cb(void* user, int tunnel_err, ft_job_result result) - auto tramp = [](void *user, ft_job_result r) noexcept { - auto *self = reinterpret_cast(user); - if (!self) return; - - try { - self->finished_ = true; - self->solve_result(r); - if (self->result_cb_) self->result_cb_(self->res_, self->resp_ec_, self->res_json_, self->res_bin_); - } catch (...) { - // swallow - } - - try { - if (auto *mod = self ? self->m_ : nullptr) { - if (mod->ft_job_result_destroy) - mod->ft_job_result_destroy(&r); - else if (mod->ft_free) { - if (r.json) mod->ft_free((void *) r.json); - if (r.bin) mod->ft_free((void *) r.bin); - } - } - } catch (...) {} - }; - - if (m_->ft_job_set_result_cb(h_, tramp, this) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_job_set_result_cb failed"); } -} - -bool BBLFileTransferJob::get_result(int &ec, int &resp_ec, std::string &json, std::vector &bin, uint32_t timeout_ms) -{ - if (!h_) throw std::runtime_error("job handle invalid"); - ft_job_result result; - if (m_->ft_job_get_result(h_, timeout_ms, &result) == ft_err::FT_EXCEPTION) return false; - solve_result(result); - m_->ft_job_result_destroy(&result); - ec = res_; - resp_ec = res_; - json = res_json_; - bin = res_bin_; - return true; -} - -void BBLFileTransferJob::start_on(IFileTransferTunnel &t) -{ - if (!h_) throw std::runtime_error("job handle invalid"); - auto *handle = reinterpret_cast(t.native()); - if (m_->ft_tunnel_start_job(handle, h_) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_tunnel_start_job failed"); } -} - -void BBLFileTransferJob::on_msg(MsgCb cb) -{ - IFileTransferJob::on_msg(std::move(cb)); - if (!h_) return; - - // C API: ft_job_msg_cb(void* user, ft_job_msg msg) - auto tramp = [](void *user, ft_job_msg m) noexcept { - auto *self = reinterpret_cast(user); - if (!self) return; - try { - if (self->msg_cb_) { self->msg_cb_(m.kind, std::string(m.json ? m.json : "")); } - } catch (...) {} - - try { - if (auto *mod = self->m_) { - if (mod->ft_job_msg_destroy) - mod->ft_job_msg_destroy(&m); - else if (mod->ft_free && m.json) - mod->ft_free((void *) m.json); - } - } catch (...) {} - }; - - if (m_->ft_job_set_msg_cb(h_, tramp, this) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_job_set_msg_cb failed"); } -} - -bool BBLFileTransferJob::try_get_msg(int &kind, std::string &json) -{ - if (!h_) return false; - ft_job_msg m{}; - int rc = m_->ft_job_try_get_msg(h_, &m); - if (rc != 0) return false; - - kind = m.kind; - json.assign(m.json ? m.json : ""); - - if (m_->ft_job_msg_destroy) - m_->ft_job_msg_destroy(&m); - else if (m_->ft_free && m.json) - m_->ft_free((void *) m.json); - - return true; -} - -bool BBLFileTransferJob::get_msg(uint32_t timeout_ms, int &kind, std::string &json) -{ - if (!h_) return false; - ft_job_msg m{}; - int rc = m_->ft_job_get_msg(h_, timeout_ms, &m); - if (rc != 0) return false; - - kind = m.kind; - json.assign(m.json ? m.json : ""); - - if (m_->ft_job_msg_destroy) - m_->ft_job_msg_destroy(&m); - else if (m_->ft_free && m.json) - m_->ft_free((void *) m.json); - - return true; -} - -void BBLFileTransferJob::solve_result(ft_job_result result) -{ - res_ = result.ec; - resp_ec_ = result.resp_ec; - - res_bin_.clear(); - if (result.bin && result.bin_size) res_bin_.assign(reinterpret_cast(result.bin), - reinterpret_cast(result.bin) + result.bin_size); - res_json_.assign(result.json ? result.json : ""); -} - BBLPrinterAgent::BBLPrinterAgent() = default; BBLPrinterAgent::~BBLPrinterAgent() = default; -void BBLPrinterAgent::prepare_file_transfer(const FileTransferRequest& request, FileTransferCallbacks cb) -{ - cancel_file_transfer(); - m_file_transfer_request = request; - m_file_transfer_callbacks = std::move(cb); - m_file_transfer_tcp = true; - m_file_transfer_try_count = 0; - start_file_transfer_attempt(++m_file_transfer_generation); -} - -void BBLPrinterAgent::start_file_transfer_attempt(uint64_t generation) -{ - FileTransferURLParams params; - params.url_state = m_file_transfer_tcp ? URL_TCP : URL_TUTK; - params.ip_address = m_file_transfer_request.device_ip; - params.username = default_lan_username(); - params.password = m_file_transfer_request.access_code; - params.device_id = m_file_transfer_request.device_id; - params.network_version = m_file_transfer_request.network_version; - params.device_version = m_file_transfer_request.device_version; - params.refresh_url = m_file_transfer_request.refresh_url; - params.client_id = m_file_transfer_request.client_id; - params.client_version = m_file_transfer_request.client_version; - - auto handle_url = [this, generation](FileTransferURLResult result) { - if (generation != m_file_transfer_generation) - return; - if (!result.is_success) { - handle_file_transfer_connection(generation, false, result.error_code, "file-transfer URL unavailable"); - return; - } - - try { - m_file_transfer_tunnel = std::make_unique(result.url); - } catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "BBLPrinterAgent: failed to create file-transfer tunnel: " << e.what(); - m_file_transfer_tunnel.reset(); - } - - if (!m_file_transfer_tunnel || !m_file_transfer_tunnel->check_valid()) { - handle_file_transfer_connection(generation, false, -1, "file-transfer tunnel unavailable"); - return; - } - - m_file_transfer_tunnel->on_connection([this, generation](bool is_success, int error_code, std::string error_msg) { - handle_file_transfer_connection(generation, is_success, error_code, std::move(error_msg)); - }); - m_file_transfer_tunnel->start_connect(); - }; - - if (m_file_transfer_tcp) { - get_file_transfer_url(m_file_transfer_request.device_id, std::move(handle_url), params); - return; - } - - if (!m_cloud_agent) { - handle_file_transfer_connection(generation, false, -1, "cloud file-transfer URL unavailable"); - return; - } - - const std::string protocols = "\"tutk\""; - m_cloud_agent->get_camera_url( - m_file_transfer_request.device_id + "|" + m_file_transfer_request.device_version + "|" + protocols, - [handle_url = std::move(handle_url)](CameraURLResult result) mutable { - FileTransferURLResult transfer_result; - transfer_result.is_success = result.is_success; - transfer_result.url = std::move(result.url); - transfer_result.error_code = result.error_code; - handle_url(std::move(transfer_result)); - }, - CameraURLParams{ - "", "", "", LVL_None, - m_file_transfer_request.device_id, - m_file_transfer_request.network_version, - m_file_transfer_request.device_version, - m_file_transfer_request.refresh_url, - m_file_transfer_request.client_id, - m_file_transfer_request.client_version, - true - }); -} - -void BBLPrinterAgent::handle_file_transfer_connection(uint64_t generation, bool is_success, int error_code, std::string error_msg) -{ - if (generation != m_file_transfer_generation) - return; - if (is_success) { - if (m_file_transfer_callbacks.on_connection) - m_file_transfer_callbacks.on_connection(true, error_code, std::move(error_msg)); - return; - } - - // Preserve the existing dialog fallback order: TCP, then TUTK for cloud - // printers, and finally the legacy FTP path handled by SendJob. - m_file_transfer_tunnel.reset(); - if (!m_file_transfer_request.lan_mode && m_file_transfer_tcp && m_file_transfer_try_count == 0) { - m_file_transfer_tcp = false; - ++m_file_transfer_try_count; - start_file_transfer_attempt(generation); - return; - } - - if (m_file_transfer_callbacks.on_connection) - m_file_transfer_callbacks.on_connection(false, error_code, std::move(error_msg)); -} - -void BBLPrinterAgent::get_file_destinations(FileTransferCallbacks cb) -{ - if (!m_file_transfer_tunnel || !m_file_transfer_tunnel->check_valid()) { - if (cb.file_transfer_error) - cb.file_transfer_error(); - return; - } - - nlohmann::json params = {{"cmd_type", 7}}; - try { - m_file_transfer_job = std::make_unique(params.dump()); - } catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "BBLPrinterAgent: failed to create media-ability job: " << e.what(); - m_file_transfer_job.reset(); - } - - if (!m_file_transfer_job || !m_file_transfer_job->check_valid()) { - if (cb.file_transfer_error) - cb.file_transfer_error(); - return; - } - - m_file_transfer_job->on_result([cb = std::move(cb)](int result, int response_error, std::string json_result, - std::vector) { - if (cb.on_destinations) - cb.on_destinations(result, response_error, std::move(json_result)); - }); - m_file_transfer_job->start_on(*m_file_transfer_tunnel); -} - -void BBLPrinterAgent::upload_file(const std::string& path, const std::string& name, const std::string& destination, - FileTransferCallbacks cb) -{ - if (!m_file_transfer_tunnel || !m_file_transfer_tunnel->check_valid()) { - if (cb.file_transfer_error) - cb.file_transfer_error(); - return; - } - - nlohmann::json params = { - {"cmd_type", 5}, - {"dest_storage", destination}, - {"dest_name", name}, - {"file_path", path} - }; - - try { - m_file_transfer_job = std::make_unique(params.dump()); - } catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "BBLPrinterAgent: failed to create upload job: " << e.what(); - m_file_transfer_job.reset(); - } - - if (!m_file_transfer_job || !m_file_transfer_job->check_valid()) { - if (cb.file_transfer_error) - cb.file_transfer_error(); - return; - } - - auto callbacks = std::make_shared(std::move(cb)); - m_file_transfer_job->on_result([callbacks](int result, int response_error, std::string json_result, - std::vector binary_result) { - if (callbacks->on_result) - callbacks->on_result(result, response_error, std::move(json_result), std::move(binary_result)); - }); - m_file_transfer_job->on_msg([callbacks](int kind, std::string json_result) { - if (kind == 0 && callbacks->on_progress) { - try { - callbacks->on_progress(nlohmann::json::parse(json_result).at("progress").get()); - } catch (...) { - BOOST_LOG_TRIVIAL(error) << "BBLPrinterAgent: failed to parse upload progress"; - } - } - }); - m_file_transfer_job->start_on(*m_file_transfer_tunnel); -} - -void BBLPrinterAgent::cancel_file_transfer() -{ - ++m_file_transfer_generation; - if (m_file_transfer_job) - m_file_transfer_job->cancel(); - m_file_transfer_job.reset(); - m_file_transfer_tunnel.reset(); - m_file_transfer_callbacks = {}; -} - void BBLPrinterAgent::set_cloud_agent(std::shared_ptr cloud) { m_cloud_agent = cloud; @@ -702,10 +319,7 @@ int BBLPrinterAgent::get_file_transfer_url(std::string dev_id, std::function( @@ -1001,16 +606,6 @@ int BBLPrinterAgent::start_send_gcode_to_sdcard(PrintParams params, OnUpdateStat int BBLPrinterAgent::start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) { - if (params.connection_type == "lan" && params.print_type == "from_normal") { - const int verify_result = verify_local_print_access(params); - if (verify_result != 0) { - BOOST_LOG_TRIVIAL(error) << "LAN connection verification failed: result=" << verify_result - << ", dev_ip=" << params.dev_ip << ", dev_id=" << params.dev_id - << ", password_length=" << params.password.size(); - return ORCA_NETWORK_ERR_ACCESS_VERIFICATION_FAILED; - } - } - return dispatch_start( BBLNetworkPlugin::instance().get_start_local_print(), params, update_fn, cancel_fn); } diff --git a/src/slic3r/Utils/BBLPrinterAgent.hpp b/src/slic3r/Utils/BBLPrinterAgent.hpp index 685f422228..94f7ea8466 100644 --- a/src/slic3r/Utils/BBLPrinterAgent.hpp +++ b/src/slic3r/Utils/BBLPrinterAgent.hpp @@ -3,83 +3,12 @@ #include "IPrinterAgent.hpp" #include "ICloudServiceAgent.hpp" -#include "FileTransferUtils.hpp" #include #include #include namespace Slic3r { -/** - * BBLFileTransferTunnel - Bambu eMMC tunnel, backed by the Bambu network - * plugin's ft_tunnel_* ABI (see FileTransferUtils.hpp). Only BBLPrinterAgent - * constructs these; callers only ever see them through IFileTransferTunnel. - */ -class BBLFileTransferTunnel : public IFileTransferTunnel -{ -public: - BBLFileTransferTunnel(const std::string &url); - ~BBLFileTransferTunnel() override { reset(); } - - void start_connect() override; - bool sync_start_connect() override; - void shutdown() override; - bool check_valid() const override { return h_ != nullptr; } - void *native() const noexcept override { return h_; } - -private: - void reset() noexcept - { - if (h_) { - m_->ft_tunnel_release(h_); - h_ = nullptr; - } - } - - FileTransferModule *m_{}; - FT_TunnelHandle *h_{}; -}; - -/** - * BBLFileTransferJob - a single ft_job_* operation (media-ability query, - * file upload, ...) run on a BBLFileTransferTunnel. Same ABI-wrapper role - * as BBLFileTransferTunnel; only BBLPrinterAgent constructs these. - */ -class BBLFileTransferJob : public IFileTransferJob -{ -public: - explicit BBLFileTransferJob(const std::string ¶ms_json); - ~BBLFileTransferJob() override { reset(); } - - bool get_result(int &ec, int &resp_ec, std::string &json, std::vector &bin, uint32_t timeout_ms) override; - void start_on(IFileTransferTunnel &t) override; - // why: unlike on_result() (fires from a trampoline registered once in the ctor), - // ft_job_set_msg_cb is only wired up here, lazily, on first real subscriber - - // that ABI call has to happen in the concrete class, not the vendor-neutral base. - void on_msg(MsgCb cb) override; - bool try_get_msg(int &kind, std::string &json) override; - bool get_msg(uint32_t timeout_ms, int &kind, std::string &json) override; - void *native() const noexcept override { return h_; } - bool check_valid() const override { return h_ != nullptr; } - void cancel() override - { - if (m_->ft_job_cancel && h_) m_->ft_job_cancel(h_); - } - -private: - void reset() noexcept - { - if (h_) { - m_->ft_job_release(h_); - h_ = nullptr; - } - } - void solve_result(ft_job_result result); - - FileTransferModule *m_{}; - FT_JobHandle *h_{}; -}; - /** * BBLPrinterAgent - BBL DLL wrapper implementation of IPrinterAgent. * @@ -175,30 +104,11 @@ public: int set_queue_on_main_fn(QueueOnMainFn fn) override; FilamentSyncMode get_filament_sync_mode() const override; - void prepare_file_transfer(const FileTransferRequest& request, FileTransferCallbacks cb) override; - void get_file_destinations(FileTransferCallbacks cb) override; - void upload_file(const std::string& path, const std::string& name, const std::string& destination, - FileTransferCallbacks cb) override; - void cancel_file_transfer() override; - - private: - int verify_local_print_access(PrintParams params); - // why: the lan/cloud DECISION stays machine-side; keep this mechanical branch in sync with publish_json. int publish(const std::string& dev_id, const nlohmann::json& j, bool lan_mode); std::shared_ptr m_cloud_agent; - std::unique_ptr m_file_transfer_tunnel; - std::unique_ptr m_file_transfer_job; - FileTransferCallbacks m_file_transfer_callbacks; - FileTransferRequest m_file_transfer_request; - bool m_file_transfer_tcp{true}; - int m_file_transfer_try_count{0}; - uint64_t m_file_transfer_generation{0}; - - void start_file_transfer_attempt(uint64_t generation); - void handle_file_transfer_connection(uint64_t generation, bool is_success, int error_code, std::string error_msg); }; } // namespace Slic3r diff --git a/src/slic3r/Utils/FileTransferUtils.cpp b/src/slic3r/Utils/FileTransferUtils.cpp index 8601f6712e..822ccdf09d 100644 --- a/src/slic3r/Utils/FileTransferUtils.cpp +++ b/src/slic3r/Utils/FileTransferUtils.cpp @@ -1,4 +1,8 @@ +#include +#include #include "FileTransferUtils.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/DeviceCore/DevManager.h" namespace Slic3r { @@ -33,4 +37,191 @@ FileTransferModule::FileTransferModule(ModuleHandle networking_module, int requi ft_job_get_msg = sym_lookup(networking_, "ft_job_get_msg"); } +FileTransferTunnel::FileTransferTunnel(FileTransferModule &m, const std::string &url) : m_(&m) +{ + // Guard against missing symbols in older Bambu networking plugins. + // These symbols were added in a newer plugin ABI; if the installed + // plugin predates them, ft_tunnel_create/ft_tunnel_set_status_cb + // will be null and calling them crashes. + if (!m_->ft_tunnel_create || !m_->ft_tunnel_set_status_cb) { + throw std::runtime_error("Bambu networking plugin is too old: missing ft_tunnel_* symbols. " + "Please update the networking plugin."); + } + FT_TunnelHandle *h{}; + if (m_->ft_tunnel_create(url.c_str(), &h) != 0 || !h) { + throw std::runtime_error("ft_tunnel_create failed"); + } + h_ = h; + + // C API: ft_status_cb(void* user, int old_status, int new_status, int err, const char* msg) + auto tramp = [](void *user, int old_status, int new_status, int err_code, const char *msg) noexcept { + auto *self = reinterpret_cast(user); + self->status_ = new_status; + if (!self->status_cb_) return; + try { + self->status_cb_(old_status, new_status, err_code, std::string(msg ? msg : "")); + } catch (...) {} + }; + if (m_->ft_tunnel_set_status_cb(h_, tramp, this) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_tunnel_set_status_cb failed"); } +} + +void FileTransferTunnel::start_connect() +{ + // C API: ft_conn_cb(void* user, int ok, int err, const char* msg) + auto tramp = [](void *user, int ok, int ec, const char *msg) noexcept { + auto *pcb = reinterpret_cast(user); + if (!pcb) return; + try { + (*pcb)(ok == 0, ec, std::string(msg ? msg : "")); + } catch (...) {} + }; + if (m_->ft_tunnel_start_connect(h_, tramp, &conn_cb_) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_tunnel_start_connect failed"); } +} + +bool FileTransferTunnel::sync_start_connect() +{ + return m_->ft_tunnel_sync_connect(h_) == FT_OK; +} + +void FileTransferTunnel::on_connection(ConnectionCb cb) { conn_cb_ = std::move(cb); } +void FileTransferTunnel::on_status(TunnelStatusCb cb) { status_cb_ = std::move(cb); } + +void FileTransferTunnel::shutdown() +{ + if (m_->ft_tunnel_shutdown) (void) m_->ft_tunnel_shutdown(h_); +} + +FileTransferJob::FileTransferJob(FileTransferModule &m, const std::string ¶ms_json) : m_(&m) +{ + FT_JobHandle *h{}; + if (m_->ft_job_create(params_json.c_str(), &h) != 0 || !h) { + + } + h_ = h; + + // C API: ft_job_result_cb(void* user, int tunnel_err, ft_job_result result) + auto tramp = [](void *user, ft_job_result r) noexcept { + auto *self = reinterpret_cast(user); + if (!self) return; + + try { + self->finished_ = true; + self->solve_result(r); + + if (self->result_cb_) self->result_cb_(self->res_, self->resp_ec_, self->res_json_, self->res_bin_); + self->m_->ft_job_result_destroy(&r); + } catch (...) { + // swallow + } + + try { + if (auto *mod = self ? self->m_ : nullptr) { + if (mod->ft_job_result_destroy) + mod->ft_job_result_destroy(&r); + else if (mod->ft_free) { + if (r.json) mod->ft_free((void *) r.json); + if (r.bin) mod->ft_free((void *) r.bin); + } + } + } catch (...) {} + }; + + if (m_->ft_job_set_result_cb(h_, tramp, this) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_job_set_result_cb failed"); } +} + +void FileTransferJob::on_result(ResultCb cb) { result_cb_ = std::move(cb); } + +bool FileTransferJob::get_result(int &ec, int &resp_ec, std::string &json, std::vector &bin, uint32_t timeout_ms) +{ + if (!h_) throw std::runtime_error("job handle invalid"); + ft_job_result result; + if (m_->ft_job_get_result(h_, timeout_ms, &result) == ft_err::FT_EXCEPTION) return false; + solve_result(result); + m_->ft_job_result_destroy(&result); + ec = res_; + resp_ec = res_; + json = res_json_; + bin = res_bin_; + return true; +} + +void FileTransferJob::start_on(FileTransferTunnel &t) +{ + if (!h_) throw std::runtime_error("job handle invalid"); + if (m_->ft_tunnel_start_job(t.native(), h_) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_tunnel_start_job failed"); } +} + +void FileTransferJob::on_msg(MsgCb cb) +{ + msg_cb_ = std::move(cb); + if (!h_) return; + + // C API: ft_job_msg_cb(void* user, ft_job_msg msg) + auto tramp = [](void *user, ft_job_msg m) noexcept { + auto *self = reinterpret_cast(user); + if (!self) return; + try { + if (self->msg_cb_) { self->msg_cb_(m.kind, std::string(m.json ? m.json : "")); } + } catch (...) {} + + try { + if (auto *mod = self->m_) { + if (mod->ft_job_msg_destroy) + mod->ft_job_msg_destroy(&m); + else if (mod->ft_free && m.json) + mod->ft_free((void *) m.json); + } + } catch (...) {} + }; + + if (m_->ft_job_set_msg_cb(h_, tramp, this) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_job_set_msg_cb failed"); } +} + +bool FileTransferJob::try_get_msg(int &kind, std::string &json) +{ + if (!h_) return false; + ft_job_msg m{}; + int rc = m_->ft_job_try_get_msg(h_, &m); + if (rc != 0) return false; + + kind = m.kind; + json.assign(m.json ? m.json : ""); + + if (m_->ft_job_msg_destroy) + m_->ft_job_msg_destroy(&m); + else if (m_->ft_free && m.json) + m_->ft_free((void *) m.json); + + return true; +} + +bool FileTransferJob::get_msg(uint32_t timeout_ms, int &kind, std::string &json) +{ + if (!h_) return false; + ft_job_msg m{}; + int rc = m_->ft_job_get_msg(h_, timeout_ms, &m); + if (rc != 0) return false; + + kind = m.kind; + json.assign(m.json ? m.json : ""); + + if (m_->ft_job_msg_destroy) + m_->ft_job_msg_destroy(&m); + else if (m_->ft_free && m.json) + m_->ft_free((void *) m.json); + + return true; +} + +void FileTransferJob::solve_result(ft_job_result result) +{ + res_ = result.ec; + resp_ec_ = result.resp_ec; + + res_bin_.clear(); + if (result.bin && result.bin_size) res_bin_.assign(reinterpret_cast(result.bin), + reinterpret_cast(result.bin) + result.bin_size); + res_json_.assign(result.json ? result.json : ""); +} + } // namespace Slic3r diff --git a/src/slic3r/Utils/FileTransferUtils.hpp b/src/slic3r/Utils/FileTransferUtils.hpp index 2e0d162ddb..ba91621a91 100644 --- a/src/slic3r/Utils/FileTransferUtils.hpp +++ b/src/slic3r/Utils/FileTransferUtils.hpp @@ -1,7 +1,13 @@ #pragma once #include +#include +#include +#include #include #include +#include +#include +#include #include #ifdef _WIN32 @@ -128,11 +134,103 @@ struct FileTransferModule FileTransferModule &operator=(const FileTransferModule &) = delete; }; -// FileTransferTunnel/FileTransferJob (the OOP wrapper around the ft_tunnel_*/ -// ft_job_* ABI below) live in BBLPrinterAgent.hpp as BBLFileTransferTunnel/ -// BBLFileTransferJob, implementing IFileTransferTunnel/IFileTransferJob -// (IPrinterAgent.hpp) - this header stays the low-level symbol-table layer -// only, same role as bambu_networking.hpp's function pointer typedefs. +class FileTransferTunnel +{ +public: + using ConnectionCb = std::function; + using TunnelStatusCb = std::function; + + explicit FileTransferTunnel(FileTransferModule &m, const std::string &url); + ~FileTransferTunnel() { reset(); } + + FileTransferTunnel(const FileTransferTunnel &) = delete; + FileTransferTunnel &operator=(const FileTransferTunnel &) = delete; + FileTransferTunnel(FileTransferTunnel &&) = delete; + FileTransferTunnel &operator=(FileTransferTunnel &&) = delete; + + void start_connect(); + bool sync_start_connect(); + void on_connection(ConnectionCb cb); + void on_status(TunnelStatusCb cb); + + void shutdown(); + + int get_status() const { return status_; } + bool check_valid() const { return h_ != nullptr; } + FT_TunnelHandle *native() const noexcept { return h_; } + +private: + void reset() noexcept + { + if (h_) { + m_->ft_tunnel_release(h_); + h_ = nullptr; + } + } + + int status_{}; + FileTransferModule *m_{}; + FT_TunnelHandle *h_{}; + ConnectionCb conn_cb_{}; + TunnelStatusCb status_cb_{}; +}; + +class FileTransferJob +{ +public: + using ResultCb = std::function bin_res)>; + using MsgCb = std::function; + + explicit FileTransferJob(FileTransferModule &m, const std::string ¶ms_json); + ~FileTransferJob() { reset(); } + + FileTransferJob(const FileTransferJob &) = delete; + FileTransferJob &operator=(const FileTransferJob &) = delete; + FileTransferJob(FileTransferJob &&) = delete; + FileTransferJob &operator=(FileTransferJob &&) = delete; + + void on_result(ResultCb cb); + + bool get_result(int &ec, int &resp_ec, std::string &json, std::vector &bin, uint32_t timeout_ms); + + void start_on(FileTransferTunnel &t); + + void on_msg(MsgCb cb); + + bool try_get_msg(int &kind, std::string &json); + + bool get_msg(uint32_t timeout_ms, int &kind, std::string &json); + + FT_JobHandle *native() const noexcept { return h_; } + bool check_valid() const { return h_ != nullptr; } + bool finished() const { return finished_; } + + void cancel() + { + if (m_->ft_job_cancel && h_) m_->ft_job_cancel(h_); + } + +private: + void reset() noexcept + { + if (h_) { + m_->ft_job_release(h_); + h_ = nullptr; + } + } + + void solve_result(ft_job_result result); + + FileTransferModule *m_{}; + FT_JobHandle *h_{}; + ResultCb result_cb_{}; + MsgCb msg_cb_{}; + bool finished_ = false; + int res_ = 0; + int resp_ec_ = 0; + std::string res_json_; + std::vector res_bin_; +}; namespace detail { inline FileTransferModule *g_mod = nullptr; @@ -154,4 +252,4 @@ inline FileTransferModule &module() return *detail::g_mod; } -} // namespace Slic3r \ No newline at end of file +} // namespace Slic3r diff --git a/src/slic3r/Utils/IPrinterAgent.hpp b/src/slic3r/Utils/IPrinterAgent.hpp index 51df990cdb..0938066970 100644 --- a/src/slic3r/Utils/IPrinterAgent.hpp +++ b/src/slic3r/Utils/IPrinterAgent.hpp @@ -10,7 +10,6 @@ // -70xx is free: the vendor occupies -1..-25 and -10xx through -60xx. #define ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED -7010 // no translation exists for this command #define ORCA_NETWORK_ERR_CAP_NOT_AVAILABLE -7020 // a translation exists; this printer lacks the capability -#define ORCA_NETWORK_ERR_ACCESS_VERIFICATION_FAILED -7030 // printer access preflight failed before printing #include #include #include @@ -50,77 +49,6 @@ enum class FilamentSyncMode { pull ///< On-demand fetch via REST API (blocking call) }; -class IFileTransferTunnel -{ -public: - using ConnectionCb = std::function; - using TunnelStatusCb = std::function; - - explicit IFileTransferTunnel(const std::string& url) : url_(url) {} - virtual ~IFileTransferTunnel() = default; - - IFileTransferTunnel(const IFileTransferTunnel&) = delete; - IFileTransferTunnel& operator=(const IFileTransferTunnel&) = delete; - IFileTransferTunnel(IFileTransferTunnel&&) = delete; - IFileTransferTunnel& operator=(IFileTransferTunnel&&) = delete; - - virtual void start_connect() = 0; - virtual bool sync_start_connect() = 0; - virtual void on_connection(ConnectionCb cb) { conn_cb_ = std::move(cb); } - virtual void on_status(TunnelStatusCb cb) { status_cb_ = std::move(cb); } - - virtual void shutdown() = 0; - - virtual int get_status() const { return status_; } - virtual bool check_valid() const = 0; - - // why: IFileTransferJob::start_on() only ever sees a tunnel through this interface, - // but needs the concrete backend handle to hand to its own start-job call - native() - // is the type-erased escape hatch, same pattern IFileTransferJob::native() already uses. - virtual void *native() const noexcept { return nullptr; } - -protected: - std::string url_; - int status_{}; - ConnectionCb conn_cb_{}; - TunnelStatusCb status_cb_{}; -}; - -class IFileTransferJob { -public: - using ResultCb = std::function bin_res)>; - using MsgCb = std::function; - - explicit IFileTransferJob(const std::string ¶ms_json) : params_json_(params_json) {} - virtual ~IFileTransferJob() = default; - - IFileTransferJob(const IFileTransferJob &) = delete; - IFileTransferJob &operator=(const IFileTransferJob &) = delete; - IFileTransferJob(IFileTransferJob &&) = delete; - IFileTransferJob &operator=(IFileTransferJob &&) = delete; - - virtual void on_result(ResultCb cb) { result_cb_ = std::move(cb); } - virtual bool get_result(int &ec, int &resp_ec, std::string &json, std::vector &bin, uint32_t timeout_ms) = 0; - virtual void start_on(IFileTransferTunnel &t) = 0; - virtual void on_msg(MsgCb cb) { msg_cb_ = std::move(cb); } - virtual bool try_get_msg(int &kind, std::string &json) = 0; - virtual bool get_msg(uint32_t timeout_ms, int &kind, std::string &json) = 0; - virtual void *native() const noexcept { return nullptr; } - virtual bool check_valid() const = 0; - virtual bool finished() const { return finished_; } - virtual void cancel() = 0; - -protected: - std::string params_json_; - ResultCb result_cb_{}; - MsgCb msg_cb_{}; - bool finished_ = false; - int res_ = 0; - int resp_ec_ = 0; - std::string res_json_; - std::vector res_bin_; -}; - /** * IPrinterAgent - Interface for printer operations. * @@ -425,55 +353,6 @@ public: */ virtual bool fetch_filament_info(std::string dev_id) { return false; } - struct FileTransferRequest - { - std::string device_id; - std::string device_ip; - std::string access_code; - std::string network_version; - std::string device_version; - std::string refresh_url; - std::string client_id; - std::string client_version; - bool lan_mode{false}; - }; - - struct FileTransferCallbacks - { - std::function on_connection; - std::function on_destinations; - std::function on_progress; - std::function binary_result)> on_result; - std::function file_transfer_error; - }; - - /** - * Prepare the agent's file-transfer session. The transport is agent-owned; - * callers must not need to know whether it is a tunnel, HTTP connection, - * or another protocol. - */ - virtual void prepare_file_transfer(const FileTransferRequest&, FileTransferCallbacks cb) - { - if (cb.file_transfer_error) - cb.file_transfer_error(); - } - - /** Query the destinations available for the prepared transfer session. */ - virtual void get_file_destinations(FileTransferCallbacks cb) - { - if (cb.file_transfer_error) - cb.file_transfer_error(); - } - - /** Upload a file using the prepared transfer session. */ - virtual void upload_file(const std::string&, const std::string&, const std::string&, FileTransferCallbacks cb) - { - if (cb.file_transfer_error) - cb.file_transfer_error(); - } - - /** Cancel the current file-transfer operation and release its resources. */ - virtual void cancel_file_transfer() {} }; } // namespace Slic3r