This commit is contained in:
bubnikv 2018-04-05 10:32:26 +02:00
commit a866011574
8 changed files with 136 additions and 41 deletions

View file

@ -36,16 +36,20 @@ struct Http::priv
::curl_slist *headerlist;
std::string buffer;
size_t limit;
bool cancel;
std::thread io_thread;
Http::CompleteFn completefn;
Http::ErrorFn errorfn;
Http::ProgressFn progressfn;
priv(const std::string &url);
~priv();
static bool ca_file_supported(::CURL *curl);
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 body_size_error();
void http_perform();
@ -55,7 +59,8 @@ Http::priv::priv(const std::string &url) :
curl(::curl_easy_init()),
form(nullptr),
form_end(nullptr),
headerlist(nullptr)
headerlist(nullptr),
cancel(false)
{
if (curl == nullptr) {
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;
}
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)
{
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_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
::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
#endif
@ -149,16 +182,16 @@ void Http::priv::http_perform()
::curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_status);
if (res != CURLE_OK) {
std::string error;
if (res == CURLE_WRITE_ERROR) {
error = std::move(body_size_error());
} else {
error = std::move(curl_error(res));
};
if (errorfn) {
errorfn(std::move(buffer), std::move(error), http_status);
if (res == CURLE_ABORTED_BY_CALLBACK) {
Progress dummyprogress(0, 0, 0, 0);
bool cancel = true;
if (progressfn) { progressfn(dummyprogress, cancel); }
}
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 {
if (completefn) {
completefn(std::move(buffer), http_status);
@ -258,6 +291,12 @@ Http& Http::on_error(ErrorFn fn)
return *this;
}
Http& Http::on_progress(ProgressFn fn)
{
if (p) { p->progressfn = std::move(fn); }
return *this;
}
Http::Ptr Http::perform()
{
auto self = std::make_shared<Http>(std::move(*this));
@ -277,6 +316,11 @@ void Http::perform_sync()
if (p) { p->http_perform(); }
}
void Http::cancel()
{
if (p) { p->cancel = true; }
}
Http Http::get(std::string url)
{
return std::move(Http{std::move(url)});
@ -297,5 +341,16 @@ bool Http::ca_file_supported()
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:
struct priv;
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::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(Progress, bool& /* cancel */)> ProgressFn;
Http(Http &&other);
@ -37,9 +50,11 @@ public:
Http& on_complete(CompleteFn fn);
Http& on_error(ErrorFn fn);
Http& on_progress(ProgressFn fn);
Ptr perform();
void perform_sync();
void cancel();
static bool ca_file_supported();
private:
@ -48,6 +63,8 @@ private:
std::unique_ptr<priv> p;
};
std::ostream& operator<<(std::ostream &, const Http::Progress &);
}

View file

@ -1,10 +1,11 @@
#include "OctoPrint.hpp"
#include <iostream>
#include <algorithm>
#include <boost/format.hpp>
#include <wx/frame.h>
#include <wx/event.h>
#include <wx/progdlg.h>
#include "libslic3r/PrintConfig.hpp"
#include "slic3r/GUI/GUI.hpp"
@ -39,36 +40,53 @@ bool OctoPrint::test(wxString &msg) const
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")));
set_auth(http);
http.form_add("print", print ? "true" : "false")
.form_add_file("file", filename)
.on_complete([=](std::string body, unsigned status) {
wxWindow *window = wxWindow::FindWindowById(windowId);
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_complete([&](std::string body, unsigned status) {
progress_dialog.Update(PROGRESS_RANGE);
})
.on_error([=](std::string body, std::string error, unsigned status) {
wxWindow *window = wxWindow::FindWindowById(windowId);
if (window == nullptr) { return; }
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);
.on_error([&](std::string body, std::string error, unsigned status) {
auto errormsg = wxString::Format("%s: %s", errortitle, format_error(error, status));
GUI::show_error(&progress_dialog, std::move(errormsg));
res = false;
})
.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

View file

@ -17,7 +17,7 @@ public:
OctoPrint(DynamicPrintConfig *config);
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:
std::string host;
std::string apikey;

View file

@ -9,5 +9,5 @@
OctoPrint(DynamicPrintConfig *config);
~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;
};