diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 3158ed0a87..f979c9405c 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -619,6 +619,8 @@ set(SLIC3R_GUI_SOURCES Utils/WebSocketClient.hpp Utils/WxFontUtils.cpp Utils/WxFontUtils.hpp + Utils/FileTransferUtils.cpp + Utils/FileTransferUtils.hpp ) add_subdirectory(GUI/DeviceCore) diff --git a/src/slic3r/GUI/StatusPanel.cpp b/src/slic3r/GUI/StatusPanel.cpp index bcc5b6e900..9b9d307bd2 100644 --- a/src/slic3r/GUI/StatusPanel.cpp +++ b/src/slic3r/GUI/StatusPanel.cpp @@ -41,7 +41,6 @@ #include "SafetyOptionsDialog.hpp" - namespace Slic3r { namespace GUI { #define TEMP_THRESHOLD_VAL 2 diff --git a/src/slic3r/Utils/FileTransferUtils.cpp b/src/slic3r/Utils/FileTransferUtils.cpp new file mode 100644 index 0000000000..5d0b3e04f2 --- /dev/null +++ b/src/slic3r/Utils/FileTransferUtils.cpp @@ -0,0 +1,213 @@ +#include +#include +#include "FileTransferUtils.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/DeviceCore/DevManager.h" + +namespace Slic3r { + +FileTransferModule::FileTransferModule(ModuleHandle networking_module, int required_abi_version) : networking_(networking_module) +{ + // basic + ft_abi_version = sym_lookup(networking_, "ft_abi_version"); + ft_free = sym_lookup(networking_, "ft_free"); + ft_job_result_destroy = sym_lookup(networking_, "ft_job_result_destroy"); + ft_job_msg_destroy = sym_lookup(networking_, "ft_job_msg_destroy"); + + // tunnel + ft_tunnel_create = sym_lookup(networking_, "ft_tunnel_create"); + ft_tunnel_retain = sym_lookup(networking_, "ft_tunnel_retain"); + ft_tunnel_release = sym_lookup(networking_, "ft_tunnel_release"); + ft_tunnel_start_connect = sym_lookup(networking_, "ft_tunnel_start_connect"); + ft_tunnel_set_status_cb = sym_lookup(networking_, "ft_tunnel_set_status_cb"); + ft_tunnel_shutdown = sym_lookup(networking_, "ft_tunnel_shutdown"); + + // job + ft_job_create = sym_lookup(networking_, "ft_job_create"); + ft_job_retain = sym_lookup(networking_, "ft_job_retain"); + ft_job_release = sym_lookup(networking_, "ft_job_release"); + ft_job_set_result_cb = sym_lookup(networking_, "ft_job_set_result_cb"); + ft_job_get_result = sym_lookup(networking_, "ft_job_get_result"); + ft_tunnel_start_job = sym_lookup(networking_, "ft_tunnel_start_job"); + ft_job_cancel = sym_lookup(networking_, "ft_job_cancel"); + + ft_job_set_msg_cb = sym_lookup(networking_, "ft_job_set_msg_cb"); + ft_job_try_get_msg = sym_lookup(networking_, "ft_job_try_get_msg"); + ft_job_get_msg = sym_lookup(networking_, "ft_job_get_msg"); +} + +FileTransferTunnel::FileTransferTunnel(FileTransferModule &m, const std::string &url) : m_(&m) +{ + FT_TunnelHandle *h{}; + if (m_->ft_tunnel_create(url.c_str(), &h) != 0 || !h) { + + } + 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"); } +} + +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 \ No newline at end of file diff --git a/src/slic3r/Utils/FileTransferUtils.hpp b/src/slic3r/Utils/FileTransferUtils.hpp new file mode 100644 index 0000000000..bb2e7d29a7 --- /dev/null +++ b/src/slic3r/Utils/FileTransferUtils.hpp @@ -0,0 +1,251 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#define FT_CALL __cdecl +using ModuleHandle = HMODULE; +#else +#include +#define FT_CALL +using ModuleHandle = void *; +#endif + +namespace Slic3r { + +extern "C" { + +struct ft_job_result +{ + int ec; + int resp_ec; + const char *json; + const void *bin; + uint32_t bin_size; +}; + +struct ft_job_msg +{ + int kind; + const char *json; +}; + +struct FT_TunnelHandle; +struct FT_JobHandle; + +typedef enum { FT_OK = 0, FT_EINVAL = -1, FT_ESTATE = -2, FT_EIO = -3, FT_ETIMEOUT = -4, FT_ECANCELLED = -5, FT_EXCEPTION = -6, FT_EUNKNOWN = -128 } ft_err; + +} + + +inline void *sym_lookup_raw(ModuleHandle mh, const char *name) +{ +#ifdef _WIN32 + return mh ? reinterpret_cast(::GetProcAddress(mh, name)) : nullptr; +#else + return mh ? ::dlsym(mh, name) : nullptr; +#endif +} + +template inline T sym_lookup(ModuleHandle mh, const char *name) +{ + void *p = sym_lookup_raw(mh, name); + if (p) { + return reinterpret_cast(p); + } + else { + BOOST_LOG_TRIVIAL(info) << std::string("symbol not found: ") + name; + return nullptr; + } +} + +using fn_ft_abi_version = int(FT_CALL *)(); +using fn_ft_free = void(FT_CALL *)(void *); +using fn_ft_job_result_destroy = void(FT_CALL *)(ft_job_result *); +using fn_ft_job_msg_destroy = void(FT_CALL *)(ft_job_msg *); + +using fn_ft_tunnel_create = ft_err(FT_CALL *)(const char *url, FT_TunnelHandle **out); +using fn_ft_tunnel_retain = void(FT_CALL *)(FT_TunnelHandle *); +using fn_ft_tunnel_release = void(FT_CALL *)(FT_TunnelHandle *); +using fn_ft_tunnel_start_connect = ft_err(FT_CALL *)(FT_TunnelHandle *, void(FT_CALL *)(void *user, int ok, int err, const char *msg), void *user); +using fn_ft_tunnel_set_status_cb = ft_err(FT_CALL *)(FT_TunnelHandle *, void(FT_CALL *)(void *user, int old_status, int new_status, int err, const char *msg), void *user); +using fn_ft_tunnel_shutdown = ft_err(FT_CALL *)(FT_TunnelHandle *); + +using fn_ft_job_create = ft_err(FT_CALL *)(const char *params_json, FT_JobHandle **out); +using fn_ft_job_retain = void(FT_CALL *)(FT_JobHandle *); +using fn_ft_job_release = void(FT_CALL *)(FT_JobHandle *); +using fn_ft_job_set_result_cb = ft_err(FT_CALL *)(FT_JobHandle *, void(FT_CALL *)(void *user, ft_job_result result), void *user); +using fn_ft_job_get_result = ft_err(FT_CALL *)(FT_JobHandle *, uint32_t timeout_ms, ft_job_result *out_result); +using fn_ft_tunnel_start_job = ft_err(FT_CALL *)(FT_TunnelHandle *, FT_JobHandle *); +using fn_ft_job_cancel = ft_err(FT_CALL *)(FT_JobHandle *); + +using fn_ft_job_msg_destroy = void(FT_CALL *)(ft_job_msg *); +using fn_ft_job_set_msg_cb = ft_err(FT_CALL *)(FT_JobHandle *, void(FT_CALL *)(void *user, ft_job_msg msg), void *user); +using fn_ft_job_try_get_msg = ft_err(FT_CALL *)(FT_JobHandle *, ft_job_msg *out_msg); +using fn_ft_job_get_msg = ft_err(FT_CALL *)(FT_JobHandle *, uint32_t timeout_ms, ft_job_msg *out_msg); + +/// +/// FileTransferModule, support file operations on printer +/// +struct FileTransferModule +{ + fn_ft_abi_version ft_abi_version{}; + fn_ft_free ft_free{}; + fn_ft_job_result_destroy ft_job_result_destroy{}; + fn_ft_job_msg_destroy ft_job_msg_destroy{}; + + // tunnel(connection) + fn_ft_tunnel_create ft_tunnel_create{}; + fn_ft_tunnel_retain ft_tunnel_retain{}; + fn_ft_tunnel_release ft_tunnel_release{}; + fn_ft_tunnel_start_connect ft_tunnel_start_connect{}; + fn_ft_tunnel_set_status_cb ft_tunnel_set_status_cb{}; + fn_ft_tunnel_shutdown ft_tunnel_shutdown{}; + + // job(operation) + fn_ft_job_create ft_job_create{}; + fn_ft_job_retain ft_job_retain{}; + fn_ft_job_release ft_job_release{}; + fn_ft_job_set_result_cb ft_job_set_result_cb{}; + fn_ft_job_get_result ft_job_get_result{}; + fn_ft_tunnel_start_job ft_tunnel_start_job{}; + fn_ft_job_cancel ft_job_cancel{}; + + fn_ft_job_set_msg_cb ft_job_set_msg_cb{}; + fn_ft_job_try_get_msg ft_job_try_get_msg{}; + fn_ft_job_get_msg ft_job_get_msg{}; + + ModuleHandle networking_{}; + + explicit FileTransferModule(ModuleHandle networking_module, int required_abi_version = 1); + + FileTransferModule(const FileTransferModule &) = delete; + FileTransferModule &operator=(const FileTransferModule &) = delete; +}; + +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(); + 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; +} + +inline void InitFTModule(ModuleHandle networking_module, int abi_required = 1) +{ + if (detail::g_mod) throw std::runtime_error("Slic3r::InitFTModule called twice"); + detail::g_mod = new FileTransferModule(networking_module, abi_required); +} +inline void UnloadFTModule() noexcept +{ + delete detail::g_mod; + detail::g_mod = nullptr; +} +inline FileTransferModule &module() +{ + if (!detail::g_mod) throw std::runtime_error("Slic3r::FTModule not initialized. Call Init() first."); + return *detail::g_mod; +} + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/Utils/NetworkAgent.cpp b/src/slic3r/Utils/NetworkAgent.cpp index 14b36285b6..5f19e8f4e2 100644 --- a/src/slic3r/Utils/NetworkAgent.cpp +++ b/src/slic3r/Utils/NetworkAgent.cpp @@ -10,7 +10,7 @@ #include "libslic3r/Utils.hpp" #include "NetworkAgent.hpp" - +#include "slic3r/Utils/FileTransferUtils.hpp" using namespace BBL; @@ -286,6 +286,9 @@ int NetworkAgent::initialize_network_module(bool using_backup) } BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", successfully loaded library %1%, module %2%")%library %netwoking_module; + // load file transfer interface + InitFTModule(netwoking_module); + //load the functions check_debug_consistent_ptr = reinterpret_cast(get_network_function("bambu_network_check_debug_consistent")); get_version_ptr = reinterpret_cast(get_network_function("bambu_network_get_version")); @@ -394,6 +397,7 @@ int NetworkAgent::initialize_network_module(bool using_backup) int NetworkAgent::unload_network_module() { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", network module %1%")%netwoking_module; + UnloadFTModule(); #if defined(_MSC_VER) || defined(_WIN32) if (netwoking_module) { FreeLibrary(netwoking_module); diff --git a/src/slic3r/Utils/NetworkAgent.hpp b/src/slic3r/Utils/NetworkAgent.hpp index d05ddb6ef9..01d16d660b 100644 --- a/src/slic3r/Utils/NetworkAgent.hpp +++ b/src/slic3r/Utils/NetworkAgent.hpp @@ -110,7 +110,6 @@ typedef int (*func_get_model_mall_rating_result)(void *agent, int job_id, std::s typedef int (*func_get_mw_user_preference)(void *agent, std::function callback); typedef int (*func_get_mw_user_4ulist)(void *agent, int seed, int limit, std::function callback); - //the NetworkAgent class class NetworkAgent {