diff --git a/doc/How to build - Windows.md b/doc/How to build - Windows.md index 104720b34..8b7d37cdd 100644 --- a/doc/How to build - Windows.md +++ b/doc/How to build - Windows.md @@ -1,8 +1,9 @@ # 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). 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 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: - - 32 bit: [slic3r-destdir-32.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=slic3r-destdir-32.7z) - - 64 bit: [slic3r-destdir-64.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=slic3r-destdir-64.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=2%2Fslic3r-destdir-64.7z) It is recommended you unpack this package into `C:\local\` as the environment setup script expects it there. diff --git a/doc/deps-build/windows/slic3r-makedeps.ps1 b/doc/deps-build/windows/slic3r-makedeps.ps1 index 8b39cae30..e256d61e4 100644 --- a/doc/deps-build/windows/slic3r-makedeps.ps1 +++ b/doc/deps-build/windows/slic3r-makedeps.ps1 @@ -37,7 +37,7 @@ if ($destdir -eq "") { } $BOOST = 'boost_1_63_0' -$CURL = 'curl-7.28.0' +$CURL = 'curl-7.58.0' $TBB_SHA = 'a0dc9bf76d0120f917b641ed095360448cabc85b' $TBB = "tbb-$TBB_SHA" diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 0138d3f39..14c2d66ae 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1481,7 +1481,11 @@ sub on_export_completed { # Send $self->{send_gcode_file} to OctoPrint. if ($send_gcode) { 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; diff --git a/xs/src/slic3r/Utils/Http.cpp b/xs/src/slic3r/Utils/Http.cpp index de28904e2..0826284d8 100644 --- a/xs/src/slic3r/Utils/Http.cpp +++ b/xs/src/slic3r/Utils/Http.cpp @@ -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(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(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(this)); + (void)xfercb_legacy; // prevent unused function warning +#else + ::curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, xfercb); + ::curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, static_cast(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(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; +} + } diff --git a/xs/src/slic3r/Utils/Http.hpp b/xs/src/slic3r/Utils/Http.hpp index 6ac5fcce1..7ed8196e6 100644 --- a/xs/src/slic3r/Utils/Http.hpp +++ b/xs/src/slic3r/Utils/Http.hpp @@ -14,9 +14,22 @@ class Http : public std::enable_shared_from_this { 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 Ptr; typedef std::function CompleteFn; typedef std::function ErrorFn; + typedef std::function 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 p; }; +std::ostream& operator<<(std::ostream &, const Http::Progress &); + } diff --git a/xs/src/slic3r/Utils/OctoPrint.cpp b/xs/src/slic3r/Utils/OctoPrint.cpp index 5bf51f470..e63a16c38 100644 --- a/xs/src/slic3r/Utils/OctoPrint.cpp +++ b/xs/src/slic3r/Utils/OctoPrint.cpp @@ -1,10 +1,11 @@ #include "OctoPrint.hpp" -#include +#include #include #include #include +#include #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 diff --git a/xs/src/slic3r/Utils/OctoPrint.hpp b/xs/src/slic3r/Utils/OctoPrint.hpp index 1f544295c..744b4fcc1 100644 --- a/xs/src/slic3r/Utils/OctoPrint.hpp +++ b/xs/src/slic3r/Utils/OctoPrint.hpp @@ -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; diff --git a/xs/xsp/Utils_OctoPrint.xsp b/xs/xsp/Utils_OctoPrint.xsp index 124f66cb5..282a3055d 100644 --- a/xs/xsp/Utils_OctoPrint.xsp +++ b/xs/xsp/Utils_OctoPrint.xsp @@ -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; };