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
|
||||
|
||||
if (get("allow_ip_resolve").empty())
|
||||
set("allow_ip_resolve", "1");
|
||||
|
||||
if (get("presets", "filament_colors").empty()) {
|
||||
set_str("presets", "filament_colors", "#F2754E");
|
||||
}
|
||||
|
|
|
@ -80,6 +80,8 @@ public:
|
|||
{ std::string value; this->get(section, key, value); return value; }
|
||||
std::string get(const std::string &key) const
|
||||
{ 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)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include <set>
|
||||
#include <mutex>
|
||||
|
||||
#include <boost/nowide/convert.hpp>
|
||||
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/listctrl.h>
|
||||
|
@ -15,8 +17,8 @@
|
|||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
#include "slic3r/Utils/Bonjour.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -61,8 +63,6 @@ BonjourDialog::BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology tech)
|
|||
, timer_state(0)
|
||||
, tech(tech)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
|
||||
const int em = GUI::wxGetApp().em_unit();
|
||||
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);
|
||||
|
||||
|
||||
auto button_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
StateColor btn_bg_green(std::pair<wxColour, int>(wxColour(0, 137, 123), StateColor::Pressed), std::pair<wxColour, int>(wxColour(38, 166, 154), StateColor::Hovered),
|
||||
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));
|
||||
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);
|
||||
// ^ Note: The Ok/Cancel labels are translated by wxWidgets
|
||||
|
||||
vsizer->Add(button_sizer, 0, wxALIGN_CENTER);
|
||||
SetSizerAndFit(vsizer);
|
||||
|
@ -253,19 +224,61 @@ void BonjourDialog::on_timer(wxTimerEvent &)
|
|||
// explicitly (wxTimerEvent should not be created by user code).
|
||||
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) {
|
||||
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;
|
||||
} else {
|
||||
label->SetLabel(GUI::from_u8((boost::format("%1%: %2%") % search_str % (_utf8(L("Finished"))+".")).str()));
|
||||
label->SetLabel(search_str + ": " + _L("Finished") + ".");
|
||||
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_
|
||||
#define slic3r_BonjourDialog_hpp_
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/string.h>
|
||||
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
|
||||
|
@ -11,7 +15,7 @@ class wxListView;
|
|||
class wxStaticText;
|
||||
class wxTimer;
|
||||
class wxTimerEvent;
|
||||
|
||||
class address;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -41,12 +45,26 @@ private:
|
|||
unsigned timer_state;
|
||||
Slic3r::PrinterTechnology tech;
|
||||
|
||||
void on_reply(BonjourReplyEvent &);
|
||||
virtual void on_reply(BonjourReplyEvent &);
|
||||
void on_timer(wxTimerEvent &);
|
||||
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)
|
||||
bool supports_multiple_printers = false;
|
||||
if (tech == ptFFF) {
|
||||
update_host_type(printer_change);
|
||||
update_host_type(printer_change);
|
||||
const auto opt = m_config->option<ConfigOptionEnum<PrintHostType>>("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");
|
||||
AuthorizationType auth_type = m_config->option<ConfigOptionEnum<AuthorizationType>>("printhost_authorization_type")->value;
|
||||
m_optgroup->show_field("printhost_apikey", auth_type == AuthorizationType::atKeyPassword);
|
||||
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 {
|
||||
m_optgroup->hide_field("printhost_authorization_type");
|
||||
m_optgroup->show_field("printhost_apikey", true);
|
||||
for (const std::string& opt_key : std::vector<std::string>{ "printhost_user", "printhost_password" })
|
||||
m_optgroup->hide_field(opt_key);
|
||||
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 {
|
||||
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)
|
||||
return;
|
||||
bool all_presets_are_from_mk3_family = false;
|
||||
Field* ht = m_optgroup->get_field("host_type");
|
||||
|
||||
wxArrayString types;
|
||||
// Append localized enum_labels
|
||||
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]));
|
||||
}
|
||||
int last_in_conf = m_config->option("host_type")->getInt(); // this is real position in last choice
|
||||
|
||||
Choice* choice = dynamic_cast<Choice*>(ht);
|
||||
choice->set_values(types);
|
||||
auto set_to_choice_and_config = [this, choice](PrintHostType type) {
|
||||
choice->set_value(static_cast<int>(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(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));
|
||||
};
|
||||
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()
|
||||
|
|
|
@ -10966,42 +10966,34 @@ void Plater::send_gcode_legacy(int plate_idx, Export3mfProgressFn proFn)
|
|||
upload_job.printhost->get_groups(groups);
|
||||
}
|
||||
|
||||
// orca merge todo
|
||||
PrintHostSendDialog dlg(default_output_file, upload_job.printhost->get_post_upload_actions(), groups);
|
||||
// PrusaLink specific: Query the server for the list of file 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) {
|
||||
upload_job.upload_data.upload_path = dlg.filename();
|
||||
upload_job.upload_data.post_action = dlg.post_action();
|
||||
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));
|
||||
|
||||
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)
|
||||
|
|
|
@ -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_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.
|
||||
// All buttons will be added later in this constructor
|
||||
, txt_filename(new wxTextCtrl(this, wxID_ANY))
|
||||
, 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)
|
||||
, m_paths(storage_paths)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
txt_filename->OSXDisableAllSmartSubstitutions();
|
||||
|
@ -70,18 +71,18 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUplo
|
|||
|
||||
if (combo_storage != nullptr) {
|
||||
// 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(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));
|
||||
if (!recent_storage.empty())
|
||||
combo_storage->SetValue(recent_storage);
|
||||
} else if (storage.GetCount() == 1){
|
||||
} else if (storage_names.GetCount() == 1){
|
||||
// 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);
|
||||
m_preselected_storage = storage.front();
|
||||
m_preselected_storage = storage_paths.front();
|
||||
}
|
||||
|
||||
|
||||
|
@ -196,7 +197,9 @@ std::string PrintHostSendDialog::storage() const
|
|||
{
|
||||
if (!combo_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)
|
||||
|
@ -226,8 +229,6 @@ void PrintHostSendDialog::EndModal(int ret)
|
|||
MsgDialog::EndModal(ret);
|
||||
}
|
||||
|
||||
|
||||
|
||||
wxDEFINE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event);
|
||||
wxDEFINE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event);
|
||||
wxDEFINE_EVENT(EVT_PRINTHOST_CANCEL, PrintHostQueueDialog::Event);
|
||||
|
@ -355,8 +356,6 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
|
|||
if (selected == wxNOT_FOUND) { return; }
|
||||
GUI::show_error(nullptr, job_list->GetTextValue(selected, COL_ERRORMSG));
|
||||
});
|
||||
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
}
|
||||
|
||||
void PrintHostQueueDialog::append_job(const PrintHostJob &job)
|
||||
|
@ -474,7 +473,7 @@ void PrintHostQueueDialog::on_error(Event &evt)
|
|||
|
||||
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(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)
|
||||
{
|
||||
/*
|
||||
wxCHECK_RET(evt.job_id < (size_t)job_list->GetItemCount(), "Out of bounds access to job list");
|
||||
|
||||
if (evt.tag == L"resolve") {
|
||||
|
@ -524,6 +524,7 @@ void PrintHostQueueDialog::on_info(Event& evt)
|
|||
} else if (evt.tag == L"set_complete_off") {
|
||||
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)
|
||||
|
@ -565,8 +566,11 @@ bool PrintHostQueueDialog::load_user_data(int udt, std::vector<int>& vector)
|
|||
auto* app_config = wxGetApp().app_config;
|
||||
auto hasget = [app_config](const std::string& name, std::vector<int>& vector)->bool {
|
||||
if (app_config->has(name)) {
|
||||
vector.push_back(std::stoi(app_config->get(name)));
|
||||
return true;
|
||||
std::string val = app_config->get(name);
|
||||
if (!val.empty() || val[0]!='\0') {
|
||||
vector.push_back(std::stoi(val));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace GUI {
|
|||
class PrintHostSendDialog : public GUI::MsgDialog
|
||||
{
|
||||
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;
|
||||
PrintHostPostUploadAction post_action() const;
|
||||
std::string group() const;
|
||||
|
@ -40,6 +40,7 @@ private:
|
|||
PrintHostPostUploadAction post_upload_action;
|
||||
wxString m_valid_suffix;
|
||||
wxString m_preselected_storage;
|
||||
wxArrayString m_paths;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -7,12 +7,17 @@
|
|||
#include <set>
|
||||
#include <unordered_map>
|
||||
#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 {
|
||||
|
||||
|
||||
|
||||
struct BonjourReply
|
||||
{
|
||||
typedef std::unordered_map<std::string, std::string> TxtData;
|
||||
|
@ -40,7 +45,6 @@ struct BonjourReply
|
|||
|
||||
std::ostream& operator<<(std::ostream &, const BonjourReply &);
|
||||
|
||||
|
||||
/// Bonjour lookup performer
|
||||
class Bonjour : public std::enable_shared_from_this<Bonjour> {
|
||||
private:
|
||||
|
@ -49,6 +53,7 @@ public:
|
|||
typedef std::shared_ptr<Bonjour> Ptr;
|
||||
typedef std::function<void(BonjourReply &&)> ReplyFn;
|
||||
typedef std::function<void()> CompleteFn;
|
||||
typedef std::function<void(const std::vector<BonjourReply>&)> ResolveFn;
|
||||
typedef std::set<std::string> TxtKeys;
|
||||
|
||||
Bonjour(std::string service);
|
||||
|
@ -65,15 +70,217 @@ public:
|
|||
// ^ Note: By default there is 1 retry (meaning 1 broadcast is sent).
|
||||
// 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.
|
||||
|
||||
// sets hostname queried by resolve()
|
||||
Bonjour& set_hostname(const std::string& hostname);
|
||||
|
||||
Bonjour& on_reply(ReplyFn 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();
|
||||
// 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:
|
||||
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 {
|
||||
#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.
|
||||
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;
|
||||
#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
|
||||
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
|
||||
{
|
||||
// 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();
|
||||
bool res = true;
|
||||
// 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;
|
||||
|
||||
std::string host = get_host_from_url(m_host);
|
||||
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);
|
||||
http
|
||||
.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");
|
||||
res = validate_version_text(text);
|
||||
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&) {
|
||||
|
@ -228,7 +236,7 @@ bool OctoPrint::test(wxString& msg) const
|
|||
auto url = make_url("api/version");
|
||||
|
||||
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));
|
||||
set_auth(http);
|
||||
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");
|
||||
res = validate_version_text(text);
|
||||
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 &) {
|
||||
|
@ -273,7 +281,6 @@ bool OctoPrint::test(wxString& msg) const
|
|||
return res;
|
||||
}
|
||||
|
||||
|
||||
wxString OctoPrint::get_test_ok_msg () const
|
||||
{
|
||||
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
|
||||
{
|
||||
return GUI::from_u8((boost::format("%s: %s\n\n%s")
|
||||
% _utf8(L("Could not connect to OctoPrint"))
|
||||
% std::string(msg.ToUTF8())
|
||||
% _utf8(L("Note: OctoPrint version at least 1.1.0 is required."))).str());
|
||||
return GUI::format_wxstr("%s: %s\n\n%s"
|
||||
, _L("Could not connect to OctoPrint")
|
||||
, msg
|
||||
, _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
|
||||
|
@ -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);
|
||||
if (!ec) {
|
||||
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")
|
||||
.set_hostname(host)
|
||||
.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;
|
||||
} else {
|
||||
// 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);
|
||||
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]);
|
||||
|
@ -379,7 +386,14 @@ bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, Progr
|
|||
% upload_parent_path.string()
|
||||
% (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));
|
||||
// "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);
|
||||
http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false")
|
||||
.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);
|
||||
if (cancel) {
|
||||
// Upload was canceled
|
||||
BOOST_LOG_TRIVIAL(info) << "Octoprint: Upload canceled";
|
||||
BOOST_LOG_TRIVIAL(info) << name << ": Upload canceled";
|
||||
result = false;
|
||||
}
|
||||
})
|
||||
|
@ -428,7 +442,7 @@ bool OctoPrint::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn p
|
|||
|
||||
#ifdef WIN32
|
||||
// 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
|
||||
{
|
||||
// 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");
|
||||
|
||||
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);
|
||||
http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false")
|
||||
.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);
|
||||
if (cancel) {
|
||||
// Upload was canceled
|
||||
BOOST_LOG_TRIVIAL(info) << "Octoprint: Upload canceled";
|
||||
BOOST_LOG_TRIVIAL(info) << name << ": Upload canceled";
|
||||
res = false;
|
||||
}
|
||||
})
|
||||
|
@ -513,11 +537,8 @@ std::string OctoPrint::make_url(const std::string &path) const
|
|||
}
|
||||
}
|
||||
|
||||
SL1Host::SL1Host(DynamicPrintConfig *config) :
|
||||
OctoPrint(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"))
|
||||
SL1Host::SL1Host(DynamicPrintConfig *config)
|
||||
: PrusaLink(config)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -531,9 +552,7 @@ wxString SL1Host::get_test_ok_msg () const
|
|||
|
||||
wxString SL1Host::get_test_failed_msg (wxString &msg) const
|
||||
{
|
||||
return GUI::from_u8((boost::format("%s: %s")
|
||||
% _utf8(L("Could not connect to Prusa SLA"))
|
||||
% std::string(msg.ToUTF8())).str());
|
||||
return GUI::format_wxstr("%s: %s", _L("Could not connect to Prusa SLA"), msg);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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(DynamicPrintConfig* config, bool show_after_message) :
|
||||
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_password(config->opt_string("printhost_password")),
|
||||
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
|
||||
{
|
||||
return GUI::from_u8((boost::format("%s: %s")
|
||||
% _utf8(L("Could not connect to PrusaLink"))
|
||||
% std::string(msg.ToUTF8())).str());
|
||||
return GUI::format_wxstr("%s: %s", _L("Could not connect to PrusaLink"), msg);
|
||||
}
|
||||
|
||||
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");
|
||||
res = validate_version_text(text);
|
||||
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&) {
|
||||
|
@ -685,7 +686,7 @@ bool PrusaLink::test(wxString& msg) const
|
|||
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();
|
||||
|
||||
|
@ -693,17 +694,22 @@ bool PrusaLink::get_storage(wxArrayString& output) const
|
|||
auto url = make_url("api/v1/storage");
|
||||
wxString error_msg;
|
||||
|
||||
struct StorageInfo{
|
||||
struct StorageInfo {
|
||||
wxString path;
|
||||
wxString name;
|
||||
bool read_only;
|
||||
long long free_space;
|
||||
bool read_only = false;
|
||||
long long free_space = -1;
|
||||
};
|
||||
std::vector<StorageInfo> storage;
|
||||
|
||||
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));
|
||||
set_auth(http);
|
||||
http.header("Accept-Language", lang);
|
||||
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;
|
||||
error_msg = L"\n\n" + boost::nowide::widen(error);
|
||||
|
@ -716,7 +722,7 @@ bool PrusaLink::get_storage(wxArrayString& output) const
|
|||
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;
|
||||
try
|
||||
{
|
||||
|
@ -731,14 +737,19 @@ bool PrusaLink::get_storage(wxArrayString& output) const
|
|||
}
|
||||
// each storage has own subtree of storage_list
|
||||
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 space = section.second.get_optional<std::string>("free_space");
|
||||
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");
|
||||
if (path && (!available || *available)) {
|
||||
StorageInfo si;
|
||||
si.name = boost::nowide::widen(*path);
|
||||
si.read_only = read_only ? *read_only : false; // If read_only is missing, assume it is NOT read only.
|
||||
si.path = boost::nowide::widen(*path);
|
||||
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.
|
||||
storage.emplace_back(std::move(si));
|
||||
}
|
||||
|
@ -756,19 +767,25 @@ bool PrusaLink::get_storage(wxArrayString& output) const
|
|||
.perform_sync();
|
||||
|
||||
for (const auto& si : storage) {
|
||||
if (!si.read_only && si.free_space > 0)
|
||||
output.push_back(si.name);
|
||||
if (!si.read_only && si.free_space > 0) {
|
||||
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
|
||||
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) {
|
||||
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;
|
||||
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");
|
||||
|
||||
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));
|
||||
set_auth(http);
|
||||
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");
|
||||
res = validate_version_text(text);
|
||||
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;
|
||||
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;
|
||||
|
||||
std::string host = get_host_from_url(m_host);
|
||||
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);
|
||||
http
|
||||
.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");
|
||||
res = validate_version_text(text);
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
@ -968,12 +993,11 @@ bool PrusaLink::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn p
|
|||
}
|
||||
|
||||
std::string url;
|
||||
bool res = true;
|
||||
std::string storage_path = (use_put ? "api/v1/files" : "api/files");
|
||||
storage_path += (upload_data.storage.empty() ? "/local" : upload_data.storage);
|
||||
#ifdef WIN32
|
||||
// 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
|
||||
{
|
||||
// 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;
|
||||
// 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);
|
||||
|
||||
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);
|
||||
// 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)
|
||||
http.header("Print-After-Upload", "True");
|
||||
|
||||
http.header("Print-After-Upload", "?1");
|
||||
http.set_put_body(upload_data.source_path)
|
||||
.header("Content-Type", "text/x.gcode")
|
||||
.header("Overwrite", "?1")
|
||||
|
@ -1057,7 +1088,17 @@ bool PrusaLink::post_inner(PrintHostUpload upload_data, std::string url, const s
|
|||
bool res = true;
|
||||
const auto upload_filename = upload_data.upload_path.filename();
|
||||
const auto upload_parent_path = upload_data.upload_path.parent_path();
|
||||
|
||||
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_http_post_header_args(http, upload_data.post_action);
|
||||
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
|
||||
{
|
||||
return _(L("Connection to PrusaConnect works correctly."));
|
||||
return _(L("Connection to Prusa Connect works correctly."));
|
||||
}
|
||||
|
||||
wxString PrusaConnect::get_test_failed_msg(wxString& msg) const
|
||||
{
|
||||
return GUI::from_u8((boost::format("%s: %s")
|
||||
% _utf8(L("Could not connect to PrusaConnect"))
|
||||
% std::string(msg.ToUTF8())).str());
|
||||
return GUI::format_wxstr("%s: %s", _L("Could not connect to Prusa Connect"), msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -55,30 +55,6 @@ private:
|
|||
#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
|
||||
{
|
||||
|
@ -94,7 +70,7 @@ public:
|
|||
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.
|
||||
bool get_storage(wxArrayString& /* storage */) const override;
|
||||
bool get_storage(wxArrayString& storage_path, wxArrayString& storage_name) const override;
|
||||
protected:
|
||||
bool test(wxString& curl_msg) 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;
|
||||
#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:
|
||||
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;
|
||||
|
@ -113,11 +95,7 @@ private:
|
|||
#ifdef WIN32
|
||||
bool test_with_resolved_ip_and_method_check(wxString& curl_msg, bool& use_put) const;
|
||||
#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;
|
||||
|
||||
#if 0
|
||||
|
@ -139,6 +117,22 @@ protected:
|
|||
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
|
||||
|
|
|
@ -66,7 +66,7 @@ public:
|
|||
virtual bool get_printers(wxArrayString & /* printers */) const { return false; }
|
||||
// Support for PrusaLink uploading to different storage. Not supported by other print hosts.
|
||||
// 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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue