OctoPrint basics working, niceties to-do
This commit is contained in:
parent
b613334b81
commit
862217a6b3
16 changed files with 450 additions and 210 deletions
|
@ -1,6 +1,7 @@
|
||||||
#ifndef slic3r_Channel_hpp_
|
#ifndef slic3r_Channel_hpp_
|
||||||
#define slic3r_Channel_hpp_
|
#define slic3r_Channel_hpp_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
@ -13,32 +14,26 @@ namespace Slic3r {
|
||||||
|
|
||||||
template<class T> class Channel
|
template<class T> class Channel
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
using UniqueLock = std::unique_lock<std::mutex>;
|
|
||||||
using Queue = std::deque<T>;
|
|
||||||
public:
|
public:
|
||||||
class Guard
|
using UniqueLock = std::unique_lock<std::mutex>;
|
||||||
|
|
||||||
|
template<class Ptr> class Unlocker
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Guard(UniqueLock lock, const Queue &queue) : m_lock(std::move(lock)), m_queue(queue) {}
|
Unlocker(UniqueLock lock) : m_lock(std::move(lock)) {}
|
||||||
Guard(const Guard &other) = delete;
|
Unlocker(const Unlocker &other) noexcept : m_lock(std::move(other.m_lock)) {} // XXX: done beacuse of MSVC 2013 not supporting init of deleter by move
|
||||||
Guard(Guard &&other) = delete;
|
Unlocker(Unlocker &&other) noexcept : m_lock(std::move(other.m_lock)) {}
|
||||||
~Guard() {}
|
Unlocker& operator=(const Unlocker &other) = delete;
|
||||||
|
Unlocker& operator=(Unlocker &&other) { m_lock = std::move(other.m_lock); }
|
||||||
|
|
||||||
// Access trampolines
|
void operator()(Ptr*) { m_lock.unlock(); }
|
||||||
size_t size() const noexcept { return m_queue.size(); }
|
|
||||||
bool empty() const noexcept { return m_queue.empty(); }
|
|
||||||
typename Queue::const_iterator begin() const noexcept { return m_queue.begin(); }
|
|
||||||
typename Queue::const_iterator end() const noexcept { return m_queue.end(); }
|
|
||||||
typename Queue::const_reference operator[](size_t i) const { return m_queue[i]; }
|
|
||||||
|
|
||||||
Guard& operator=(const Guard &other) = delete;
|
|
||||||
Guard& operator=(Guard &&other) = delete;
|
|
||||||
private:
|
private:
|
||||||
UniqueLock m_lock;
|
mutable UniqueLock m_lock; // XXX: mutable: see above
|
||||||
const Queue &m_queue;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using Queue = std::deque<T>;
|
||||||
|
using LockedConstPtr = std::unique_ptr<const Queue, Unlocker<const Queue>>;
|
||||||
|
using LockedPtr = std::unique_ptr<Queue, Unlocker<Queue>>;
|
||||||
|
|
||||||
Channel() {}
|
Channel() {}
|
||||||
~Channel() {}
|
~Channel() {}
|
||||||
|
@ -56,7 +51,7 @@ public:
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
UniqueLock lock(m_mutex);
|
UniqueLock lock(m_mutex);
|
||||||
m_queue.push_back(std::forward(item));
|
m_queue.push_back(std::forward<T>(item));
|
||||||
}
|
}
|
||||||
if (! silent) { m_condition.notify_one(); }
|
if (! silent) { m_condition.notify_one(); }
|
||||||
}
|
}
|
||||||
|
@ -82,19 +77,22 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlocked observers
|
// Unlocked observers/hints
|
||||||
// Thread unsafe! Keep in mind you need to re-verify the result after acquiring lock!
|
// Thread unsafe! Keep in mind you need to re-verify the result after locking!
|
||||||
size_t size() const noexcept { return m_queue.size(); }
|
size_t size_hint() const noexcept { return m_queue.size(); }
|
||||||
bool empty() const noexcept { return m_queue.empty(); }
|
|
||||||
|
|
||||||
Guard read() const
|
LockedConstPtr lock_read() const
|
||||||
{
|
{
|
||||||
return Guard(UniqueLock(m_mutex), m_queue);
|
return LockedConstPtr(&m_queue, Unlocker<const Queue>(UniqueLock(m_mutex)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LockedPtr lock_rw()
|
||||||
|
{
|
||||||
|
return LockedPtr(&m_queue, Unlocker<Queue>(UniqueLock(m_mutex)));
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
Queue m_queue;
|
Queue m_queue;
|
||||||
std::mutex m_mutex;
|
mutable std::mutex m_mutex;
|
||||||
std::condition_variable m_condition;
|
std::condition_variable m_condition;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -65,11 +65,6 @@ PrinterTechnology BackgroundSlicingProcess::current_printer_technology() const
|
||||||
return m_print->technology();
|
return m_print->technology();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isspace(int ch)
|
|
||||||
{
|
|
||||||
return std::isspace(ch) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function may one day be merged into the Print, but historically the print was separated
|
// This function may one day be merged into the Print, but historically the print was separated
|
||||||
// from the G-code generator.
|
// from the G-code generator.
|
||||||
void BackgroundSlicingProcess::process_fff()
|
void BackgroundSlicingProcess::process_fff()
|
||||||
|
@ -88,6 +83,27 @@ void BackgroundSlicingProcess::process_fff()
|
||||||
m_print->set_status(95, "Running post-processing scripts");
|
m_print->set_status(95, "Running post-processing scripts");
|
||||||
run_post_process_scripts(export_path, m_fff_print->config());
|
run_post_process_scripts(export_path, m_fff_print->config());
|
||||||
m_print->set_status(100, "G-code file exported to " + export_path);
|
m_print->set_status(100, "G-code file exported to " + export_path);
|
||||||
|
} else if (! m_upload_job.empty()) {
|
||||||
|
// A print host upload job has been scheduled
|
||||||
|
|
||||||
|
// XXX: is fs::path::string() right?
|
||||||
|
|
||||||
|
// Generate a unique temp path to which the gcode is copied
|
||||||
|
boost::filesystem::path source_path = boost::filesystem::temp_directory_path()
|
||||||
|
/ boost::filesystem::unique_path(".printhost.%%%%-%%%%-%%%%-%%%%.gcode");
|
||||||
|
|
||||||
|
if (copy_file(m_temp_output_path, source_path.string()) != 0) {
|
||||||
|
throw std::runtime_error("Copying of the temporary G-code to the output G-code failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_print->set_status(95, "Running post-processing scripts");
|
||||||
|
run_post_process_scripts(source_path.string(), m_fff_print->config());
|
||||||
|
m_print->set_status(100, (boost::format("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue") % m_upload_job.printhost->get_host()).str());
|
||||||
|
|
||||||
|
m_upload_job.upload_data.source_path = std::move(source_path);
|
||||||
|
m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string());
|
||||||
|
|
||||||
|
GUI::wxGetApp().printhost_job_queue().enqueue(std::move(m_upload_job));
|
||||||
} else {
|
} else {
|
||||||
m_print->set_status(100, "Slicing complete");
|
m_print->set_status(100, "Slicing complete");
|
||||||
}
|
}
|
||||||
|
@ -373,13 +389,10 @@ void BackgroundSlicingProcess::schedule_upload(Slic3r::PrintHostJob upload_job)
|
||||||
if (! m_export_path.empty())
|
if (! m_export_path.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const boost::filesystem::path path = boost::filesystem::temp_directory_path()
|
|
||||||
/ boost::filesystem::unique_path(".upload.%%%%-%%%%-%%%%-%%%%.gcode");
|
|
||||||
|
|
||||||
// Guard against entering the export step before changing the export path.
|
// Guard against entering the export step before changing the export path.
|
||||||
tbb::mutex::scoped_lock lock(m_print->state_mutex());
|
tbb::mutex::scoped_lock lock(m_print->state_mutex());
|
||||||
this->invalidate_step(bspsGCodeFinalize);
|
this->invalidate_step(bspsGCodeFinalize);
|
||||||
m_export_path = path.string();
|
m_export_path = std::string();
|
||||||
m_upload_job = std::move(upload_job);
|
m_upload_job = std::move(upload_job);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,6 @@ GUI_App::GUI_App()
|
||||||
: wxApp()
|
: wxApp()
|
||||||
#if ENABLE_IMGUI
|
#if ENABLE_IMGUI
|
||||||
, m_imgui(new ImGuiWrapper())
|
, m_imgui(new ImGuiWrapper())
|
||||||
, m_printhost_queue(new PrintHostJobQueue())
|
|
||||||
#endif // ENABLE_IMGUI
|
#endif // ENABLE_IMGUI
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -142,6 +141,8 @@ bool GUI_App::OnInit()
|
||||||
update_mode();
|
update_mode();
|
||||||
SetTopWindow(mainframe);
|
SetTopWindow(mainframe);
|
||||||
|
|
||||||
|
m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
|
||||||
|
|
||||||
CallAfter([this]() {
|
CallAfter([this]() {
|
||||||
// temporary workaround for the correct behavior of the Scrolled sidebar panel
|
// temporary workaround for the correct behavior of the Scrolled sidebar panel
|
||||||
auto& panel = sidebar();
|
auto& panel = sidebar();
|
||||||
|
|
|
@ -92,7 +92,7 @@ class GUI_App : public wxApp
|
||||||
std::unique_ptr<ImGuiWrapper> m_imgui;
|
std::unique_ptr<ImGuiWrapper> m_imgui;
|
||||||
#endif // ENABLE_IMGUI
|
#endif // ENABLE_IMGUI
|
||||||
|
|
||||||
std::unique_ptr<PrintHostJobQueue> m_printhost_queue;
|
std::unique_ptr<PrintHostJobQueue> m_printhost_job_queue;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool OnInit() override;
|
bool OnInit() override;
|
||||||
|
@ -164,7 +164,7 @@ public:
|
||||||
ImGuiWrapper* imgui() { return m_imgui.get(); }
|
ImGuiWrapper* imgui() { return m_imgui.get(); }
|
||||||
#endif // ENABLE_IMGUI
|
#endif // ENABLE_IMGUI
|
||||||
|
|
||||||
PrintHostJobQueue& printhost_queue() { return *m_printhost_queue.get(); }
|
PrintHostJobQueue& printhost_job_queue() { return *m_printhost_job_queue.get(); }
|
||||||
|
|
||||||
};
|
};
|
||||||
DECLARE_APP(GUI_App)
|
DECLARE_APP(GUI_App)
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "ProgressStatusBar.hpp"
|
#include "ProgressStatusBar.hpp"
|
||||||
#include "3DScene.hpp"
|
#include "3DScene.hpp"
|
||||||
#include "AppConfig.hpp"
|
#include "AppConfig.hpp"
|
||||||
|
#include "PrintHostDialogs.hpp"
|
||||||
#include "wxExtensions.hpp"
|
#include "wxExtensions.hpp"
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
|
|
||||||
|
@ -30,7 +31,8 @@ namespace GUI {
|
||||||
MainFrame::MainFrame(const bool no_plater, const bool loaded) :
|
MainFrame::MainFrame(const bool no_plater, const bool loaded) :
|
||||||
wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE),
|
wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE),
|
||||||
m_no_plater(no_plater),
|
m_no_plater(no_plater),
|
||||||
m_loaded(loaded)
|
m_loaded(loaded),
|
||||||
|
m_printhost_queue_dlg(new PrintHostQueueDialog(this))
|
||||||
{
|
{
|
||||||
// Load the icon either from the exe, or from the ico file.
|
// Load the icon either from the exe, or from the ico file.
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
|
@ -375,6 +377,10 @@ void MainFrame::init_menubar()
|
||||||
append_menu_item(windowMenu, wxID_ANY, L("Select Printer Settings Tab\tCtrl+4"), L("Show the printer settings"),
|
append_menu_item(windowMenu, wxID_ANY, L("Select Printer Settings Tab\tCtrl+4"), L("Show the printer settings"),
|
||||||
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer_empty.png");
|
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer_empty.png");
|
||||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
|
|
||||||
|
windowMenu->AppendSeparator();
|
||||||
|
append_menu_item(windowMenu, wxID_ANY, L("Print Host Upload Queue"), L("Display the Print Host Upload Queue window"),
|
||||||
|
[this](wxCommandEvent&) { m_printhost_queue_dlg->ShowModal(); }, "arrow_up.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
// View menu
|
// View menu
|
||||||
|
|
|
@ -21,7 +21,9 @@ class ProgressStatusBar;
|
||||||
|
|
||||||
namespace GUI
|
namespace GUI
|
||||||
{
|
{
|
||||||
|
|
||||||
class Tab;
|
class Tab;
|
||||||
|
class PrintHostQueueDialog;
|
||||||
|
|
||||||
enum QuickSlice
|
enum QuickSlice
|
||||||
{
|
{
|
||||||
|
@ -52,6 +54,8 @@ class MainFrame : public wxFrame
|
||||||
wxMenuItem* m_menu_item_repeat { nullptr };
|
wxMenuItem* m_menu_item_repeat { nullptr };
|
||||||
wxMenuItem* m_menu_item_reslice_now { nullptr };
|
wxMenuItem* m_menu_item_reslice_now { nullptr };
|
||||||
|
|
||||||
|
PrintHostQueueDialog *m_printhost_queue_dlg;
|
||||||
|
|
||||||
std::string get_base_name(const wxString full_name) const ;
|
std::string get_base_name(const wxString full_name) const ;
|
||||||
std::string get_dir_name(const wxString full_name) const ;
|
std::string get_dir_name(const wxString full_name) const ;
|
||||||
|
|
||||||
|
@ -93,6 +97,8 @@ public:
|
||||||
void select_tab(size_t tab) const;
|
void select_tab(size_t tab) const;
|
||||||
void select_view(const std::string& direction);
|
void select_view(const std::string& direction);
|
||||||
|
|
||||||
|
PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; }
|
||||||
|
|
||||||
Plater* m_plater { nullptr };
|
Plater* m_plater { nullptr };
|
||||||
wxNotebook* m_tabpanel { nullptr };
|
wxNotebook* m_tabpanel { nullptr };
|
||||||
wxProgressDialog* m_progress_dialog { nullptr };
|
wxProgressDialog* m_progress_dialog { nullptr };
|
||||||
|
|
|
@ -3144,7 +3144,7 @@ void Plater::send_gcode()
|
||||||
}
|
}
|
||||||
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
|
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
|
||||||
|
|
||||||
Slic3r::PrintHostSendDialog dlg(default_output_file);
|
PrintHostSendDialog dlg(default_output_file);
|
||||||
if (dlg.ShowModal() == wxID_OK) {
|
if (dlg.ShowModal() == wxID_OK) {
|
||||||
upload_job.upload_data.upload_path = dlg.filename();
|
upload_job.upload_data.upload_path = dlg.filename();
|
||||||
upload_job.upload_data.start_print = dlg.start_print();
|
upload_job.upload_data.start_print = dlg.start_print();
|
||||||
|
|
|
@ -1,20 +1,28 @@
|
||||||
#include "PrintHostDialogs.hpp"
|
#include "PrintHostDialogs.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <wx/frame.h>
|
#include <wx/frame.h>
|
||||||
#include <wx/event.h>
|
|
||||||
#include <wx/progdlg.h>
|
#include <wx/progdlg.h>
|
||||||
#include <wx/sizer.h>
|
#include <wx/sizer.h>
|
||||||
#include <wx/stattext.h>
|
#include <wx/stattext.h>
|
||||||
#include <wx/textctrl.h>
|
#include <wx/textctrl.h>
|
||||||
#include <wx/checkbox.h>
|
#include <wx/checkbox.h>
|
||||||
|
#include <wx/button.h>
|
||||||
|
#include <wx/dataview.h>
|
||||||
|
#include <wx/wupdlock.h>
|
||||||
|
#include <wx/debug.h>
|
||||||
|
|
||||||
#include "slic3r/GUI/GUI.hpp"
|
#include "GUI.hpp"
|
||||||
#include "slic3r/GUI/MsgDialog.hpp"
|
#include "MsgDialog.hpp"
|
||||||
#include "slic3r/GUI/I18N.hpp"
|
#include "I18N.hpp"
|
||||||
|
#include "../Utils/PrintHost.hpp"
|
||||||
|
|
||||||
namespace fs = boost::filesystem;
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
namespace GUI {
|
||||||
|
|
||||||
|
|
||||||
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path)
|
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path)
|
||||||
: MsgDialog(nullptr, _(L("Send G-Code to printer host")), _(L("Upload to Printer Host with the following filename:")), wxID_NONE)
|
: MsgDialog(nullptr, _(L("Send G-Code to printer host")), _(L("Upload to Printer Host with the following filename:")), wxID_NONE)
|
||||||
|
@ -45,5 +53,95 @@ fs::path PrintHostSendDialog::filename() const
|
||||||
|
|
||||||
bool PrintHostSendDialog::start_print() const
|
bool PrintHostSendDialog::start_print() const
|
||||||
{
|
{
|
||||||
return box_print->GetValue(); }
|
return box_print->GetValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
wxDEFINE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event);
|
||||||
|
wxDEFINE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event);
|
||||||
|
|
||||||
|
PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id)
|
||||||
|
: wxEvent(winid, eventType)
|
||||||
|
, job_id(job_id)
|
||||||
|
{}
|
||||||
|
|
||||||
|
PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id, int progress)
|
||||||
|
: wxEvent(winid, eventType)
|
||||||
|
, job_id(job_id)
|
||||||
|
, progress(progress)
|
||||||
|
{}
|
||||||
|
|
||||||
|
PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id, wxString error)
|
||||||
|
: wxEvent(winid, eventType)
|
||||||
|
, job_id(job_id)
|
||||||
|
, error(std::move(error))
|
||||||
|
{}
|
||||||
|
|
||||||
|
wxEvent *PrintHostQueueDialog::Event::Clone() const
|
||||||
|
{
|
||||||
|
return new Event(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
|
||||||
|
: wxDialog(parent, wxID_ANY, _(L("Print host upload queue")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||||
|
, on_progress_evt(this, EVT_PRINTHOST_PROGRESS, &PrintHostQueueDialog::on_progress, this)
|
||||||
|
, on_error_evt(this, EVT_PRINTHOST_ERROR, &PrintHostQueueDialog::on_error, this)
|
||||||
|
{
|
||||||
|
enum { HEIGHT = 800, WIDTH = 400, SPACING = 5 };
|
||||||
|
|
||||||
|
SetMinSize(wxSize(HEIGHT, WIDTH));
|
||||||
|
|
||||||
|
auto *topsizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
|
job_list = new wxDataViewListCtrl(this, wxID_ANY);
|
||||||
|
job_list->AppendTextColumn("ID", wxDATAVIEW_CELL_INERT);
|
||||||
|
job_list->AppendProgressColumn("Progress", wxDATAVIEW_CELL_INERT);
|
||||||
|
job_list->AppendTextColumn("Status", wxDATAVIEW_CELL_INERT);
|
||||||
|
job_list->AppendTextColumn("Host", wxDATAVIEW_CELL_INERT);
|
||||||
|
job_list->AppendTextColumn("Filename", wxDATAVIEW_CELL_INERT);
|
||||||
|
|
||||||
|
auto *btnsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
auto *btn_cancel = new wxButton(this, wxID_DELETE, _(L("Cancel selected")));
|
||||||
|
auto *btn_close = new wxButton(this, wxID_CANCEL, _(L("Close")));
|
||||||
|
btnsizer->Add(btn_cancel, 0, wxRIGHT, SPACING);
|
||||||
|
btnsizer->AddStretchSpacer();
|
||||||
|
btnsizer->Add(btn_close);
|
||||||
|
|
||||||
|
topsizer->Add(job_list, 1, wxEXPAND | wxBOTTOM, SPACING);
|
||||||
|
topsizer->Add(btnsizer, 0, wxEXPAND);
|
||||||
|
SetSizer(topsizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostQueueDialog::append_job(const PrintHostJob &job)
|
||||||
|
{
|
||||||
|
wxCHECK_RET(!job.empty(), "PrintHostQueueDialog: Attempt to append an empty job");
|
||||||
|
|
||||||
|
wxVector<wxVariant> fields;
|
||||||
|
fields.push_back(wxVariant(wxString::Format("%d", job_list->GetItemCount() + 1)));
|
||||||
|
fields.push_back(wxVariant(0));
|
||||||
|
fields.push_back(wxVariant(_(L("Enqueued"))));
|
||||||
|
fields.push_back(wxVariant(job.printhost->get_host()));
|
||||||
|
fields.push_back(wxVariant(job.upload_data.upload_path.string()));
|
||||||
|
job_list->AppendItem(fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostQueueDialog::on_progress(Event &evt)
|
||||||
|
{
|
||||||
|
wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list");
|
||||||
|
|
||||||
|
const wxVariant status(evt.progress < 100 ? _(L("Uploading")) : _(L("Complete")));
|
||||||
|
|
||||||
|
job_list->SetValue(wxVariant(evt.progress), evt.job_id, 1);
|
||||||
|
job_list->SetValue(status, evt.job_id, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostQueueDialog::on_error(Event &evt)
|
||||||
|
{
|
||||||
|
wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list");
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
|
@ -2,24 +2,27 @@
|
||||||
#define slic3r_PrintHostSendDialog_hpp_
|
#define slic3r_PrintHostSendDialog_hpp_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
#include <wx/string.h>
|
#include <wx/string.h>
|
||||||
#include <wx/frame.h>
|
|
||||||
#include <wx/event.h>
|
#include <wx/event.h>
|
||||||
#include <wx/progdlg.h>
|
#include <wx/dialog.h>
|
||||||
#include <wx/sizer.h>
|
|
||||||
#include <wx/stattext.h>
|
|
||||||
#include <wx/textctrl.h>
|
|
||||||
#include <wx/checkbox.h>
|
|
||||||
|
|
||||||
#include "slic3r/GUI/GUI.hpp"
|
#include "GUI.hpp"
|
||||||
#include "slic3r/GUI/MsgDialog.hpp"
|
#include "GUI_Utils.hpp"
|
||||||
|
#include "MsgDialog.hpp"
|
||||||
|
#include "../Utils/PrintHost.hpp"
|
||||||
|
|
||||||
|
class wxTextCtrl;
|
||||||
|
class wxCheckBox;
|
||||||
|
class wxDataViewListCtrl;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
struct PrintHostJob;
|
||||||
|
|
||||||
|
namespace GUI {
|
||||||
|
|
||||||
|
|
||||||
class PrintHostSendDialog : public GUI::MsgDialog
|
class PrintHostSendDialog : public GUI::MsgDialog
|
||||||
{
|
{
|
||||||
|
@ -38,12 +41,38 @@ private:
|
||||||
class PrintHostQueueDialog : public wxDialog
|
class PrintHostQueueDialog : public wxDialog
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PrintHostQueueDialog();
|
class Event : public wxEvent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
size_t job_id;
|
||||||
|
int progress = 0; // in percent
|
||||||
|
wxString error;
|
||||||
|
|
||||||
|
Event(wxEventType eventType, int winid, size_t job_id);
|
||||||
|
Event(wxEventType eventType, int winid, size_t job_id, int progress);
|
||||||
|
Event(wxEventType eventType, int winid, size_t job_id, wxString error);
|
||||||
|
|
||||||
|
virtual wxEvent *Clone() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
PrintHostQueueDialog(wxWindow *parent);
|
||||||
|
|
||||||
|
void append_job(const PrintHostJob &job);
|
||||||
private:
|
private:
|
||||||
|
wxDataViewListCtrl *job_list;
|
||||||
|
// Note: EventGuard prevents delivery of progress evts to a freed PrintHostQueueDialog
|
||||||
|
EventGuard on_progress_evt;
|
||||||
|
EventGuard on_error_evt;
|
||||||
|
|
||||||
|
void on_progress(Event&);
|
||||||
|
void on_error(Event&);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
wxDECLARE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event);
|
||||||
|
wxDECLARE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event);
|
||||||
|
|
||||||
}
|
|
||||||
|
}}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
#include "slic3r/GUI/GUI.hpp"
|
#include "slic3r/GUI/GUI.hpp"
|
||||||
#include "slic3r/GUI/I18N.hpp"
|
#include "slic3r/GUI/I18N.hpp"
|
||||||
#include "slic3r/GUI/MsgDialog.hpp"
|
#include "slic3r/GUI/MsgDialog.hpp"
|
||||||
#include "slic3r/GUI/PrintHostDialogs.hpp" // XXX
|
|
||||||
#include "Http.hpp"
|
#include "Http.hpp"
|
||||||
|
|
||||||
namespace fs = boost::filesystem;
|
namespace fs = boost::filesystem;
|
||||||
|
@ -55,89 +54,90 @@ wxString Duet::get_test_failed_msg (wxString &msg) const
|
||||||
return wxString::Format("%s: %s", _(L("Could not connect to Duet")), msg);
|
return wxString::Format("%s: %s", _(L("Could not connect to Duet")), msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Duet::send_gcode(const std::string &filename) const
|
// bool Duet::send_gcode(const std::string &filename) const
|
||||||
{
|
// {
|
||||||
enum { PROGRESS_RANGE = 1000 };
|
// enum { PROGRESS_RANGE = 1000 };
|
||||||
|
|
||||||
const auto errortitle = _(L("Error while uploading to the Duet"));
|
// const auto errortitle = _(L("Error while uploading to the Duet"));
|
||||||
fs::path filepath(filename);
|
// fs::path filepath(filename);
|
||||||
|
|
||||||
PrintHostSendDialog send_dialog(filepath.filename());
|
// GUI::PrintHostSendDialog send_dialog(filepath.filename());
|
||||||
if (send_dialog.ShowModal() != wxID_OK) { return false; }
|
// if (send_dialog.ShowModal() != wxID_OK) { return false; }
|
||||||
|
|
||||||
const bool print = send_dialog.start_print();
|
// const bool print = send_dialog.start_print();
|
||||||
const auto upload_filepath = send_dialog.filename();
|
// const auto upload_filepath = send_dialog.filename();
|
||||||
const auto upload_filename = upload_filepath.filename();
|
// const auto upload_filename = upload_filepath.filename();
|
||||||
const auto upload_parent_path = upload_filepath.parent_path();
|
// const auto upload_parent_path = upload_filepath.parent_path();
|
||||||
|
|
||||||
wxProgressDialog progress_dialog(
|
// wxProgressDialog progress_dialog(
|
||||||
_(L("Duet upload")),
|
// _(L("Duet upload")),
|
||||||
_(L("Sending G-code file to Duet...")),
|
// _(L("Sending G-code file to Duet...")),
|
||||||
PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
|
// PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
|
||||||
progress_dialog.Pulse();
|
// progress_dialog.Pulse();
|
||||||
|
|
||||||
wxString connect_msg;
|
// wxString connect_msg;
|
||||||
if (!connect(connect_msg)) {
|
// if (!connect(connect_msg)) {
|
||||||
auto errormsg = wxString::Format("%s: %s", errortitle, connect_msg);
|
// auto errormsg = wxString::Format("%s: %s", errortitle, connect_msg);
|
||||||
GUI::show_error(&progress_dialog, std::move(errormsg));
|
// GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
bool res = true;
|
// bool res = true;
|
||||||
|
|
||||||
auto upload_cmd = get_upload_url(upload_filepath.string());
|
// auto upload_cmd = get_upload_url(upload_filepath.string());
|
||||||
BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filename: %2%, path: %3%, print: %4%, command: %5%")
|
// BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filename: %2%, path: %3%, print: %4%, command: %5%")
|
||||||
% filepath.string()
|
// % filepath.string()
|
||||||
% upload_filename.string()
|
// % upload_filename.string()
|
||||||
% upload_parent_path.string()
|
// % upload_parent_path.string()
|
||||||
% print
|
// % print
|
||||||
% upload_cmd;
|
// % upload_cmd;
|
||||||
|
|
||||||
auto http = Http::post(std::move(upload_cmd));
|
// auto http = Http::post(std::move(upload_cmd));
|
||||||
http.set_post_body(filename)
|
// http.set_post_body(filename)
|
||||||
.on_complete([&](std::string body, unsigned status) {
|
// .on_complete([&](std::string body, unsigned status) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body;
|
// BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body;
|
||||||
progress_dialog.Update(PROGRESS_RANGE);
|
// progress_dialog.Update(PROGRESS_RANGE);
|
||||||
|
|
||||||
int err_code = get_err_code_from_body(body);
|
// int err_code = get_err_code_from_body(body);
|
||||||
if (err_code != 0) {
|
// if (err_code != 0) {
|
||||||
auto msg = format_error(body, L("Unknown error occured"), 0);
|
// auto msg = format_error(body, L("Unknown error occured"), 0);
|
||||||
GUI::show_error(&progress_dialog, std::move(msg));
|
// GUI::show_error(&progress_dialog, std::move(msg));
|
||||||
res = false;
|
// res = false;
|
||||||
} else if (print) {
|
// } else if (print) {
|
||||||
wxString errormsg;
|
// wxString errormsg;
|
||||||
res = start_print(errormsg, upload_filepath.string());
|
// res = start_print(errormsg, upload_filepath.string());
|
||||||
if (!res) {
|
// if (!res) {
|
||||||
GUI::show_error(&progress_dialog, std::move(errormsg));
|
// GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
.on_error([&](std::string body, std::string error, unsigned status) {
|
// .on_error([&](std::string body, std::string error, unsigned status) {
|
||||||
BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
|
// BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
|
||||||
auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status));
|
// auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status));
|
||||||
GUI::show_error(&progress_dialog, std::move(errormsg));
|
// GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||||
res = false;
|
// res = false;
|
||||||
})
|
// })
|
||||||
.on_progress([&](Http::Progress progress, bool &cancel) {
|
// .on_progress([&](Http::Progress progress, bool &cancel) {
|
||||||
if (cancel) {
|
// if (cancel) {
|
||||||
// Upload was canceled
|
// // Upload was canceled
|
||||||
res = false;
|
// res = false;
|
||||||
} else if (progress.ultotal > 0) {
|
// } else if (progress.ultotal > 0) {
|
||||||
int value = PROGRESS_RANGE * progress.ulnow / progress.ultotal;
|
// 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
|
// cancel = !progress_dialog.Update(std::min(value, PROGRESS_RANGE - 1)); // Cap the value to prevent premature dialog closing
|
||||||
} else {
|
// } else {
|
||||||
cancel = !progress_dialog.Pulse();
|
// cancel = !progress_dialog.Pulse();
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
.perform_sync();
|
// .perform_sync();
|
||||||
|
|
||||||
disconnect();
|
// disconnect();
|
||||||
|
|
||||||
return res;
|
// return res;
|
||||||
}
|
// }
|
||||||
|
|
||||||
bool Duet::upload(PrintHostUpload upload_data) const
|
bool Duet::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const
|
||||||
{
|
{
|
||||||
|
// XXX: TODO
|
||||||
throw "unimplemented";
|
throw "unimplemented";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,11 +22,10 @@ public:
|
||||||
bool test(wxString &curl_msg) const;
|
bool test(wxString &curl_msg) const;
|
||||||
wxString get_test_ok_msg () const;
|
wxString get_test_ok_msg () const;
|
||||||
wxString get_test_failed_msg (wxString &msg) const;
|
wxString get_test_failed_msg (wxString &msg) const;
|
||||||
// Send gcode file to duet, filename is expected to be in UTF-8
|
bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const;
|
||||||
bool send_gcode(const std::string &filename) const;
|
|
||||||
bool upload(PrintHostUpload upload_data) const;
|
|
||||||
bool has_auto_discovery() const;
|
bool has_auto_discovery() const;
|
||||||
bool can_test() const;
|
bool can_test() const;
|
||||||
|
virtual std::string get_host() const { return host; }
|
||||||
private:
|
private:
|
||||||
std::string host;
|
std::string host;
|
||||||
std::string password;
|
std::string password;
|
||||||
|
|
|
@ -4,9 +4,10 @@
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
|
#include <wx/progdlg.h>
|
||||||
|
|
||||||
#include "libslic3r/PrintConfig.hpp"
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
#include "slic3r/GUI/I18N.hpp"
|
#include "slic3r/GUI/I18N.hpp"
|
||||||
#include "slic3r/GUI/PrintHostDialogs.hpp" // XXX
|
|
||||||
#include "Http.hpp"
|
#include "Http.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,32 +60,19 @@ wxString OctoPrint::get_test_failed_msg (wxString &msg) const
|
||||||
_(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required.")));
|
_(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OctoPrint::send_gcode(const std::string &filename) const
|
bool OctoPrint::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const
|
||||||
{
|
{
|
||||||
enum { PROGRESS_RANGE = 1000 };
|
const auto upload_filename = upload_data.upload_path.filename();
|
||||||
|
const auto upload_parent_path = upload_data.upload_path.parent_path();
|
||||||
const auto errortitle = _(L("Error while uploading to the OctoPrint server"));
|
|
||||||
fs::path filepath(filename);
|
|
||||||
|
|
||||||
PrintHostSendDialog send_dialog(filepath.filename());
|
|
||||||
if (send_dialog.ShowModal() != wxID_OK) { return false; }
|
|
||||||
|
|
||||||
const bool print = send_dialog.start_print();
|
|
||||||
const auto upload_filepath = send_dialog.filename();
|
|
||||||
const auto upload_filename = upload_filepath.filename();
|
|
||||||
const auto upload_parent_path = upload_filepath.parent_path();
|
|
||||||
|
|
||||||
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;
|
wxString test_msg;
|
||||||
if (!test(test_msg)) {
|
if (! test(test_msg)) {
|
||||||
auto errormsg = wxString::Format("%s: %s", errortitle, test_msg);
|
|
||||||
GUI::show_error(&progress_dialog, std::move(errormsg));
|
// TODO:
|
||||||
return false;
|
|
||||||
|
// auto errormsg = wxString::Format("%s: %s", errortitle, test_msg);
|
||||||
|
// GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||||
|
// return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool res = true;
|
bool res = true;
|
||||||
|
@ -92,36 +80,31 @@ bool OctoPrint::send_gcode(const std::string &filename) const
|
||||||
auto url = make_url("api/files/local");
|
auto url = make_url("api/files/local");
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Uploading file %1% at %2%, filename: %3%, path: %4%, print: %5%")
|
BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Uploading file %1% at %2%, filename: %3%, path: %4%, print: %5%")
|
||||||
% filepath.string()
|
% upload_data.source_path.string()
|
||||||
% url
|
% url
|
||||||
% upload_filename.string()
|
% upload_filename.string()
|
||||||
% upload_parent_path.string()
|
% upload_parent_path.string()
|
||||||
% print;
|
% upload_data.start_print;
|
||||||
|
|
||||||
auto http = Http::post(std::move(url));
|
auto http = Http::post(std::move(url));
|
||||||
set_auth(http);
|
set_auth(http);
|
||||||
http.form_add("print", print ? "true" : "false")
|
http.form_add("print", upload_data.start_print ? "true" : "false")
|
||||||
.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
|
.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
|
||||||
.form_add_file("file", filename, upload_filename.string())
|
.form_add_file("file", upload_data.source_path.string(), upload_filename.string())
|
||||||
.on_complete([&](std::string body, unsigned status) {
|
.on_complete([&](std::string body, unsigned status) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: File uploaded: HTTP %1%: %2%") % status % body;
|
BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: File uploaded: HTTP %1%: %2%") % status % body;
|
||||||
progress_dialog.Update(PROGRESS_RANGE);
|
|
||||||
})
|
})
|
||||||
.on_error([&](std::string body, std::string error, unsigned status) {
|
.on_error([&](std::string body, std::string error, unsigned status) {
|
||||||
BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
|
BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
|
||||||
auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status));
|
error_fn(std::move(body), std::move(error), status);
|
||||||
GUI::show_error(&progress_dialog, std::move(errormsg));
|
|
||||||
res = false;
|
res = false;
|
||||||
})
|
})
|
||||||
.on_progress([&](Http::Progress progress, bool &cancel) {
|
.on_progress([&](Http::Progress progress, bool &cancel) {
|
||||||
|
prorgess_fn(std::move(progress), cancel);
|
||||||
if (cancel) {
|
if (cancel) {
|
||||||
// Upload was canceled
|
// Upload was canceled
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Octoprint: Upload canceled";
|
||||||
res = false;
|
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();
|
.perform_sync();
|
||||||
|
@ -129,11 +112,6 @@ bool OctoPrint::send_gcode(const std::string &filename) const
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OctoPrint::upload(PrintHostUpload upload_data) const
|
|
||||||
{
|
|
||||||
throw "unimplemented";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OctoPrint::has_auto_discovery() const
|
bool OctoPrint::has_auto_discovery() const
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -22,11 +22,10 @@ public:
|
||||||
bool test(wxString &curl_msg) const;
|
bool test(wxString &curl_msg) const;
|
||||||
wxString get_test_ok_msg () const;
|
wxString get_test_ok_msg () const;
|
||||||
wxString get_test_failed_msg (wxString &msg) const;
|
wxString get_test_failed_msg (wxString &msg) const;
|
||||||
// Send gcode file to octoprint, filename is expected to be in UTF-8
|
bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const;
|
||||||
bool send_gcode(const std::string &filename) const;
|
|
||||||
bool upload(PrintHostUpload upload_data) const;
|
|
||||||
bool has_auto_discovery() const;
|
bool has_auto_discovery() const;
|
||||||
bool can_test() const;
|
bool can_test() const;
|
||||||
|
virtual std::string get_host() const { return host; }
|
||||||
private:
|
private:
|
||||||
std::string host;
|
std::string host;
|
||||||
std::string apikey;
|
std::string apikey;
|
||||||
|
|
|
@ -1,15 +1,21 @@
|
||||||
#include "OctoPrint.hpp"
|
#include "PrintHost.hpp"
|
||||||
#include "Duet.hpp"
|
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
|
#include <wx/app.h>
|
||||||
|
|
||||||
#include "libslic3r/PrintConfig.hpp"
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
#include "libslic3r/Channel.hpp"
|
#include "libslic3r/Channel.hpp"
|
||||||
|
#include "OctoPrint.hpp"
|
||||||
|
#include "Duet.hpp"
|
||||||
|
#include "../GUI/PrintHostDialogs.hpp"
|
||||||
|
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
using boost::optional;
|
using boost::optional;
|
||||||
|
using Slic3r::GUI::PrintHostQueueDialog;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -30,30 +36,130 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
|
||||||
|
|
||||||
struct PrintHostJobQueue::priv
|
struct PrintHostJobQueue::priv
|
||||||
{
|
{
|
||||||
std::vector<PrintHostJob> jobs;
|
// XXX: comment on how bg thread works
|
||||||
Channel<unsigned> channel;
|
|
||||||
|
PrintHostJobQueue *q;
|
||||||
|
|
||||||
|
Channel<PrintHostJob> channel_jobs;
|
||||||
|
Channel<size_t> channel_cancels;
|
||||||
|
size_t job_id = 0;
|
||||||
|
int prev_progress = -1;
|
||||||
|
|
||||||
std::thread bg_thread;
|
std::thread bg_thread;
|
||||||
optional<PrintHostJob> bg_job;
|
bool bg_exit = false;
|
||||||
|
|
||||||
|
PrintHostQueueDialog *queue_dialog;
|
||||||
|
|
||||||
|
priv(PrintHostJobQueue *q) : q(q) {}
|
||||||
|
|
||||||
|
void start_bg_thread();
|
||||||
|
void bg_thread_main();
|
||||||
|
void progress_fn(Http::Progress progress, bool &cancel);
|
||||||
|
void error_fn(std::string body, std::string error, unsigned http_status);
|
||||||
|
void perform_job(PrintHostJob the_job);
|
||||||
};
|
};
|
||||||
|
|
||||||
PrintHostJobQueue::PrintHostJobQueue()
|
PrintHostJobQueue::PrintHostJobQueue(PrintHostQueueDialog *queue_dialog)
|
||||||
: p(new priv())
|
: p(new priv(this))
|
||||||
{
|
{
|
||||||
std::shared_ptr<priv> p2 = p;
|
p->queue_dialog = queue_dialog;
|
||||||
p->bg_thread = std::thread([p2]() {
|
|
||||||
// Wait for commands on the channel:
|
|
||||||
auto cmd = p2->channel.pop();
|
|
||||||
// TODO
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintHostJobQueue::~PrintHostJobQueue()
|
PrintHostJobQueue::~PrintHostJobQueue()
|
||||||
{
|
{
|
||||||
// TODO: stop the thread
|
if (p && p->bg_thread.joinable()) {
|
||||||
// if (p && p->bg_thread.joinable()) {
|
p->bg_exit = true;
|
||||||
// p->bg_thread.detach();
|
p->channel_jobs.push(PrintHostJob()); // Push an empty job to wake up bg_thread in case it's sleeping
|
||||||
// }
|
p->bg_thread.detach(); // Let the background thread go, it should exit on its own
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostJobQueue::priv::start_bg_thread()
|
||||||
|
{
|
||||||
|
if (bg_thread.joinable()) { return; }
|
||||||
|
|
||||||
|
std::shared_ptr<priv> p2 = q->p;
|
||||||
|
bg_thread = std::thread([p2]() {
|
||||||
|
p2->bg_thread_main();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostJobQueue::priv::bg_thread_main()
|
||||||
|
{
|
||||||
|
// bg thread entry point
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Pick up jobs from the job channel:
|
||||||
|
while (! bg_exit) {
|
||||||
|
auto job = channel_jobs.pop(); // Sleeps in a cond var if there are no jobs
|
||||||
|
if (! job.cancelled) {
|
||||||
|
perform_job(std::move(job));
|
||||||
|
}
|
||||||
|
job_id++;
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
wxTheApp->OnUnhandledException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostJobQueue::priv::progress_fn(Http::Progress progress, bool &cancel)
|
||||||
|
{
|
||||||
|
if (bg_exit) {
|
||||||
|
cancel = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (channel_cancels.size_hint() > 0) {
|
||||||
|
// Lock both queues
|
||||||
|
auto cancels = channel_cancels.lock_rw();
|
||||||
|
auto jobs = channel_jobs.lock_rw();
|
||||||
|
|
||||||
|
for (size_t cancel_id : *cancels) {
|
||||||
|
if (cancel_id == job_id) {
|
||||||
|
cancel = true;
|
||||||
|
} else if (cancel_id > job_id) {
|
||||||
|
jobs->at(cancel_id - job_id).cancelled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cancels->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
int gui_progress = progress.ultotal > 0 ? 100*progress.ulnow / progress.ultotal : 0;
|
||||||
|
if (gui_progress != prev_progress) {
|
||||||
|
auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, gui_progress);
|
||||||
|
wxQueueEvent(queue_dialog, evt);
|
||||||
|
prev_progress = gui_progress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostJobQueue::priv::error_fn(std::string body, std::string error, unsigned http_status)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job)
|
||||||
|
{
|
||||||
|
if (bg_exit || the_job.empty()) { return; }
|
||||||
|
|
||||||
|
const fs::path gcode_path = the_job.upload_data.source_path;
|
||||||
|
|
||||||
|
the_job.printhost->upload(std::move(the_job.upload_data),
|
||||||
|
[this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); },
|
||||||
|
[this](std::string body, std::string error, unsigned http_status) { this->error_fn(std::move(body), std::move(error), http_status); }
|
||||||
|
);
|
||||||
|
|
||||||
|
auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, 100);
|
||||||
|
wxQueueEvent(queue_dialog, evt);
|
||||||
|
|
||||||
|
fs::remove(gcode_path); // XXX: error handling
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostJobQueue::enqueue(PrintHostJob job)
|
||||||
|
{
|
||||||
|
p->start_bg_thread();
|
||||||
|
p->queue_dialog->append_job(job);
|
||||||
|
p->channel_jobs.push(std::move(job));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
#include <wx/string.h>
|
#include <wx/string.h>
|
||||||
|
|
||||||
|
#include "Http.hpp"
|
||||||
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -29,11 +31,10 @@ public:
|
||||||
virtual bool test(wxString &curl_msg) const = 0;
|
virtual bool test(wxString &curl_msg) const = 0;
|
||||||
virtual wxString get_test_ok_msg () const = 0;
|
virtual wxString get_test_ok_msg () const = 0;
|
||||||
virtual wxString get_test_failed_msg (wxString &msg) const = 0;
|
virtual wxString get_test_failed_msg (wxString &msg) const = 0;
|
||||||
// Send gcode file to print host, filename is expected to be in UTF-8
|
virtual bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const = 0;
|
||||||
virtual bool send_gcode(const std::string &filename) const = 0; // XXX: remove in favor of upload()
|
|
||||||
virtual bool upload(PrintHostUpload upload_data) const = 0;
|
|
||||||
virtual bool has_auto_discovery() const = 0;
|
virtual bool has_auto_discovery() const = 0;
|
||||||
virtual bool can_test() const = 0;
|
virtual bool can_test() const = 0;
|
||||||
|
virtual std::string get_host() const = 0;
|
||||||
|
|
||||||
static PrintHost* get_print_host(DynamicPrintConfig *config);
|
static PrintHost* get_print_host(DynamicPrintConfig *config);
|
||||||
};
|
};
|
||||||
|
@ -43,6 +44,7 @@ struct PrintHostJob
|
||||||
{
|
{
|
||||||
PrintHostUpload upload_data;
|
PrintHostUpload upload_data;
|
||||||
std::unique_ptr<PrintHost> printhost;
|
std::unique_ptr<PrintHost> printhost;
|
||||||
|
bool cancelled = false;
|
||||||
|
|
||||||
PrintHostJob() {}
|
PrintHostJob() {}
|
||||||
PrintHostJob(const PrintHostJob&) = delete;
|
PrintHostJob(const PrintHostJob&) = delete;
|
||||||
|
@ -68,10 +70,12 @@ struct PrintHostJob
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
namespace GUI { class PrintHostQueueDialog; }
|
||||||
|
|
||||||
class PrintHostJobQueue
|
class PrintHostJobQueue
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PrintHostJobQueue();
|
PrintHostJobQueue(GUI::PrintHostQueueDialog *queue_dialog);
|
||||||
PrintHostJobQueue(const PrintHostJobQueue &) = delete;
|
PrintHostJobQueue(const PrintHostJobQueue &) = delete;
|
||||||
PrintHostJobQueue(PrintHostJobQueue &&other) = delete;
|
PrintHostJobQueue(PrintHostJobQueue &&other) = delete;
|
||||||
~PrintHostJobQueue();
|
~PrintHostJobQueue();
|
||||||
|
@ -79,6 +83,9 @@ public:
|
||||||
PrintHostJobQueue& operator=(const PrintHostJobQueue &) = delete;
|
PrintHostJobQueue& operator=(const PrintHostJobQueue &) = delete;
|
||||||
PrintHostJobQueue& operator=(PrintHostJobQueue &&other) = delete;
|
PrintHostJobQueue& operator=(PrintHostJobQueue &&other) = delete;
|
||||||
|
|
||||||
|
void enqueue(PrintHostJob job);
|
||||||
|
void cancel(size_t id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct priv;
|
struct priv;
|
||||||
std::shared_ptr<priv> p;
|
std::shared_ptr<priv> p;
|
||||||
|
|
Loading…
Add table
Reference in a new issue