mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-08 07:27:41 -06:00
fix errors after cherry picking commits
This commit is contained in:
parent
85251de418
commit
9bab2e2efa
12 changed files with 522 additions and 245 deletions
|
@ -335,6 +335,9 @@ void AppConfig::set_defaults()
|
||||||
// }
|
// }
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
|
if (get("allow_ip_resolve").empty())
|
||||||
|
set("allow_ip_resolve", "1");
|
||||||
|
|
||||||
if (get("presets", "filament_colors").empty()) {
|
if (get("presets", "filament_colors").empty()) {
|
||||||
set_str("presets", "filament_colors", "#F2754E");
|
set_str("presets", "filament_colors", "#F2754E");
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,8 @@ public:
|
||||||
{ std::string value; this->get(section, key, value); return value; }
|
{ std::string value; this->get(section, key, value); return value; }
|
||||||
std::string get(const std::string &key) const
|
std::string get(const std::string &key) const
|
||||||
{ std::string value; this->get("app", key, value); return value; }
|
{ std::string value; this->get("app", key, value); return value; }
|
||||||
|
bool get_bool(const std::string &key) const
|
||||||
|
{ return this->get(key) == "true"; }
|
||||||
void set(const std::string §ion, const std::string &key, const std::string &value)
|
void set(const std::string §ion, const std::string &key, const std::string &value)
|
||||||
{
|
{
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <boost/nowide/convert.hpp>
|
||||||
|
|
||||||
#include <wx/sizer.h>
|
#include <wx/sizer.h>
|
||||||
#include <wx/button.h>
|
#include <wx/button.h>
|
||||||
#include <wx/listctrl.h>
|
#include <wx/listctrl.h>
|
||||||
|
@ -15,8 +17,8 @@
|
||||||
#include "slic3r/GUI/GUI.hpp"
|
#include "slic3r/GUI/GUI.hpp"
|
||||||
#include "slic3r/GUI/GUI_App.hpp"
|
#include "slic3r/GUI/GUI_App.hpp"
|
||||||
#include "slic3r/GUI/I18N.hpp"
|
#include "slic3r/GUI/I18N.hpp"
|
||||||
|
#include "slic3r/GUI/format.hpp"
|
||||||
#include "slic3r/Utils/Bonjour.hpp"
|
#include "slic3r/Utils/Bonjour.hpp"
|
||||||
#include "Widgets/Button.hpp"
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -61,8 +63,6 @@ BonjourDialog::BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology tech)
|
||||||
, timer_state(0)
|
, timer_state(0)
|
||||||
, tech(tech)
|
, tech(tech)
|
||||||
{
|
{
|
||||||
SetBackgroundColour(*wxWHITE);
|
|
||||||
|
|
||||||
const int em = GUI::wxGetApp().em_unit();
|
const int em = GUI::wxGetApp().em_unit();
|
||||||
list->SetMinSize(wxSize(80 * em, 30 * em));
|
list->SetMinSize(wxSize(80 * em, 30 * em));
|
||||||
|
|
||||||
|
@ -81,39 +81,10 @@ BonjourDialog::BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology tech)
|
||||||
|
|
||||||
vsizer->Add(list, 1, wxEXPAND | wxALL, em);
|
vsizer->Add(list, 1, wxEXPAND | wxALL, em);
|
||||||
|
|
||||||
|
wxBoxSizer *button_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
auto button_sizer = new wxBoxSizer(wxHORIZONTAL);
|
button_sizer->Add(new wxButton(this, wxID_OK, "OK"), 0, wxALL, em);
|
||||||
|
button_sizer->Add(new wxButton(this, wxID_CANCEL, "Cancel"), 0, wxALL, em);
|
||||||
StateColor btn_bg_green(std::pair<wxColour, int>(wxColour(0, 137, 123), StateColor::Pressed), std::pair<wxColour, int>(wxColour(38, 166, 154), StateColor::Hovered),
|
// ^ Note: The Ok/Cancel labels are translated by wxWidgets
|
||||||
std::pair<wxColour, int>(wxColour(0, 150, 136), StateColor::Normal));
|
|
||||||
|
|
||||||
StateColor btn_bg_white(std::pair<wxColour, int>(wxColour(206, 206, 206), StateColor::Pressed), std::pair<wxColour, int>(wxColour(238, 238, 238), StateColor::Hovered),
|
|
||||||
std::pair<wxColour, int>(*wxWHITE, StateColor::Normal));
|
|
||||||
|
|
||||||
auto m_button_ok = new Button(this, _L("OK"));
|
|
||||||
m_button_ok->SetBackgroundColor(btn_bg_green);
|
|
||||||
m_button_ok->SetBorderColor(*wxWHITE);
|
|
||||||
m_button_ok->SetTextColor(*wxWHITE);
|
|
||||||
m_button_ok->SetFont(Label::Body_12);
|
|
||||||
m_button_ok->SetSize(wxSize(FromDIP(58), FromDIP(24)));
|
|
||||||
m_button_ok->SetMinSize(wxSize(FromDIP(58), FromDIP(24)));
|
|
||||||
m_button_ok->SetCornerRadius(FromDIP(12));
|
|
||||||
|
|
||||||
m_button_ok->Bind(wxEVT_LEFT_DOWN, [this](auto &e) { this->EndModal(wxID_OK); });
|
|
||||||
|
|
||||||
auto m_button_cancel = new Button(this, _L("Cancel"));
|
|
||||||
m_button_cancel->SetBackgroundColor(btn_bg_white);
|
|
||||||
m_button_cancel->SetBorderColor(wxColour(38, 46, 48));
|
|
||||||
m_button_cancel->SetFont(Label::Body_12);
|
|
||||||
m_button_cancel->SetSize(wxSize(FromDIP(58), FromDIP(24)));
|
|
||||||
m_button_cancel->SetMinSize(wxSize(FromDIP(58), FromDIP(24)));
|
|
||||||
m_button_cancel->SetCornerRadius(FromDIP(12));
|
|
||||||
|
|
||||||
m_button_cancel->Bind(wxEVT_LEFT_DOWN, [this](auto &e) { this->EndModal(wxID_CANCEL); });
|
|
||||||
|
|
||||||
button_sizer->AddStretchSpacer();
|
|
||||||
button_sizer->Add(m_button_ok, 0, wxALL, FromDIP(5));
|
|
||||||
button_sizer->Add(m_button_cancel, 0, wxALL, FromDIP(5));
|
|
||||||
|
|
||||||
vsizer->Add(button_sizer, 0, wxALIGN_CENTER);
|
vsizer->Add(button_sizer, 0, wxALIGN_CENTER);
|
||||||
SetSizerAndFit(vsizer);
|
SetSizerAndFit(vsizer);
|
||||||
|
@ -253,19 +224,61 @@ void BonjourDialog::on_timer(wxTimerEvent &)
|
||||||
// explicitly (wxTimerEvent should not be created by user code).
|
// explicitly (wxTimerEvent should not be created by user code).
|
||||||
void BonjourDialog::on_timer_process()
|
void BonjourDialog::on_timer_process()
|
||||||
{
|
{
|
||||||
const auto search_str = _utf8(L("Searching for devices"));
|
const auto search_str = _L("Searching for devices");
|
||||||
|
|
||||||
if (timer_state > 0) {
|
if (timer_state > 0) {
|
||||||
const std::string dots(timer_state, '.');
|
const std::string dots(timer_state, '.');
|
||||||
label->SetLabel(GUI::from_u8((boost::format("%1% %2%") % search_str % dots).str()));
|
label->SetLabel(search_str + dots);
|
||||||
timer_state = (timer_state) % 3 + 1;
|
timer_state = (timer_state) % 3 + 1;
|
||||||
} else {
|
} else {
|
||||||
label->SetLabel(GUI::from_u8((boost::format("%1%: %2%") % search_str % (_utf8(L("Finished"))+".")).str()));
|
label->SetLabel(search_str + ": " + _L("Finished") + ".");
|
||||||
timer->Stop();
|
timer->Stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IPListDialog::IPListDialog(wxWindow* parent, const wxString& hostname, const std::vector<boost::asio::ip::address>& ips, size_t& selected_index)
|
||||||
|
: wxDialog(parent, wxID_ANY, _(L("Multiple resolved IP addresses")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||||
|
, m_list(new wxListView(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxSIMPLE_BORDER))
|
||||||
|
, m_selected_index (selected_index)
|
||||||
|
{
|
||||||
|
const int em = GUI::wxGetApp().em_unit();
|
||||||
|
m_list->SetMinSize(wxSize(40 * em, 30 * em));
|
||||||
|
|
||||||
|
wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
|
auto* label = new wxStaticText(this, wxID_ANY, GUI::format_wxstr(_L("There are several IP addresses resolving to hostname %1%.\nPlease select one that should be used."), hostname));
|
||||||
|
vsizer->Add(label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, em);
|
||||||
|
|
||||||
|
m_list->SetSingleStyle(wxLC_SINGLE_SEL);
|
||||||
|
m_list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 40 * em);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ips.size(); i++)
|
||||||
|
m_list->InsertItem(i, boost::nowide::widen(ips[i].to_string()));
|
||||||
|
|
||||||
|
m_list->Select(0);
|
||||||
|
|
||||||
|
vsizer->Add(m_list, 1, wxEXPAND | wxALL, em);
|
||||||
|
|
||||||
|
wxBoxSizer* button_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
button_sizer->Add(new wxButton(this, wxID_OK, "OK"), 0, wxALL, em);
|
||||||
|
button_sizer->Add(new wxButton(this, wxID_CANCEL, "Cancel"), 0, wxALL, em);
|
||||||
|
|
||||||
|
vsizer->Add(button_sizer, 0, wxALIGN_CENTER);
|
||||||
|
SetSizerAndFit(vsizer);
|
||||||
|
|
||||||
|
GUI::wxGetApp().UpdateDlgDarkUI(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPListDialog::~IPListDialog()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void IPListDialog::EndModal(int retCode)
|
||||||
|
{
|
||||||
|
if (retCode == wxID_OK) {
|
||||||
|
m_selected_index = (size_t)m_list->GetFirstSelected();
|
||||||
|
}
|
||||||
|
wxDialog::EndModal(retCode);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
#ifndef slic3r_BonjourDialog_hpp_
|
#ifndef slic3r_BonjourDialog_hpp_
|
||||||
#define slic3r_BonjourDialog_hpp_
|
#define slic3r_BonjourDialog_hpp_
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include <boost/asio/ip/address.hpp>
|
||||||
|
|
||||||
#include <wx/dialog.h>
|
#include <wx/dialog.h>
|
||||||
|
#include <wx/string.h>
|
||||||
|
|
||||||
#include "libslic3r/PrintConfig.hpp"
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
|
|
||||||
|
@ -11,7 +15,7 @@ class wxListView;
|
||||||
class wxStaticText;
|
class wxStaticText;
|
||||||
class wxTimer;
|
class wxTimer;
|
||||||
class wxTimerEvent;
|
class wxTimerEvent;
|
||||||
|
class address;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -41,12 +45,26 @@ private:
|
||||||
unsigned timer_state;
|
unsigned timer_state;
|
||||||
Slic3r::PrinterTechnology tech;
|
Slic3r::PrinterTechnology tech;
|
||||||
|
|
||||||
void on_reply(BonjourReplyEvent &);
|
virtual void on_reply(BonjourReplyEvent &);
|
||||||
void on_timer(wxTimerEvent &);
|
void on_timer(wxTimerEvent &);
|
||||||
void on_timer_process();
|
void on_timer_process();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class IPListDialog : public wxDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IPListDialog(wxWindow* parent, const wxString& hostname, const std::vector<boost::asio::ip::address>& ips, size_t& selected_index);
|
||||||
|
IPListDialog(IPListDialog&&) = delete;
|
||||||
|
IPListDialog(const IPListDialog&) = delete;
|
||||||
|
IPListDialog& operator=(IPListDialog&&) = delete;
|
||||||
|
IPListDialog& operator=(const IPListDialog&) = delete;
|
||||||
|
~IPListDialog();
|
||||||
|
|
||||||
|
virtual void EndModal(int retCode) wxOVERRIDE;
|
||||||
|
private:
|
||||||
|
wxListView* m_list;
|
||||||
|
size_t& m_selected_index;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -406,24 +406,36 @@ void PhysicalPrinterDialog::update(bool printer_change)
|
||||||
// Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment)
|
// Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment)
|
||||||
bool supports_multiple_printers = false;
|
bool supports_multiple_printers = false;
|
||||||
if (tech == ptFFF) {
|
if (tech == ptFFF) {
|
||||||
update_host_type(printer_change);
|
update_host_type(printer_change);
|
||||||
const auto opt = m_config->option<ConfigOptionEnum<PrintHostType>>("host_type");
|
const auto opt = m_config->option<ConfigOptionEnum<PrintHostType>>("host_type");
|
||||||
m_optgroup->show_field("host_type");
|
m_optgroup->show_field("host_type");
|
||||||
if (opt->value == htPrusaLink)
|
|
||||||
{
|
// hide PrusaConnect address
|
||||||
|
if (Field* printhost_field = m_optgroup->get_field("print_host"); printhost_field) {
|
||||||
|
if (wxTextCtrl* temp = dynamic_cast<wxTextCtrl*>(printhost_field->getWindow()); temp && temp->GetValue() == L"https://connect.prusa3d.com") {
|
||||||
|
temp->SetValue(wxString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (opt->value == htPrusaLink) { // PrusaConnect does NOT allow http digest
|
||||||
m_optgroup->show_field("printhost_authorization_type");
|
m_optgroup->show_field("printhost_authorization_type");
|
||||||
AuthorizationType auth_type = m_config->option<ConfigOptionEnum<AuthorizationType>>("printhost_authorization_type")->value;
|
AuthorizationType auth_type = m_config->option<ConfigOptionEnum<AuthorizationType>>("printhost_authorization_type")->value;
|
||||||
m_optgroup->show_field("printhost_apikey", auth_type == AuthorizationType::atKeyPassword);
|
m_optgroup->show_field("printhost_apikey", auth_type == AuthorizationType::atKeyPassword);
|
||||||
for (const char* opt_key : { "printhost_user", "printhost_password" })
|
for (const char* opt_key : { "printhost_user", "printhost_password" })
|
||||||
m_optgroup->show_field(opt_key, auth_type == AuthorizationType::atUserPassword);
|
m_optgroup->show_field(opt_key, auth_type == AuthorizationType::atUserPassword);
|
||||||
} else {
|
} else {
|
||||||
m_optgroup->hide_field("printhost_authorization_type");
|
m_optgroup->hide_field("printhost_authorization_type");
|
||||||
m_optgroup->show_field("printhost_apikey", true);
|
m_optgroup->show_field("printhost_apikey", true);
|
||||||
for (const std::string& opt_key : std::vector<std::string>{ "printhost_user", "printhost_password" })
|
for (const std::string& opt_key : std::vector<std::string>{ "printhost_user", "printhost_password" })
|
||||||
m_optgroup->hide_field(opt_key);
|
m_optgroup->hide_field(opt_key);
|
||||||
supports_multiple_printers = opt && opt->value == htRepetier;
|
supports_multiple_printers = opt && opt->value == htRepetier;
|
||||||
|
if (opt->value == htPrusaConnect) { // automatically show default prusaconnect address
|
||||||
|
if (Field* printhost_field = m_optgroup->get_field("print_host"); printhost_field) {
|
||||||
|
if (wxTextCtrl* temp = dynamic_cast<wxTextCtrl*>(printhost_field->getWindow()); temp && temp->GetValue().IsEmpty()) {
|
||||||
|
temp->SetValue(L"https://connect.prusa3d.com");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_optgroup->set_value("host_type", int(PrintHostType::htOctoPrint), false);
|
m_optgroup->set_value("host_type", int(PrintHostType::htOctoPrint), false);
|
||||||
|
@ -453,30 +465,23 @@ void PhysicalPrinterDialog::update_host_type(bool printer_change)
|
||||||
{
|
{
|
||||||
if (m_config == nullptr)
|
if (m_config == nullptr)
|
||||||
return;
|
return;
|
||||||
bool all_presets_are_from_mk3_family = false;
|
|
||||||
Field* ht = m_optgroup->get_field("host_type");
|
Field* ht = m_optgroup->get_field("host_type");
|
||||||
|
|
||||||
wxArrayString types;
|
wxArrayString types;
|
||||||
// Append localized enum_labels
|
int last_in_conf = m_config->option("host_type")->getInt(); // this is real position in last choice
|
||||||
assert(ht->m_opt.enum_labels.size() == ht->m_opt.enum_values.size());
|
|
||||||
for (size_t i = 0; i < ht->m_opt.enum_labels.size(); i++) {
|
|
||||||
if (ht->m_opt.enum_values[i] == "prusalink" && !all_presets_are_from_mk3_family)
|
|
||||||
continue;
|
|
||||||
types.Add(_(ht->m_opt.enum_labels[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
Choice* choice = dynamic_cast<Choice*>(ht);
|
Choice* choice = dynamic_cast<Choice*>(ht);
|
||||||
choice->set_values(types);
|
choice->set_values(types);
|
||||||
auto set_to_choice_and_config = [this, choice](PrintHostType type) {
|
int index_in_choice = (printer_change ? std::clamp(last_in_conf - ((int)ht->m_opt.enum_values.size() - (int)types.size()), 0, (int)ht->m_opt.enum_values.size() - 1) : last_in_conf);
|
||||||
choice->set_value(static_cast<int>(type));
|
choice->set_value(index_in_choice);
|
||||||
|
if ("prusalink" == ht->m_opt.enum_values.at(index_in_choice))
|
||||||
|
m_config->set_key_value("host_type", new ConfigOptionEnum<PrintHostType>(htPrusaLink));
|
||||||
|
else if ("prusaconnect" == ht->m_opt.enum_values.at(index_in_choice))
|
||||||
|
m_config->set_key_value("host_type", new ConfigOptionEnum<PrintHostType>(htPrusaConnect));
|
||||||
|
else {
|
||||||
|
int host_type = std::clamp(index_in_choice + ((int)ht->m_opt.enum_values.size() - (int)types.size()), 0, (int)ht->m_opt.enum_values.size() - 1);
|
||||||
|
PrintHostType type = static_cast<PrintHostType>(host_type);
|
||||||
m_config->set_key_value("host_type", new ConfigOptionEnum<PrintHostType>(type));
|
m_config->set_key_value("host_type", new ConfigOptionEnum<PrintHostType>(type));
|
||||||
};
|
}
|
||||||
if ((printer_change && all_presets_are_from_mk3_family) || all_presets_are_from_mk3_family)
|
|
||||||
set_to_choice_and_config(htPrusaLink);
|
|
||||||
else if ((printer_change && !all_presets_are_from_mk3_family) || (!all_presets_are_from_mk3_family && m_config->option<ConfigOptionEnum<PrintHostType>>("host_type")->value == htPrusaLink))
|
|
||||||
set_to_choice_and_config(htOctoPrint);
|
|
||||||
else
|
|
||||||
choice->set_value(m_config->option("host_type")->getInt());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicalPrinterDialog::update_printers()
|
void PhysicalPrinterDialog::update_printers()
|
||||||
|
|
|
@ -10966,42 +10966,34 @@ void Plater::send_gcode_legacy(int plate_idx, Export3mfProgressFn proFn)
|
||||||
upload_job.printhost->get_groups(groups);
|
upload_job.printhost->get_groups(groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
// orca merge todo
|
// PrusaLink specific: Query the server for the list of file groups.
|
||||||
PrintHostSendDialog dlg(default_output_file, upload_job.printhost->get_post_upload_actions(), groups);
|
wxArrayString storage_paths;
|
||||||
|
wxArrayString storage_names;
|
||||||
|
{
|
||||||
|
wxBusyCursor wait;
|
||||||
|
try {
|
||||||
|
upload_job.printhost->get_storage(storage_paths, storage_names);
|
||||||
|
} catch (const Slic3r::IOError& ex) {
|
||||||
|
show_error(this, ex.what(), false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintHostSendDialog dlg(default_output_file, upload_job.printhost->get_post_upload_actions(), groups, storage_paths, storage_names);
|
||||||
if (dlg.ShowModal() == wxID_OK) {
|
if (dlg.ShowModal() == wxID_OK) {
|
||||||
upload_job.upload_data.upload_path = dlg.filename();
|
upload_job.upload_data.upload_path = dlg.filename();
|
||||||
upload_job.upload_data.post_action = dlg.post_action();
|
upload_job.upload_data.post_action = dlg.post_action();
|
||||||
upload_job.upload_data.group = dlg.group();
|
upload_job.upload_data.group = dlg.group();
|
||||||
|
upload_job.upload_data.storage = dlg.storage();
|
||||||
|
|
||||||
|
// Show "Is printer clean" dialog for PrusaConnect - Upload and print.
|
||||||
|
if (std::string(upload_job.printhost->get_name()) == "PrusaConnect" && upload_job.upload_data.post_action == PrintHostPostUploadAction::StartPrint) {
|
||||||
|
GUI::MessageDialog dlg(nullptr, _L("Is the printer ready? Is the print sheet in place, empty and clean?"), _L("Upload and Print"), wxOK | wxCANCEL);
|
||||||
|
if (dlg.ShowModal() != wxID_OK)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
p->export_gcode(fs::path(), false, std::move(upload_job));
|
p->export_gcode(fs::path(), false, std::move(upload_job));
|
||||||
|
|
||||||
try {
|
|
||||||
json j;
|
|
||||||
switch (dlg.post_action()) {
|
|
||||||
case PrintHostPostUploadAction::None:
|
|
||||||
j["post_action"] = "Upload";
|
|
||||||
break;
|
|
||||||
case PrintHostPostUploadAction::StartPrint:
|
|
||||||
j["post_action"] = "StartPrint";
|
|
||||||
break;
|
|
||||||
case PrintHostPostUploadAction::StartSimulation:
|
|
||||||
j["post_action"] = "StartSimulation";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
|
|
||||||
if (preset_bundle) {
|
|
||||||
j["gcode_printer_model"] = preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (physical_printer_config) {
|
|
||||||
j["printer_preset"] = physical_printer_config->opt_string("inherits");
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkAgent *agent = wxGetApp().getAgent();
|
|
||||||
} catch (...) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int Plater::send_gcode(int plate_idx, Export3mfProgressFn proFn)
|
int Plater::send_gcode(int plate_idx, Export3mfProgressFn proFn)
|
||||||
|
|
|
@ -38,13 +38,14 @@ static const char *CONFIG_KEY_PATH = "printhost_path";
|
||||||
static const char *CONFIG_KEY_GROUP = "printhost_group";
|
static const char *CONFIG_KEY_GROUP = "printhost_group";
|
||||||
static const char* CONFIG_KEY_STORAGE = "printhost_storage";
|
static const char* CONFIG_KEY_STORAGE = "printhost_storage";
|
||||||
|
|
||||||
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUploadActions post_actions, const wxArrayString &groups, const wxArrayString& storage)
|
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUploadActions post_actions, const wxArrayString &groups, const wxArrayString& storage_paths, const wxArrayString& storage_names)
|
||||||
: MsgDialog(static_cast<wxWindow*>(wxGetApp().mainframe), _L("Send G-Code to printer host"), _L("Upload to Printer Host with the following filename:"), 0) // Set style = 0 to avoid default creation of the "OK" button.
|
: MsgDialog(static_cast<wxWindow*>(wxGetApp().mainframe), _L("Send G-Code to printer host"), _L("Upload to Printer Host with the following filename:"), 0) // Set style = 0 to avoid default creation of the "OK" button.
|
||||||
// All buttons will be added later in this constructor
|
// All buttons will be added later in this constructor
|
||||||
, txt_filename(new wxTextCtrl(this, wxID_ANY))
|
, txt_filename(new wxTextCtrl(this, wxID_ANY))
|
||||||
, combo_groups(!groups.IsEmpty() ? new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, groups, wxCB_READONLY) : nullptr)
|
, combo_groups(!groups.IsEmpty() ? new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, groups, wxCB_READONLY) : nullptr)
|
||||||
, combo_storage(storage.GetCount() > 1 ? new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, storage, wxCB_READONLY) : nullptr)
|
, combo_storage(storage_names.GetCount() > 1 ? new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, storage_names, wxCB_READONLY) : nullptr)
|
||||||
, post_upload_action(PrintHostPostUploadAction::None)
|
, post_upload_action(PrintHostPostUploadAction::None)
|
||||||
|
, m_paths(storage_paths)
|
||||||
{
|
{
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
txt_filename->OSXDisableAllSmartSubstitutions();
|
txt_filename->OSXDisableAllSmartSubstitutions();
|
||||||
|
@ -70,18 +71,18 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUplo
|
||||||
|
|
||||||
if (combo_storage != nullptr) {
|
if (combo_storage != nullptr) {
|
||||||
// PrusaLink specific: User needs to choose a storage
|
// PrusaLink specific: User needs to choose a storage
|
||||||
auto* label_group = new wxStaticText(this, wxID_ANY, _L("Upload to storage:"));
|
auto* label_group = new wxStaticText(this, wxID_ANY, _L("Upload to storage") + ":");
|
||||||
content_sizer->Add(label_group);
|
content_sizer->Add(label_group);
|
||||||
content_sizer->Add(combo_storage, 0, wxBOTTOM, 2 * VERT_SPACING);
|
content_sizer->Add(combo_storage, 0, wxBOTTOM, 2 * VERT_SPACING);
|
||||||
combo_storage->SetValue(storage.front());
|
combo_storage->SetValue(storage_names.front());
|
||||||
wxString recent_storage = from_u8(app_config->get("recent", CONFIG_KEY_STORAGE));
|
wxString recent_storage = from_u8(app_config->get("recent", CONFIG_KEY_STORAGE));
|
||||||
if (!recent_storage.empty())
|
if (!recent_storage.empty())
|
||||||
combo_storage->SetValue(recent_storage);
|
combo_storage->SetValue(recent_storage);
|
||||||
} else if (storage.GetCount() == 1){
|
} else if (storage_names.GetCount() == 1){
|
||||||
// PrusaLink specific: Show which storage has been detected.
|
// PrusaLink specific: Show which storage has been detected.
|
||||||
auto* label_group = new wxStaticText(this, wxID_ANY, _L("Upload to storage: ") + storage.front());
|
auto* label_group = new wxStaticText(this, wxID_ANY, _L("Upload to storage") + ": " + storage_names.front());
|
||||||
content_sizer->Add(label_group);
|
content_sizer->Add(label_group);
|
||||||
m_preselected_storage = storage.front();
|
m_preselected_storage = storage_paths.front();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -196,7 +197,9 @@ std::string PrintHostSendDialog::storage() const
|
||||||
{
|
{
|
||||||
if (!combo_storage)
|
if (!combo_storage)
|
||||||
return GUI::format("%1%", m_preselected_storage);
|
return GUI::format("%1%", m_preselected_storage);
|
||||||
return boost::nowide::narrow(combo_storage->GetValue());
|
if (combo_storage->GetSelection() < 0 || combo_storage->GetSelection() >= int(m_paths.size()))
|
||||||
|
return {};
|
||||||
|
return boost::nowide::narrow(m_paths[combo_storage->GetSelection()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintHostSendDialog::EndModal(int ret)
|
void PrintHostSendDialog::EndModal(int ret)
|
||||||
|
@ -226,8 +229,6 @@ void PrintHostSendDialog::EndModal(int ret)
|
||||||
MsgDialog::EndModal(ret);
|
MsgDialog::EndModal(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
wxDEFINE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event);
|
wxDEFINE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event);
|
||||||
wxDEFINE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event);
|
wxDEFINE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event);
|
||||||
wxDEFINE_EVENT(EVT_PRINTHOST_CANCEL, PrintHostQueueDialog::Event);
|
wxDEFINE_EVENT(EVT_PRINTHOST_CANCEL, PrintHostQueueDialog::Event);
|
||||||
|
@ -355,8 +356,6 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
|
||||||
if (selected == wxNOT_FOUND) { return; }
|
if (selected == wxNOT_FOUND) { return; }
|
||||||
GUI::show_error(nullptr, job_list->GetTextValue(selected, COL_ERRORMSG));
|
GUI::show_error(nullptr, job_list->GetTextValue(selected, COL_ERRORMSG));
|
||||||
});
|
});
|
||||||
|
|
||||||
wxGetApp().UpdateDlgDarkUI(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintHostQueueDialog::append_job(const PrintHostJob &job)
|
void PrintHostQueueDialog::append_job(const PrintHostJob &job)
|
||||||
|
@ -474,7 +473,7 @@ void PrintHostQueueDialog::on_error(Event &evt)
|
||||||
|
|
||||||
set_state(evt.job_id, ST_ERROR);
|
set_state(evt.job_id, ST_ERROR);
|
||||||
|
|
||||||
auto errormsg = from_u8((boost::format("%1%\n%2%") % _utf8(L("Error uploading to print host:")) % std::string(evt.status.ToUTF8())).str());
|
auto errormsg = format_wxstr("%1%\n%2%", _L("Error uploading to print host") + ":", evt.status);
|
||||||
job_list->SetValue(wxVariant(0), evt.job_id, COL_PROGRESS);
|
job_list->SetValue(wxVariant(0), evt.job_id, COL_PROGRESS);
|
||||||
job_list->SetValue(wxVariant(errormsg), evt.job_id, COL_ERRORMSG); // Stashes the error message into a hidden column for later
|
job_list->SetValue(wxVariant(errormsg), evt.job_id, COL_ERRORMSG); // Stashes the error message into a hidden column for later
|
||||||
|
|
||||||
|
@ -505,6 +504,7 @@ void PrintHostQueueDialog::on_cancel(Event &evt)
|
||||||
|
|
||||||
void PrintHostQueueDialog::on_info(Event& evt)
|
void PrintHostQueueDialog::on_info(Event& evt)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
wxCHECK_RET(evt.job_id < (size_t)job_list->GetItemCount(), "Out of bounds access to job list");
|
wxCHECK_RET(evt.job_id < (size_t)job_list->GetItemCount(), "Out of bounds access to job list");
|
||||||
|
|
||||||
if (evt.tag == L"resolve") {
|
if (evt.tag == L"resolve") {
|
||||||
|
@ -524,6 +524,7 @@ void PrintHostQueueDialog::on_info(Event& evt)
|
||||||
} else if (evt.tag == L"set_complete_off") {
|
} else if (evt.tag == L"set_complete_off") {
|
||||||
wxGetApp().notification_manager()->set_upload_job_notification_comp_on_100(evt.job_id + 1, false);
|
wxGetApp().notification_manager()->set_upload_job_notification_comp_on_100(evt.job_id + 1, false);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintHostQueueDialog::get_active_jobs(std::vector<std::pair<std::string, std::string>>& ret)
|
void PrintHostQueueDialog::get_active_jobs(std::vector<std::pair<std::string, std::string>>& ret)
|
||||||
|
@ -565,8 +566,11 @@ bool PrintHostQueueDialog::load_user_data(int udt, std::vector<int>& vector)
|
||||||
auto* app_config = wxGetApp().app_config;
|
auto* app_config = wxGetApp().app_config;
|
||||||
auto hasget = [app_config](const std::string& name, std::vector<int>& vector)->bool {
|
auto hasget = [app_config](const std::string& name, std::vector<int>& vector)->bool {
|
||||||
if (app_config->has(name)) {
|
if (app_config->has(name)) {
|
||||||
vector.push_back(std::stoi(app_config->get(name)));
|
std::string val = app_config->get(name);
|
||||||
return true;
|
if (!val.empty() || val[0]!='\0') {
|
||||||
|
vector.push_back(std::stoi(val));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace GUI {
|
||||||
class PrintHostSendDialog : public GUI::MsgDialog
|
class PrintHostSendDialog : public GUI::MsgDialog
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PrintHostSendDialog(const boost::filesystem::path &path, PrintHostPostUploadActions post_actions, const wxArrayString& groups, const wxArrayString& storage);
|
PrintHostSendDialog(const boost::filesystem::path &path, PrintHostPostUploadActions post_actions, const wxArrayString& groups, const wxArrayString& storage_paths, const wxArrayString& storage_names);
|
||||||
boost::filesystem::path filename() const;
|
boost::filesystem::path filename() const;
|
||||||
PrintHostPostUploadAction post_action() const;
|
PrintHostPostUploadAction post_action() const;
|
||||||
std::string group() const;
|
std::string group() const;
|
||||||
|
@ -40,6 +40,7 @@ private:
|
||||||
PrintHostPostUploadAction post_upload_action;
|
PrintHostPostUploadAction post_upload_action;
|
||||||
wxString m_valid_suffix;
|
wxString m_valid_suffix;
|
||||||
wxString m_preselected_storage;
|
wxString m_preselected_storage;
|
||||||
|
wxArrayString m_paths;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,12 +7,17 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <boost/asio/ip/address.hpp>
|
|
||||||
|
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/asio/ip/address.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
#include <boost/system/error_code.hpp>
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct BonjourReply
|
struct BonjourReply
|
||||||
{
|
{
|
||||||
typedef std::unordered_map<std::string, std::string> TxtData;
|
typedef std::unordered_map<std::string, std::string> TxtData;
|
||||||
|
@ -40,7 +45,6 @@ struct BonjourReply
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream &, const BonjourReply &);
|
std::ostream& operator<<(std::ostream &, const BonjourReply &);
|
||||||
|
|
||||||
|
|
||||||
/// Bonjour lookup performer
|
/// Bonjour lookup performer
|
||||||
class Bonjour : public std::enable_shared_from_this<Bonjour> {
|
class Bonjour : public std::enable_shared_from_this<Bonjour> {
|
||||||
private:
|
private:
|
||||||
|
@ -49,6 +53,7 @@ public:
|
||||||
typedef std::shared_ptr<Bonjour> Ptr;
|
typedef std::shared_ptr<Bonjour> Ptr;
|
||||||
typedef std::function<void(BonjourReply &&)> ReplyFn;
|
typedef std::function<void(BonjourReply &&)> ReplyFn;
|
||||||
typedef std::function<void()> CompleteFn;
|
typedef std::function<void()> CompleteFn;
|
||||||
|
typedef std::function<void(const std::vector<BonjourReply>&)> ResolveFn;
|
||||||
typedef std::set<std::string> TxtKeys;
|
typedef std::set<std::string> TxtKeys;
|
||||||
|
|
||||||
Bonjour(std::string service);
|
Bonjour(std::string service);
|
||||||
|
@ -65,15 +70,217 @@ public:
|
||||||
// ^ Note: By default there is 1 retry (meaning 1 broadcast is sent).
|
// ^ Note: By default there is 1 retry (meaning 1 broadcast is sent).
|
||||||
// Timeout is per one retry, ie. total time spent listening = retries * timeout.
|
// Timeout is per one retry, ie. total time spent listening = retries * timeout.
|
||||||
// If retries > 1, then care needs to be taken as more than one reply from the same service may be received.
|
// If retries > 1, then care needs to be taken as more than one reply from the same service may be received.
|
||||||
|
|
||||||
|
// sets hostname queried by resolve()
|
||||||
|
Bonjour& set_hostname(const std::string& hostname);
|
||||||
|
|
||||||
Bonjour& on_reply(ReplyFn fn);
|
Bonjour& on_reply(ReplyFn fn);
|
||||||
Bonjour& on_complete(CompleteFn fn);
|
Bonjour& on_complete(CompleteFn fn);
|
||||||
|
|
||||||
|
Bonjour& on_resolve(ResolveFn fn);
|
||||||
|
// lookup all devices by given TxtKeys
|
||||||
|
// each correct reply is passed back in ReplyFn, finishes with CompleteFn
|
||||||
Ptr lookup();
|
Ptr lookup();
|
||||||
|
// performs resolving of hostname into vector of ip adresses passed back by ResolveFn
|
||||||
|
// needs set_hostname and on_resolve to be called before.
|
||||||
|
Ptr resolve();
|
||||||
|
// resolve on the current thread
|
||||||
|
void resolve_sync();
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<priv> p;
|
std::unique_ptr<priv> p;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BonjourRequest
|
||||||
|
{
|
||||||
|
static const boost::asio::ip::address_v4 MCAST_IP4;
|
||||||
|
static const boost::asio::ip::address_v6 MCAST_IP6;
|
||||||
|
static const uint16_t MCAST_PORT;
|
||||||
|
|
||||||
|
std::vector<char> m_data;
|
||||||
|
|
||||||
|
static boost::optional<BonjourRequest> make_PTR(const std::string& service, const std::string& protocol);
|
||||||
|
static boost::optional<BonjourRequest> make_A(const std::string& hostname);
|
||||||
|
static boost::optional<BonjourRequest> make_AAAA(const std::string& hostname);
|
||||||
|
private:
|
||||||
|
BonjourRequest(std::vector<char>&& data) : m_data(std::move(data)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class LookupSocket;
|
||||||
|
class ResolveSocket;
|
||||||
|
|
||||||
|
// Session is created for each async_receive of socket. On receive, its handle_receive method is called (Thru io_service->post).
|
||||||
|
// ReplyFn is called if correct datagram was received.
|
||||||
|
class UdpSession
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UdpSession(Bonjour::ReplyFn rfn);
|
||||||
|
virtual void handle_receive(const boost::system::error_code& error, size_t bytes) = 0;
|
||||||
|
std::vector<char> buffer;
|
||||||
|
boost::asio::ip::udp::endpoint remote_endpoint;
|
||||||
|
protected:
|
||||||
|
Bonjour::ReplyFn replyfn;
|
||||||
|
};
|
||||||
|
typedef std::shared_ptr<UdpSession> SharedSession;
|
||||||
|
// Session for LookupSocket
|
||||||
|
class LookupSession : public UdpSession
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LookupSession(const LookupSocket* sckt, Bonjour::ReplyFn rfn) : UdpSession(rfn), socket(sckt) {}
|
||||||
|
void handle_receive(const boost::system::error_code& error, size_t bytes) override;
|
||||||
|
protected:
|
||||||
|
// const pointer to socket to get needed data as txt_keys etc.
|
||||||
|
const LookupSocket* socket;
|
||||||
|
};
|
||||||
|
// Session for ResolveSocket
|
||||||
|
class ResolveSession : public UdpSession
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ResolveSession(const ResolveSocket* sckt, Bonjour::ReplyFn rfn) : UdpSession(rfn), socket(sckt) {}
|
||||||
|
void handle_receive(const boost::system::error_code& error, size_t bytes) override;
|
||||||
|
protected:
|
||||||
|
// const pointer to seocket to get hostname during handle_receive
|
||||||
|
const ResolveSocket* socket;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Udp socket, starts receiving answers after first send() call until io_service is stopped.
|
||||||
|
class UdpSocket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Two constructors: 1st is with interface which must be resolved before calling this
|
||||||
|
UdpSocket(Bonjour::ReplyFn replyfn
|
||||||
|
, const boost::asio::ip::address& multicast_address
|
||||||
|
, const boost::asio::ip::address& interface_address
|
||||||
|
, std::shared_ptr< boost::asio::io_service > io_service);
|
||||||
|
|
||||||
|
UdpSocket(Bonjour::ReplyFn replyfn
|
||||||
|
, const boost::asio::ip::address& multicast_address
|
||||||
|
, std::shared_ptr< boost::asio::io_service > io_service);
|
||||||
|
|
||||||
|
void send();
|
||||||
|
void async_receive();
|
||||||
|
void cancel() { socket.cancel(); }
|
||||||
|
protected:
|
||||||
|
void receive_handler(SharedSession session, const boost::system::error_code& error, size_t bytes);
|
||||||
|
virtual SharedSession create_session() const = 0;
|
||||||
|
|
||||||
|
Bonjour::ReplyFn replyfn;
|
||||||
|
boost::asio::ip::address multicast_address;
|
||||||
|
boost::asio::ip::udp::socket socket;
|
||||||
|
boost::asio::ip::udp::endpoint mcast_endpoint;
|
||||||
|
std::shared_ptr< boost::asio::io_service > io_service;
|
||||||
|
std::vector<BonjourRequest> requests;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LookupSocket : public UdpSocket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LookupSocket(Bonjour::TxtKeys txt_keys
|
||||||
|
, std::string service
|
||||||
|
, std::string service_dn
|
||||||
|
, std::string protocol
|
||||||
|
, Bonjour::ReplyFn replyfn
|
||||||
|
, const boost::asio::ip::address& multicast_address
|
||||||
|
, const boost::asio::ip::address& interface_address
|
||||||
|
, std::shared_ptr< boost::asio::io_service > io_service)
|
||||||
|
: UdpSocket(replyfn, multicast_address, interface_address, io_service)
|
||||||
|
, txt_keys(txt_keys)
|
||||||
|
, service(service)
|
||||||
|
, service_dn(service_dn)
|
||||||
|
, protocol(protocol)
|
||||||
|
{
|
||||||
|
assert(!service.empty() && replyfn);
|
||||||
|
create_request();
|
||||||
|
}
|
||||||
|
|
||||||
|
LookupSocket(Bonjour::TxtKeys txt_keys
|
||||||
|
, std::string service
|
||||||
|
, std::string service_dn
|
||||||
|
, std::string protocol
|
||||||
|
, Bonjour::ReplyFn replyfn
|
||||||
|
, const boost::asio::ip::address& multicast_address
|
||||||
|
, std::shared_ptr< boost::asio::io_service > io_service)
|
||||||
|
: UdpSocket(replyfn, multicast_address, io_service)
|
||||||
|
, txt_keys(txt_keys)
|
||||||
|
, service(service)
|
||||||
|
, service_dn(service_dn)
|
||||||
|
, protocol(protocol)
|
||||||
|
{
|
||||||
|
assert(!service.empty() && replyfn);
|
||||||
|
create_request();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Bonjour::TxtKeys get_txt_keys() const { return txt_keys; }
|
||||||
|
const std::string get_service() const { return service; }
|
||||||
|
const std::string get_service_dn() const { return service_dn; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
SharedSession create_session() const override;
|
||||||
|
void create_request()
|
||||||
|
{
|
||||||
|
requests.clear();
|
||||||
|
// create PTR request
|
||||||
|
if (auto rqst = BonjourRequest::make_PTR(service, protocol); rqst)
|
||||||
|
requests.push_back(std::move(rqst.get()));
|
||||||
|
}
|
||||||
|
boost::optional<BonjourRequest> request;
|
||||||
|
Bonjour::TxtKeys txt_keys;
|
||||||
|
std::string service;
|
||||||
|
std::string service_dn;
|
||||||
|
std::string protocol;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ResolveSocket : public UdpSocket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ResolveSocket(const std::string& hostname
|
||||||
|
, Bonjour::ReplyFn replyfn
|
||||||
|
, const boost::asio::ip::address& multicast_address
|
||||||
|
, const boost::asio::ip::address& interface_address
|
||||||
|
, std::shared_ptr< boost::asio::io_service > io_service)
|
||||||
|
: UdpSocket(replyfn, multicast_address, interface_address, io_service)
|
||||||
|
, hostname(hostname)
|
||||||
|
|
||||||
|
{
|
||||||
|
assert(!hostname.empty() && replyfn);
|
||||||
|
create_requests();
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveSocket(const std::string& hostname
|
||||||
|
, Bonjour::ReplyFn replyfn
|
||||||
|
, const boost::asio::ip::address& multicast_address
|
||||||
|
, std::shared_ptr< boost::asio::io_service > io_service)
|
||||||
|
: UdpSocket(replyfn, multicast_address, io_service)
|
||||||
|
, hostname(hostname)
|
||||||
|
|
||||||
|
{
|
||||||
|
assert(!hostname.empty() && replyfn);
|
||||||
|
create_requests();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_hostname() const { return hostname; }
|
||||||
|
protected:
|
||||||
|
SharedSession create_session() const override;
|
||||||
|
void create_requests()
|
||||||
|
{
|
||||||
|
requests.clear();
|
||||||
|
// BonjourRequest::make_A / AAAA is now implemented to add .local correctly after the hostname.
|
||||||
|
// If that is unsufficient, we need to change make_A / AAAA and pass full hostname.
|
||||||
|
std::string trimmed_hostname = hostname;
|
||||||
|
if (size_t dot_pos = trimmed_hostname.find_first_of('.'); dot_pos != std::string::npos)
|
||||||
|
trimmed_hostname = trimmed_hostname.substr(0, dot_pos);
|
||||||
|
if (auto rqst = BonjourRequest::make_A(trimmed_hostname); rqst)
|
||||||
|
requests.push_back(std::move(rqst.get()));
|
||||||
|
|
||||||
|
trimmed_hostname = hostname;
|
||||||
|
if (size_t dot_pos = trimmed_hostname.find_first_of('.'); dot_pos != std::string::npos)
|
||||||
|
trimmed_hostname = trimmed_hostname.substr(0, dot_pos);
|
||||||
|
if (auto rqst = BonjourRequest::make_AAAA(trimmed_hostname); rqst)
|
||||||
|
requests.push_back(std::move(rqst.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string hostname;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,38 @@ namespace Slic3r {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
|
std::string get_host_from_url(const std::string& url_in)
|
||||||
|
{
|
||||||
|
std::string url = url_in;
|
||||||
|
// add http:// if there is no scheme
|
||||||
|
size_t double_slash = url.find("//");
|
||||||
|
if (double_slash == std::string::npos)
|
||||||
|
url = "http://" + url;
|
||||||
|
std::string out = url;
|
||||||
|
CURLU* hurl = curl_url();
|
||||||
|
if (hurl) {
|
||||||
|
// Parse the input URL.
|
||||||
|
CURLUcode rc = curl_url_set(hurl, CURLUPART_URL, url.c_str(), 0);
|
||||||
|
if (rc == CURLUE_OK) {
|
||||||
|
// Replace the address.
|
||||||
|
char* host;
|
||||||
|
rc = curl_url_get(hurl, CURLUPART_HOST, &host, 0);
|
||||||
|
if (rc == CURLUE_OK) {
|
||||||
|
out = host;
|
||||||
|
curl_free(host);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to get host form URL " << url;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to parse URL " << url;
|
||||||
|
curl_url_cleanup(hurl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to allocate curl_url";
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
|
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
|
||||||
std::string substitute_host(const std::string& orig_addr, std::string sub_addr)
|
std::string substitute_host(const std::string& orig_addr, std::string sub_addr)
|
||||||
{
|
{
|
||||||
|
@ -96,38 +128,6 @@ std::string substitute_host(const std::string& orig_addr, std::string sub_addr)
|
||||||
return out;
|
return out;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_host_from_url(const std::string& url_in)
|
|
||||||
{
|
|
||||||
std::string url = url_in;
|
|
||||||
// add http:// if there is no scheme
|
|
||||||
size_t double_slash = url.find("//");
|
|
||||||
if (double_slash == std::string::npos)
|
|
||||||
url = "http://" + url;
|
|
||||||
std::string out = url;
|
|
||||||
CURLU* hurl = curl_url();
|
|
||||||
if (hurl) {
|
|
||||||
// Parse the input URL.
|
|
||||||
CURLUcode rc = curl_url_set(hurl, CURLUPART_URL, url.c_str(), 0);
|
|
||||||
if (rc == CURLUE_OK) {
|
|
||||||
// Replace the address.
|
|
||||||
char* host;
|
|
||||||
rc = curl_url_get(hurl, CURLUPART_HOST, &host, 0);
|
|
||||||
if (rc == CURLUE_OK) {
|
|
||||||
out = host;
|
|
||||||
curl_free(host);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to get host form URL " << url;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to parse URL " << url;
|
|
||||||
curl_url_cleanup(hurl);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to allocate curl_url";
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
#endif // WIN32
|
#endif // WIN32
|
||||||
std::string escape_string(const std::string& unescaped)
|
std::string escape_string(const std::string& unescaped)
|
||||||
{
|
{
|
||||||
|
@ -170,7 +170,7 @@ const char* OctoPrint::get_name() const { return "OctoPrint"; }
|
||||||
bool OctoPrint::test_with_resolved_ip(wxString &msg) const
|
bool OctoPrint::test_with_resolved_ip(wxString &msg) const
|
||||||
{
|
{
|
||||||
// Since the request is performed synchronously here,
|
// Since the request is performed synchronously here,
|
||||||
// it is ok to refer to `msg` from within the closure
|
// it is ok to refer to `msg` from within the closure
|
||||||
const char* name = get_name();
|
const char* name = get_name();
|
||||||
bool res = true;
|
bool res = true;
|
||||||
// Msg contains ip string.
|
// Msg contains ip string.
|
||||||
|
@ -179,7 +179,15 @@ bool OctoPrint::test_with_resolved_ip(wxString &msg) const
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
|
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
|
||||||
|
|
||||||
|
std::string host = get_host_from_url(m_host);
|
||||||
auto http = Http::get(url);//std::move(url));
|
auto http = Http::get(url);//std::move(url));
|
||||||
|
// "Host" header is necessary here. We have resolved IP address and subsituted it into "url" variable.
|
||||||
|
// And when creating Http object above, libcurl automatically includes "Host" header from address it got.
|
||||||
|
// Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back.
|
||||||
|
// Not changing the host would work on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734).
|
||||||
|
// Also when allow_ip_resolve = 0, this is not needed, but it should not break anything if it stays.
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc7230#section-5.4
|
||||||
|
http.header("Host", host);
|
||||||
set_auth(http);
|
set_auth(http);
|
||||||
http
|
http
|
||||||
.on_error([&](std::string body, std::string error, unsigned status) {
|
.on_error([&](std::string body, std::string error, unsigned status) {
|
||||||
|
@ -203,7 +211,7 @@ bool OctoPrint::test_with_resolved_ip(wxString &msg) const
|
||||||
const auto text = ptree.get_optional<std::string>("text");
|
const auto text = ptree.get_optional<std::string>("text");
|
||||||
res = validate_version_text(text);
|
res = validate_version_text(text);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "OctoPrint")).str());
|
msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const std::exception&) {
|
catch (const std::exception&) {
|
||||||
|
@ -228,7 +236,7 @@ bool OctoPrint::test(wxString& msg) const
|
||||||
auto url = make_url("api/version");
|
auto url = make_url("api/version");
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
|
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
|
||||||
|
// Here we do not have to add custom "Host" header - the url contains host filled by user and libCurl will set the header by itself.
|
||||||
auto http = Http::get(std::move(url));
|
auto http = Http::get(std::move(url));
|
||||||
set_auth(http);
|
set_auth(http);
|
||||||
http.on_error([&](std::string body, std::string error, unsigned status) {
|
http.on_error([&](std::string body, std::string error, unsigned status) {
|
||||||
|
@ -252,7 +260,7 @@ bool OctoPrint::test(wxString& msg) const
|
||||||
const auto text = ptree.get_optional<std::string>("text");
|
const auto text = ptree.get_optional<std::string>("text");
|
||||||
res = validate_version_text(text);
|
res = validate_version_text(text);
|
||||||
if (! res) {
|
if (! res) {
|
||||||
msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "OctoPrint")).str());
|
msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const std::exception &) {
|
catch (const std::exception &) {
|
||||||
|
@ -273,7 +281,6 @@ bool OctoPrint::test(wxString& msg) const
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
wxString OctoPrint::get_test_ok_msg () const
|
wxString OctoPrint::get_test_ok_msg () const
|
||||||
{
|
{
|
||||||
return _(L("Connection to OctoPrint works correctly."));
|
return _(L("Connection to OctoPrint works correctly."));
|
||||||
|
@ -281,10 +288,10 @@ wxString OctoPrint::get_test_ok_msg () const
|
||||||
|
|
||||||
wxString OctoPrint::get_test_failed_msg (wxString &msg) const
|
wxString OctoPrint::get_test_failed_msg (wxString &msg) const
|
||||||
{
|
{
|
||||||
return GUI::from_u8((boost::format("%s: %s\n\n%s")
|
return GUI::format_wxstr("%s: %s\n\n%s"
|
||||||
% _utf8(L("Could not connect to OctoPrint"))
|
, _L("Could not connect to OctoPrint")
|
||||||
% std::string(msg.ToUTF8())
|
, msg
|
||||||
% _utf8(L("Note: OctoPrint version at least 1.1.0 is required."))).str());
|
, _L("Note: OctoPrint version at least 1.1.0 is required."));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
|
bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
|
||||||
|
@ -300,7 +307,7 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro
|
||||||
boost::asio::ip::address host_ip = boost::asio::ip::make_address(host, ec);
|
boost::asio::ip::address host_ip = boost::asio::ip::make_address(host, ec);
|
||||||
if (!ec) {
|
if (!ec) {
|
||||||
resolved_addr.push_back(host_ip);
|
resolved_addr.push_back(host_ip);
|
||||||
} else if ( GUI::get_app_config()->get("allow_ip_resolve") == "1" && boost::algorithm::ends_with(host, ".local")){
|
} else if ( GUI::get_app_config()->get_bool("allow_ip_resolve") && boost::algorithm::ends_with(host, ".local")){
|
||||||
Bonjour("octoprint")
|
Bonjour("octoprint")
|
||||||
.set_hostname(host)
|
.set_hostname(host)
|
||||||
.set_retries(5) // number of rounds of queries send
|
.set_retries(5) // number of rounds of queries send
|
||||||
|
@ -340,7 +347,7 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// There are multiple addresses - user needs to choose which to use.
|
// There are multiple addresses - user needs to choose which to use.
|
||||||
size_t selected_index = resolved_addr.size();
|
size_t selected_index = resolved_addr.size();
|
||||||
IPListDialog dialog(nullptr, boost::nowide::widen(m_host), resolved_addr, selected_index);
|
IPListDialog dialog(nullptr, boost::nowide::widen(m_host), resolved_addr, selected_index);
|
||||||
if (dialog.ShowModal() == wxID_OK && selected_index < resolved_addr.size()) {
|
if (dialog.ShowModal() == wxID_OK && selected_index < resolved_addr.size()) {
|
||||||
return upload_inner_with_resolved_ip(std::move(upload_data), prorgess_fn, error_fn, info_fn, resolved_addr[selected_index]);
|
return upload_inner_with_resolved_ip(std::move(upload_data), prorgess_fn, error_fn, info_fn, resolved_addr[selected_index]);
|
||||||
|
@ -379,7 +386,14 @@ bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, Progr
|
||||||
% upload_parent_path.string()
|
% upload_parent_path.string()
|
||||||
% (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false");
|
% (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false");
|
||||||
|
|
||||||
|
std::string host = get_host_from_url(m_host);
|
||||||
auto http = Http::post(url);//std::move(url));
|
auto http = Http::post(url);//std::move(url));
|
||||||
|
// "Host" header is necessary here. We have resolved IP address and subsituted it into "url" variable.
|
||||||
|
// And when creating Http object above, libcurl automatically includes "Host" header from address it got.
|
||||||
|
// Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back.
|
||||||
|
// Not changing the host would work on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734).
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc7230#section-5.4
|
||||||
|
http.header("Host", host);
|
||||||
set_auth(http);
|
set_auth(http);
|
||||||
http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false")
|
http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false")
|
||||||
.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
|
.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
|
||||||
|
@ -397,7 +411,7 @@ bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, Progr
|
||||||
prorgess_fn(std::move(progress), cancel);
|
prorgess_fn(std::move(progress), cancel);
|
||||||
if (cancel) {
|
if (cancel) {
|
||||||
// Upload was canceled
|
// Upload was canceled
|
||||||
BOOST_LOG_TRIVIAL(info) << "Octoprint: Upload canceled";
|
BOOST_LOG_TRIVIAL(info) << name << ": Upload canceled";
|
||||||
result = false;
|
result = false;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -428,7 +442,7 @@ bool OctoPrint::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn p
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
|
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
|
||||||
if (m_host.find("https://") == 0 || test_msg_or_host_ip.empty() || GUI::get_app_config()->get("allow_ip_resolve") != "1")
|
if (m_host.find("https://") == 0 || test_msg_or_host_ip.empty() || !GUI::get_app_config()->get_bool("allow_ip_resolve"))
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
{
|
{
|
||||||
// If https is entered we assume signed ceritificate is being used
|
// If https is entered we assume signed ceritificate is being used
|
||||||
|
@ -458,6 +472,16 @@ bool OctoPrint::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn p
|
||||||
% (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false");
|
% (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false");
|
||||||
|
|
||||||
auto http = Http::post(std::move(url));
|
auto http = Http::post(std::move(url));
|
||||||
|
#ifdef WIN32
|
||||||
|
// "Host" header is necessary here. In the workaround above (two mDNS..) we have got IP address from test connection and subsituted it into "url" variable.
|
||||||
|
// And when creating Http object above, libcurl automatically includes "Host" header from address it got.
|
||||||
|
// Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back.
|
||||||
|
// Not changing the host would work on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734).
|
||||||
|
// Also when allow_ip_resolve = 0, this is not needed, but it should not break anything if it stays.
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc7230#section-5.4
|
||||||
|
std::string host = get_host_from_url(m_host);
|
||||||
|
http.header("Host", host);
|
||||||
|
#endif // _WIN32
|
||||||
set_auth(http);
|
set_auth(http);
|
||||||
http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false")
|
http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false")
|
||||||
.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
|
.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
|
||||||
|
@ -474,7 +498,7 @@ bool OctoPrint::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn p
|
||||||
prorgess_fn(std::move(progress), cancel);
|
prorgess_fn(std::move(progress), cancel);
|
||||||
if (cancel) {
|
if (cancel) {
|
||||||
// Upload was canceled
|
// Upload was canceled
|
||||||
BOOST_LOG_TRIVIAL(info) << "Octoprint: Upload canceled";
|
BOOST_LOG_TRIVIAL(info) << name << ": Upload canceled";
|
||||||
res = false;
|
res = false;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -513,11 +537,8 @@ std::string OctoPrint::make_url(const std::string &path) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SL1Host::SL1Host(DynamicPrintConfig *config) :
|
SL1Host::SL1Host(DynamicPrintConfig *config)
|
||||||
OctoPrint(config),
|
: PrusaLink(config)
|
||||||
m_authorization_type(dynamic_cast<const ConfigOptionEnum<AuthorizationType>*>(config->option("printhost_authorization_type"))->value),
|
|
||||||
m_username(config->opt_string("printhost_user")),
|
|
||||||
m_password(config->opt_string("printhost_password"))
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,9 +552,7 @@ wxString SL1Host::get_test_ok_msg () const
|
||||||
|
|
||||||
wxString SL1Host::get_test_failed_msg (wxString &msg) const
|
wxString SL1Host::get_test_failed_msg (wxString &msg) const
|
||||||
{
|
{
|
||||||
return GUI::from_u8((boost::format("%s: %s")
|
return GUI::format_wxstr("%s: %s", _L("Could not connect to Prusa SLA"), msg);
|
||||||
% _utf8(L("Could not connect to Prusa SLA"))
|
|
||||||
% std::string(msg.ToUTF8())).str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SL1Host::validate_version_text(const boost::optional<std::string> &version_text) const
|
bool SL1Host::validate_version_text(const boost::optional<std::string> &version_text) const
|
||||||
|
@ -541,26 +560,10 @@ bool SL1Host::validate_version_text(const boost::optional<std::string> &version_
|
||||||
return version_text ? boost::starts_with(*version_text, "Prusa SLA") : false;
|
return version_text ? boost::starts_with(*version_text, "Prusa SLA") : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SL1Host::set_auth(Http &http) const
|
|
||||||
{
|
|
||||||
switch (m_authorization_type) {
|
|
||||||
case atKeyPassword:
|
|
||||||
http.header("X-Api-Key", get_apikey());
|
|
||||||
break;
|
|
||||||
case atUserPassword:
|
|
||||||
http.auth_digest(m_username, m_password);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! get_cafile().empty()) {
|
|
||||||
http.ca_file(get_cafile());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrusaLink
|
// PrusaLink
|
||||||
PrusaLink::PrusaLink(DynamicPrintConfig* config, bool show_after_message) :
|
PrusaLink::PrusaLink(DynamicPrintConfig* config, bool show_after_message) :
|
||||||
OctoPrint(config),
|
OctoPrint(config),
|
||||||
m_authorization_type(dynamic_cast<const ConfigOptionEnum<AuthorizationType>*>(config->option("printhost_authorization_type"))->value),
|
m_authorization_type(config->option<ConfigOptionEnum<AuthorizationType>>("printhost_authorization_type")->value),
|
||||||
m_username(config->opt_string("printhost_user")),
|
m_username(config->opt_string("printhost_user")),
|
||||||
m_password(config->opt_string("printhost_password")),
|
m_password(config->opt_string("printhost_password")),
|
||||||
m_show_after_message(show_after_message)
|
m_show_after_message(show_after_message)
|
||||||
|
@ -576,9 +579,7 @@ wxString PrusaLink::get_test_ok_msg() const
|
||||||
|
|
||||||
wxString PrusaLink::get_test_failed_msg(wxString& msg) const
|
wxString PrusaLink::get_test_failed_msg(wxString& msg) const
|
||||||
{
|
{
|
||||||
return GUI::from_u8((boost::format("%s: %s")
|
return GUI::format_wxstr("%s: %s", _L("Could not connect to PrusaLink"), msg);
|
||||||
% _utf8(L("Could not connect to PrusaLink"))
|
|
||||||
% std::string(msg.ToUTF8())).str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PrusaLink::validate_version_text(const boost::optional<std::string>& version_text) const
|
bool PrusaLink::validate_version_text(const boost::optional<std::string>& version_text) const
|
||||||
|
@ -664,7 +665,7 @@ bool PrusaLink::test(wxString& msg) const
|
||||||
const auto text = ptree.get_optional<std::string>("text");
|
const auto text = ptree.get_optional<std::string>("text");
|
||||||
res = validate_version_text(text);
|
res = validate_version_text(text);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "OctoPrint")).str());
|
msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : "OctoPrint"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const std::exception&) {
|
catch (const std::exception&) {
|
||||||
|
@ -685,7 +686,7 @@ bool PrusaLink::test(wxString& msg) const
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PrusaLink::get_storage(wxArrayString& output) const
|
bool PrusaLink::get_storage(wxArrayString& storage_path, wxArrayString& storage_name) const
|
||||||
{
|
{
|
||||||
const char* name = get_name();
|
const char* name = get_name();
|
||||||
|
|
||||||
|
@ -693,17 +694,22 @@ bool PrusaLink::get_storage(wxArrayString& output) const
|
||||||
auto url = make_url("api/v1/storage");
|
auto url = make_url("api/v1/storage");
|
||||||
wxString error_msg;
|
wxString error_msg;
|
||||||
|
|
||||||
struct StorageInfo{
|
struct StorageInfo {
|
||||||
|
wxString path;
|
||||||
wxString name;
|
wxString name;
|
||||||
bool read_only;
|
bool read_only = false;
|
||||||
long long free_space;
|
long long free_space = -1;
|
||||||
};
|
};
|
||||||
std::vector<StorageInfo> storage;
|
std::vector<StorageInfo> storage;
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get storage at: %2%") % name % url;
|
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get storage at: %2%") % name % url;
|
||||||
|
|
||||||
|
wxString wlang = GUI::wxGetApp().current_language_code();
|
||||||
|
std::string lang = GUI::format(wlang.SubString(0, 1));
|
||||||
|
|
||||||
auto http = Http::get(std::move(url));
|
auto http = Http::get(std::move(url));
|
||||||
set_auth(http);
|
set_auth(http);
|
||||||
|
http.header("Accept-Language", lang);
|
||||||
http.on_error([&](std::string body, std::string error, unsigned status) {
|
http.on_error([&](std::string body, std::string error, unsigned status) {
|
||||||
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting storage: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
|
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting storage: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
|
||||||
error_msg = L"\n\n" + boost::nowide::widen(error);
|
error_msg = L"\n\n" + boost::nowide::widen(error);
|
||||||
|
@ -716,7 +722,7 @@ bool PrusaLink::get_storage(wxArrayString& output) const
|
||||||
res = true;
|
res = true;
|
||||||
|
|
||||||
})
|
})
|
||||||
.on_complete([&, this](std::string body, unsigned) {
|
.on_complete([&](std::string body, unsigned) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got storage: %2%") % name % body;
|
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got storage: %2%") % name % body;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -731,14 +737,19 @@ bool PrusaLink::get_storage(wxArrayString& output) const
|
||||||
}
|
}
|
||||||
// each storage has own subtree of storage_list
|
// each storage has own subtree of storage_list
|
||||||
for (const auto& section : ptree.front().second) {
|
for (const auto& section : ptree.front().second) {
|
||||||
|
const auto name = section.second.get_optional<std::string>("name");
|
||||||
const auto path = section.second.get_optional<std::string>("path");
|
const auto path = section.second.get_optional<std::string>("path");
|
||||||
const auto space = section.second.get_optional<std::string>("free_space");
|
const auto space = section.second.get_optional<std::string>("free_space");
|
||||||
const auto read_only = section.second.get_optional<bool>("read_only");
|
const auto read_only = section.second.get_optional<bool>("read_only");
|
||||||
|
const auto ro = section.second.get_optional<bool>("ro"); // In PrusaLink 0.7.0RC2 "read_only" value is stored under "ro".
|
||||||
const auto available = section.second.get_optional<bool>("available");
|
const auto available = section.second.get_optional<bool>("available");
|
||||||
if (path && (!available || *available)) {
|
if (path && (!available || *available)) {
|
||||||
StorageInfo si;
|
StorageInfo si;
|
||||||
si.name = boost::nowide::widen(*path);
|
si.path = boost::nowide::widen(*path);
|
||||||
si.read_only = read_only ? *read_only : false; // If read_only is missing, assume it is NOT read only.
|
si.name = name ? boost::nowide::widen(*name) : wxString();
|
||||||
|
// If read_only is missing, assume it is NOT read only.
|
||||||
|
// si.read_only = read_only ? *read_only : false; // version without "ro"
|
||||||
|
si.read_only = (read_only ? *read_only : (ro ? *ro : false));
|
||||||
si.free_space = space ? std::stoll(*space) : 1; // If free_space is missing, assume there is free space.
|
si.free_space = space ? std::stoll(*space) : 1; // If free_space is missing, assume there is free space.
|
||||||
storage.emplace_back(std::move(si));
|
storage.emplace_back(std::move(si));
|
||||||
}
|
}
|
||||||
|
@ -756,19 +767,25 @@ bool PrusaLink::get_storage(wxArrayString& output) const
|
||||||
.perform_sync();
|
.perform_sync();
|
||||||
|
|
||||||
for (const auto& si : storage) {
|
for (const auto& si : storage) {
|
||||||
if (!si.read_only && si.free_space > 0)
|
if (!si.read_only && si.free_space > 0) {
|
||||||
output.push_back(si.name);
|
storage_path.push_back(si.path);
|
||||||
|
storage_name.push_back(si.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res && output.empty())
|
if (res && storage_path.empty()) {
|
||||||
{
|
|
||||||
if (!storage.empty()) { // otherwise error_msg is already filled
|
if (!storage.empty()) { // otherwise error_msg is already filled
|
||||||
error_msg = L"\n\n" + _L("Storages found:") + L" \n";
|
error_msg = L"\n\n" + _L("Storages found") + L": \n";
|
||||||
for (const auto& si : storage) {
|
for (const auto& si : storage) {
|
||||||
error_msg += si.name + L" : " + (si.read_only ? _L("read only") : _L("no free space")) + L"\n";
|
error_msg += GUI::format_wxstr(si.read_only ?
|
||||||
|
// TRN %1% = storage path
|
||||||
|
_L("%1% : read only") :
|
||||||
|
// TRN %1% = storage path
|
||||||
|
_L("%1% : no free space"), si.path) + L"\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::string message = GUI::format(_L("Upload has failed. There is no suitable storage found at %1%.%2%"), m_host, error_msg);
|
// TRN %1% = host
|
||||||
|
std::string message = GUI::format(_L("Upload has failed. There is no suitable storage found at %1%."), m_host) + GUI::into_u8(error_msg);
|
||||||
BOOST_LOG_TRIVIAL(error) << message;
|
BOOST_LOG_TRIVIAL(error) << message;
|
||||||
throw Slic3r::IOError(message);
|
throw Slic3r::IOError(message);
|
||||||
}
|
}
|
||||||
|
@ -787,7 +804,7 @@ bool PrusaLink::test_with_method_check(wxString& msg, bool& use_put) const
|
||||||
auto url = make_url("api/version");
|
auto url = make_url("api/version");
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
|
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
|
||||||
|
// Here we do not have to add custom "Host" header - the url contains host filled by user and libCurl will set the header by itself.
|
||||||
auto http = Http::get(std::move(url));
|
auto http = Http::get(std::move(url));
|
||||||
set_auth(http);
|
set_auth(http);
|
||||||
http.on_error([&](std::string body, std::string error, unsigned status) {
|
http.on_error([&](std::string body, std::string error, unsigned status) {
|
||||||
|
@ -811,7 +828,7 @@ bool PrusaLink::test_with_method_check(wxString& msg, bool& use_put) const
|
||||||
const auto text = ptree.get_optional<std::string>("text");
|
const auto text = ptree.get_optional<std::string>("text");
|
||||||
res = validate_version_text(text);
|
res = validate_version_text(text);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "OctoPrint")).str());
|
msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : "OctoPrint"));
|
||||||
use_put = false;
|
use_put = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -860,7 +877,15 @@ bool PrusaLink::test_with_resolved_ip_and_method_check(wxString& msg, bool& use_
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
|
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
|
||||||
|
|
||||||
|
std::string host = get_host_from_url(m_host);
|
||||||
auto http = Http::get(url);//std::move(url));
|
auto http = Http::get(url);//std::move(url));
|
||||||
|
// "Host" header is necessary here. We have resolved IP address and subsituted it into "url" variable.
|
||||||
|
// And when creating Http object above, libcurl automatically includes "Host" header from address it got.
|
||||||
|
// Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back.
|
||||||
|
// Not changing the host would work on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734).
|
||||||
|
// Also when allow_ip_resolve = 0, this is not needed, but it should not break anything if it stays.
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc7230#section-5.4
|
||||||
|
http.header("Host", host);
|
||||||
set_auth(http);
|
set_auth(http);
|
||||||
http
|
http
|
||||||
.on_error([&](std::string body, std::string error, unsigned status) {
|
.on_error([&](std::string body, std::string error, unsigned status) {
|
||||||
|
@ -884,7 +909,7 @@ bool PrusaLink::test_with_resolved_ip_and_method_check(wxString& msg, bool& use_
|
||||||
const auto text = ptree.get_optional<std::string>("text");
|
const auto text = ptree.get_optional<std::string>("text");
|
||||||
res = validate_version_text(text);
|
res = validate_version_text(text);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "OctoPrint")).str());
|
msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : "OctoPrint"));
|
||||||
use_put = false;
|
use_put = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -968,12 +993,11 @@ bool PrusaLink::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn p
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string url;
|
std::string url;
|
||||||
bool res = true;
|
|
||||||
std::string storage_path = (use_put ? "api/v1/files" : "api/files");
|
std::string storage_path = (use_put ? "api/v1/files" : "api/files");
|
||||||
storage_path += (upload_data.storage.empty() ? "/local" : upload_data.storage);
|
storage_path += (upload_data.storage.empty() ? "/local" : upload_data.storage);
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
|
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
|
||||||
if (m_host.find("https://") == 0 || test_msg_or_host_ip.empty() || GUI::get_app_config()->get("allow_ip_resolve") != "1")
|
if (m_host.find("https://") == 0 || test_msg_or_host_ip.empty() || !GUI::get_app_config()->get_bool("allow_ip_resolve"))
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
{
|
{
|
||||||
// If https is entered we assume signed ceritificate is being used
|
// If https is entered we assume signed ceritificate is being used
|
||||||
|
@ -1015,13 +1039,20 @@ bool PrusaLink::put_inner(PrintHostUpload upload_data, std::string url, const st
|
||||||
bool res = true;
|
bool res = true;
|
||||||
// Percent escape all filenames in on path and add it to the url. This is different from POST.
|
// Percent escape all filenames in on path and add it to the url. This is different from POST.
|
||||||
url += "/" + escape_path_by_element(upload_data.upload_path);
|
url += "/" + escape_path_by_element(upload_data.upload_path);
|
||||||
|
|
||||||
Http http = Http::put(std::move(url));
|
Http http = Http::put(std::move(url));
|
||||||
|
#ifdef WIN32
|
||||||
|
// "Host" header is necessary here. We have resolved IP address and subsituted it into "url" variable.
|
||||||
|
// And when creating Http object above, libcurl automatically includes "Host" header from address it got.
|
||||||
|
// Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back.
|
||||||
|
// Not changing the host would work on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734).
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc7230#section-5.4
|
||||||
|
std::string host = get_host_from_url(m_host);
|
||||||
|
http.header("Host", host);
|
||||||
|
#endif // _WIN32
|
||||||
set_auth(http);
|
set_auth(http);
|
||||||
// This is ugly, but works. There was an error at PrusaLink side that accepts any string at Print-After-Upload as true, thus False was also triggering print after upload.
|
// This is ugly, but works. There was an error at PrusaLink side that accepts any string at Print-After-Upload as true, thus False was also triggering print after upload.
|
||||||
if (upload_data.post_action == PrintHostPostUploadAction::StartPrint)
|
if (upload_data.post_action == PrintHostPostUploadAction::StartPrint)
|
||||||
http.header("Print-After-Upload", "True");
|
http.header("Print-After-Upload", "?1");
|
||||||
|
|
||||||
http.set_put_body(upload_data.source_path)
|
http.set_put_body(upload_data.source_path)
|
||||||
.header("Content-Type", "text/x.gcode")
|
.header("Content-Type", "text/x.gcode")
|
||||||
.header("Overwrite", "?1")
|
.header("Overwrite", "?1")
|
||||||
|
@ -1057,7 +1088,17 @@ bool PrusaLink::post_inner(PrintHostUpload upload_data, std::string url, const s
|
||||||
bool res = true;
|
bool res = true;
|
||||||
const auto upload_filename = upload_data.upload_path.filename();
|
const auto upload_filename = upload_data.upload_path.filename();
|
||||||
const auto upload_parent_path = upload_data.upload_path.parent_path();
|
const auto upload_parent_path = upload_data.upload_path.parent_path();
|
||||||
|
|
||||||
Http http = Http::post(std::move(url));
|
Http http = Http::post(std::move(url));
|
||||||
|
#ifdef WIN32
|
||||||
|
// "Host" header is necessary here. We have resolved IP address and subsituted it into "url" variable.
|
||||||
|
// And when creating Http object above, libcurl automatically includes "Host" header from address it got.
|
||||||
|
// Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back.
|
||||||
|
// Not changing the host would work on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734).
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc7230#section-5.4
|
||||||
|
std::string host = get_host_from_url(m_host);
|
||||||
|
http.header("Host", host);
|
||||||
|
#endif // _WIN32
|
||||||
set_auth(http);
|
set_auth(http);
|
||||||
set_http_post_header_args(http, upload_data.post_action);
|
set_http_post_header_args(http, upload_data.post_action);
|
||||||
http.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
|
http.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
|
||||||
|
@ -1128,14 +1169,11 @@ void PrusaConnect::set_http_post_header_args(Http& http, PrintHostPostUploadActi
|
||||||
|
|
||||||
wxString PrusaConnect::get_test_ok_msg() const
|
wxString PrusaConnect::get_test_ok_msg() const
|
||||||
{
|
{
|
||||||
return _(L("Connection to PrusaConnect works correctly."));
|
return _(L("Connection to Prusa Connect works correctly."));
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString PrusaConnect::get_test_failed_msg(wxString& msg) const
|
wxString PrusaConnect::get_test_failed_msg(wxString& msg) const
|
||||||
{
|
{
|
||||||
return GUI::from_u8((boost::format("%s: %s")
|
return GUI::format_wxstr("%s: %s", _L("Could not connect to Prusa Connect"), msg);
|
||||||
% _utf8(L("Could not connect to PrusaConnect"))
|
|
||||||
% std::string(msg.ToUTF8())).str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,30 +55,6 @@ private:
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
class SL1Host: public OctoPrint
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SL1Host(DynamicPrintConfig *config);
|
|
||||||
~SL1Host() override = default;
|
|
||||||
|
|
||||||
const char* get_name() const override;
|
|
||||||
|
|
||||||
wxString get_test_ok_msg() const override;
|
|
||||||
wxString get_test_failed_msg(wxString &msg) const override;
|
|
||||||
PrintHostPostUploadActions get_post_upload_actions() const override { return {}; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool validate_version_text(const boost::optional<std::string> &version_text) const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void set_auth(Http &http) const override;
|
|
||||||
|
|
||||||
// Host authorization type.
|
|
||||||
AuthorizationType m_authorization_type;
|
|
||||||
// username and password for HTTP Digest Authentization (RFC RFC2617)
|
|
||||||
std::string m_username;
|
|
||||||
std::string m_password;
|
|
||||||
};
|
|
||||||
|
|
||||||
class PrusaLink : public OctoPrint
|
class PrusaLink : public OctoPrint
|
||||||
{
|
{
|
||||||
|
@ -94,7 +70,7 @@ public:
|
||||||
virtual PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; }
|
virtual PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; }
|
||||||
|
|
||||||
// gets possible storage to be uploaded to. This allows different printer to have different storage. F.e. local vs sdcard vs usb.
|
// gets possible storage to be uploaded to. This allows different printer to have different storage. F.e. local vs sdcard vs usb.
|
||||||
bool get_storage(wxArrayString& /* storage */) const override;
|
bool get_storage(wxArrayString& storage_path, wxArrayString& storage_name) const override;
|
||||||
protected:
|
protected:
|
||||||
bool test(wxString& curl_msg) const override;
|
bool test(wxString& curl_msg) const override;
|
||||||
bool validate_version_text(const boost::optional<std::string>& version_text) const override;
|
bool validate_version_text(const boost::optional<std::string>& version_text) const override;
|
||||||
|
@ -106,6 +82,12 @@ protected:
|
||||||
bool upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn, const boost::asio::ip::address& resolved_addr) const override;
|
bool upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn, const boost::asio::ip::address& resolved_addr) const override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Host authorization type.
|
||||||
|
AuthorizationType m_authorization_type;
|
||||||
|
// username and password for HTTP Digest Authentization (RFC RFC2617)
|
||||||
|
std::string m_username;
|
||||||
|
std::string m_password;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool test_with_method_check(wxString& curl_msg, bool& use_put) const;
|
bool test_with_method_check(wxString& curl_msg, bool& use_put) const;
|
||||||
bool put_inner(PrintHostUpload upload_data, std::string url, const std::string& name, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const;
|
bool put_inner(PrintHostUpload upload_data, std::string url, const std::string& name, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const;
|
||||||
|
@ -113,11 +95,7 @@ private:
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
bool test_with_resolved_ip_and_method_check(wxString& curl_msg, bool& use_put) const;
|
bool test_with_resolved_ip_and_method_check(wxString& curl_msg, bool& use_put) const;
|
||||||
#endif
|
#endif
|
||||||
// Host authorization type.
|
|
||||||
AuthorizationType m_authorization_type;
|
|
||||||
// username and password for HTTP Digest Authentization (RFC RFC2617)
|
|
||||||
std::string m_username;
|
|
||||||
std::string m_password;
|
|
||||||
bool m_show_after_message;
|
bool m_show_after_message;
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -139,6 +117,22 @@ protected:
|
||||||
void set_http_post_header_args(Http& http, PrintHostPostUploadAction post_action) const override;
|
void set_http_post_header_args(Http& http, PrintHostPostUploadAction post_action) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SL1Host : public PrusaLink
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SL1Host(DynamicPrintConfig* config);
|
||||||
|
~SL1Host() override = default;
|
||||||
|
|
||||||
|
const char* get_name() const override;
|
||||||
|
|
||||||
|
wxString get_test_ok_msg() const override;
|
||||||
|
wxString get_test_failed_msg(wxString& msg) const override;
|
||||||
|
PrintHostPostUploadActions get_post_upload_actions() const override { return {}; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool validate_version_text(const boost::optional<std::string>& version_text) const override;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -66,7 +66,7 @@ public:
|
||||||
virtual bool get_printers(wxArrayString & /* printers */) const { return false; }
|
virtual bool get_printers(wxArrayString & /* printers */) const { return false; }
|
||||||
// Support for PrusaLink uploading to different storage. Not supported by other print hosts.
|
// Support for PrusaLink uploading to different storage. Not supported by other print hosts.
|
||||||
// Returns false if not supported or fail.
|
// Returns false if not supported or fail.
|
||||||
virtual bool get_storage(wxArrayString& /* storage */) const { return false; }
|
virtual bool get_storage(wxArrayString& /*storage_path*/, wxArrayString& /*storage_name*/) const { return false; }
|
||||||
|
|
||||||
static PrintHost* get_print_host(DynamicPrintConfig *config);
|
static PrintHost* get_print_host(DynamicPrintConfig *config);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue