mirror of
https://git.mirrors.martin98.com/https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-28 09:32:04 +08:00

with the following refactorings: 1) Removed the "printhost_slug" config from the Printer config and from all the Printer config related spots. 2) "printhost_slug" renamed to "printhost_port". Slug sounds nasty. 3) Improved error reporting of RepetierHost class. 4) Refactored for the new "Physical Printers" Following refactorings were done independently of the Repetier pull request: 1) Removed PrintHost static print config. 2) Clean-up after conversion of print host configuration from Printer config to Physical Printer config. 3) Fixed some issues, where the Printer config was still queried for host configuration. Vojtech believes that this should not happen after the host configuration is converted to physical printers. Vojtech still feels that more refactoring is needed in regard to porting the host configuration from Printer profile to the new Physical Printer profile.
280 lines
7.4 KiB
C++
280 lines
7.4 KiB
C++
#include "PrintHost.hpp"
|
|
|
|
#include <vector>
|
|
#include <thread>
|
|
#include <exception>
|
|
#include <boost/optional.hpp>
|
|
#include <boost/log/trivial.hpp>
|
|
#include <boost/filesystem.hpp>
|
|
|
|
#include <wx/string.h>
|
|
#include <wx/app.h>
|
|
#include <wx/arrstr.h>
|
|
|
|
#include "libslic3r/PrintConfig.hpp"
|
|
#include "libslic3r/Channel.hpp"
|
|
#include "OctoPrint.hpp"
|
|
#include "Duet.hpp"
|
|
#include "FlashAir.hpp"
|
|
#include "AstroBox.hpp"
|
|
#include "Repetier.hpp"
|
|
#include "../GUI/PrintHostDialogs.hpp"
|
|
|
|
namespace fs = boost::filesystem;
|
|
using boost::optional;
|
|
using Slic3r::GUI::PrintHostQueueDialog;
|
|
|
|
namespace Slic3r {
|
|
|
|
|
|
PrintHost::~PrintHost() {}
|
|
|
|
PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
|
|
{
|
|
PrinterTechnology tech = ptFFF;
|
|
|
|
{
|
|
const auto opt = config->option<ConfigOptionEnum<PrinterTechnology>>("printer_technology");
|
|
if (opt != nullptr) {
|
|
tech = opt->value;
|
|
}
|
|
}
|
|
|
|
if (tech == ptFFF) {
|
|
const auto opt = config->option<ConfigOptionEnum<PrintHostType>>("host_type");
|
|
const auto host_type = opt != nullptr ? opt->value : htOctoPrint;
|
|
|
|
switch (host_type) {
|
|
case htOctoPrint: return new OctoPrint(config);
|
|
case htDuet: return new Duet(config);
|
|
case htFlashAir: return new FlashAir(config);
|
|
case htAstroBox: return new AstroBox(config);
|
|
case htRepetier: return new Repetier(config);
|
|
default: return nullptr;
|
|
}
|
|
} else {
|
|
return new SL1Host(config);
|
|
}
|
|
}
|
|
|
|
wxString PrintHost::format_error(const std::string &body, const std::string &error, unsigned status) const
|
|
{
|
|
if (status != 0) {
|
|
auto wxbody = wxString::FromUTF8(body.data());
|
|
return wxString::Format("HTTP %u: %s", status, wxbody);
|
|
} else {
|
|
return wxString::FromUTF8(error.data());
|
|
}
|
|
}
|
|
|
|
|
|
struct PrintHostJobQueue::priv
|
|
{
|
|
// XXX: comment on how bg thread works
|
|
|
|
PrintHostJobQueue *q;
|
|
|
|
Channel<PrintHostJob> channel_jobs;
|
|
Channel<size_t> channel_cancels;
|
|
size_t job_id = 0;
|
|
int prev_progress = -1;
|
|
fs::path source_to_remove;
|
|
|
|
std::thread bg_thread;
|
|
bool bg_exit = false;
|
|
|
|
PrintHostQueueDialog *queue_dialog;
|
|
|
|
priv(PrintHostJobQueue *q) : q(q) {}
|
|
|
|
void emit_progress(int progress);
|
|
void emit_error(wxString error);
|
|
void emit_cancel(size_t id);
|
|
void start_bg_thread();
|
|
void stop_bg_thread();
|
|
void bg_thread_main();
|
|
void progress_fn(Http::Progress progress, bool &cancel);
|
|
void remove_source(const fs::path &path);
|
|
void remove_source();
|
|
void perform_job(PrintHostJob the_job);
|
|
};
|
|
|
|
PrintHostJobQueue::PrintHostJobQueue(PrintHostQueueDialog *queue_dialog)
|
|
: p(new priv(this))
|
|
{
|
|
p->queue_dialog = queue_dialog;
|
|
}
|
|
|
|
PrintHostJobQueue::~PrintHostJobQueue()
|
|
{
|
|
if (p) { p->stop_bg_thread(); }
|
|
}
|
|
|
|
void PrintHostJobQueue::priv::emit_progress(int progress)
|
|
{
|
|
auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, progress);
|
|
wxQueueEvent(queue_dialog, evt);
|
|
}
|
|
|
|
void PrintHostJobQueue::priv::emit_error(wxString error)
|
|
{
|
|
auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_ERROR, queue_dialog->GetId(), job_id, std::move(error));
|
|
wxQueueEvent(queue_dialog, evt);
|
|
}
|
|
|
|
void PrintHostJobQueue::priv::emit_cancel(size_t id)
|
|
{
|
|
auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_CANCEL, queue_dialog->GetId(), id);
|
|
wxQueueEvent(queue_dialog, evt);
|
|
}
|
|
|
|
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::stop_bg_thread()
|
|
{
|
|
if (bg_thread.joinable()) {
|
|
bg_exit = true;
|
|
channel_jobs.push(PrintHostJob()); // Push an empty job to wake up bg_thread in case it's sleeping
|
|
bg_thread.detach(); // Let the background thread go, it should exit on its own
|
|
}
|
|
}
|
|
|
|
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.empty()) {
|
|
// This happens when the thread is being stopped
|
|
break;
|
|
}
|
|
|
|
source_to_remove = job.upload_data.source_path;
|
|
|
|
BOOST_LOG_TRIVIAL(debug) << boost::format("PrintHostJobQueue/bg_thread: Received job: [%1%]: `%2%` -> `%3%`, cancelled: %4%")
|
|
% job_id
|
|
% job.upload_data.upload_path
|
|
% job.printhost->get_host()
|
|
% job.cancelled;
|
|
|
|
if (! job.cancelled) {
|
|
perform_job(std::move(job));
|
|
}
|
|
|
|
remove_source();
|
|
job_id++;
|
|
}
|
|
} catch (const std::exception &e) {
|
|
emit_error(e.what());
|
|
}
|
|
|
|
// Cleanup leftover files, if any
|
|
remove_source();
|
|
auto jobs = channel_jobs.lock_rw();
|
|
for (const PrintHostJob &job : *jobs) {
|
|
remove_source(job.upload_data.source_path);
|
|
}
|
|
}
|
|
|
|
void PrintHostJobQueue::priv::progress_fn(Http::Progress progress, bool &cancel)
|
|
{
|
|
if (cancel) {
|
|
// When cancel is true from the start, Http indicates request has been cancelled
|
|
emit_cancel(job_id);
|
|
return;
|
|
}
|
|
|
|
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) {
|
|
const size_t idx = cancel_id - job_id - 1;
|
|
if (idx < jobs->size()) {
|
|
jobs->at(idx).cancelled = true;
|
|
BOOST_LOG_TRIVIAL(debug) << boost::format("PrintHostJobQueue: Job id %1% cancelled") % cancel_id;
|
|
emit_cancel(cancel_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
cancels->clear();
|
|
}
|
|
|
|
if (! cancel) {
|
|
int gui_progress = progress.ultotal > 0 ? 100*progress.ulnow / progress.ultotal : 0;
|
|
if (gui_progress != prev_progress) {
|
|
emit_progress(gui_progress);
|
|
prev_progress = gui_progress;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PrintHostJobQueue::priv::remove_source(const fs::path &path)
|
|
{
|
|
if (! path.empty()) {
|
|
boost::system::error_code ec;
|
|
fs::remove(path, ec);
|
|
if (ec) {
|
|
BOOST_LOG_TRIVIAL(error) << boost::format("PrintHostJobQueue: Error removing file `%1%`: %2%") % path % ec;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PrintHostJobQueue::priv::remove_source()
|
|
{
|
|
remove_source(source_to_remove);
|
|
source_to_remove.clear();
|
|
}
|
|
|
|
void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job)
|
|
{
|
|
emit_progress(0); // Indicate the upload is starting
|
|
|
|
bool success = the_job.printhost->upload(std::move(the_job.upload_data),
|
|
[this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); },
|
|
[this](wxString error) {
|
|
emit_error(std::move(error));
|
|
}
|
|
);
|
|
|
|
if (success) {
|
|
emit_progress(100);
|
|
}
|
|
}
|
|
|
|
void PrintHostJobQueue::enqueue(PrintHostJob job)
|
|
{
|
|
p->start_bg_thread();
|
|
p->queue_dialog->append_job(job);
|
|
p->channel_jobs.push(std::move(job));
|
|
}
|
|
|
|
void PrintHostJobQueue::cancel(size_t id)
|
|
{
|
|
p->channel_cancels.push(id);
|
|
}
|
|
|
|
|
|
}
|