Octoprint (#796)
* Octoprint: GUI for CA file, improvements * Octoprint: Add GUI for Bonjour lookup, bugfixes * Octoprint: Bonjour browser: Cleanup Perl interaction * Octoprint: Bonjour: Perform several broadcast, UI fixes * Octoprint: Add files to localization list * Http: Disable CA File setting on SSL backends that don't support it
This commit is contained in:
parent
8d4b603572
commit
c88d2780ce
@ -217,11 +217,7 @@ sub _init_tabpanel {
|
|||||||
$self->{is_disabled_button_browse} = (!eval "use Net::Bonjour; 1") ? 1 : 0 ;
|
$self->{is_disabled_button_browse} = (!eval "use Net::Bonjour; 1") ? 1 : 0 ;
|
||||||
# A variable to inform C++ Tab implementation about user_agent
|
# A variable to inform C++ Tab implementation about user_agent
|
||||||
$self->{is_user_agent} = (eval "use LWP::UserAgent; 1") ? 1 : 0 ;
|
$self->{is_user_agent} = (eval "use LWP::UserAgent; 1") ? 1 : 0 ;
|
||||||
Slic3r::GUI::create_preset_tabs($self->{no_controller},
|
Slic3r::GUI::create_preset_tabs($self->{no_controller}, $VALUE_CHANGE_EVENT, $PRESETS_CHANGED_EVENT);
|
||||||
$self->{is_disabled_button_browse},
|
|
||||||
$self->{is_user_agent},
|
|
||||||
$VALUE_CHANGE_EVENT, $PRESETS_CHANGED_EVENT,
|
|
||||||
$BUTTON_BROWSE_EVENT, $BUTTON_TEST_EVENT);
|
|
||||||
$self->{options_tabs} = {};
|
$self->{options_tabs} = {};
|
||||||
for my $tab_name (qw(print filament printer)) {
|
for my $tab_name (qw(print filament printer)) {
|
||||||
$self->{options_tabs}{$tab_name} = Slic3r::GUI::get_preset_tab("$tab_name");
|
$self->{options_tabs}{$tab_name} = Slic3r::GUI::get_preset_tab("$tab_name");
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -9,6 +9,8 @@ xs/src/slic3r/GUI/2DBed.cpp
|
|||||||
xs/src/slic3r/GUI/PresetHints.cpp
|
xs/src/slic3r/GUI/PresetHints.cpp
|
||||||
xs/src/slic3r/GUI/Preferences.hpp
|
xs/src/slic3r/GUI/Preferences.hpp
|
||||||
xs/src/slic3r/GUI/Preferences.cpp
|
xs/src/slic3r/GUI/Preferences.cpp
|
||||||
|
xs/src/slic3r/GUI/BonjourDialog.cpp
|
||||||
|
xs/src/slic3r/Utils/OctoPrint.cpp
|
||||||
xs/src/libslic3r/PrintConfig.cpp
|
xs/src/libslic3r/PrintConfig.cpp
|
||||||
xs/src/libslic3r/GCode/PreviewData.cpp
|
xs/src/libslic3r/GCode/PreviewData.cpp
|
||||||
lib/Slic3r/GUI.pm
|
lib/Slic3r/GUI.pm
|
||||||
|
@ -199,6 +199,8 @@ add_library(libslic3r_gui STATIC
|
|||||||
${LIBDIR}/slic3r/GUI/2DBed.hpp
|
${LIBDIR}/slic3r/GUI/2DBed.hpp
|
||||||
${LIBDIR}/slic3r/GUI/wxExtensions.cpp
|
${LIBDIR}/slic3r/GUI/wxExtensions.cpp
|
||||||
${LIBDIR}/slic3r/GUI/wxExtensions.hpp
|
${LIBDIR}/slic3r/GUI/wxExtensions.hpp
|
||||||
|
${LIBDIR}/slic3r/GUI/BonjourDialog.cpp
|
||||||
|
${LIBDIR}/slic3r/GUI/BonjourDialog.hpp
|
||||||
${LIBDIR}/slic3r/Utils/Http.cpp
|
${LIBDIR}/slic3r/Utils/Http.cpp
|
||||||
${LIBDIR}/slic3r/Utils/Http.hpp
|
${LIBDIR}/slic3r/Utils/Http.hpp
|
||||||
${LIBDIR}/slic3r/Utils/OctoPrint.cpp
|
${LIBDIR}/slic3r/Utils/OctoPrint.cpp
|
||||||
|
200
xs/src/slic3r/GUI/BonjourDialog.cpp
Normal file
200
xs/src/slic3r/GUI/BonjourDialog.cpp
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
#include "slic3r/Utils/Bonjour.hpp" // On Windows, boost needs to be included before wxWidgets headers
|
||||||
|
|
||||||
|
#include "BonjourDialog.hpp"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <wx/sizer.h>
|
||||||
|
#include <wx/button.h>
|
||||||
|
#include <wx/listctrl.h>
|
||||||
|
#include <wx/stattext.h>
|
||||||
|
#include <wx/timer.h>
|
||||||
|
|
||||||
|
#include "slic3r/GUI/GUI.hpp"
|
||||||
|
#include "slic3r/Utils/Bonjour.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
|
||||||
|
struct BonjourReplyEvent : public wxEvent
|
||||||
|
{
|
||||||
|
BonjourReply reply;
|
||||||
|
|
||||||
|
BonjourReplyEvent(wxEventType eventType, int winid, BonjourReply &&reply) :
|
||||||
|
wxEvent(winid, eventType),
|
||||||
|
reply(std::move(reply))
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual wxEvent *Clone() const
|
||||||
|
{
|
||||||
|
return new BonjourReplyEvent(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
wxDEFINE_EVENT(EVT_BONJOUR_REPLY, BonjourReplyEvent);
|
||||||
|
|
||||||
|
wxDECLARE_EVENT(EVT_BONJOUR_COMPLETE, wxCommandEvent);
|
||||||
|
wxDEFINE_EVENT(EVT_BONJOUR_COMPLETE, wxCommandEvent);
|
||||||
|
|
||||||
|
class ReplySet: public std::set<BonjourReply> {};
|
||||||
|
|
||||||
|
struct LifetimeGuard
|
||||||
|
{
|
||||||
|
std::mutex mutex;
|
||||||
|
BonjourDialog *dialog;
|
||||||
|
|
||||||
|
LifetimeGuard(BonjourDialog *dialog) : dialog(dialog) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
BonjourDialog::BonjourDialog(wxWindow *parent) :
|
||||||
|
wxDialog(parent, wxID_ANY, _(L("Network lookup"))),
|
||||||
|
list(new wxListView(this, wxID_ANY, wxDefaultPosition, wxSize(800, 300))),
|
||||||
|
replies(new ReplySet),
|
||||||
|
label(new wxStaticText(this, wxID_ANY, "")),
|
||||||
|
timer(new wxTimer()),
|
||||||
|
timer_state(0)
|
||||||
|
{
|
||||||
|
wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
|
vsizer->Add(label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10);
|
||||||
|
|
||||||
|
list->SetSingleStyle(wxLC_SINGLE_SEL);
|
||||||
|
list->SetSingleStyle(wxLC_SORT_DESCENDING);
|
||||||
|
list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 50);
|
||||||
|
list->AppendColumn(_(L("Hostname")), wxLIST_FORMAT_LEFT, 100);
|
||||||
|
list->AppendColumn(_(L("Service name")), wxLIST_FORMAT_LEFT, 200);
|
||||||
|
list->AppendColumn(_(L("OctoPrint version")), wxLIST_FORMAT_LEFT, 50);
|
||||||
|
|
||||||
|
vsizer->Add(list, 1, wxEXPAND | wxALL, 10);
|
||||||
|
|
||||||
|
wxBoxSizer *button_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
button_sizer->Add(new wxButton(this, wxID_OK, "OK"), 0, wxALL, 10);
|
||||||
|
button_sizer->Add(new wxButton(this, wxID_CANCEL, "Cancel"), 0, wxALL, 10);
|
||||||
|
// ^ Note: The Ok/Cancel labels are translated by wxWidgets
|
||||||
|
|
||||||
|
vsizer->Add(button_sizer, 0, wxALIGN_CENTER);
|
||||||
|
SetSizerAndFit(vsizer);
|
||||||
|
|
||||||
|
Bind(EVT_BONJOUR_REPLY, &BonjourDialog::on_reply, this);
|
||||||
|
|
||||||
|
Bind(EVT_BONJOUR_COMPLETE, [this](wxCommandEvent &) {
|
||||||
|
this->timer_state = 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
Bind(wxEVT_TIMER, &BonjourDialog::on_timer, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
BonjourDialog::~BonjourDialog()
|
||||||
|
{
|
||||||
|
// Needed bacuse of forward defs
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BonjourDialog::show_and_lookup()
|
||||||
|
{
|
||||||
|
Show(); // Because we need GetId() to work before ShowModal()
|
||||||
|
|
||||||
|
timer->Stop();
|
||||||
|
timer->SetOwner(this);
|
||||||
|
timer_state = 1;
|
||||||
|
timer->Start(1000);
|
||||||
|
wxTimerEvent evt_dummy;
|
||||||
|
on_timer(evt_dummy);
|
||||||
|
|
||||||
|
// The background thread needs to queue messages for this dialog
|
||||||
|
// and for that it needs a valid pointer to it (mandated by the wxWidgets API).
|
||||||
|
// Here we put the pointer under a shared_ptr and protect it by a mutex,
|
||||||
|
// so that both threads can access it safely.
|
||||||
|
auto dguard = std::make_shared<LifetimeGuard>(this);
|
||||||
|
|
||||||
|
bonjour = std::move(Bonjour("octoprint")
|
||||||
|
.set_retries(3)
|
||||||
|
.set_timeout(4)
|
||||||
|
.on_reply([dguard](BonjourReply &&reply) {
|
||||||
|
std::lock_guard<std::mutex> lock_guard(dguard->mutex);
|
||||||
|
auto dialog = dguard->dialog;
|
||||||
|
if (dialog != nullptr) {
|
||||||
|
auto evt = new BonjourReplyEvent(EVT_BONJOUR_REPLY, dialog->GetId(), std::move(reply));
|
||||||
|
wxQueueEvent(dialog, evt);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on_complete([dguard]() {
|
||||||
|
std::lock_guard<std::mutex> lock_guard(dguard->mutex);
|
||||||
|
auto dialog = dguard->dialog;
|
||||||
|
if (dialog != nullptr) {
|
||||||
|
auto evt = new wxCommandEvent(EVT_BONJOUR_COMPLETE, dialog->GetId());
|
||||||
|
wxQueueEvent(dialog, evt);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.lookup()
|
||||||
|
);
|
||||||
|
|
||||||
|
bool res = ShowModal() == wxID_OK && list->GetFirstSelected() >= 0;
|
||||||
|
{
|
||||||
|
// Tell the background thread the dialog is going away...
|
||||||
|
std::lock_guard<std::mutex> lock_guard(dguard->mutex);
|
||||||
|
dguard->dialog = nullptr;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString BonjourDialog::get_selected() const
|
||||||
|
{
|
||||||
|
auto sel = list->GetFirstSelected();
|
||||||
|
return sel >= 0 ? list->GetItemText(sel) : wxString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Private
|
||||||
|
|
||||||
|
void BonjourDialog::on_reply(BonjourReplyEvent &e)
|
||||||
|
{
|
||||||
|
if (replies->find(e.reply) != replies->end()) {
|
||||||
|
// We already have this reply
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
replies->insert(std::move(e.reply));
|
||||||
|
|
||||||
|
auto selected = get_selected();
|
||||||
|
list->DeleteAllItems();
|
||||||
|
|
||||||
|
// The whole list is recreated so that we benefit from it already being sorted in the set.
|
||||||
|
// (And also because wxListView's sorting API is bananas.)
|
||||||
|
for (const auto &reply : *replies) {
|
||||||
|
auto item = list->InsertItem(0, reply.full_address);
|
||||||
|
list->SetItem(item, 1, reply.hostname);
|
||||||
|
list->SetItem(item, 2, reply.service_name);
|
||||||
|
list->SetItem(item, 3, reply.version);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
this->list->SetColumnWidth(i, wxLIST_AUTOSIZE);
|
||||||
|
if (this->list->GetColumnWidth(i) < 100) { this->list->SetColumnWidth(i, 100); }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!selected.IsEmpty()) {
|
||||||
|
// Attempt to preserve selection
|
||||||
|
auto hit = list->FindItem(-1, selected);
|
||||||
|
if (hit >= 0) { list->SetItemState(hit, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BonjourDialog::on_timer(wxTimerEvent &)
|
||||||
|
{
|
||||||
|
const auto search_str = _(L("Searching for devices"));
|
||||||
|
|
||||||
|
if (timer_state > 0) {
|
||||||
|
const std::string dots(timer_state, '.');
|
||||||
|
label->SetLabel(wxString::Format("%s %s", search_str, dots));
|
||||||
|
timer_state = (timer_state) % 3 + 1;
|
||||||
|
} else {
|
||||||
|
label->SetLabel(wxString::Format("%s: %s", search_str, _(L("Finished."))));
|
||||||
|
timer->Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
49
xs/src/slic3r/GUI/BonjourDialog.hpp
Normal file
49
xs/src/slic3r/GUI/BonjourDialog.hpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#ifndef slic3r_BonjourDialog_hpp_
|
||||||
|
#define slic3r_BonjourDialog_hpp_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <wx/dialog.h>
|
||||||
|
|
||||||
|
class wxListView;
|
||||||
|
class wxStaticText;
|
||||||
|
class wxTimer;
|
||||||
|
class wxTimerEvent;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
class Bonjour;
|
||||||
|
class BonjourReplyEvent;
|
||||||
|
class ReplySet;
|
||||||
|
|
||||||
|
|
||||||
|
class BonjourDialog: public wxDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BonjourDialog(wxWindow *parent);
|
||||||
|
BonjourDialog(BonjourDialog &&) = delete;
|
||||||
|
BonjourDialog(const BonjourDialog &) = delete;
|
||||||
|
BonjourDialog &operator=(BonjourDialog &&) = delete;
|
||||||
|
BonjourDialog &operator=(const BonjourDialog &) = delete;
|
||||||
|
~BonjourDialog();
|
||||||
|
|
||||||
|
bool show_and_lookup();
|
||||||
|
wxString get_selected() const;
|
||||||
|
private:
|
||||||
|
wxListView *list;
|
||||||
|
std::unique_ptr<ReplySet> replies;
|
||||||
|
wxStaticText *label;
|
||||||
|
std::shared_ptr<Bonjour> bonjour;
|
||||||
|
std::unique_ptr<wxTimer> timer;
|
||||||
|
unsigned timer_state;
|
||||||
|
|
||||||
|
void on_reply(BonjourReplyEvent &);
|
||||||
|
void on_timer(wxTimerEvent &);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -261,7 +261,7 @@ void SpinCtrl::BUILD() {
|
|||||||
// # when it was changed from the text control, so the on_change callback
|
// # when it was changed from the text control, so the on_change callback
|
||||||
// # gets the old one, and on_kill_focus resets the control to the old value.
|
// # gets the old one, and on_kill_focus resets the control to the old value.
|
||||||
// # As a workaround, we get the new value from $event->GetString and store
|
// # As a workaround, we get the new value from $event->GetString and store
|
||||||
// # here temporarily so that we can return it from $self->get_value
|
// # here temporarily so that we can return it from $self->get_value
|
||||||
std::string value = e.GetString().utf8_str().data();
|
std::string value = e.GetString().utf8_str().data();
|
||||||
if (is_matched(value, "^\\d+$"))
|
if (is_matched(value, "^\\d+$"))
|
||||||
tmp_value = std::stoi(value);
|
tmp_value = std::stoi(value);
|
||||||
@ -365,9 +365,9 @@ void Choice::set_selection()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Choice::set_value(const std::string value) //! Redundant?
|
void Choice::set_value(const std::string value, bool change_event) //! Redundant?
|
||||||
{
|
{
|
||||||
m_disable_change_event = true;
|
m_disable_change_event = !change_event;
|
||||||
|
|
||||||
size_t idx=0;
|
size_t idx=0;
|
||||||
for (auto el : m_opt.enum_values)
|
for (auto el : m_opt.enum_values)
|
||||||
@ -384,9 +384,9 @@ void Choice::set_value(const std::string value) //! Redundant?
|
|||||||
m_disable_change_event = false;
|
m_disable_change_event = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Choice::set_value(boost::any value)
|
void Choice::set_value(boost::any value, bool change_event)
|
||||||
{
|
{
|
||||||
m_disable_change_event = true;
|
m_disable_change_event = !change_event;
|
||||||
|
|
||||||
switch (m_opt.type){
|
switch (m_opt.type){
|
||||||
case coInt:
|
case coInt:
|
||||||
@ -429,7 +429,7 @@ void Choice::set_values(const std::vector<std::string> values)
|
|||||||
return;
|
return;
|
||||||
m_disable_change_event = true;
|
m_disable_change_event = true;
|
||||||
|
|
||||||
// # it looks that Clear() also clears the text field in recent wxWidgets versions,
|
// # it looks that Clear() also clears the text field in recent wxWidgets versions,
|
||||||
// # but we want to preserve it
|
// # but we want to preserve it
|
||||||
auto ww = dynamic_cast<wxComboBox*>(window);
|
auto ww = dynamic_cast<wxComboBox*>(window);
|
||||||
auto value = ww->GetValue();
|
auto value = ww->GetValue();
|
||||||
@ -541,9 +541,9 @@ void PointCtrl::BUILD()
|
|||||||
y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y));
|
y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PointCtrl::set_value(const Pointf value)
|
void PointCtrl::set_value(const Pointf value, bool change_event)
|
||||||
{
|
{
|
||||||
m_disable_change_event = true;
|
m_disable_change_event = !change_event;
|
||||||
|
|
||||||
double val = value.x;
|
double val = value.x;
|
||||||
x_textctrl->SetValue(val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None));
|
x_textctrl->SetValue(val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None));
|
||||||
@ -553,7 +553,7 @@ void PointCtrl::set_value(const Pointf value)
|
|||||||
m_disable_change_event = false;
|
m_disable_change_event = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PointCtrl::set_value(boost::any value)
|
void PointCtrl::set_value(boost::any value, bool change_event)
|
||||||
{
|
{
|
||||||
Pointf pt;
|
Pointf pt;
|
||||||
Pointf *ptf = boost::any_cast<Pointf>(&value);
|
Pointf *ptf = boost::any_cast<Pointf>(&value);
|
||||||
@ -579,7 +579,7 @@ void PointCtrl::set_value(boost::any value)
|
|||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
set_value(pt);
|
set_value(pt, change_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::any PointCtrl::get_value()
|
boost::any PointCtrl::get_value()
|
||||||
|
@ -78,7 +78,7 @@ public:
|
|||||||
/// Sets a value for this control.
|
/// Sets a value for this control.
|
||||||
/// subclasses should overload with a specific version
|
/// subclasses should overload with a specific version
|
||||||
/// Postcondition: Method does not fire the on_change event.
|
/// Postcondition: Method does not fire the on_change event.
|
||||||
virtual void set_value(boost::any value) = 0;
|
virtual void set_value(boost::any value, bool change_event) = 0;
|
||||||
|
|
||||||
/// Gets a boost::any representing this control.
|
/// Gets a boost::any representing this control.
|
||||||
/// subclasses should overload with a specific version
|
/// subclasses should overload with a specific version
|
||||||
@ -134,13 +134,13 @@ public:
|
|||||||
void BUILD();
|
void BUILD();
|
||||||
wxWindow* window {nullptr};
|
wxWindow* window {nullptr};
|
||||||
|
|
||||||
virtual void set_value(std::string value) {
|
virtual void set_value(std::string value, bool change_event = false) {
|
||||||
m_disable_change_event = true;
|
m_disable_change_event = !change_event;
|
||||||
dynamic_cast<wxTextCtrl*>(window)->SetValue(wxString(value));
|
dynamic_cast<wxTextCtrl*>(window)->SetValue(wxString(value));
|
||||||
m_disable_change_event = false;
|
m_disable_change_event = false;
|
||||||
}
|
}
|
||||||
virtual void set_value(boost::any value) {
|
virtual void set_value(boost::any value, bool change_event = false) {
|
||||||
m_disable_change_event = true;
|
m_disable_change_event = !change_event;
|
||||||
dynamic_cast<wxTextCtrl*>(window)->SetValue(boost::any_cast<wxString>(value));
|
dynamic_cast<wxTextCtrl*>(window)->SetValue(boost::any_cast<wxString>(value));
|
||||||
m_disable_change_event = false;
|
m_disable_change_event = false;
|
||||||
}
|
}
|
||||||
@ -161,13 +161,13 @@ public:
|
|||||||
wxWindow* window{ nullptr };
|
wxWindow* window{ nullptr };
|
||||||
void BUILD() override;
|
void BUILD() override;
|
||||||
|
|
||||||
void set_value(const bool value) {
|
void set_value(const bool value, bool change_event = false) {
|
||||||
m_disable_change_event = true;
|
m_disable_change_event = !change_event;
|
||||||
dynamic_cast<wxCheckBox*>(window)->SetValue(value);
|
dynamic_cast<wxCheckBox*>(window)->SetValue(value);
|
||||||
m_disable_change_event = false;
|
m_disable_change_event = false;
|
||||||
}
|
}
|
||||||
void set_value(boost::any value) {
|
void set_value(boost::any value, bool change_event = false) {
|
||||||
m_disable_change_event = true;
|
m_disable_change_event = !change_event;
|
||||||
dynamic_cast<wxCheckBox*>(window)->SetValue(boost::any_cast<bool>(value));
|
dynamic_cast<wxCheckBox*>(window)->SetValue(boost::any_cast<bool>(value));
|
||||||
m_disable_change_event = false;
|
m_disable_change_event = false;
|
||||||
}
|
}
|
||||||
@ -189,13 +189,13 @@ public:
|
|||||||
wxWindow* window{ nullptr };
|
wxWindow* window{ nullptr };
|
||||||
void BUILD() override;
|
void BUILD() override;
|
||||||
|
|
||||||
void set_value(const std::string value) {
|
void set_value(const std::string value, bool change_event = false) {
|
||||||
m_disable_change_event = true;
|
m_disable_change_event = !change_event;
|
||||||
dynamic_cast<wxSpinCtrl*>(window)->SetValue(value);
|
dynamic_cast<wxSpinCtrl*>(window)->SetValue(value);
|
||||||
m_disable_change_event = false;
|
m_disable_change_event = false;
|
||||||
}
|
}
|
||||||
void set_value(boost::any value) {
|
void set_value(boost::any value, bool change_event = false) {
|
||||||
m_disable_change_event = true;
|
m_disable_change_event = !change_event;
|
||||||
dynamic_cast<wxSpinCtrl*>(window)->SetValue(boost::any_cast<int>(value));
|
dynamic_cast<wxSpinCtrl*>(window)->SetValue(boost::any_cast<int>(value));
|
||||||
m_disable_change_event = false;
|
m_disable_change_event = false;
|
||||||
}
|
}
|
||||||
@ -218,8 +218,8 @@ public:
|
|||||||
void BUILD() override;
|
void BUILD() override;
|
||||||
|
|
||||||
void set_selection();
|
void set_selection();
|
||||||
void set_value(const std::string value);
|
void set_value(const std::string value, bool change_event = false);
|
||||||
void set_value(boost::any value);
|
void set_value(boost::any value, bool change_event = false);
|
||||||
void set_values(const std::vector<std::string> values);
|
void set_values(const std::vector<std::string> values);
|
||||||
boost::any get_value() override;
|
boost::any get_value() override;
|
||||||
|
|
||||||
@ -237,13 +237,13 @@ public:
|
|||||||
wxWindow* window{ nullptr };
|
wxWindow* window{ nullptr };
|
||||||
void BUILD() override;
|
void BUILD() override;
|
||||||
|
|
||||||
void set_value(const std::string value) {
|
void set_value(const std::string value, bool change_event = false) {
|
||||||
m_disable_change_event = true;
|
m_disable_change_event = !change_event;
|
||||||
dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(value);
|
dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(value);
|
||||||
m_disable_change_event = false;
|
m_disable_change_event = false;
|
||||||
}
|
}
|
||||||
void set_value(boost::any value) {
|
void set_value(boost::any value, bool change_event = false) {
|
||||||
m_disable_change_event = true;
|
m_disable_change_event = !change_event;
|
||||||
dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(boost::any_cast<wxString>(value));
|
dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(boost::any_cast<wxString>(value));
|
||||||
m_disable_change_event = false;
|
m_disable_change_event = false;
|
||||||
}
|
}
|
||||||
@ -267,8 +267,8 @@ public:
|
|||||||
|
|
||||||
void BUILD() override;
|
void BUILD() override;
|
||||||
|
|
||||||
void set_value(const Pointf value);
|
void set_value(const Pointf value, bool change_event = false);
|
||||||
void set_value(boost::any value);
|
void set_value(boost::any value, bool change_event = false);
|
||||||
boost::any get_value() override;
|
boost::any get_value() override;
|
||||||
|
|
||||||
void enable() override {
|
void enable() override {
|
||||||
|
@ -358,24 +358,17 @@ void open_preferences_dialog(int event_preferences)
|
|||||||
dlg->ShowModal();
|
dlg->ShowModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
void create_preset_tabs(bool no_controller, bool is_disabled_button_browse, bool is_user_agent,
|
void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed)
|
||||||
int event_value_change, int event_presets_changed,
|
|
||||||
int event_button_browse, int event_button_test)
|
|
||||||
{
|
{
|
||||||
add_created_tab(new TabPrint (g_wxTabPanel, no_controller));
|
add_created_tab(new TabPrint (g_wxTabPanel, no_controller));
|
||||||
add_created_tab(new TabFilament (g_wxTabPanel, no_controller));
|
add_created_tab(new TabFilament (g_wxTabPanel, no_controller));
|
||||||
add_created_tab(new TabPrinter (g_wxTabPanel, no_controller, is_disabled_button_browse, is_user_agent));
|
add_created_tab(new TabPrinter (g_wxTabPanel, no_controller));
|
||||||
for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++ i) {
|
for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++ i) {
|
||||||
Tab *tab = dynamic_cast<Tab*>(g_wxTabPanel->GetPage(i));
|
Tab *tab = dynamic_cast<Tab*>(g_wxTabPanel->GetPage(i));
|
||||||
if (! tab)
|
if (! tab)
|
||||||
continue;
|
continue;
|
||||||
tab->set_event_value_change(wxEventType(event_value_change));
|
tab->set_event_value_change(wxEventType(event_value_change));
|
||||||
tab->set_event_presets_changed(wxEventType(event_presets_changed));
|
tab->set_event_presets_changed(wxEventType(event_presets_changed));
|
||||||
if (tab->name() == "printer"){
|
|
||||||
TabPrinter* tab_printer = static_cast<TabPrinter*>(tab);
|
|
||||||
tab_printer->set_event_button_browse(wxEventType(event_button_browse));
|
|
||||||
tab_printer->set_event_button_test(wxEventType(event_button_test));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -591,19 +584,6 @@ wxString from_u8(const std::string &str)
|
|||||||
return wxString::FromUTF8(str.c_str());
|
return wxString::FromUTF8(str.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
wxWindow *get_widget_by_id(int id)
|
|
||||||
{
|
|
||||||
if (g_wxMainFrame == nullptr) {
|
|
||||||
throw std::runtime_error("Main frame not set");
|
|
||||||
}
|
|
||||||
|
|
||||||
wxWindow *window = g_wxMainFrame->FindWindow(id);
|
|
||||||
if (window == nullptr) {
|
|
||||||
throw std::runtime_error((boost::format("Could not find widget by ID: %1%") % id).str());
|
|
||||||
}
|
|
||||||
|
|
||||||
return window;
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer)
|
void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer)
|
||||||
{
|
{
|
||||||
|
@ -86,9 +86,7 @@ void add_debug_menu(wxMenuBar *menu, int event_language_change);
|
|||||||
void open_preferences_dialog(int event_preferences);
|
void open_preferences_dialog(int event_preferences);
|
||||||
|
|
||||||
// Create a new preset tab (print, filament and printer),
|
// Create a new preset tab (print, filament and printer),
|
||||||
void create_preset_tabs(bool no_controller, bool is_disabled_button_browse, bool is_user_agent,
|
void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed);
|
||||||
int event_value_change, int event_presets_changed,
|
|
||||||
int event_button_browse, int event_button_test);
|
|
||||||
TabIface* get_preset_tab_iface(char *name);
|
TabIface* get_preset_tab_iface(char *name);
|
||||||
|
|
||||||
// add it at the end of the tab panel.
|
// add it at the end of the tab panel.
|
||||||
@ -127,7 +125,6 @@ wxString L_str(const std::string &str);
|
|||||||
// Return wxString from std::string in UTF8
|
// Return wxString from std::string in UTF8
|
||||||
wxString from_u8(const std::string &str);
|
wxString from_u8(const std::string &str);
|
||||||
|
|
||||||
wxWindow *get_widget_by_id(int id);
|
|
||||||
|
|
||||||
void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer);
|
void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer);
|
||||||
|
|
||||||
|
@ -97,9 +97,9 @@ public:
|
|||||||
if (m_fields.find(id) == m_fields.end()) return nullptr;
|
if (m_fields.find(id) == m_fields.end()) return nullptr;
|
||||||
return m_fields.at(id).get();
|
return m_fields.at(id).get();
|
||||||
}
|
}
|
||||||
bool set_value(t_config_option_key id, boost::any value) {
|
bool set_value(t_config_option_key id, boost::any value, bool change_event = false) {
|
||||||
if (m_fields.find(id) == m_fields.end()) return false;
|
if (m_fields.find(id) == m_fields.end()) return false;
|
||||||
m_fields.at(id)->set_value(value);
|
m_fields.at(id)->set_value(value, change_event);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
boost::any get_value(t_config_option_key id) {
|
boost::any get_value(t_config_option_key id) {
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
#include "PresetBundle.hpp"
|
#include "PresetBundle.hpp"
|
||||||
#include "PresetHints.hpp"
|
#include "PresetHints.hpp"
|
||||||
#include "../../libslic3r/Utils.hpp"
|
#include "../../libslic3r/Utils.hpp"
|
||||||
|
#include "slic3r/Utils/Http.hpp"
|
||||||
|
#include "slic3r/Utils/OctoPrint.hpp"
|
||||||
|
#include "BonjourDialog.hpp"
|
||||||
|
|
||||||
#include <wx/app.h>
|
#include <wx/app.h>
|
||||||
#include <wx/button.h>
|
#include <wx/button.h>
|
||||||
@ -14,6 +17,7 @@
|
|||||||
#include <wx/treectrl.h>
|
#include <wx/treectrl.h>
|
||||||
#include <wx/imaglist.h>
|
#include <wx/imaglist.h>
|
||||||
#include <wx/settings.h>
|
#include <wx/settings.h>
|
||||||
|
#include <wx/filedlg.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
|
||||||
@ -1102,39 +1106,18 @@ void TabPrinter::build()
|
|||||||
}
|
}
|
||||||
|
|
||||||
optgroup = page->new_optgroup(_(L("OctoPrint upload")));
|
optgroup = page->new_optgroup(_(L("OctoPrint upload")));
|
||||||
// # append two buttons to the Host line
|
|
||||||
auto octoprint_host_browse = [this] (wxWindow* parent) {
|
auto octoprint_host_browse = [this, optgroup] (wxWindow* parent) {
|
||||||
auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
|
auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
|
||||||
// btn->SetFont($Slic3r::GUI::small_font);
|
|
||||||
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG));
|
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG));
|
||||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
sizer->Add(btn);
|
sizer->Add(btn);
|
||||||
|
|
||||||
if (m_is_disabled_button_browse)
|
btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent e) {
|
||||||
btn->Disable();
|
BonjourDialog dialog(parent);
|
||||||
|
if (dialog.show_and_lookup()) {
|
||||||
btn->Bind(wxEVT_BUTTON, [this, parent](wxCommandEvent e){
|
optgroup->set_value("octoprint_host", std::move(dialog.get_selected()), true);
|
||||||
if (m_event_button_browse > 0){
|
|
||||||
wxCommandEvent event(m_event_button_browse);
|
|
||||||
event.SetString("Button BROWSE was clicked!");
|
|
||||||
g_wxMainFrame->ProcessWindowEvent(event);
|
|
||||||
}
|
}
|
||||||
// // # look for devices
|
|
||||||
// auto entries;
|
|
||||||
// {
|
|
||||||
// my $res = Net::Bonjour->new('http');
|
|
||||||
// $res->discover;
|
|
||||||
// $entries = [$res->entries];
|
|
||||||
// }
|
|
||||||
// if (@{$entries}) {
|
|
||||||
// my $dlg = Slic3r::GUI::BonjourBrowser->new($self, $entries);
|
|
||||||
// $self->_load_key_value('octoprint_host', $dlg->GetValue . ":".$dlg->GetPort)
|
|
||||||
// if $dlg->ShowModal == wxID_OK;
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// auto msg_window = new wxMessageDialog(parent, "No Bonjour device found", "Device Browser", wxOK | wxICON_INFORMATION);
|
|
||||||
// msg_window->ShowModal();
|
|
||||||
// }
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return sizer;
|
return sizer;
|
||||||
@ -1143,33 +1126,23 @@ void TabPrinter::build()
|
|||||||
auto octoprint_host_test = [this](wxWindow* parent) {
|
auto octoprint_host_test = [this](wxWindow* parent) {
|
||||||
auto btn = m_octoprint_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")),
|
auto btn = m_octoprint_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")),
|
||||||
wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
|
wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
|
||||||
// btn->SetFont($Slic3r::GUI::small_font);
|
|
||||||
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG));
|
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG));
|
||||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
sizer->Add(btn);
|
sizer->Add(btn);
|
||||||
|
|
||||||
btn->Bind(wxEVT_BUTTON, [this, parent](wxCommandEvent e) {
|
btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) {
|
||||||
if (m_event_button_test > 0){
|
OctoPrint octoprint(m_config);
|
||||||
wxCommandEvent event(m_event_button_test);
|
wxString msg;
|
||||||
event.SetString("Button TEST was clicked!");
|
if (octoprint.test(msg)) {
|
||||||
g_wxMainFrame->ProcessWindowEvent(event);
|
show_info(this, _(L("Connection to OctoPrint works correctly.")), _(L("Success!")));
|
||||||
|
} else {
|
||||||
|
const auto text = wxString::Format("%s: %s\n\n%s",
|
||||||
|
_(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required."))
|
||||||
|
);
|
||||||
|
show_error(this, text);
|
||||||
}
|
}
|
||||||
// my $ua = LWP::UserAgent->new;
|
|
||||||
// $ua->timeout(10);
|
|
||||||
//
|
|
||||||
// my $res = $ua->get(
|
|
||||||
// "http://".$self->{config}->octoprint_host . "/api/version",
|
|
||||||
// 'X-Api-Key' = > $self->{config}->octoprint_apikey,
|
|
||||||
// );
|
|
||||||
// if ($res->is_success) {
|
|
||||||
// show_info(parent, "Connection to OctoPrint works correctly.", "Success!");
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// show_error(parent,
|
|
||||||
// "I wasn't able to connect to OctoPrint (".$res->status_line . "). "
|
|
||||||
// . "Check hostname and OctoPrint version (at least 1.1.0 is required).");
|
|
||||||
// }
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return sizer;
|
return sizer;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1179,6 +1152,45 @@ void TabPrinter::build()
|
|||||||
optgroup->append_line(host_line);
|
optgroup->append_line(host_line);
|
||||||
optgroup->append_single_option_line("octoprint_apikey");
|
optgroup->append_single_option_line("octoprint_apikey");
|
||||||
|
|
||||||
|
if (Http::ca_file_supported()) {
|
||||||
|
|
||||||
|
Line cafile_line = optgroup->create_single_option_line("octoprint_cafile");
|
||||||
|
|
||||||
|
auto octoprint_cafile_browse = [this, optgroup] (wxWindow* parent) {
|
||||||
|
auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
|
||||||
|
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG));
|
||||||
|
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
sizer->Add(btn);
|
||||||
|
|
||||||
|
btn->Bind(wxEVT_BUTTON, [this, optgroup] (wxCommandEvent e){
|
||||||
|
static const auto filemasks = _(L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*"));
|
||||||
|
wxFileDialog openFileDialog(this, _(L("Open CA certificate file")), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||||
|
if (openFileDialog.ShowModal() != wxID_CANCEL) {
|
||||||
|
optgroup->set_value("octoprint_cafile", std::move(openFileDialog.GetPath()), true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return sizer;
|
||||||
|
};
|
||||||
|
|
||||||
|
cafile_line.append_widget(octoprint_cafile_browse);
|
||||||
|
optgroup->append_line(cafile_line);
|
||||||
|
|
||||||
|
auto octoprint_cafile_hint = [this, optgroup] (wxWindow* parent) {
|
||||||
|
auto txt = new wxStaticText(parent, wxID_ANY,
|
||||||
|
_(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate.")));
|
||||||
|
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
sizer->Add(txt);
|
||||||
|
return sizer;
|
||||||
|
};
|
||||||
|
|
||||||
|
Line cafile_hint { "", "" };
|
||||||
|
cafile_hint.full_width = 1;
|
||||||
|
cafile_hint.widget = std::move(octoprint_cafile_hint);
|
||||||
|
optgroup->append_line(cafile_hint);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
optgroup = page->new_optgroup(_(L("Firmware")));
|
optgroup = page->new_optgroup(_(L("Firmware")));
|
||||||
optgroup->append_single_option_line("gcode_flavor");
|
optgroup->append_single_option_line("gcode_flavor");
|
||||||
|
|
||||||
@ -1337,12 +1349,7 @@ void TabPrinter::update(){
|
|||||||
m_serial_test_btn->Disable();
|
m_serial_test_btn->Disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
en = !m_config->opt_string("octoprint_host").empty();
|
m_octoprint_host_test_btn->Enable(!m_config->opt_string("octoprint_host").empty());
|
||||||
if ( en && m_is_user_agent)
|
|
||||||
m_octoprint_host_test_btn->Enable();
|
|
||||||
else
|
|
||||||
m_octoprint_host_test_btn->Disable();
|
|
||||||
get_field("octoprint_apikey")->toggle(en);
|
|
||||||
|
|
||||||
bool have_multiple_extruders = m_extruders_count > 1;
|
bool have_multiple_extruders = m_extruders_count > 1;
|
||||||
get_field("toolchange_gcode")->toggle(have_multiple_extruders);
|
get_field("toolchange_gcode")->toggle(have_multiple_extruders);
|
||||||
|
@ -214,11 +214,6 @@ public:
|
|||||||
//Slic3r::GUI::Tab::Printer;
|
//Slic3r::GUI::Tab::Printer;
|
||||||
class TabPrinter : public Tab
|
class TabPrinter : public Tab
|
||||||
{
|
{
|
||||||
bool m_is_disabled_button_browse;
|
|
||||||
bool m_is_user_agent;
|
|
||||||
// similar event by clicking Buttons "Browse" & "Test"
|
|
||||||
wxEventType m_event_button_browse = 0;
|
|
||||||
wxEventType m_event_button_test = 0;
|
|
||||||
public:
|
public:
|
||||||
wxButton* m_serial_test_btn;
|
wxButton* m_serial_test_btn;
|
||||||
wxButton* m_octoprint_host_test_btn;
|
wxButton* m_octoprint_host_test_btn;
|
||||||
@ -228,10 +223,7 @@ public:
|
|||||||
std::vector<PageShp> m_extruder_pages;
|
std::vector<PageShp> m_extruder_pages;
|
||||||
|
|
||||||
TabPrinter() {}
|
TabPrinter() {}
|
||||||
TabPrinter(wxNotebook* parent, bool no_controller, bool is_disabled_btn_browse, bool is_user_agent) :
|
TabPrinter(wxNotebook* parent, bool no_controller) : Tab(parent, _(L("Printer Settings")), "printer", no_controller) {}
|
||||||
Tab(parent, _(L("Printer Settings")), "printer", no_controller),
|
|
||||||
m_is_disabled_button_browse(is_disabled_btn_browse),
|
|
||||||
m_is_user_agent(is_user_agent) {}
|
|
||||||
~TabPrinter(){}
|
~TabPrinter(){}
|
||||||
|
|
||||||
void build() override;
|
void build() override;
|
||||||
@ -240,10 +232,6 @@ public:
|
|||||||
void extruders_count_changed(size_t extruders_count);
|
void extruders_count_changed(size_t extruders_count);
|
||||||
void build_extruder_pages();
|
void build_extruder_pages();
|
||||||
void on_preset_loaded() override;
|
void on_preset_loaded() override;
|
||||||
|
|
||||||
// Set the events to the callbacks posted to the main frame window (currently implemented in Perl).
|
|
||||||
void set_event_button_browse(wxEventType evt) { m_event_button_browse = evt; }
|
|
||||||
void set_event_button_test(wxEventType evt) { m_event_button_test = evt; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class SavePresetWindow :public wxDialog
|
class SavePresetWindow :public wxDialog
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
#include "Bonjour.hpp"
|
#include "Bonjour.hpp"
|
||||||
|
|
||||||
#include <iostream> // XXX
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <unordered_map>
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -23,16 +21,18 @@ namespace asio = boost::asio;
|
|||||||
using boost::asio::ip::udp;
|
using boost::asio::ip::udp;
|
||||||
|
|
||||||
|
|
||||||
// TODO: Fuzzing test (done without TXT)
|
|
||||||
// FIXME: check char retype to unsigned
|
|
||||||
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
|
||||||
// Minimal implementation of a MDNS/DNS-SD client
|
// Minimal implementation of a MDNS/DNS-SD client
|
||||||
// This implementation is extremely simple, only the bits that are useful
|
// This implementation is extremely simple, only the bits that are useful
|
||||||
// for very basic MDNS discovery are present.
|
// for basic MDNS discovery of OctoPi devices are present.
|
||||||
|
// However, the bits that are present are implemented with security in mind.
|
||||||
|
// Only fully correct DNS replies are allowed through.
|
||||||
|
// While decoding the decoder will bail the moment it encounters anything fishy.
|
||||||
|
// At least that's the idea. To help prove this is actually the case,
|
||||||
|
// the implementations has been tested with AFL.
|
||||||
|
|
||||||
|
|
||||||
struct DnsName: public std::string
|
struct DnsName: public std::string
|
||||||
{
|
{
|
||||||
@ -48,8 +48,7 @@ struct DnsName: public std::string
|
|||||||
return boost::none;
|
return boost::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for recursion depth to prevent parsing names that are nested too deeply
|
// Check for recursion depth to prevent parsing names that are nested too deeply or end up cyclic:
|
||||||
// or end up cyclic:
|
|
||||||
if (depth >= MAX_RECURSION) {
|
if (depth >= MAX_RECURSION) {
|
||||||
return boost::none;
|
return boost::none;
|
||||||
}
|
}
|
||||||
@ -443,6 +442,30 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream &os, const DnsMessage &msg)
|
||||||
|
{
|
||||||
|
os << "DnsMessage(ID: " << msg.header.id << ", "
|
||||||
|
<< "Q: " << (msg.question ? msg.question->name.c_str() : "none") << ", "
|
||||||
|
<< "A: " << (msg.rr_a ? msg.rr_a->ip.to_string() : "none") << ", "
|
||||||
|
<< "AAAA: " << (msg.rr_aaaa ? msg.rr_aaaa->ip.to_string() : "none") << ", "
|
||||||
|
<< "services: [";
|
||||||
|
|
||||||
|
enum { SRV_PRINT_MAX = 3 };
|
||||||
|
unsigned i = 0;
|
||||||
|
for (const auto &sdpair : msg.sdmap) {
|
||||||
|
os << sdpair.first << ", ";
|
||||||
|
|
||||||
|
if (++i >= SRV_PRINT_MAX) {
|
||||||
|
os << "...";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
os << "])";
|
||||||
|
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct BonjourRequest
|
struct BonjourRequest
|
||||||
{
|
{
|
||||||
@ -515,6 +538,7 @@ struct Bonjour::priv
|
|||||||
const std::string protocol;
|
const std::string protocol;
|
||||||
const std::string service_dn;
|
const std::string service_dn;
|
||||||
unsigned timeout;
|
unsigned timeout;
|
||||||
|
unsigned retries;
|
||||||
uint16_t rq_id;
|
uint16_t rq_id;
|
||||||
|
|
||||||
std::vector<char> buffer;
|
std::vector<char> buffer;
|
||||||
@ -524,6 +548,7 @@ struct Bonjour::priv
|
|||||||
|
|
||||||
priv(std::string service, std::string protocol);
|
priv(std::string service, std::string protocol);
|
||||||
|
|
||||||
|
std::string strip_service_dn(const std::string &service_name) const;
|
||||||
void udp_receive(udp::endpoint from, size_t bytes);
|
void udp_receive(udp::endpoint from, size_t bytes);
|
||||||
void lookup_perform();
|
void lookup_perform();
|
||||||
};
|
};
|
||||||
@ -533,11 +558,26 @@ Bonjour::priv::priv(std::string service, std::string protocol) :
|
|||||||
protocol(std::move(protocol)),
|
protocol(std::move(protocol)),
|
||||||
service_dn((boost::format("_%1%._%2%.local") % this->service % this->protocol).str()),
|
service_dn((boost::format("_%1%._%2%.local") % this->service % this->protocol).str()),
|
||||||
timeout(10),
|
timeout(10),
|
||||||
|
retries(1),
|
||||||
rq_id(0)
|
rq_id(0)
|
||||||
{
|
{
|
||||||
buffer.resize(DnsMessage::MAX_SIZE);
|
buffer.resize(DnsMessage::MAX_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Bonjour::priv::strip_service_dn(const std::string &service_name) const
|
||||||
|
{
|
||||||
|
if (service_name.size() <= service_dn.size()) {
|
||||||
|
return service_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto needle = service_name.rfind(service_dn);
|
||||||
|
if (needle == service_name.size() - service_dn.size()) {
|
||||||
|
return service_name.substr(0, needle - 1);
|
||||||
|
} else {
|
||||||
|
return service_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes)
|
void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes)
|
||||||
{
|
{
|
||||||
if (bytes == 0 || !replyfn) {
|
if (bytes == 0 || !replyfn) {
|
||||||
@ -557,7 +597,10 @@ void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto &srv = *sdpair.second.srv;
|
const auto &srv = *sdpair.second.srv;
|
||||||
BonjourReply reply(ip, sdpair.first, srv.hostname);
|
auto service_name = strip_service_dn(sdpair.first);
|
||||||
|
|
||||||
|
std::string path;
|
||||||
|
std::string version;
|
||||||
|
|
||||||
if (sdpair.second.txt) {
|
if (sdpair.second.txt) {
|
||||||
static const std::string tag_path = "path=";
|
static const std::string tag_path = "path=";
|
||||||
@ -565,13 +608,14 @@ void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes)
|
|||||||
|
|
||||||
for (const auto &value : sdpair.second.txt->values) {
|
for (const auto &value : sdpair.second.txt->values) {
|
||||||
if (value.size() > tag_path.size() && value.compare(0, tag_path.size(), tag_path) == 0) {
|
if (value.size() > tag_path.size() && value.compare(0, tag_path.size(), tag_path) == 0) {
|
||||||
reply.path = value.substr(tag_path.size());
|
path = std::move(value.substr(tag_path.size()));
|
||||||
} else if (value.size() > tag_version.size() && value.compare(0, tag_version.size(), tag_version) == 0) {
|
} else if (value.size() > tag_version.size() && value.compare(0, tag_version.size(), tag_version) == 0) {
|
||||||
reply.version = value.substr(tag_version.size());
|
version = std::move(value.substr(tag_version.size()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BonjourReply reply(ip, srv.port, std::move(service_name), srv.hostname, std::move(path), std::move(version));
|
||||||
replyfn(std::move(reply));
|
replyfn(std::move(reply));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -595,15 +639,26 @@ void Bonjour::priv::lookup_perform()
|
|||||||
udp::endpoint mcast(BonjourRequest::MCAST_IP4, BonjourRequest::MCAST_PORT);
|
udp::endpoint mcast(BonjourRequest::MCAST_IP4, BonjourRequest::MCAST_PORT);
|
||||||
socket.send_to(asio::buffer(brq->data), mcast);
|
socket.send_to(asio::buffer(brq->data), mcast);
|
||||||
|
|
||||||
bool timeout = false;
|
bool expired = false;
|
||||||
|
bool retry = false;
|
||||||
asio::deadline_timer timer(io_service);
|
asio::deadline_timer timer(io_service);
|
||||||
timer.expires_from_now(boost::posix_time::seconds(10));
|
retries--;
|
||||||
timer.async_wait([=, &timeout](const error_code &error) {
|
std::function<void(const error_code &)> timer_handler = [&](const error_code &error) {
|
||||||
timeout = true;
|
if (retries == 0 || error) {
|
||||||
|
expired = true;
|
||||||
if (self->completefn) {
|
if (self->completefn) {
|
||||||
self->completefn();
|
self->completefn();
|
||||||
}
|
}
|
||||||
});
|
} else {
|
||||||
|
retry = true;
|
||||||
|
retries--;
|
||||||
|
timer.expires_from_now(boost::posix_time::seconds(timeout));
|
||||||
|
timer.async_wait(timer_handler);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
timer.expires_from_now(boost::posix_time::seconds(timeout));
|
||||||
|
timer.async_wait(timer_handler);
|
||||||
|
|
||||||
udp::endpoint recv_from;
|
udp::endpoint recv_from;
|
||||||
const auto recv_handler = [&](const error_code &error, size_t bytes) {
|
const auto recv_handler = [&](const error_code &error, size_t bytes) {
|
||||||
@ -612,8 +667,11 @@ void Bonjour::priv::lookup_perform()
|
|||||||
socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler);
|
socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler);
|
||||||
|
|
||||||
while (io_service.run_one()) {
|
while (io_service.run_one()) {
|
||||||
if (timeout) {
|
if (expired) {
|
||||||
socket.cancel();
|
socket.cancel();
|
||||||
|
} else if (retry) {
|
||||||
|
retry = false;
|
||||||
|
socket.send_to(asio::buffer(brq->data), mcast);
|
||||||
} else {
|
} else {
|
||||||
buffer.resize(DnsMessage::MAX_SIZE);
|
buffer.resize(DnsMessage::MAX_SIZE);
|
||||||
socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler);
|
socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler);
|
||||||
@ -626,13 +684,39 @@ void Bonjour::priv::lookup_perform()
|
|||||||
|
|
||||||
// API - public part
|
// API - public part
|
||||||
|
|
||||||
BonjourReply::BonjourReply(boost::asio::ip::address ip, std::string service_name, std::string hostname) :
|
BonjourReply::BonjourReply(boost::asio::ip::address ip, uint16_t port, std::string service_name, std::string hostname, std::string path, std::string version) :
|
||||||
ip(std::move(ip)),
|
ip(std::move(ip)),
|
||||||
|
port(port),
|
||||||
service_name(std::move(service_name)),
|
service_name(std::move(service_name)),
|
||||||
hostname(std::move(hostname)),
|
hostname(std::move(hostname)),
|
||||||
path("/"),
|
path(path.empty() ? std::move(std::string("/")) : std::move(path)),
|
||||||
version("Unknown")
|
version(version.empty() ? std::move(std::string("Unknown")) : std::move(version))
|
||||||
{}
|
{
|
||||||
|
std::string proto;
|
||||||
|
std::string port_suffix;
|
||||||
|
if (port == 443) { proto = "https://"; }
|
||||||
|
if (port != 443 && port != 80) { port_suffix = std::to_string(port).insert(0, 1, ':'); }
|
||||||
|
if (this->path[0] != '/') { this->path.insert(0, 1, '/'); }
|
||||||
|
full_address = proto + ip.to_string() + port_suffix;
|
||||||
|
if (this->path != "/") { full_address += path; }
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BonjourReply::operator==(const BonjourReply &other) const
|
||||||
|
{
|
||||||
|
return this->full_address == other.full_address
|
||||||
|
&& this->service_name == other.service_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BonjourReply::operator<(const BonjourReply &other) const
|
||||||
|
{
|
||||||
|
if (this->ip != other.ip) {
|
||||||
|
// So that the common case doesn't involve string comparison
|
||||||
|
return this->ip < other.ip;
|
||||||
|
} else {
|
||||||
|
auto cmp = this->full_address.compare(other.full_address);
|
||||||
|
return cmp != 0 ? cmp < 0 : this->service_name < other.service_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream &os, const BonjourReply &reply)
|
std::ostream& operator<<(std::ostream &os, const BonjourReply &reply)
|
||||||
{
|
{
|
||||||
@ -641,6 +725,7 @@ std::ostream& operator<<(std::ostream &os, const BonjourReply &reply)
|
|||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Bonjour::Bonjour(std::string service, std::string protocol) :
|
Bonjour::Bonjour(std::string service, std::string protocol) :
|
||||||
p(new priv(std::move(service), std::move(protocol)))
|
p(new priv(std::move(service), std::move(protocol)))
|
||||||
{}
|
{}
|
||||||
@ -660,6 +745,12 @@ Bonjour& Bonjour::set_timeout(unsigned timeout)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bonjour& Bonjour::set_retries(unsigned retries)
|
||||||
|
{
|
||||||
|
if (p && retries > 0) { p->retries = retries; }
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
Bonjour& Bonjour::on_reply(ReplyFn fn)
|
Bonjour& Bonjour::on_reply(ReplyFn fn)
|
||||||
{
|
{
|
||||||
if (p) { p->replyfn = std::move(fn); }
|
if (p) { p->replyfn = std::move(fn); }
|
||||||
@ -687,18 +778,4 @@ Bonjour::Ptr Bonjour::lookup()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Bonjour::pokus() // XXX
|
|
||||||
{
|
|
||||||
auto bonjour = Bonjour("octoprint")
|
|
||||||
.set_timeout(15)
|
|
||||||
.on_reply([](BonjourReply &&reply) {
|
|
||||||
std::cerr << "BonjourReply: " << reply << std::endl;
|
|
||||||
})
|
|
||||||
.on_complete([](){
|
|
||||||
std::cerr << "MDNS lookup complete" << std::endl;
|
|
||||||
})
|
|
||||||
.lookup();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,31 @@
|
|||||||
#ifndef slic3r_Bonjour_hpp_
|
#ifndef slic3r_Bonjour_hpp_
|
||||||
#define slic3r_Bonjour_hpp_
|
#define slic3r_Bonjour_hpp_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
// #include <ostream>
|
|
||||||
#include <boost/asio/ip/address.hpp>
|
#include <boost/asio/ip/address.hpp>
|
||||||
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
|
||||||
// TODO: reply data structure
|
|
||||||
struct BonjourReply
|
struct BonjourReply
|
||||||
{
|
{
|
||||||
boost::asio::ip::address ip;
|
boost::asio::ip::address ip;
|
||||||
|
uint16_t port;
|
||||||
std::string service_name;
|
std::string service_name;
|
||||||
std::string hostname;
|
std::string hostname;
|
||||||
|
std::string full_address;
|
||||||
std::string path;
|
std::string path;
|
||||||
std::string version;
|
std::string version;
|
||||||
|
|
||||||
BonjourReply(boost::asio::ip::address ip, std::string service_name, std::string hostname);
|
BonjourReply() = delete;
|
||||||
|
BonjourReply(boost::asio::ip::address ip, uint16_t port, std::string service_name, std::string hostname, std::string path, std::string version);
|
||||||
|
|
||||||
|
bool operator==(const BonjourReply &other) const;
|
||||||
|
bool operator<(const BonjourReply &other) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream &, const BonjourReply &);
|
std::ostream& operator<<(std::ostream &, const BonjourReply &);
|
||||||
@ -32,7 +37,7 @@ private:
|
|||||||
struct priv;
|
struct priv;
|
||||||
public:
|
public:
|
||||||
typedef std::shared_ptr<Bonjour> Ptr;
|
typedef std::shared_ptr<Bonjour> Ptr;
|
||||||
typedef std::function<void(BonjourReply &&reply)> ReplyFn;
|
typedef std::function<void(BonjourReply &&)> ReplyFn;
|
||||||
typedef std::function<void()> CompleteFn;
|
typedef std::function<void()> CompleteFn;
|
||||||
|
|
||||||
Bonjour(std::string service, std::string protocol = "tcp");
|
Bonjour(std::string service, std::string protocol = "tcp");
|
||||||
@ -40,12 +45,15 @@ public:
|
|||||||
~Bonjour();
|
~Bonjour();
|
||||||
|
|
||||||
Bonjour& set_timeout(unsigned timeout);
|
Bonjour& set_timeout(unsigned timeout);
|
||||||
|
Bonjour& set_retries(unsigned retries);
|
||||||
|
// ^ 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.
|
||||||
|
|
||||||
Bonjour& on_reply(ReplyFn fn);
|
Bonjour& on_reply(ReplyFn fn);
|
||||||
Bonjour& on_complete(CompleteFn fn);
|
Bonjour& on_complete(CompleteFn fn);
|
||||||
|
|
||||||
Ptr lookup();
|
Ptr lookup();
|
||||||
|
|
||||||
static void pokus(); // XXX: remove
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<priv> p;
|
std::unique_ptr<priv> p;
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <iostream>
|
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
|
|
||||||
@ -45,7 +44,9 @@ struct Http::priv
|
|||||||
priv(const std::string &url);
|
priv(const std::string &url);
|
||||||
~priv();
|
~priv();
|
||||||
|
|
||||||
|
static bool ca_file_supported(::CURL *curl);
|
||||||
static size_t writecb(void *data, size_t size, size_t nmemb, void *userp);
|
static size_t writecb(void *data, size_t size, size_t nmemb, void *userp);
|
||||||
|
std::string curl_error(CURLcode curlcode);
|
||||||
std::string body_size_error();
|
std::string body_size_error();
|
||||||
void http_perform();
|
void http_perform();
|
||||||
};
|
};
|
||||||
@ -71,6 +72,29 @@ Http::priv::~priv()
|
|||||||
::curl_slist_free_all(headerlist);
|
::curl_slist_free_all(headerlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Http::priv::ca_file_supported(::CURL *curl)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
bool res = false;
|
||||||
|
#else
|
||||||
|
bool res = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (curl == nullptr) { return res; }
|
||||||
|
|
||||||
|
#if LIBCURL_VERSION_MAJOR >= 7 && LIBCURL_VERSION_MINOR >= 48
|
||||||
|
::curl_tlssessioninfo *tls;
|
||||||
|
if (::curl_easy_getinfo(curl, CURLINFO_TLS_SSL_PTR, &tls) == CURLE_OK) {
|
||||||
|
if (tls->backend == CURLSSLBACKEND_SCHANNEL || tls->backend == CURLSSLBACKEND_DARWINSSL) {
|
||||||
|
// With Windows and OS X native SSL support, cert files cannot be set
|
||||||
|
res = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
size_t Http::priv::writecb(void *data, size_t size, size_t nmemb, void *userp)
|
size_t Http::priv::writecb(void *data, size_t size, size_t nmemb, void *userp)
|
||||||
{
|
{
|
||||||
auto self = static_cast<priv*>(userp);
|
auto self = static_cast<priv*>(userp);
|
||||||
@ -88,6 +112,14 @@ size_t Http::priv::writecb(void *data, size_t size, size_t nmemb, void *userp)
|
|||||||
return realsize;
|
return realsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Http::priv::curl_error(CURLcode curlcode)
|
||||||
|
{
|
||||||
|
return (boost::format("%1% (%2%)")
|
||||||
|
% ::curl_easy_strerror(curlcode)
|
||||||
|
% curlcode
|
||||||
|
).str();
|
||||||
|
}
|
||||||
|
|
||||||
std::string Http::priv::body_size_error()
|
std::string Http::priv::body_size_error()
|
||||||
{
|
{
|
||||||
return (boost::format("HTTP body data size exceeded limit (%1% bytes)") % limit).str();
|
return (boost::format("HTTP body data size exceeded limit (%1% bytes)") % limit).str();
|
||||||
@ -121,7 +153,7 @@ void Http::priv::http_perform()
|
|||||||
if (res == CURLE_WRITE_ERROR) {
|
if (res == CURLE_WRITE_ERROR) {
|
||||||
error = std::move(body_size_error());
|
error = std::move(body_size_error());
|
||||||
} else {
|
} else {
|
||||||
error = ::curl_easy_strerror(res);
|
error = std::move(curl_error(res));
|
||||||
};
|
};
|
||||||
|
|
||||||
if (errorfn) {
|
if (errorfn) {
|
||||||
@ -180,7 +212,7 @@ Http& Http::remove_header(std::string name)
|
|||||||
|
|
||||||
Http& Http::ca_file(const std::string &name)
|
Http& Http::ca_file(const std::string &name)
|
||||||
{
|
{
|
||||||
if (p) {
|
if (p && priv::ca_file_supported(p->curl)) {
|
||||||
::curl_easy_setopt(p->curl, CURLOPT_CAINFO, name.c_str());
|
::curl_easy_setopt(p->curl, CURLOPT_CAINFO, name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,5 +289,13 @@ Http Http::post(std::string url)
|
|||||||
return http;
|
return http;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Http::ca_file_supported()
|
||||||
|
{
|
||||||
|
::CURL *curl = ::curl_easy_init();
|
||||||
|
bool res = priv::ca_file_supported(curl);
|
||||||
|
if (curl != nullptr) { ::curl_easy_cleanup(curl); }
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ public:
|
|||||||
Ptr perform();
|
Ptr perform();
|
||||||
void perform_sync();
|
void perform_sync();
|
||||||
|
|
||||||
|
static bool ca_file_supported();
|
||||||
private:
|
private:
|
||||||
Http(const std::string &url);
|
Http(const std::string &url);
|
||||||
|
|
||||||
|
@ -20,16 +20,19 @@ OctoPrint::OctoPrint(DynamicPrintConfig *config) :
|
|||||||
cafile(config->opt_string("octoprint_cafile"))
|
cafile(config->opt_string("octoprint_cafile"))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
std::string OctoPrint::test() const
|
bool OctoPrint::test(wxString &msg) const
|
||||||
{
|
{
|
||||||
// Since the request is performed synchronously here,
|
// Since the request is performed synchronously here,
|
||||||
// it is ok to refer to `res` from within the closure
|
// it is ok to refer to `msg` from within the closure
|
||||||
std::string res;
|
|
||||||
|
|
||||||
auto http = Http::get(std::move(make_url("api/version")));
|
bool res = true;
|
||||||
|
|
||||||
|
auto url = std::move(make_url("api/version"));
|
||||||
|
auto http = Http::get(std::move(url));
|
||||||
set_auth(http);
|
set_auth(http);
|
||||||
http.on_error([&](std::string, std::string error, unsigned status) {
|
http.on_error([&](std::string, std::string error, unsigned status) {
|
||||||
res = format_error(error, status);
|
res = false;
|
||||||
|
msg = format_error(error, status);
|
||||||
})
|
})
|
||||||
.perform_sync();
|
.perform_sync();
|
||||||
|
|
||||||
@ -43,21 +46,26 @@ void OctoPrint::send_gcode(int windowId, int completeEvt, int errorEvt, const st
|
|||||||
http.form_add("print", print ? "true" : "false")
|
http.form_add("print", print ? "true" : "false")
|
||||||
.form_add_file("file", filename)
|
.form_add_file("file", filename)
|
||||||
.on_complete([=](std::string body, unsigned status) {
|
.on_complete([=](std::string body, unsigned status) {
|
||||||
wxWindow *window = GUI::get_widget_by_id(windowId);
|
wxWindow *window = wxWindow::FindWindowById(windowId);
|
||||||
|
if (window == nullptr) { return; }
|
||||||
|
|
||||||
wxCommandEvent* evt = new wxCommandEvent(completeEvt);
|
wxCommandEvent* evt = new wxCommandEvent(completeEvt);
|
||||||
evt->SetString("G-code file successfully uploaded to the OctoPrint server");
|
evt->SetString(_(L("G-code file successfully uploaded to the OctoPrint server")));
|
||||||
evt->SetInt(100);
|
evt->SetInt(100);
|
||||||
wxQueueEvent(window, evt);
|
wxQueueEvent(window, evt);
|
||||||
})
|
})
|
||||||
.on_error([=](std::string body, std::string error, unsigned status) {
|
.on_error([=](std::string body, std::string error, unsigned status) {
|
||||||
wxWindow *window = GUI::get_widget_by_id(windowId);
|
wxWindow *window = wxWindow::FindWindowById(windowId);
|
||||||
|
if (window == nullptr) { return; }
|
||||||
|
|
||||||
wxCommandEvent* evt_complete = new wxCommandEvent(completeEvt);
|
wxCommandEvent* evt_complete = new wxCommandEvent(completeEvt);
|
||||||
evt_complete->SetInt(100);
|
evt_complete->SetInt(100);
|
||||||
wxQueueEvent(window, evt_complete);
|
wxQueueEvent(window, evt_complete);
|
||||||
|
|
||||||
wxCommandEvent* evt_error = new wxCommandEvent(errorEvt);
|
wxCommandEvent* evt_error = new wxCommandEvent(errorEvt);
|
||||||
evt_error->SetString(wxString::Format("Error while uploading to the OctoPrint server: %s", format_error(error, status)));
|
evt_error->SetString(wxString::Format("%s: %s",
|
||||||
|
_(L("Error while uploading to the OctoPrint server")),
|
||||||
|
format_error(error, status)));
|
||||||
wxQueueEvent(window, evt_error);
|
wxQueueEvent(window, evt_error);
|
||||||
})
|
})
|
||||||
.perform();
|
.perform();
|
||||||
@ -85,19 +93,15 @@ std::string OctoPrint::make_url(const std::string &path) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string OctoPrint::format_error(std::string error, unsigned status)
|
wxString OctoPrint::format_error(std::string error, unsigned status)
|
||||||
{
|
{
|
||||||
|
const wxString wxerror = error;
|
||||||
|
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
std::string res{"HTTP "};
|
return wxString::Format("HTTP %u: %s", status,
|
||||||
res.append(std::to_string(status));
|
(status == 401 ? _(L("Invalid API key")) : wxerror));
|
||||||
|
|
||||||
if (status == 401) {
|
|
||||||
res.append(": Invalid API key");
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::move(res);
|
|
||||||
} else {
|
} else {
|
||||||
return std::move(error);
|
return std::move(wxerror);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
#define slic3r_OctoPrint_hpp_
|
#define slic3r_OctoPrint_hpp_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <wx/string.h>
|
||||||
|
|
||||||
// #include "Http.hpp" // XXX: ?
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -16,8 +16,7 @@ class OctoPrint
|
|||||||
public:
|
public:
|
||||||
OctoPrint(DynamicPrintConfig *config);
|
OctoPrint(DynamicPrintConfig *config);
|
||||||
|
|
||||||
std::string test() const;
|
bool test(wxString &curl_msg) const;
|
||||||
// XXX: style
|
|
||||||
void send_gcode(int windowId, int completeEvt, int errorEvt, const std::string &filename, bool print = false) const;
|
void send_gcode(int windowId, int completeEvt, int errorEvt, const std::string &filename, bool print = false) const;
|
||||||
private:
|
private:
|
||||||
std::string host;
|
std::string host;
|
||||||
@ -26,7 +25,7 @@ private:
|
|||||||
|
|
||||||
void set_auth(Http &http) const;
|
void set_auth(Http &http) const;
|
||||||
std::string make_url(const std::string &path) const;
|
std::string make_url(const std::string &path) const;
|
||||||
static std::string format_error(std::string error, unsigned status);
|
static wxString format_error(std::string error, unsigned status);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,12 +35,8 @@ void set_tab_panel(SV *ui)
|
|||||||
void add_debug_menu(SV *ui, int event_language_change)
|
void add_debug_menu(SV *ui, int event_language_change)
|
||||||
%code%{ Slic3r::GUI::add_debug_menu((wxMenuBar*)wxPli_sv_2_object(aTHX_ ui, "Wx::MenuBar"), event_language_change); %};
|
%code%{ Slic3r::GUI::add_debug_menu((wxMenuBar*)wxPli_sv_2_object(aTHX_ ui, "Wx::MenuBar"), event_language_change); %};
|
||||||
|
|
||||||
void create_preset_tabs(bool no_controller, bool is_disabled_button_browse, bool is_user_agent,
|
void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed)
|
||||||
int event_value_change, int event_presets_changed,
|
%code%{ Slic3r::GUI::create_preset_tabs(no_controller, event_value_change, event_presets_changed); %};
|
||||||
int event_button_browse, int event_button_test)
|
|
||||||
%code%{ Slic3r::GUI::create_preset_tabs(no_controller, is_disabled_button_browse, is_user_agent,
|
|
||||||
event_value_change, event_presets_changed,
|
|
||||||
event_button_browse, event_button_test); %};
|
|
||||||
|
|
||||||
Ref<TabIface> get_preset_tab(char *name)
|
Ref<TabIface> get_preset_tab(char *name)
|
||||||
%code%{ RETVAL=Slic3r::GUI::get_preset_tab_iface(name); %};
|
%code%{ RETVAL=Slic3r::GUI::get_preset_tab_iface(name); %};
|
||||||
|
@ -9,6 +9,5 @@
|
|||||||
OctoPrint(DynamicPrintConfig *config);
|
OctoPrint(DynamicPrintConfig *config);
|
||||||
~OctoPrint();
|
~OctoPrint();
|
||||||
|
|
||||||
std::string test() const;
|
|
||||||
void send_gcode(int windowId, int completeEvt, int errorEvt, std::string filename, bool print = false) const;
|
void send_gcode(int windowId, int completeEvt, int errorEvt, std::string filename, bool print = false) const;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user