Octoprint (#804)

* Octoprint progress dialog

* Fix curl version on Windows
This commit is contained in:
Vojtech Kral 2018-04-04 11:18:22 +02:00 committed by bubnikv
parent 00324a14b8
commit b0840065ed
8 changed files with 136 additions and 41 deletions

View File

@ -1,8 +1,9 @@
# Building Slic3r PE on Microsoft Windows # Building Slic3r PE on Microsoft Windows
The currently supported way of building Slic3r PE on Windows is with MS Visual Studio 2013 The currently supported way of building Slic3r PE on Windows is with CMake and MS Visual Studio 2013
using our Perl binary distribution (compiled from official Perl sources). using our Perl binary distribution (compiled from official Perl sources).
You can use the free [Visual Studio 2013 Community Edition](https://www.visualstudio.com/vs/older-downloads/). You can use the free [Visual Studio 2013 Community Edition](https://www.visualstudio.com/vs/older-downloads/).
CMake installer can be downloaded from [the official website](https://cmake.org/download/).
Other setups (such as mingw + Strawberry Perl) _may_ work, but we cannot guarantee this will work Other setups (such as mingw + Strawberry Perl) _may_ work, but we cannot guarantee this will work
and cannot provide guidance. and cannot provide guidance.
@ -26,8 +27,8 @@ Apart from wxWidgets and Perl, you will also need additional dependencies:
We have prepared a binary package of the listed libraries: We have prepared a binary package of the listed libraries:
- 32 bit: [slic3r-destdir-32.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=slic3r-destdir-32.7z) - 32 bit: [slic3r-destdir-32.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=2%2Fslic3r-destdir-32.7z)
- 64 bit: [slic3r-destdir-64.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=slic3r-destdir-64.7z) - 64 bit: [slic3r-destdir-64.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=2%2Fslic3r-destdir-64.7z)
It is recommended you unpack this package into `C:\local\` as the environment It is recommended you unpack this package into `C:\local\` as the environment
setup script expects it there. setup script expects it there.

View File

@ -37,7 +37,7 @@ if ($destdir -eq "") {
} }
$BOOST = 'boost_1_63_0' $BOOST = 'boost_1_63_0'
$CURL = 'curl-7.28.0' $CURL = 'curl-7.58.0'
$TBB_SHA = 'a0dc9bf76d0120f917b641ed095360448cabc85b' $TBB_SHA = 'a0dc9bf76d0120f917b641ed095360448cabc85b'
$TBB = "tbb-$TBB_SHA" $TBB = "tbb-$TBB_SHA"

View File

@ -1481,7 +1481,11 @@ sub on_export_completed {
# Send $self->{send_gcode_file} to OctoPrint. # Send $self->{send_gcode_file} to OctoPrint.
if ($send_gcode) { if ($send_gcode) {
my $op = Slic3r::OctoPrint->new($self->{config}); my $op = Slic3r::OctoPrint->new($self->{config});
$op->send_gcode($self->GetId(), $PROGRESS_BAR_EVENT, $ERROR_EVENT, $self->{send_gcode_file}); if ($op->send_gcode($self->{send_gcode_file})) {
$self->statusbar->SetStatusText(L("OctoPrint upload finished."));
} else {
$self->statusbar->SetStatusText("");
}
} }
$self->{print_file} = undef; $self->{print_file} = undef;

View File

@ -36,16 +36,20 @@ struct Http::priv
::curl_slist *headerlist; ::curl_slist *headerlist;
std::string buffer; std::string buffer;
size_t limit; size_t limit;
bool cancel;
std::thread io_thread; std::thread io_thread;
Http::CompleteFn completefn; Http::CompleteFn completefn;
Http::ErrorFn errorfn; Http::ErrorFn errorfn;
Http::ProgressFn progressfn;
priv(const std::string &url); priv(const std::string &url);
~priv(); ~priv();
static bool ca_file_supported(::CURL *curl); 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);
static int xfercb(void *userp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow);
static int xfercb_legacy(void *userp, double dltotal, double dlnow, double ultotal, double ulnow);
std::string curl_error(CURLcode curlcode); std::string curl_error(CURLcode curlcode);
std::string body_size_error(); std::string body_size_error();
void http_perform(); void http_perform();
@ -55,7 +59,8 @@ Http::priv::priv(const std::string &url) :
curl(::curl_easy_init()), curl(::curl_easy_init()),
form(nullptr), form(nullptr),
form_end(nullptr), form_end(nullptr),
headerlist(nullptr) headerlist(nullptr),
cancel(false)
{ {
if (curl == nullptr) { if (curl == nullptr) {
throw std::runtime_error(std::string("Could not construct Curl object")); throw std::runtime_error(std::string("Could not construct Curl object"));
@ -112,6 +117,24 @@ size_t Http::priv::writecb(void *data, size_t size, size_t nmemb, void *userp)
return realsize; return realsize;
} }
int Http::priv::xfercb(void *userp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
{
auto self = static_cast<priv*>(userp);
bool cb_cancel = false;
if (self->progressfn) {
Progress progress(dltotal, dlnow, ultotal, ulnow);
self->progressfn(progress, cb_cancel);
}
return self->cancel || cb_cancel;
}
int Http::priv::xfercb_legacy(void *userp, double dltotal, double dlnow, double ultotal, double ulnow)
{
return xfercb(userp, dltotal, dlnow, ultotal, ulnow);
}
std::string Http::priv::curl_error(CURLcode curlcode) std::string Http::priv::curl_error(CURLcode curlcode)
{ {
return (boost::format("%1% (%2%)") return (boost::format("%1% (%2%)")
@ -132,6 +155,16 @@ void Http::priv::http_perform()
::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writecb); ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writecb);
::curl_easy_setopt(curl, CURLOPT_WRITEDATA, static_cast<void*>(this)); ::curl_easy_setopt(curl, CURLOPT_WRITEDATA, static_cast<void*>(this));
::curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
#if LIBCURL_VERSION_MAJOR >= 7 && LIBCURL_VERSION_MINOR >= 32
::curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xfercb);
::curl_easy_setopt(curl, CURLOPT_XFERINFODATA, static_cast<void*>(this));
(void)xfercb_legacy; // prevent unused function warning
#else
::curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, xfercb);
::curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, static_cast<void*>(this));
#endif
#ifndef NDEBUG #ifndef NDEBUG
::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
#endif #endif
@ -149,16 +182,16 @@ void Http::priv::http_perform()
::curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_status); ::curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_status);
if (res != CURLE_OK) { if (res != CURLE_OK) {
std::string error; if (res == CURLE_ABORTED_BY_CALLBACK) {
if (res == CURLE_WRITE_ERROR) { Progress dummyprogress(0, 0, 0, 0);
error = std::move(body_size_error()); bool cancel = true;
} else { if (progressfn) { progressfn(dummyprogress, cancel); }
error = std::move(curl_error(res));
};
if (errorfn) {
errorfn(std::move(buffer), std::move(error), http_status);
} }
else if (res == CURLE_WRITE_ERROR) {
if (errorfn) { errorfn(std::move(buffer), std::move(body_size_error()), http_status); }
} else {
if (errorfn) { errorfn(std::move(buffer), std::move(curl_error(res)), http_status); }
};
} else { } else {
if (completefn) { if (completefn) {
completefn(std::move(buffer), http_status); completefn(std::move(buffer), http_status);
@ -258,6 +291,12 @@ Http& Http::on_error(ErrorFn fn)
return *this; return *this;
} }
Http& Http::on_progress(ProgressFn fn)
{
if (p) { p->progressfn = std::move(fn); }
return *this;
}
Http::Ptr Http::perform() Http::Ptr Http::perform()
{ {
auto self = std::make_shared<Http>(std::move(*this)); auto self = std::make_shared<Http>(std::move(*this));
@ -277,6 +316,11 @@ void Http::perform_sync()
if (p) { p->http_perform(); } if (p) { p->http_perform(); }
} }
void Http::cancel()
{
if (p) { p->cancel = true; }
}
Http Http::get(std::string url) Http Http::get(std::string url)
{ {
return std::move(Http{std::move(url)}); return std::move(Http{std::move(url)});
@ -297,5 +341,16 @@ bool Http::ca_file_supported()
return res; return res;
} }
std::ostream& operator<<(std::ostream &os, const Http::Progress &progress)
{
os << "Http::Progress("
<< "dltotal = " << progress.dltotal
<< ", dlnow = " << progress.dlnow
<< ", ultotal = " << progress.ultotal
<< ", ulnow = " << progress.ulnow
<< ")";
return os;
}
} }

View File

@ -14,9 +14,22 @@ class Http : public std::enable_shared_from_this<Http> {
private: private:
struct priv; struct priv;
public: public:
struct Progress
{
size_t dltotal;
size_t dlnow;
size_t ultotal;
size_t ulnow;
Progress(size_t dltotal, size_t dlnow, size_t ultotal, size_t ulnow) :
dltotal(dltotal), dlnow(dlnow), ultotal(ultotal), ulnow(ulnow)
{}
};
typedef std::shared_ptr<Http> Ptr; typedef std::shared_ptr<Http> Ptr;
typedef std::function<void(std::string /* body */, unsigned /* http_status */)> CompleteFn; typedef std::function<void(std::string /* body */, unsigned /* http_status */)> CompleteFn;
typedef std::function<void(std::string /* body */, std::string /* error */, unsigned /* http_status */)> ErrorFn; typedef std::function<void(std::string /* body */, std::string /* error */, unsigned /* http_status */)> ErrorFn;
typedef std::function<void(Progress, bool& /* cancel */)> ProgressFn;
Http(Http &&other); Http(Http &&other);
@ -37,9 +50,11 @@ public:
Http& on_complete(CompleteFn fn); Http& on_complete(CompleteFn fn);
Http& on_error(ErrorFn fn); Http& on_error(ErrorFn fn);
Http& on_progress(ProgressFn fn);
Ptr perform(); Ptr perform();
void perform_sync(); void perform_sync();
void cancel();
static bool ca_file_supported(); static bool ca_file_supported();
private: private:
@ -48,6 +63,8 @@ private:
std::unique_ptr<priv> p; std::unique_ptr<priv> p;
}; };
std::ostream& operator<<(std::ostream &, const Http::Progress &);
} }

View File

@ -1,10 +1,11 @@
#include "OctoPrint.hpp" #include "OctoPrint.hpp"
#include <iostream> #include <algorithm>
#include <boost/format.hpp> #include <boost/format.hpp>
#include <wx/frame.h> #include <wx/frame.h>
#include <wx/event.h> #include <wx/event.h>
#include <wx/progdlg.h>
#include "libslic3r/PrintConfig.hpp" #include "libslic3r/PrintConfig.hpp"
#include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI.hpp"
@ -39,36 +40,53 @@ bool OctoPrint::test(wxString &msg) const
return res; return res;
} }
void OctoPrint::send_gcode(int windowId, int completeEvt, int errorEvt, const std::string &filename, bool print) const bool OctoPrint::send_gcode(const std::string &filename, bool print) const
{ {
enum { PROGRESS_RANGE = 1000 };
const auto errortitle = _(L("Error while uploading to the OctoPrint server"));
wxProgressDialog progress_dialog(
_(L("OctoPrint upload")),
_(L("Sending G-code file to the OctoPrint server...")),
PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
progress_dialog.Pulse();
wxString test_msg;
if (!test(test_msg)) {
auto errormsg = wxString::Format("%s: %s", errortitle, test_msg);
GUI::show_error(&progress_dialog, std::move(errormsg));
return false;
}
bool res = true;
auto http = Http::post(std::move(make_url("api/files/local"))); auto http = Http::post(std::move(make_url("api/files/local")));
set_auth(http); set_auth(http);
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 = wxWindow::FindWindowById(windowId); progress_dialog.Update(PROGRESS_RANGE);
if (window == nullptr) { return; }
wxCommandEvent* evt = new wxCommandEvent(completeEvt);
evt->SetString(_(L("G-code file successfully uploaded to the OctoPrint server")));
evt->SetInt(100);
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 = wxWindow::FindWindowById(windowId); auto errormsg = wxString::Format("%s: %s", errortitle, format_error(error, status));
if (window == nullptr) { return; } GUI::show_error(&progress_dialog, std::move(errormsg));
res = false;
wxCommandEvent* evt_complete = new wxCommandEvent(completeEvt);
evt_complete->SetInt(100);
wxQueueEvent(window, evt_complete);
wxCommandEvent* evt_error = new wxCommandEvent(errorEvt);
evt_error->SetString(wxString::Format("%s: %s",
_(L("Error while uploading to the OctoPrint server")),
format_error(error, status)));
wxQueueEvent(window, evt_error);
}) })
.perform(); .on_progress([&](Http::Progress progress, bool &cancel) {
if (cancel) {
// Upload was canceled
res = false;
} else if (progress.ultotal > 0) {
int value = PROGRESS_RANGE * progress.ulnow / progress.ultotal;
cancel = !progress_dialog.Update(std::min(value, PROGRESS_RANGE - 1)); // Cap the value to prevent premature dialog closing
} else {
cancel = !progress_dialog.Pulse();
}
})
.perform_sync();
return res;
} }
void OctoPrint::set_auth(Http &http) const void OctoPrint::set_auth(Http &http) const

View File

@ -17,7 +17,7 @@ public:
OctoPrint(DynamicPrintConfig *config); OctoPrint(DynamicPrintConfig *config);
bool test(wxString &curl_msg) const; bool test(wxString &curl_msg) const;
void send_gcode(int windowId, int completeEvt, int errorEvt, const std::string &filename, bool print = false) const; bool send_gcode(const std::string &filename, bool print = false) const;
private: private:
std::string host; std::string host;
std::string apikey; std::string apikey;

View File

@ -9,5 +9,5 @@
OctoPrint(DynamicPrintConfig *config); OctoPrint(DynamicPrintConfig *config);
~OctoPrint(); ~OctoPrint();
void send_gcode(int windowId, int completeEvt, int errorEvt, std::string filename, bool print = false) const; bool send_gcode(std::string filename, bool print = false) const;
}; };