Merge branch 'feat/printer-agent-infra' into refactor/connect-printer-api

This commit is contained in:
Ian Chua
2026-09-23 14:48:33 +08:00
committed by GitHub
26 changed files with 491 additions and 170 deletions
+2 -2
View File
@@ -997,8 +997,8 @@ elseif (WIN32)
find_library(LIBAVCODEC_LIBRARY NAMES avcodec PATHS ${CMAKE_PREFIX_PATH}/lib NO_DEFAULT_PATH) find_library(LIBAVCODEC_LIBRARY NAMES avcodec PATHS ${CMAKE_PREFIX_PATH}/lib NO_DEFAULT_PATH)
find_library(LIBSWSCALE_LIBRARY NAMES swscale PATHS ${CMAKE_PREFIX_PATH}/lib NO_DEFAULT_PATH) find_library(LIBSWSCALE_LIBRARY NAMES swscale PATHS ${CMAKE_PREFIX_PATH}/lib NO_DEFAULT_PATH)
find_library(LIBAVUTIL_LIBRARY NAMES avutil PATHS ${CMAKE_PREFIX_PATH}/lib NO_DEFAULT_PATH) find_library(LIBAVUTIL_LIBRARY NAMES avutil PATHS ${CMAKE_PREFIX_PATH}/lib NO_DEFAULT_PATH)
if (NOT LIBAVCODEC_LIBRARY OR NOT LIBSWSCALE_LIBRARY OR NOT LIBAVUTIL_LIBRARY) if (NOT LIBAVFORMAT_LIBRARY OR NOT LIBAVCODEC_LIBRARY OR NOT LIBSWSCALE_LIBRARY OR NOT LIBAVUTIL_LIBRARY)
message(FATAL_ERROR "FFmpeg (avcodec/swscale/avutil) not found under ${CMAKE_PREFIX_PATH}/lib. Rebuild the deps.") message(FATAL_ERROR "FFmpeg (avformat/avcodec/swscale/avutil) not found under ${CMAKE_PREFIX_PATH}/lib. Rebuild the deps.")
endif () endif ()
target_link_libraries(libslic3r_gui ${LIBAVFORMAT_LIBRARY} ${LIBAVCODEC_LIBRARY} ${LIBSWSCALE_LIBRARY} ${LIBAVUTIL_LIBRARY}) target_link_libraries(libslic3r_gui ${LIBAVFORMAT_LIBRARY} ${LIBAVCODEC_LIBRARY} ${LIBSWSCALE_LIBRARY} ${LIBAVUTIL_LIBRARY})
target_include_directories(libslic3r_gui SYSTEM PRIVATE ${CMAKE_PREFIX_PATH}/include) target_include_directories(libslic3r_gui SYSTEM PRIVATE ${CMAKE_PREFIX_PATH}/include)
+15 -19
View File
@@ -6,6 +6,7 @@
#include "Widgets/Label.hpp" #include "Widgets/Label.hpp"
#include "MsgDialog.hpp" #include "MsgDialog.hpp"
#include "libslic3r/Print.hpp" #include "libslic3r/Print.hpp"
#include "PrePrintChecker.hpp"
#include "DeviceCore/DevConfig.h" #include "DeviceCore/DevConfig.h"
#include "DeviceCore/DevConfigUtil.h" #include "DeviceCore/DevConfigUtil.h"
@@ -1639,19 +1640,6 @@ void CalibrationPresetPage::update_combobox_filaments(MachineObject* obj)
select_default_compatible_filament(); select_default_compatible_filament();
} }
bool CalibrationPresetPage::is_blocking_printing()
{
DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager();
if (!dev) return true;
MachineObject* obj_ = dev->get_selected_machine();
if (obj_ == nullptr) return true;
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
const auto source_model = preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle);
return !DevPrinterConfigUtil::is_printer_model_compatible(source_model, *obj_);
}
bool CalibrationPresetPage::is_nozzle_info_synced() const bool CalibrationPresetPage::is_nozzle_info_synced() const
{ {
if (!curr_obj || !curr_obj->is_info_ready()) if (!curr_obj || !curr_obj->is_info_ready())
@@ -1733,11 +1721,13 @@ void CalibrationPresetPage::update_show_status()
} }
} }
//if (is_blocking_printing()) { bool has_optional_printer_model = DevPrinterConfigUtil::is_optional_printer_model_id(obj_->printer_type);
// show_status(CaliPresetPageStatus::CaliPresetStatusUnsupportedPrinter); if (PresetBundle *preset_bundle = wxGetApp().preset_bundle) {
// return; has_optional_printer_model = has_optional_printer_model ||
//} DevPrinterConfigUtil::is_optional_printer_model_id(
//else preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle));
}
if (obj_->is_connecting() || !obj_->is_connected()) { if (obj_->is_connecting() || !obj_->is_connected()) {
show_status(CaliPresetPageStatus::CaliPresetStatusInConnecting); show_status(CaliPresetPageStatus::CaliPresetStatusInConnecting);
return; return;
@@ -1789,7 +1779,9 @@ void CalibrationPresetPage::update_show_status()
return; return;
} }
show_status(CaliPresetPageStatus::CaliPresetStatusNormal); show_status(has_optional_printer_model ?
CaliPresetPageStatus::CaliPresetStatusOptionalPrinterModel :
CaliPresetPageStatus::CaliPresetStatusNormal);
} }
@@ -1843,6 +1835,10 @@ void CalibrationPresetPage::show_status(CaliPresetPageStatus status)
Layout(); Layout();
Fit(); Fit();
} }
else if (status == CaliPresetPageStatus::CaliPresetStatusOptionalPrinterModel) {
update_print_status_msg(PrePrintChecker::get_pre_state_msg(PrintDialogStatus::PrintStatusOptionalPrinterModel), true);
Enable_Send_Button(true);
}
else if (status == CaliPresetPageStatus::CaliPresetStatusNoUserLogin) { else if (status == CaliPresetPageStatus::CaliPresetStatusNoUserLogin) {
wxString msg_text = _L("No login account, only printers in LAN mode are displayed."); wxString msg_text = _L("No login account, only printers in LAN mode are displayed.");
update_print_status_msg(msg_text, false); update_print_status_msg(msg_text, false);
@@ -152,7 +152,8 @@ enum CaliPresetPageStatus
CaliPresetStatusInConnecting, CaliPresetStatusInConnecting,
CaliPresetStatusFilamentIncompatible, CaliPresetStatusFilamentIncompatible,
CaliPresetStatusLanModeSDcardNotAvailable, CaliPresetStatusLanModeSDcardNotAvailable,
CaliPresetStatusDifferentNozzleDiameters CaliPresetStatusDifferentNozzleDiameters,
CaliPresetStatusOptionalPrinterModel
}; };
class CalibrationPresetPage : public CalibrationWizardPage class CalibrationPresetPage : public CalibrationWizardPage
@@ -268,7 +269,6 @@ protected:
bool is_nozzle_info_synced() const; bool is_nozzle_info_synced() const;
void show_status(CaliPresetPageStatus status); void show_status(CaliPresetPageStatus status);
void Enable_Send_Button(bool enable); void Enable_Send_Button(bool enable);
bool is_blocking_printing();
bool need_check_sdcard(MachineObject* obj); bool need_check_sdcard(MachineObject* obj);
CaliPresetPageStatus get_status() { return m_page_status; } CaliPresetPageStatus get_status() { return m_page_status; }
+56 -30
View File
@@ -1,8 +1,9 @@
#include "DevManager.h"
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <exception> #include <exception>
#include "DevManager.h" #include <libslic3r/AppConfig.hpp>
#include "CloudProvider.hpp" #include "CloudProvider.hpp"
#include "DevUtil.h" #include "DevUtil.h"
@@ -48,9 +49,11 @@ namespace {
namespace Slic3r namespace Slic3r
{ {
DeviceManager::DeviceManager(NetworkAgent* agent) DeviceManager::DeviceManager(NetworkAgent* agent, bool enable_refresher, AppConfig* app_config)
{ {
m_agent = agent; m_agent = agent;
m_app_config = app_config;
if (enable_refresher)
m_refresher = new DeviceManagerRefresher(this); m_refresher = new DeviceManagerRefresher(this);
DevPrinterConfigUtil::InitFilePath(resources_dir()); DevPrinterConfigUtil::InitFilePath(resources_dir());
@@ -62,9 +65,14 @@ namespace Slic3r
} }
} }
AppConfig* DeviceManager::get_app_config() const
{
return m_app_config ? m_app_config : GUI::wxGetApp().app_config;
}
void DeviceManager::load_local_machines_from_config() void DeviceManager::load_local_machines_from_config()
{ {
AppConfig* config = GUI::wxGetApp().app_config; AppConfig* config = get_app_config();
if (!config) if (!config)
return; return;
const auto local_machines = config->get_local_machines(); const auto local_machines = config->get_local_machines();
@@ -90,9 +98,8 @@ namespace Slic3r
} }
} }
void DeviceManager::update_local_machine(const MachineObject& m) void DeviceManager::update_local_machine(const MachineObject& m, AppConfig* config)
{ {
AppConfig* config = GUI::wxGetApp().app_config;
if (config) { if (config) {
if (m.is_lan_mode_printer()) { if (m.is_lan_mode_printer()) {
if (m.has_access_right()) { if (m.has_access_right()) {
@@ -113,6 +120,7 @@ namespace Slic3r
DeviceManager::~DeviceManager() DeviceManager::~DeviceManager()
{ {
if (m_refresher)
delete m_refresher; delete m_refresher;
for (auto it = localMachineList.begin(); it != localMachineList.end(); it++) for (auto it = localMachineList.begin(); it != localMachineList.end(); it++)
@@ -173,14 +181,22 @@ namespace Slic3r
return printer_agent ? printer_agent->get_agent_info().id : ""; return printer_agent ? printer_agent->get_agent_info().id : "";
} }
std::string DeviceManager::get_current_cloud_provider() const
{
const std::string agent_id = get_current_printer_agent_id();
if (!agent_id.empty())
return agent_id == BBL_PRINTER_AGENT_ID ? BBL_CLOUD_PROVIDER : ORCA_CLOUD_PROVIDER;
return GUI::wxGetApp().get_printer_cloud_provider();
}
void DeviceManager::EnableMultiMachine(bool enable) void DeviceManager::EnableMultiMachine(bool enable)
{ {
m_agent->enable_multi_machine(enable); m_agent->enable_multi_machine(enable);
m_enable_mutil_machine = enable; m_enable_mutil_machine = enable;
} }
void DeviceManager::start_refresher() { m_refresher->Start(); } void DeviceManager::start_refresher() { if (m_refresher) m_refresher->Start(); }
void DeviceManager::stop_refresher() { m_refresher->Stop(); } void DeviceManager::stop_refresher() { if (m_refresher) m_refresher->Stop(); }
void DeviceManager::keep_alive() void DeviceManager::keep_alive()
@@ -297,6 +313,8 @@ namespace Slic3r
/* update localMachineList */ /* update localMachineList */
it = localMachineList.find(dev_id); it = localMachineList.find(dev_id);
AppConfig* config = get_app_config();
if (it != localMachineList.end()) { if (it != localMachineList.end()) {
// update properties // update properties
/* ip changed */ /* ip changed */
@@ -386,7 +404,6 @@ namespace Slic3r
obj->m_is_online = true; obj->m_is_online = true;
//load access code //load access code
AppConfig* config = Slic3r::GUI::wxGetApp().app_config;
if (config) { if (config) {
obj->set_access_code(get_access_code_with_legacy_fallback(config, dev_id, obj->printer_agent_id), false); obj->set_access_code(get_access_code_with_legacy_fallback(config, dev_id, obj->printer_agent_id), false);
} }
@@ -400,7 +417,7 @@ namespace Slic3r
<< ", ip = " << dev_ip <<", printer_name = " << dev_name << ", ip = " << dev_ip <<", printer_name = " << dev_name
<< ", con_type= " << connect_type <<", signal= " << printer_signal << ", bind_state= " << bind_state; << ", con_type= " << connect_type <<", signal= " << printer_signal << ", bind_state= " << bind_state;
} }
update_local_machine(*obj); update_local_machine(*obj, config);
} }
catch (...) { catch (...) {
; ;
@@ -435,11 +452,16 @@ namespace Slic3r
obj->last_alive = Slic3r::Utils::get_current_time_utc(); obj->last_alive = Slic3r::Utils::get_current_time_utc();
obj->set_access_code(access_code, false); obj->set_access_code(access_code, false);
update_local_machine(*obj); update_local_machine(*obj, get_app_config());
return obj; return obj;
} }
void DeviceManager::update_local_machine(const MachineObject& m)
{
update_local_machine(m, GUI::wxGetApp().app_config);
}
int DeviceManager::query_bind_status(std::string& msg, const std::string& provider) int DeviceManager::query_bind_status(std::string& msg, const std::string& provider)
{ {
if (!m_agent) if (!m_agent)
@@ -573,13 +595,13 @@ namespace Slic3r
<< " cur_selected=" << selected_machine; << " cur_selected=" << selected_machine;
auto my_machine_list = get_my_machine_list(get_current_printer_agent_id()); auto my_machine_list = get_my_machine_list(get_current_printer_agent_id());
auto it = my_machine_list.find(dev_id); auto it = my_machine_list.find(dev_id);
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: set_selected_machine lookup dev_id=" << dev_id BOOST_LOG_TRIVIAL(trace) << "Orca diagnostic: set_selected_machine lookup dev_id=" << dev_id
<< " found=" << (it != my_machine_list.end()) << " found=" << (it != my_machine_list.end())
<< " my_machine_count=" << my_machine_list.size() << " my_machine_count=" << my_machine_list.size()
<< " current_agent=" << get_current_printer_agent_id() << " current_agent=" << get_current_printer_agent_id()
<< " provider=" << GUI::wxGetApp().get_printer_cloud_provider(); << " provider=" << GUI::wxGetApp().get_printer_cloud_provider();
if (it != my_machine_list.end() && it->second) { if (it != my_machine_list.end() && it->second) {
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: target machine dev_id=" << it->second->get_dev_id() BOOST_LOG_TRIVIAL(trace) << "Orca diagnostic: target machine dev_id=" << it->second->get_dev_id()
<< " printer_agent_id=" << it->second->printer_agent_id << " printer_agent_id=" << it->second->printer_agent_id
<< " connection_type=" << it->second->connection_type() << " connection_type=" << it->second->connection_type()
<< " dev_connection_type=" << it->second->dev_connection_type; << " dev_connection_type=" << it->second->dev_connection_type;
@@ -598,7 +620,7 @@ namespace Slic3r
} }
else if (last_selected->second->connection_type() == "cloud") { else if (last_selected->second->connection_type() == "cloud") {
const int result = m_agent->set_user_selected_machine(""); const int result = m_agent->set_user_selected_machine("");
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: cleared previous cloud selection dev_id=" BOOST_LOG_TRIVIAL(trace) << "Orca diagnostic: cleared previous cloud selection dev_id="
<< selected_machine << " result=" << result; << selected_machine << " result=" << result;
} }
} }
@@ -635,7 +657,8 @@ namespace Slic3r
it->second->reset(); it->second->reset();
#if !BBL_RELEASE_TO_PUBLIC #if !BBL_RELEASE_TO_PUBLIC
it->second->connect(Slic3r::GUI::wxGetApp().app_config->get("enable_ssl_for_mqtt") == "true" ? true : false); AppConfig* config = get_app_config();
it->second->connect(config && config->get("enable_ssl_for_mqtt") == "true");
#else #else
it->second->connect(it->second->local_use_ssl); it->second->connect(it->second->local_use_ssl);
#endif #endif
@@ -652,7 +675,7 @@ namespace Slic3r
// diff dev_id, cloud => set_user_selected_machine(new) // diff dev_id, cloud => set_user_selected_machine(new)
BOOST_LOG_TRIVIAL(info) << "set_selected_machine: select new cloud machine, dev_id =" << dev_id; BOOST_LOG_TRIVIAL(info) << "set_selected_machine: select new cloud machine, dev_id =" << dev_id;
const int result = m_agent->set_user_selected_machine(dev_id); const int result = m_agent->set_user_selected_machine(dev_id);
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: set new cloud selection dev_id=" BOOST_LOG_TRIVIAL(trace) << "Orca diagnostic: set new cloud selection dev_id="
<< dev_id << " result=" << result; << dev_id << " result=" << result;
it->second->reset(); it->second->reset();
} }
@@ -661,7 +684,8 @@ namespace Slic3r
BOOST_LOG_TRIVIAL(info) << "set_selected_machine: select new lan machine, dev_id =" << dev_id; BOOST_LOG_TRIVIAL(info) << "set_selected_machine: select new lan machine, dev_id =" << dev_id;
it->second->reset(); it->second->reset();
#if !BBL_RELEASE_TO_PUBLIC #if !BBL_RELEASE_TO_PUBLIC
it->second->connect(Slic3r::GUI::wxGetApp().app_config->get("enable_ssl_for_mqtt") == "true" ? true : false); AppConfig* config = get_app_config();
it->second->connect(config && config->get("enable_ssl_for_mqtt") == "true");
#else #else
it->second->connect(it->second->local_use_ssl); it->second->connect(it->second->local_use_ssl);
#endif #endif
@@ -681,7 +705,7 @@ namespace Slic3r
selected_machine = dev_id; selected_machine = dev_id;
record_user_last_machine(selected_machine); record_user_last_machine(selected_machine);
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: DeviceManager selection complete selected_machine=" BOOST_LOG_TRIVIAL(trace) << "Orca diagnostic: DeviceManager selection complete selected_machine="
<< selected_machine; << selected_machine;
return true; return true;
} }
@@ -714,7 +738,7 @@ namespace Slic3r
BOOST_LOG_TRIVIAL(trace) << "add_user_subscribe: " << it->first; BOOST_LOG_TRIVIAL(trace) << "add_user_subscribe: " << it->first;
} }
const int result = m_agent->add_subscribe(dev_list); const int result = m_agent->add_subscribe(dev_list);
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: add_user_subscribe count=" << dev_list.size() BOOST_LOG_TRIVIAL(trace) << "Orca diagnostic: add_user_subscribe count=" << dev_list.size()
<< " result=" << result; << " result=" << result;
} }
@@ -729,7 +753,7 @@ namespace Slic3r
BOOST_LOG_TRIVIAL(trace) << "del_user_subscribe: " << it->first; BOOST_LOG_TRIVIAL(trace) << "del_user_subscribe: " << it->first;
} }
const int result = m_agent->del_subscribe(dev_list); const int result = m_agent->del_subscribe(dev_list);
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: del_user_subscribe count=" << dev_list.size() BOOST_LOG_TRIVIAL(trace) << "Orca diagnostic: del_user_subscribe count=" << dev_list.size()
<< " result=" << result; << " result=" << result;
} }
@@ -851,14 +875,15 @@ namespace Slic3r
json j = json::parse(body); json j = json::parse(body);
const bool has_request_context = j.contains("provider") && j.contains("agent_id") && j.contains("generation"); const bool has_request_context = j.contains("provider") && j.contains("agent_id") && j.contains("generation");
const std::string current_provider = get_current_cloud_provider();
const std::string provider = j.contains("provider") ? j["provider"].get<std::string>() const std::string provider = j.contains("provider") ? j["provider"].get<std::string>()
: GUI::wxGetApp().get_printer_cloud_provider(); : current_provider;
const std::string agent_id = j.contains("agent_id") ? j["agent_id"].get<std::string>() const std::string agent_id = j.contains("agent_id") ? j["agent_id"].get<std::string>()
: get_current_printer_agent_id(); : get_current_printer_agent_id();
const std::uint64_t generation = j.value("generation", std::uint64_t(0)); const std::uint64_t generation = j.value("generation", std::uint64_t(0));
if (has_request_context && if (has_request_context &&
(provider != GUI::wxGetApp().get_printer_cloud_provider() || (provider != current_provider ||
agent_id != get_current_printer_agent_id() || agent_id != get_current_printer_agent_id() ||
generation != (m_agent ? m_agent->get_user_machine_list_generation() : 0))) { generation != (m_agent ? m_agent->get_user_machine_list_generation() : 0))) {
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ": ignoring stale response provider=" BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ": ignoring stale response provider="
@@ -903,7 +928,8 @@ namespace Slic3r
if (obj->get_dev_ip().empty()) if (obj->get_dev_ip().empty())
{ {
obj->get_dev_ip() = Slic3r::GUI::wxGetApp().app_config->get("ip_address", dev_id); if (AppConfig* config = get_app_config())
obj->get_dev_ip() = config->get("ip_address", dev_id);
} }
userMachineList.insert(std::make_pair(dev_id, obj)); userMachineList.insert(std::make_pair(dev_id, obj));
} }
@@ -948,7 +974,7 @@ namespace Slic3r
obj->set_access_code(acc_code); obj->set_access_code(acc_code);
} }
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: parsed cloud machine dev_id=" << dev_id BOOST_LOG_TRIVIAL(trace) << "Orca diagnostic: parsed cloud machine dev_id=" << dev_id
<< " name=" << obj->get_dev_name() << " name=" << obj->get_dev_name()
<< " agent_id=" << obj->printer_agent_id << " agent_id=" << obj->printer_agent_id
<< " connection_type=" << obj->connection_type() << " connection_type=" << obj->connection_type()
@@ -968,7 +994,7 @@ namespace Slic3r
iterat++; iterat++;
} }
} }
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: parse_user_print_info complete provider=" << provider BOOST_LOG_TRIVIAL(trace) << "Orca diagnostic: parse_user_print_info complete provider=" << provider
<< " parsed_count=" << new_list.size() << " parsed_count=" << new_list.size()
<< " stored_count=" << userMachineList.size(); << " stored_count=" << userMachineList.size();
} }
@@ -987,29 +1013,29 @@ namespace Slic3r
unsigned int http_code; unsigned int http_code;
std::string body; std::string body;
int result = m_agent->get_user_print_info(&http_code, &body, provider); int result = m_agent->get_user_print_info(&http_code, &body, provider);
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: get_user_print_info provider=" << provider BOOST_LOG_TRIVIAL(trace) << "Orca diagnostic: get_user_print_info provider=" << provider
<< " result=" << result << " http_code=" << http_code << " result=" << result << " http_code=" << http_code
<< " body_bytes=" << body.size(); << " body_bytes=" << body.size();
if (result == 0) if (result == 0)
{ {
// parse_user_print_info and on_machine_alive (SSDP for discovery) both mutate the same userMachineList map. // parse_user_print_info and on_machine_alive (SSDP for discovery) both mutate the same userMachineList map.
// on_machine_alive mutates the map on the UI thread, do the same for parse_user_print_info. // on_machine_alive mutates the map on the UI thread, do the same for parse_user_print_info.
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: queueing parse_user_print_info on UI thread"; BOOST_LOG_TRIVIAL(trace) << "Orca diagnostic: queueing parse_user_print_info on UI thread";
Slic3r::GUI::wxGetApp().CallAfter([this, body]() { parse_user_print_info(body); }); Slic3r::GUI::wxGetApp().CallAfter([this, body]() { parse_user_print_info(body); });
} }
} }
void DeviceManager::record_user_last_machine(const std::string& dev_id) void DeviceManager::record_user_last_machine(const std::string& dev_id)
{ {
if (Slic3r::GUI::wxGetApp().app_config) { if (AppConfig* config = get_app_config()) {
Slic3r::GUI::wxGetApp().app_config->set("user_last_selected_machine", dev_id); config->set("user_last_selected_machine", dev_id);
} }
} }
std::string DeviceManager::get_user_last_machine() const std::string DeviceManager::get_user_last_machine() const
{ {
if (Slic3r::GUI::wxGetApp().app_config) { if (AppConfig* config = get_app_config()) {
const auto& user_last_machine = Slic3r::GUI::wxGetApp().app_config->get("user_last_selected_machine"); const auto& user_last_machine = config->get("user_last_selected_machine");
if (!user_last_machine.empty()) { if (!user_last_machine.empty()) {
return user_last_machine; return user_last_machine;
} else if (m_agent) { } else if (m_agent) {
+9 -3
View File
@@ -12,6 +12,7 @@ namespace Slic3r
struct BBLocalMachine; struct BBLocalMachine;
class MachineObject; class MachineObject;
class NetworkAgent; class NetworkAgent;
class AppConfig;
namespace GUI { namespace GUI {
class GUI_App; class GUI_App;
@@ -24,6 +25,7 @@ class DeviceManager
friend class DeviceManagerRefresher; friend class DeviceManagerRefresher;
private: private:
NetworkAgent* m_agent{ nullptr }; NetworkAgent* m_agent{ nullptr };
AppConfig* m_app_config{ nullptr };
DeviceManagerRefresher* m_refresher{ nullptr }; DeviceManagerRefresher* m_refresher{ nullptr };
bool m_enable_mutil_machine = false; bool m_enable_mutil_machine = false;
@@ -35,11 +37,13 @@ private:
std::map<std::string, MachineObject*> userMachineList; /* dev_id -> MachineObject* cloudMachine of User */ std::map<std::string, MachineObject*> userMachineList; /* dev_id -> MachineObject* cloudMachine of User */
public: public:
DeviceManager(NetworkAgent* agent = nullptr); DeviceManager(NetworkAgent* agent = nullptr, bool enable_refresher = true,
AppConfig* app_config = nullptr);
~DeviceManager(); ~DeviceManager();
public: public:
NetworkAgent* get_agent() const { return m_agent; } NetworkAgent* get_agent() const { return m_agent; }
AppConfig* get_app_config() const;
void set_agent(NetworkAgent* agent); void set_agent(NetworkAgent* agent);
void start_refresher(); void start_refresher();
@@ -121,6 +125,7 @@ private:
void keep_alive(); void keep_alive();
void check_pushing(); void check_pushing();
std::string get_current_cloud_provider() const;
void OnMachineBindStateChanged(MachineObject* obj, const std::string& new_state); void OnMachineBindStateChanged(MachineObject* obj, const std::string& new_state);
void OnSelectedMachineChanged(const std::string& pre_dev_id, const std::string& new_dev_id); void OnSelectedMachineChanged(const std::string& pre_dev_id, const std::string& new_dev_id);
@@ -133,14 +138,15 @@ public:
std::string connection_type, std::string bind_state, std::string version, std::string connection_type, std::string bind_state, std::string version,
std::string access_code); std::string access_code);
static void update_local_machine(const MachineObject& m); static void update_local_machine(const MachineObject& m);
static void update_local_machine(const MachineObject& m, AppConfig* config);
}; };
class DeviceManagerRefresher : public wxObject class DeviceManagerRefresher : public wxObject
{ {
wxTimer* m_timer{ nullptr }; wxTimer* m_timer{nullptr};
int m_timer_interval_msec = 5000; int m_timer_interval_msec = 5000;
DeviceManager* m_manager{ nullptr }; DeviceManager* m_manager{nullptr};
public: public:
DeviceManagerRefresher(DeviceManager* manger); DeviceManagerRefresher(DeviceManager* manger);
+12 -6
View File
@@ -17,6 +17,7 @@
#include "ReleaseNote.hpp" #include "ReleaseNote.hpp"
#include <thread> #include <thread>
#include <mutex> #include <mutex>
#include <charconv>
#include <codecvt> #include <codecvt>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/typeof/typeof.hpp> #include <boost/typeof/typeof.hpp>
@@ -477,7 +478,7 @@ void MachineObject::set_access_code(std::string code, bool only_refresh)
{ {
this->access_code = code; this->access_code = code;
if (only_refresh) { if (only_refresh) {
AppConfig* config = GUI::wxGetApp().app_config; AppConfig* config = m_manager ? m_manager->get_app_config() : GUI::wxGetApp().app_config;
if (config) { if (config) {
if (is_lan_mode_printer()) { if (is_lan_mode_printer()) {
// why: LAN codes are scoped via BBLocalMachine::access_code, keyed by dev_id and // why: LAN codes are scoped via BBLocalMachine::access_code, keyed by dev_id and
@@ -490,7 +491,7 @@ void MachineObject::set_access_code(std::string code, bool only_refresh)
// fresh from the cloud API's current response, so there's no cross-agent leakage // fresh from the cloud API's current response, so there's no cross-agent leakage
// risk to guard against there. // risk to guard against there.
if (!code.empty()) { if (!code.empty()) {
DeviceManager::update_local_machine(*this); DeviceManager::update_local_machine(*this, config);
} else { } else {
// Only patch an existing record's code - don't persist a brand-new // Only patch an existing record's code - don't persist a brand-new
// never-bound entry just because set_access_code("") was called on it. // never-bound entry just because set_access_code("") was called on it.
@@ -2603,9 +2604,14 @@ void MachineObject::set_print_state(std::string status)
// why: printer agents can report progress without BBL cloud task identity. // why: printer agents can report progress without BBL cloud task identity.
void MachineObject::update_print_progress(const json& value) void MachineObject::update_print_progress(const json& value)
{ {
if (value.is_string()) if (value.is_string()) {
mc_print_percent = stoi(value.get<std::string>()); const std::string progress = value.get<std::string>();
else if (value.is_number_integer()) int parsed_progress;
const auto result = std::from_chars(progress.data(), progress.data() + progress.size(), parsed_progress);
if (result.ec != std::errc{} || result.ptr != progress.data() + progress.size())
return;
mc_print_percent = parsed_progress;
} else if (value.is_number_integer())
mc_print_percent = value.get<int>(); mc_print_percent = value.get<int>();
else else
return; return;
@@ -4650,7 +4656,7 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
if (diff.count() > 10.0f) { if (diff.count() > 10.0f) {
BOOST_LOG_TRIVIAL(trace) << "parse_json timeout = " << diff.count(); BOOST_LOG_TRIVIAL(trace) << "parse_json timeout = " << diff.count();
} }
DeviceManager::update_local_machine(*this); DeviceManager::update_local_machine(*this, m_manager ? m_manager->get_app_config() : GUI::wxGetApp().app_config);
return 0; return 0;
} }
+14 -12
View File
@@ -2383,19 +2383,21 @@ GUI_App::~GUI_App()
bool GUI_App::is_blocking_printing(MachineObject *obj_) bool GUI_App::is_blocking_printing(MachineObject *obj_)
{ {
DeviceManager *dev = Slic3r::GUI::wxGetApp().getDeviceManager();
if (!dev) return true;
if (obj_ == nullptr) {
obj_ = dev->get_selected_machine();
}
if (!obj_)
{
return false;
}
PresetBundle *preset_bundle = wxGetApp().preset_bundle; PresetBundle *preset_bundle = wxGetApp().preset_bundle;
std::string source_model = preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle); const std::string source_model = preset_bundle
? preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle)
: std::string();
return is_blocking_printing(obj_, source_model);
}
bool GUI_App::is_blocking_printing(MachineObject *obj_, const std::string& source_model)
{
DeviceManager *dev = getDeviceManager();
if (!dev) return true;
if (obj_ == nullptr)
obj_ = dev->get_selected_machine();
if (!obj_)
return false;
return !DevPrinterConfigUtil::is_printer_model_compatible(source_model, *obj_); return !DevPrinterConfigUtil::is_printer_model_compatible(source_model, *obj_);
} }
+1
View File
@@ -368,6 +368,7 @@ public:
EAppMode get_app_mode() const { return m_app_mode; } EAppMode get_app_mode() const { return m_app_mode; }
Slic3r::DeviceManager* getDeviceManager() { return m_device_manager; } Slic3r::DeviceManager* getDeviceManager() { return m_device_manager; }
bool is_blocking_printing(MachineObject *obj_ = nullptr); bool is_blocking_printing(MachineObject *obj_ = nullptr);
bool is_blocking_printing(MachineObject *obj_, const std::string& source_model);
Slic3r::TaskManager* getTaskManager() { return m_task_manager; } Slic3r::TaskManager* getTaskManager() { return m_task_manager; }
HMSQuery* get_hms_query() { return hms_query; } HMSQuery* get_hms_query() { return hms_query; }
NetworkAgent* getAgent() { return m_agent; } NetworkAgent* getAgent() { return m_agent; }
+3 -3
View File
@@ -261,7 +261,7 @@ void MonitorPanel::msw_rescale()
void MonitorPanel::select_machine(std::string machine_sn) void MonitorPanel::select_machine(std::string machine_sn)
{ {
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: MonitorPanel::select_machine queueing machine_sn=" << machine_sn; BOOST_LOG_TRIVIAL(trace) << "Orca diagnostic: MonitorPanel::select_machine queueing machine_sn=" << machine_sn;
wxCommandEvent *event = new wxCommandEvent(wxEVT_COMMAND_CHOICE_SELECTED); wxCommandEvent *event = new wxCommandEvent(wxEVT_COMMAND_CHOICE_SELECTED);
event->SetString(machine_sn); event->SetString(machine_sn);
wxQueueEvent(this, event); wxQueueEvent(this, event);
@@ -280,7 +280,7 @@ void MonitorPanel::on_select_printer(wxCommandEvent& event)
{ {
Slic3r::DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); Slic3r::DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager();
const std::string requested_dev_id = event.GetString().ToStdString(); const std::string requested_dev_id = event.GetString().ToStdString();
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: MonitorPanel::on_select_printer requested_dev_id=" BOOST_LOG_TRIVIAL(trace) << "Orca diagnostic: MonitorPanel::on_select_printer requested_dev_id="
<< requested_dev_id << " device_manager=" << (dev ? "set" : "null"); << requested_dev_id << " device_manager=" << (dev ? "set" : "null");
if (!dev) return; if (!dev) return;
@@ -289,7 +289,7 @@ void MonitorPanel::on_select_printer(wxCommandEvent& event)
} }
const bool selected = dev->set_selected_machine(requested_dev_id); const bool selected = dev->set_selected_machine(requested_dev_id);
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: MonitorPanel::on_select_printer set_selected_machine result=" BOOST_LOG_TRIVIAL(trace) << "Orca diagnostic: MonitorPanel::on_select_printer set_selected_machine result="
<< selected << " selected_dev_id=" << selected << " selected_dev_id="
<< (dev->get_selected_machine() ? dev->get_selected_machine()->get_dev_id() : "<null>"); << (dev->get_selected_machine() ? dev->get_selected_machine()->get_dev_id() : "<null>");
if (!selected) if (!selected)
+1 -14
View File
@@ -3,7 +3,6 @@
#include "GUI_App.hpp" #include "GUI_App.hpp"
#include "MainFrame.hpp" #include "MainFrame.hpp"
#include "DeviceCore/DevConfigUtil.h"
namespace Slic3r { namespace Slic3r {
namespace GUI { namespace GUI {
@@ -52,7 +51,7 @@ void DeviceItem::sync_state()
state_printable = 6; state_printable = 6;
} }
if (is_blocking_printing(obj_)) { if (wxGetApp().is_blocking_printing(obj_)) {
state_printable = 5; state_printable = 5;
} }
@@ -105,18 +104,6 @@ void DeviceItem::unselected()
} }
} }
bool DeviceItem::is_blocking_printing(MachineObject* obj_)
{
DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager();
if (!dev) return true;
std::string source_model = "";
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
source_model = preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle);
return !DevPrinterConfigUtil::is_printer_model_compatible(source_model, *obj_);
}
void DeviceItem::update_item(const DeviceItem* item) void DeviceItem::update_item(const DeviceItem* item)
{ {
// Except for the selected status, everything else is updated // Except for the selected status, everything else is updated
-1
View File
@@ -59,7 +59,6 @@ public:
void selected(); void selected();
void unselected(); void unselected();
bool is_blocking_printing(MachineObject* obj_);
void update_item(const DeviceItem* item); void update_item(const DeviceItem* item);
}; };
+1 -1
View File
@@ -171,7 +171,7 @@ public:
void add_with_checkbox(PrintDialogStatus state, wxString msg, wxString checkbox_label, bool checked, std::function<void(bool)> checkbox_callback); void add_with_checkbox(PrintDialogStatus state, wxString msg, wxString checkbox_label, bool checked, std::function<void(bool)> checkbox_callback);
static ::std::string get_print_status_info(PrintDialogStatus status); static ::std::string get_print_status_info(PrintDialogStatus status);
wxString get_pre_state_msg(PrintDialogStatus status); static wxString get_pre_state_msg(PrintDialogStatus status);
static bool is_error(PrintDialogStatus status) { return (PrintStatusErrorBegin < status) && (PrintStatusErrorEnd > status); }; static bool is_error(PrintDialogStatus status) { return (PrintStatusErrorBegin < status) && (PrintStatusErrorEnd > status); };
static bool is_error_printer(PrintDialogStatus status) { return (PrintStatusPrinterErrorBegin < status) && (PrintStatusPrinterErrorEnd > status); }; static bool is_error_printer(PrintDialogStatus status) { return (PrintStatusPrinterErrorBegin < status) && (PrintStatusPrinterErrorEnd > status); };
static bool is_error_filament(PrintDialogStatus status) { return (PrintStatusFilamentErrorBegin < status) && (PrintStatusFilamentErrorEnd > status); }; static bool is_error_filament(PrintDialogStatus status) { return (PrintStatusFilamentErrorBegin < status) && (PrintStatusFilamentErrorEnd > status); };
+5 -15
View File
@@ -2518,23 +2518,13 @@ void SelectMachineDialog::on_cancel(wxCloseEvent &event)
bool SelectMachineDialog::is_blocking_printing(MachineObject* obj_) bool SelectMachineDialog::is_blocking_printing(MachineObject* obj_)
{ {
DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); if (m_print_type == PrintFromType::FROM_NORMAL)
if (!dev) return true; return wxGetApp().is_blocking_printing(obj_);
auto target_model = obj_->printer_type;
std::string source_model = "";
if (m_print_type == PrintFromType::FROM_NORMAL) { std::string source_model;
PresetBundle* preset_bundle = wxGetApp().preset_bundle; if (m_print_type == PrintFromType::FROM_SDCARD_VIEW && !m_required_data_plate_data_list.empty())
source_model = preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle);
}else if (m_print_type == PrintFromType::FROM_SDCARD_VIEW) {
if (m_required_data_plate_data_list.size() > 0) {
source_model = m_required_data_plate_data_list[m_print_plate_idx]->printer_model_id; source_model = m_required_data_plate_data_list[m_print_plate_idx]->printer_model_id;
} return wxGetApp().is_blocking_printing(obj_, source_model);
}
return !DevPrinterConfigUtil::is_printer_model_compatible(source_model, *obj_);
} }
static std::unordered_set<int> _get_used_nozzle_idxes() static std::unordered_set<int> _get_used_nozzle_idxes()
+1 -1
View File
@@ -738,7 +738,7 @@ void SendMultiMachinePage::on_send(wxCommandEvent& event)
if (obj && obj->is_online() && !obj->can_abort() && !obj->is_in_upgrading() && it->second->get_state_selected() == 1 && it->second->state_printable <= 2) { if (obj && obj->is_online() && !obj->can_abort() && !obj->is_in_upgrading() && it->second->get_state_selected() == 1 && it->second->state_printable <= 2) {
if (!it->second->is_blocking_printing(obj)) { if (!wxGetApp().is_blocking_printing(obj)) {
PrintParams params = request_params(obj); PrintParams params = request_params(obj);
print_params.push_back(params); print_params.push_back(params);
} }
+1 -11
View File
@@ -1277,7 +1277,7 @@ void SendToPrinterDialog::update_show_status()
reset_timeout(); reset_timeout();
// reading done // reading done
if (is_blocking_printing(obj_)) { if (wxGetApp().is_blocking_printing(obj_)) {
show_status(PrintDialogStatus::PrintStatusUnsupportedPrinter); show_status(PrintDialogStatus::PrintStatusUnsupportedPrinter);
return; return;
} }
@@ -1342,16 +1342,6 @@ void SendToPrinterDialog::update_show_status()
} }
} }
bool SendToPrinterDialog::is_blocking_printing(MachineObject* obj_)
{
DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager();
if (!dev) return true;
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
auto source_model = preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle);
return !DevPrinterConfigUtil::is_printer_model_compatible(source_model, *obj_);
}
void SendToPrinterDialog::Enable_Refresh_Button(bool en) void SendToPrinterDialog::Enable_Refresh_Button(bool en)
{ {
if (!en) { if (!en) {
-1
View File
@@ -181,7 +181,6 @@ public:
void reset_timeout(); void reset_timeout();
void update_user_printer(); void update_user_printer();
void update_show_status(); void update_show_status();
bool is_blocking_printing(MachineObject* obj_);
void prepare(int print_plate_idx); void prepare(int print_plate_idx);
void check_focus(wxWindow* window); void check_focus(wxWindow* window);
void check_fcous_state(wxWindow* window); void check_fcous_state(wxWindow* window);
+23 -12
View File
@@ -1742,6 +1742,8 @@ void SyncAmsInfoDialog::show_status(PrintDialogStatus status, std::vector<wxStri
update_print_status_msg(msg_text, true, true); update_print_status_msg(msg_text, true, true);
} else if (status == PrintDialogStatus::PrintStatusAmsMappingSuccess) { } else if (status == PrintDialogStatus::PrintStatusAmsMappingSuccess) {
update_print_status_msg(wxEmptyString, false, false); update_print_status_msg(wxEmptyString, false, false);
} else if (status == PrintDialogStatus::PrintStatusOptionalPrinterModel) {
update_print_status_msg(PrePrintChecker::get_pre_state_msg(PrintDialogStatus::PrintStatusOptionalPrinterModel), true, true);
} else if (status == PrintDialogStatus::PrintStatusAmsMappingInvalid) { } else if (status == PrintDialogStatus::PrintStatusAmsMappingInvalid) {
update_print_status_msg(wxEmptyString, true, false); update_print_status_msg(wxEmptyString, true, false);
} else if (status == PrintDialogStatus::PrintStatusAmsMappingMixInvalid) { } else if (status == PrintDialogStatus::PrintStatusAmsMappingMixInvalid) {
@@ -1861,19 +1863,13 @@ void SyncAmsInfoDialog::on_cancel(wxCloseEvent &event)
bool SyncAmsInfoDialog::is_blocking_printing(MachineObject *obj_) bool SyncAmsInfoDialog::is_blocking_printing(MachineObject *obj_)
{ {
DeviceManager *dev = Slic3r::GUI::wxGetApp().getDeviceManager(); if (m_print_type == PrintFromType::FROM_NORMAL)
if (!dev) return true; return wxGetApp().is_blocking_printing(obj_);
std::string source_model = "";
if (m_print_type == PrintFromType::FROM_NORMAL) { std::string source_model;
PresetBundle *preset_bundle = wxGetApp().preset_bundle; if (m_print_type == PrintFromType::FROM_SDCARD_VIEW && !m_required_data_plate_data_list.empty())
source_model = preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle); source_model = m_required_data_plate_data_list[m_print_plate_idx]->printer_model_id;
return wxGetApp().is_blocking_printing(obj_, source_model);
} else if (m_print_type == PrintFromType::FROM_SDCARD_VIEW) {
if (m_required_data_plate_data_list.size() > 0) { source_model = m_required_data_plate_data_list[m_print_plate_idx]->printer_model_id; }
}
return !DevPrinterConfigUtil::is_printer_model_compatible(source_model, *obj_);
} }
bool SyncAmsInfoDialog::is_same_nozzle_type(std::string &filament_type, NozzleType &tag_nozzle_type) bool SyncAmsInfoDialog::is_same_nozzle_type(std::string &filament_type, NozzleType &tag_nozzle_type)
@@ -2263,6 +2259,18 @@ void SyncAmsInfoDialog::update_show_status()
reset_timeout(); reset_timeout();
bool has_optional_printer_model = DevPrinterConfigUtil::is_optional_printer_model_id(obj_->printer_type);
if (m_print_type == PrintFromType::FROM_NORMAL) {
if (PresetBundle *preset_bundle = wxGetApp().preset_bundle) {
has_optional_printer_model = has_optional_printer_model ||
DevPrinterConfigUtil::is_optional_printer_model_id(
preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle));
}
} else if (m_print_type == PrintFromType::FROM_SDCARD_VIEW && !m_required_data_plate_data_list.empty()) {
has_optional_printer_model = has_optional_printer_model ||
DevPrinterConfigUtil::is_optional_printer_model_id(m_required_data_plate_data_list[m_print_plate_idx]->printer_model_id);
}
if (!obj_->GetConfig()->SupportPrintAllPlates() && m_print_plate_idx == PLATE_ALL_IDX) { if (!obj_->GetConfig()->SupportPrintAllPlates() && m_print_plate_idx == PLATE_ALL_IDX) {
show_status(PrintDialogStatus::PrintStatusNotSupportedPrintAll); show_status(PrintDialogStatus::PrintStatusNotSupportedPrintAll);
return; return;
@@ -2356,6 +2364,9 @@ void SyncAmsInfoDialog::update_show_status()
} }
} }
} }
if (has_optional_printer_model)
show_status(PrintDialogStatus::PrintStatusOptionalPrinterModel);
} }
bool SyncAmsInfoDialog::has_timelapse_warning() bool SyncAmsInfoDialog::has_timelapse_warning()
+16 -4
View File
@@ -4,13 +4,23 @@
namespace Slic3r { namespace GUI { namespace Slic3r { namespace GUI {
WebMediaController::WebMediaController(wxWebView* webview) : m_webview(webview) namespace {
void initialize_webview(wxWebView* webview)
{ {
if (!m_webview) if (!webview)
return; return;
m_webview->SetBackgroundColour(*wxBLACK); webview->SetBackgroundColour(*wxBLACK);
m_webview->SetPage("<html><head><style>html,body{margin:0;height:100%;background:#000;}</style></head><body></body></html>", ""); webview->SetPage("<html><head><style>html,body{margin:0;height:100%;background:#000;}</style></head><body></body></html>", "");
}
} // namespace
WebMediaController::WebMediaController(wxWebView* webview)
: m_webview(webview)
{
initialize_webview(m_webview);
} }
void WebMediaController::Load(wxURI url) void WebMediaController::Load(wxURI url)
@@ -71,6 +81,8 @@ void WebMediaController::Stop()
if (m_webview) { if (m_webview) {
m_webview->RunScript("if(typeof stopCameraRefresh==='function') stopCameraRefresh();"); m_webview->RunScript("if(typeof stopCameraRefresh==='function') stopCameraRefresh();");
m_webview->Stop(); m_webview->Stop();
m_webview->SetPage("", "about:blank");
m_webview->ClearHistory();
} }
m_url.clear(); m_url.clear();
} }
+1 -1
View File
@@ -23,7 +23,7 @@ public:
void Stop() override; void Stop() override;
private: private:
wxWebView* m_webview; wxWebView* m_webview = nullptr;
std::string m_url; std::string m_url;
CameraStreamMode m_stream_mode = CameraStreamMode::http; CameraStreamMode m_stream_mode = CameraStreamMode::http;
}; };
+6 -6
View File
@@ -950,11 +950,11 @@ std::string NetworkAgent::get_user_selected_machine()
int NetworkAgent::set_user_selected_machine(std::string dev_id) int NetworkAgent::set_user_selected_machine(std::string dev_id)
{ {
BOOST_LOG_TRIVIAL(info) << "NetworkAgent::set_user_selected_machine: dev_id=" << dev_id BOOST_LOG_TRIVIAL(trace) << "NetworkAgent::set_user_selected_machine: dev_id=" << dev_id
<< " printer_agent=" << (m_printer_agent ? m_printer_agent->get_agent_info().id : "<null>"); << " printer_agent=" << (m_printer_agent ? m_printer_agent->get_agent_info().id : "<null>");
if (m_printer_agent) { if (m_printer_agent) {
const int result = m_printer_agent->set_user_selected_machine(dev_id); const int result = m_printer_agent->set_user_selected_machine(dev_id);
BOOST_LOG_TRIVIAL(info) << "NetworkAgent::set_user_selected_machine: result=" << result; BOOST_LOG_TRIVIAL(trace) << "NetworkAgent::set_user_selected_machine: result=" << result;
return result; return result;
} }
BOOST_LOG_TRIVIAL(warning) << "NetworkAgent::set_user_selected_machine: no printer agent"; BOOST_LOG_TRIVIAL(warning) << "NetworkAgent::set_user_selected_machine: no printer agent";
@@ -977,11 +977,11 @@ int NetworkAgent::stop_subscribe(std::string module)
int NetworkAgent::add_subscribe(std::vector<std::string> dev_list) int NetworkAgent::add_subscribe(std::vector<std::string> dev_list)
{ {
BOOST_LOG_TRIVIAL(info) << "NetworkAgent::add_subscribe: count=" << dev_list.size() BOOST_LOG_TRIVIAL(trace) << "NetworkAgent::add_subscribe: count=" << dev_list.size()
<< " printer_agent=" << (m_printer_agent ? m_printer_agent->get_agent_info().id : "<null>"); << " printer_agent=" << (m_printer_agent ? m_printer_agent->get_agent_info().id : "<null>");
if (m_printer_agent) { if (m_printer_agent) {
const int result = m_printer_agent->add_subscribe(std::move(dev_list)); const int result = m_printer_agent->add_subscribe(std::move(dev_list));
BOOST_LOG_TRIVIAL(info) << "NetworkAgent::add_subscribe: result=" << result; BOOST_LOG_TRIVIAL(trace) << "NetworkAgent::add_subscribe: result=" << result;
return result; return result;
} }
BOOST_LOG_TRIVIAL(warning) << "NetworkAgent::add_subscribe: no printer agent"; BOOST_LOG_TRIVIAL(warning) << "NetworkAgent::add_subscribe: no printer agent";
@@ -990,11 +990,11 @@ int NetworkAgent::add_subscribe(std::vector<std::string> dev_list)
int NetworkAgent::del_subscribe(std::vector<std::string> dev_list) int NetworkAgent::del_subscribe(std::vector<std::string> dev_list)
{ {
BOOST_LOG_TRIVIAL(info) << "NetworkAgent::del_subscribe: count=" << dev_list.size() BOOST_LOG_TRIVIAL(trace) << "NetworkAgent::del_subscribe: count=" << dev_list.size()
<< " printer_agent=" << (m_printer_agent ? m_printer_agent->get_agent_info().id : "<null>"); << " printer_agent=" << (m_printer_agent ? m_printer_agent->get_agent_info().id : "<null>");
if (m_printer_agent) { if (m_printer_agent) {
const int result = m_printer_agent->del_subscribe(std::move(dev_list)); const int result = m_printer_agent->del_subscribe(std::move(dev_list));
BOOST_LOG_TRIVIAL(info) << "NetworkAgent::del_subscribe: result=" << result; BOOST_LOG_TRIVIAL(trace) << "NetworkAgent::del_subscribe: result=" << result;
return result; return result;
} }
BOOST_LOG_TRIVIAL(warning) << "NetworkAgent::del_subscribe: no printer agent"; BOOST_LOG_TRIVIAL(warning) << "NetworkAgent::del_subscribe: no printer agent";
@@ -25,6 +25,12 @@
} ORCA_PY_AGENT_CATCH(name) \ } ORCA_PY_AGENT_CATCH(name) \
return printer_agent_failure<ret>() return printer_agent_failure<ret>()
#define ORCA_PY_AGENT_OVERRIDE_DEFAULT(ret, name, ...) \
try { \
ORCA_PY_OVERRIDE_AUDITED([] {}, PYBIND11_OVERRIDE, ret, PrinterAgentPluginCapability, name, ##__VA_ARGS__); \
} ORCA_PY_AGENT_CATCH(name) \
return printer_agent_failure<ret>()
namespace Slic3r { namespace Slic3r {
// NetworkAgent's no-agent answer: -1 for a status code, the empty value (false, "", none) otherwise. // NetworkAgent's no-agent answer: -1 for a status code, the empty value (false, "", none) otherwise.
template<typename T> T printer_agent_failure() template<typename T> T printer_agent_failure()
@@ -67,56 +73,56 @@ public:
int command_ams_refresh_rfid(std::string dev_id, std::string tray_id, int sequence_id, bool lan_mode) override int command_ams_refresh_rfid(std::string dev_id, std::string tray_id, int sequence_id, bool lan_mode) override
{ {
ORCA_PY_AGENT_OVERRIDE(int, command_ams_refresh_rfid, dev_id, tray_id, sequence_id, lan_mode); ORCA_PY_AGENT_OVERRIDE_DEFAULT(int, command_ams_refresh_rfid, dev_id, tray_id, sequence_id, lan_mode);
} }
int command_ams_calibrate(std::string dev_id, int ams_id, int sequence_id, bool lan_mode) override int command_ams_calibrate(std::string dev_id, int ams_id, int sequence_id, bool lan_mode) override
{ {
ORCA_PY_AGENT_OVERRIDE(int, command_ams_calibrate, dev_id, ams_id, sequence_id, lan_mode); ORCA_PY_AGENT_OVERRIDE_DEFAULT(int, command_ams_calibrate, dev_id, ams_id, sequence_id, lan_mode);
} }
int command_ams_select_tray(std::string dev_id, std::string tray_id, int sequence_id, bool lan_mode) override int command_ams_select_tray(std::string dev_id, std::string tray_id, int sequence_id, bool lan_mode) override
{ {
ORCA_PY_AGENT_OVERRIDE(int, command_ams_select_tray, dev_id, tray_id, sequence_id, lan_mode); ORCA_PY_AGENT_OVERRIDE_DEFAULT(int, command_ams_select_tray, dev_id, tray_id, sequence_id, lan_mode);
} }
int command_start_camera(std::string dev_id) override int command_start_camera(std::string dev_id) override
{ {
ORCA_PY_AGENT_OVERRIDE(int, command_start_camera, dev_id); ORCA_PY_AGENT_OVERRIDE_DEFAULT(int, command_start_camera, dev_id);
} }
int command_xyz_abs(std::string dev_id, int sequence_id, bool lan_mode) override int command_xyz_abs(std::string dev_id, int sequence_id, bool lan_mode) override
{ {
ORCA_PY_AGENT_OVERRIDE(int, command_xyz_abs, dev_id, sequence_id, lan_mode); ORCA_PY_AGENT_OVERRIDE_DEFAULT(int, command_xyz_abs, dev_id, sequence_id, lan_mode);
} }
int command_auto_leveling(std::string dev_id, int sequence_id, bool lan_mode) override int command_auto_leveling(std::string dev_id, int sequence_id, bool lan_mode) override
{ {
ORCA_PY_AGENT_OVERRIDE(int, command_auto_leveling, dev_id, sequence_id, lan_mode); ORCA_PY_AGENT_OVERRIDE_DEFAULT(int, command_auto_leveling, dev_id, sequence_id, lan_mode);
} }
int command_go_home(std::string dev_id, bool is_printing, bool supports_mqtt_homing, int command_go_home(std::string dev_id, bool is_printing, bool supports_mqtt_homing,
int sequence_id, bool lan_mode) override int sequence_id, bool lan_mode) override
{ {
ORCA_PY_AGENT_OVERRIDE(int, command_go_home, dev_id, is_printing, supports_mqtt_homing, sequence_id, lan_mode); ORCA_PY_AGENT_OVERRIDE_DEFAULT(int, command_go_home, dev_id, is_printing, supports_mqtt_homing, sequence_id, lan_mode);
} }
int command_set_bed(std::string dev_id, int temp, bool supports_mqtt_bed_ctrl, int command_set_bed(std::string dev_id, int temp, bool supports_mqtt_bed_ctrl,
int sequence_id, bool lan_mode) override int sequence_id, bool lan_mode) override
{ {
ORCA_PY_AGENT_OVERRIDE(int, command_set_bed, dev_id, temp, supports_mqtt_bed_ctrl, sequence_id, lan_mode); ORCA_PY_AGENT_OVERRIDE_DEFAULT(int, command_set_bed, dev_id, temp, supports_mqtt_bed_ctrl, sequence_id, lan_mode);
} }
int command_set_nozzle(std::string dev_id, int temp, int sequence_id, bool lan_mode) override int command_set_nozzle(std::string dev_id, int temp, int sequence_id, bool lan_mode) override
{ {
ORCA_PY_AGENT_OVERRIDE(int, command_set_nozzle, dev_id, temp, sequence_id, lan_mode); ORCA_PY_AGENT_OVERRIDE_DEFAULT(int, command_set_nozzle, dev_id, temp, sequence_id, lan_mode);
} }
int command_axis_control(std::string dev_id, std::string axis, double unit, double input_val, int command_axis_control(std::string dev_id, std::string axis, double unit, double input_val,
int speed, bool is_core_xy, bool supports_mqtt_axis_control, int speed, bool is_core_xy, bool supports_mqtt_axis_control,
int sequence_id, bool lan_mode) override int sequence_id, bool lan_mode) override
{ {
ORCA_PY_AGENT_OVERRIDE(int, command_axis_control, dev_id, axis, unit, input_val, speed, ORCA_PY_AGENT_OVERRIDE_DEFAULT(int, command_axis_control, dev_id, axis, unit, input_val, speed,
is_core_xy, supports_mqtt_axis_control, sequence_id, lan_mode); is_core_xy, supports_mqtt_axis_control, sequence_id, lan_mode);
} }
@@ -153,22 +159,22 @@ public:
FilamentSyncMode get_filament_sync_mode() const override FilamentSyncMode get_filament_sync_mode() const override
{ {
ORCA_PY_AGENT_OVERRIDE(FilamentSyncMode, get_filament_sync_mode); ORCA_PY_AGENT_OVERRIDE_DEFAULT(FilamentSyncMode, get_filament_sync_mode);
} }
CameraStreamMode get_camera_stream_mode() const override CameraStreamMode get_camera_stream_mode() const override
{ {
ORCA_PY_AGENT_OVERRIDE(CameraStreamMode, get_camera_stream_mode); ORCA_PY_AGENT_OVERRIDE_DEFAULT(CameraStreamMode, get_camera_stream_mode);
} }
std::string get_camera_url() const override std::string get_camera_url() const override
{ {
ORCA_PY_AGENT_OVERRIDE(std::string, get_camera_url); ORCA_PY_AGENT_OVERRIDE_DEFAULT(std::string, get_camera_url);
} }
bool fetch_filament_info(std::string dev_id, FilamentSyncMode sync_mode) override bool fetch_filament_info(std::string dev_id, FilamentSyncMode sync_mode) override
{ {
ORCA_PY_AGENT_OVERRIDE(bool, fetch_filament_info, dev_id, sync_mode); ORCA_PY_AGENT_OVERRIDE_DEFAULT(bool, fetch_filament_info, dev_id, sync_mode);
} }
int check_cert() override int check_cert() override
@@ -178,7 +184,7 @@ public:
void install_device_cert(std::string dev_id, bool lan_only) override void install_device_cert(std::string dev_id, bool lan_only) override
{ {
ORCA_PY_AGENT_OVERRIDE(void, install_device_cert, dev_id, lan_only); ORCA_PY_AGENT_OVERRIDE_DEFAULT(void, install_device_cert, dev_id, lan_only);
} }
int ping_bind(std::string ping_code) override int ping_bind(std::string ping_code) override
@@ -213,7 +219,7 @@ public:
int get_hms_snapshot(std::string dev_id, std::string file_name, std::function<void(std::string, int)> callback) override int get_hms_snapshot(std::string dev_id, std::string file_name, std::function<void(std::string, int)> callback) override
{ {
ORCA_PY_AGENT_OVERRIDE(int, get_hms_snapshot, dev_id, file_name, callback); ORCA_PY_AGENT_OVERRIDE_DEFAULT(int, get_hms_snapshot, dev_id, file_name, callback);
} }
int set_server_callback(OnServerErrFn fn) override int set_server_callback(OnServerErrFn fn) override
@@ -264,8 +270,8 @@ public:
// request_bind_ticket returns its ticket through a std::string* out-param, which pybind11 // request_bind_ticket returns its ticket through a std::string* out-param, which pybind11
// cannot marshal back through a plain override. We dispatch manually: the Python plugin // cannot marshal back through a plain override. We dispatch manually: the Python plugin
// returns a (result, ticket) tuple, which we unpack into the int result and the out-param. // returns a (result, ticket) tuple, which we unpack into the int result and the out-param.
// Not required to be implemented, so a missing override falls back to the base default // Not required to be implemented, so a missing override answers with the same failure value
// instead of failing, mirroring what PYBIND11_OVERRIDE does for the other optional methods. // as the other printer-agent operations. Leave the out-param untouched on failure.
int request_bind_ticket(std::string* ticket) override int request_bind_ticket(std::string* ticket) override
{ {
try { try {
@@ -277,7 +283,7 @@ public:
pybind11::function override = pybind11::function override =
pybind11::get_override(static_cast<const PrinterAgentPluginCapability*>(this), "request_bind_ticket"); pybind11::get_override(static_cast<const PrinterAgentPluginCapability*>(this), "request_bind_ticket");
if (!override) if (!override)
return PrinterAgentPluginCapability::request_bind_ticket(ticket); return printer_agent_failure<int>();
try { try {
pybind11::tuple result = override().cast<pybind11::tuple>(); pybind11::tuple result = override().cast<pybind11::tuple>();
if (ticket) if (ticket)
+2
View File
@@ -6,6 +6,8 @@ add_executable(${_TEST_NAME}_tests
test_dev_mapping.cpp test_dev_mapping.cpp
test_filament_bitmap_utils.cpp test_filament_bitmap_utils.cpp
test_device_progress.cpp test_device_progress.cpp
test_device_manager_integration.cpp
test_web_media_controller.cpp
test_network_versions.cpp test_network_versions.cpp
test_action_source.cpp test_action_source.cpp
test_plugin_host_api.cpp test_plugin_host_api.cpp
@@ -0,0 +1,159 @@
#include <catch2/catch_all.hpp>
#include <slic3r/GUI/DeviceCore/DevManager.h>
#include <slic3r/GUI/DeviceManager.hpp>
#include <libslic3r/AppConfig.hpp>
#include <slic3r/Utils/NetworkAgent.hpp>
#include <slic3r/Utils/OrcaCloudServiceAgent.hpp>
#include <slic3r/Utils/OrcaPrinterAgent.hpp>
#include <nlohmann/json.hpp>
#include <memory>
#include <cstdint>
#include <string>
#include <utility>
using namespace Slic3r;
using json = nlohmann::json;
namespace {
class StubCloudAgent final : public OrcaCloudServiceAgent
{
public:
StubCloudAgent() : OrcaCloudServiceAgent("") {}
int get_user_print_info(unsigned int* http_code, std::string* http_body) override
{
if (http_code)
*http_code = 200;
if (http_body)
*http_body = R"({"devices":[]})";
return 0;
}
std::string get_user_name() override { return "integration-test-user"; }
};
class TestPrinterAgent final : public OrcaPrinterAgent
{
public:
explicit TestPrinterAgent(std::string id)
: OrcaPrinterAgent(""), m_info{std::move(id), "Integration Test Agent", "1.0", "test agent"}
{
}
AgentInfo get_agent_info() override { return m_info; }
private:
AgentInfo m_info;
};
struct ScopedAppConfig
{
AppConfig config;
};
std::string machine_list_response(const std::string& provider, const std::string& agent_id,
std::uint64_t generation, const std::string& name)
{
json machine;
machine["dev_id"] = "integration-device";
machine["dev_name"] = name;
machine["dev_online"] = true;
machine["task_status"] = "idle";
json response;
response["provider"] = provider;
response["agent_id"] = agent_id;
response["generation"] = generation;
response["devices"] = json::array({machine});
return response.dump();
}
} // namespace
TEST_CASE("Network agent stamps user-machine responses with request context", "[DeviceManager][integration]")
{
auto cloud = std::make_shared<StubCloudAgent>();
NetworkAgent network(cloud, nullptr);
network.set_printer_agent(std::make_shared<TestPrinterAgent>("integration-agent-a"));
const std::uint64_t generation_before = network.get_user_machine_list_generation();
unsigned int http_code = 0;
std::string body;
REQUIRE(network.get_user_print_info(&http_code, &body, ORCA_CLOUD_PROVIDER) == 0);
const json response = json::parse(body);
CHECK(http_code == 200);
CHECK(response["provider"] == ORCA_CLOUD_PROVIDER);
CHECK(response["agent_id"] == "integration-agent-a");
CHECK(response["generation"] == generation_before + 1);
}
TEST_CASE("Device manager ignores stale cloud machine responses", "[DeviceManager][integration]")
{
ScopedAppConfig app_config;
NetworkAgent network(nullptr, std::make_shared<TestPrinterAgent>("integration-agent"));
network.set_printer_agent(std::make_shared<TestPrinterAgent>("integration-agent"));
DeviceManager manager(&network, false, &app_config.config);
const std::uint64_t current_generation = network.get_user_machine_list_generation();
manager.parse_user_print_info(machine_list_response(ORCA_CLOUD_PROVIDER, "integration-agent",
current_generation, "Fresh name"));
auto machines = manager.get_user_machinelist();
REQUIRE(machines.size() == 1);
REQUIRE(machines.at("integration-device") != nullptr);
CHECK(machines.at("integration-device")->get_dev_name() == "Fresh name");
manager.parse_user_print_info(machine_list_response(BBL_CLOUD_PROVIDER, "integration-agent",
current_generation, "Stale provider"));
manager.parse_user_print_info(machine_list_response(ORCA_CLOUD_PROVIDER, "other-agent",
current_generation, "Stale agent"));
manager.parse_user_print_info(machine_list_response(ORCA_CLOUD_PROVIDER, "integration-agent",
current_generation + 1, "Stale generation"));
machines = manager.get_user_machinelist();
REQUIRE(machines.size() == 1);
CHECK(machines.at("integration-device")->get_dev_name() == "Fresh name");
}
TEST_CASE("Device manager filters and rehomes devices by printer-agent ownership", "[DeviceManager][integration]")
{
ScopedAppConfig app_config;
auto agent_a = std::make_shared<TestPrinterAgent>("integration-agent-a");
auto agent_b = std::make_shared<TestPrinterAgent>("integration-agent-b");
NetworkAgent network(nullptr, agent_a);
DeviceManager manager(&network, false, &app_config.config);
BBLocalMachine machine;
machine.dev_id = "integration-lan-device";
machine.dev_name = "Integration LAN device";
machine.dev_ip = "192.0.2.10";
machine.printer_type = "C11";
MachineObject* object = manager.insert_local_device(machine, "lan", "free", "", "access-code");
REQUIRE(object != nullptr);
CHECK(object->printer_agent_id == "integration-agent-a");
CHECK(manager.get_my_machine_list("integration-agent-a").count(machine.dev_id) == 1);
CHECK(manager.get_my_machine_list("integration-agent-b").empty());
network.set_printer_agent(agent_b);
CHECK(manager.get_my_machine_list("integration-agent-b").empty());
manager.on_machine_alive(R"({
"dev_name":"Rediscovered device",
"dev_id":"integration-lan-device",
"dev_ip":"192.0.2.10",
"dev_type":"C11",
"dev_signal":"strong",
"connect_type":"lan",
"bind_state":"free"
})");
CHECK(object->printer_agent_id == "integration-agent-b");
CHECK(manager.get_my_machine_list("integration-agent-a").empty());
CHECK(manager.get_my_machine_list("integration-agent-b").count(machine.dev_id) == 1);
}
+1 -1
View File
@@ -115,7 +115,7 @@ TEST_CASE("Malformed string progress leaves a fresh machine unchanged", "[Device
MachineObject machine(nullptr, nullptr, "test", "test-device", "127.0.0.1"); MachineObject machine(nullptr, nullptr, "test", "test-device", "127.0.0.1");
REQUIRE(machine.subtask_ == nullptr); REQUIRE(machine.subtask_ == nullptr);
CHECK_THROWS_AS(machine.update_print_progress(json("not-a-percent")), std::invalid_argument); CHECK_NOTHROW(machine.update_print_progress(json("not-a-percent")));
CHECK(machine.mc_print_percent == 0); CHECK(machine.mc_print_percent == 0);
CHECK(machine.subtask_ == nullptr); CHECK(machine.subtask_ == nullptr);
} }
@@ -115,6 +115,22 @@ TEST_CASE("A printer agent that omits its operations answers like a missing agen
check_answers_like_no_agent(*agent); check_answers_like_no_agent(*agent);
} }
TEST_CASE("A printer agent uses IPrinterAgent defaults for omitted commands", "[PluginPrinterAgent][Python]")
{
ScopedPluginManager plugin_system;
if (!plugin_system.initialized)
SKIP("Bundled Python interpreter unavailable: " + PythonInterpreter::instance().last_error());
py::gil_scoped_acquire gil;
auto agent = make_agent(" def send_message(self, dev_id, json_str, qos, flag): return 7\n"
" def send_message_to_printer(self, dev_id, json_str, qos, flag): return 8\n");
REQUIRE(agent.agent);
CHECK(agent->command_xyz_abs("dev", 1, false) == 7);
CHECK(agent->command_set_nozzle("dev", 200, 2, true) == 8);
CHECK(agent->command_ams_refresh_rfid("dev", "tray", 3, false) == ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED);
}
TEST_CASE("A printer agent operation returning the wrong type answers like a missing agent", "[PluginPrinterAgent][Python]") TEST_CASE("A printer agent operation returning the wrong type answers like a missing agent", "[PluginPrinterAgent][Python]")
{ {
ScopedPluginManager plugin_system; ScopedPluginManager plugin_system;
@@ -0,0 +1,113 @@
#include <catch2/catch_all.hpp>
#include <slic3r/GUI/WebMediaController.hpp>
#include <wx/webview.h>
#include <string>
#include <vector>
using namespace Slic3r;
using namespace Slic3r::GUI;
namespace {
class StubWebView final : public wxWebView
{
public:
bool SetBackgroundColour(const wxColour&) override
{
events.emplace_back("background");
return true;
}
bool Create(wxWindow*, wxWindowID, const wxString&, const wxPoint&, const wxSize&, long, const wxString&) override { return true; }
wxString GetCurrentTitle() const override { return {}; }
wxString GetCurrentURL() const override { return {}; }
bool IsBusy() const override { return false; }
bool IsEditable() const override { return false; }
void LoadURL(const wxString& url) override
{
events.emplace_back("url");
loaded_url = url.ToStdString();
}
void Print() override {}
void RegisterHandler(wxSharedPtr<wxWebViewHandler>) override {}
void Reload(wxWebViewReloadFlags) override {}
void SetEditable(bool) override {}
void Stop() override { events.emplace_back("stop"); }
bool CanGoBack() const override { return false; }
bool CanGoForward() const override { return false; }
void GoBack() override {}
void GoForward() override {}
void ClearHistory() override { events.emplace_back("history"); }
void EnableHistory(bool) override {}
wxVector<wxSharedPtr<wxWebViewHistoryItem>> GetBackwardHistory() override { return {}; }
wxVector<wxSharedPtr<wxWebViewHistoryItem>> GetForwardHistory() override { return {}; }
void LoadHistoryItem(wxSharedPtr<wxWebViewHistoryItem>) override {}
bool CanSetZoomType(wxWebViewZoomType) const override { return false; }
float GetZoomFactor() const override { return 1.0f; }
wxWebViewZoomType GetZoomType() const override { return wxWEBVIEW_ZOOM_TYPE_LAYOUT; }
void SetZoomFactor(float) override {}
void SetZoomType(wxWebViewZoomType) override {}
bool CanUndo() const override { return false; }
bool CanRedo() const override { return false; }
void Undo() override {}
void Redo() override {}
void* GetNativeBackend() const override { return nullptr; }
bool RunScript(const wxString& javascript, wxString*) const override
{
events.emplace_back("script");
script = javascript.ToStdString();
return true;
}
protected:
void DoSetPage(const wxString& html, const wxString& base_url) override
{
events.emplace_back("page");
page = html.ToStdString();
page_base = base_url.ToStdString();
}
public:
mutable std::vector<std::string> events;
std::string page;
std::string page_base;
mutable std::string script;
std::string loaded_url;
};
} // namespace
TEST_CASE("Web media controller tears down a snapshot lifecycle", "[WebMediaController][integration]")
{
StubWebView view;
WebMediaController controller(&view);
controller.set_mode(CameraStreamMode::http_snapshot);
controller.Load(wxURI("http://camera.example/frame.jpg"));
controller.Play();
REQUIRE(view.events.size() == 3);
CHECK(view.events[0] == "background");
CHECK(view.events[1] == "page");
CHECK(view.events[2] == "page");
CHECK(view.page.find("stopCameraRefresh") != std::string::npos);
CHECK(view.page.find("http://camera.example/frame.jpg") != std::string::npos);
controller.Stop();
REQUIRE(view.events.size() == 7);
CHECK(view.events[3] == "script");
CHECK(view.events[4] == "stop");
CHECK(view.events[5] == "page");
CHECK(view.events[6] == "history");
CHECK(view.script == "if(typeof stopCameraRefresh==='function') stopCameraRefresh();");
CHECK(view.page.empty());
CHECK(view.page_base == "about:blank");
controller.Play();
CHECK(view.page.find("http://camera.example/frame.jpg") == std::string::npos);
}