diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 734820832c..34591722c7 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -301,6 +301,8 @@ set(SLIC3R_GUI_SOURCES GUI/Jobs/ProgressIndicator.hpp GUI/Jobs/PrintJob.hpp GUI/Jobs/PrintJob.cpp + GUI/Jobs/SendJob.hpp + GUI/Jobs/SendJob.cpp GUI/Jobs/BindJob.hpp GUI/Jobs/BindJob.cpp GUI/Jobs/NotificationProgressIndicator.hpp @@ -350,6 +352,8 @@ set(SLIC3R_GUI_SOURCES GUI/BindDialog.hpp GUI/SelectMachine.hpp GUI/SelectMachine.cpp + GUI/SendToPrinter.hpp + GUI/SendToPrinter.cpp GUI/AmsMappingPopup.hpp GUI/AmsMappingPopup.cpp GUI/ReleaseNote.hpp diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 0edf217efc..01e8bab0de 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -26,6 +26,7 @@ wxDEFINE_EVENT(EVT_GLTOOLBAR_PRINT_PLATE, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_EXPORT_GCODE, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_EXPORT_SLICED_FILE, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_PRINT_SELECT, SimpleEvent); +wxDEFINE_EVENT(EVT_GLTOOLBAR_SEND_TO_PRINTER, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_ADD, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_DELETE, SimpleEvent); diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index af95aa2d0a..61aa09a31c 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -26,6 +26,7 @@ wxDECLARE_EVENT(EVT_GLTOOLBAR_PRINT_PLATE, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_EXPORT_GCODE, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_EXPORT_SLICED_FILE, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_PRINT_SELECT, SimpleEvent); +wxDECLARE_EVENT(EVT_GLTOOLBAR_SEND_TO_PRINTER, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_ADD, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_DELETE, SimpleEvent); diff --git a/src/slic3r/GUI/Jobs/SendJob.cpp b/src/slic3r/GUI/Jobs/SendJob.cpp new file mode 100644 index 0000000000..73146bf0ac --- /dev/null +++ b/src/slic3r/GUI/Jobs/SendJob.cpp @@ -0,0 +1,311 @@ +#include "SendJob.hpp" +#include "libslic3r/MTUtils.hpp" +#include "libslic3r/Model.hpp" +#include "libslic3r/PresetBundle.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/GUI_App.hpp" + +namespace Slic3r { +namespace GUI { + +static wxString check_gcode_failed_str = _L("Abnormal print file data. Please slice again"); +static wxString printjob_cancel_str = _L("Task canceled"); +static wxString timeout_to_upload_str = _L("Upload task timed out. Please check the network problem and try again"); +static wxString failed_in_cloud_service_str = _L("Cloud service connection failed. Please try again."); +static wxString file_is_not_exists_str = _L("Print file not found, please slice again"); +static wxString file_over_size_str = _L("The print file exceeds the maximum allowable size (1GB). Please simplify the model and slice again"); +static wxString print_canceled_str = _L("Task canceled"); +static wxString upload_failed_str = _L("Failed uploading print file"); +static wxString upload_login_failed_str = _L("Wrong Access code"); + + +static wxString sending_over_lan_str = _L("Sending gcode file over LAN"); +static wxString sending_over_cloud_str = _L("Sending gcode file through cloud service"); + +SendJob::SendJob(std::shared_ptr pri, Plater* plater, std::string dev_id) +: PlaterJob{ std::move(pri), plater }, + m_dev_id(dev_id) +{ + m_print_job_completed_id = plater->get_print_finished_event(); +} + +void SendJob::prepare() +{ + m_plater->get_print_job_data(&job_data); +} + +void SendJob::on_exception(const std::exception_ptr &eptr) +{ + try { + if (eptr) + std::rethrow_exception(eptr); + } catch (std::exception &e) { + PlaterJob::on_exception(eptr); + } +} + +wxString SendJob::get_http_error_msg(unsigned int status, std::string body) +{ + int code = 0; + std::string error; + std::string message; + wxString result; + if (status >= 400 && status < 500) + try { + json j = json::parse(body); + if (j.contains("code")) { + if (!j["code"].is_null()) + code = j["code"].get(); + } + if (j.contains("error")) { + if (!j["error"].is_null()) + error = j["error"].get(); + } + if (j.contains("message")) { + if (!j["message"].is_null()) + message = j["message"].get(); + } + switch (status) { + ; + } + } + catch (...) { + ; + } + else if (status == 503) { + return _L("Service Unavailable"); + } + else { + wxString unkown_text = _L("Unkown Error."); + unkown_text += wxString::Format("status=%u, body=%s", status, body); + return unkown_text; + } + + BOOST_LOG_TRIVIAL(error) << "http_error: status=" << status << ", code=" << code << ", error=" << error; + + result = wxString::Format("code=%u, error=%s", code, from_u8(error)); + return result; +} + +inline std::string get_transform_string(int bytes) +{ + float ms = (float)bytes / 1024.0f / 1024.0f; + float ks = (float)bytes / 1024.0f; + char buffer[32]; + if (ms > 0) + ::sprintf(buffer, "%.1fM", ms); + else if (ks > 0) + ::sprintf(buffer, "%.1fK", ks); + else + ::sprintf(buffer, "%.1fK", ks); + return buffer; +} + +void SendJob::process() +{ + /* display info */ + wxString msg; + int curr_percent = 10; + NetworkAgent* m_agent = wxGetApp().getAgent(); + AppConfig* config = wxGetApp().app_config; + + if (this->connection_type == "lan") { + msg = _L("Sending gcode file over LAN"); + } + else { + msg = _L("Sending gcode file through cloud service"); + } + + int result = -1; + unsigned int http_code; + std::string http_body; + + int total_plate_num = m_plater->get_partplate_list().get_plate_count(); + + PartPlate* plate = m_plater->get_partplate_list().get_plate(job_data.plate_idx); + if (plate == nullptr) { + plate = m_plater->get_partplate_list().get_curr_plate(); + if (plate == nullptr) + return; + } + + /* check gcode is valid */ + if (!plate->is_valid_gcode_file()) { + update_status(curr_percent, check_gcode_failed_str); + return; + } + + if (was_canceled()) { + update_status(curr_percent, printjob_cancel_str); + return; + } + + std::string project_name = wxGetApp().plater()->get_project_name().ToUTF8().data(); + int curr_plate_idx = 0; + if (job_data.plate_idx >= 0) + curr_plate_idx = job_data.plate_idx + 1; + else if (job_data.plate_idx == PLATE_CURRENT_IDX) + curr_plate_idx = m_plater->get_partplate_list().get_curr_plate_index() + 1; + + BBL::PrintParams params; + params.dev_id = m_dev_id; + //params.project_name = wxGetApp().plater()->get_project_name().ToUTF8().data(); + params.project_name = wxGetApp().plater()->get_project_name().utf8_string(); + + params.preset_name = wxGetApp().preset_bundle->prints.get_selected_preset_name(); + params.filename = job_data._3mf_path.string(); + params.config_filename = job_data._3mf_config_path.string(); + params.plate_index = curr_plate_idx; + params.ams_mapping = this->task_ams_mapping; + params.connection_type = this->connection_type; + params.task_use_ams = this->task_use_ams; + + // local print access + params.dev_ip = m_dev_ip; + params.username = "bblp"; + params.password = m_access_code; + wxString error_text; + wxString msg_text; + + auto update_fn = [this, &msg, &curr_percent, &error_text](int stage, int code, std::string info) { + if (stage == SendingPrintJobStage::PrintingStageCreate) { + if (this->connection_type == "lan") { + msg = _L("Sending gcode file over LAN"); + } else { + msg = _L("Sending gcode file to sdcard"); + } + curr_percent = 25; + } + else if (stage == SendingPrintJobStage::PrintingStageUpload) { + if (code == 0 && !info.empty()) { + if (this->connection_type == "lan") { + msg = _L("Sending gcode file over LAN"); + } + else { + msg = _L("Sending gcode file to sdcard"); + } + msg += wxString::Format("(%s)", info); + curr_percent = 40; + this->update_status(curr_percent, msg); + } + } + else if (stage == SendingPrintJobStage::PrintingStageFinished) { + curr_percent = 100; + msg = wxString::Format(_L("Successfully sent.")); + } + else { + if (this->connection_type == "lan") { + msg = _L("Sending gcode file over LAN"); + } + else { + msg = _L("Sending gcode file over LAN"); + //msg = _L("Sending gcode file through cloud service"); + } + } + if (code != 0) { + error_text = this->get_http_error_msg(code, info); + msg += wxString::Format("[%s]", error_text); + } + this->update_status(curr_percent, msg); + }; + + auto cancel_fn = [this]() { + return was_canceled(); + }; + + + if (params.connection_type != "lan") { + if (params.dev_ip.empty()) + params.comments = "no_ip"; + else if (this->cloud_print_only) + params.comments = "low_version"; + else if (!this->has_sdcard) + params.comments = "no_sdcard"; + else if (params.password.empty()) + params.comments = "no_password"; + + if (!params.password.empty() + && !params.dev_ip.empty() + && this->has_sdcard) { + // try to send local with record + BOOST_LOG_TRIVIAL(info) << "send_job: try to send gcode to printer"; + this->update_status(curr_percent, _L("Sending gcode file over LAN")); + result = m_agent->start_send_gcode_to_sdcard(params, update_fn, cancel_fn); + if (result == BAMBU_NETWORK_ERR_FTP_LOGIN_DENIED) { + params.comments = "wrong_code"; + } else if (result == BAMBU_NETWORK_ERR_FTP_UPLOAD_FAILED) { + params.comments = "upload_failed"; + } else { + params.comments = (boost::format("failed(%1%)") % result).str(); + } + if (result < 0) { + // try to send with cloud + BOOST_LOG_TRIVIAL(info) << "send_job: try to send gcode file to printer"; + this->update_status(curr_percent, _L("Sending gcode file over LAN")); + } + } else { + BOOST_LOG_TRIVIAL(info) << "send_job: try to send gcode file to printer"; + this->update_status(curr_percent, _L("Sending gcode file over LAN")); + } + } else { + if (this->has_sdcard) { + this->update_status(curr_percent, _L("Sending gcode file over LAN")); + result = m_agent->start_send_gcode_to_sdcard(params, update_fn, cancel_fn); + } else { + this->update_status(curr_percent, _L("An SD card needs to be inserted before printing via LAN.")); + return; + } + } + + if (was_canceled()) { + update_status(curr_percent, printjob_cancel_str); + return; + } + + if (result < 0) { + if (result == BAMBU_NETWORK_ERR_FTP_LOGIN_DENIED) { + msg_text = upload_failed_str; + } if (result == BAMBU_NETWORK_ERR_FILE_NOT_EXIST) { + msg_text = file_is_not_exists_str; + } else if (result == BAMBU_NETWORK_ERR_FILE_OVER_SIZE) { + msg_text = file_over_size_str; + } else if (result == BAMBU_NETWORK_ERR_CHECK_MD5_FAILED) { + msg_text = failed_in_cloud_service_str; + } else if (result == BAMBU_NETWORK_ERR_INVALID_PARAMS) { + msg_text = upload_failed_str; + } else if (result == BAMBU_NETWORK_ERR_CANCELED) { + msg_text = print_canceled_str; + } else if (result == BAMBU_NETWORK_ERR_TIMEOUT) { + msg_text = timeout_to_upload_str; + } else if (result == BAMBU_NETWORK_ERR_INVALID_RESULT) { + msg_text = upload_failed_str; + } else if (result == BAMBU_NETWORK_ERR_FTP_UPLOAD_FAILED) { + msg_text = upload_failed_str; + } else { + update_status(curr_percent, failed_in_cloud_service_str); + } + if (!error_text.IsEmpty()) + msg_text += wxString::Format("[%s]", error_text); + update_status(curr_percent, msg_text); + BOOST_LOG_TRIVIAL(error) << "send_job: failed, result = " << result; + } else { + BOOST_LOG_TRIVIAL(error) << "send_job: send ok."; + m_success_fun(); + m_job_finished = true; + } +} + +void SendJob::on_success(std::function success) +{ + m_success_fun = success; +} + + +void SendJob::finalize() { + if (was_canceled()) return; + + Job::finalize(); +} + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Jobs/SendJob.hpp b/src/slic3r/GUI/Jobs/SendJob.hpp new file mode 100644 index 0000000000..7411b2b1ad --- /dev/null +++ b/src/slic3r/GUI/Jobs/SendJob.hpp @@ -0,0 +1,60 @@ +#ifndef SendJOB_HPP +#define SendJOB_HPP + +#include +#include +#include "PlaterJob.hpp" +#include "PrintJob.hpp" + +namespace fs = boost::filesystem; + +namespace Slic3r { +namespace GUI { + +typedef std::function OnUpdateStatusFn; +typedef std::function WasCancelledFn; + +class SendJob : public PlaterJob +{ + PrintPrepareData job_data; + std::string m_dev_id; + bool m_job_finished{ false }; + int m_print_job_completed_id = 0; + std::function m_success_fun{nullptr}; + +protected: + + void prepare() override; + + void on_exception(const std::exception_ptr &) override; +public: + SendJob(std::shared_ptr pri, Plater *plater, std::string dev_id = ""); + + + std::string m_dev_ip; + std::string m_access_code; + std::string task_bed_type; + std::string task_ams_mapping; + std::string connection_type; + + bool cloud_print_only { false }; + bool has_sdcard { false }; + bool task_use_ams { true }; + + wxWindow* m_parent{nullptr}; + + int status_range() const override + { + return 100; + } + + wxString get_http_error_msg(unsigned int status, std::string body); + bool is_finished() { return m_job_finished; } + void process() override; + void on_success(std::function success); + void finalize() override; +}; + +}} + +#endif diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 471c163351..ba2d1912a5 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1226,7 +1226,7 @@ wxBoxSizer* MainFrame::create_side_tools() m_print_select = ePrintPlate; m_slice_btn = new SideButton(this, _L("Slice"), ""); - m_print_btn = new SideButton(this, _L("Print"), ""); + m_print_btn = new SideButton(this, _L("Send and Print"), ""); m_print_option_btn = new SideButton(this, "", "sidebutton_dropdown", 0, FromDIP(14)); update_side_button_style(); @@ -1270,7 +1270,9 @@ wxBoxSizer* MainFrame::create_side_tools() else if (m_print_select == eExportGcode) wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_EXPORT_GCODE)); else if (m_print_select == eExportSlicedFile) - wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_EXPORT_SLICED_FILE)); + wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_EXPORT_SLICED_FILE)); + else if (m_print_select == eSendToPrinter) + wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_SEND_TO_PRINTER)); }); // only support single plate currently @@ -1322,13 +1324,13 @@ wxBoxSizer* MainFrame::create_side_tools() p->Dismiss(); }); #endif - SideButton* print_plate_btn = new SideButton(p, _L("Print"), ""); + SideButton* print_plate_btn = new SideButton(p, _L("Send and Print"), ""); print_plate_btn->SetCornerRadius(0); SideButton* export_sliced_file_btn = new SideButton(p, _L("Export sliced file"), ""); export_sliced_file_btn->SetCornerRadius(0); print_plate_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { - m_print_btn->SetLabel(_L("Print")); + m_print_btn->SetLabel(_L("Send and Print")); m_print_select = ePrintPlate; m_print_enable = get_enable_print_status(); m_print_btn->Enable(m_print_enable); @@ -1345,11 +1347,24 @@ wxBoxSizer* MainFrame::create_side_tools() this->Layout(); p->Dismiss(); }); + + SideButton* send_to_printer_btn = new SideButton(p, _L("Send"), ""); + send_to_printer_btn->SetCornerRadius(0); + send_to_printer_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { + m_print_btn->SetLabel(_L("Send")); + m_print_select = eSendToPrinter; + if (m_print_enable) + m_print_enable = get_enable_print_status(); + m_print_btn->Enable(m_print_enable); + this->Layout(); + p->Dismiss(); + }); #if ENABEL_PRINT_ALL p->append_button(print_all_btn); #endif p->append_button(print_plate_btn); p->append_button(export_sliced_file_btn); + p->append_button(send_to_printer_btn); p->Popup(m_print_btn); } ); @@ -1441,7 +1456,14 @@ bool MainFrame::get_enable_print_status() { enable = false; } - } + } + else if (m_print_select == eSendToPrinter) + { + if (!current_plate->is_slice_result_ready_for_print()) + { + enable = false; + } + } BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": m_print_select %1%, enable= %2% ")%m_print_select %enable; diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 8611f4e446..4899998467 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -173,7 +173,8 @@ class MainFrame : public DPIFrame ePrintPlate = 1, eExportSlicedFile = 2, eExportGcode = 3, - + eSendGcode = 4, + eSendToPrinter = 5, }; enum SliceSelectType diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 80cda8dd24..0388495451 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -84,6 +84,7 @@ #include "Jobs/NotificationProgressIndicator.hpp" #include "BackgroundSlicingProcess.hpp" #include "SelectMachine.hpp" +#include "SendToPrinter.hpp" #include "PublishDialog.hpp" #include "ConfigWizard.hpp" #include "../Utils/ASCIIFolding.hpp" @@ -1458,6 +1459,7 @@ struct Plater::priv MenuFactory menus; SelectMachineDialog* m_select_machine_dlg = nullptr; + SendToPrinterDialog* m_send_to_sdcard_dlg = nullptr; PublishDialog *m_publish_dlg = nullptr; // Data @@ -1914,14 +1916,14 @@ struct Plater::priv void update_fff_scene_only_shells(bool only_shells = true); //BBS: add popup object table logic bool PopupObjectTable(int object_id, int volume_id, const wxPoint& position); - + void on_action_send_to_printer(); private: void update_fff_scene(); void update_sla_scene(); void undo_redo_to(std::vector::const_iterator it_snapshot); void update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bool temp_snapshot_was_taken = false); - + void on_action_export_to_sdcard(SimpleEvent&); // path to project folder stored with no extension boost::filesystem::path m_project_folder; @@ -2222,6 +2224,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) q->Bind(EVT_GLTOOLBAR_PRINT_ALL, &priv::on_action_print_all, this); q->Bind(EVT_GLTOOLBAR_EXPORT_GCODE, &priv::on_action_export_gcode, this); q->Bind(EVT_GLTOOLBAR_EXPORT_SLICED_FILE, &priv::on_action_export_sliced_file, this); + q->Bind(EVT_GLTOOLBAR_SEND_TO_PRINTER, &priv::on_action_export_to_sdcard, this); q->Bind(EVT_GLCANVAS_PLATE_SELECT, &priv::on_plate_selected, this); q->Bind(EVT_DOWNLOAD_PROJECT, &priv::on_action_download_project, this); q->Bind(EVT_IMPORT_MODEL_ID, &priv::on_action_request_model_id, this); @@ -5598,6 +5601,14 @@ void Plater::priv::on_action_print_plate(SimpleEvent&) m_select_machine_dlg->ShowModal(); } + +void Plater::priv::on_action_send_to_printer() +{ + if (!m_send_to_sdcard_dlg) m_send_to_sdcard_dlg = new SendToPrinterDialog(q); + m_send_to_sdcard_dlg->prepare(partplate_list.get_curr_plate_index()); + m_send_to_sdcard_dlg->ShowModal(); +} + void Plater::priv::on_action_select_sliced_plate(wxCommandEvent &evt) { if (q != nullptr) { @@ -5636,6 +5647,14 @@ void Plater::priv::on_action_export_sliced_file(SimpleEvent&) } } +void Plater::priv::on_action_export_to_sdcard(SimpleEvent&) +{ + if (q != nullptr) { + BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ":received export sliced file event\n"; + q->send_to_printer(); + } +} + //BBS: add plate select logic void Plater::priv::on_plate_selected(SimpleEvent&) { @@ -8258,6 +8277,11 @@ void Plater::export_gcode(bool prefer_removable) } } +void Plater::send_to_printer() +{ + p->on_action_send_to_printer(); +} + //BBS export gcode 3mf to file void Plater::export_gcode_3mf() { diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 45a56f55a9..04f436fa92 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -20,6 +20,7 @@ #include "PartPlate.hpp" #include "GUI_App.hpp" #include "Jobs/PrintJob.hpp" +#include "Jobs/SendJob.hpp" #include "libslic3r/Model.hpp" class wxButton; @@ -296,6 +297,7 @@ public: void segment(size_t obj_idx, size_t instance_idx, double smoothing_alpha=0.5, int segment_number=5); void merge(size_t obj_idx, std::vector& vol_indeces); + void send_to_printer(); void export_gcode(bool prefer_removable); void export_gcode_3mf(); void export_core_3mf(); diff --git a/src/slic3r/GUI/SelectMachine.cpp b/src/slic3r/GUI/SelectMachine.cpp index 5156959023..23cf2358f3 100644 --- a/src/slic3r/GUI/SelectMachine.cpp +++ b/src/slic3r/GUI/SelectMachine.cpp @@ -897,7 +897,7 @@ wxString SelectMachineDialog::format_text(wxString &m_msg) } SelectMachineDialog::SelectMachineDialog(Plater *plater) - : DPIDialog(static_cast(wxGetApp().mainframe), wxID_ANY, _L("Send print job to"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) + : DPIDialog(static_cast(wxGetApp().mainframe), wxID_ANY, _L("Send and Print"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) , m_plater(plater), m_export_3mf_cancel(false) , m_mapping_popup(AmsMapingPopup(this)) , m_mapping_tip_popup(AmsMapingTipPopup(this)) @@ -1033,7 +1033,7 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater) m_sizer_prepare->Add(0, 0, 1, wxTOP, FromDIP(22)); m_sizer_pcont->Add(0, 0, 1, wxEXPAND, 0); - m_button_ensure = new Button(m_panel_prepare, _L("Send")); + m_button_ensure = new Button(m_panel_prepare, _L("Print")); m_button_ensure->SetBackgroundColor(btn_bg_enable); m_button_ensure->SetBorderColor(btn_bg_enable); m_button_ensure->SetTextColor(*wxWHITE); diff --git a/src/slic3r/GUI/SendToPrinter.cpp b/src/slic3r/GUI/SendToPrinter.cpp new file mode 100644 index 0000000000..a3d7e37c5b --- /dev/null +++ b/src/slic3r/GUI/SendToPrinter.cpp @@ -0,0 +1,998 @@ +#include "SendToPrinter.hpp" +#include "I18N.hpp" + +#include "libslic3r/Utils.hpp" +#include "libslic3r/Thread.hpp" +#include "GUI.hpp" +#include "GUI_App.hpp" +#include "GUI_Preview.hpp" +#include "MainFrame.hpp" +#include "format.hpp" +#include "Widgets/ProgressDialog.hpp" +#include "Widgets/RoundedRectangle.hpp" +#include "Widgets/StaticBox.hpp" +#include "ConnectPrinter.hpp" + +#include +#include +#include +#include +#include +#include "BitmapCache.hpp" + +namespace Slic3r { +namespace GUI { + +#define INITIAL_NUMBER_OF_MACHINES 0 +#define LIST_REFRESH_INTERVAL 200 +#define MACHINE_LIST_REFRESH_INTERVAL 2000 + +wxDEFINE_EVENT(EVT_UPDATE_USER_MACHINE_LIST, wxCommandEvent); +wxDEFINE_EVENT(EVT_PRINT_JOB_CANCEL, wxCommandEvent); +wxDEFINE_EVENT(EVT_SEND_JOB_SUCCESS, wxCommandEvent); + +void SendToPrinterDialog::stripWhiteSpace(std::string& str) +{ + if (str == "") { return; } + + string::iterator cur_it; + cur_it = str.begin(); + + while (cur_it != str.end()) { + if ((*cur_it) == '\n' || (*cur_it) == ' ') { + cur_it = str.erase(cur_it); + } + else { + cur_it++; + } + } +} + +wxString SendToPrinterDialog::format_text(wxString &m_msg) +{ + + if (wxGetApp().app_config->get("language") != "zh_CN") { return m_msg; } + + wxString out_txt = m_msg; + wxString count_txt = ""; + int new_line_pos = 0; + + for (int i = 0; i < m_msg.length(); i++) { + auto text_size = m_statictext_printer_msg->GetTextExtent(count_txt); + if (text_size.x < (FromDIP(400))) { + count_txt += m_msg[i]; + } + else { + out_txt.insert(i - 1, '\n'); + count_txt = ""; + } + } + return out_txt; +} + +SendToPrinterDialog::SendToPrinterDialog(Plater *plater) + : DPIDialog(static_cast(wxGetApp().mainframe), wxID_ANY, _L("Send to Printer"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) + , m_plater(plater), m_export_3mf_cancel(false) +{ +#ifdef __WINDOWS__ + SetDoubleBuffered(true); +#endif //__WINDOWS__ + + // bind + Bind(wxEVT_CLOSE_WINDOW, &SendToPrinterDialog::on_cancel, this); + + // font + SetFont(wxGetApp().normal_font()); + + // icon + std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % resources_dir()).str(); + SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); + + Freeze(); + SetBackgroundColour(m_colour_def_color); + + m_sizer_main = new wxBoxSizer(wxVERTICAL); + + m_sizer_main->SetMinSize(wxSize(0, -1)); + m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + m_line_top->SetBackgroundColour(wxColour(166, 169, 170)); + + m_scrollable_region = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_sizer_scrollable_region = new wxBoxSizer(wxVERTICAL); + + m_panel_image = new wxPanel(m_scrollable_region, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_panel_image->SetBackgroundColour(m_colour_def_color); + + sizer_thumbnail = new wxBoxSizer(wxVERTICAL); + m_thumbnailPanel = new ThumbnailPanel(m_panel_image); + m_thumbnailPanel->SetSize(wxSize(FromDIP(256), FromDIP(256))); + m_thumbnailPanel->SetMinSize(wxSize(FromDIP(256), FromDIP(256))); + m_thumbnailPanel->SetMaxSize(wxSize(FromDIP(256), FromDIP(256))); + sizer_thumbnail->Add(m_thumbnailPanel, 0, wxEXPAND, 0); + m_panel_image->SetSizer(sizer_thumbnail); + m_panel_image->Layout(); + + wxBoxSizer *m_sizer_basic = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *m_sizer_basic_weight = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *m_sizer_basic_time = new wxBoxSizer(wxHORIZONTAL); + + auto timeimg = new wxStaticBitmap(m_scrollable_region, wxID_ANY, create_scaled_bitmap("print-time", this, 18), wxDefaultPosition, wxSize(FromDIP(18), FromDIP(18)), 0); + m_sizer_basic_weight->Add(timeimg, 1, wxEXPAND | wxALL, FromDIP(5)); + m_stext_time = new wxStaticText(m_scrollable_region, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); + m_sizer_basic_weight->Add(m_stext_time, 0, wxALL, FromDIP(5)); + m_sizer_basic->Add(m_sizer_basic_weight, 0, wxALIGN_CENTER, 0); + m_sizer_basic->Add(0, 0, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30)); + + auto weightimg = new wxStaticBitmap(m_scrollable_region, wxID_ANY, create_scaled_bitmap("print-weight", this, 18), wxDefaultPosition, wxSize(FromDIP(18), FromDIP(18)), 0); + m_sizer_basic_time->Add(weightimg, 1, wxEXPAND | wxALL, FromDIP(5)); + m_stext_weight = new wxStaticText(m_scrollable_region, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + m_sizer_basic_time->Add(m_stext_weight, 0, wxALL, FromDIP(5)); + m_sizer_basic->Add(m_sizer_basic_time, 0, wxALIGN_CENTER, 0); + + m_line_materia = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + m_line_materia->SetForegroundColour(wxColour(238, 238, 238)); + m_line_materia->SetBackgroundColour(wxColour(238, 238, 238)); + + wxBoxSizer *m_sizer_printer = new wxBoxSizer(wxHORIZONTAL); + + m_stext_printer_title = new wxStaticText(this, wxID_ANY, L("Printer"), wxDefaultPosition, wxSize(-1, -1), 0); + m_stext_printer_title->SetFont(::Label::Head_14); + m_stext_printer_title->Wrap(-1); + m_stext_printer_title->SetForegroundColour(m_colour_bold_color); + m_stext_printer_title->SetBackgroundColour(m_colour_def_color); + + m_sizer_printer->Add(m_stext_printer_title, 0, wxALL | wxLEFT, FromDIP(5)); + m_sizer_printer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(12)); + + m_comboBox_printer = new ::ComboBox(this, wxID_ANY, L(""), wxDefaultPosition, wxSize(FromDIP(250), -1), 0, nullptr, wxCB_READONLY); + m_comboBox_printer->Bind(wxEVT_COMBOBOX, &SendToPrinterDialog::on_selection_changed, this); + + m_sizer_printer->Add(m_comboBox_printer, 1, wxEXPAND | wxRIGHT, FromDIP(5)); + btn_bg_enable = StateColor(std::pair(wxColour(27, 136, 68), StateColor::Pressed), std::pair(wxColour(61, 203, 115), StateColor::Hovered), + std::pair(wxColour(0, 174, 66), StateColor::Normal)); + + m_button_refresh = new Button(this, _L("Refresh")); + m_button_refresh->SetBackgroundColor(btn_bg_enable); + m_button_refresh->SetBorderColor(btn_bg_enable); + m_button_refresh->SetTextColor(*wxWHITE); + m_button_refresh->SetSize(SELECT_MACHINE_DIALOG_BUTTON_SIZE); + m_button_refresh->SetMinSize(SELECT_MACHINE_DIALOG_BUTTON_SIZE); + m_button_refresh->SetCornerRadius(FromDIP(10)); + m_button_refresh->Bind(wxEVT_BUTTON, &SendToPrinterDialog::on_refresh, this); + m_sizer_printer->Add(m_button_refresh, 0, wxALL | wxLEFT, FromDIP(5)); + + m_statictext_printer_msg = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL); + m_statictext_printer_msg->SetFont(::Label::Body_13); + m_statictext_printer_msg->Hide(); + + // line schedule + m_line_schedule = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1)); + m_line_schedule->SetBackgroundColour(wxColour(238, 238, 238)); + + m_sizer_bottom = new wxBoxSizer(wxVERTICAL); + m_simplebook = new wxSimplebook(this, wxID_ANY, wxDefaultPosition, SELECT_MACHINE_DIALOG_SIMBOOK_SIZE, 0); + + // perpare mode + m_panel_prepare = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_panel_prepare->SetBackgroundColour(m_colour_def_color); + // m_panel_prepare->SetBackgroundColour(wxColour(135,206,250)); + wxBoxSizer *m_sizer_prepare = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *m_sizer_pcont = new wxBoxSizer(wxHORIZONTAL); + + m_sizer_prepare->Add(0, 0, 1, wxTOP, FromDIP(22)); + m_sizer_pcont->Add(0, 0, 1, wxEXPAND, 0); + m_button_ensure = new Button(m_panel_prepare, _L("Send")); + m_button_ensure->SetBackgroundColor(btn_bg_enable); + m_button_ensure->SetBorderColor(btn_bg_enable); + m_button_ensure->SetTextColor(*wxWHITE); + m_button_ensure->SetSize(SELECT_MACHINE_DIALOG_BUTTON_SIZE); + m_button_ensure->SetMinSize(SELECT_MACHINE_DIALOG_BUTTON_SIZE); + m_button_ensure->SetCornerRadius(FromDIP(12)); + + m_button_ensure->Bind(wxEVT_BUTTON, &SendToPrinterDialog::on_ok, this); + m_sizer_pcont->Add(m_button_ensure, 0, wxEXPAND | wxBOTTOM, FromDIP(10)); + m_sizer_prepare->Add(m_sizer_pcont, 0, wxEXPAND, 0); + m_panel_prepare->SetSizer(m_sizer_prepare); + m_panel_prepare->Layout(); + m_simplebook->AddPage(m_panel_prepare, wxEmptyString, true); + + // sending mode + m_status_bar = std::make_shared(m_simplebook); + m_panel_sending = m_status_bar->get_panel(); + m_simplebook->AddPage(m_panel_sending, wxEmptyString, false); + + // finish mode + m_panel_finish = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_panel_finish->SetBackgroundColour(wxColour(135, 206, 250)); + wxBoxSizer *m_sizer_finish = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *m_sizer_finish_v = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *m_sizer_finish_h = new wxBoxSizer(wxHORIZONTAL); + + auto imgsize = FromDIP(25); + auto completedimg = new wxStaticBitmap(m_panel_finish, wxID_ANY, create_scaled_bitmap("completed", m_panel_finish, 25), wxDefaultPosition, wxSize(imgsize, imgsize), 0); + m_sizer_finish_h->Add(completedimg, 0, wxALIGN_CENTER | wxALL, FromDIP(5)); + + m_statictext_finish = new wxStaticText(m_panel_finish, wxID_ANY, L("send completed"), wxDefaultPosition, wxDefaultSize, 0); + m_statictext_finish->Wrap(-1); + m_statictext_finish->SetForegroundColour(wxColour(0, 174, 66)); + m_sizer_finish_h->Add(m_statictext_finish, 0, wxALIGN_CENTER | wxALL, FromDIP(5)); + + m_sizer_finish_v->Add(m_sizer_finish_h, 1, wxALIGN_CENTER, 0); + + m_sizer_finish->Add(m_sizer_finish_v, 1, wxALIGN_CENTER, 0); + + m_panel_finish->SetSizer(m_sizer_finish); + m_panel_finish->Layout(); + m_sizer_finish->Fit(m_panel_finish); + m_simplebook->AddPage(m_panel_finish, wxEmptyString, false); + + // bind + Bind(EVT_UPDATE_USER_MACHINE_LIST, &SendToPrinterDialog::update_printer_combobox, this); + Bind(EVT_PRINT_JOB_CANCEL, &SendToPrinterDialog::on_print_job_cancel, this); + + + m_sizer_scrollable_region->Add(m_panel_image, 0, wxALIGN_CENTER_HORIZONTAL, 0); + m_sizer_scrollable_region->Add(0, 0, 0, wxTOP, FromDIP(10)); + m_sizer_scrollable_region->Add(m_sizer_basic, 0, wxALIGN_CENTER_HORIZONTAL, 0); + m_scrollable_region->SetSizer(m_sizer_scrollable_region); + m_scrollable_region->Layout(); + + + m_sizer_main->Add(m_line_top, 0, wxEXPAND, 0); + m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(22)); + m_sizer_main->Add(m_scrollable_region, 0, wxALIGN_CENTER_HORIZONTAL, 0); + m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(8)); + m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(8)); + m_sizer_main->Add(m_line_materia, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30)); + m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(14)); + m_sizer_main->Add(m_sizer_printer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30)); + m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(5)); + m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(8)); + m_sizer_main->Add(m_statictext_printer_msg, 0, wxALIGN_CENTER_HORIZONTAL, 0); + m_sizer_main->Add(0, 1, 0, wxTOP, FromDIP(20)); + m_sizer_main->Add(0, 1, 0, wxTOP, FromDIP(12)); + m_sizer_main->Add(m_line_schedule, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30)); + m_sizer_main->Add(m_simplebook, 0, wxALIGN_CENTER_HORIZONTAL, 0); + m_sizer_main->Add(m_sizer_bottom, 0, wxALIGN_CENTER_HORIZONTAL); + m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(15)); + + SetSizer(m_sizer_main); + Layout(); + Fit(); + Thaw(); + + init_bind(); + init_timer(); + // CenterOnParent(); + Centre(wxBOTH); +} + +void SendToPrinterDialog::prepare_mode() +{ + m_is_in_sending_mode = false; + if (m_send_job) { + m_send_job->join(); + } + + if (wxIsBusy()) + wxEndBusyCursor(); + Enable_Send_Button(true); + + m_status_bar->reset(); + if (m_simplebook->GetSelection() != 0) { + m_simplebook->SetSelection(0); + } +} + +void SendToPrinterDialog::sending_mode() +{ + m_is_in_sending_mode = true; + if (m_simplebook->GetSelection() != 1){ + m_simplebook->SetSelection(1); + Layout(); + Fit(); + } +} + +void SendToPrinterDialog::prepare(int print_plate_idx) +{ + m_print_plate_idx = print_plate_idx; +} + +void SendToPrinterDialog::update_priner_status_msg(wxString msg, bool is_warning) +{ + auto colour = is_warning ? wxColour(0xFF, 0x6F, 0x00) : wxColour(0x6B, 0x6B, 0x6B); + m_statictext_printer_msg->SetForegroundColour(colour); + + if (msg.empty()) { + if (!m_statictext_printer_msg->GetLabel().empty()) { + m_statictext_printer_msg->SetLabel(wxEmptyString); + m_statictext_printer_msg->Hide(); + Layout(); + Fit(); + } + } else { + msg = format_text(msg); + + auto str_new = msg.ToStdString(); + stripWhiteSpace(str_new); + + auto str_old = m_statictext_printer_msg->GetLabel().ToStdString(); + stripWhiteSpace(str_old); + + if (str_new != str_old) { + if (m_statictext_printer_msg->GetLabel() != msg) { + m_statictext_printer_msg->SetLabel(msg); + m_statictext_printer_msg->SetMinSize(wxSize(FromDIP(400), -1)); + m_statictext_printer_msg->SetMaxSize(wxSize(FromDIP(400), -1)); + m_statictext_printer_msg->Wrap(FromDIP(400)); + m_statictext_printer_msg->Show(); + Layout(); + Fit(); + } + } + } +} + +void SendToPrinterDialog::update_print_status_msg(wxString msg, bool is_warning, bool is_printer_msg) +{ + if (is_printer_msg) { + update_priner_status_msg(msg, is_warning); + } else { + update_priner_status_msg(wxEmptyString, false); + } +} + + +void SendToPrinterDialog::init_model() +{ + machine_model = new MachineListModel; + m_dataViewListCtrl_machines->AssociateModel(machine_model.get()); + m_dataViewListCtrl_machines->AppendTextColumn("Printer Name", MachineListModel::Col_MachineName, wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, + wxDATAVIEW_COL_SORTABLE); + + m_dataViewListCtrl_machines->AppendTextColumn("SN(dev_id)", MachineListModel::Col_MachineSN, wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, + wxDATAVIEW_COL_RESIZABLE); + + m_dataViewListCtrl_machines->AppendTextColumn("Status", MachineListModel::Col_MachinePrintingStatus, wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, + wxDATAVIEW_COL_RESIZABLE); + + m_dataViewListCtrl_machines->AppendTextColumn("TaskName", MachineListModel::Col_MachineTaskName, wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, + wxDATAVIEW_COL_RESIZABLE); + + m_dataViewListCtrl_machines->AppendTextColumn("Connection", MachineListModel::Col_MachineConnection, wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, + wxDATAVIEW_COL_RESIZABLE); +} + +void SendToPrinterDialog::init_bind() +{ + Bind(wxEVT_TIMER, &SendToPrinterDialog::on_timer, this); +} + +void SendToPrinterDialog::init_timer() +{ + m_refresh_timer = new wxTimer(); + m_refresh_timer->SetOwner(this); +} + +void SendToPrinterDialog::on_cancel(wxCloseEvent &event) +{ + if (m_send_job) { + if (m_send_job->is_running()) { + m_send_job->cancel(); + m_send_job->join(); + } + } + this->EndModal(wxID_CANCEL); +} + +void SendToPrinterDialog::on_ok(wxCommandEvent &event) +{ + BOOST_LOG_TRIVIAL(info) << "print_job: on_ok to send"; + m_is_canceled = false; + Enable_Send_Button(false); + if (m_is_in_sending_mode) + return; + + int result = 0; + if (m_printer_last_select.empty()) { + return; + } + + DeviceManager *dev = Slic3r::GUI::wxGetApp().getDeviceManager(); + if (!dev) return; + + MachineObject *obj_ = dev->get_selected_machine(); + assert(obj_->dev_id == m_printer_last_select); + if (obj_ == nullptr) { + return; + } + + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", print_job: for send task, current printer id = " << m_printer_last_select << std::endl; + show_status(PrintDialogStatus::PrintStatusSending); + + m_status_bar->reset(); + m_status_bar->set_prog_block(); + m_status_bar->set_cancel_callback_fina([this]() { + BOOST_LOG_TRIVIAL(info) << "print_job: enter canceled"; + if (m_send_job) { + if (m_send_job->is_running()) { + BOOST_LOG_TRIVIAL(info) << "send_job: canceled"; + m_send_job->cancel(); + } + m_send_job->join(); + } + m_is_canceled = true; + wxCommandEvent* event = new wxCommandEvent(EVT_PRINT_JOB_CANCEL); + wxQueueEvent(this, event); + }); + + if (m_is_canceled) { + BOOST_LOG_TRIVIAL(info) << "send_job: m_is_canceled"; + //m_status_bar->set_status_text(task_canceled_text); + return; + } + + // enter sending mode + sending_mode(); + + result = m_plater->send_gcode(m_print_plate_idx, [this](int export_stage, int current, int total, bool &cancel) { + if (this->m_is_canceled) return; + bool cancelled = false; + wxString msg = _L("Preparing print job"); + m_status_bar->update_status(msg, cancelled, 10, true); + m_export_3mf_cancel = cancel = cancelled; + }); + + if (m_is_canceled || m_export_3mf_cancel) { + BOOST_LOG_TRIVIAL(info) << "send_job: m_export_3mf_cancel or m_is_canceled"; + //m_status_bar->set_status_text(task_canceled_text); + return; + } + + if (result < 0) { + wxString msg = _L("Abnormal print file data. Please slice again"); + m_status_bar->set_status_text(msg); + return; + } + + // export config 3mf if needed + if (!obj_->is_lan_mode_printer()) { + result = m_plater->export_config_3mf(m_print_plate_idx); + if (result < 0) { + BOOST_LOG_TRIVIAL(trace) << "export_config_3mf failed, result = " << result; + return; + } + } + if (m_is_canceled || m_export_3mf_cancel) { + BOOST_LOG_TRIVIAL(info) << "send_job: m_export_3mf_cancel or m_is_canceled"; + //m_status_bar->set_status_text(task_canceled_text); + return; + } + + /* std::string file_name = ""; + auto default_output_file = wxGetApp().plater()->get_export_gcode_filename(".3mf"); + if (!default_output_file.empty()) { + fs::path default_output_file_path = boost::filesystem::path(default_output_file.c_str()); + file_name = default_output_file_path.filename().string(); + }*/ + + + + m_send_job = std::make_shared(m_status_bar, m_plater, m_printer_last_select); + m_send_job->m_dev_ip = obj_->dev_ip; + m_send_job->m_access_code = obj_->access_code; + m_send_job->connection_type = obj_->connection_type(); + m_send_job->cloud_print_only = true; + m_send_job->has_sdcard = obj_->has_sdcard(); + + + m_send_job->on_success([this]() { + enable_prepare_mode = true; + //prepare_mode(); + }); + + enable_prepare_mode = false; + m_send_job->start(); + BOOST_LOG_TRIVIAL(info) << "send_job: send print job"; +} + +void SendToPrinterDialog::update_user_machine_list() +{ + NetworkAgent* m_agent = wxGetApp().getAgent(); + if (m_agent && m_agent->is_user_login()) { + boost::thread get_print_info_thread = Slic3r::create_thread([&] { + NetworkAgent* agent = wxGetApp().getAgent(); + unsigned int http_code; + std::string body; + int result = agent->get_user_print_info(&http_code, &body); + if (result == 0) { + m_print_info = body; + } + else { + m_print_info = ""; + } + wxCommandEvent event(EVT_UPDATE_USER_MACHINE_LIST); + event.SetEventObject(this); + wxPostEvent(this, event); + }); + } else { + wxCommandEvent event(EVT_UPDATE_USER_MACHINE_LIST); + event.SetEventObject(this); + wxPostEvent(this, event); + } +} + +void SendToPrinterDialog::on_refresh(wxCommandEvent &event) +{ + BOOST_LOG_TRIVIAL(info) << "m_printer_last_select: on_refresh"; + show_status(PrintDialogStatus::PrintStatusRefreshingMachineList); + + update_user_machine_list(); +} + +void SendToPrinterDialog::on_print_job_cancel(wxCommandEvent &evt) +{ + BOOST_LOG_TRIVIAL(info) << "print_job: canceled"; + show_status(PrintDialogStatus::PrintStatusSendingCanceled); + // enter prepare mode + prepare_mode(); +} + +std::vector SendToPrinterDialog::sort_string(std::vector strArray) +{ + std::vector outputArray; + std::sort(strArray.begin(), strArray.end()); + std::vector::iterator st; + for (st = strArray.begin(); st != strArray.end(); st++) { outputArray.push_back(*st); } + + return outputArray; +} + +bool SendToPrinterDialog::is_timeout() +{ + if (timeout_count > 15 * 1000 / LIST_REFRESH_INTERVAL) { + return true; + } + return false; +} + +void SendToPrinterDialog::reset_timeout() +{ + timeout_count = 0; +} + +void SendToPrinterDialog::update_user_printer() +{ + Slic3r::DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); + if (!dev) return; + + // update user print info + if (!m_print_info.empty()) { + dev->parse_user_print_info(m_print_info); + m_print_info = ""; + } + + // clear machine list + m_list.clear(); + m_comboBox_printer->Clear(); + std::vector machine_list; + wxArrayString machine_list_name; + std::map option_list; + + option_list = dev->get_my_machine_list(); + + // same machine only appear once + for (auto it = option_list.begin(); it != option_list.end(); it++) { + if (it->second && (it->second->is_online() || it->second->is_connected())) { + machine_list.push_back(it->second->dev_name); + } + } + machine_list = sort_string(machine_list); + for (auto tt = machine_list.begin(); tt != machine_list.end(); tt++) { + for (auto it = option_list.begin(); it != option_list.end(); it++) { + if (it->second->dev_name == *tt) { + m_list.push_back(it->second); + wxString dev_name_text = from_u8(it->second->dev_name); + if (it->second->is_lan_mode_printer()) { + dev_name_text += "(LAN)"; + } + machine_list_name.Add(dev_name_text); + break; + } + } + } + + m_comboBox_printer->Set(machine_list_name); + + MachineObject* obj = dev->get_selected_machine(); + if (obj) { + m_printer_last_select = obj->dev_id; + } else { + m_printer_last_select = ""; + } + + if (m_list.size() > 0) { + // select a default machine + if (m_printer_last_select.empty()) { + m_printer_last_select = m_list[0]->dev_id; + m_comboBox_printer->SetSelection(0); + wxCommandEvent event(wxEVT_COMBOBOX); + event.SetEventObject(m_comboBox_printer); + wxPostEvent(m_comboBox_printer, event); + } + for (auto i = 0; i < m_list.size(); i++) { + if (m_list[i]->dev_id == m_printer_last_select) { + m_comboBox_printer->SetSelection(i); + wxCommandEvent event(wxEVT_COMBOBOX); + event.SetEventObject(m_comboBox_printer); + wxPostEvent(m_comboBox_printer, event); + } + } + } + else { + m_printer_last_select = ""; + m_comboBox_printer->SetTextLabel(""); + } + + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "for send task, current printer id = " << m_printer_last_select << std::endl; +} + +void SendToPrinterDialog::update_printer_combobox(wxCommandEvent &event) +{ + show_status(PrintDialogStatus::PrintStatusInit); + update_user_printer(); +} + +void SendToPrinterDialog::on_timer(wxTimerEvent &event) +{ + wxGetApp().reset_to_active(); + update_show_status(); +} + +void SendToPrinterDialog::on_selection_changed(wxCommandEvent &event) +{ + /* reset timeout and reading printer info */ + m_status_bar->reset(); + timeout_count = 0; + + auto selection = m_comboBox_printer->GetSelection(); + DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); + if (!dev) return; + + MachineObject* obj = nullptr; + for (int i = 0; i < m_list.size(); i++) { + if (i == selection) { + m_printer_last_select = m_list[i]->dev_id; + obj = m_list[i]; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "for send task, current printer id = " << m_printer_last_select << std::endl; + break; + } + } + + if (obj) { + obj->command_get_version(); + dev->set_selected_machine(m_printer_last_select); + } else { + BOOST_LOG_TRIVIAL(error) << "on_selection_changed dev_id not found"; + return; + } + + update_show_status(); +} + +void SendToPrinterDialog::update_show_status() +{ + NetworkAgent* agent = Slic3r::GUI::wxGetApp().getAgent(); + DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); + if (!agent) return; + if (!dev) return; + MachineObject* obj_ = dev->get_my_machine(m_printer_last_select); + if (!obj_) { + if (agent) { + if (agent->is_user_login()) { + show_status(PrintDialogStatus::PrintStatusInvalidPrinter); + } + else { + show_status(PrintDialogStatus::PrintStatusNoUserLogin); + } + } + return; + } + + /* check cloud machine connections */ + if (!obj_->is_lan_mode_printer()) { + if (!agent->is_server_connected()) { + agent->refresh_connection(); + show_status(PrintDialogStatus::PrintStatusConnectingServer); + reset_timeout(); + return; + } + } + + if (!obj_->is_info_ready()) { + if (is_timeout()) { + (PrintDialogStatus::PrintStatusReadingTimeout); + return; + } + else { + timeout_count++; + show_status(PrintDialogStatus::PrintStatusReading); + return; + } + return; + } + + reset_timeout(); + + // reading done + if (obj_->is_in_upgrading()) { + show_status(PrintDialogStatus::PrintStatusInUpgrading); + return; + } + else if (obj_->is_system_printing()) { + show_status(PrintDialogStatus::PrintStatusInSystemPrinting); + return; + } + + // check sdcard when if lan mode printer + /* if (obj_->is_lan_mode_printer()) { + }*/ + if (!obj_->has_sdcard()) { + show_status(PrintDialogStatus::PrintStatusNoSdcard); + return; + } + + show_status(PrintDialogStatus::PrintStatusReadingFinished); +} + +void SendToPrinterDialog::Enable_Refresh_Button(bool en) +{ + if (!en) { + if (m_button_refresh->IsEnabled()) { + m_button_refresh->Disable(); + m_button_refresh->SetBackgroundColor(wxColour(0x90, 0x90, 0x90)); + m_button_refresh->SetBorderColor(wxColour(0x90, 0x90, 0x90)); + } + } else { + if (!m_button_refresh->IsEnabled()) { + m_button_refresh->Enable(); + m_button_refresh->SetBackgroundColor(btn_bg_enable); + m_button_refresh->SetBorderColor(btn_bg_enable); + } + } +} + +void SendToPrinterDialog::show_status(PrintDialogStatus status, std::vector params) +{ + if (m_print_status != status) + BOOST_LOG_TRIVIAL(info) << "select_machine_dialog: show_status = " << status; + m_print_status = status; + + // m_comboBox_printer + if (status == PrintDialogStatus::PrintStatusRefreshingMachineList) + m_comboBox_printer->Disable(); + else + m_comboBox_printer->Enable(); + + // m_panel_warn m_simplebook + if (status == PrintDialogStatus::PrintStatusSending) { + sending_mode(); + } + else { + if (enable_prepare_mode) { + prepare_mode(); + } + } + + // other + if (status == PrintDialogStatus::PrintStatusInit) { + update_print_status_msg(wxEmptyString, false, false); + Enable_Send_Button(false); + Enable_Refresh_Button(true); + } + else if (status == PrintDialogStatus::PrintStatusNoUserLogin) { + wxString msg_text = _L("No login account, only printers in LAN mode are displayed"); + update_print_status_msg(msg_text, false, true); + Enable_Send_Button(false); + Enable_Refresh_Button(true); + } + else if (status == PrintDialogStatus::PrintStatusInvalidPrinter) { + update_print_status_msg(wxEmptyString, true, true); + Enable_Send_Button(false); + Enable_Refresh_Button(true); + } + else if (status == PrintDialogStatus::PrintStatusConnectingServer) { + wxString msg_text = _L("Connecting to server"); + update_print_status_msg(msg_text, true, true); + Enable_Send_Button(true); + Enable_Refresh_Button(true); + } + else if (status == PrintDialogStatus::PrintStatusReading) { + wxString msg_text = _L("Synchronizing device information"); + update_print_status_msg(msg_text, false, true); + Enable_Send_Button(false); + Enable_Refresh_Button(true); + } + else if (status == PrintDialogStatus::PrintStatusReadingFinished) { + update_print_status_msg(wxEmptyString, false, true); + Enable_Send_Button(true); + Enable_Refresh_Button(true); + } + else if (status == PrintDialogStatus::PrintStatusReadingTimeout) { + wxString msg_text = _L("Synchronizing device information time out"); + update_print_status_msg(msg_text, true, true); + Enable_Send_Button(true); + Enable_Refresh_Button(true); + } + else if (status == PrintDialogStatus::PrintStatusInUpgrading) { + wxString msg_text = _L("Cannot send the print task when the upgrade is in progress"); + update_print_status_msg(msg_text, true, true); + Enable_Send_Button(false); + Enable_Refresh_Button(true); + } + else if (status == PrintDialogStatus::PrintStatusRefreshingMachineList) { + update_print_status_msg(wxEmptyString, false, true); + Enable_Send_Button(false); + Enable_Refresh_Button(false); + } + else if (status == PrintDialogStatus::PrintStatusSending) { + Enable_Send_Button(false); + Enable_Refresh_Button(false); + } + else if (status == PrintDialogStatus::PrintStatusSendingCanceled) { + Enable_Send_Button(true); + Enable_Refresh_Button(true); + } + else if (status == PrintDialogStatus::PrintStatusNoSdcard) { + wxString msg_text = _L("An SD card needs to be inserted before printing via LAN."); + update_print_status_msg(msg_text, true, true); + Enable_Send_Button(false); + Enable_Refresh_Button(true); + } + else { + Enable_Send_Button(true); + Enable_Refresh_Button(true); + } +} + + +void SendToPrinterDialog::Enable_Send_Button(bool en) +{ + if (!en) { + if (m_button_ensure->IsEnabled()) { + m_button_ensure->Disable(); + m_button_ensure->SetBackgroundColor(wxColour(0x90, 0x90, 0x90)); + m_button_ensure->SetBorderColor(wxColour(0x90, 0x90, 0x90)); + } + } else { + if (!m_button_ensure->IsEnabled()) { + m_button_ensure->Enable(); + m_button_ensure->SetBackgroundColor(btn_bg_enable); + m_button_ensure->SetBorderColor(btn_bg_enable); + } + } +} + +void SendToPrinterDialog::on_dpi_changed(const wxRect &suggested_rect) +{ + m_button_refresh->SetMinSize(SELECT_MACHINE_DIALOG_BUTTON_SIZE); + m_button_refresh->SetCornerRadius(FromDIP(12)); + m_button_ensure->SetMinSize(SELECT_MACHINE_DIALOG_BUTTON_SIZE); + m_button_ensure->SetCornerRadius(FromDIP(12)); + m_status_bar->msw_rescale(); + Fit(); + Refresh(); +} + +void SendToPrinterDialog::set_default() +{ + enable_prepare_mode = true; + //clear combobox + m_list.clear(); + m_comboBox_printer->Clear(); + m_printer_last_select = ""; + m_print_info = ""; + m_comboBox_printer->SetValue(wxEmptyString); + m_comboBox_printer->Enable(); + // rset status bar + m_status_bar->reset(); + + NetworkAgent* agent = wxGetApp().getAgent(); + if (agent) { + if (agent->is_user_login()) { + show_status(PrintDialogStatus::PrintStatusInit); + } else { + show_status(PrintDialogStatus::PrintStatusNoUserLogin); + } + } + + // thumbmail + //wxBitmap bitmap; + ThumbnailData &data = m_plater->get_partplate_list().get_curr_plate()->thumbnail_data; + if (data.is_valid()) { + wxImage image(data.width, data.height); + image.InitAlpha(); + for (unsigned int r = 0; r < data.height; ++r) { + unsigned int rr = (data.height - 1 - r) * data.width; + for (unsigned int c = 0; c < data.width; ++c) { + unsigned char *px = (unsigned char *) data.pixels.data() + 4 * (rr + c); + image.SetRGB((int) c, (int) r, px[0], px[1], px[2]); + image.SetAlpha((int) c, (int) r, px[3]); + } + } + image = image.Rescale(FromDIP(256), FromDIP(256)); + m_thumbnailPanel->set_thumbnail(image); + } + + std::vector materials; + std::vector display_materials; + { + auto preset_bundle = wxGetApp().preset_bundle; + for (auto filament_name : preset_bundle->filament_presets) { + for (auto iter = preset_bundle->filaments.lbegin(); iter != preset_bundle->filaments.end(); iter++) { + if (filament_name.compare(iter->name) == 0) { + std::string display_filament_type; + std::string filament_type = iter->config.get_filament_type(display_filament_type); + display_materials.push_back(display_filament_type); + materials.push_back(filament_type); + } + } + } + } + + m_scrollable_region->Layout(); + m_scrollable_region->Fit(); + Layout(); + Fit(); + + + wxSize screenSize = wxGetDisplaySize(); + auto dialogSize = this->GetSize(); + + + // basic info + auto aprint_stats = m_plater->get_partplate_list().get_current_fff_print().print_statistics(); + wxString time; + PartPlate *plate = m_plater->get_partplate_list().get_curr_plate(); + if (plate) { + if (plate->get_slice_result()) { time = wxString::Format("%s", get_bbl_monitor_time_dhm(plate->get_slice_result()->print_statistics.modes[0].time)); } + } + + char weight[64]; + ::sprintf(weight, " %.2f g", aprint_stats.total_weight); + + m_stext_time->SetLabel(time); + m_stext_weight->SetLabel(weight); +} + +bool SendToPrinterDialog::Show(bool show) +{ + show_status(PrintDialogStatus::PrintStatusInit); + + // set default value when show this dialog + if (show) { + wxGetApp().reset_to_active(); + set_default(); + update_user_machine_list(); + } + + if (show) { + m_refresh_timer->Start(LIST_REFRESH_INTERVAL); + } else { + m_refresh_timer->Stop(); + } + + Layout(); + Fit(); + if (show) { CenterOnParent(); } + return DPIDialog::Show(show); +} + +SendToPrinterDialog::~SendToPrinterDialog() +{ + delete m_refresh_timer; +} + +} +} \ No newline at end of file diff --git a/src/slic3r/GUI/SendToPrinter.hpp b/src/slic3r/GUI/SendToPrinter.hpp new file mode 100644 index 0000000000..814138d9c6 --- /dev/null +++ b/src/slic3r/GUI/SendToPrinter.hpp @@ -0,0 +1,162 @@ +#ifndef slic3r_GUI_SendToSDcard_hpp_ +#define slic3r_GUI_SendToSDcard_hpp_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "SelectMachine.hpp" +#include "GUI_Utils.hpp" +#include "wxExtensions.hpp" +#include "DeviceManager.hpp" +#include "Plater.hpp" +#include "BBLStatusBar.hpp" +#include "BBLStatusBarSend.hpp" +#include "Widgets/Label.hpp" +#include "Widgets/Button.hpp" +#include "Widgets/CheckBox.hpp" +#include "Widgets/ComboBox.hpp" +#include "Widgets/ScrolledWindow.hpp" +#include +#include + +namespace Slic3r { +namespace GUI { + +class SendToPrinterDialog : public DPIDialog +{ +private: + void init_model(); + void init_bind(); + void init_timer(); + + int m_print_plate_idx; + PrintDialogStatus m_print_status { PrintStatusInit }; + + std::string m_printer_last_select; + std::vector m_bedtype_list; + std::map m_checkbox_list; + + wxColour m_colour_def_color{ wxColour(255, 255, 255) }; + wxColour m_colour_bold_color{ wxColour(38, 46, 48) }; + +protected: + Plater* m_plater{ nullptr }; + wxPanel* m_line_top{ nullptr }; + wxPanel* m_panel_image{ nullptr }; + wxStaticText* m_stext_time{ nullptr }; + wxStaticText* m_stext_weight{ nullptr }; + wxPanel* m_line_materia{ nullptr }; + wxStaticText* m_stext_printer_title{ nullptr }; + + wxStaticText* m_statictext_printer_msg{ nullptr }; + wxStaticBitmap* m_staticbitmap{ nullptr }; + ThumbnailPanel* m_thumbnailPanel{ nullptr }; + + ::ComboBox* m_comboBox_printer{ nullptr }; + ::ComboBox* m_comboBox_bed{ nullptr }; + wxStaticText* m_staticText_bed_title{ nullptr }; + wxPanel* m_line_schedule{ nullptr }; + wxPanel* m_panel_sending{ nullptr }; + wxStaticText* m_stext_sending{ nullptr }; + wxPanel* m_panel_prepare{ nullptr }; + Button* m_button_refresh{ nullptr }; + Button* m_button_ensure{ nullptr }; + wxPanel* m_panel_finish{ nullptr }; + wxSimplebook* m_simplebook{ nullptr }; + wxStaticText* m_statictext_finish{ nullptr }; + + StateColor btn_bg_enable; + int m_current_filament_id; + bool m_is_in_sending_mode{ false }; + + wxBoxSizer* sizer_thumbnail; + wxBoxSizer* m_sizer_main; + wxBoxSizer* m_sizer_bottom; + + bool enable_prepare_mode{true}; + bool m_need_adaptation_screen{ false }; + wxPanel* m_scrollable_region; + wxBoxSizer* m_sizer_scrollable_region; + + + void stripWhiteSpace(std::string& str); + wxString format_text(wxString& m_msg); + void update_priner_status_msg(wxString msg, bool is_warning = false); + void update_print_status_msg(wxString msg, bool is_warning = false, bool is_printer = true); + +public: + SendToPrinterDialog(Plater* plater = nullptr); + ~SendToPrinterDialog(); + + void prepare_mode(); + void sending_mode(); + void prepare(int print_plate_idx); + bool Show(bool show); + + /* model */ + wxObjectDataPtr machine_model; + std::shared_ptr m_status_bar; + bool m_export_3mf_cancel{ false }; + bool m_is_canceled{ false }; + +protected: + std::vector m_list; + wxDataViewCtrl* m_dataViewListCtrl_machines{ nullptr }; + wxStaticText* m_staticText_left{ nullptr }; + wxHyperlinkCtrl* m_hyperlink_add_machine{ nullptr }; + wxGauge* m_gauge_job_progress{ nullptr }; + wxPanel* m_panel_status{ nullptr }; + wxButton* m_button_cancel{ nullptr }; + + std::string m_print_info; + int timeout_count = 0; + bool is_timeout(); + void reset_timeout(); + void update_user_printer(); + void update_show_status(); + + wxTimer* m_refresh_timer{ nullptr }; + + std::shared_ptr m_send_job{nullptr}; + + // Virtual event handlers, overide them in your derived class + void update_printer_combobox(wxCommandEvent& event); + void on_cancel(wxCloseEvent& event); + void on_ok(wxCommandEvent& event); + void on_refresh(wxCommandEvent& event); + void on_print_job_cancel(wxCommandEvent& evt); + void set_default(); + void on_timer(wxTimerEvent& event); + void on_selection_changed(wxCommandEvent& event); + void Enable_Refresh_Button(bool en); + void show_status(PrintDialogStatus status, std::vector params = std::vector()); + void Enable_Send_Button(bool en); + void on_dpi_changed(const wxRect& suggested_rect) override; + void update_user_machine_list(); + std::vector sort_string(std::vector strArray); +}; + +} +} + +#endif diff --git a/src/slic3r/Utils/NetworkAgent.cpp b/src/slic3r/Utils/NetworkAgent.cpp index d8f0d5b4cb..2779c2c77a 100644 --- a/src/slic3r/Utils/NetworkAgent.cpp +++ b/src/slic3r/Utils/NetworkAgent.cpp @@ -72,6 +72,7 @@ func_get_user_selected_machine NetworkAgent::get_user_selected_machine_ptr func_set_user_selected_machine NetworkAgent::set_user_selected_machine_ptr = nullptr; func_start_print NetworkAgent::start_print_ptr = nullptr; func_start_local_print_with_record NetworkAgent::start_local_print_with_record_ptr = nullptr; +func_start_send_gcode_to_sdcard NetworkAgent::start_send_gcode_to_sdcard_ptr = nullptr; func_start_local_print NetworkAgent::start_local_print_ptr = nullptr; func_get_user_presets NetworkAgent::get_user_presets_ptr = nullptr; func_request_setting_id NetworkAgent::request_setting_id_ptr = nullptr; @@ -208,6 +209,7 @@ int NetworkAgent::initialize_network_module(bool using_backup) set_user_selected_machine_ptr = reinterpret_cast(get_network_function("bambu_network_set_user_selected_machine")); start_print_ptr = reinterpret_cast(get_network_function("bambu_network_start_print")); start_local_print_with_record_ptr = reinterpret_cast(get_network_function("bambu_network_start_local_print_with_record")); + start_send_gcode_to_sdcard_ptr = reinterpret_cast(get_network_function("bambu_network_start_send_gcode_to_sdcard")); start_local_print_ptr = reinterpret_cast(get_network_function("bambu_network_start_local_print")); get_user_presets_ptr = reinterpret_cast(get_network_function("bambu_network_get_user_presets")); request_setting_id_ptr = reinterpret_cast(get_network_function("bambu_network_request_setting_id")); @@ -298,6 +300,7 @@ int NetworkAgent::unload_network_module() set_user_selected_machine_ptr = nullptr; start_print_ptr = nullptr; start_local_print_with_record_ptr = nullptr; + start_send_gcode_to_sdcard_ptr = nullptr; start_local_print_ptr = nullptr; get_user_presets_ptr = nullptr; request_setting_id_ptr = nullptr; @@ -841,6 +844,17 @@ int NetworkAgent::start_local_print_with_record(PrintParams params, OnUpdateStat return ret; } +int NetworkAgent::start_send_gcode_to_sdcard(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) +{ + int ret = 0; + if (network_agent && start_send_gcode_to_sdcard_ptr) { + ret = start_send_gcode_to_sdcard_ptr(network_agent, params, update_fn, cancel_fn); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" : network_agent=%1%, ret=%2%, dev_id=%3%, task_name=%4%, project_name=%5%") + % network_agent % ret % params.dev_id % params.task_name % params.project_name; + } + return ret; +} + int NetworkAgent::start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) { int ret = 0; diff --git a/src/slic3r/Utils/NetworkAgent.hpp b/src/slic3r/Utils/NetworkAgent.hpp index 57631b97eb..1f46d10c87 100644 --- a/src/slic3r/Utils/NetworkAgent.hpp +++ b/src/slic3r/Utils/NetworkAgent.hpp @@ -51,6 +51,7 @@ typedef std::string (*func_get_user_selected_machine)(void *agent); typedef int (*func_set_user_selected_machine)(void *agent, std::string dev_id); typedef int (*func_start_print)(void *agent, PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn); typedef int (*func_start_local_print_with_record)(void *agent, PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn); +typedef int (*func_start_send_gcode_to_sdcard)(void *agent, PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn); typedef int (*func_start_local_print)(void *agent, PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn); typedef int (*func_get_user_presets)(void *agent, std::map>* user_presets); typedef std::string (*func_request_setting_id)(void *agent, std::string name, std::map* values_map, unsigned int* http_code); @@ -130,6 +131,7 @@ public: int set_user_selected_machine(std::string dev_id); int start_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn); int start_local_print_with_record(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn); + int start_send_gcode_to_sdcard(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn); int start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn); int get_user_presets(std::map>* user_presets); std::string request_setting_id(std::string name, std::map* values_map, unsigned int* http_code); @@ -198,6 +200,7 @@ private: static func_set_user_selected_machine set_user_selected_machine_ptr; static func_start_print start_print_ptr; static func_start_local_print_with_record start_local_print_with_record_ptr; + static func_start_send_gcode_to_sdcard start_send_gcode_to_sdcard_ptr; static func_start_local_print start_local_print_ptr; static func_get_user_presets get_user_presets_ptr; static func_request_setting_id request_setting_id_ptr;