Merge branch 'dk_connect'

This commit is contained in:
Lukas Matena 2024-05-21 08:27:18 +02:00
commit 906146a788
18 changed files with 748 additions and 285 deletions

View File

@ -3681,6 +3681,7 @@ bool GUI_App::select_printer_from_connect(const std::string& msg)
{
// parse message
std::string model_name = plater()->get_user_account()->get_keyword_from_json(msg, "printer_model");
std::string uuid = plater()->get_user_account()->get_keyword_from_json(msg, "uuid");
if (model_name.empty()) {
std::vector<std::string> compatible_printers;
plater()->get_user_account()->fill_supported_printer_models_from_json(msg, compatible_printers);
@ -3707,6 +3708,7 @@ bool GUI_App::select_printer_from_connect(const std::string& msg)
NotificationType::SelectPrinterFromConnect
, printer_preset ? NotificationManager::NotificationLevel::ImportantNotificationLevel : NotificationManager::NotificationLevel::WarningNotificationLevel
, out);
plater()->get_user_account()->set_current_printer_uuid_from_connect(uuid);
return printer_preset;
}
@ -3725,11 +3727,29 @@ bool GUI_App::select_filament_preset(const Preset* preset, size_t extruder_index
}
void GUI_App::search_and_select_filaments(const std::string& material, size_t extruder_index, std::string& out_message)
{
const DynamicPrintConfig& config = preset_bundle->extruders_filaments[extruder_index].get_selected_preset()->config;
const Preset* preset = preset_bundle->extruders_filaments[extruder_index].get_selected_preset();
// selected is ok
if (config.has("filament_type") && config.option("filament_type")->serialize() == material) {
if (!preset->is_default && preset->config.has("filament_type") && preset->config.option("filament_type")->serialize() == material) {
return;
}
// find installed compatible filament that is Prusa with suitable type and select it
for (const auto& filament : preset_bundle->extruders_filaments[extruder_index]) {
if (filament.is_compatible
&& !filament.preset->is_default
&& filament.preset->is_visible
&& (!filament.preset->vendor || !filament.preset->vendor->templates_profile)
&& filament.preset->config.has("filament_type")
&& filament.preset->config.option("filament_type")->serialize() == material
&& filament.preset->name.compare(0, 9, "Prusament") == 0
&& select_filament_preset(filament.preset, extruder_index)
)
{
out_message += /*(extruder_count == 1)
? GUI::format(_L("Selected Filament:\n%1%"), filament_preset.preset->name)
: */GUI::format(_L("Extruder %1%: Selected Filament %2%\n"), extruder_index + 1, filament.preset->name);
return;
}
}
// find first installed compatible filament with suitable type and select it
for (const auto& filament : preset_bundle->extruders_filaments[extruder_index]) {
if (filament.is_compatible
@ -3798,7 +3818,15 @@ void GUI_App::select_filament_from_connect(const std::string& msg)
}
}
void GUI_App::handle_connect_request_printer_pick(const std::string& msg)
void GUI_App::handle_connect_request_printer_select(const std::string& msg)
{
// Here comes code from ConnectWebViewPanel
// It only contains uuid of a printer to be selected
// Lets queue it and wait on result. The result is send via event to plater, where it is send to handle_connect_request_printer_select_inner
std::string uuid = plater()->get_user_account()->get_keyword_from_json(msg, "uuid");
plater()->get_user_account()->enqueue_printer_data_action(uuid);
}
void GUI_App::handle_connect_request_printer_select_inner(const std::string & msg)
{
BOOST_LOG_TRIVIAL(debug) << "Handling web request: " << msg;
// return to plater

View File

@ -413,7 +413,8 @@ public:
int request_user_unbind(std::string dev_id) { return 0; }
bool select_printer_from_connect(const std::string& cmd);
void select_filament_from_connect(const std::string& cmd);
void handle_connect_request_printer_pick(const std::string& cmd);
void handle_connect_request_printer_select(const std::string& cmd);
void handle_connect_request_printer_select_inner(const std::string& cmd);
void show_printer_webview_tab();
// return true if preset vas invisible and we have to installed it to make it selectable
bool select_printer_preset(const Preset* printer_preset);

View File

@ -272,8 +272,8 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
//FIXME it seems this method is not called on application start-up, at least not on Windows. Why?
// The same applies to wxEVT_CREATE, it is not being called on startup on Windows.
Bind(wxEVT_ACTIVATE, [this](wxActivateEvent& event) {
if (m_plater != nullptr && event.GetActive())
m_plater->on_activate();
if (m_plater != nullptr)
m_plater->on_activate(event.GetActive());
event.Skip();
});
@ -404,17 +404,11 @@ void MainFrame::update_layout()
{
m_plater->Reparent(m_tabpanel);
m_plater->Layout();
// m_tabpanel->InsertNewPage(0, m_plater, _L("Plater"), std::string("plater"), true);
m_main_sizer->Add(m_tabpanel, 1, wxEXPAND | wxTOP, 1);
m_plater->Show();
m_tabpanel->ShowFull();
m_tmp_top_bar->Hide();
// update Tabs
if (old_layout == ESettingsLayout::Dlg)
if (int sel = m_tabpanel->GetSelection(); sel != wxNOT_FOUND)
m_tabpanel->SetSelection(sel+1);// call SetSelection to correct layout after switching from Dlg to Old mode
break;
}
case ESettingsLayout::Dlg:
@ -749,6 +743,7 @@ void MainFrame::create_preset_tabs()
void MainFrame::add_connect_webview_tab()
{
if (m_connect_webview_added) {
m_connect_webview->resend_config();
return;
}
// parameters of InsertNewPage (to prevent ambigous overloaded function)

View File

@ -963,7 +963,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
this->q->Bind(EVT_UA_AVATAR_SUCCESS, [this](UserAccountSuccessEvent& evt) {
boost::filesystem::path path = user_account->get_avatar_path(true);
FILE* file;
file = fopen(path.string().c_str(), "wb");
file = boost::nowide::fopen(path.generic_string().c_str(), "wb");
if (file == NULL) {
BOOST_LOG_TRIVIAL(error) << "Failed to create file to store avatar picture at: " << path;
return;
}
fwrite(evt.data.c_str(), 1, evt.data.size(), file);
fclose(file);
this->main_frame->refresh_account_menu(true);
@ -971,6 +975,17 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
wxGetApp().update_login_dialog();
#endif // 0
});
this->q->Bind(EVT_UA_PRUSACONNECT_PRINTER_DATA_SUCCESS, [this](UserAccountSuccessEvent& evt) {
this->user_account->set_current_printer_data(evt.data);
wxGetApp().handle_connect_request_printer_select_inner(evt.data);
});
this->q->Bind(EVT_UA_PRUSACONNECT_PRINTER_DATA_FAIL, [this](UserAccountFailEvent& evt) {
BOOST_LOG_TRIVIAL(error) << "Failed communication with Prusa Account: " << evt.data;
user_account->on_communication_fail();
std::string msg = _u8L("Failed to select printer from PrusaConnect.");
this->notification_manager->close_notification_of_type(NotificationType::SelectFilamentFromConnect);
this->notification_manager->push_notification(NotificationType::SelectFilamentFromConnect, NotificationManager::NotificationLevel::WarningNotificationLevel, msg);
});
}
wxGetApp().other_instance_message_handler()->init(this->q);
@ -5873,8 +5888,11 @@ void Plater::connect_gcode()
{
assert(p->user_account->is_logged());
std::string dialog_msg;
if(PrinterPickWebViewDialog(this, dialog_msg).ShowModal() != wxID_OK) {
return;
{
PrinterPickWebViewDialog dialog(this, dialog_msg);
if (dialog.ShowModal() != wxID_OK) {
return;
}
}
if (dialog_msg.empty()) {
show_error(this, _L("Failed to select a printer. PrusaConnect did not return a value."));
@ -5882,151 +5900,41 @@ void Plater::connect_gcode()
}
BOOST_LOG_TRIVIAL(debug) << "Message from Printer pick webview: " << dialog_msg;
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
// Connect data
std::vector<std::string> compatible_printers;
p->user_account->fill_supported_printer_models_from_json(dialog_msg, compatible_printers);
std::string connect_nozzle = p->user_account->get_nozzle_from_json(dialog_msg);
/*
{
set_ready: boolean, // uzivatel potvrdil ze je tiskarne ready a muze se tisknout, pouziva se pro tisknout ted a odlozeny tisk
position: -1 | 0, // -1 = posledni ve fronte, 0 = prvni ve fronte
wait_until: number | undefined, // timestamp pro odlozeny tisk
file_name: string, // tady budeme predavat jak se uzivatel rozhodl soubor pojmenovat, kdyz ho neprejmenuje, tak vratime to stejne co nam predtim posle slicer
printer_uuid: string // uuid vybrane tiskarny
}
*/
const Preset* selected_printer_preset = &wxGetApp().preset_bundle->printers.get_selected_preset();
std::vector<std::string> connect_materials;
p->user_account->fill_material_from_json(dialog_msg, connect_materials);
std::vector<const Preset*> compatible_printer_presets;
for (const std::string& cp : compatible_printers) {
const Preset* found_preset = preset_bundle->printers.find_system_preset_by_model_and_variant(cp, connect_nozzle);
if (found_preset) {
compatible_printer_presets.emplace_back(found_preset);
}
}
if (compatible_printer_presets.empty()) {
show_error(this, _L("No compatible printer presets found."));
return;
}
// Selected profiles
const Preset* selected_printer_preset = &preset_bundle->printers.get_selected_preset();
const std::string selected_printer_model_serialized = selected_printer_preset->config.option("printer_model")->serialize();
bool selected_filament_ok = true;
if (Preset::printer_technology(selected_printer_preset->config) == ptFFF) {
size_t extruder_count = preset_bundle->extruders_filaments.size();
for (size_t i = 0; i < extruder_count; i++) {
if (connect_materials.size() <= i) {
selected_filament_ok = false;
break;
}
const Preset* selected_filament_preset = preset_bundle->extruders_filaments[i].get_selected_preset();
if (selected_filament_preset && selected_filament_preset->config.has("filament_type")
&& selected_filament_preset->config.option("filament_type")->serialize() != connect_materials[i])
{
selected_filament_ok = false;
break;
}
}
}
bool is_first = compatible_printer_presets.front()->name == selected_printer_preset->name;
bool found = false;
for (const Preset* connect_preset : compatible_printer_presets) {
if (!connect_preset) {
continue;
}
if (selected_printer_preset->name == connect_preset->name) {
found = true;
break;
}
}
// Dialog to select action
if (!found) {
wxString line1 = _L("The printer you've selected for upload is not compatible with profiles selected for slicing.");
wxString line2 = GUI::format_wxstr(_L("PrusaSlicer Profile:\n%1%"), selected_printer_preset->name);
wxString line3 = _L("Known profiles compatible with printer selected for upload:");
wxString printers_line;
for (const Preset* connect_preset : compatible_printer_presets) {
if (!connect_preset) {
continue;
}
printers_line += GUI::format_wxstr(_L("\n%1%"), connect_preset->name);
}
wxString line4 = _L("Do you still wish to upload?");
wxString message = GUI::format_wxstr("%1%\n\n%2%\n\n%3%%4%\n\n%5%", line1, line2, line3, printers_line,line4);
MessageDialog msg_dialog(this, message, _L("Do you wish to upload?"), wxYES_NO);
auto modal_res = msg_dialog.ShowModal();
if (modal_res != wxID_YES) {
return;
}
} else if (!is_first) {
wxString line1 = _L("The printer you've selected for upload might not be compatible with profiles selected for slicing.");
wxString line2 = GUI::format_wxstr(_L("PrusaSlicer Profile:\n%1%"), selected_printer_preset->name);
wxString line3 = _L("Known profiles compatible with printer selected for upload:");
wxString printers_line;
for (const Preset* connect_preset : compatible_printer_presets) {
if (!connect_preset) {
continue;
}
printers_line += GUI::format_wxstr(_L("\n%1%"), connect_preset->name);
}
wxString line4 = _L("Do you still wish to upload?");
wxString message = GUI::format_wxstr("%1%\n\n%2%\n\n%3%%4%\n\n%5%", line1, line2, line3, printers_line, line4);
MessageDialog msg_dialog(this, message, _L("Do you wish to upload?"), wxYES_NO);
auto modal_res = msg_dialog.ShowModal();
if (modal_res != wxID_YES) {
return;
}
}
if (!connect_materials.empty() && !selected_filament_ok) {
wxString line1 = _L("The printer you've selected has different filament type than filament profile selected for slicing.");
wxString connect_filament_types = "\n";
for (size_t i = 0; i < connect_materials.size(); i++) {
connect_filament_types += GUI::format_wxstr(_L("Extruder %1%: %2%\n"), i + 1, connect_materials[i]);
}
wxString line2 = GUI::format_wxstr(_L("PrusaConnect Filament Type: %1%"), connect_filament_types);
wxString selected_filament_types = "\n";
for (size_t i = 0; i < preset_bundle->extruders_filaments.size(); i++) {
const Preset* selected_filament_preset = preset_bundle->extruders_filaments[i].get_selected_preset();
std::string filament_serialized;
if (selected_filament_preset && selected_filament_preset->config.has("filament_type")) {
filament_serialized = selected_filament_preset->config.option("filament_type")->serialize();
}
selected_filament_types += GUI::format_wxstr(_L("Extruder %1%: %2%\n"), i + 1, filament_serialized);
}
wxString line3 = GUI::format_wxstr(_L("PrusaSlicer Filament Type: %1%"), selected_filament_types);
wxString line4 = _L("Do you still wish to upload?");
wxString message = GUI::format_wxstr("%1%\n\n%2%\n%3%\n\n%4%", line1, line2, line3, line4);
MessageDialog msg_dialog(this, message, _L("Do you wish to upload?"), wxYES_NO);
auto modal_res = msg_dialog.ShowModal();
if (modal_res != wxID_YES) {
return;
}
}
const std::string connect_state = p->user_account->get_keyword_from_json(dialog_msg, "connect_state");
const std::string printer_state = p->user_account->get_keyword_from_json(dialog_msg, "printer_state");
const std::map<std::string, ConnectPrinterState>& printer_state_table = p->user_account->get_printer_state_table();
const auto state = printer_state_table.find(connect_state);
assert(state != printer_state_table.end());
// TODO: all states that does not allow to upload
if (state->second == ConnectPrinterState::CONNECT_PRINTER_OFFLINE) {
show_error(this, _L("Failed to select a printer. Chosen printer is in offline state."));
return;
}
const std::string uuid = p->user_account->get_keyword_from_json(dialog_msg, "uuid");
const std::string set_ready = p->user_account->get_keyword_from_json(dialog_msg, "set_ready");
const std::string position = p->user_account->get_keyword_from_json(dialog_msg, "position");
const std::string wait_until = p->user_account->get_keyword_from_json(dialog_msg, "wait_until");
const std::string filename = p->user_account->get_keyword_from_json(dialog_msg, "filename");
const std::string printer_uuid = p->user_account->get_keyword_from_json(dialog_msg, "printer_uuid");
const std::string team_id = p->user_account->get_keyword_from_json(dialog_msg, "team_id");
if (uuid.empty() || team_id.empty()) {
show_error(this, _L("Failed to select a printer. Missing data (uuid and team id) for chosen printer."));
return;
}
PhysicalPrinter ph_printer("connect_temp_printer", wxGetApp().preset_bundle->physical_printers.default_config(), *selected_printer_preset);
ph_printer.config.set_key_value("host_type", new ConfigOptionEnum<PrintHostType>(htPrusaConnectNew));
// use existing structures to pass data
ph_printer.config.opt_string("printhost_apikey") = team_id;
ph_printer.config.opt_string("print_host") = uuid;
ph_printer.config.opt_string("print_host") = printer_uuid;
DynamicPrintConfig* physical_printer_config = &ph_printer.config;
send_gcode_inner(physical_printer_config);
PrintHostJob upload_job(physical_printer_config);
assert(!upload_job.empty());
upload_job.upload_data.set_ready = set_ready;
upload_job.upload_data.position = position;
upload_job.upload_data.wait_until = wait_until;
upload_job.upload_data.upload_path = boost::filesystem::path(filename);
p->export_gcode(fs::path(), false, std::move(upload_job));
}
void Plater::send_gcode()
@ -6066,6 +5974,32 @@ void Plater::send_gcode()
send_gcode_inner(physical_printer_config);
}
std::string Plater::get_upload_filename()
{
// Obtain default output path
fs::path default_output_file;
try {
// Update the background processing, so that the placeholder parser will get the correct values for the ouput file template.
// Also if there is something wrong with the current configuration, a pop-up dialog will be shown and the export will not be performed.
unsigned int state = this->p->update_restart_background_process(false, false);
if (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID)
return {};
default_output_file = this->p->background_process.output_filepath_for_project(into_path(get_project_filename(".3mf")));
}
catch (const Slic3r::PlaceholderParserError& ex) {
// Show the error with monospaced font.
show_error(this, ex.what(), true);
return {};
}
catch (const std::exception& ex) {
show_error(this, ex.what(), false);
return {};
}
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
return default_output_file.filename().string();
}
void Plater::send_gcode_inner(DynamicPrintConfig* physical_printer_config)
{
PrintHostJob upload_job(physical_printer_config);
@ -6366,9 +6300,12 @@ void Plater::force_print_bed_update()
p->config->opt_string("printer_model", true) = "\x01\x00\x01";
}
void Plater::on_activate()
void Plater::on_activate(bool active)
{
this->p->show_delayed_error_message();
this->p->user_account->on_activate_window(active);
if (active) {
this->p->show_delayed_error_message();
}
}
// Get vector of extruder colors considering filament color, if extruder color is undefined.

View File

@ -227,6 +227,7 @@ public:
void send_gcode_inner(DynamicPrintConfig* physical_printer_config);
void eject_drive();
void connect_gcode();
std::string get_upload_filename();
void take_snapshot(const std::string &snapshot_name);
void take_snapshot(const wxString &snapshot_name);
@ -252,7 +253,7 @@ public:
void force_filament_cb_update();
void force_print_bed_update();
// On activating the parent window.
void on_activate();
void on_activate(bool active);
std::vector<std::string> get_extruder_colors_from_plater_config(const GCodeProcessorResult* const result = nullptr) const;
std::vector<std::string> get_colors_for_color_print(const GCodeProcessorResult* const result = nullptr) const;

View File

@ -932,19 +932,11 @@ static std::string get_connect_state_suffix_for_printer(const Preset& printer_pr
// process real data from Connect
if (auto printer_state_map = wxGetApp().plater()->get_user_account()->get_printer_state_map();
!printer_state_map.empty()) {
// printer_state_map has only one nozzle value. Take a first value from "nozzle_diameter" opt.
std::string nozzle_diameter_serialized;
if (printer_preset.config.has("nozzle_diameter")) {
nozzle_diameter_serialized = dynamic_cast<const ConfigOptionFloats*>(printer_preset.config.option("nozzle_diameter"))->serialize();
if (size_t comma = nozzle_diameter_serialized.find(','); comma != std::string::npos)
nozzle_diameter_serialized = nozzle_diameter_serialized.substr(0, comma);
}
for (const auto& [printer_model_nozzle_pair, states] : printer_state_map) {
if (printer_model_nozzle_pair.first == printer_preset.config.opt_string("printer_model")
&& (printer_model_nozzle_pair.second.empty()
|| printer_model_nozzle_pair.second == nozzle_diameter_serialized)
) {
&& printer_model_nozzle_pair.second == printer_preset.config.opt_string("printer_variant"))
{
PrinterStatesCount states_cnt = get_printe_states_count(states);
if (states_cnt.available_cnt > 0)
@ -963,26 +955,17 @@ static wxString get_connect_info_line(const Preset& printer_preset)
{
if (auto printer_state_map = wxGetApp().plater()->get_user_account()->get_printer_state_map();
!printer_state_map.empty()) {
// printer_state_map has only one nozzle value. Take a first value from "nozzle_diameter" opt.
std::string nozzle_diameter_serialized;
if (printer_preset.config.has("nozzle_diameter")) {
nozzle_diameter_serialized = dynamic_cast<const ConfigOptionFloats*>(printer_preset.config.option("nozzle_diameter"))->serialize();
if (size_t comma = nozzle_diameter_serialized.find(','); comma != std::string::npos)
nozzle_diameter_serialized = nozzle_diameter_serialized.substr(0, comma);
}
for (const auto& [printer_model_nozzle_pair, states] : printer_state_map) {
if (printer_model_nozzle_pair.first == printer_preset.config.opt_string("printer_model")
&& (printer_model_nozzle_pair.second.empty()
|| printer_model_nozzle_pair.second == nozzle_diameter_serialized)
) {
&& printer_model_nozzle_pair.second == printer_preset.config.opt_string("printer_variant"))
{
PrinterStatesCount states_cnt = get_printe_states_count(states);
return format_wxstr(_L("Available: %1%, Offline: %2%, Busy: %3%. Total: %4% printers"),
return format_wxstr(_L("Available: %1%, Offline: %2%, Busy: %3%"),
format("<b><span color=\"green\">%1%</span></b>" , states_cnt.available_cnt),
format("<b><span color=\"red\">%1%</span></b>" , states_cnt.offline_cnt),
format("<b><span color=\"yellow\">%1%</span></b>", states_cnt.busy_cnt),
format("<b>%1%</b>", states_cnt.total));
format("<b><span color=\"yellow\">%1%</span></b>", states_cnt.busy_cnt));
}
}
}

View File

@ -1,6 +1,7 @@
#include "UserAccount.hpp"
#include "format.hpp"
#include "GUI.hpp"
#include "libslic3r/Utils.hpp"
@ -78,7 +79,7 @@ boost::filesystem::path UserAccount::get_avatar_path(bool logged) const
{
if (logged) {
const std::string filename = "prusaslicer-avatar-" + m_instance_hash + m_avatar_extension;
return boost::filesystem::path(wxStandardPaths::Get().GetTempDir().utf8_str().data()) / filename;
return into_path(wxStandardPaths::Get().GetTempDir()) / filename;
} else {
return boost::filesystem::path(resources_dir()) / "icons" / "user.svg";
}
@ -96,6 +97,10 @@ void UserAccount::enqueue_avatar_action()
{
m_communication->enqueue_avatar_action(m_account_user_data["avatar"]);
}
void UserAccount::enqueue_printer_data_action(const std::string& uuid)
{
m_communication->enqueue_printer_data_action(uuid);
}
bool UserAccount::on_login_code_recieved(const std::string& url_message)
{
@ -193,6 +198,24 @@ namespace {
}
return pt::ptree();
}
void fill_supported_printer_models_from_json_inner(const pt::ptree& ptree, std::vector<std::string>& result) {
std::string printer_model = parse_tree_for_param(ptree, "printer_model");
if (!printer_model.empty()) {
result.emplace_back(printer_model);
}
pt::ptree out = parse_tree_for_subtree(ptree, "supported_printer_models");
if (out.empty()) {
BOOST_LOG_TRIVIAL(error) << "Failed to find supported_printer_models in printer detail.";
return;
}
for (const auto& sub : out) {
if (printer_model != sub.second.data()) {
result.emplace_back(sub.second.data());
}
}
}
}
bool UserAccount::on_connect_printers_success(const std::string& data, AppConfig* app_config, bool& out_printers_changed)
@ -303,6 +326,36 @@ bool UserAccount::on_connect_uiid_map_success(const std::string& data, AppConfig
return on_connect_printers_success(data, app_config, out_printers_changed);
}
std::string UserAccount::get_current_printer_uuid_from_connect(const std::string& selected_printer_id) const
{
if (m_current_printer_data_json_from_connect.empty() || m_current_printer_uuid_from_connect.empty()) {
return {};
}
pt::ptree ptree;
try {
std::stringstream ss(m_current_printer_data_json_from_connect);
pt::read_json(ss, ptree);
}
catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "Could not parse Printer data from Connect. " << e.what();
return {};
}
std::string data_uuid = parse_tree_for_param(ptree, "uuid");
assert(data_uuid == m_current_printer_uuid_from_connect);
//std::string model_name = parse_tree_for_param(ptree, "printer_model");
std::vector<std::string> compatible_printers;
fill_supported_printer_models_from_json_inner(ptree, compatible_printers);
if (compatible_printers.empty()) {
return {};
}
return std::find(compatible_printers.begin(), compatible_printers.end(), selected_printer_id) == compatible_printers.end() ? "" : m_current_printer_uuid_from_connect;
}
std::string UserAccount::get_nozzle_from_json(const std::string& message) const
{
std::string out;
@ -353,21 +406,7 @@ void UserAccount::fill_supported_printer_models_from_json(const std::string& jso
pt::ptree ptree;
pt::read_json(ss, ptree);
std::string printer_model = parse_tree_for_param(ptree, "printer_model");
if (!printer_model.empty()) {
result.emplace_back(printer_model);
}
pt::ptree out = parse_tree_for_subtree(ptree, "supported_printer_models");
if (out.empty()) {
BOOST_LOG_TRIVIAL(error) << "Failed to find supported_printer_models in printer detail.";
return;
}
for (const auto& sub : out) {
if (printer_model != sub.second.data()) {
result.emplace_back(sub.second.data());
}
}
fill_supported_printer_models_from_json_inner(ptree, result);
}
catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "Could not parse prusaconnect message. " << e.what();

View File

@ -45,7 +45,7 @@ public:
void enqueue_connect_status_action();
void enqueue_connect_printer_models_action();
void enqueue_avatar_action();
void enqueue_printer_data_action(const std::string& uuid);
// Clears all data and connections, called on logout or EVT_UA_RESET
void clear();
@ -58,6 +58,8 @@ public:
bool on_connect_printers_success(const std::string& data, AppConfig* app_config, bool& out_printers_changed);
bool on_connect_uiid_map_success(const std::string& data, AppConfig* app_config, bool& out_printers_changed);
void on_activate_window(bool active) { m_communication->on_activate_window(active); }
std::string get_username() const { return m_username; }
std::string get_access_token();
std::string get_shared_session_key();
@ -72,6 +74,10 @@ public:
const std::map<std::string, ConnectPrinterState>& get_printer_state_table() const { return printer_state_table; }
void set_current_printer_uuid_from_connect(const std::string& uuid) { m_current_printer_uuid_from_connect = uuid; }
std::string get_current_printer_uuid_from_connect(const std::string& selected_printer_id) const;
void set_current_printer_data(const std::string& data) { m_current_printer_data_json_from_connect = data; }
private:
void set_username(const std::string& username);
@ -86,6 +92,9 @@ private:
size_t m_fail_counter { 0 };
std::string m_avatar_extension;
std::string m_current_printer_uuid_from_connect;
std::string m_current_printer_data_json_from_connect;
const std::map<std::string, ConnectPrinterState> printer_state_table = {
{"OFFLINE" , ConnectPrinterState::CONNECT_PRINTER_OFFLINE},
{"PRINTING" , ConnectPrinterState::CONNECT_PRINTER_PRINTING},

View File

@ -333,6 +333,21 @@ void UserAccountCommunication::enqueue_avatar_action(const std::string& url)
}
wakeup_session_thread();
}
void UserAccountCommunication::enqueue_printer_data_action(const std::string& uuid)
{
{
std::lock_guard<std::mutex> lock(m_session_mutex);
if (!m_session->is_initialized()) {
BOOST_LOG_TRIVIAL(error) << "Connect Printers endpoint connection failed - Not Logged in.";
return;
}
m_session->enqueue_action(UserAccountActionID::USER_ACCOUNT_ACTION_CONNECT_DATA_FROM_UUID, nullptr, nullptr, uuid);
}
wakeup_session_thread();
}
void UserAccountCommunication::init_session_thread()
{
m_thread = std::thread([this]() {
@ -340,11 +355,15 @@ void UserAccountCommunication::init_session_thread()
// Wait for 5 seconds or wakeup call
{
std::unique_lock<std::mutex> lck(m_thread_stop_mutex);
m_thread_stop_condition.wait_for(lck, std::chrono::seconds(5), [this] { return m_thread_stop || m_thread_wakeup; });
m_thread_stop_condition.wait_for(lck, std::chrono::seconds(10), [this] { return m_thread_stop || m_thread_wakeup; });
}
if (m_thread_stop)
// Stop the worker thread.
break;
// Do not process_action_queue if window is not active and thread was not forced to wakeup
if (!m_window_is_active && !m_thread_wakeup) {
continue;
}
m_thread_wakeup = false;
{
std::lock_guard<std::mutex> lock(m_session_mutex);
@ -354,6 +373,12 @@ void UserAccountCommunication::init_session_thread()
});
}
void UserAccountCommunication::on_activate_window(bool active)
{
std::lock_guard<std::mutex> lck(m_thread_stop_mutex);
m_window_is_active = active;
}
void UserAccountCommunication::wakeup_session_thread()
{
{

View File

@ -43,6 +43,7 @@ public:
void enqueue_connect_printer_models_action();
void enqueue_avatar_action(const std::string& url);
void enqueue_test_connection();
void enqueue_printer_data_action(const std::string& uuid);
// Callbacks - called from UI after receiving Event from Session thread. Some might use Session thread.
//
@ -50,6 +51,7 @@ public:
// Exchanges code for tokens and shared_session_key
void on_login_code_recieved(const std::string& url_message);
void on_activate_window(bool active);
void set_username(const std::string& username);
void set_remember_session(bool b);
@ -70,6 +72,7 @@ private:
std::condition_variable m_thread_stop_condition;
bool m_thread_stop { false };
bool m_thread_wakeup{ false };
bool m_window_is_active{ true };
std::string m_code_verifier;
wxEvtHandler* m_evt_handler;
AppConfig* m_app_config;

View File

@ -25,8 +25,10 @@ wxDEFINE_EVENT(EVT_UA_SUCCESS, UserAccountSuccessEvent);
wxDEFINE_EVENT(EVT_UA_PRUSACONNECT_STATUS_SUCCESS, UserAccountSuccessEvent);
wxDEFINE_EVENT(EVT_UA_PRUSACONNECT_PRINTER_MODELS_SUCCESS, UserAccountSuccessEvent);
wxDEFINE_EVENT(EVT_UA_AVATAR_SUCCESS, UserAccountSuccessEvent);
wxDEFINE_EVENT(EVT_UA_PRUSACONNECT_PRINTER_DATA_SUCCESS, UserAccountSuccessEvent);
wxDEFINE_EVENT(EVT_UA_FAIL, UserAccountFailEvent);
wxDEFINE_EVENT(EVT_UA_RESET, UserAccountFailEvent);
wxDEFINE_EVENT(EVT_UA_PRUSACONNECT_PRINTER_DATA_FAIL, UserAccountFailEvent);
void UserActionPost::perform(/*UNUSED*/ wxEvtHandler* evt_handler, /*UNUSED*/ const std::string& access_token, UserActionSuccessFn success_callback, UserActionFailFn fail_callback, const std::string& input) const
{

View File

@ -23,8 +23,10 @@ wxDECLARE_EVENT(EVT_UA_SUCCESS, UserAccountSuccessEvent);
wxDECLARE_EVENT(EVT_UA_PRUSACONNECT_STATUS_SUCCESS, UserAccountSuccessEvent);
wxDECLARE_EVENT(EVT_UA_PRUSACONNECT_PRINTER_MODELS_SUCCESS, UserAccountSuccessEvent);
wxDECLARE_EVENT(EVT_UA_AVATAR_SUCCESS, UserAccountSuccessEvent);
wxDECLARE_EVENT(EVT_UA_PRUSACONNECT_PRINTER_DATA_SUCCESS, UserAccountSuccessEvent);
wxDECLARE_EVENT(EVT_UA_FAIL, UserAccountFailEvent); // Soft fail - clears only after some number of fails
wxDECLARE_EVENT(EVT_UA_RESET, UserAccountFailEvent); // Hard fail - clears all
wxDECLARE_EVENT(EVT_UA_PRUSACONNECT_PRINTER_DATA_FAIL, UserAccountFailEvent); // Failed to get data for printer to select, soft fail, action does not repeat
typedef std::function<void(const std::string& body)> UserActionSuccessFn;
@ -41,6 +43,7 @@ enum class UserAccountActionID {
USER_ACCOUNT_ACTION_CONNECT_STATUS, // status of all printers by UUID
USER_ACCOUNT_ACTION_CONNECT_PRINTER_MODELS, // status of all printers by UUID with printer_model. Should be called once to save printer models.
USER_ACCOUNT_ACTION_AVATAR,
USER_ACCOUNT_ACTION_CONNECT_DATA_FROM_UUID,
};
class UserAction
{
@ -115,6 +118,7 @@ public:
m_actions[UserAccountActionID::USER_ACCOUNT_ACTION_CONNECT_STATUS] = std::make_unique<UserActionGetWithEvent>("CONNECT_STATUS", "https://connect.prusa3d.com/slicer/status", EVT_UA_PRUSACONNECT_STATUS_SUCCESS, EVT_UA_FAIL);
m_actions[UserAccountActionID::USER_ACCOUNT_ACTION_CONNECT_PRINTER_MODELS] = std::make_unique<UserActionGetWithEvent>("CONNECT_PRINTER_MODELS", "https://connect.prusa3d.com/slicer/printer_list", EVT_UA_PRUSACONNECT_PRINTER_MODELS_SUCCESS, EVT_UA_FAIL);
m_actions[UserAccountActionID::USER_ACCOUNT_ACTION_AVATAR] = std::make_unique<UserActionGetWithEvent>("AVATAR", "https://media.printables.com/media/", EVT_UA_AVATAR_SUCCESS, EVT_UA_FAIL);
m_actions[UserAccountActionID::USER_ACCOUNT_ACTION_CONNECT_DATA_FROM_UUID] = std::make_unique<UserActionGetWithEvent>("USER_ACCOUNT_ACTION_CONNECT_DATA_FROM_UUID", "https://connect.prusa3d.com/app/printers/", EVT_UA_PRUSACONNECT_PRINTER_DATA_SUCCESS, EVT_UA_FAIL);
}
~UserAccountSession()
{
@ -126,6 +130,7 @@ public:
m_actions[UserAccountActionID::USER_ACCOUNT_ACTION_TEST_CONNECTION].reset(nullptr);
m_actions[UserAccountActionID::USER_ACCOUNT_ACTION_CONNECT_STATUS].reset(nullptr);
m_actions[UserAccountActionID::USER_ACCOUNT_ACTION_AVATAR].reset(nullptr);
m_actions[UserAccountActionID::USER_ACCOUNT_ACTION_CONNECT_DATA_FROM_UUID].reset(nullptr);
}
void clear() {
m_access_token.clear();

View File

@ -1,12 +1,13 @@
#include "WebView.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/GUI.hpp"
#include <wx/uri.h>
#include <wx/webview.h>
#include <boost/log/trivial.hpp>
wxWebView* WebView::CreateWebView(wxWindow * parent, const wxString& url)
wxWebView* WebView::CreateWebView(wxWindow * parent, const wxString& url, std::vector<std::string>& message_handlers)
{
#if wxUSE_WEBVIEW_EDGE
bool backend_available = wxWebView::IsBackendAvailable(wxWebViewBackendEdge);
@ -21,8 +22,8 @@ wxWebView* WebView::CreateWebView(wxWindow * parent, const wxString& url)
if (webView) {
wxString correct_url = url.empty() ? wxString("") : wxURI(url).BuildURI();
#ifdef __WIN32__
webView->SetUserAgent(wxString::Format("PrusaSlicer/v%s", SLIC3R_VERSION));
#ifdef __WIN32_
webView->SetUserAgent(SLIC3R_APP_FULL_NAME);
webView->Create(parent, wxID_ANY, correct_url, wxDefaultPosition, wxDefaultSize);
//We register the wxfs:// protocol for testing purposes
//webView->RegisterHandler(wxSharedPtr<wxWebViewHandler>(new wxWebViewArchiveHandler("wxfs")));
@ -34,15 +35,17 @@ wxWebView* WebView::CreateWebView(wxWindow * parent, const wxString& url)
// And the memory: file system
//webView->RegisterHandler(wxSharedPtr<wxWebViewHandler>(new wxWebViewFSHandler("memory")));
webView->Create(parent, wxID_ANY, correct_url, wxDefaultPosition, wxDefaultSize);
webView->SetUserAgent(wxString::Format("PrusaSlicer/v%s", SLIC3R_VERSION));
webView->SetUserAgent(wxString::FromUTF8(SLIC3R_APP_FULL_NAME));
#endif
#ifndef __WIN32__
Slic3r::GUI::wxGetApp().CallAfter([webView] {
Slic3r::GUI::wxGetApp().CallAfter([message_handlers, webView] {
#endif
if (!webView->AddScriptMessageHandler("_prusaSlicer")) {
// TODO: dialog to user !!!
//wxLogError("Could not add script message handler");
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Could not add script message handler";
for (const std::string& handler : message_handlers) {
if (!webView->AddScriptMessageHandler(Slic3r::GUI::into_u8(handler))) {
// TODO: dialog to user !!!
//wxLogError("Could not add script message handler");
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Could not add script message handler " << handler;
}
}
#ifndef __WIN32__
});

View File

@ -7,7 +7,7 @@ class wxString;
namespace WebView
{
wxWebView *CreateWebView(wxWindow *parent, const wxString& url);
wxWebView *CreateWebView(wxWindow *parent, const wxString& url, std::vector<std::string>& message_handlers);
};
#endif // !slic3r_GUI_WebView_hpp_

View File

@ -22,10 +22,11 @@ namespace Slic3r {
namespace GUI {
WebViewPanel::WebViewPanel(wxWindow *parent, const wxString& default_url, const std::string& loading_html/* = "loading"*/)
WebViewPanel::WebViewPanel(wxWindow *parent, const wxString& default_url, const std::vector<std::string>& message_handler_names, const std::string& loading_html/* = "loading"*/)
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)
, m_default_url (default_url)
, m_loading_html(loading_html)
, m_script_message_hadler_names(message_handler_names)
{
wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
#ifdef DEBUG_URL_PANEL
@ -70,7 +71,7 @@ WebViewPanel::WebViewPanel(wxWindow *parent, const wxString& default_url, const
SetSizer(topsizer);
// Create the webview
m_browser = WebView::CreateWebView(this, /*m_default_url*/ GUI::format_wxstr("file://%1%/web/%2%.html", boost::filesystem::path(resources_dir()).generic_string(), m_loading_html));
m_browser = WebView::CreateWebView(this, /*m_default_url*/ GUI::format_wxstr("file://%1%/web/%2%.html", boost::filesystem::path(resources_dir()).generic_string(), m_loading_html), m_script_message_hadler_names);
if (!m_browser) {
wxStaticText* text = new wxStaticText(this, wxID_ANY, _L("Failed to load a web browser."));
topsizer->Add(text, 0, wxALIGN_LEFT | wxBOTTOM, 10);
@ -95,8 +96,6 @@ WebViewPanel::WebViewPanel(wxWindow *parent, const wxString& default_url, const
m_dev_tools = m_tools_menu->AppendCheckItem(wxID_ANY, _L("Enable Dev Tools"));
#endif
//Zoom
m_zoomFactor = 100;
Bind(wxEVT_SHOW, &WebViewPanel::on_show, this);
@ -124,8 +123,6 @@ WebViewPanel::WebViewPanel(wxWindow *parent, const wxString& default_url, const
#endif
//Connect the idle events
Bind(wxEVT_IDLE, &WebViewPanel::on_idle, this);
Bind(wxEVT_CLOSE_WINDOW, &WebViewPanel::on_close, this);
}
WebViewPanel::~WebViewPanel()
@ -247,11 +244,6 @@ void WebViewPanel::on_reload_button(wxCommandEvent& WXUNUSED(evt))
m_browser->Reload();
}
void WebViewPanel::on_close(wxCloseEvent& evt)
{
this->Hide();
}
void WebViewPanel::on_script_message(wxWebViewEvent& evt)
{
}
@ -438,7 +430,7 @@ case type: \
WX_ERROR_CASE(wxWEBVIEW_NAV_ERR_OTHER);
}
BOOST_LOG_TRIVIAL(error) << "WebView error: " << category;
BOOST_LOG_TRIVIAL(error) << "WebViewPanel error: " << category;
load_error_page();
#ifdef DEBUG_URL_PANEL
m_info->ShowMessage(_L("An error occurred loading ") + evt.GetURL() + "\n" +
@ -471,10 +463,13 @@ SourceViewDialog::SourceViewDialog(wxWindow* parent, wxString source) :
ConnectRequestHandler::ConnectRequestHandler()
{
m_actions["REQUEST_ACCESS_TOKEN"] = std::bind(&ConnectRequestHandler::on_request_access_token, this);
m_actions["REQUEST_CONFIG"] = std::bind(&ConnectRequestHandler::on_request_config, this);
m_actions["UPDATE_SELECTED_PRINTER"] = std::bind(&ConnectRequestHandler::on_request_update_selected_printer_action, this);
m_actions["WEBAPP_READY"] = std::bind(&ConnectRequestHandler::request_compatible_printers, this);
m_actions["REQUEST_ACCESS_TOKEN"] = std::bind(&ConnectRequestHandler::on_connect_action_request_access_token, this);
m_actions["REQUEST_CONFIG"] = std::bind(&ConnectRequestHandler::on_connect_action_request_config, this);
m_actions["WEBAPP_READY"] = std::bind(&ConnectRequestHandler::on_connect_action_webapp_ready, this);
m_actions["SELECT_PRINTER"] = std::bind(&ConnectRequestHandler::on_connect_action_select_printer, this);
m_actions["PRINT"] = std::bind(&ConnectRequestHandler::on_connect_action_print, this);
// obsolete
//m_actions["REQUEST_SELECTED_PRINTER"] = std::bind(&ConnectRequestHandler::on_connect_action_print, this);
}
ConnectRequestHandler::~ConnectRequestHandler()
{
@ -516,17 +511,17 @@ void ConnectRequestHandler::handle_message(const std::string& message)
void ConnectRequestHandler::resend_config()
{
on_request_config();
on_connect_action_request_config();
}
void ConnectRequestHandler::on_request_access_token()
void ConnectRequestHandler::on_connect_action_request_access_token()
{
std::string token = wxGetApp().plater()->get_user_account()->get_access_token();
wxString script = GUI::format_wxstr("window._prusaConnect_v1.setAccessToken(\'%1%\')", token);
run_script_bridge(script);
}
void ConnectRequestHandler::on_request_config()
void ConnectRequestHandler::on_connect_action_request_config()
{
/*
accessToken?: string;
@ -535,7 +530,6 @@ void ConnectRequestHandler::on_request_config()
language?: ConnectLanguage;
sessionId?: string;
*/
const std::string token = wxGetApp().plater()->get_user_account()->get_access_token();
//const std::string sesh = wxGetApp().plater()->get_user_account()->get_shared_session_key();
const std::string dark_mode = wxGetApp().dark_mode() ? "DARK" : "LIGHT";
@ -548,7 +542,7 @@ void ConnectRequestHandler::on_request_config()
}
ConnectWebViewPanel::ConnectWebViewPanel(wxWindow* parent)
: WebViewPanel(parent, L"https://connect.prusa3d.com/connect-slicer-app/", "connect_loading")
: WebViewPanel(parent, L"https://connect.prusa3d.com/", { "_prusaSlicer" }, "connect_loading")
{
}
@ -569,15 +563,19 @@ void ConnectWebViewPanel::sys_color_changed()
resend_config();
}
void ConnectWebViewPanel::on_request_update_selected_printer_action()
void ConnectWebViewPanel::on_connect_action_select_printer()
{
assert(!m_message_data.empty());
wxGetApp().handle_connect_request_printer_pick(m_message_data);
wxGetApp().handle_connect_request_printer_select(m_message_data);
}
void ConnectWebViewPanel::on_connect_action_print()
{
// PRINT request is not defined for ConnectWebViewPanel
assert(true);
}
PrinterWebViewPanel::PrinterWebViewPanel(wxWindow* parent, const wxString& default_url)
: WebViewPanel(parent, default_url)
: WebViewPanel(parent, default_url, {})
{
if (!m_browser)
return;
@ -650,16 +648,51 @@ void PrinterWebViewPanel::sys_color_changed()
{
}
WebViewDialog::WebViewDialog(wxWindow* parent, const wxString& url, const wxString& dialog_name, const wxSize& size, const std::string& loading_html/* = "loading"*/)
WebViewDialog::WebViewDialog(wxWindow* parent, const wxString& url, const wxString& dialog_name, const wxSize& size, const std::vector<std::string>& message_handler_names, const std::string& loading_html/* = "loading"*/)
: wxDialog(parent, wxID_ANY, dialog_name, wxDefaultPosition, size, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
, m_loading_html(loading_html)
, m_script_message_hadler_names (message_handler_names)
{
wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
#ifdef DEBUG_URL_PANEL
// Create the button
bSizer_toolbar = new wxBoxSizer(wxHORIZONTAL);
m_button_back = new wxButton(this, wxID_ANY, wxT("Back"), wxDefaultPosition, wxDefaultSize, 0);
m_button_back->Enable(false);
bSizer_toolbar->Add(m_button_back, 0, wxALL, 5);
m_button_forward = new wxButton(this, wxID_ANY, wxT("Forward"), wxDefaultPosition, wxDefaultSize, 0);
m_button_forward->Enable(false);
bSizer_toolbar->Add(m_button_forward, 0, wxALL, 5);
m_button_stop = new wxButton(this, wxID_ANY, wxT("Stop"), wxDefaultPosition, wxDefaultSize, 0);
bSizer_toolbar->Add(m_button_stop, 0, wxALL, 5);
m_button_reload = new wxButton(this, wxID_ANY, wxT("Reload"), wxDefaultPosition, wxDefaultSize, 0);
bSizer_toolbar->Add(m_button_reload, 0, wxALL, 5);
m_url = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
bSizer_toolbar->Add(m_url, 1, wxALL | wxEXPAND, 5);
m_button_tools = new wxButton(this, wxID_ANY, wxT("Tools"), wxDefaultPosition, wxDefaultSize, 0);
bSizer_toolbar->Add(m_button_tools, 0, wxALL, 5);
// Create panel for find toolbar.
wxPanel* panel = new wxPanel(this);
topsizer->Add(bSizer_toolbar, 0, wxEXPAND, 0);
topsizer->Add(panel, wxSizerFlags().Expand());
// Create sizer for panel.
wxBoxSizer* panel_sizer = new wxBoxSizer(wxVERTICAL);
panel->SetSizer(panel_sizer);
#endif
topsizer->SetMinSize(size);
SetSizerAndFit(topsizer);
// Create the webview
m_browser = WebView::CreateWebView(this, GUI::format_wxstr("file://%1%/web/%2%.html", boost::filesystem::path(resources_dir()).generic_string(), m_loading_html));
m_browser = WebView::CreateWebView(this, GUI::format_wxstr("file://%1%/web/%2%.html", boost::filesystem::path(resources_dir()).generic_string(), m_loading_html), m_script_message_hadler_names);
if (!m_browser) {
wxStaticText* text = new wxStaticText(this, wxID_ANY, _L("Failed to load a web browser."));
topsizer->Add(text, 0, wxALIGN_LEFT | wxBOTTOM, 10);
@ -668,27 +701,349 @@ WebViewDialog::WebViewDialog(wxWindow* parent, const wxString& url, const wxStri
topsizer->Add(m_browser, wxSizerFlags().Expand().Proportion(1));
#ifdef DEBUG_URL_PANEL
// Create the Tools menu
m_tools_menu = new wxMenu();
wxMenuItem* viewSource = m_tools_menu->Append(wxID_ANY, _L("View Source"));
wxMenuItem* viewText = m_tools_menu->Append(wxID_ANY, _L("View Text"));
m_tools_menu->AppendSeparator();
wxMenu* script_menu = new wxMenu;
m_script_custom = script_menu->Append(wxID_ANY, "Custom script");
m_tools_menu->AppendSubMenu(script_menu, _L("Run Script"));
wxMenuItem* addUserScript = m_tools_menu->Append(wxID_ANY, _L("Add user script"));
wxMenuItem* setCustomUserAgent = m_tools_menu->Append(wxID_ANY, _L("Set custom user agent"));
m_context_menu = m_tools_menu->AppendCheckItem(wxID_ANY, _L("Enable Context Menu"));
m_dev_tools = m_tools_menu->AppendCheckItem(wxID_ANY, _L("Enable Dev Tools"));
#endif
Bind(wxEVT_SHOW, &WebViewDialog::on_show, this);
Bind(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, &WebViewDialog::on_script_message, this, m_browser->GetId());
// Connect the webview events
Bind(wxEVT_WEBVIEW_ERROR, &WebViewDialog::on_error, this, m_browser->GetId());
//Connect the idle events
Bind(wxEVT_IDLE, &WebViewDialog::on_idle, this);
#ifdef DEBUG_URL_PANEL
// Connect the button events
Bind(wxEVT_BUTTON, &WebViewDialog::on_back_button, this, m_button_back->GetId());
Bind(wxEVT_BUTTON, &WebViewDialog::on_forward_button, this, m_button_forward->GetId());
Bind(wxEVT_BUTTON, &WebViewDialog::on_stop_button, this, m_button_stop->GetId());
Bind(wxEVT_BUTTON, &WebViewDialog::on_reload_button, this, m_button_reload->GetId());
Bind(wxEVT_BUTTON, &WebViewDialog::on_tools_clicked, this, m_button_tools->GetId());
Bind(wxEVT_TEXT_ENTER, &WebViewDialog::on_url, this, m_url->GetId());
// Connect the menu events
Bind(wxEVT_MENU, &WebViewDialog::on_view_source_request, this, viewSource->GetId());
Bind(wxEVT_MENU, &WebViewDialog::on_view_text_request, this, viewText->GetId());
Bind(wxEVT_MENU, &WebViewDialog::On_enable_context_menu, this, m_context_menu->GetId());
Bind(wxEVT_MENU, &WebViewDialog::On_enable_dev_tools, this, m_dev_tools->GetId());
Bind(wxEVT_MENU, &WebViewDialog::on_run_script_custom, this, m_script_custom->GetId());
Bind(wxEVT_MENU, &WebViewDialog::on_add_user_script, this, addUserScript->GetId());
#endif
Bind(wxEVT_CLOSE_WINDOW, ([this](wxCloseEvent& evt) { EndModal(wxID_CANCEL); }));
m_browser->LoadURL(url);
#ifdef DEBUG_URL_PANEL
m_url->SetLabelText(url);
#endif
}
WebViewDialog::~WebViewDialog()
{
}
void WebViewDialog::on_idle(wxIdleEvent& WXUNUSED(evt))
{
if (!m_browser)
return;
if (m_browser->IsBusy()) {
wxSetCursor(wxCURSOR_ARROWWAIT);
} else {
wxSetCursor(wxNullCursor);
if (m_load_error_page) {
m_load_error_page = false;
m_browser->LoadURL(GUI::format_wxstr("file://%1%/web/connection_failed.html", boost::filesystem::path(resources_dir()).generic_string()));
}
}
#ifdef DEBUG_URL_PANEL
m_button_stop->Enable(m_browser->IsBusy());
#endif
}
/**
* Callback invoked when user entered an URL and pressed enter
*/
void WebViewDialog::on_url(wxCommandEvent& WXUNUSED(evt))
{
if (!m_browser)
return;
#ifdef DEBUG_URL_PANEL
m_browser->LoadURL(m_url->GetValue());
m_browser->SetFocus();
#endif
}
/**
* Callback invoked when user pressed the "back" button
*/
void WebViewDialog::on_back_button(wxCommandEvent& WXUNUSED(evt))
{
if (!m_browser)
return;
m_browser->GoBack();
}
/**
* Callback invoked when user pressed the "forward" button
*/
void WebViewDialog::on_forward_button(wxCommandEvent& WXUNUSED(evt))
{
if (!m_browser)
return;
m_browser->GoForward();
}
/**
* Callback invoked when user pressed the "stop" button
*/
void WebViewDialog::on_stop_button(wxCommandEvent& WXUNUSED(evt))
{
if (!m_browser)
return;
m_browser->Stop();
}
/**
* Callback invoked when user pressed the "reload" button
*/
void WebViewDialog::on_reload_button(wxCommandEvent& WXUNUSED(evt))
{
if (!m_browser)
return;
m_browser->Reload();
}
void WebViewDialog::on_script_message(wxWebViewEvent& evt)
{
}
/**
* Invoked when user selects the "View Source" menu item
*/
void WebViewDialog::on_view_source_request(wxCommandEvent& WXUNUSED(evt))
{
if (!m_browser)
return;
SourceViewDialog dlg(this, m_browser->GetPageSource());
dlg.ShowModal();
}
/**
* Invoked when user selects the "View Text" menu item
*/
void WebViewDialog::on_view_text_request(wxCommandEvent& WXUNUSED(evt))
{
if (!m_browser)
return;
wxDialog textViewDialog(this, wxID_ANY, "Page Text",
wxDefaultPosition, wxSize(700, 500),
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, m_browser->GetPageText(),
wxDefaultPosition, wxDefaultSize,
wxTE_MULTILINE |
wxTE_RICH |
wxTE_READONLY);
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(text, 1, wxEXPAND);
SetSizer(sizer);
textViewDialog.ShowModal();
}
/**
* Invoked when user selects the "Menu" item
*/
void WebViewDialog::on_tools_clicked(wxCommandEvent& WXUNUSED(evt))
{
if (!m_browser)
return;
#ifdef DEBUG_URL_PANEL
m_context_menu->Check(m_browser->IsContextMenuEnabled());
m_dev_tools->Check(m_browser->IsAccessToDevToolsEnabled());
wxPoint position = ScreenToClient(wxGetMousePosition());
PopupMenu(m_tools_menu, position.x, position.y);
#endif
}
void WebViewDialog::on_run_script_custom(wxCommandEvent& WXUNUSED(evt))
{
wxTextEntryDialog dialog
(
this,
"Please enter JavaScript code to execute",
wxGetTextFromUserPromptStr,
m_javascript,
wxOK | wxCANCEL | wxCENTRE | wxTE_MULTILINE
);
if (dialog.ShowModal() != wxID_OK)
return;
run_script(dialog.GetValue());
}
void WebViewDialog::on_add_user_script(wxCommandEvent& WXUNUSED(evt))
{
wxString userScript = "window.wx_test_var = 'wxWidgets webview sample';";
wxTextEntryDialog dialog
(
this,
"Enter the JavaScript code to run as the initialization script that runs before any script in the HTML document.",
wxGetTextFromUserPromptStr,
userScript,
wxOK | wxCANCEL | wxCENTRE | wxTE_MULTILINE
);
if (dialog.ShowModal() != wxID_OK)
return;
if (!m_browser->AddUserScript(dialog.GetValue()))
wxLogError("Could not add user script");
}
void WebViewDialog::on_set_custom_user_agent(wxCommandEvent& WXUNUSED(evt))
{
if (!m_browser)
return;
wxString customUserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_1_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Mobile/15E148 Safari/604.1";
wxTextEntryDialog dialog
(
this,
"Enter the custom user agent string you would like to use.",
wxGetTextFromUserPromptStr,
customUserAgent,
wxOK | wxCANCEL | wxCENTRE
);
if (dialog.ShowModal() != wxID_OK)
return;
if (!m_browser->SetUserAgent(customUserAgent))
wxLogError("Could not set custom user agent");
}
void WebViewDialog::on_clear_selection(wxCommandEvent& WXUNUSED(evt))
{
if (!m_browser)
return;
m_browser->ClearSelection();
}
void WebViewDialog::on_delete_selection(wxCommandEvent& WXUNUSED(evt))
{
if (!m_browser)
return;
m_browser->DeleteSelection();
}
void WebViewDialog::on_select_all(wxCommandEvent& WXUNUSED(evt))
{
if (!m_browser)
return;
m_browser->SelectAll();
}
void WebViewDialog::On_enable_context_menu(wxCommandEvent& evt)
{
if (!m_browser)
return;
m_browser->EnableContextMenu(evt.IsChecked());
}
void WebViewDialog::On_enable_dev_tools(wxCommandEvent& evt)
{
if (!m_browser)
return;
m_browser->EnableAccessToDevTools(evt.IsChecked());
}
/**
* Callback invoked when a loading error occurs
*/
void WebViewDialog::on_error(wxWebViewEvent& evt)
{
#define WX_ERROR_CASE(type) \
case type: \
category = #type; \
break;
wxString category;
switch (evt.GetInt())
{
WX_ERROR_CASE(wxWEBVIEW_NAV_ERR_CONNECTION);
WX_ERROR_CASE(wxWEBVIEW_NAV_ERR_CERTIFICATE);
WX_ERROR_CASE(wxWEBVIEW_NAV_ERR_AUTH);
WX_ERROR_CASE(wxWEBVIEW_NAV_ERR_SECURITY);
WX_ERROR_CASE(wxWEBVIEW_NAV_ERR_NOT_FOUND);
WX_ERROR_CASE(wxWEBVIEW_NAV_ERR_REQUEST);
WX_ERROR_CASE(wxWEBVIEW_NAV_ERR_USER_CANCELLED);
WX_ERROR_CASE(wxWEBVIEW_NAV_ERR_OTHER);
}
BOOST_LOG_TRIVIAL(error) << "WebViewDialog error: " << category;
load_error_page();
}
void WebViewDialog::load_error_page()
{
if (!m_browser)
return;
m_browser->Stop();
m_load_error_page = true;
}
void WebViewDialog::run_script(const wxString& javascript)
{
if (!m_browser)
return;
// Remember the script we run in any case, so the next time the user opens
// the "Run Script" dialog box, it is shown there for convenient updating.
m_javascript = javascript;
BOOST_LOG_TRIVIAL(debug) << "RunScript " << javascript;
m_browser->RunScriptAsync(javascript);
}
void WebViewDialog::EndModal(int retCode)
{
if (m_browser) {
for (const std::string& handler : m_script_message_hadler_names) {
m_browser->RemoveScriptMessageHandler(GUI::into_u8(handler));
}
}
wxDialog::EndModal(retCode);
}
PrinterPickWebViewDialog::PrinterPickWebViewDialog(wxWindow* parent, std::string& ret_val)
: WebViewDialog(parent
, L"https://connect.prusa3d.com/connect-slicer-app/printer-list"
, L"https://connect.prusa3d.com/slicer-select-printer"
, _L("Choose a printer")
, wxSize(std::max(parent->GetClientSize().x / 2, 100 * wxGetApp().em_unit()), std::max(parent->GetClientSize().y / 2, 50 * wxGetApp().em_unit()))
,{"_prusaSlicer"}
, "connect_loading")
, m_ret_val(ret_val)
{
@ -710,13 +1065,18 @@ void PrinterPickWebViewDialog::on_script_message(wxWebViewEvent& evt)
handle_message(into_u8(evt.GetString()));
}
void PrinterPickWebViewDialog::on_request_update_selected_printer_action()
void PrinterPickWebViewDialog::on_connect_action_select_printer()
{
// SELECT_PRINTER request is not defined for PrinterPickWebViewDialog
assert(true);
}
void PrinterPickWebViewDialog::on_connect_action_print()
{
m_ret_val = m_message_data;
this->EndModal(wxID_OK);
}
void PrinterPickWebViewDialog::request_compatible_printers()
void PrinterPickWebViewDialog::on_connect_action_webapp_ready()
{
if (Preset::printer_technology(wxGetApp().preset_bundle->printers.get_selected_preset().config) == ptFFF) {
@ -732,24 +1092,24 @@ void PrinterPickWebViewDialog::request_compatible_printers_FFF()
//material: Material;
//nozzleDiameter: number;
//printerType: string;
//filename: string;
//}
const Preset& selected_printer = wxGetApp().preset_bundle->printers.get_selected_preset();
const Preset& selected_filament = wxGetApp().preset_bundle->filaments.get_selected_preset();
std::string nozzle_diameter_serialized = dynamic_cast<const ConfigOptionFloats*>(selected_printer.config.option("nozzle_diameter"))->serialize();
// Sending only first nozzle diamenter for now.
if (size_t comma = nozzle_diameter_serialized.find(','); comma != std::string::npos)
nozzle_diameter_serialized = nozzle_diameter_serialized.substr(0, comma);
std::string nozzle_diameter_serialized = selected_printer.config.opt_string("printer_variant");
// Sending only first filament type for now. This should change to array of values
const std::string filament_type_serialized = selected_filament.config.option("filament_type")->serialize();
const std::string printer_model_serialized = selected_printer.config.option("printer_model")->serialize();
const std::string uuid = wxGetApp().plater()->get_user_account()->get_current_printer_uuid_from_connect(printer_model_serialized);
const std::string filename = wxGetApp().plater()->get_upload_filename();
const std::string request = GUI::format(
"{"
"\"printerUuid\": \"%4%\", "
"\"printerModel\": \"%3%\", "
"\"nozzleDiameter\": %2%, "
"\"material\": \"%1%\" "
"}", filament_type_serialized, nozzle_diameter_serialized, printer_model_serialized);
"\"material\": \"%1%\", "
"\"filename\": \"%5%\" "
"}", filament_type_serialized, nozzle_diameter_serialized, printer_model_serialized, uuid, filename);
wxString script = GUI::format_wxstr("window._prusaConnect_v1.requestCompatiblePrinter(%1%)", request);
run_script(script);
@ -760,11 +1120,15 @@ void PrinterPickWebViewDialog::request_compatible_printers_SLA()
const std::string printer_model_serialized = selected_printer.config.option("printer_model")->serialize();
const Preset& selected_material = wxGetApp().preset_bundle->sla_materials.get_selected_preset();
const std::string material_type_serialized = selected_material.config.option("material_type")->serialize();
const std::string uuid = wxGetApp().plater()->get_user_account()->get_current_printer_uuid_from_connect(printer_model_serialized);
const std::string filename = wxGetApp().plater()->get_upload_filename();
const std::string request = GUI::format(
"{"
"\"printerUuid\": \"%3%\", "
"\"material\": \"%1%\", "
"\"printerModel\": \"%2%\" "
"}", material_type_serialized, printer_model_serialized);
"\"printerModel\": \"%2%\", "
"\"filename\": \"%4%\" "
"}", material_type_serialized, printer_model_serialized, uuid, filename);
wxString script = GUI::format_wxstr("window._prusaConnect_v1.requestCompatiblePrinter(%1%)", request);
run_script(script);

View File

@ -16,7 +16,7 @@ namespace GUI {
class WebViewPanel : public wxPanel
{
public:
WebViewPanel(wxWindow *parent, const wxString& default_url, const std::string& loading_html = "loading");
WebViewPanel(wxWindow *parent, const wxString& default_url, const std::vector<std::string>& message_handler_names, const std::string& loading_html = "loading");
virtual ~WebViewPanel();
void load_url(const wxString& url);
@ -47,7 +47,6 @@ public:
void on_select_all(wxCommandEvent& evt);
void On_enable_context_menu(wxCommandEvent& evt);
void On_enable_dev_tools(wxCommandEvent& evt);
void on_close(wxCloseEvent& evt);
wxString get_default_url() const { return m_default_url; }
void set_default_url(const wxString& url) { m_default_url = url; }
@ -55,7 +54,7 @@ public:
virtual void sys_color_changed();
protected:
wxWebView* m_browser;
wxWebView* m_browser { nullptr };
bool m_load_default_url { false };
#ifdef DEBUG_URL_PANEL
@ -76,7 +75,6 @@ protected:
wxMenuItem* m_context_menu;
wxMenuItem* m_dev_tools;
#endif
long m_zoomFactor;
// Last executed JavaScript snippet, for convenience.
wxString m_javascript;
@ -88,6 +86,75 @@ protected:
bool m_load_error_page { false };
bool m_shown { false };
std::vector<std::string> m_script_message_hadler_names;
};
class WebViewDialog : public wxDialog
{
public:
WebViewDialog(wxWindow* parent, const wxString& url, const wxString& dialog_name, const wxSize& size, const std::vector<std::string>& message_handler_names, const std::string& loading_html = "loading");
virtual ~WebViewDialog();
virtual void on_show(wxShowEvent& evt) = 0;
virtual void on_script_message(wxWebViewEvent& evt) = 0;
void on_idle(wxIdleEvent& evt);
void on_url(wxCommandEvent& evt);
void on_back_button(wxCommandEvent& evt);
void on_forward_button(wxCommandEvent& evt);
void on_stop_button(wxCommandEvent& evt);
void on_reload_button(wxCommandEvent& evt);
void on_view_source_request(wxCommandEvent& evt);
void on_view_text_request(wxCommandEvent& evt);
void on_tools_clicked(wxCommandEvent& evt);
void on_error(wxWebViewEvent& evt);
void on_run_script_custom(wxCommandEvent& evt);
void on_add_user_script(wxCommandEvent& evt);
void on_set_custom_user_agent(wxCommandEvent& evt);
void on_clear_selection(wxCommandEvent& evt);
void on_delete_selection(wxCommandEvent& evt);
void on_select_all(wxCommandEvent& evt);
void On_enable_context_menu(wxCommandEvent& evt);
void On_enable_dev_tools(wxCommandEvent& evt);
void run_script(const wxString& javascript);
void load_error_page();
virtual void EndModal(int retCode) wxOVERRIDE;
protected:
wxWebView* m_browser {nullptr};
std::string m_loading_html;
bool m_load_error_page{ false };
#ifdef DEBUG_URL_PANEL
wxBoxSizer* bSizer_toolbar;
wxButton* m_button_back;
wxButton* m_button_forward;
wxButton* m_button_stop;
wxButton* m_button_reload;
wxTextCtrl* m_url;
wxButton* m_button_tools;
wxMenu* m_tools_menu;
wxMenuItem* m_script_custom;
wxStaticText* m_info_text;
wxMenuItem* m_context_menu;
wxMenuItem* m_dev_tools;
#endif
// Last executed JavaScript snippet, for convenience.
wxString m_javascript;
wxString m_response_js;
wxString m_default_url;
std::vector<std::string> m_script_message_hadler_names;
};
class ConnectRequestHandler
@ -100,11 +167,12 @@ public:
void resend_config();
protected:
// action callbacs stored in m_actions
virtual void on_request_access_token();
virtual void on_request_config();
virtual void on_request_update_selected_printer_action() = 0;
virtual void on_connect_action_request_access_token();
virtual void on_connect_action_request_config();
virtual void on_connect_action_select_printer() = 0;
virtual void on_connect_action_print() = 0;
virtual void run_script_bridge(const wxString& script) = 0;
virtual void request_compatible_printers() = 0;
virtual void on_connect_action_webapp_ready() = 0;
std::map<std::string, std::function<void(void)>> m_actions;
std::string m_message_data;
@ -119,8 +187,9 @@ public:
void logout();
void sys_color_changed() override;
protected:
void on_request_update_selected_printer_action() override;
void request_compatible_printers() override {}
void on_connect_action_select_printer() override;
void on_connect_action_print() override;
void on_connect_action_webapp_ready() override {}
void run_script_bridge(const wxString& script) override {run_script(script); }
};
@ -144,23 +213,6 @@ private:
bool m_api_key_sent {false};
};
class WebViewDialog : public wxDialog
{
public:
WebViewDialog(wxWindow* parent, const wxString& url, const wxString& dialog_name, const wxSize& size, const std::string& loading_html = "loading");
virtual ~WebViewDialog();
virtual void on_show(wxShowEvent& evt) = 0;
virtual void on_script_message(wxWebViewEvent& evt) = 0;
void run_script(const wxString& javascript);
protected:
wxWebView* m_browser;
std::string m_loading_html;
};
class PrinterPickWebViewDialog : public WebViewDialog, public ConnectRequestHandler
{
public:
@ -168,11 +220,13 @@ public:
void on_show(wxShowEvent& evt) override;
void on_script_message(wxWebViewEvent& evt) override;
protected:
void on_request_update_selected_printer_action() override;
void request_compatible_printers() override;
void on_connect_action_select_printer() override;
void on_connect_action_print() override;
void on_connect_action_webapp_ready() override;
void request_compatible_printers_FFF();
void request_compatible_printers_SLA();
void run_script_bridge(const wxString& script) override { run_script(script); }
private:
std::string& m_ret_val;
};

View File

@ -41,6 +41,10 @@ struct PrintHostUpload
std::string storage;
PrintHostPostUploadAction post_action { PrintHostPostUploadAction::None };
std::string set_ready;
std::string position;
std::string wait_until;
};
class PrintHost

View File

@ -90,8 +90,8 @@ bool PrusaConnectNew::init_upload(PrintHostUpload upload_data, std::string& out)
const std::string name = get_name();
const std::string file_size = std::to_string(size);
const std::string access_token = GUI::wxGetApp().plater()->get_user_account()->get_access_token();
const std::string escaped_upload_path = escape_path_by_element(upload_data.upload_path);
const std::string escaped_upload_filename = escape_path_by_element(upload_data.upload_path.filename());
//const std::string upload_path = upload_data.upload_path.generic_string();
const std::string upload_filename = upload_data.upload_path.filename().string();
std::string url = GUI::format("%1%/app/users/teams/%2%/uploads", get_host(), m_team_id);
const std::string request_body_json = GUI::format(
"{"
@ -101,9 +101,9 @@ bool PrusaConnectNew::init_upload(PrintHostUpload upload_data, std::string& out)
"\"force\": true, "
"\"printer_uuid\": \"%4%\""
"}"
, escaped_upload_filename
, upload_filename
, file_size
, upload_data.storage + "/" + escaped_upload_path
, upload_data.upload_path.generic_string()
, m_uuid
);
@ -156,10 +156,20 @@ bool PrusaConnectNew::upload(PrintHostUpload upload_data, ProgressFn progress_fn
}
const std::string name = get_name();
const std::string access_token = GUI::wxGetApp().plater()->get_user_account()->get_access_token();
const std::string escaped_upload_path = escape_string(upload_data.storage + "/" + upload_data.upload_path.string());
const std::string to_print = upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false";
const std::string to_queue = upload_data.post_action == PrintHostPostUploadAction::QueuePrint ? "true" : "false";
std::string url = GUI::format("%1%/app/teams/%2%/files/raw?upload_id=%3%&force=true&printer_uuid=%4%&path=%5%&to_print=%6%&to_queue=%7%", get_host(), m_team_id, upload_id, m_uuid, escaped_upload_path, to_print, to_queue);
const std::string escaped_upload_path = upload_data.storage + "/" + escape_path_by_element(upload_data.upload_path.string());
const std::string set_ready = upload_data.set_ready.empty() ? "" : "&set_ready=" + upload_data.set_ready;
const std::string position = upload_data.position.empty() ? "" : "&position=" + upload_data.position;
const std::string wait_until = upload_data.wait_until.empty() ? "" : "&wait_until=" + upload_data.wait_until;
const std::string url = GUI::format(
"%1%/app/teams/%2%/files/raw"
"?upload_id=%3%"
"&force=true"
"&printer_uuid=%4%"
"&path=%5%"
"%6%"
"%7%"
"%8%"
, get_host(), m_team_id, upload_id, m_uuid, escaped_upload_path, set_ready, position, wait_until);
bool res = true;
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%")