#include "BBLNetworkPlugin.hpp" #include "NetworkAgent.hpp" #include #include #include #include #include #include "libslic3r/Utils.hpp" #include "slic3r/Utils/FileTransferUtils.hpp" #if !defined(_MSC_VER) && !defined(_WIN32) #include #endif namespace Slic3r { #define BAMBU_SOURCE_LIBRARY "BambuSource" namespace { // Named in the load log: the bound generation is what ties a crash report to an ABI choice. // The label is the whitelist row's series, so it can never drift from the dispatch table. const char* network_abi_name(NetworkAbi abi) { for (size_t i = 0; i < AVAILABLE_NETWORK_VERSIONS_COUNT; ++i) if (AVAILABLE_NETWORK_VERSIONS[i].abi == abi) return AVAILABLE_NETWORK_VERSIONS[i].version; return "unsupported"; } } // namespace // ============================================================================ // Singleton Implementation // ============================================================================ // Static pointer initialization (null by default, created on first access) BBLNetworkPlugin* BBLNetworkPlugin::s_instance = nullptr; BBLNetworkPlugin& BBLNetworkPlugin::instance() { static std::once_flag flag; std::call_once(flag, [] { s_instance = new BBLNetworkPlugin(); }); return *s_instance; } void BBLNetworkPlugin::shutdown() { // Note: Do not call instance() after shutdown() - the singleton is destroyed. if (s_instance) { delete s_instance; s_instance = nullptr; } } BBLNetworkPlugin::BBLNetworkPlugin() = default; BBLNetworkPlugin::~BBLNetworkPlugin() { unload(); // unload() destroys the agent first (see the note there) } // ============================================================================ // Module Lifecycle // ============================================================================ int BBLNetworkPlugin::initialize(bool using_backup, const std::string& version) { clear_load_error(); std::string library; std::string data_dir_str = data_dir(); boost::filesystem::path data_dir_path(data_dir_str); auto plugin_folder = data_dir_path / "plugins"; if (using_backup) { plugin_folder = plugin_folder / "backup"; } if (version.empty()) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": version is required but not provided"; set_load_error( "Network library version not specified", "A version must be specified to load the network library", "" ); return -1; } // Auto-migration: If loading legacy version and versioned library doesn't exist, // but unversioned legacy library does exist, copy it to versioned format if (is_legacy_version(version)) { boost::filesystem::path versioned_path; boost::filesystem::path legacy_path; #if defined(_MSC_VER) || defined(_WIN32) versioned_path = plugin_folder / (std::string(BAMBU_NETWORK_LIBRARY) + "_" + version + ".dll"); legacy_path = plugin_folder / (std::string(BAMBU_NETWORK_LIBRARY) + ".dll"); #elif defined(__WXMAC__) versioned_path = plugin_folder / (std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + "_" + version + ".dylib"); legacy_path = plugin_folder / (std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + ".dylib"); #else versioned_path = plugin_folder / (std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + "_" + version + ".so"); legacy_path = plugin_folder / (std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + ".so"); #endif if (!boost::filesystem::exists(versioned_path) && boost::filesystem::exists(legacy_path)) { try { boost::filesystem::copy(legacy_path, versioned_path); } catch (const std::exception& e) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": failed to copy legacy library: " << e.what(); } } } // Load versioned library. In the normal plugins folder a bare series (02.08.01) resolves to // whatever same-series build is actually on disk (see resolve_library_path); the backup // folder keeps the exact versioned name. #if defined(_MSC_VER) || defined(_WIN32) std::string versioned_name = std::string(BAMBU_NETWORK_LIBRARY) + "_" + version + ".dll"; #elif defined(__WXMAC__) std::string versioned_name = std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + "_" + version + ".dylib"; #else std::string versioned_name = std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + "_" + version + ".so"; #endif library = using_backup ? (plugin_folder / versioned_name).string() : resolve_library_path(version); #if defined(_MSC_VER) || defined(_WIN32) wchar_t lib_wstr[256]; memset(lib_wstr, 0, sizeof(lib_wstr)); ::MultiByteToWideChar(CP_UTF8, NULL, library.c_str(), strlen(library.c_str())+1, lib_wstr, sizeof(lib_wstr) / sizeof(lib_wstr[0])); m_networking_module = LoadLibrary(lib_wstr); if (!m_networking_module) { std::string library_path = get_libpath_in_current_directory(std::string(BAMBU_NETWORK_LIBRARY)); if (library_path.empty()) { set_load_error( "Network library not found", "Could not locate versioned library: " + library, library ); return -1; } memset(lib_wstr, 0, sizeof(lib_wstr)); ::MultiByteToWideChar(CP_UTF8, NULL, library_path.c_str(), strlen(library_path.c_str())+1, lib_wstr, sizeof(lib_wstr) / sizeof(lib_wstr[0])); m_networking_module = LoadLibrary(lib_wstr); } #else m_networking_module = dlopen(library.c_str(), RTLD_LAZY); if (!m_networking_module) { char* dll_error = dlerror(); BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": dlopen failed: " << (dll_error ? dll_error : "unknown error"); set_load_error( "Failed to load network library", dll_error ? std::string(dll_error) : "Unknown dlopen error", library ); } #endif if (!m_networking_module) { if (!m_load_error.has_error) { set_load_error( "Network library failed to load", "LoadLibrary/dlopen returned null", library ); } return -1; } // Load file transfer interface InitFTModule(m_networking_module); // Load all function pointers load_all_function_pointers(); // Key the generation on the library that actually loaded, not the version asked for: // resolve_library_path() serves any same-series build. m_get_version is read directly, not // via get_version(), which would substitute the "00.00.00.00" sentinel and pick no generation. const std::string loaded_version = m_get_version ? m_get_version() : std::string(); m_network_abi = network_plugin_abi(loaded_version.empty() ? version : loaded_version); // A library reporting a series this build has no ABI for stays loaded but uncallable - // check_networking_version() then reports it as incompatible and offers the update flow. if (m_network_abi == NetworkAbi::Unsupported) { BOOST_LOG_TRIVIAL(warning) << "BBLNetworkPlugin::initialize: no ABI for version " << (loaded_version.empty() ? version : loaded_version) << ", plug-in calls are disabled"; } BOOST_LOG_TRIVIAL(info) << "BBLNetworkPlugin::initialize: abi=" << network_abi_name(m_network_abi) << ", library=" << library << ", version=" << (loaded_version.empty() ? "unknown" : loaded_version) << ", send_message=" << (m_send_message ? "loaded" : "null") << ", start_print=" << (m_start_print ? "loaded" : "null") << ", start_local_print=" << (m_start_local_print ? "loaded" : "null") << ", get_my_token=" << (m_get_my_token ? "loaded" : "null"); return 0; } int BBLNetworkPlugin::unload() { // Orca: destroy the plugin agent while its creating DLL is still loaded, so the void* handle // never dangles into freed memory. A stale m_agent surviving the unload makes create_agent() // short-circuit on has_agent() after a hot reload, and the next call into the freshly loaded // DLL dereferences the old-DLL handle -> access violation. destroy_agent(); UnloadFTModule(); #if defined(_MSC_VER) || defined(_WIN32) if (m_networking_module) { FreeLibrary(m_networking_module); m_networking_module = NULL; } if (m_source_module) { FreeLibrary(m_source_module); m_source_module = NULL; } #else if (m_networking_module) { dlclose(m_networking_module); m_networking_module = NULL; } if (m_source_module) { dlclose(m_source_module); m_source_module = NULL; } #endif clear_all_function_pointers(); // Safe to reset only because every pointer was nulled just above and every dispatcher is // guarded on a non-null pointer, so no stale generation is reachable. m_network_abi = NetworkAbi::Unsupported; return 0; } bool BBLNetworkPlugin::is_loaded() const { return m_networking_module != nullptr; } std::string BBLNetworkPlugin::get_version() const { bool consistent = true; // Check the debug consistent first if (m_check_debug_consistent) { #if defined(NDEBUG) consistent = m_check_debug_consistent(false); #else consistent = m_check_debug_consistent(true); #endif } if (!consistent) { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", inconsistent library, return 00.00.00.00!"); return "00.00.00.00"; } if (m_get_version) { return m_get_version(); } BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", get_version not supported, return 00.00.00.00!"); return "00.00.00.00"; } // ============================================================================ // Agent Lifecycle // ============================================================================ void* BBLNetworkPlugin::create_agent(const std::string& log_dir) { if (m_agent) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": reusing existing agent " << m_agent; return m_agent; } if (m_create_agent) { m_agent = m_create_agent(log_dir); } BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": created agent " << m_agent; return m_agent; } int BBLNetworkPlugin::destroy_agent() { int ret = 0; if (m_agent && m_destroy_agent) { ret = m_destroy_agent(m_agent); } m_agent = nullptr; return ret; } // ============================================================================ // DLL Module Accessors // ============================================================================ #if defined(_MSC_VER) || defined(_WIN32) HMODULE BBLNetworkPlugin::get_source_module() #else void* BBLNetworkPlugin::get_source_module() #endif { if ((m_source_module) || (!m_networking_module)) return m_source_module; std::string library; std::string data_dir_str = data_dir(); boost::filesystem::path data_dir_path(data_dir_str); auto plugin_folder = data_dir_path / "plugins"; #if defined(_MSC_VER) || defined(_WIN32) wchar_t lib_wstr[128]; library = plugin_folder.string() + "/" + std::string(BAMBU_SOURCE_LIBRARY) + ".dll"; memset(lib_wstr, 0, sizeof(lib_wstr)); ::MultiByteToWideChar(CP_UTF8, NULL, library.c_str(), strlen(library.c_str())+1, lib_wstr, sizeof(lib_wstr) / sizeof(lib_wstr[0])); m_source_module = LoadLibrary(lib_wstr); if (!m_source_module) { std::string library_path = get_libpath_in_current_directory(std::string(BAMBU_SOURCE_LIBRARY)); if (library_path.empty()) { return m_source_module; } memset(lib_wstr, 0, sizeof(lib_wstr)); ::MultiByteToWideChar(CP_UTF8, NULL, library_path.c_str(), strlen(library_path.c_str()) + 1, lib_wstr, sizeof(lib_wstr) / sizeof(lib_wstr[0])); m_source_module = LoadLibrary(lib_wstr); } #else #if defined(__WXMAC__) library = plugin_folder.string() + "/" + std::string("lib") + std::string(BAMBU_SOURCE_LIBRARY) + ".dylib"; #else library = plugin_folder.string() + "/" + std::string("lib") + std::string(BAMBU_SOURCE_LIBRARY) + ".so"; #endif m_source_module = dlopen(library.c_str(), RTLD_LAZY); #endif return m_source_module; } void* BBLNetworkPlugin::get_function(const char* name) { void* function = nullptr; if (!m_networking_module) return function; #if defined(_MSC_VER) || defined(_WIN32) function = reinterpret_cast(GetProcAddress(m_networking_module, name)); #else function = dlsym(m_networking_module, name); #endif return function; } // ============================================================================ // Utility Methods // ============================================================================ std::string BBLNetworkPlugin::get_libpath_in_current_directory(const std::string& library_name) { std::string lib_path; #if defined(_MSC_VER) || defined(_WIN32) wchar_t file_name[512]; DWORD ret = GetModuleFileNameW(NULL, file_name, 512); if (!ret) { return lib_path; } int size_needed = ::WideCharToMultiByte(0, 0, file_name, wcslen(file_name), nullptr, 0, nullptr, nullptr); std::string file_name_string(size_needed, 0); ::WideCharToMultiByte(0, 0, file_name, wcslen(file_name), file_name_string.data(), size_needed, nullptr, nullptr); std::size_t found = file_name_string.find("orca-slicer.exe"); if (found == (file_name_string.size() - 16)) { lib_path = library_name + ".dll"; lib_path = file_name_string.replace(found, 16, lib_path); } #else (void)library_name; #endif return lib_path; } std::string BBLNetworkPlugin::get_versioned_library_path(const std::string& version) { std::string data_dir_str = data_dir(); boost::filesystem::path data_dir_path(data_dir_str); auto plugin_folder = data_dir_path / "plugins"; #if defined(_MSC_VER) || defined(_WIN32) return (plugin_folder / (std::string(BAMBU_NETWORK_LIBRARY) + "_" + version + ".dll")).string(); #elif defined(__WXMAC__) return (plugin_folder / (std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + "_" + version + ".dylib")).string(); #else return (plugin_folder / (std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + "_" + version + ".so")).string(); #endif } std::string BBLNetworkPlugin::resolve_library_path(const std::string& version) { std::string exact = get_versioned_library_path(version); if (boost::filesystem::exists(exact)) return exact; // A bare series (02.08.01) is physically present only as a specific build (02.08.01.53) when // the startup file-rename was skipped or failed. Resolve to the newest same-series build // actually on disk. Custom and legacy names are exact and never resolved. if (is_series_managed_version(version)) { const std::string series = network_plugin_series(version); std::string best; for (const auto& v : scan_plugin_versions()) if (is_series_managed_version(v) && network_plugin_series(v) == series && (best.empty() || v > best)) best = v; if (!best.empty()) return get_versioned_library_path(best); } return exact; // nonexistent -> caller downloads } bool BBLNetworkPlugin::versioned_library_exists(const std::string& version) { if (version.empty()) return false; if (boost::filesystem::exists(resolve_library_path(version))) return true; if (is_legacy_version(version)) { return legacy_library_exists(); } return false; } bool BBLNetworkPlugin::legacy_library_exists() { std::string data_dir_str = data_dir(); boost::filesystem::path data_dir_path(data_dir_str); auto plugin_folder = data_dir_path / "plugins"; #if defined(_MSC_VER) || defined(_WIN32) auto legacy_path = plugin_folder / (std::string(BAMBU_NETWORK_LIBRARY) + ".dll"); #elif defined(__WXMAC__) auto legacy_path = plugin_folder / (std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + ".dylib"); #else auto legacy_path = plugin_folder / (std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + ".so"); #endif return boost::filesystem::exists(legacy_path); } void BBLNetworkPlugin::remove_legacy_library() { std::string data_dir_str = data_dir(); boost::filesystem::path data_dir_path(data_dir_str); auto plugin_folder = data_dir_path / "plugins"; #if defined(_MSC_VER) || defined(_WIN32) auto legacy_path = plugin_folder / (std::string(BAMBU_NETWORK_LIBRARY) + ".dll"); #elif defined(__WXMAC__) auto legacy_path = plugin_folder / (std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + ".dylib"); #else auto legacy_path = plugin_folder / (std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + ".so"); #endif if (boost::filesystem::exists(legacy_path)) { boost::system::error_code ec; boost::filesystem::remove(legacy_path, ec); } } std::vector BBLNetworkPlugin::scan_plugin_versions() { std::vector discovered_versions; std::string data_dir_str = data_dir(); boost::filesystem::path plugin_folder = boost::filesystem::path(data_dir_str) / "plugins"; if (!boost::filesystem::is_directory(plugin_folder)) { return discovered_versions; } #if defined(_MSC_VER) || defined(_WIN32) std::string prefix = std::string(BAMBU_NETWORK_LIBRARY) + "_"; std::string extension = ".dll"; #elif defined(__WXMAC__) std::string prefix = std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + "_"; std::string extension = ".dylib"; #else std::string prefix = std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + "_"; std::string extension = ".so"; #endif boost::system::error_code ec; for (auto& entry : boost::filesystem::directory_iterator(plugin_folder, ec)) { if (ec) { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": error iterating directory: " << ec.message(); break; } if (!boost::filesystem::is_regular_file(entry.status())) continue; std::string filename = entry.path().filename().string(); if (filename.rfind(prefix, 0) != 0) continue; if (filename.size() <= extension.size() || filename.compare(filename.size() - extension.size(), extension.size(), extension) != 0) continue; std::string version = filename.substr(prefix.size(), filename.size() - prefix.size() - extension.size()); discovered_versions.push_back(version); } return discovered_versions; } // ============================================================================ // Error Handling // ============================================================================ void BBLNetworkPlugin::clear_load_error() { m_load_error = NetworkLibraryLoadError{}; } void BBLNetworkPlugin::set_load_error(const std::string& message, const std::string& technical_details, const std::string& attempted_path) { m_load_error.has_error = true; m_load_error.message = message; m_load_error.technical_details = technical_details; m_load_error.attempted_path = attempted_path; } // ============================================================================ // ABI Conversion Helpers // ============================================================================ PrintParams_Legacy BBLNetworkPlugin::as_legacy(PrintParams& param) { PrintParams_Legacy l; l.dev_id = std::move(param.dev_id); l.task_name = std::move(param.task_name); l.project_name = std::move(param.project_name); l.preset_name = std::move(param.preset_name); l.filename = std::move(param.filename); l.config_filename = std::move(param.config_filename); l.plate_index = param.plate_index; l.ftp_folder = std::move(param.ftp_folder); l.ftp_file = std::move(param.ftp_file); l.ftp_file_md5 = std::move(param.ftp_file_md5); l.ams_mapping = std::move(param.ams_mapping); l.ams_mapping_info = std::move(param.ams_mapping_info); l.connection_type = std::move(param.connection_type); l.comments = std::move(param.comments); l.origin_profile_id = param.origin_profile_id; l.stl_design_id = param.stl_design_id; l.origin_model_id = std::move(param.origin_model_id); l.print_type = std::move(param.print_type); l.dst_file = std::move(param.dst_file); l.dev_name = std::move(param.dev_name); l.dev_ip = std::move(param.dev_ip); l.use_ssl_for_ftp = param.use_ssl_for_ftp; l.use_ssl_for_mqtt = param.use_ssl_for_mqtt; l.username = std::move(param.username); l.password = std::move(param.password); l.task_bed_leveling = param.task_bed_leveling; l.task_flow_cali = param.task_flow_cali; l.task_vibration_cali = param.task_vibration_cali; l.task_layer_inspect = param.task_layer_inspect; l.task_record_timelapse = param.task_record_timelapse; l.task_use_ams = param.task_use_ams; l.task_bed_type = std::move(param.task_bed_type); l.extra_options = std::move(param.extra_options); return l; } // Every PrintParams field except the four the 02.08.01 series added // (task_timelapse_use_internal, extruder_cali_manual_mode, svc_context, slicer_uid). PrintParams_0203 BBLNetworkPlugin::as_0203(PrintParams& param) { PrintParams_0203 p; p.dev_id = std::move(param.dev_id); p.task_name = std::move(param.task_name); p.project_name = std::move(param.project_name); p.preset_name = std::move(param.preset_name); p.filename = std::move(param.filename); p.config_filename = std::move(param.config_filename); p.plate_index = param.plate_index; p.ftp_folder = std::move(param.ftp_folder); p.ftp_file = std::move(param.ftp_file); p.ftp_file_md5 = std::move(param.ftp_file_md5); p.nozzle_mapping = std::move(param.nozzle_mapping); p.ams_mapping = std::move(param.ams_mapping); p.ams_mapping2 = std::move(param.ams_mapping2); p.ams_mapping_info = std::move(param.ams_mapping_info); p.nozzles_info = std::move(param.nozzles_info); p.connection_type = std::move(param.connection_type); p.comments = std::move(param.comments); p.origin_profile_id = param.origin_profile_id; p.stl_design_id = param.stl_design_id; p.origin_model_id = std::move(param.origin_model_id); p.print_type = std::move(param.print_type); p.dst_file = std::move(param.dst_file); p.dev_name = std::move(param.dev_name); p.dev_ip = std::move(param.dev_ip); p.use_ssl_for_ftp = param.use_ssl_for_ftp; p.use_ssl_for_mqtt = param.use_ssl_for_mqtt; p.username = std::move(param.username); p.password = std::move(param.password); p.task_bed_leveling = param.task_bed_leveling; p.task_flow_cali = param.task_flow_cali; p.task_vibration_cali = param.task_vibration_cali; p.task_layer_inspect = param.task_layer_inspect; p.task_record_timelapse = param.task_record_timelapse; p.task_use_ams = param.task_use_ams; p.task_bed_type = std::move(param.task_bed_type); p.extra_options = std::move(param.extra_options); p.auto_bed_leveling = param.auto_bed_leveling; p.auto_flow_cali = param.auto_flow_cali; p.auto_offset_cali = param.auto_offset_cali; p.task_ext_change_assist = param.task_ext_change_assist; p.try_emmc_print = param.try_emmc_print; return p; } // ============================================================================ // Function Pointer Loading // ============================================================================ void BBLNetworkPlugin::load_all_function_pointers() { m_check_debug_consistent = reinterpret_cast(get_function("bambu_network_check_debug_consistent")); m_get_version = reinterpret_cast(get_function("bambu_network_get_version")); m_create_agent = reinterpret_cast(get_function("bambu_network_create_agent")); m_destroy_agent = reinterpret_cast(get_function("bambu_network_destroy_agent")); m_init_log = reinterpret_cast(get_function("bambu_network_init_log")); m_set_config_dir = reinterpret_cast(get_function("bambu_network_set_config_dir")); m_set_cert_file = reinterpret_cast(get_function("bambu_network_set_cert_file")); m_set_country_code = reinterpret_cast(get_function("bambu_network_set_country_code")); m_start = reinterpret_cast(get_function("bambu_network_start")); m_set_on_ssdp_msg_fn = reinterpret_cast(get_function("bambu_network_set_on_ssdp_msg_fn")); m_set_on_printer_connected_fn = reinterpret_cast(get_function("bambu_network_set_on_printer_connected_fn")); m_set_on_server_connected_fn = reinterpret_cast(get_function("bambu_network_set_on_server_connected_fn")); m_set_on_http_error_fn = reinterpret_cast(get_function("bambu_network_set_on_http_error_fn")); m_set_get_country_code_fn = reinterpret_cast(get_function("bambu_network_set_get_country_code_fn")); m_set_on_subscribe_failure_fn = reinterpret_cast(get_function("bambu_network_set_on_subscribe_failure_fn")); m_set_on_message_fn = reinterpret_cast(get_function("bambu_network_set_on_message_fn")); m_set_on_user_message_fn = reinterpret_cast(get_function("bambu_network_set_on_user_message_fn")); m_set_on_local_connect_fn = reinterpret_cast(get_function("bambu_network_set_on_local_connect_fn")); m_set_on_local_message_fn = reinterpret_cast(get_function("bambu_network_set_on_local_message_fn")); m_set_queue_on_main_fn = reinterpret_cast(get_function("bambu_network_set_queue_on_main_fn")); m_connect_server = reinterpret_cast(get_function("bambu_network_connect_server")); m_is_server_connected = reinterpret_cast(get_function("bambu_network_is_server_connected")); m_refresh_connection = reinterpret_cast(get_function("bambu_network_refresh_connection")); m_start_subscribe = reinterpret_cast(get_function("bambu_network_start_subscribe")); m_stop_subscribe = reinterpret_cast(get_function("bambu_network_stop_subscribe")); m_add_subscribe = reinterpret_cast(get_function("bambu_network_add_subscribe")); m_del_subscribe = reinterpret_cast(get_function("bambu_network_del_subscribe")); m_enable_multi_machine = reinterpret_cast(get_function("bambu_network_enable_multi_machine")); m_send_message = reinterpret_cast(get_function("bambu_network_send_message")); m_connect_printer = reinterpret_cast(get_function("bambu_network_connect_printer")); m_disconnect_printer = reinterpret_cast(get_function("bambu_network_disconnect_printer")); m_send_message_to_printer = reinterpret_cast(get_function("bambu_network_send_message_to_printer")); m_check_cert = reinterpret_cast(get_function("bambu_network_update_cert")); m_install_device_cert = reinterpret_cast(get_function("bambu_network_install_device_cert")); m_start_discovery = reinterpret_cast(get_function("bambu_network_start_discovery")); m_change_user = reinterpret_cast(get_function("bambu_network_change_user")); m_is_user_login = reinterpret_cast(get_function("bambu_network_is_user_login")); m_user_logout = reinterpret_cast(get_function("bambu_network_user_logout")); m_get_user_id = reinterpret_cast(get_function("bambu_network_get_user_id")); m_get_user_name = reinterpret_cast(get_function("bambu_network_get_user_name")); m_get_user_avatar = reinterpret_cast(get_function("bambu_network_get_user_avatar")); m_get_user_nickanme = reinterpret_cast(get_function("bambu_network_get_user_nickanme")); m_build_login_cmd = reinterpret_cast(get_function("bambu_network_build_login_cmd")); m_build_logout_cmd = reinterpret_cast(get_function("bambu_network_build_logout_cmd")); m_build_login_info = reinterpret_cast(get_function("bambu_network_build_login_info")); m_ping_bind = reinterpret_cast(get_function("bambu_network_ping_bind")); m_bind_detect = reinterpret_cast(get_function("bambu_network_bind_detect")); m_set_server_callback = reinterpret_cast(get_function("bambu_network_set_server_callback")); m_bind = reinterpret_cast(get_function("bambu_network_bind")); m_unbind = reinterpret_cast(get_function("bambu_network_unbind")); m_get_bambulab_host = reinterpret_cast(get_function("bambu_network_get_bambulab_host")); m_get_user_selected_machine = reinterpret_cast(get_function("bambu_network_get_user_selected_machine")); m_set_user_selected_machine = reinterpret_cast(get_function("bambu_network_set_user_selected_machine")); m_start_print = reinterpret_cast(get_function("bambu_network_start_print")); m_start_local_print_with_record = reinterpret_cast(get_function("bambu_network_start_local_print_with_record")); m_start_send_gcode_to_sdcard = reinterpret_cast(get_function("bambu_network_start_send_gcode_to_sdcard")); m_start_local_print = reinterpret_cast(get_function("bambu_network_start_local_print")); m_start_sdcard_print = reinterpret_cast(get_function("bambu_network_start_sdcard_print")); m_get_user_presets = reinterpret_cast(get_function("bambu_network_get_user_presets")); m_request_setting_id = reinterpret_cast(get_function("bambu_network_request_setting_id")); m_put_setting = reinterpret_cast(get_function("bambu_network_put_setting")); m_get_setting_list = reinterpret_cast(get_function("bambu_network_get_setting_list")); m_get_setting_list2 = reinterpret_cast(get_function("bambu_network_get_setting_list2")); m_delete_setting = reinterpret_cast(get_function("bambu_network_delete_setting")); m_set_extra_http_header = reinterpret_cast(get_function("bambu_network_set_extra_http_header")); m_get_my_message = reinterpret_cast(get_function("bambu_network_get_my_message")); m_check_user_task_report = reinterpret_cast(get_function("bambu_network_check_user_task_report")); m_get_user_print_info = reinterpret_cast(get_function("bambu_network_get_user_print_info")); m_get_user_tasks = reinterpret_cast(get_function("bambu_network_get_user_tasks")); m_get_printer_firmware = reinterpret_cast(get_function("bambu_network_get_printer_firmware")); m_get_task_plate_index = reinterpret_cast(get_function("bambu_network_get_task_plate_index")); m_get_user_info = reinterpret_cast(get_function("bambu_network_get_user_info")); m_request_bind_ticket = reinterpret_cast(get_function("bambu_network_request_bind_ticket")); m_get_subtask_info = reinterpret_cast(get_function("bambu_network_get_subtask_info")); m_get_slice_info = reinterpret_cast(get_function("bambu_network_get_slice_info")); m_query_bind_status = reinterpret_cast(get_function("bambu_network_query_bind_status")); m_modify_printer_name = reinterpret_cast(get_function("bambu_network_modify_printer_name")); m_get_camera_url = reinterpret_cast(get_function("bambu_network_get_camera_url")); m_get_design_staffpick = reinterpret_cast(get_function("bambu_network_get_design_staffpick")); m_start_publish = reinterpret_cast(get_function("bambu_network_start_publish")); m_get_model_publish_url = reinterpret_cast(get_function("bambu_network_get_model_publish_url")); m_get_subtask = reinterpret_cast(get_function("bambu_network_get_subtask")); m_get_model_mall_home_url = reinterpret_cast(get_function("bambu_network_get_model_mall_home_url")); m_get_model_mall_detail_url = reinterpret_cast(get_function("bambu_network_get_model_mall_detail_url")); m_get_my_profile = reinterpret_cast(get_function("bambu_network_get_my_profile")); m_get_my_token = reinterpret_cast(get_function("bambu_network_get_my_token")); m_track_enable = reinterpret_cast(get_function("bambu_network_track_enable")); m_track_remove_files = reinterpret_cast(get_function("bambu_network_track_remove_files")); m_track_event = reinterpret_cast(get_function("bambu_network_track_event")); m_track_header = reinterpret_cast(get_function("bambu_network_track_header")); m_track_update_property = reinterpret_cast(get_function("bambu_network_track_update_property")); m_track_get_property = reinterpret_cast(get_function("bambu_network_track_get_property")); m_put_model_mall_rating = reinterpret_cast(get_function("bambu_network_put_model_mall_rating")); m_get_oss_config = reinterpret_cast(get_function("bambu_network_get_oss_config")); m_put_rating_picture_oss = reinterpret_cast(get_function("bambu_network_put_rating_picture_oss")); m_get_model_mall_rating_result = reinterpret_cast(get_function("bambu_network_get_model_mall_rating")); m_get_mw_user_preference = reinterpret_cast(get_function("bambu_network_get_mw_user_preference")); m_get_mw_user_4ulist = reinterpret_cast(get_function("bambu_network_get_mw_user_4ulist")); // Bound late; anything a generation does not export resolves to null so callers no-op. // See the typedefs for which generation introduced each of these. m_set_on_user_login_fn = reinterpret_cast(get_function("bambu_network_set_on_user_login_fn")); m_get_studio_info_url = reinterpret_cast(get_function("bambu_network_get_studio_info_url")); m_report_consent = reinterpret_cast(get_function("bambu_network_report_consent")); m_get_camera_url_for_golive = reinterpret_cast(get_function("bambu_network_get_camera_url_for_golive")); m_get_hms_snapshot = reinterpret_cast(get_function("bambu_network_get_hms_snapshot")); m_get_filament_spools = reinterpret_cast(get_function("bambu_network_get_filament_spools")); m_create_filament_spool = reinterpret_cast(get_function("bambu_network_create_filament_spool")); m_update_filament_spool = reinterpret_cast(get_function("bambu_network_update_filament_spool")); m_delete_filament_spools = reinterpret_cast(get_function("bambu_network_delete_filament_spools")); m_get_filament_config = reinterpret_cast(get_function("bambu_network_get_filament_config")); m_sync_ams_filaments = reinterpret_cast(get_function("bambu_network_sync_ams_filaments")); } void BBLNetworkPlugin::clear_all_function_pointers() { m_check_debug_consistent = nullptr; m_get_version = nullptr; m_create_agent = nullptr; m_destroy_agent = nullptr; m_init_log = nullptr; m_set_config_dir = nullptr; m_set_cert_file = nullptr; m_set_country_code = nullptr; m_start = nullptr; m_set_on_ssdp_msg_fn = nullptr; m_set_on_printer_connected_fn = nullptr; m_set_on_server_connected_fn = nullptr; m_set_on_http_error_fn = nullptr; m_set_get_country_code_fn = nullptr; m_set_on_subscribe_failure_fn = nullptr; m_set_on_message_fn = nullptr; m_set_on_user_message_fn = nullptr; m_set_on_local_connect_fn = nullptr; m_set_on_local_message_fn = nullptr; m_set_queue_on_main_fn = nullptr; m_connect_server = nullptr; m_is_server_connected = nullptr; m_refresh_connection = nullptr; m_start_subscribe = nullptr; m_stop_subscribe = nullptr; m_add_subscribe = nullptr; m_del_subscribe = nullptr; m_enable_multi_machine = nullptr; m_send_message = nullptr; m_connect_printer = nullptr; m_disconnect_printer = nullptr; m_send_message_to_printer = nullptr; m_check_cert = nullptr; m_install_device_cert = nullptr; m_start_discovery = nullptr; m_change_user = nullptr; m_is_user_login = nullptr; m_user_logout = nullptr; m_get_user_id = nullptr; m_get_user_name = nullptr; m_get_user_avatar = nullptr; m_get_user_nickanme = nullptr; m_build_login_cmd = nullptr; m_build_logout_cmd = nullptr; m_build_login_info = nullptr; m_ping_bind = nullptr; m_bind_detect = nullptr; m_set_server_callback = nullptr; m_bind = nullptr; m_unbind = nullptr; m_get_bambulab_host = nullptr; m_get_user_selected_machine = nullptr; m_set_user_selected_machine = nullptr; m_start_print = nullptr; m_start_local_print_with_record = nullptr; m_start_send_gcode_to_sdcard = nullptr; m_start_local_print = nullptr; m_start_sdcard_print = nullptr; m_get_user_presets = nullptr; m_request_setting_id = nullptr; m_put_setting = nullptr; m_get_setting_list = nullptr; m_get_setting_list2 = nullptr; m_delete_setting = nullptr; m_set_extra_http_header = nullptr; m_get_my_message = nullptr; m_check_user_task_report = nullptr; m_get_user_print_info = nullptr; m_get_user_tasks = nullptr; m_get_printer_firmware = nullptr; m_get_task_plate_index = nullptr; m_get_user_info = nullptr; m_request_bind_ticket = nullptr; m_get_subtask_info = nullptr; m_get_slice_info = nullptr; m_query_bind_status = nullptr; m_modify_printer_name = nullptr; m_get_camera_url = nullptr; m_get_design_staffpick = nullptr; m_start_publish = nullptr; m_get_model_publish_url = nullptr; m_get_subtask = nullptr; m_get_model_mall_home_url = nullptr; m_get_model_mall_detail_url = nullptr; m_get_my_profile = nullptr; m_get_my_token = nullptr; m_track_enable = nullptr; m_track_remove_files = nullptr; m_track_event = nullptr; m_track_header = nullptr; m_track_update_property = nullptr; m_track_get_property = nullptr; m_put_model_mall_rating = nullptr; m_get_oss_config = nullptr; m_put_rating_picture_oss = nullptr; m_get_model_mall_rating_result = nullptr; m_get_mw_user_preference = nullptr; m_get_mw_user_4ulist = nullptr; m_set_on_user_login_fn = nullptr; m_get_studio_info_url = nullptr; m_report_consent = nullptr; m_get_camera_url_for_golive = nullptr; m_get_hms_snapshot = nullptr; m_get_filament_spools = nullptr; m_create_filament_spool = nullptr; m_update_filament_spool = nullptr; m_delete_filament_spools = nullptr; m_get_filament_config = nullptr; m_sync_ams_filaments = nullptr; } std::vector get_all_available_versions() { // get_version() reports the "00.00.00.00" sentinel when nothing is loaded; resolve // that here so the list builder only ever sees a real version or an empty string. const BBLNetworkPlugin& plugin = BBLNetworkPlugin::instance(); return get_all_available_versions(plugin.is_loaded() ? plugin.get_version() : std::string()); } std::vector get_all_available_versions(const std::string& loaded_version) { std::vector result; std::set all_known_versions; for (size_t i = 0; i < AVAILABLE_NETWORK_VERSIONS_COUNT; ++i) { result.push_back(NetworkLibraryVersionInfo::from_static(AVAILABLE_NETWORK_VERSIONS[i])); all_known_versions.insert(AVAILABLE_NETWORK_VERSIONS[i].version); } std::vector discovered = BBLNetworkPlugin::scan_plugin_versions(); // A managed build (pure dotted-numeric AA.BB.CC[.DD]) is represented by its series entry // above - the OTA-installed 02.08.01.53 and a bare 02.08.01 both collapse into the single // 02.08.01 row. Only a custom-named build a user dropped in (02.08.01_custom, ..-dev) earns // its own row, and only when its series is one this build can actually load. The part past // the series is stored as the "suffix" so the entry sorts and renders nested under it. for (const auto& version : discovered) { if (all_known_versions.count(version) > 0) continue; if (is_series_managed_version(version)) continue; if (!is_supported_network_version(version)) continue; const std::string series = network_plugin_series(version); const std::string sfx = version.size() > series.size() ? version.substr(series.size()) : version; result.push_back(NetworkLibraryVersionInfo::from_discovered(version, series, sfx)); all_known_versions.insert(version); } // Newest first. Version components are fixed-width and zero-padded, so a plain // string compare orders them numerically, and the legacy series sorts last on its // own. Suffixed dev builds sort directly under the base version they build on. std::sort(result.begin(), result.end(), [](const NetworkLibraryVersionInfo& a, const NetworkLibraryVersionInfo& b) { if (a.base_version != b.base_version) return a.base_version > b.base_version; return a.suffix < b.suffix; }); const std::string loaded_series = network_plugin_series(loaded_version); for (auto& info : result) { // A managed (series) entry matches when a managed build of the same series is loaded - // the loaded plug-in reports its full build (02.08.01.53) but the row is the series. // Custom and legacy entries match their exact reported version. info.is_loaded = !loaded_version.empty() && (info.version == loaded_version || (is_series_managed_version(info.version) && is_series_managed_version(loaded_version) && network_plugin_series(info.version) == loaded_series)); info.is_latest = false; } // "(Latest)" goes on the highest full version in the list, which after the sort is // simply the first entry without a dev suffix - an OTA-installed build can be newer // than the newest whitelisted entry. get_latest_network_version() intentionally // keeps returning the static whitelist default, which drives the download and // update-check decisions. auto latest = std::find_if(result.begin(), result.end(), [](const NetworkLibraryVersionInfo& info) { return info.suffix.empty(); }); if (latest != result.end()) latest->is_latest = true; return result; } } // namespace Slic3r