Part skip from Orca for Bambu printers (#10875)
Ported part skip from BambuStudio. Thanks bambulab! <img width="1350" height="902" alt="image" src="https://github.com/user-attachments/assets/d7f89950-3420-4ea5-bbb6-e53c875e7422" /> Haven't fully tested yet! ### Minimum Firmware Requirements (from BambuStudio release page): - **X1C**: v01.09.02.12 or later - **H2D**: v01.02.00.00 or later - **Other models**: Currently not supported. Feature support coming in future firmware updates. > ⚠️ Make sure your printer firmware is up to date to use this feature.
This commit is contained in:
@@ -4,6 +4,8 @@ project(libslic3r_gui)
|
||||
include(PrecompiledHeader)
|
||||
|
||||
set(SLIC3R_GUI_SOURCES
|
||||
GUI/Widgets/AnimaController.hpp
|
||||
GUI/Widgets/AnimaController.cpp
|
||||
Config/Snapshot.cpp
|
||||
Config/Snapshot.hpp
|
||||
Config/Version.cpp
|
||||
@@ -371,6 +373,11 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/SavePresetDialog.hpp
|
||||
GUI/SceneRaycaster.cpp
|
||||
GUI/SceneRaycaster.hpp
|
||||
GUI/PartSkipCommon.hpp
|
||||
GUI/PartSkipDialog.cpp
|
||||
GUI/PartSkipDialog.hpp
|
||||
GUI/SkipPartCanvas.cpp
|
||||
GUI/SkipPartCanvas.hpp
|
||||
GUI/Search.cpp
|
||||
GUI/Search.hpp
|
||||
GUI/Selection.cpp
|
||||
|
||||
@@ -535,6 +535,13 @@ bool MachineObject::is_lan_mode_printer() const
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string MachineObject::convertToIp(long long ip)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << ((ip >> 0) & 0xFF) << "." << ((ip >> 8) & 0xFF) << "." << ((ip >> 16) & 0xFF) << "." << ((ip >> 24) & 0xFF);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
PrinterSeries MachineObject::get_printer_series() const
|
||||
{
|
||||
std::string series = DeviceManager::get_printer_series(printer_type);
|
||||
@@ -1853,6 +1860,17 @@ int MachineObject::command_control_fan_val(FanType fan_type, int val)
|
||||
}
|
||||
|
||||
|
||||
int MachineObject::command_task_partskip(std::vector<int> part_ids)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "command_task_partskip: ";
|
||||
json j;
|
||||
j["print"]["command"] = "skip_objects";
|
||||
j["print"]["obj_list"] = part_ids;
|
||||
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
|
||||
return this->publish_json(j.dump(), 1);
|
||||
}
|
||||
|
||||
int MachineObject::command_task_abort()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "command_task_abort: ";
|
||||
@@ -2723,7 +2741,7 @@ void MachineObject::reset()
|
||||
vt_tray.reset();
|
||||
|
||||
subtask_ = nullptr;
|
||||
|
||||
m_partskip_ids.clear();
|
||||
}
|
||||
|
||||
void MachineObject::set_print_state(std::string status)
|
||||
@@ -2959,6 +2977,29 @@ int MachineObject::parse_json(std::string payload, bool key_field_only)
|
||||
print_json.load_compatible_settings(printer_type, "");
|
||||
print_json.diff2all_base_reset(j_pre);
|
||||
}
|
||||
|
||||
if (j_pre["print"].contains("s_obj")){
|
||||
if(j_pre["print"]["s_obj"].is_array()){
|
||||
m_partskip_ids.clear();
|
||||
for(auto it=j_pre["print"]["s_obj"].begin(); it!=j_pre["print"]["s_obj"].end(); it++){
|
||||
m_partskip_ids.push_back(it.value().get<int>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (j_pre["print"].contains("plate_idx")){ // && m_plate_index == -1
|
||||
if (j_pre["print"]["plate_idx"].is_number())
|
||||
{
|
||||
m_plate_index = j_pre["print"]["plate_idx"].get<int>();
|
||||
}
|
||||
else if (j_pre["print"]["plate_idx"].is_string())
|
||||
{
|
||||
try
|
||||
{
|
||||
m_plate_index = std::stoi(j_pre["print"]["plate_idx"].get<std::string>());
|
||||
}
|
||||
catch (...) { BOOST_LOG_TRIVIAL(error) << "parse_json: failed to convert plate_idx to int"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3423,8 +3464,24 @@ int MachineObject::parse_json(std::string payload, bool key_field_only)
|
||||
if (jj["net"].contains("conf")) {
|
||||
network_wired = (jj["net"]["conf"].get<int>() & (0x1)) != 0;
|
||||
}
|
||||
if (jj["net"].contains("info")) {
|
||||
for (auto info_item = jj["net"]["info"].begin(); info_item != jj["net"]["info"].end(); info_item++) {
|
||||
|
||||
if (info_item->contains("ip")) {
|
||||
auto tmp_dev_ip = (*info_item)["ip"].get<int64_t>();
|
||||
if (tmp_dev_ip == 0)
|
||||
continue ;
|
||||
else {
|
||||
set_dev_ip(convertToIp(tmp_dev_ip));
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region online
|
||||
@@ -5170,6 +5227,7 @@ void MachineObject::update_slice_info(std::string project_id, std::string profil
|
||||
|
||||
if (plate_idx >= 0) {
|
||||
plate_index = plate_idx;
|
||||
this->m_plate_index = plate_idx;
|
||||
}
|
||||
else {
|
||||
std::string subtask_json;
|
||||
@@ -5232,8 +5290,7 @@ void MachineObject::update_slice_info(std::string project_id, std::string profil
|
||||
BOOST_LOG_TRIVIAL(error) << "task_info: get subtask id failed!";
|
||||
}
|
||||
}
|
||||
|
||||
this->m_plate_index = plate_index;
|
||||
// this->m_plate_index = plate_index;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -5562,6 +5619,8 @@ void MachineObject::parse_new_info(json print)
|
||||
is_support_nozzle_blob_detection = get_flag_bits(fun, 13);
|
||||
is_support_upgrade_kit = get_flag_bits(cfg, 14);
|
||||
is_support_command_homing = get_flag_bits(fun, 32);
|
||||
is_support_brtc = get_flag_bits(fun, 31);
|
||||
is_support_partskip = get_flag_bits(fun, 49);
|
||||
}
|
||||
|
||||
/*aux*/
|
||||
|
||||
@@ -509,6 +509,7 @@ public:
|
||||
int subscribe_counter{3};
|
||||
std::string dev_connection_type; /* lan | cloud */
|
||||
std::string connection_type() { return dev_connection_type; }
|
||||
|
||||
std::string dev_connection_name; /* lan | eth */
|
||||
void set_dev_ip(std::string ip) {dev_ip = ip;}
|
||||
std::string get_ftp_folder();
|
||||
@@ -520,6 +521,7 @@ public:
|
||||
void erase_user_access_code();
|
||||
std::string get_user_access_code() const;
|
||||
bool is_lan_mode_printer() const;
|
||||
std::string convertToIp(long long ip);
|
||||
|
||||
//PRINTER_TYPE printer_type = PRINTER_3DPrinter_UKNOWN;
|
||||
std::string printer_type; /* model_id */
|
||||
@@ -831,6 +833,9 @@ public:
|
||||
int xcam_filament_tangle_detect_count = 0;
|
||||
int ams_print_option_count = 0;
|
||||
|
||||
// part skip
|
||||
std::vector<int> m_partskip_ids;
|
||||
|
||||
//supported features
|
||||
bool is_support_chamber_edit{false};
|
||||
bool is_support_extrusion_cali{false};
|
||||
@@ -870,6 +875,8 @@ public:
|
||||
bool is_support_agora{false};
|
||||
bool is_support_upgrade_kit{false};
|
||||
bool is_support_command_homing { false };// fun[32]
|
||||
bool is_support_brtc{false}; // fun[31], support tcp and upload protocol
|
||||
bool is_support_partskip{false};
|
||||
|
||||
bool installed_upgrade_kit{false};
|
||||
int nozzle_max_temperature = -1;
|
||||
@@ -952,6 +959,7 @@ public:
|
||||
int command_control_fan_val(FanType fan_type, int val);
|
||||
int command_task_abort();
|
||||
/* cancelled the job_id */
|
||||
int command_task_partskip(std::vector<int> part_ids);
|
||||
int command_task_cancel(std::string job_id);
|
||||
int command_task_pause();
|
||||
int command_task_resume();
|
||||
|
||||
@@ -297,7 +297,12 @@ void MediaFilePanel::SetMachineObject(MachineObject* obj)
|
||||
case PrinterFileSystem::Initializing: icon = m_bmp_loading; msg = _L("Initializing..."); break;
|
||||
case PrinterFileSystem::Connecting: icon = m_bmp_loading; msg = _L("Connecting..."); break;
|
||||
case PrinterFileSystem::Failed: icon = m_bmp_failed; if (extra != 1) msg = _L("Please check the network and try again. You can restart or update the printer if the issue persists."); break;
|
||||
case PrinterFileSystem::ListSyncing: icon = m_bmp_loading; msg = _L("Loading file list..."); break;
|
||||
case PrinterFileSystem::ListSyncing: {
|
||||
icon = m_bmp_loading;
|
||||
msg = _L("Loading file list...");
|
||||
fs->ListAllFiles();
|
||||
break;
|
||||
}
|
||||
case PrinterFileSystem::ListReady: icon = extra == 0 ? m_bmp_empty : m_bmp_failed; msg = extra == 0 ? _L("No files") : _L("Load failed"); break;
|
||||
}
|
||||
int err = fs->GetLastError();
|
||||
|
||||
18
src/slic3r/GUI/PartSkipCommon.hpp
Normal file
18
src/slic3r/GUI/PartSkipCommon.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef PARTSKIPCOMMON_H
|
||||
#define PARTSKIPCOMMON_H
|
||||
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
enum PartState {
|
||||
psUnCheck,
|
||||
psChecked,
|
||||
psSkipped
|
||||
};
|
||||
|
||||
|
||||
typedef std::vector<std::pair<int, PartState>> PartsInfo;
|
||||
|
||||
}}
|
||||
|
||||
#endif // PARTSKIPCOMMON_H
|
||||
1045
src/slic3r/GUI/PartSkipDialog.cpp
Normal file
1045
src/slic3r/GUI/PartSkipDialog.cpp
Normal file
File diff suppressed because it is too large
Load Diff
163
src/slic3r/GUI/PartSkipDialog.hpp
Normal file
163
src/slic3r/GUI/PartSkipDialog.hpp
Normal file
@@ -0,0 +1,163 @@
|
||||
#include <wx/panel.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/gbsizer.h>
|
||||
#include <wx/webrequest.h>
|
||||
#include <wx/control.h>
|
||||
#include <wx/dcclient.h>
|
||||
#include <wx/display.h>
|
||||
#include <wx/mstream.h>
|
||||
#include <wx/sstream.h>
|
||||
#include <wx/zstream.h>
|
||||
#include <wx/window.h>
|
||||
#include <wx/dcgraph.h>
|
||||
#include <wx/simplebook.h>
|
||||
|
||||
#include "Widgets/Label.hpp"
|
||||
#include "Widgets/CheckBox.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
#include "Widgets/AnimaController.hpp"
|
||||
#include "DeviceManager.hpp"
|
||||
#include "PartSkipCommon.hpp"
|
||||
#include "Printer/PrinterFileSystem.h"
|
||||
#include "I18N.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
class SkipPartCanvas;
|
||||
|
||||
enum URL_STATE {
|
||||
URL_TCP,
|
||||
URL_TUTK,
|
||||
};
|
||||
|
||||
class PartSkipConfirmDialog : public DPIDialog
|
||||
{
|
||||
private:
|
||||
protected:
|
||||
Label *m_msg_label;
|
||||
Label *m_tip_label;
|
||||
Button *m_apply_button;
|
||||
|
||||
public:
|
||||
PartSkipConfirmDialog(wxWindow *parent);
|
||||
~PartSkipConfirmDialog();
|
||||
|
||||
void on_dpi_changed(const wxRect &suggested_rect);
|
||||
Button *GetConfirmButton();
|
||||
void SetMsgLabel(wxString msg);
|
||||
void SetTipLabel(wxString msg);
|
||||
bool Show(bool show);
|
||||
};
|
||||
|
||||
class PartSkipDialog : public DPIDialog
|
||||
{
|
||||
public:
|
||||
PartSkipDialog(wxWindow *parent);
|
||||
~PartSkipDialog();
|
||||
void on_dpi_changed(const wxRect &suggested_rect);
|
||||
bool Show(bool show);
|
||||
|
||||
void UpdatePartsStateFromPrinter(MachineObject *obj_);
|
||||
void SetSimplebookPage(int page);
|
||||
void InitSchedule(MachineObject *obj_);
|
||||
void InitDialogUI();
|
||||
int GetAllSkippedPartsNum();
|
||||
|
||||
MachineObject *m_obj{nullptr};
|
||||
|
||||
wxSimplebook *m_simplebook;
|
||||
wxPanel *m_book_third_panel;
|
||||
wxPanel *m_book_second_panel;
|
||||
wxPanel *m_book_first_panel;
|
||||
|
||||
SkipPartCanvas *m_canvas;
|
||||
Button *m_zoom_in_btn;
|
||||
Button *m_zoom_out_btn;
|
||||
Button *m_switch_drag_btn;
|
||||
CheckBox *m_all_checkbox;
|
||||
Button *m_percent_label;
|
||||
Label *m_all_label;
|
||||
wxPanel *m_line;
|
||||
wxPanel *m_line_top;
|
||||
wxScrolledWindow *m_list_view;
|
||||
|
||||
wxPanel *m_dlg_placeholder;
|
||||
Label *m_cnt_label;
|
||||
Label *m_tot_label;
|
||||
|
||||
Button *m_apply_btn;
|
||||
|
||||
Label *m_loading_label;
|
||||
Label *m_retry_label;
|
||||
ScalableBitmap *m_retry_icon;
|
||||
wxStaticBitmap *m_retry_bitmap;
|
||||
|
||||
wxBoxSizer *m_sizer;
|
||||
wxBoxSizer *m_dlg_sizer;
|
||||
wxBoxSizer *m_dlg_content_sizer;
|
||||
wxBoxSizer *m_dlg_btn_sizer;
|
||||
wxBoxSizer *m_canvas_sizer;
|
||||
wxBoxSizer *m_canvas_btn_sizer;
|
||||
wxBoxSizer *m_list_sizer;
|
||||
wxBoxSizer *m_scroll_sizer;
|
||||
wxBoxSizer *m_book_first_sizer;
|
||||
wxBoxSizer *m_book_second_sizer;
|
||||
wxBoxSizer *m_book_second_btn_sizer;
|
||||
Button *m_second_retry_btn;
|
||||
AnimaIcon *m_loading_icon;
|
||||
|
||||
private:
|
||||
int m_plate_idx{-1};
|
||||
int m_zoom_percent{100};
|
||||
bool m_is_drag{false};
|
||||
bool m_print_lock{true};
|
||||
bool m_enable_apply_btn{false};
|
||||
bool is_model_support_partskip{false};
|
||||
|
||||
std::map<uint32_t, PartState> m_parts_state;
|
||||
std::map<uint32_t, std::string> m_parts_name;
|
||||
std::vector<int> m_partskip_ids;
|
||||
|
||||
enum URL_STATE m_url_state = URL_STATE::URL_TCP;
|
||||
|
||||
PartsInfo GetPartsInfo();
|
||||
bool is_drag_mode();
|
||||
|
||||
boost::shared_ptr<PrinterFileSystem> m_file_sys;
|
||||
bool m_file_sys_result{false};
|
||||
std::string m_timestamp;
|
||||
std::string m_tmp_path;
|
||||
std::vector<string> m_local_paths;
|
||||
std::vector<string> m_target_paths;
|
||||
std::string create_tmp_path();
|
||||
|
||||
bool is_local_file_existed(const std::vector<string> &local_paths);
|
||||
|
||||
void DownloadPartsFile();
|
||||
void OnFileSystemEvent(wxCommandEvent &event);
|
||||
void OnFileSystemResult(wxCommandEvent &event);
|
||||
void fetchUrl(boost::weak_ptr<PrinterFileSystem> wfs);
|
||||
|
||||
void OnZoomIn(wxCommandEvent &event);
|
||||
void OnZoomOut(wxCommandEvent &event);
|
||||
void OnSwitchDrag(wxCommandEvent &event);
|
||||
void OnZoomPercent(wxCommandEvent &event);
|
||||
void UpdatePartsStateFromCanvas(wxCommandEvent &event);
|
||||
|
||||
void UpdateZoomPercent();
|
||||
void UpdateCountLabel();
|
||||
void UpdateDialogUI();
|
||||
void UpdateApplyButtonStatus();
|
||||
bool IsAllChecked();
|
||||
bool IsAllCancled();
|
||||
|
||||
void OnRetryButton(wxCommandEvent &event);
|
||||
void OnAllCheckbox(wxCommandEvent &event);
|
||||
void OnApplyDialog(wxCommandEvent &event);
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -39,18 +39,31 @@ wxDEFINE_EVENT(EVT_FILE_CHANGED, wxCommandEvent);
|
||||
wxDEFINE_EVENT(EVT_SELECT_CHANGED, wxCommandEvent);
|
||||
wxDEFINE_EVENT(EVT_THUMBNAIL, wxCommandEvent);
|
||||
wxDEFINE_EVENT(EVT_DOWNLOAD, wxCommandEvent);
|
||||
wxDEFINE_EVENT(EVT_RAMDOWNLOAD, wxCommandEvent);
|
||||
wxDEFINE_EVENT(EVT_MEDIA_ABILITY_CHANGED, wxCommandEvent);
|
||||
wxDEFINE_EVENT(EVT_UPLOADING, wxCommandEvent);
|
||||
wxDEFINE_EVENT(EVT_UPLOAD_CHANGED, wxCommandEvent);
|
||||
|
||||
wxDEFINE_EVENT(EVT_FILE_CALLBACK, wxCommandEvent);
|
||||
|
||||
static wxBitmap default_thumbnail;
|
||||
|
||||
static std::map<int, std::string> error_messages = {
|
||||
{PrinterFileSystem::ERROR_PIPE, L("Reconnecting the printer, the operation cannot be completed immediately, please try again later.")},
|
||||
{PrinterFileSystem::ERROR_RES_BUSY, L("The device cannot handle more conversations. Please retry later.")},
|
||||
{PrinterFileSystem::FILE_NO_EXIST, L("File does not exist.")},
|
||||
{PrinterFileSystem::FILE_CHECK_ERR, L("File checksum error. Please retry.")},
|
||||
{PrinterFileSystem::FILE_TYPE_ERR, L("Not supported on the current printer version.")},
|
||||
{PrinterFileSystem::STORAGE_UNAVAILABLE, L("Storage unavailable, insert SD card.")}
|
||||
{PrinterFileSystem::ERROR_PIPE, L("Reconnecting the printer, the operation cannot be completed immediately, please try again later.")},
|
||||
{PrinterFileSystem::ERROR_RES_BUSY, L("The device cannot handle more conversations. Please retry later.")},
|
||||
{PrinterFileSystem::ERROR_TIME_OUT, L("Timeout, please try again.")},
|
||||
{PrinterFileSystem::FILE_NO_EXIST, L("File does not exist.")},
|
||||
{PrinterFileSystem::FILE_CHECK_ERR, L("File checksum error. Please retry.")},
|
||||
{PrinterFileSystem::FILE_TYPE_ERR, L("Not supported on the current printer version.")},
|
||||
{PrinterFileSystem::STORAGE_UNAVAILABLE, L("Please check if the SD card is inserted into the printer.\nIf it still cannot be read, you can try formatting the SD card.")},
|
||||
{PrinterFileSystem::API_VERSION_UNSUPPORT, L("The firmware version of the printer is too low. Please update the firmware and try again.")},
|
||||
{PrinterFileSystem::FILE_EXIST, L("The file already exists, do you want to replace it?")},
|
||||
{PrinterFileSystem::STORAGE_SPACE_NOT_ENOUGH, L("Insufficient storage space, please clear the space and try again.")},
|
||||
{PrinterFileSystem::FILE_CREATE_ERR, L("File creation failed, please try again.")},
|
||||
{PrinterFileSystem::FILE_WRITE_ERR, L("File write failed, please try again.")},
|
||||
{PrinterFileSystem::MD5_COMPARE_ERR, L("MD5 verification failed, please try again.")},
|
||||
{PrinterFileSystem::FILE_RENAME_ERR, L("File renaming failed, please try again.")},
|
||||
{PrinterFileSystem::SEND_ERR, L("File upload failed, please try again.")}
|
||||
};
|
||||
|
||||
struct StaticBambuLib : BambuLib {
|
||||
@@ -117,7 +130,6 @@ void PrinterFileSystem::SetFileType(FileType type, std::string const &storage)
|
||||
return;
|
||||
m_status = Status::ListSyncing;
|
||||
SendChangedEvent(EVT_STATUS_CHANGED, m_status);
|
||||
ListAllFiles();
|
||||
}
|
||||
|
||||
void PrinterFileSystem::SetGroupMode(GroupMode mode)
|
||||
@@ -248,6 +260,156 @@ struct PrinterFileSystem::Download : Progress
|
||||
boost::uuids::detail::md5 boost_md5;
|
||||
};
|
||||
|
||||
struct PrinterFileSystem::Upload : Progress
|
||||
{
|
||||
std::string error;
|
||||
boost::uint32_t frag_id{0};
|
||||
MD5_CTX ctx;
|
||||
boost::filesystem::ifstream ifs;
|
||||
};
|
||||
|
||||
void PrinterFileSystem::GetPickImages(const std::vector<std::string> &local_paths, const std::vector<std::string> &targetpaths)
|
||||
{
|
||||
m_download_states.clear();
|
||||
|
||||
GetPickImage(1, local_paths[0], targetpaths[0]);
|
||||
GetPickImage(2, local_paths[1], targetpaths[1]);
|
||||
GetPickImage(3, local_paths[2], targetpaths[2]);
|
||||
|
||||
}
|
||||
|
||||
void PrinterFileSystem::GetPickImage(int id, const std::string &local_path, const std::string &targetpath)
|
||||
{
|
||||
json j;
|
||||
|
||||
j["sequence_id"] = id;
|
||||
j["version"] = 1;
|
||||
j["peer_host"] = "studio";
|
||||
j["command"] = "get_project_file";
|
||||
j["file_rel_path"] = targetpath;
|
||||
|
||||
std::string param = j.dump();
|
||||
|
||||
DownloadRamFile(16, local_path, param);
|
||||
}
|
||||
|
||||
|
||||
void PrinterFileSystem::DownloadRamFile(int index, const std::string &local_path, const std::string & param)
|
||||
{
|
||||
std::shared_ptr<Download> download(new Download);
|
||||
download->local_path = local_path;
|
||||
|
||||
json req;
|
||||
req["path"] = "mem:/" + std::to_string(index);
|
||||
req["offset"] = 0;
|
||||
req["mem_dl_param_size"] = param.size();
|
||||
|
||||
m_download_seq = SendRequest<Progress>(
|
||||
FILE_DOWNLOAD, req,
|
||||
[download](json const &resp, Progress &prog, unsigned char const *data) -> int {
|
||||
size_t size = resp.value("size", 0);
|
||||
prog.size = resp["offset"];
|
||||
prog.total = resp["total"];
|
||||
|
||||
if (resp.contains("mem_dl_param_size")) {
|
||||
size_t s = resp["mem_dl_param_size"].get<size_t>();
|
||||
std::string json_str(reinterpret_cast<const char *>(data), s);
|
||||
// OutputDebugStringA(json_str.c_str());
|
||||
// OutputDebugStringA("\n");
|
||||
json mem_dl_json = json::parse(json_str);
|
||||
// download->mem_dl_param_size = size;
|
||||
if (!mem_dl_json.contains("result") || mem_dl_json["result"] == 1 ) {
|
||||
wxLogWarning("Download failed: result = 1");
|
||||
return ERROR_JSON;
|
||||
}
|
||||
if(mem_dl_json.contains("size") && mem_dl_json["size"] == 0 )
|
||||
return FILE_SIZE_ERR;
|
||||
|
||||
return CONTINUE;
|
||||
}
|
||||
|
||||
if (prog.size == 0 ) {
|
||||
download->ofs.open(download->local_path, std::ios::binary);
|
||||
if (!download->ofs) {
|
||||
download->error = last_system_error();
|
||||
wxLogWarning("DownloadImageFromRam open error: %s\n", wxString::FromUTF8(download->error));
|
||||
return FILE_OPEN_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
download->ofs.write(reinterpret_cast<const char *>(data), size);
|
||||
if (!download->ofs) {
|
||||
download->error = last_system_error();
|
||||
wxLogWarning("DownloadImageFromRam write error: %s\n", wxString::FromUTF8(download->error));
|
||||
return FILE_READ_WRITE_ERR;
|
||||
}
|
||||
|
||||
download->boost_md5.process_bytes(data, size);
|
||||
|
||||
prog.size += size;
|
||||
download->total = prog.total;
|
||||
download->size = prog.size;
|
||||
|
||||
if (prog.size < prog.total) {
|
||||
return 0;
|
||||
}
|
||||
download->ofs.close();
|
||||
|
||||
std::string md5 = resp["file_md5"];
|
||||
boost::uuids::detail::md5::digest_type digest;
|
||||
download->boost_md5.get_digest(digest);
|
||||
for (int i = 0; i < 4; ++i) digest[i] = boost::endian::endian_reverse(digest[i]);
|
||||
std::string str_md5;
|
||||
const auto char_digest = reinterpret_cast<const char *>(&digest[0]);
|
||||
boost::algorithm::hex(char_digest, char_digest + sizeof(digest), std::back_inserter(str_md5));
|
||||
if (!boost::iequals(str_md5, md5)) {
|
||||
wxLogWarning("DownloadImageFromRam checksum error: %s != %s\n", str_md5, md5);
|
||||
boost::system::error_code ec;
|
||||
boost::filesystem::rename(download->local_path, download->local_path + ".tmp", ec);
|
||||
return FILE_CHECK_ERR;
|
||||
}
|
||||
return SUCCESS;
|
||||
},
|
||||
|
||||
[this, download](int result, Progress const &data) {
|
||||
//OutputDebugStringA(std::to_string(result).c_str());
|
||||
//OutputDebugStringA("\n");
|
||||
if (result == CONTINUE) { return; }
|
||||
std::string msg;
|
||||
if (result == SUCCESS) {
|
||||
if (std::filesystem::exists(download->local_path)) {
|
||||
m_download_states.emplace_back(true);
|
||||
BOOST_LOG_TRIVIAL(info) <<"DownloadImageFromRam finished: " << download->local_path << "result = " << result;
|
||||
}else{
|
||||
m_download_states.emplace_back(false);
|
||||
BOOST_LOG_TRIVIAL(warning) <<"DownloadImageFromRam finished, but file not exist: " << download->local_path << "result = " << result;
|
||||
}
|
||||
} else if (result != CONTINUE) {
|
||||
m_download_states.emplace_back(false);
|
||||
BOOST_LOG_TRIVIAL(warning) << "DownloadImageFromRam failed: " << download->error << "result = " << result;
|
||||
}
|
||||
|
||||
if(m_download_states.size() == 3){
|
||||
if(m_download_states[0] && m_download_states[1] && m_download_states[2]){
|
||||
SendChangedEvent(EVT_RAMDOWNLOAD, SUCCESS);
|
||||
}else{
|
||||
// FILE_NO_EXIST is not really error_code
|
||||
SendChangedEvent(EVT_RAMDOWNLOAD, FILE_NO_EXIST);
|
||||
}
|
||||
}else{
|
||||
BOOST_LOG_TRIVIAL(warning) << "m_download_states current size is : " << m_download_states.size();
|
||||
}
|
||||
},param);
|
||||
}
|
||||
|
||||
void PrinterFileSystem::SendExistedFile(){
|
||||
SendChangedEvent(EVT_RAMDOWNLOAD, SUCCESS);
|
||||
}
|
||||
void PrinterFileSystem::SendConnectFail(){
|
||||
SendChangedEvent(EVT_RAMDOWNLOAD, ERROR_PIPE);
|
||||
}
|
||||
|
||||
|
||||
void PrinterFileSystem::DownloadFiles(size_t index, std::string const &path)
|
||||
{
|
||||
if (index == (size_t) -1) {
|
||||
@@ -281,6 +443,10 @@ void PrinterFileSystem::DownloadFiles(size_t index, std::string const &path)
|
||||
DownloadNextFile();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrinterFileSystem::DownloadCheckFiles(std::string const &path)
|
||||
{
|
||||
for (size_t i = 0; i < m_file_list.size(); ++i) {
|
||||
@@ -524,6 +690,16 @@ void PrinterFileSystem::Stop(bool quit)
|
||||
m_cond.notify_all();
|
||||
}
|
||||
|
||||
void PrinterFileSystem::SetUploadFile(const std::string &path, const std::string &name, const std::string &select_storage)
|
||||
{
|
||||
if (!m_upload_file) {
|
||||
m_upload_file = std::make_unique<UploadFile>();
|
||||
}
|
||||
m_upload_file->path = path;
|
||||
m_upload_file->name = name;
|
||||
m_upload_file->select_storage = select_storage;
|
||||
}
|
||||
|
||||
void PrinterFileSystem::BuildGroups()
|
||||
{
|
||||
m_group_year.clear();
|
||||
@@ -1030,21 +1206,216 @@ void PrinterFileSystem::DumpLog(void * thiz, int, tchar const *msg)
|
||||
static_cast<PrinterFileSystem*>(thiz)->Bambu_FreeLogMsg(msg);
|
||||
}
|
||||
|
||||
boost::uint32_t PrinterFileSystem::SendRequest(int type, json const &req, callback_t2 const &callback)
|
||||
boost::uint32_t PrinterFileSystem::RequestMediaAbility(int api_version)
|
||||
{
|
||||
json req;
|
||||
req["peer"] = "studio";
|
||||
req["api_version"] = api_version;
|
||||
|
||||
return SendRequest<MediaAbilityList>(
|
||||
REQUEST_MEDIA_ABILITY, req, [this](const json &resp, MediaAbilityList &list, auto) -> int {
|
||||
json abliity_list = resp["storage"];
|
||||
list = abliity_list.get<MediaAbilityList>();
|
||||
return 0;
|
||||
},
|
||||
[this](int result, MediaAbilityList list){
|
||||
if (result != 0) {
|
||||
m_last_error = result;
|
||||
m_media_ability_list.clear();
|
||||
SendChangedEvent(EVT_MEDIA_ABILITY_CHANGED, RequestMediaAbilityStatus::S_FAILED, "", m_last_error);
|
||||
return result;
|
||||
}
|
||||
|
||||
m_media_ability_list.swap(list);
|
||||
SendChangedEvent(EVT_MEDIA_ABILITY_CHANGED, RequestMediaAbilityStatus::S_SUCCESS);
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
void PrinterFileSystem::RequestUploadFile()
|
||||
{
|
||||
json req;
|
||||
req["type"] = "model";
|
||||
req["storage"] = m_upload_file->select_storage;
|
||||
req["path"] = m_upload_file->name;
|
||||
|
||||
m_upload_file->upload = std::make_unique<Upload>();
|
||||
boost::filesystem::path path = boost::filesystem::path(m_upload_file->path);
|
||||
boost::system::error_code ec;
|
||||
boost::uint32_t file_size = boost::filesystem::file_size(path, ec);
|
||||
|
||||
req["total"] = file_size;
|
||||
m_upload_file->size = file_size;
|
||||
m_upload_file->upload->total = file_size;
|
||||
|
||||
m_upload_seq = SendRequest(
|
||||
FILE_UPLOAD, req,
|
||||
[this](int result, const json& resp, auto) -> int{
|
||||
if (result != SUCCESS && result != CONTINUE && result != FILE_EXIST) {
|
||||
std::string error_msg = "";
|
||||
if (result == ERROR_CANCEL) {
|
||||
error_msg = L("User cancels task.");
|
||||
} else if (result == FILE_READ_WRITE_ERR || result == FILE_OPEN_ERR) {
|
||||
error_msg = L("Failed to read file, please try again.");
|
||||
}
|
||||
wxLogWarning("PrinterFileSystem::UploadFile error: %d\n", result);
|
||||
SendChangedEvent(EVT_UPLOAD_CHANGED, FF_UPLOADCANCEL, error_msg, result);
|
||||
} else if (result == SUCCESS) {
|
||||
SendChangedEvent(EVT_UPLOADING, 100);
|
||||
SendChangedEvent(EVT_UPLOAD_CHANGED, FF_UPLOADDONE);
|
||||
} else if (result == CONTINUE || result == FILE_EXIST) {
|
||||
if (m_upload_file) {
|
||||
m_upload_file->chunk_size = resp["chunk_size"];
|
||||
m_upload_file->upload->size = resp["offset"];
|
||||
m_upload_file->flags |= FF_UPLOADING;
|
||||
}
|
||||
|
||||
{
|
||||
boost::unique_lock l(m_mutex);
|
||||
auto cb = [this, upload_file = m_upload_file, seq = m_upload_seq](std::string &msg) -> int {
|
||||
return UploadFileTask(upload_file, seq, msg);
|
||||
};
|
||||
m_produce_message_cb_map[m_upload_seq] = cb;
|
||||
}
|
||||
|
||||
return CONTINUE;
|
||||
}
|
||||
|
||||
// reset m_upload_file
|
||||
if (m_upload_file) {
|
||||
m_upload_file.reset();
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
int PrinterFileSystem::UploadFileTask(std::shared_ptr<UploadFile> upload_file, boost::uint64_t seq, std::string &msg)
|
||||
{
|
||||
if (!upload_file)
|
||||
return FILE_OPEN_ERR;
|
||||
|
||||
if (!(upload_file->flags & FF_UPLOADING))
|
||||
return FILE_OPEN_ERR;
|
||||
|
||||
auto &upload = upload_file->upload;
|
||||
if (!upload->ifs.is_open()) {
|
||||
upload->ifs.open(upload_file->path, std::ios::binary);
|
||||
if (!upload_file->upload->ifs) {
|
||||
wxLogWarning("PrinterFileSystem::UploadFile open error: %s\n", wxString::FromUTF8(upload_file->path));
|
||||
return FILE_OPEN_ERR;
|
||||
}
|
||||
MD5_Init(&upload->ctx);
|
||||
}
|
||||
|
||||
const boost::uint32_t buffer_size = upload_file->chunk_size * 1024;
|
||||
char *buffer = new char[buffer_size];
|
||||
|
||||
upload->ifs.seekg(upload->size, std::ios::beg);
|
||||
upload->ifs.read(buffer, buffer_size);
|
||||
boost::int32_t read_size = upload->ifs.gcount();
|
||||
|
||||
if (read_size <= 0) {
|
||||
wxLogWarning("PrinterFileSystem::Upload read error.\n");
|
||||
upload->ifs.close();
|
||||
|
||||
if (buffer) {
|
||||
delete[] buffer;
|
||||
buffer = nullptr;
|
||||
}
|
||||
return FILE_READ_WRITE_ERR;
|
||||
}
|
||||
|
||||
json req;
|
||||
req["frag_id"] = upload->frag_id;
|
||||
req["offset"] = upload->size;
|
||||
req["size"] = read_size;
|
||||
|
||||
MD5_Update(&upload->ctx, buffer, read_size);
|
||||
upload->size += read_size;
|
||||
if (upload->size == upload->total) {
|
||||
unsigned char digest[16];
|
||||
MD5_Final(digest, &upload->ctx);
|
||||
char md5_str[33];
|
||||
for (int j = 0; j < 16; j++) { sprintf(&md5_str[j * 2], "%02X", (unsigned int) digest[j]); }
|
||||
std::string md5_out = std::string(md5_str);
|
||||
std::transform(md5_out.begin(), md5_out.end(), md5_out.begin(), ::tolower);
|
||||
|
||||
req["file_md5"] = md5_out;
|
||||
// OutputDebugStringA(md5_out.c_str());
|
||||
// OutputDebugStringA("\n");
|
||||
}
|
||||
|
||||
if (m_upload_file && m_upload_file->flags & FF_UPLOADING) {
|
||||
upload->frag_id++;
|
||||
upload->progress = upload->size * 100 / upload->total;
|
||||
int progress = upload->progress == 100 ? 99 : upload->progress;
|
||||
SendChangedEvent(EVT_UPLOADING, progress);
|
||||
}
|
||||
|
||||
json root;
|
||||
|
||||
root["cmdtype"] = FILE_UPLOAD;
|
||||
root["sequence"] = seq;
|
||||
root["req"] = req;
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << root;
|
||||
oss << "\n\n";
|
||||
oss << std::string(buffer, read_size);
|
||||
msg = oss.str();
|
||||
|
||||
if (buffer) {
|
||||
delete[] buffer;
|
||||
buffer = nullptr;
|
||||
}
|
||||
|
||||
if (upload->size == upload->total) {
|
||||
upload->ifs.close();
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
return CONTINUE;
|
||||
}
|
||||
|
||||
PrinterFileSystem::MediaAbilityList PrinterFileSystem::GetMediaAbilityList() const
|
||||
{
|
||||
return m_media_ability_list;
|
||||
}
|
||||
|
||||
void PrinterFileSystem::CancelUploadTask(bool send_cancel_req)
|
||||
{
|
||||
if (!m_upload_file)
|
||||
return;
|
||||
|
||||
if (send_cancel_req) {
|
||||
CancelRequest(m_upload_seq);
|
||||
} else {
|
||||
CancelRequests2({m_upload_seq});
|
||||
}
|
||||
}
|
||||
|
||||
boost::uint32_t PrinterFileSystem::SendRequest(int type, json const &req, callback_t2 const &callback,const std::string& param)
|
||||
{
|
||||
if (m_session.tunnel == nullptr) {
|
||||
Retry();
|
||||
callback(ERROR_PIPE, json(), nullptr);
|
||||
return 0;
|
||||
}
|
||||
boost::uint32_t seq = m_sequence + m_callbacks.size();
|
||||
boost::uint32_t seq = m_sequence + m_callbacks.size();
|
||||
json root;
|
||||
root["cmdtype"] = type;
|
||||
root["sequence"] = seq;
|
||||
root["req"] = req;
|
||||
std::ostringstream oss;
|
||||
oss << root;
|
||||
auto msg = oss.str();
|
||||
|
||||
if (!param.empty()) {
|
||||
oss << "\n\n";
|
||||
oss << param;
|
||||
}
|
||||
// OutputDebugStringA(oss.str().c_str());
|
||||
// OutputDebugStringA("\n");
|
||||
auto msg = oss.str();
|
||||
boost::unique_lock l(m_mutex);
|
||||
m_messages.push_back(msg);
|
||||
m_callbacks.push_back(callback);
|
||||
@@ -1085,13 +1456,15 @@ void PrinterFileSystem::CancelRequests2(std::vector<boost::uint32_t> const &seqs
|
||||
for (auto &f : seqs) {
|
||||
boost::uint32_t seq = f;
|
||||
seq -= m_sequence;
|
||||
if (size_t(seq) >= m_callbacks.size())
|
||||
continue;
|
||||
if (size_t(seq) >= m_callbacks.size()) continue;
|
||||
auto &c = m_callbacks[seq];
|
||||
if (c == nullptr)
|
||||
continue;
|
||||
if (c == nullptr) continue;
|
||||
callbacks.emplace_back(f, c);
|
||||
c = nullptr;
|
||||
|
||||
// erase m_produce_message_cb
|
||||
if (m_produce_message_cb_map.find(seq) != m_produce_message_cb_map.end())
|
||||
m_produce_message_cb_map.erase(seq);
|
||||
}
|
||||
while (!m_callbacks.empty() && m_callbacks.front() == nullptr) {
|
||||
m_callbacks.pop_front();
|
||||
@@ -1118,6 +1491,45 @@ void PrinterFileSystem::RecvMessageThread()
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (m_messages.empty() && !m_produce_message_cb_map.empty()) {
|
||||
auto it = m_produce_message_cb_map.begin();
|
||||
while(it != m_produce_message_cb_map.end()) {
|
||||
std::string msg;
|
||||
auto prodeuce_message_cb = it->second;
|
||||
l.unlock();
|
||||
int res = prodeuce_message_cb(msg);
|
||||
l.lock();
|
||||
if (res == CONTINUE || res == SUCCESS) {
|
||||
m_messages.emplace_back(msg);
|
||||
if (res == SUCCESS) {
|
||||
it = m_produce_message_cb_map.erase(it);
|
||||
continue;
|
||||
}
|
||||
it++;
|
||||
} else {
|
||||
int seq2 = it->first - m_sequence;
|
||||
// erase it
|
||||
it = m_produce_message_cb_map.erase(it);
|
||||
if (size_t(seq2) >= m_callbacks.size())
|
||||
continue;
|
||||
auto c = m_callbacks[seq2];
|
||||
if (c == nullptr)
|
||||
continue;;
|
||||
m_callbacks[seq2] = nullptr;
|
||||
if (seq2 == 0) {
|
||||
// if produce message return error, erase callback and sequence should plus
|
||||
while (!m_callbacks.empty() && m_callbacks.front() == nullptr) {
|
||||
m_callbacks.pop_front();
|
||||
++m_sequence;
|
||||
}
|
||||
}
|
||||
|
||||
l.unlock();
|
||||
c(res, json(), nullptr);
|
||||
l.lock();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!m_messages.empty()) {
|
||||
auto & msg = m_messages.front();
|
||||
// OutputDebugStringA(msg.c_str());
|
||||
@@ -1201,7 +1613,7 @@ void PrinterFileSystem::HandleResponse(boost::unique_lock<boost::mutex> &l, Bamb
|
||||
int result2 = c(result, resp, json_end);
|
||||
l.lock();
|
||||
if (result2 != CONTINUE) {
|
||||
int seq2 = seq - m_sequence;
|
||||
int seq2 = seq - m_sequence;
|
||||
m_callbacks[seq2] = callback_t2();
|
||||
if (seq2 == 0) {
|
||||
while (!m_callbacks.empty() && m_callbacks.front() == nullptr) {
|
||||
@@ -1214,6 +1626,11 @@ void PrinterFileSystem::HandleResponse(boost::unique_lock<boost::mutex> &l, Bamb
|
||||
CancelRequest(seq);
|
||||
l.lock();
|
||||
}
|
||||
|
||||
// error should erase m_produce_message_cb
|
||||
if (m_produce_message_cb_map.find(seq2) != m_produce_message_cb_map.end()) {
|
||||
m_produce_message_cb_map.erase(seq2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1232,7 +1649,7 @@ void PrinterFileSystem::Reconnect(boost::unique_lock<boost::mutex> &l, int resul
|
||||
if (m_session.owner == nullptr)
|
||||
return;
|
||||
json r;
|
||||
while (!m_callbacks.empty()) {
|
||||
while(!m_callbacks.empty()) {
|
||||
auto c = m_callbacks.front();
|
||||
m_callbacks.pop_front();
|
||||
++m_sequence;
|
||||
@@ -1245,7 +1662,9 @@ void PrinterFileSystem::Reconnect(boost::unique_lock<boost::mutex> &l, int resul
|
||||
while (m_stopped) {
|
||||
if (m_session.owner == nullptr)
|
||||
return;
|
||||
m_cond.wait(l);
|
||||
m_status = Status::Reconnecting;
|
||||
SendChangedEvent(EVT_STATUS_CHANGED, m_status);
|
||||
m_cond.wait(l);
|
||||
}
|
||||
wxLogMessage("PrinterFileSystem::Reconnect Initializing");
|
||||
m_status = Status::Initializing;
|
||||
@@ -1271,6 +1690,7 @@ void PrinterFileSystem::Reconnect(boost::unique_lock<boost::mutex> &l, int resul
|
||||
Bambu_Tunnel tunnel = nullptr;
|
||||
int ret = Bambu_Create(&tunnel, url.c_str());
|
||||
if (ret == 0) {
|
||||
|
||||
Bambu_SetLogger(tunnel, DumpLog, this);
|
||||
ret = Bambu_Open(tunnel);
|
||||
}
|
||||
@@ -1297,15 +1717,19 @@ void PrinterFileSystem::Reconnect(boost::unique_lock<boost::mutex> &l, int resul
|
||||
}
|
||||
wxLogMessage("PrinterFileSystem::Reconnect Failed");
|
||||
m_status = Status::Failed;
|
||||
|
||||
SendChangedEvent(EVT_STATUS_CHANGED, m_status, "", url.size() < 2 ? 1 : m_last_error);
|
||||
m_cond.timed_wait(l, boost::posix_time::seconds(10));
|
||||
}
|
||||
m_status = Status::ListSyncing;
|
||||
SendChangedEvent(EVT_STATUS_CHANGED, m_status);
|
||||
|
||||
#ifdef PRINTER_FILE_SYSTEM_TEST
|
||||
PostCallback([this] { SendChangedEvent(EVT_FILE_CHANGED); });
|
||||
#else
|
||||
PostCallback([this] { m_task_flags = 0; ListAllFiles(); });
|
||||
PostCallback([this] {
|
||||
m_task_flags = 0;
|
||||
m_status = Status::ListSyncing;
|
||||
SendChangedEvent(EVT_STATUS_CHANGED, m_status);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -22,39 +22,53 @@ wxDECLARE_EVENT(EVT_FILE_CHANGED, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_SELECT_CHANGED, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_THUMBNAIL, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_DOWNLOAD, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_RAMDOWNLOAD, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_MEDIA_ABILITY_CHANGED, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_UPLOADING, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_UPLOAD_CHANGED, wxCommandEvent);
|
||||
|
||||
class PrinterFileSystem : public wxEvtHandler, public boost::enable_shared_from_this<PrinterFileSystem>, BambuLib
|
||||
{
|
||||
static const int CTRL_TYPE = 0x3001;
|
||||
|
||||
enum {
|
||||
LIST_INFO = 0x0001,
|
||||
SUB_FILE = 0x0002,
|
||||
FILE_DEL = 0x0003,
|
||||
FILE_DOWNLOAD = 0X0004,
|
||||
NOTIFY_FIRST = 0x0100,
|
||||
LIST_CHANGE_NOTIFY = 0x0100,
|
||||
LIST_RESYNC_NOTIFY = 0x0101,
|
||||
TASK_CANCEL = 0x1000
|
||||
LIST_INFO = 0x0001,
|
||||
SUB_FILE = 0x0002,
|
||||
FILE_DEL = 0x0003,
|
||||
FILE_DOWNLOAD = 0x0004,
|
||||
FILE_UPLOAD = 0x0005,
|
||||
REQUEST_MEDIA_ABILITY = 0x0007,
|
||||
NOTIFY_FIRST = 0x0100,
|
||||
LIST_CHANGE_NOTIFY = 0x0100,
|
||||
LIST_RESYNC_NOTIFY = 0x0101,
|
||||
TASK_CANCEL = 0x1000
|
||||
};
|
||||
|
||||
public:
|
||||
enum {
|
||||
SUCCESS = 0,
|
||||
CONTINUE = 1,
|
||||
ERROR_JSON = 2,
|
||||
ERROR_PIPE = 3,
|
||||
ERROR_CANCEL = 4,
|
||||
ERROR_RES_BUSY = 5,
|
||||
|
||||
FILE_NO_EXIST = 10,
|
||||
FILE_NAME_INVALID = 11,
|
||||
FILE_SIZE_ERR = 12,
|
||||
FILE_OPEN_ERR = 13,
|
||||
FILE_READ_WRITE_ERR = 14,
|
||||
FILE_CHECK_ERR = 15,
|
||||
FILE_TYPE_ERR = 16,
|
||||
STORAGE_UNAVAILABLE = 17,
|
||||
SUCCESS = 0,
|
||||
CONTINUE = 1,
|
||||
ERROR_JSON = 2,
|
||||
ERROR_PIPE = 3,
|
||||
ERROR_CANCEL = 4,
|
||||
ERROR_RES_BUSY = 5,
|
||||
ERROR_TIME_OUT = 6,
|
||||
FILE_NO_EXIST = 10,
|
||||
FILE_NAME_INVALID = 11,
|
||||
FILE_SIZE_ERR = 12,
|
||||
FILE_OPEN_ERR = 13,
|
||||
FILE_READ_WRITE_ERR = 14,
|
||||
FILE_CHECK_ERR = 15,
|
||||
FILE_TYPE_ERR = 16,
|
||||
STORAGE_UNAVAILABLE = 17,
|
||||
API_VERSION_UNSUPPORT = 18,
|
||||
FILE_EXIST = 19,
|
||||
STORAGE_SPACE_NOT_ENOUGH = 20,
|
||||
FILE_CREATE_ERR = 21,
|
||||
FILE_WRITE_ERR = 22,
|
||||
MD5_COMPARE_ERR = 23,
|
||||
FILE_RENAME_ERR = 24,
|
||||
SEND_ERR = 25,
|
||||
};
|
||||
|
||||
|
||||
@@ -90,12 +104,28 @@ public:
|
||||
template<typename T> using Callback = std::function<void(int, T)>;
|
||||
|
||||
enum Flags {
|
||||
FF_SELECT = 1,
|
||||
FF_THUMNAIL = 2, // Thumbnail ready
|
||||
FF_DOWNLOAD = 4, // Request download
|
||||
FF_DELETED = 8, // Request delete
|
||||
FF_FETCH_MODEL = 16,// Request model
|
||||
FF_THUMNAIL_RETRY = 0x100, // Thumbnail need retry
|
||||
FF_SELECT = 1,
|
||||
FF_THUMNAIL = 2, // Thumbnail ready
|
||||
FF_DOWNLOAD = 4, // Request download
|
||||
FF_DELETED = 8, // Request delete
|
||||
FF_FETCH_MODEL = 16, // Request model
|
||||
FF_UPLOADING = 1 << 5, // File uploading
|
||||
FF_UPLOADDONE = 1 << 6, // File upload done
|
||||
FF_UPLOADCANCEL = 1 << 7, // File upload cancel
|
||||
FF_THUMNAIL_RETRY = 0x100, // Thumbnail need retry
|
||||
};
|
||||
|
||||
enum UploadStatus
|
||||
{
|
||||
Uploading = 1 << 0,
|
||||
UploadDone = 1 << 1,
|
||||
UploadCancel = 1 << 2,
|
||||
};
|
||||
|
||||
enum RequestMediaAbilityStatus
|
||||
{
|
||||
S_SUCCESS,
|
||||
S_FAILED
|
||||
};
|
||||
|
||||
struct Progress
|
||||
@@ -106,6 +136,7 @@ public:
|
||||
};
|
||||
|
||||
struct Download;
|
||||
struct Upload;
|
||||
|
||||
struct File
|
||||
{
|
||||
@@ -128,9 +159,23 @@ public:
|
||||
friend bool operator<(File const & l, File const & r) { return l.time > r.time; }
|
||||
};
|
||||
|
||||
struct UploadFile
|
||||
{
|
||||
std::string name;
|
||||
std::string path;
|
||||
std::string select_storage;
|
||||
int flags{0};
|
||||
boost::uint32_t size{0};
|
||||
boost::uint32_t chunk_size{0}; // KB
|
||||
std::unique_ptr<Upload> upload;
|
||||
|
||||
bool IsUploading() const { return flags & FF_UPLOADING; }
|
||||
};
|
||||
|
||||
struct Void {};
|
||||
|
||||
typedef std::vector<File> FileList;
|
||||
typedef std::vector<std::string> MediaAbilityList;
|
||||
|
||||
void ListAllFiles();
|
||||
|
||||
@@ -138,6 +183,17 @@ public:
|
||||
|
||||
void DownloadFiles(size_t index, std::string const &path);
|
||||
|
||||
void GetPickImage(int id, const std::string &local_path, const std::string &path);
|
||||
|
||||
void GetPickImages(const std::vector<std::string> &local_paths, const std::vector<std::string> &targetpaths);
|
||||
|
||||
|
||||
void DownloadRamFile(int index, const std::string &local_path, const std::string ¶m);
|
||||
|
||||
void SendExistedFile();
|
||||
|
||||
void SendConnectFail();
|
||||
|
||||
void DownloadCheckFiles(std::string const &path);
|
||||
|
||||
bool DownloadCheckFile(size_t index);
|
||||
@@ -170,6 +226,7 @@ public:
|
||||
ListSyncing,
|
||||
ListReady,
|
||||
Failed,
|
||||
Reconnecting,
|
||||
};
|
||||
|
||||
Status GetStatus() const { return m_status; }
|
||||
@@ -185,6 +242,16 @@ public:
|
||||
|
||||
void Stop(bool quit = false);
|
||||
|
||||
boost::uint32_t RequestMediaAbility(int api_version);
|
||||
|
||||
void RequestUploadFile();
|
||||
|
||||
MediaAbilityList GetMediaAbilityList() const;
|
||||
|
||||
void SetUploadFile(const std::string& path, const std::string& name, const std::string& select_storage);
|
||||
|
||||
void CancelUploadTask(bool send_cancel_req = true);
|
||||
|
||||
private:
|
||||
void BuildGroups();
|
||||
|
||||
@@ -217,13 +284,14 @@ private:
|
||||
|
||||
typedef std::function<int(int, json const &resp, unsigned char const *data)> callback_t2;
|
||||
|
||||
template <typename T>
|
||||
boost::uint32_t SendRequest(int type, json const& req, Translator<T> const& translator, Callback<T> const& callback)
|
||||
typedef std::function<int(std::string &msg)> callback_t3;
|
||||
|
||||
template<typename T> boost::uint32_t SendRequest(int type, json const &req, Translator<T> const &translator, Callback<T> const &callback, const std::string ¶m = "")
|
||||
{
|
||||
auto c = [translator, callback, this](int result, json const &resp, unsigned char const *data) -> int
|
||||
{
|
||||
T t;
|
||||
if (result == 0 || result == CONTINUE) {
|
||||
if (result == 0 || result == CONTINUE || result == FILE_EXIST) {
|
||||
try {
|
||||
int n = (translator != nullptr) ? translator(resp, t, data) : 0;
|
||||
result = n == 0 ? result : n;
|
||||
@@ -235,7 +303,7 @@ private:
|
||||
PostCallback<T>(callback, result, t);
|
||||
return result;
|
||||
};
|
||||
return SendRequest(type, req, c);
|
||||
return SendRequest(type, req, c, param);
|
||||
}
|
||||
|
||||
template<typename T> using Applier = std::function<void(T const &)>;
|
||||
@@ -265,7 +333,7 @@ private:
|
||||
InstallNotify(type, c);
|
||||
}
|
||||
|
||||
boost::uint32_t SendRequest(int type, json const &req, callback_t2 const &callback);
|
||||
boost::uint32_t SendRequest(int type, json const &req, callback_t2 const &callback, const std::string ¶m = "");
|
||||
|
||||
void InstallNotify(int type, callback_t2 const &callback);
|
||||
|
||||
@@ -289,6 +357,8 @@ private:
|
||||
|
||||
void PostCallback(std::function<void(void)> const & callback);
|
||||
|
||||
int UploadFileTask(std::shared_ptr<UploadFile> upload_file, boost::uint64_t seq, std::string &msg);
|
||||
|
||||
protected:
|
||||
FileType m_file_type = F_INVALID_TYPE;
|
||||
std::string m_file_storage;
|
||||
@@ -298,6 +368,7 @@ protected:
|
||||
std::vector<size_t> m_group_year;
|
||||
std::vector<size_t> m_group_month;
|
||||
std::vector<int> m_group_flags;
|
||||
std::shared_ptr<UploadFile> m_upload_file;
|
||||
|
||||
private:
|
||||
size_t m_select_count = 0;
|
||||
@@ -305,6 +376,8 @@ private:
|
||||
size_t m_lock_end = 0;
|
||||
int m_task_flags = 0;
|
||||
|
||||
std::vector<bool> m_download_states;
|
||||
|
||||
private:
|
||||
struct Session
|
||||
{
|
||||
@@ -315,6 +388,7 @@ private:
|
||||
boost::uint32_t m_sequence = 0;
|
||||
boost::uint32_t m_download_seq = 0;
|
||||
boost::uint32_t m_fetch_model_seq = 0;
|
||||
boost::uint32_t m_upload_seq = 0;
|
||||
std::deque<std::string> m_messages;
|
||||
std::deque<callback_t2> m_callbacks;
|
||||
std::deque<callback_t2> m_notifies;
|
||||
@@ -324,6 +398,9 @@ private:
|
||||
boost::thread m_recv_thread;
|
||||
Status m_status;
|
||||
int m_last_error = 0;
|
||||
|
||||
MediaAbilityList m_media_ability_list;
|
||||
std::map<boost::uint32_t, callback_t3> m_produce_message_cb_map;
|
||||
};
|
||||
|
||||
#endif // !slic3r_GUI_PrinterFileSystem_h_
|
||||
|
||||
@@ -88,7 +88,9 @@ enum PrintDialogStatus {
|
||||
PrintStatusNotSupportedPrintAll,
|
||||
PrintStatusBlankPlate,
|
||||
PrintStatusUnsupportedPrinter,
|
||||
PrintStatusTimelapseWarning
|
||||
PrintStatusTimelapseWarning,
|
||||
PrintStatusPublicInitFailed,
|
||||
PrintStatusPublicUploadFiled
|
||||
};
|
||||
|
||||
class Material
|
||||
|
||||
@@ -29,12 +29,13 @@ namespace GUI {
|
||||
#define LIST_REFRESH_INTERVAL 200
|
||||
#define MACHINE_LIST_REFRESH_INTERVAL 2000
|
||||
|
||||
constexpr int timeout_period = 15000; // ms
|
||||
|
||||
wxDEFINE_EVENT(EVT_UPDATE_USER_MACHINE_LIST, wxCommandEvent);
|
||||
wxDEFINE_EVENT(EVT_PRINT_JOB_CANCEL, wxCommandEvent);
|
||||
wxDEFINE_EVENT(EVT_SEND_JOB_SUCCESS, wxCommandEvent);
|
||||
wxDEFINE_EVENT(EVT_CLEAR_IPADDRESS, wxCommandEvent);
|
||||
|
||||
|
||||
void SendToPrinterDialog::stripWhiteSpace(std::string& str)
|
||||
{
|
||||
if (str == "") { return; }
|
||||
@@ -260,6 +261,14 @@ SendToPrinterDialog::SendToPrinterDialog(Plater *plater)
|
||||
m_button_refresh->Bind(wxEVT_BUTTON, &SendToPrinterDialog::on_refresh, this);
|
||||
m_sizer_printer->Add(m_button_refresh, 0, wxALL | wxLEFT, FromDIP(5));
|
||||
|
||||
|
||||
/*select storage*/
|
||||
m_storage_panel = new wxPanel(this);
|
||||
m_storage_panel->SetBackgroundColour(*wxWHITE);
|
||||
m_storage_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_storage_panel->SetSizer(m_storage_sizer);
|
||||
m_storage_panel->Layout();
|
||||
|
||||
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->SetForegroundColour(*wxBLACK);
|
||||
@@ -517,6 +526,7 @@ SendToPrinterDialog::SendToPrinterDialog(Plater *plater)
|
||||
m_sizer_main->Add(m_line_materia, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30));
|
||||
m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(12));
|
||||
m_sizer_main->Add(m_sizer_printer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30));
|
||||
m_sizer_main->Add(m_storage_panel, 0, wxALIGN_CENTER|wxTOP, FromDIP(8));
|
||||
m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(11));
|
||||
m_sizer_main->Add(m_statictext_printer_msg, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
m_sizer_main->Add(0, 1, 0, wxTOP, FromDIP(22));
|
||||
@@ -538,6 +548,49 @@ SendToPrinterDialog::SendToPrinterDialog(Plater *plater)
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
}
|
||||
|
||||
std::string SendToPrinterDialog::get_storage_selected()
|
||||
{
|
||||
for (const auto& radio : m_storage_radioBox) {
|
||||
if (radio->GetValue()) {
|
||||
return radio->GetLabel().ToStdString();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void SendToPrinterDialog::update_storage_list(std::vector<std::string> storages)
|
||||
{
|
||||
m_storage_radioBox.clear();
|
||||
m_storage_panel->DestroyChildren();
|
||||
|
||||
for (int i=0; i < storages.size(); i++) {
|
||||
RadioBox* radiobox = new RadioBox(m_storage_panel);
|
||||
Label* storage_text = new Label(m_storage_panel, storages[i]);
|
||||
radiobox->SetLabel(storages[i]);
|
||||
radiobox->Bind(wxEVT_LEFT_DOWN, [this, radiobox](auto& e) {
|
||||
for (const auto& radio : m_storage_radioBox) {
|
||||
radio->SetValue(false);
|
||||
}
|
||||
radiobox->SetValue(true);
|
||||
});
|
||||
|
||||
if (m_storage_radioBox.size() > 0) {
|
||||
m_storage_sizer->Add(0, 0, 0, wxEXPAND|wxLEFT, FromDIP(6));
|
||||
auto radio = m_storage_radioBox.front();
|
||||
radio->SetValue(true);
|
||||
}
|
||||
|
||||
m_storage_sizer->Add(radiobox, 0, wxALIGN_CENTER, 0);
|
||||
m_storage_sizer->Add(0, 0, 0, wxEXPAND|wxLEFT, FromDIP(6));
|
||||
m_storage_sizer->Add(storage_text, 0, wxALIGN_CENTER,0);
|
||||
m_storage_radioBox.push_back(radiobox);
|
||||
}
|
||||
|
||||
m_storage_panel->Layout();
|
||||
m_storage_panel->Fit();
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
void SendToPrinterDialog::update_print_error_info(int code, std::string msg, std::string extra)
|
||||
{
|
||||
m_print_error_code = code;
|
||||
@@ -669,6 +722,15 @@ void SendToPrinterDialog::init_timer()
|
||||
void SendToPrinterDialog::on_cancel(wxCloseEvent &event)
|
||||
{
|
||||
m_worker->cancel_all();
|
||||
if (m_file_sys) {
|
||||
m_file_sys->CancelUploadTask();
|
||||
|
||||
if (m_task_timer && m_task_timer->IsRunning()) {
|
||||
m_task_timer->Stop();
|
||||
m_task_timer.reset();
|
||||
}
|
||||
}
|
||||
|
||||
this->EndModal(wxID_CANCEL);
|
||||
}
|
||||
|
||||
@@ -706,6 +768,9 @@ void SendToPrinterDialog::on_ok(wxCommandEvent &event)
|
||||
m_status_bar->set_cancel_callback_fina([this]() {
|
||||
BOOST_LOG_TRIVIAL(info) << "print_job: enter canceled";
|
||||
m_worker->cancel_all();
|
||||
if (m_file_sys) {
|
||||
m_file_sys->CancelUploadTask();
|
||||
}
|
||||
m_is_canceled = true;
|
||||
wxCommandEvent* event = new wxCommandEvent(EVT_PRINT_JOB_CANCEL);
|
||||
wxQueueEvent(this, event);
|
||||
@@ -760,42 +825,70 @@ void SendToPrinterDialog::on_ok(wxCommandEvent &event)
|
||||
fs::path default_output_file_path = boost::filesystem::path(default_output_file.c_str());
|
||||
file_name = default_output_file_path.filename().string();
|
||||
}*/
|
||||
if (obj_->is_lan_mode_printer()) {
|
||||
update_print_status_msg(wxEmptyString, false, false);
|
||||
if (m_file_sys) {
|
||||
PrintPrepareData print_data;
|
||||
m_plater->get_print_job_data(&print_data);
|
||||
std::string project_name = m_current_project_name.utf8_string() + ".3mf";
|
||||
std::string _3mf_path = print_data._3mf_path.string();
|
||||
m_file_sys->SetUploadFile(_3mf_path, project_name, get_storage_selected());
|
||||
m_file_sys->RequestUploadFile();
|
||||
|
||||
// time out
|
||||
if (m_task_timer && m_task_timer->IsRunning())
|
||||
m_task_timer->Stop();
|
||||
|
||||
m_task_timer.reset(new wxTimer());
|
||||
m_task_timer->SetOwner(this);
|
||||
|
||||
this->Bind(
|
||||
wxEVT_TIMER,
|
||||
[this, wfs = boost::weak_ptr(m_file_sys)](auto e) {
|
||||
show_status(PrintDialogStatus::PrintStatusPublicUploadFiled);
|
||||
|
||||
boost::shared_ptr fs(wfs.lock());
|
||||
if (!fs) return;
|
||||
fs->CancelUploadTask(false);
|
||||
update_print_status_msg(_L("Upload file timeout, please check if the firmware version supports it."), false, true);
|
||||
},
|
||||
m_task_timer->GetId());
|
||||
m_task_timer->StartOnce(timeout_period);
|
||||
}
|
||||
} else {
|
||||
auto m_send_job = std::make_unique<SendJob>(m_printer_last_select);
|
||||
m_send_job->m_dev_ip = obj_->dev_ip;
|
||||
m_send_job->m_access_code = obj_->get_access_code();
|
||||
m_send_job->m_dev_ip = obj_->dev_ip;
|
||||
m_send_job->m_access_code = obj_->get_access_code();
|
||||
|
||||
|
||||
#if !BBL_RELEASE_TO_PUBLIC
|
||||
m_send_job->m_local_use_ssl_for_ftp = wxGetApp().app_config->get("enable_ssl_for_ftp") == "true" ? true : false;
|
||||
m_send_job->m_local_use_ssl_for_mqtt = wxGetApp().app_config->get("enable_ssl_for_mqtt") == "true" ? true : false;
|
||||
m_send_job->m_local_use_ssl_for_ftp = wxGetApp().app_config->get("enable_ssl_for_ftp") == "true" ? true : false;
|
||||
m_send_job->m_local_use_ssl_for_mqtt = wxGetApp().app_config->get("enable_ssl_for_mqtt") == "true" ? true : false;
|
||||
#else
|
||||
m_send_job->m_local_use_ssl_for_ftp = obj_->local_use_ssl_for_ftp;
|
||||
m_send_job->m_local_use_ssl_for_mqtt = obj_->local_use_ssl_for_mqtt;
|
||||
m_send_job->m_local_use_ssl_for_ftp = obj_->local_use_ssl_for_ftp;
|
||||
m_send_job->m_local_use_ssl_for_mqtt = obj_->local_use_ssl_for_mqtt;
|
||||
#endif
|
||||
|
||||
m_send_job->connection_type = obj_->connection_type();
|
||||
m_send_job->cloud_print_only = true;
|
||||
m_send_job->has_sdcard = obj_->get_sdcard_state() == MachineObject::SdcardState::HAS_SDCARD_NORMAL;
|
||||
m_send_job->set_project_name(m_current_project_name.utf8_string());
|
||||
m_send_job->connection_type = obj_->connection_type();
|
||||
m_send_job->cloud_print_only = true;
|
||||
m_send_job->has_sdcard = obj_->get_sdcard_state() == MachineObject::SdcardState::HAS_SDCARD_NORMAL;
|
||||
m_send_job->set_project_name(m_current_project_name.utf8_string());
|
||||
|
||||
enable_prepare_mode = false;
|
||||
enable_prepare_mode = false;
|
||||
|
||||
m_send_job->on_check_ip_address_fail([this](int result) {
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_CLEAR_IPADDRESS);
|
||||
wxQueueEvent(this, evt);
|
||||
wxGetApp().show_ip_address_enter_dialog();
|
||||
});
|
||||
m_send_job->on_check_ip_address_fail([this](int result) {
|
||||
wxCommandEvent *evt = new wxCommandEvent(EVT_CLEAR_IPADDRESS);
|
||||
wxQueueEvent(this, evt);
|
||||
wxGetApp().show_ip_address_enter_dialog();
|
||||
});
|
||||
|
||||
if (obj_->is_lan_mode_printer()) {
|
||||
m_send_job->set_check_mode();
|
||||
m_send_job->check_and_continue();
|
||||
if (obj_->is_lan_mode_printer()) {
|
||||
m_send_job->set_check_mode();
|
||||
m_send_job->check_and_continue();
|
||||
}
|
||||
|
||||
replace_job(*m_worker, std::move(m_send_job));
|
||||
}
|
||||
|
||||
replace_job(*m_worker, std::move(m_send_job));
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "send_job: send print job";
|
||||
}
|
||||
|
||||
@@ -837,9 +930,10 @@ void SendToPrinterDialog::update_user_machine_list()
|
||||
void SendToPrinterDialog::on_refresh(wxCommandEvent &event)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "m_printer_last_select: on_refresh";
|
||||
show_status(PrintDialogStatus::PrintStatusRefreshingMachineList);
|
||||
|
||||
update_user_machine_list();
|
||||
/* show_status(PrintDialogStatus::PrintStatusRefreshingMachineList);
|
||||
update_user_machine_list();*/
|
||||
/*todo refresh*/
|
||||
if (m_file_sys) { m_file_sys->Retry(); }
|
||||
}
|
||||
|
||||
void SendToPrinterDialog::on_print_job_cancel(wxCommandEvent &evt)
|
||||
@@ -991,8 +1085,11 @@ void SendToPrinterDialog::on_selection_changed(wxCommandEvent &event)
|
||||
obj->command_request_push_all();
|
||||
if (!dev->get_selected_machine()) {
|
||||
dev->set_selected_machine(m_printer_last_select, true);
|
||||
if (m_file_sys) m_file_sys.reset();
|
||||
}else if (dev->get_selected_machine()->dev_id != m_printer_last_select) {
|
||||
update_storage_list(std::vector<std::string>());
|
||||
dev->set_selected_machine(m_printer_last_select, true);
|
||||
if (m_file_sys) m_file_sys.reset();
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1076,8 +1173,137 @@ void SendToPrinterDialog::update_show_status()
|
||||
}
|
||||
|
||||
if (!m_is_in_sending_mode) {
|
||||
show_status(PrintDialogStatus::PrintStatusReadingFinished);
|
||||
return;
|
||||
if (obj_->connection_type() == "lan") {
|
||||
show_status(PrintDialogStatus::PrintStatusReadingFinished);
|
||||
return;
|
||||
} else if (obj_->connection_type() == "cloud") {
|
||||
Enable(obj_ && obj_->is_connected() && obj_->m_push_count > 0);
|
||||
std::string dev_id = obj_->dev_ip;
|
||||
if (m_file_sys) {
|
||||
if (dev_id == m_device_select) {
|
||||
if ((m_waiting_enable && IsEnabled()) || (m_waiting_support && obj_->file_remote))
|
||||
m_file_sys->Retry();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_device_select.swap(dev_id);
|
||||
m_file_sys = boost::make_shared<PrinterFileSystem>();
|
||||
m_file_sys->Attached();
|
||||
|
||||
m_file_sys->Bind(EVT_STATUS_CHANGED, [this, wfs = boost::weak_ptr(m_file_sys)](auto e) {
|
||||
e.Skip();
|
||||
boost::shared_ptr fs(wfs.lock());
|
||||
if (!fs) return;
|
||||
|
||||
wxString msg;
|
||||
int status = e.GetInt();
|
||||
int extra = e.GetExtraLong();
|
||||
switch (status) {
|
||||
case PrinterFileSystem::Initializing:
|
||||
case PrinterFileSystem::Connecting: show_status(PrintDialogStatus::PrintStatusReading); break;
|
||||
case PrinterFileSystem::ListSyncing: {
|
||||
show_status(PrintDialogStatus::PrintStatusReading);
|
||||
boost::uint32_t seq = fs->RequestMediaAbility(3);
|
||||
|
||||
if (m_task_timer && m_task_timer->IsRunning())
|
||||
m_task_timer->Stop();
|
||||
|
||||
m_task_timer.reset(new wxTimer());
|
||||
m_task_timer->SetOwner(this);
|
||||
|
||||
this->Bind(wxEVT_TIMER, [this, wfs_1 = boost::weak_ptr(fs), seq](auto e) {
|
||||
show_status(PrintDialogStatus::PrintStatusPublicUploadFiled);
|
||||
boost::shared_ptr fs_1(wfs_1.lock());
|
||||
if (!fs_1) return;
|
||||
fs_1->CancelUploadTask(false);
|
||||
update_print_status_msg(_L("Media capability acquisition timeout, please check if the firmware version supports it."), false, true);
|
||||
}, m_task_timer->GetId());
|
||||
m_task_timer->StartOnce(timeout_period);
|
||||
|
||||
break;
|
||||
}
|
||||
case PrinterFileSystem::Failed: msg = _L("Please check the network and try again, You can restart or update the printer if the issue persists."); break;
|
||||
}
|
||||
|
||||
if (!msg.empty()) {
|
||||
show_status(PrintDialogStatus::PrintStatusPublicInitFailed);
|
||||
update_print_status_msg(msg, false, true);
|
||||
}
|
||||
|
||||
if (e.GetInt() == PrinterFileSystem::Initializing) {
|
||||
CallAfter([=] {
|
||||
boost::shared_ptr fs(wfs.lock());
|
||||
if (!fs) return;
|
||||
fetchUrl(boost::weak_ptr(fs));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
m_file_sys->Bind(EVT_MEDIA_ABILITY_CHANGED, [this, wfs = boost::weak_ptr(m_file_sys)](auto e) {
|
||||
boost::shared_ptr fs(wfs.lock());
|
||||
if (!fs) return;
|
||||
|
||||
if (m_task_timer && m_task_timer->IsRunning()) {
|
||||
m_task_timer->Stop();
|
||||
m_task_timer.reset();
|
||||
}
|
||||
|
||||
m_ability_list = fs->GetMediaAbilityList();
|
||||
|
||||
if (e.GetInt() == PrinterFileSystem::RequestMediaAbilityStatus::S_SUCCESS) {
|
||||
update_storage_list(m_ability_list);
|
||||
show_status(PrintDialogStatus::PrintStatusReadingFinished);
|
||||
} else {
|
||||
show_status(PrintDialogStatus::PrintStatusPublicInitFailed);
|
||||
update_print_status_msg(e.GetString(), false, true);
|
||||
}
|
||||
});
|
||||
|
||||
m_file_sys->Bind(EVT_UPLOADING, [this, wfs = boost::weak_ptr(m_file_sys)](auto e) {
|
||||
boost::shared_ptr fs(wfs.lock());
|
||||
if (!fs) return;
|
||||
int progress = e.GetInt();
|
||||
m_status_bar->set_progress(10 + std::floor(progress * 0.9));
|
||||
|
||||
if (m_task_timer && m_task_timer->IsRunning()) m_task_timer->Stop();
|
||||
|
||||
if (progress == 99) {
|
||||
m_task_timer.reset(new wxTimer());
|
||||
m_task_timer->SetOwner(this);
|
||||
|
||||
this->Bind(
|
||||
wxEVT_TIMER,
|
||||
[this, wfs = boost::weak_ptr(m_file_sys)](auto e) {
|
||||
show_status(PrintDialogStatus::PrintStatusPublicUploadFiled);
|
||||
boost::shared_ptr fs(wfs.lock());
|
||||
if (!fs) return;
|
||||
fs->CancelUploadTask(false);
|
||||
update_print_status_msg(_L("Upload file timeout, please check if the firmware version supports it."), false, true);
|
||||
},
|
||||
m_task_timer->GetId());
|
||||
m_task_timer->StartOnce(timeout_period);
|
||||
}
|
||||
});
|
||||
m_file_sys->Bind(EVT_UPLOAD_CHANGED, [this, wfs = boost::weak_ptr(m_file_sys)](auto e) {
|
||||
boost::shared_ptr fs(wfs.lock());
|
||||
if (!fs) return;
|
||||
|
||||
if (e.GetInt() == PrinterFileSystem::FF_UPLOADDONE) {
|
||||
show_status(PrintDialogStatus::PrintStatusReadingFinished);
|
||||
wxCommandEvent *evt = new wxCommandEvent(m_plater->get_send_finished_event());
|
||||
evt->SetString(from_u8(m_current_project_name.utf8_string()));
|
||||
wxQueueEvent(m_plater, evt);
|
||||
} else if (PrinterFileSystem::FF_UPLOADCANCEL) {
|
||||
show_status(PrintDialogStatus::PrintStatusPublicUploadFiled);
|
||||
wxString err_msg = e.GetString();
|
||||
if (err_msg.IsEmpty())
|
||||
err_msg = _u8L("Sending failed, please try again!");
|
||||
update_print_status_msg(err_msg, false, true);
|
||||
}
|
||||
});
|
||||
m_file_sys->Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1139,7 +1365,7 @@ void SendToPrinterDialog::show_status(PrintDialogStatus status, std::vector<wxSt
|
||||
if (status == PrintDialogStatus::PrintStatusInit) {
|
||||
update_print_status_msg(wxEmptyString, false, false);
|
||||
Enable_Send_Button(false);
|
||||
Enable_Refresh_Button(true);
|
||||
Enable_Refresh_Button(false);
|
||||
}
|
||||
else if (status == PrintDialogStatus::PrintStatusNoUserLogin) {
|
||||
wxString msg_text = _L("No login account, only printers in LAN mode are displayed");
|
||||
@@ -1217,6 +1443,13 @@ void SendToPrinterDialog::show_status(PrintDialogStatus status, std::vector<wxSt
|
||||
update_print_status_msg(msg_text, true, true);
|
||||
Enable_Send_Button(false);
|
||||
Enable_Refresh_Button(true);
|
||||
} else if (status == PrintDialogStatus::PrintStatusPublicInitFailed) {
|
||||
Enable_Send_Button(false);
|
||||
Enable_Refresh_Button(true);
|
||||
} else if (status == PrintDialogStatus::PrintStatusPublicUploadFiled) {
|
||||
prepare_mode();
|
||||
Enable_Send_Button(true);
|
||||
Enable_Refresh_Button(true);
|
||||
}
|
||||
else {
|
||||
Enable_Send_Button(true);
|
||||
@@ -1369,6 +1602,7 @@ bool SendToPrinterDialog::Show(bool show)
|
||||
|
||||
// set default value when show this dialog
|
||||
if (show) {
|
||||
update_storage_list(std::vector<std::string>());
|
||||
wxGetApp().reset_to_active();
|
||||
set_default();
|
||||
update_user_machine_list();
|
||||
@@ -1383,12 +1617,84 @@ bool SendToPrinterDialog::Show(bool show)
|
||||
Layout();
|
||||
Fit();
|
||||
if (show) { CenterOnParent(); }
|
||||
|
||||
if (m_file_sys) {
|
||||
show ? m_file_sys->Start() : m_file_sys->Stop();
|
||||
}
|
||||
|
||||
return DPIDialog::Show(show);
|
||||
}
|
||||
|
||||
extern wxString hide_passwd(wxString url, std::vector<wxString> const &passwords);
|
||||
extern void refresh_agora_url(char const *device, char const *dev_ver, char const *channel, void *context, void (*callback)(void *context, char const *url));
|
||||
|
||||
void SendToPrinterDialog::fetchUrl(boost::weak_ptr<PrinterFileSystem> wfs)
|
||||
{
|
||||
boost::shared_ptr fs(wfs.lock());
|
||||
if (!fs) return;
|
||||
|
||||
if (!IsEnabled()) {
|
||||
m_waiting_enable = true;
|
||||
fs->SetUrl("0");
|
||||
return;
|
||||
}
|
||||
|
||||
m_waiting_enable = false;
|
||||
DeviceManager *dm = GUI::wxGetApp().getDeviceManager();
|
||||
MachineObject *obj = dm->get_selected_machine();
|
||||
|
||||
std::string dev_ver = obj->get_ota_version();
|
||||
std::string dev_id = obj->dev_id;
|
||||
int remote_proto = obj->file_remote;
|
||||
if (!remote_proto) {
|
||||
m_waiting_support = true;
|
||||
fs->SetUrl("0");
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj->is_camera_busy_off()) {
|
||||
fs->SetUrl("0");
|
||||
return;
|
||||
}
|
||||
|
||||
m_waiting_support = false;
|
||||
NetworkAgent *agent = wxGetApp().getAgent();
|
||||
std::string agent_version = agent ? agent->get_version() : "";
|
||||
|
||||
if (agent) {
|
||||
std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""};
|
||||
agent->get_camera_url(obj->dev_id + "|" + dev_ver + "|" + protocols[remote_proto],
|
||||
[this, wfs, m = dev_id, v = agent->get_version(), dv = dev_ver](std::string url) {
|
||||
if (boost::algorithm::starts_with(url, "bambu:///")) {
|
||||
url += "&device=" + m;
|
||||
url += "&net_ver=" + v;
|
||||
url += "&dev_ver=" + dv;
|
||||
url += "&refresh_url=" + boost::lexical_cast<std::string>(&refresh_agora_url);
|
||||
url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid");
|
||||
url += "&cli_ver=" + std::string(SLIC3R_VERSION);
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << "SendToPrinter::fetchUrl: camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="});
|
||||
std::cout << "SendToPrinter::fetchUrl: camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="});
|
||||
CallAfter([=] {
|
||||
boost::shared_ptr fs(wfs.lock());
|
||||
if (!fs) return;
|
||||
if (boost::algorithm::starts_with(url, "bambu:///")) {
|
||||
fs->SetUrl(url);
|
||||
} else {
|
||||
fs->SetUrl("3");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
SendToPrinterDialog::~SendToPrinterDialog()
|
||||
{
|
||||
delete m_refresh_timer;
|
||||
if (m_task_timer && m_task_timer->IsRunning())
|
||||
m_task_timer->Stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "Widgets/ScrolledWindow.hpp"
|
||||
#include <wx/simplebook.h>
|
||||
#include <wx/hashmap.h>
|
||||
#include "Printer/PrinterFileSystem.h"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
@@ -62,6 +63,7 @@ private:
|
||||
std::string m_print_error_extra;
|
||||
std::string m_print_info;
|
||||
std::string m_printer_last_select;
|
||||
std::string m_device_select;
|
||||
wxString m_current_project_name;
|
||||
|
||||
TextInput* m_rename_input{ nullptr };
|
||||
@@ -83,6 +85,8 @@ private:
|
||||
wxPanel* m_panel_image{ nullptr };
|
||||
wxPanel* m_rename_normal_panel{ nullptr };
|
||||
wxPanel* m_line_materia{ nullptr };
|
||||
wxBoxSizer* m_storage_sizer{ nullptr };
|
||||
wxPanel* m_storage_panel{ nullptr };
|
||||
wxSimplebook* m_simplebook{ nullptr };
|
||||
wxStaticText* m_statictext_finish{ nullptr };
|
||||
wxStaticText* m_stext_sending{ nullptr };
|
||||
@@ -111,10 +115,17 @@ private:
|
||||
wxColour m_colour_def_color{ wxColour(255, 255, 255) };
|
||||
wxColour m_colour_bold_color{ wxColour(38, 46, 48) };
|
||||
wxTimer* m_refresh_timer{ nullptr };
|
||||
std::unique_ptr<wxTimer> m_task_timer{ nullptr };
|
||||
std::shared_ptr<BBLStatusBarSend> m_status_bar;
|
||||
std::unique_ptr<Worker> m_worker;
|
||||
wxScrolledWindow* m_sw_print_failed_info{nullptr};
|
||||
std::shared_ptr<int> m_token = std::make_shared<int>(0);
|
||||
std::vector<RadioBox*> m_storage_radioBox;
|
||||
|
||||
bool m_waiting_support{ false };
|
||||
bool m_waiting_enable{ false };
|
||||
boost::shared_ptr<PrinterFileSystem> m_file_sys;
|
||||
std::vector<std::string> m_ability_list;
|
||||
|
||||
public:
|
||||
SendToPrinterDialog(Plater* plater = nullptr);
|
||||
@@ -153,8 +164,13 @@ public:
|
||||
void show_print_failed_info(bool show, int code = 0, wxString description = wxEmptyString, wxString extra = wxEmptyString);
|
||||
void update_print_error_info(int code, std::string msg, std::string extra);
|
||||
void on_change_color_mode() { wxGetApp().UpdateDlgDarkUI(this); }
|
||||
void update_storage_list(std::vector<std::string> storages);
|
||||
std::string get_storage_selected();
|
||||
|
||||
wxString format_text(wxString& m_msg);
|
||||
std::vector<std::string> sort_string(std::vector<std::string> strArray);
|
||||
|
||||
void fetchUrl(boost::weak_ptr<PrinterFileSystem> wfs);
|
||||
};
|
||||
|
||||
wxDECLARE_EVENT(EVT_CLEAR_IPADDRESS, wxCommandEvent);
|
||||
|
||||
697
src/slic3r/GUI/SkipPartCanvas.cpp
Normal file
697
src/slic3r/GUI/SkipPartCanvas.cpp
Normal file
@@ -0,0 +1,697 @@
|
||||
#include <GL/glew.h>
|
||||
#include "SkipPartCanvas.hpp"
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <expat.h>
|
||||
#include <earcut/earcut.hpp>
|
||||
#include <libslic3r/Color.hpp>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
|
||||
wxDEFINE_EVENT(EVT_ZOOM_PERCENT, wxCommandEvent);
|
||||
wxDEFINE_EVENT(EVT_CANVAS_PART, wxCommandEvent);
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
SkipPartCanvas::SkipPartCanvas(wxWindow *parent, const wxGLAttributes& dispAttrs)
|
||||
: wxGLCanvas(parent, dispAttrs) {
|
||||
context_ = new wxGLContext(this);
|
||||
this->Bind(wxEVT_PAINT, &SkipPartCanvas::OnPaint, this);
|
||||
this->Bind(wxEVT_MOUSEWHEEL, &SkipPartCanvas::OnMouseWheel, this);
|
||||
this->Bind(wxEVT_LEFT_DOWN, &SkipPartCanvas::OnMouseLeftDown, this);
|
||||
this->Bind(wxEVT_LEFT_DCLICK, &SkipPartCanvas::OnMouseLeftDown, this);
|
||||
this->Bind(wxEVT_LEFT_UP, &SkipPartCanvas::OnMouseLeftUp, this);
|
||||
this->Bind(wxEVT_RIGHT_DOWN, &SkipPartCanvas::OnMouseRightDown, this);
|
||||
this->Bind(wxEVT_RIGHT_UP, &SkipPartCanvas::OnMouseRightUp, this);
|
||||
this->Bind(wxEVT_SIZE, &SkipPartCanvas::OnSize, this);
|
||||
this->Bind(wxEVT_MOTION, &SkipPartCanvas::OnMouseMotion, this);
|
||||
}
|
||||
|
||||
void SkipPartCanvas::LoadPickImage(const std::string & path)
|
||||
{
|
||||
if(!std::filesystem::exists(path)) return;
|
||||
|
||||
auto ParseShapeId = [](cv::Mat image, const std::vector<std::vector<cv::Point>> &contours, const std::vector<cv::Vec4i> &hierarchy, int root_idx) -> uint32_t {
|
||||
cv::Mat mask = cv::Mat::zeros(image.size(), CV_8UC1);
|
||||
|
||||
cv::drawContours(mask, contours, root_idx, 255, cv::FILLED);
|
||||
|
||||
int child = hierarchy[root_idx][2];
|
||||
while (child != -1) {
|
||||
cv::drawContours(mask, contours, child, 0, cv::FILLED);
|
||||
child = hierarchy[child][0];
|
||||
}
|
||||
std::vector<cv::Vec3b> pixels;
|
||||
for (int y = 0; y < image.rows; ++y) {
|
||||
for (int x = 0; x < image.cols; ++x) {
|
||||
if (mask.at<uchar>(y, x)) { pixels.push_back(image.at<cv::Vec3b>(y, x)); }
|
||||
}
|
||||
}
|
||||
|
||||
std::map<cv::Vec3b, int, std::function<bool(const cv::Vec3b &, const cv::Vec3b &)>> colorCount(
|
||||
[](const cv::Vec3b &a, const cv::Vec3b &b) { return std::lexicographical_compare(a.val, a.val + 3, b.val, b.val + 3); });
|
||||
|
||||
for (auto &c : pixels) colorCount[c]++;
|
||||
|
||||
cv::Vec3b main_color;
|
||||
int max_count = 0;
|
||||
int total_count = 0;
|
||||
for (const auto &kv : colorCount) {
|
||||
if (kv.second > max_count) {
|
||||
max_count = kv.second;
|
||||
main_color = kv.first;
|
||||
}
|
||||
total_count += kv.second;
|
||||
}
|
||||
|
||||
SkipIdHelper helper{main_color[2], main_color[1], main_color[0]};
|
||||
helper.reverse();
|
||||
return (max_count * 2 > total_count) ? helper.value : 0;
|
||||
};
|
||||
|
||||
parts_state_.clear();
|
||||
parts_triangles_.clear();
|
||||
pick_parts_.clear();
|
||||
int preffered_w{FromDIP(400)}, preffered_h{FromDIP(400)};
|
||||
cv::Mat src_image = cv::imread(path, cv::IMREAD_UNCHANGED);
|
||||
cv::cvtColor(src_image, src_image, cv::COLOR_BGRA2BGR); // remove alpha
|
||||
float zoom_x{static_cast<float>(preffered_w) / src_image.cols};
|
||||
float zoom_y{static_cast<float>(preffered_h) / src_image.rows};
|
||||
float image_scale{0};
|
||||
if (abs(zoom_x - 1) > abs(zoom_y - 1))
|
||||
image_scale = zoom_x;
|
||||
else
|
||||
image_scale = zoom_y;
|
||||
image_view_scale_ = 1 / image_scale;
|
||||
pick_image_ = src_image;
|
||||
std::vector<cv::Mat> channels;
|
||||
cv::Mat gray; // convert to gray
|
||||
cv::cvtColor(pick_image_, gray, cv::COLOR_BGR2GRAY);
|
||||
cv::Mat mask; // convery to binary
|
||||
cv::threshold(gray, mask, 0, 255, cv::THRESH_BINARY);
|
||||
std::vector<std::vector<cv::Point>> pick_counters;
|
||||
std::vector<cv::Vec4i> hierarchy;
|
||||
cv::findContours(mask, pick_counters, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_TC89_KCOS);
|
||||
auto compute_depth = [&](int idx) {
|
||||
int depth = 0;
|
||||
while (hierarchy[idx][3] != -1) {
|
||||
depth++;
|
||||
idx = hierarchy[idx][3];
|
||||
}
|
||||
return depth;
|
||||
};
|
||||
for (int i = 0; i < pick_counters.size(); ++i) {
|
||||
int depth = compute_depth(i);
|
||||
int parent = hierarchy[i][3];
|
||||
if (parent != -1) continue;
|
||||
|
||||
auto id = ParseShapeId(pick_image_, pick_counters, hierarchy, i);
|
||||
if (id > 0) {
|
||||
std::vector<FloatPoint> flat_points;
|
||||
std::vector<std::vector<FloatPoint>> polygon;
|
||||
|
||||
// part body
|
||||
{
|
||||
polygon.emplace_back();
|
||||
for (const auto &pt : pick_counters[i]) {
|
||||
FloatPoint fp{pt.x * 1.0f, pt.y * 1.0f};
|
||||
polygon.back().push_back(fp);
|
||||
flat_points.push_back(fp);
|
||||
}
|
||||
int child = hierarchy[i][2];
|
||||
while (child != -1) {
|
||||
polygon.emplace_back();
|
||||
for (const auto &pt : pick_counters[child]) {
|
||||
FloatPoint fp{pt.x * 1.0f, pt.y * 1.0f};
|
||||
polygon.back().push_back(fp);
|
||||
flat_points.push_back(fp);
|
||||
}
|
||||
child = hierarchy[child][0];
|
||||
}
|
||||
std::vector<uint32_t> indices = mapbox::earcut<uint32_t>(polygon);
|
||||
std::vector<FloatPoint> final_counter;
|
||||
for (size_t j = 0; j < indices.size(); j += 3) {
|
||||
final_counter.push_back(flat_points[indices[j]]);
|
||||
final_counter.push_back(flat_points[indices[j + 1]]);
|
||||
final_counter.push_back(flat_points[indices[j + 2]]);
|
||||
}
|
||||
|
||||
parts_triangles_[id].emplace_back(final_counter);
|
||||
}
|
||||
// part outlines
|
||||
{
|
||||
pick_parts_[id].emplace_back(pick_counters[i]);
|
||||
int child = hierarchy[i][2];
|
||||
while (child != -1) {
|
||||
pick_parts_[id].emplace_back(pick_counters[child]);
|
||||
child = hierarchy[child][0];
|
||||
}
|
||||
}
|
||||
if (parts_state_.find(id) == parts_state_.end()) parts_state_.emplace(id, psUnCheck);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkipPartCanvas::ZoomIn(const int zoom_percent)
|
||||
{
|
||||
SetZoomPercent(zoom_percent_ + zoom_percent);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void SkipPartCanvas::ZoomOut(const int zoom_percent)
|
||||
{
|
||||
SetZoomPercent(zoom_percent_ - zoom_percent);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void SkipPartCanvas::SwitchDrag(const bool drag_on)
|
||||
{
|
||||
fixed_draging_ = drag_on;
|
||||
AutoSetCursor();
|
||||
}
|
||||
|
||||
|
||||
void SkipPartCanvas::UpdatePartsInfo(const PartsInfo& parts)
|
||||
{
|
||||
for (auto const& part : parts) {
|
||||
if (auto res = parts_state_.find(part.first); res != parts_state_.end())
|
||||
res->second = part.second;
|
||||
}
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void DrawRoundedRect(float x, float y, float width, float height, float radius, const ColorRGB& color, int segments = 16)
|
||||
{
|
||||
glColor3f(color.r(), color.g(), color.b());
|
||||
|
||||
// 1. Draw center rectangle
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2f(x + radius, y + radius);
|
||||
glVertex2f(x + width - radius, y + radius);
|
||||
glVertex2f(x + width - radius, y + height - radius);
|
||||
glVertex2f(x + radius, y + height - radius);
|
||||
glEnd();
|
||||
|
||||
// 2. Draw side rectangles (excluding corners)
|
||||
glBegin(GL_QUADS);
|
||||
// Left
|
||||
glVertex2f(x, y + radius);
|
||||
glVertex2f(x + radius, y + radius);
|
||||
glVertex2f(x + radius, y + height - radius);
|
||||
glVertex2f(x, y + height - radius);
|
||||
|
||||
// Right
|
||||
glVertex2f(x + width - radius, y + radius);
|
||||
glVertex2f(x + width, y + radius);
|
||||
glVertex2f(x + width, y + height - radius);
|
||||
glVertex2f(x + width - radius, y + height - radius);
|
||||
|
||||
// Top
|
||||
glVertex2f(x + radius, y + height - radius);
|
||||
glVertex2f(x + width - radius, y + height - radius);
|
||||
glVertex2f(x + width - radius, y + height);
|
||||
glVertex2f(x + radius, y + height);
|
||||
|
||||
// Bottom
|
||||
glVertex2f(x + radius, y);
|
||||
glVertex2f(x + width - radius, y);
|
||||
glVertex2f(x + width - radius, y + radius);
|
||||
glVertex2f(x + radius, y + radius);
|
||||
glEnd();
|
||||
|
||||
// 3. Draw corners
|
||||
auto drawCorner = [&](float cx, float cy, float startAngle) {
|
||||
glBegin(GL_TRIANGLE_FAN);
|
||||
glVertex2f(cx, cy);
|
||||
for (int i = 0; i <= segments; ++i) {
|
||||
float angle = startAngle + (M_PI * 0.5f) * (float)i / segments;
|
||||
glVertex2f(cx + cosf(angle) * radius, cy + sinf(angle) * radius);
|
||||
}
|
||||
glEnd();
|
||||
};
|
||||
|
||||
drawCorner(x + radius, y + radius, M_PI); // bottom-left
|
||||
drawCorner(x + width - radius, y + radius, 1.5f * M_PI); // bottom-right
|
||||
drawCorner(x + width - radius, y + height - radius, 0.0f); // top-right
|
||||
drawCorner(x + radius, y + height - radius, 0.5f * M_PI); // top-left
|
||||
}
|
||||
|
||||
|
||||
void SkipPartCanvas::Render()
|
||||
{
|
||||
constexpr float border_w = 3.f;
|
||||
constexpr int uncheckd_stencil =1;
|
||||
constexpr int checkd_stencil = 2;
|
||||
constexpr int skipped_stencil = 3;
|
||||
|
||||
SetCurrent(*context_);
|
||||
glPushAttrib(GL_ALL_ATTRIB_BITS);
|
||||
|
||||
int w, h;
|
||||
GetClientSize(&w, &h);
|
||||
#if defined(__APPLE__)
|
||||
double scale = GetDPIScaleFactor();
|
||||
glViewport(0, 0, w * scale, h * scale);
|
||||
#else
|
||||
glViewport(0, 0, w, h);
|
||||
#endif
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
auto view_rect = ViewPtToImagePt(wxPoint(w, h));
|
||||
glOrtho(offset_.x, view_rect.x, view_rect.y, offset_.y, -1, 1);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
glClearColor(parent_color_.r(), parent_color_.g(), parent_color_.b(), 1.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
float rx = offset_.x;
|
||||
float ry = offset_.y;
|
||||
float rw = view_rect.x - offset_.x;
|
||||
float rh = view_rect.y - offset_.y;
|
||||
float radius = std::min(rw, rh) * 0.05f;
|
||||
|
||||
DrawRoundedRect(rx, ry, rw, rh, radius, ColorRGB{0.9f, 0.9f, 0.9f});
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
|
||||
auto draw_shape = [this, border_w](const int stencil, const PartState part_type, const ColorRGB& rgb) {
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glStencilFunc(GL_ALWAYS, stencil, 0xFF);
|
||||
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
|
||||
|
||||
for (const auto& contour : parts_triangles_) {
|
||||
auto part_info = parts_state_.find(contour.first);
|
||||
if (part_info == parts_state_.end() || part_info->second != part_type)
|
||||
continue;
|
||||
glColor3f(1, 1, 1);
|
||||
for (const auto &contour_item : contour.second) {
|
||||
glBegin(GL_TRIANGLES);
|
||||
for (size_t i = 0; i < contour_item.size(); i += 3) {
|
||||
glVertex2f(contour_item[i][0], contour_item[i][1]);
|
||||
glVertex2f(contour_item[i + 1][0], contour_item[i + 1][1]);
|
||||
glVertex2f(contour_item[i + 2][0], contour_item[i + 2][1]);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& contour : pick_parts_) {
|
||||
if (contour.first != this->hover_id_) continue;
|
||||
auto part_info = parts_state_.find(contour.first);
|
||||
if (part_info == parts_state_.end() || part_info->second != part_type)
|
||||
continue;
|
||||
|
||||
glColor3f(rgb.r(), rgb.g(), rgb.b());
|
||||
glLineWidth(border_w);
|
||||
for (const auto &contour_item : contour.second) {
|
||||
glBegin(GL_LINE_LOOP);
|
||||
for (const auto &pt : contour_item) { glVertex2f(pt.x, pt.y); }
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
};
|
||||
// draw unchecked shapes
|
||||
// stencil1 => unchecked
|
||||
draw_shape(uncheckd_stencil, psUnCheck, ColorRGB{0, 174 / 255.f, 66 / 255.f});
|
||||
|
||||
// draw checked shapes
|
||||
// stencil2 => checked
|
||||
draw_shape(checkd_stencil, psChecked, ColorRGB{208 / 255.f, 27 / 255.f, 66 / 255.f});
|
||||
|
||||
// draw skipped shapes
|
||||
// stencil3 => skipped
|
||||
draw_shape(skipped_stencil, psSkipped, ColorRGB{95 / 255.f, 95 / 255.f, 95 / 255.f});
|
||||
|
||||
auto draw_mask = [this, view_rect, border_w, w, h](const int stencil, const PartState part_type,
|
||||
const ColorRGB& background, const ColorRGB& line, const ColorRGB& bound) {
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glStencilFunc(GL_EQUAL, stencil, 0xFF);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // Don't change stencil
|
||||
glColor3f(background.r(), background.g(), background.b());
|
||||
glBegin(GL_POLYGON);
|
||||
glVertex2f(offset_.x, offset_.y);
|
||||
glVertex2f(offset_.x, view_rect.y);
|
||||
glVertex2f(view_rect.x, view_rect.y);
|
||||
glVertex2f(view_rect.x, offset_.y);
|
||||
glEnd();
|
||||
// draw main color
|
||||
glColor3f(line.r(), line.g(), line.b());
|
||||
// re-draw shape bound
|
||||
for (const auto& contour : pick_parts_) {
|
||||
auto part_info = parts_state_.find(contour.first);
|
||||
if (part_info == parts_state_.end() || part_info->second != part_type)
|
||||
continue;
|
||||
glColor3f(bound.r(), bound.g(), bound.b());
|
||||
glLineWidth(border_w);
|
||||
for (const auto &contour_item : contour.second) {
|
||||
glBegin(GL_LINE_LOOP);
|
||||
for (const auto &pt : contour_item) { glVertex2f(pt.x, pt.y); }
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
draw_mask(checkd_stencil, psChecked, ColorRGB{239 / 255.f, 175 / 255.f, 175 / 255.f},
|
||||
ColorRGB{225 / 255.f, 71 / 255.f, 71 / 255.f}, ColorRGB{208 / 255.f, 27 / 255.f, 27 / 255.f});
|
||||
|
||||
draw_mask(skipped_stencil, psSkipped, ColorRGB{159 / 255.f, 159 / 255.f, 159 / 255.f},
|
||||
ColorRGB{95 / 255.f, 95 / 255.f, 95 / 255.f}, ColorRGB{95 / 255.f, 95 / 255.f, 95 / 255.f});
|
||||
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
|
||||
glPopAttrib();
|
||||
glFlush();
|
||||
}
|
||||
|
||||
void SkipPartCanvas::DebugLogLine(std::string str)
|
||||
{
|
||||
//if (!log_ctrl)
|
||||
// return;
|
||||
//log_ctrl->AppendText(str + "\n");
|
||||
}
|
||||
|
||||
void SkipPartCanvas::SendSelectEvent(int id, PartState state) {
|
||||
wxCommandEvent evt(EVT_CANVAS_PART);
|
||||
evt.SetExtraLong(id);
|
||||
evt.SetInt(static_cast<int>(state));
|
||||
wxPostEvent(this, evt);
|
||||
}
|
||||
void SkipPartCanvas::SendZoomEvent(int zoom_percent) {
|
||||
wxCommandEvent evt(EVT_ZOOM_PERCENT);
|
||||
evt.SetInt(zoom_percent_);
|
||||
wxPostEvent(this, evt);
|
||||
}
|
||||
|
||||
inline double SkipPartCanvas::Zoom() const
|
||||
{
|
||||
return zoom_percent_ / 100.0f;
|
||||
}
|
||||
|
||||
inline wxPoint SkipPartCanvas::ViewPtToImagePt(const wxPoint& view_pt) const
|
||||
{
|
||||
return wxPoint(view_pt.x * image_view_scale_ / Zoom(), view_pt.y * image_view_scale_ / Zoom()) + offset_;
|
||||
}
|
||||
|
||||
uint32_t SkipPartCanvas::GetIdAtImagePt(const wxPoint& image_pt) const
|
||||
{
|
||||
if (image_pt.x >= 0 && image_pt.x < pick_image_.cols
|
||||
&& image_pt.y >= 0 && image_pt.y < pick_image_.rows) {
|
||||
// at(row, col)=>at(y, x)
|
||||
cv::Vec3b bgr = pick_image_.at<cv::Vec3b>(image_pt.y, image_pt.x);
|
||||
SkipIdHelper helper{bgr[2], bgr[1], bgr[0]};
|
||||
helper.reverse();
|
||||
return helper.value;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline uint32_t SkipPartCanvas::GetIdAtViewPt(const wxPoint& view_pt) const
|
||||
{
|
||||
wxPoint pt_at_image = ViewPtToImagePt(view_pt);
|
||||
return GetIdAtImagePt(pt_at_image);
|
||||
}
|
||||
|
||||
void SkipPartCanvas::SetZoomPercent(const int value)
|
||||
{
|
||||
zoom_percent_ = std::clamp(value, 100, 1000);
|
||||
std::ostringstream oss;
|
||||
oss << "zoom to " << zoom_percent_;
|
||||
DebugLogLine(oss.str());
|
||||
|
||||
SendZoomEvent(zoom_percent_);
|
||||
}
|
||||
|
||||
void SkipPartCanvas::SetOffset(const wxPoint& value)
|
||||
{
|
||||
int w, h;
|
||||
GetClientSize(&w, &h);
|
||||
int max_w = static_cast<int>(w * (1 - 1 / Zoom())) >= 0 ? static_cast<int>(w * (1 - 1 / Zoom())) : 0;
|
||||
int max_h = static_cast<int>(w * (1 - 1 / Zoom())) >= 0 ? static_cast<int>(h * (1 - 1 / Zoom())) : 0;
|
||||
offset_.x = std::clamp(value.x, 0, max_w);
|
||||
offset_.y = std::clamp(value.y, 0, max_h);
|
||||
}
|
||||
|
||||
void SkipPartCanvas::AutoSetCursor()
|
||||
{
|
||||
if(is_draging_ || fixed_draging_)
|
||||
SetCursor(wxCursor(wxCURSOR_HAND));
|
||||
else
|
||||
SetCursor(wxCursor(wxCURSOR_NONE));
|
||||
}
|
||||
|
||||
void SkipPartCanvas::StartDrag(const wxPoint& mouse_pt)
|
||||
{
|
||||
drag_start_pt_ = mouse_pt;
|
||||
drag_start_offset_ = offset_;
|
||||
is_draging_ = true;
|
||||
AutoSetCursor();
|
||||
}
|
||||
|
||||
void SkipPartCanvas::ProcessDrag(const wxPoint& mouse_pt)
|
||||
{
|
||||
wxPoint drag_offset = (mouse_pt - drag_start_pt_) * image_view_scale_;
|
||||
SetOffset(- wxPoint(drag_offset.x / Zoom(), drag_offset.y / Zoom()) + drag_start_offset_);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void SkipPartCanvas::ProcessHover(const wxPoint& mouse_pt)
|
||||
{
|
||||
auto id_at_mouse = GetIdAtViewPt(mouse_pt);
|
||||
int new_hover_id { -1 };
|
||||
auto part_state = parts_state_.find(id_at_mouse);
|
||||
if (part_state != parts_state_.end() && part_state->second == psUnCheck) {
|
||||
new_hover_id = id_at_mouse;
|
||||
};
|
||||
if (new_hover_id != this->hover_id_) {
|
||||
this->hover_id_ = new_hover_id;
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void SkipPartCanvas::EndDrag()
|
||||
{
|
||||
is_draging_ = false;
|
||||
AutoSetCursor();
|
||||
}
|
||||
|
||||
void SkipPartCanvas::OnPaint(wxPaintEvent &event)
|
||||
{
|
||||
wxPaintDC dc(this);
|
||||
if (!IsShown()) return;
|
||||
|
||||
SetCurrent(*context_);
|
||||
|
||||
Render();
|
||||
SwapBuffers();
|
||||
}
|
||||
|
||||
void SkipPartCanvas::OnSize(wxSizeEvent& event)
|
||||
{
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void SkipPartCanvas::OnMouseLeftDown(wxMouseEvent& event)
|
||||
{
|
||||
DebugLogLine("OnMouseLeftDown");
|
||||
if (!event.LeftIsDown()) {
|
||||
event.Skip();
|
||||
DebugLogLine("skip----OnMouseLeftDown");
|
||||
return;
|
||||
}
|
||||
if (fixed_draging_)
|
||||
StartDrag(wxPoint(event.GetX(), event.GetY()));
|
||||
left_down_ = true;
|
||||
}
|
||||
|
||||
void SkipPartCanvas::OnMouseLeftUp(wxMouseEvent& event)
|
||||
{
|
||||
DebugLogLine("OnMouseLeftUp");
|
||||
if (event.LeftIsDown() || !left_down_) {
|
||||
event.Skip();
|
||||
DebugLogLine("skip----OnMouseLeftUp");
|
||||
return;
|
||||
}
|
||||
auto id_at_mouse = GetIdAtViewPt(wxPoint(event.GetX(), event.GetY()));
|
||||
auto part_state = parts_state_.find(id_at_mouse);
|
||||
if (part_state != parts_state_.end() && part_state->second != psSkipped) {
|
||||
if (part_state->second == psUnCheck)
|
||||
part_state = parts_state_.insert_or_assign(part_state->first, psChecked).first;
|
||||
else
|
||||
part_state = parts_state_.insert_or_assign(part_state->first, psUnCheck).first;
|
||||
// if (select_callback_)
|
||||
// select_callback_(part_state->first, part_state->second);
|
||||
SendSelectEvent(part_state->first, part_state->second);
|
||||
}
|
||||
left_down_ = false;
|
||||
if (fixed_draging_)
|
||||
EndDrag();
|
||||
else {
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void SkipPartCanvas::OnMouseRightDown(wxMouseEvent& event)
|
||||
{
|
||||
DebugLogLine("OnMouseRightDown");
|
||||
if (!event.RightIsDown()) {
|
||||
event.Skip();
|
||||
DebugLogLine("skip----OnMouseRightDown");
|
||||
return;
|
||||
}
|
||||
StartDrag(wxPoint(event.GetX(), event.GetY()));
|
||||
}
|
||||
|
||||
void SkipPartCanvas::OnMouseRightUp(wxMouseEvent& event)
|
||||
{
|
||||
DebugLogLine("OnMouseRightUp");
|
||||
if (event.RightIsDown() || !is_draging_) {
|
||||
event.Skip();
|
||||
DebugLogLine("skip----OnMouseRightUp");
|
||||
return;
|
||||
}
|
||||
EndDrag();
|
||||
}
|
||||
|
||||
void SkipPartCanvas::OnMouseMotion(wxMouseEvent& event)
|
||||
{
|
||||
ProcessHover(wxPoint(event.GetX(), event.GetY()));
|
||||
if (!event.RightIsDown() && !(event.LeftIsDown() && fixed_draging_)) {
|
||||
event.Skip();
|
||||
return;
|
||||
}
|
||||
ProcessDrag(wxPoint(event.GetX(), event.GetY()));
|
||||
}
|
||||
|
||||
void SkipPartCanvas::OnMouseWheel(wxMouseEvent& event)
|
||||
{
|
||||
wxPoint view_mouse = wxPoint(event.GetX(), event.GetY());
|
||||
auto pre_image_pos = ViewPtToImagePt(view_mouse);
|
||||
SetZoomPercent(zoom_percent_ + 10 * (event.GetWheelRotation() / 120.0));
|
||||
auto now_image_pos = ViewPtToImagePt(view_mouse);
|
||||
SetOffset(offset_ - (now_image_pos - pre_image_pos));
|
||||
Refresh();
|
||||
}
|
||||
|
||||
// Base class with error messages management
|
||||
|
||||
void _BBS_3MF_Base::add_error(const std::string &error) const
|
||||
{
|
||||
boost::unique_lock l(mutex);
|
||||
m_errors.push_back(error);
|
||||
}
|
||||
void _BBS_3MF_Base::clear_errors() { m_errors.clear(); }
|
||||
|
||||
void _BBS_3MF_Base::log_errors()
|
||||
{
|
||||
for (const std::string &error : m_errors) BOOST_LOG_TRIVIAL(error) << error;
|
||||
}
|
||||
|
||||
|
||||
ModelSettingHelper::ModelSettingHelper(const std::string &path) : path_(path) {}
|
||||
|
||||
bool ModelSettingHelper::Parse()
|
||||
{
|
||||
boost::nowide::fstream fs(path_);
|
||||
if (!fs) {
|
||||
add_error("Failed to open file\n");
|
||||
return false;
|
||||
}
|
||||
XML_Parser parser = XML_ParserCreate(nullptr);
|
||||
if (!parser) {
|
||||
add_error("Unable to create parser");
|
||||
return false;
|
||||
}
|
||||
XML_SetUserData(parser, this);
|
||||
XML_SetElementHandler(parser, ModelSettingHelper::StartElementHandler, ModelSettingHelper::EndElementHandler);
|
||||
|
||||
try {
|
||||
char buffer[4000];
|
||||
while (fs.read(buffer, sizeof(buffer)) || fs.gcount() > 0) {
|
||||
auto ret = XML_Parse(parser, buffer, static_cast<int>(fs.gcount()), fs.eof());
|
||||
if (ret != XML_STATUS_OK) {
|
||||
add_error("return value of XML_Parse doesn't match XM_STATUS_OK");
|
||||
XML_ParserFree(parser);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
add_error(std::string("exception:") + e.what());
|
||||
XML_ParserFree(parser);
|
||||
return false;
|
||||
}
|
||||
XML_ParserFree(parser);
|
||||
return true;
|
||||
}
|
||||
|
||||
void XMLCALL ModelSettingHelper::StartElementHandler(void *userData, const XML_Char *name, const XML_Char **atts)
|
||||
{
|
||||
ModelSettingHelper *self = static_cast<ModelSettingHelper *>(userData);
|
||||
if (strcmp(name, "plate") == 0) {
|
||||
self->context_.current_plate = PlateInfo(); // start a new plate
|
||||
self->context_.in_plate = true;
|
||||
} else if (strcmp(name, "metadata") == 0 && self->context_.in_plate) {
|
||||
std::string key, value;
|
||||
for (int i = 0; atts[i]; i += 2) {
|
||||
if (strcmp(atts[i], "key") == 0) key = atts[i + 1];
|
||||
if (strcmp(atts[i], "value") == 0) value = atts[i + 1];
|
||||
}
|
||||
if (key == "index") { self->context_.current_plate.index = std::stoi(value); }
|
||||
if (key == "label_object_enabled") { self->context_.current_plate.label_object_enabled = value == "true"; }
|
||||
} else if (strcmp(name, "object") == 0 && self->context_.in_plate) {
|
||||
ObjectInfo obj;
|
||||
for (int i = 0; atts[i]; i += 2) {
|
||||
if (strcmp(atts[i], "identify_id") == 0) obj.identify_id = atoi(atts[i + 1]);
|
||||
if (strcmp(atts[i], "name") == 0) obj.name = atts[i + 1];
|
||||
}
|
||||
self->context_.current_plate.objects.push_back(obj);
|
||||
}
|
||||
}
|
||||
|
||||
void XMLCALL ModelSettingHelper::EndElementHandler(void *userData, const XML_Char *name)
|
||||
{
|
||||
ModelSettingHelper *self = static_cast<ModelSettingHelper *>(userData);
|
||||
if (strcmp(name, "plate") == 0 && self->context_.in_plate) {
|
||||
self->context_.plates.push_back(self->context_.current_plate);
|
||||
self->context_.current_plate = PlateInfo(); // reset
|
||||
self->context_.in_plate = false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ObjectInfo> ModelSettingHelper::GetPlateObjects(int plate_idx) {
|
||||
for (const auto &plate : context_.plates) {
|
||||
if (plate.index == plate_idx) {
|
||||
return plate.objects;
|
||||
}
|
||||
}
|
||||
return std::vector<ObjectInfo>();
|
||||
}
|
||||
|
||||
bool ModelSettingHelper::GetLabelObjectEnabled(int plate_idx)
|
||||
{
|
||||
for (const auto &plate : context_.plates) {
|
||||
if (plate.index == plate_idx) { return plate.label_object_enabled; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ModelSettingHelper::DataHandler(const XML_Char *s, int len)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
169
src/slic3r/GUI/SkipPartCanvas.hpp
Normal file
169
src/slic3r/GUI/SkipPartCanvas.hpp
Normal file
@@ -0,0 +1,169 @@
|
||||
#ifndef SKIPPARTCANVAS_H
|
||||
#define SKIPPARTCANVAS_H
|
||||
#include <wx/wx.h>
|
||||
#include <wx/glcanvas.h>
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <wx/textctrl.h>
|
||||
#include <vector>
|
||||
#include <expat.h>
|
||||
#include <libslic3r/Color.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include "PartSkipCommon.hpp"
|
||||
|
||||
wxDECLARE_EVENT(EVT_ZOOM_PERCENT, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_CANVAS_PART, wxCommandEvent);
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
||||
using Coord = float;
|
||||
using FloatPoint = std::array<Coord, 2>;
|
||||
|
||||
|
||||
struct ObjectInfo {
|
||||
std::string name{""};
|
||||
int identify_id{-1};
|
||||
PartState state{psUnCheck};
|
||||
};
|
||||
|
||||
class SkipPartCanvas : public wxGLCanvas
|
||||
{
|
||||
union SkipIdHelper
|
||||
{
|
||||
uint32_t value = 0;
|
||||
|
||||
struct
|
||||
{
|
||||
uint8_t b;
|
||||
uint8_t g;
|
||||
uint8_t r;
|
||||
uint8_t _padding;
|
||||
};
|
||||
|
||||
uint8_t bytes[4];
|
||||
|
||||
SkipIdHelper() = default;
|
||||
|
||||
SkipIdHelper(uint8_t red, uint8_t green, uint8_t blue)
|
||||
: r(red), g(green), b(blue), _padding(0) {}
|
||||
|
||||
SkipIdHelper(uint32_t val): value(val){}
|
||||
void reverse() {
|
||||
uint8_t tmp{r};
|
||||
r = b;
|
||||
b = tmp;
|
||||
}
|
||||
};
|
||||
public:
|
||||
SkipPartCanvas(wxWindow *parent, const wxGLAttributes& dispAttrs);
|
||||
~SkipPartCanvas() = default;
|
||||
|
||||
void SetParentBackground(const ColorRGB& color) {
|
||||
parent_color_ = color;
|
||||
}
|
||||
|
||||
void LoadPickImage(const std::string& path);
|
||||
void ZoomIn(const int zoom_percent);
|
||||
void ZoomOut(const int zoom_percent);
|
||||
void SwitchDrag(const bool drag_on);
|
||||
void UpdatePartsInfo(const PartsInfo& parts);
|
||||
void SetZoomPercent(const int value);
|
||||
void SetOffset(const wxPoint &value);
|
||||
|
||||
wxTextCtrl* log_ctrl;
|
||||
protected:
|
||||
void OnPaint(wxPaintEvent& event);
|
||||
void OnSize(wxSizeEvent& event);
|
||||
void OnMouseLeftDown(wxMouseEvent& event);
|
||||
void OnMouseLeftUp(wxMouseEvent& event);
|
||||
void OnMouseRightDown(wxMouseEvent& event);
|
||||
void OnMouseRightUp(wxMouseEvent& event);
|
||||
void OnMouseMotion(wxMouseEvent& event);
|
||||
void OnMouseWheel(wxMouseEvent& event);
|
||||
private:
|
||||
wxGLContext* context_;
|
||||
cv::Mat pick_image_;
|
||||
std::unordered_map < uint32_t, std::vector<std::vector<FloatPoint>>> parts_triangles_;
|
||||
std::unordered_map < uint32_t, std::vector<std::vector<cv::Point>>> pick_parts_;
|
||||
std::unordered_map<uint32_t, PartState> parts_state_;
|
||||
bool gl_inited_{false};
|
||||
int zoom_percent_{100};
|
||||
wxPoint offset_{0,0};
|
||||
wxPoint drag_start_offset_{0,0};
|
||||
wxPoint drag_start_pt_{0,0};
|
||||
bool is_draging_{false};
|
||||
bool fixed_draging_{false};
|
||||
bool left_down_{false};
|
||||
ColorRGB parent_color_ = ColorRGB();
|
||||
int hover_id_{-1};
|
||||
double image_view_scale_{1};
|
||||
|
||||
void SendSelectEvent(int id, PartState state);
|
||||
void SendZoomEvent(int zoom_percent);
|
||||
|
||||
inline double Zoom() const;
|
||||
inline wxPoint ViewPtToImagePt(const wxPoint& view_pt) const;
|
||||
uint32_t GetIdAtImagePt(const wxPoint& image_pt) const;
|
||||
inline uint32_t GetIdAtViewPt(const wxPoint& view_pt) const;
|
||||
|
||||
void ProcessHover(const wxPoint& mouse_pt);
|
||||
void AutoSetCursor();
|
||||
void StartDrag(const wxPoint& mouse_pt);
|
||||
void ProcessDrag(const wxPoint& mouse_pt);
|
||||
void EndDrag();
|
||||
|
||||
void Render();
|
||||
|
||||
void DebugLogLine(std::string str);
|
||||
};
|
||||
|
||||
class _BBS_3MF_Base
|
||||
{
|
||||
mutable boost::mutex mutex;
|
||||
mutable std::vector<std::string> m_errors;
|
||||
|
||||
protected:
|
||||
void add_error(const std::string& error) const;
|
||||
void clear_errors();
|
||||
|
||||
public:
|
||||
void log_errors();
|
||||
};
|
||||
|
||||
struct PlateInfo
|
||||
{
|
||||
int index{-1};
|
||||
std::vector<ObjectInfo> objects;
|
||||
bool label_object_enabled = false;
|
||||
};
|
||||
|
||||
class ModelSettingHelper : public _BBS_3MF_Base
|
||||
{
|
||||
struct ParseContext
|
||||
{
|
||||
std::vector<PlateInfo> plates;
|
||||
PlateInfo current_plate;
|
||||
ObjectInfo temp_object;
|
||||
bool in_plate = false;
|
||||
};
|
||||
|
||||
public:
|
||||
ModelSettingHelper(const std::string &path);
|
||||
|
||||
bool Parse();
|
||||
std::vector<ObjectInfo> GetPlateObjects(int plate_idx);
|
||||
bool GetLabelObjectEnabled(int plate_idx);
|
||||
|
||||
private:
|
||||
std::string path_;
|
||||
ParseContext context_;
|
||||
|
||||
static void XMLCALL StartElementHandler(void *userData, const XML_Char *name, const XML_Char **atts);
|
||||
static void XMLCALL EndElementHandler(void *userData, const XML_Char *name);
|
||||
void DataHandler(const XML_Char *s, int len);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
#endif //SKIPPARTCANVAS_H
|
||||
@@ -233,12 +233,6 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
|
||||
|
||||
bSizer_task_name->Add(task_name_panel, 0, wxEXPAND, FromDIP(5));
|
||||
|
||||
|
||||
/* wxFlexGridSizer *fgSizer_task = new wxFlexGridSizer(2, 2, 0, 0);
|
||||
fgSizer_task->AddGrowableCol(0);
|
||||
fgSizer_task->SetFlexibleDirection(wxVERTICAL);
|
||||
fgSizer_task->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);*/
|
||||
|
||||
m_printing_stage_value = new wxStaticText(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT | wxST_ELLIPSIZE_END);
|
||||
m_printing_stage_value->Wrap(-1);
|
||||
m_printing_stage_value->SetMaxSize(wxSize(FromDIP(800),-1));
|
||||
@@ -261,24 +255,34 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
|
||||
|
||||
m_staticText_profile_value->SetForegroundColour(0x6B6B6B);
|
||||
|
||||
auto progress_lr_panel = new wxPanel(parent, wxID_ANY);
|
||||
progress_lr_panel->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
auto m_panel_progress = new wxPanel(parent, wxID_ANY);
|
||||
m_panel_progress->SetBackgroundColour(*wxWHITE);
|
||||
auto m_sizer_progressbar = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_gauge_progress = new ProgressBar(m_panel_progress, wxID_ANY, 100, wxDefaultPosition, wxDefaultSize);
|
||||
m_gauge_progress = new ProgressBar(progress_lr_panel, wxID_ANY, 100, wxDefaultPosition, wxDefaultSize);
|
||||
m_gauge_progress->SetValue(0);
|
||||
m_gauge_progress->SetHeight(PROGRESSBAR_HEIGHT);
|
||||
m_gauge_progress->SetMaxSize(wxSize(FromDIP(600), -1));
|
||||
m_panel_progress->SetSizer(m_sizer_progressbar);
|
||||
m_panel_progress->Layout();
|
||||
m_panel_progress->SetSize(wxSize(-1, FromDIP(24)));
|
||||
m_panel_progress->SetMaxSize(wxSize(-1, FromDIP(24)));
|
||||
|
||||
wxBoxSizer *bSizer_task_btn = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
bSizer_task_btn->Add(FromDIP(10), 0, 0);
|
||||
|
||||
m_button_pause_resume = new ScalableButton(m_panel_progress, wxID_ANY, "print_control_pause", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER,true);
|
||||
StateColor white_bg(std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Disabled), std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Pressed),
|
||||
std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Hovered), std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Enabled),
|
||||
std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Normal));
|
||||
|
||||
m_button_partskip = new Button(progress_lr_panel, wxEmptyString, "print_control_partskip_disable", 0, 20, wxID_ANY);
|
||||
m_button_partskip->Enable(false);
|
||||
m_button_partskip->Hide();
|
||||
m_button_partskip->SetBackgroundColor(white_bg);
|
||||
m_button_partskip->SetIcon("print_control_partskip_disable");
|
||||
m_button_partskip->SetBorderColor(*wxWHITE);
|
||||
m_button_partskip->SetFont(Label::Body_12);
|
||||
m_button_partskip->SetCornerRadius(0);
|
||||
m_button_partskip->SetToolTip(_L("Parts Skip"));
|
||||
m_button_partskip->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { m_button_partskip->SetIcon("print_control_partskip_hover"); });
|
||||
m_button_partskip->Bind(wxEVT_LEAVE_WINDOW, [this](auto &e) { m_button_partskip->SetIcon("print_control_partskip"); });
|
||||
|
||||
m_button_pause_resume = new ScalableButton(progress_lr_panel, wxID_ANY, "print_control_pause", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER,true);
|
||||
|
||||
m_button_pause_resume->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) {
|
||||
if (m_button_pause_resume->GetToolTipText() == _L("Pause")) {
|
||||
@@ -301,7 +305,7 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
|
||||
}
|
||||
});
|
||||
|
||||
m_button_abort = new ScalableButton(m_panel_progress, wxID_ANY, "print_control_stop", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true);
|
||||
m_button_abort = new ScalableButton(progress_lr_panel, wxID_ANY, "print_control_stop", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true);
|
||||
m_button_abort->SetToolTip(_L("Stop"));
|
||||
|
||||
m_button_abort->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) {
|
||||
@@ -312,19 +316,14 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
|
||||
m_button_abort->SetBitmap_("print_control_stop"); }
|
||||
);
|
||||
|
||||
m_sizer_progressbar->Add(m_gauge_progress, 1, wxALIGN_CENTER_VERTICAL, 0);
|
||||
m_sizer_progressbar->Add(0, 0, 0, wxEXPAND|wxLEFT, FromDIP(18));
|
||||
m_sizer_progressbar->Add(m_button_pause_resume, 0, wxALL, FromDIP(5));
|
||||
m_sizer_progressbar->Add(0, 0, 0, wxEXPAND|wxLEFT, FromDIP(18));
|
||||
m_sizer_progressbar->Add(m_button_abort, 0, wxALL, FromDIP(5));
|
||||
|
||||
wxBoxSizer *bSizer_buttons = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxBoxSizer *bSizer_text = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxPanel* penel_bottons = new wxPanel(parent);
|
||||
wxPanel* penel_text = new wxPanel(penel_bottons);
|
||||
wxBoxSizer *bSizer_finish_time = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxPanel* penel_text = new wxPanel(progress_lr_panel);
|
||||
wxPanel* penel_finish_time = new wxPanel(progress_lr_panel);
|
||||
|
||||
penel_text->SetBackgroundColour(*wxWHITE);
|
||||
penel_bottons->SetBackgroundColour(*wxWHITE);
|
||||
penel_finish_time->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
wxBoxSizer *sizer_percent = new wxBoxSizer(wxVERTICAL);
|
||||
sizer_percent->Add(0, 0, 1, wxEXPAND, 0);
|
||||
@@ -357,55 +356,65 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
|
||||
m_staticText_progress_left->SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC")));
|
||||
m_staticText_progress_left->SetForegroundColour(wxColour(146, 146, 146));
|
||||
|
||||
// Orca: display the end time of the print
|
||||
m_staticText_progress_end = new wxStaticText(penel_text, wxID_ANY, L("N/A"), wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_staticText_progress_end->Wrap(-1);
|
||||
m_staticText_progress_end->SetFont(
|
||||
wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC")));
|
||||
m_staticText_progress_end->SetForegroundColour(wxColour(146, 146, 146));
|
||||
|
||||
//fgSizer_task->Add(bSizer_buttons, 0, wxEXPAND, 0);
|
||||
//fgSizer_task->Add(0, 0, 0, wxEXPAND, FromDIP(5));
|
||||
|
||||
wxPanel* panel_button_block = new wxPanel(penel_bottons, wxID_ANY);
|
||||
panel_button_block->SetMinSize(wxSize(TASK_BUTTON_SIZE.x * 2 + FromDIP(5) * 4, -1));
|
||||
panel_button_block->SetMinSize(wxSize(TASK_BUTTON_SIZE.x * 2 + FromDIP(5) * 4, -1));
|
||||
panel_button_block->SetSize(wxSize(TASK_BUTTON_SIZE.x * 2 + FromDIP(5) * 2, -1));
|
||||
panel_button_block->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
m_staticText_layers = new wxStaticText(penel_text, wxID_ANY, _L("Layer: N/A"));
|
||||
m_staticText_layers->SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC")));
|
||||
m_staticText_layers->SetForegroundColour(wxColour(146, 146, 146));
|
||||
m_staticText_layers->Hide();
|
||||
|
||||
//bSizer_text->Add(m_staticText_progress_percent, 0, wxALL, 0);
|
||||
bSizer_text->Add(sizer_percent, 0, wxEXPAND, 0);
|
||||
bSizer_text->Add(sizer_percent_icon, 0, wxEXPAND, 0);
|
||||
bSizer_text->Add(0, 0, 1, wxEXPAND, 0);
|
||||
bSizer_text->Add(m_staticText_layers, 0, wxALIGN_CENTER | wxALL, 0);
|
||||
bSizer_text->Add(m_staticText_layers, 0, wxALIGN_CENTER_VERTICAL | wxALL, 0);
|
||||
bSizer_text->Add(0, 0, 0, wxLEFT, FromDIP(20));
|
||||
bSizer_text->Add(m_staticText_progress_left, 0, wxALIGN_CENTER | wxALL, 0);
|
||||
// Orca: display the end time of the print
|
||||
bSizer_text->Add(0, 0, 0, wxLEFT, FromDIP(8));
|
||||
bSizer_text->Add(m_staticText_progress_end, 0, wxALIGN_CENTER | wxALL, 0);
|
||||
bSizer_text->Add(m_staticText_progress_left, 0, wxALIGN_CENTER_VERTICAL | wxALL, 0);
|
||||
|
||||
penel_text->SetMaxSize(wxSize(FromDIP(600), -1));
|
||||
// penel_text->SetMaxSize(wxSize(FromDIP(600), -1));
|
||||
penel_text->SetSizer(bSizer_text);
|
||||
penel_text->Layout();
|
||||
|
||||
bSizer_buttons->Add(penel_text, 1, wxEXPAND | wxALL, 0);
|
||||
bSizer_buttons->Add(panel_button_block, 0, wxALIGN_CENTER | wxALL, 0);
|
||||
// Orca: display the end time of the print
|
||||
m_staticText_progress_end = new wxStaticText(penel_finish_time, wxID_ANY, L("N/A"), wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_staticText_progress_end->Wrap(-1);
|
||||
m_staticText_progress_end->SetFont(
|
||||
wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC")));
|
||||
m_staticText_progress_end->SetForegroundColour(wxColour(146, 146, 146));
|
||||
bSizer_finish_time->Add(0, 0, 1, wxEXPAND, 0);
|
||||
bSizer_finish_time->Add(m_staticText_progress_end, 0, wxLEFT | wxEXPAND, 0);
|
||||
// penel_finish_time->SetMaxSize(wxSize(FromDIP(600), -1));
|
||||
penel_finish_time->SetSizer(bSizer_finish_time);
|
||||
penel_finish_time->Layout();
|
||||
|
||||
penel_bottons->SetSizer(bSizer_buttons);
|
||||
penel_bottons->Layout();
|
||||
auto progress_lr_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto progress_left_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
auto progress_right_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
progress_left_sizer->Add(penel_text, 0, wxEXPAND | wxALL, 0);
|
||||
progress_left_sizer->Add(m_gauge_progress, 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(10));
|
||||
progress_left_sizer->Add(penel_finish_time, 0, wxEXPAND |wxALL, 0);
|
||||
// progress_left_sizer->SetMaxSize(wxSize(FromDIP(600), -1));
|
||||
|
||||
progress_right_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18));
|
||||
progress_right_sizer->Add(m_button_partskip, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0));//5
|
||||
progress_right_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18));
|
||||
progress_right_sizer->Add(m_button_pause_resume, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0));
|
||||
progress_right_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18));
|
||||
progress_right_sizer->Add(m_button_abort, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0));
|
||||
progress_right_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18));
|
||||
|
||||
progress_lr_sizer->Add(progress_left_sizer, 1, wxEXPAND | wxALL, 0);
|
||||
progress_lr_sizer->Add(progress_right_sizer, 0, wxEXPAND | wxALL , 0);
|
||||
|
||||
progress_lr_panel->SetSizer(progress_lr_sizer);
|
||||
progress_lr_panel->SetMaxSize(wxSize(FromDIP(720), -1));
|
||||
|
||||
progress_lr_panel->Layout();
|
||||
progress_lr_panel->Fit();
|
||||
|
||||
bSizer_subtask_info->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(14));
|
||||
bSizer_subtask_info->Add(bSizer_task_name, 0, wxEXPAND|wxRIGHT, FromDIP(18));
|
||||
bSizer_subtask_info->Add(m_staticText_profile_value, 0, wxEXPAND | wxTOP, FromDIP(5));
|
||||
bSizer_subtask_info->Add(m_printing_stage_value, 0, wxEXPAND | wxTOP, FromDIP(5));
|
||||
bSizer_subtask_info->Add(penel_bottons, 0, wxEXPAND | wxTOP, FromDIP(10));
|
||||
bSizer_subtask_info->Add(m_panel_progress, 0, wxEXPAND|wxRIGHT, FromDIP(25));
|
||||
|
||||
bSizer_subtask_info->Add(progress_lr_panel, 0, wxEXPAND | wxTOP, FromDIP(5));
|
||||
|
||||
m_printing_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_printing_sizer->SetMinSize(wxSize(PAGE_MIN_WIDTH, -1));
|
||||
@@ -413,7 +422,6 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
|
||||
m_printing_sizer->Add(FromDIP(8), 0, 0, wxEXPAND, 0);
|
||||
m_printing_sizer->Add(bSizer_subtask_info, 1, wxALL | wxEXPAND, 0);
|
||||
|
||||
|
||||
m_staticline = new wxPanel( parent, wxID_ANY);
|
||||
m_staticline->SetBackgroundColour(wxColour(238,238,238));
|
||||
m_staticline->Layout();
|
||||
@@ -638,6 +646,24 @@ void PrintingTaskPanel::reset_printing_value()
|
||||
this->set_plate_index(-1);
|
||||
}
|
||||
|
||||
void PrintingTaskPanel::enable_partskip_button(MachineObject* obj, bool enable)
|
||||
{
|
||||
int stage = 0;
|
||||
bool in_calibration_mode = false;
|
||||
if( obj && (obj->print_type == "system" || CalibUtils::get_calib_mode_by_name(obj->subtask_name, stage) != CalibMode::Calib_None)){
|
||||
in_calibration_mode = true;
|
||||
}
|
||||
|
||||
if (!enable || in_calibration_mode) {
|
||||
m_button_partskip->Enable(false);
|
||||
m_button_partskip->SetLabel("");
|
||||
m_button_partskip->SetIcon("print_control_partskip_disable");
|
||||
}else if(obj && obj->is_support_brtc){
|
||||
m_button_partskip->Enable(true);
|
||||
m_button_partskip->SetIcon("print_control_partskip");
|
||||
}
|
||||
}
|
||||
|
||||
void PrintingTaskPanel::enable_pause_resume_button(bool enable, std::string type)
|
||||
{
|
||||
if (!enable) {
|
||||
@@ -1731,6 +1757,7 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co
|
||||
m_switch_cham_fan->SetValue(false);
|
||||
|
||||
/* set default enable state */
|
||||
m_project_task_panel->enable_partskip_button(nullptr, false);
|
||||
m_project_task_panel->enable_pause_resume_button(false, "resume_disable");
|
||||
m_project_task_panel->enable_abort_button(false);
|
||||
|
||||
@@ -1751,6 +1778,7 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co
|
||||
|
||||
// Connect Events
|
||||
m_project_task_panel->get_bitmap_thumbnail()->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::refresh_thumbnail_webrequest), NULL, this);
|
||||
m_project_task_panel->get_partskip_button()->Connect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_subtask_partskip), NULL, this);
|
||||
m_project_task_panel->get_pause_resume_button()->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_pause_resume), NULL, this);
|
||||
m_project_task_panel->get_abort_button()->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_abort), NULL, this);
|
||||
m_project_task_panel->get_market_scoring_button()->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_scoring), NULL, this);
|
||||
@@ -1810,6 +1838,7 @@ StatusPanel::~StatusPanel()
|
||||
{
|
||||
// Disconnect Events
|
||||
m_project_task_panel->get_bitmap_thumbnail()->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::refresh_thumbnail_webrequest), NULL, this);
|
||||
m_project_task_panel->get_partskip_button()->Disconnect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_subtask_partskip), NULL, this);
|
||||
m_project_task_panel->get_pause_resume_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_pause_resume), NULL, this);
|
||||
m_project_task_panel->get_abort_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_abort), NULL, this);
|
||||
m_project_task_panel->get_market_scoring_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_scoring), NULL, this);
|
||||
@@ -1940,6 +1969,35 @@ void StatusPanel::on_market_retry(wxCommandEvent &event)
|
||||
}
|
||||
}
|
||||
|
||||
void StatusPanel::update_partskip_button(MachineObject *obj) {
|
||||
if (!obj) return;
|
||||
|
||||
auto partskip_button = m_project_task_panel->get_partskip_button();
|
||||
if( obj->is_support_partskip ){
|
||||
partskip_button->Show();
|
||||
}else{
|
||||
partskip_button->Hide();
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << "part skip: is_support_partskip: "<< obj->is_support_partskip;
|
||||
}
|
||||
|
||||
void StatusPanel::on_subtask_partskip(wxCommandEvent &event)
|
||||
{
|
||||
if (m_partskip_dlg == nullptr) {
|
||||
m_partskip_dlg = new PartSkipDialog(this->GetParent());
|
||||
}
|
||||
|
||||
auto dm = GUI::wxGetApp().getDeviceManager();
|
||||
m_partskip_dlg->InitSchedule(dm->get_selected_machine());
|
||||
BOOST_LOG_TRIVIAL(info) << "part skip: initial part skip dialog.";
|
||||
if(m_partskip_dlg->ShowModal() == wxID_OK){
|
||||
int cnt = m_partskip_dlg->GetAllSkippedPartsNum();
|
||||
m_project_task_panel->set_part_skipped_count(cnt);
|
||||
m_project_task_panel->set_part_skipped_dirty(5);
|
||||
BOOST_LOG_TRIVIAL(info) << "part skip: prepare to filter printer dirty data.";
|
||||
}
|
||||
}
|
||||
|
||||
void StatusPanel::on_subtask_pause_resume(wxCommandEvent &event)
|
||||
{
|
||||
if (obj) {
|
||||
@@ -3084,14 +3142,18 @@ void StatusPanel::update_subtask(MachineObject *obj)
|
||||
}
|
||||
|
||||
update_model_info();
|
||||
update_partskip_button(obj);
|
||||
|
||||
if (obj->is_system_printing()
|
||||
|| obj->is_in_calibration()) {
|
||||
reset_printing_values();
|
||||
} else if (obj->is_in_printing() || obj->print_status == "FINISH") {
|
||||
update_partskip_subtask(obj);
|
||||
|
||||
if (obj->is_in_prepare() || obj->print_status == "SLICING") {
|
||||
m_project_task_panel->market_scoring_hide();
|
||||
m_project_task_panel->get_request_failed_panel()->Hide();
|
||||
m_project_task_panel->enable_partskip_button(nullptr, false);
|
||||
m_project_task_panel->enable_abort_button(false);
|
||||
m_project_task_panel->enable_pause_resume_button(false, "pause_disable");
|
||||
wxString prepare_text;
|
||||
@@ -3133,7 +3195,7 @@ void StatusPanel::update_subtask(MachineObject *obj)
|
||||
} else {
|
||||
m_project_task_panel->enable_pause_resume_button(true, "pause");
|
||||
}
|
||||
|
||||
m_project_task_panel->enable_partskip_button(obj, true);
|
||||
// update printing stage
|
||||
m_project_task_panel->update_left_time(obj->mc_left_time);
|
||||
if (obj->subtask_) {
|
||||
@@ -3150,6 +3212,7 @@ void StatusPanel::update_subtask(MachineObject *obj)
|
||||
if (obj->is_printing_finished()) {
|
||||
obj->update_model_task();
|
||||
m_project_task_panel->enable_abort_button(false);
|
||||
m_project_task_panel->enable_partskip_button(nullptr, false);
|
||||
m_project_task_panel->enable_pause_resume_button(false, "resume_disable");
|
||||
// is makeworld subtask
|
||||
if (wxGetApp().has_model_mall() && obj->is_makeworld_subtask()) {
|
||||
@@ -3217,6 +3280,32 @@ void StatusPanel::update_subtask(MachineObject *obj)
|
||||
Layout();
|
||||
}
|
||||
|
||||
void StatusPanel::update_partskip_subtask(MachineObject *obj){
|
||||
if (!obj) return;
|
||||
if (!obj->subtask_) return;
|
||||
|
||||
auto partskip_button = m_project_task_panel->get_partskip_button();
|
||||
if (partskip_button) {
|
||||
int part_cnt = 0;
|
||||
if(m_project_task_panel->get_part_skipped_dirty() > 0){
|
||||
m_project_task_panel->set_part_skipped_dirty(m_project_task_panel->get_part_skipped_dirty() - 1);
|
||||
part_cnt = m_project_task_panel->get_part_skipped_count();
|
||||
BOOST_LOG_TRIVIAL(info) << "part skip: stop recv printer dirty data.";
|
||||
}else{
|
||||
part_cnt = obj->m_partskip_ids.size();
|
||||
BOOST_LOG_TRIVIAL(info) << "part skip: recv printer normal data.";
|
||||
}
|
||||
if (part_cnt > 0)
|
||||
partskip_button->SetLabel(wxString::Format(_L("(%d)"), part_cnt));
|
||||
else
|
||||
partskip_button->SetLabel("");
|
||||
}
|
||||
|
||||
if(m_partskip_dlg && m_partskip_dlg->IsShown()) {
|
||||
m_partskip_dlg->UpdatePartsStateFromPrinter(obj);
|
||||
}
|
||||
}
|
||||
|
||||
void StatusPanel::update_cloud_subtask(MachineObject *obj)
|
||||
{
|
||||
if (!obj) return;
|
||||
@@ -3281,6 +3370,7 @@ void StatusPanel::update_sdcard_subtask(MachineObject *obj)
|
||||
|
||||
void StatusPanel::reset_printing_values()
|
||||
{
|
||||
m_project_task_panel->enable_partskip_button(nullptr, false);
|
||||
m_project_task_panel->enable_pause_resume_button(false, "pause_disable");
|
||||
m_project_task_panel->enable_abort_button(false);
|
||||
m_project_task_panel->reset_printing_value();
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "Widgets/AMSControl.hpp"
|
||||
#include "Widgets/FanControl.hpp"
|
||||
#include "HMS.hpp"
|
||||
#include "PartSkipDialog.hpp"
|
||||
|
||||
class StepIndicator;
|
||||
|
||||
@@ -160,7 +161,7 @@ public:
|
||||
|
||||
|
||||
private:
|
||||
MachineObject* m_obj;
|
||||
MachineObject* m_obj{nullptr};
|
||||
ScalableBitmap m_thumbnail_placeholder;
|
||||
wxBitmap m_thumbnail_bmp_display;
|
||||
ScalableBitmap m_bitmap_use_time;
|
||||
@@ -192,6 +193,7 @@ private:
|
||||
wxStaticBitmap* m_bitmap_static_use_weight;
|
||||
ScalableButton* m_button_pause_resume;
|
||||
ScalableButton* m_button_abort;
|
||||
Button* m_button_partskip;
|
||||
Button* m_button_market_scoring;
|
||||
Button* m_button_clean;
|
||||
Button * m_button_market_retry;
|
||||
@@ -203,6 +205,10 @@ private:
|
||||
std::vector<ScalableButton *> m_score_star;
|
||||
bool m_star_count_dirty = false;
|
||||
|
||||
// partskip button
|
||||
int m_part_skipped_count{ 0 };
|
||||
int m_part_skipped_dirty{ 0 };
|
||||
|
||||
ProgressBar* m_gauge_progress;
|
||||
Label* m_error_text;
|
||||
PrintingTaskType m_type;
|
||||
@@ -217,6 +223,7 @@ public:
|
||||
void msw_rescale();
|
||||
|
||||
public:
|
||||
void enable_partskip_button(MachineObject* obj, bool enable);
|
||||
void enable_pause_resume_button(bool enable, std::string type);
|
||||
void enable_abort_button(bool enable);
|
||||
void update_subtask_name(wxString name);
|
||||
@@ -236,6 +243,7 @@ public:
|
||||
public:
|
||||
ScalableButton* get_abort_button() {return m_button_abort;};
|
||||
ScalableButton* get_pause_resume_button() {return m_button_pause_resume;};
|
||||
Button* get_partskip_button() { return m_button_partskip; };
|
||||
Button* get_market_scoring_button() {return m_button_market_scoring;};
|
||||
Button * get_market_retry_buttom() { return m_button_market_retry; };
|
||||
Button* get_clean_button() {return m_button_clean;};
|
||||
@@ -246,6 +254,10 @@ public:
|
||||
std::vector<ScalableButton *> &get_score_star() { return m_score_star; }
|
||||
bool get_star_count_dirty() { return m_star_count_dirty; }
|
||||
void set_star_count_dirty(bool dirty) { m_star_count_dirty = dirty; }
|
||||
int get_part_skipped_count() { return m_part_skipped_count; }
|
||||
void set_part_skipped_count(int count) { m_part_skipped_count = count; }
|
||||
int get_part_skipped_dirty() { return m_part_skipped_dirty; }
|
||||
void set_part_skipped_dirty(int dirty) { m_part_skipped_dirty = dirty; }
|
||||
void set_has_reted_text(bool has_rated);
|
||||
void paint(wxPaintEvent&);
|
||||
};
|
||||
@@ -328,6 +340,7 @@ protected:
|
||||
wxStaticText * m_staticText_progress_left;
|
||||
wxStaticText * m_staticText_layers;
|
||||
Button * m_button_report;
|
||||
Button * m_button_partskip;
|
||||
ScalableButton *m_button_pause_resume;
|
||||
ScalableButton *m_button_abort;
|
||||
Button * m_button_clean;
|
||||
@@ -402,6 +415,7 @@ protected:
|
||||
PrintingTaskPanel * m_project_task_panel;
|
||||
|
||||
// Virtual event handlers, override them in your derived class
|
||||
virtual void on_subtask_partskip(wxCommandEvent &event) { event.Skip(); }
|
||||
virtual void on_subtask_pause_resume(wxCommandEvent &event) { event.Skip(); }
|
||||
virtual void on_subtask_abort(wxCommandEvent &event) { event.Skip(); }
|
||||
virtual void on_lamp_switch(wxCommandEvent &event) { event.Skip(); }
|
||||
@@ -484,6 +498,7 @@ protected:
|
||||
FanControlPopup* m_fan_control_popup{nullptr};
|
||||
|
||||
ExtrusionCalibration *m_extrusion_cali_dlg{nullptr};
|
||||
PartSkipDialog *m_partskip_dlg{nullptr};
|
||||
|
||||
wxString m_request_url;
|
||||
bool m_start_loading_thumbnail = false;
|
||||
@@ -525,6 +540,7 @@ protected:
|
||||
|
||||
void on_market_scoring(wxCommandEvent &event);
|
||||
void on_market_retry(wxCommandEvent &event);
|
||||
void on_subtask_partskip(wxCommandEvent &event);
|
||||
void on_subtask_pause_resume(wxCommandEvent &event);
|
||||
void on_subtask_abort(wxCommandEvent &event);
|
||||
void on_print_error_clean(wxCommandEvent &event);
|
||||
@@ -602,6 +618,7 @@ protected:
|
||||
void update_basic_print_data(bool def = false);
|
||||
void update_model_info();
|
||||
void update_subtask(MachineObject* obj);
|
||||
void update_partskip_subtask(MachineObject *obj);
|
||||
void update_cloud_subtask(MachineObject *obj);
|
||||
void update_sdcard_subtask(MachineObject *obj);
|
||||
void update_temp_ctrl(MachineObject *obj);
|
||||
@@ -621,6 +638,9 @@ protected:
|
||||
void update_camera_state(MachineObject* obj);
|
||||
bool show_vcamera = false;
|
||||
|
||||
// partskip button
|
||||
void update_partskip_button(MachineObject* obj);
|
||||
|
||||
public:
|
||||
void update_error_message();
|
||||
|
||||
|
||||
77
src/slic3r/GUI/Widgets/AnimaController.cpp
Normal file
77
src/slic3r/GUI/Widgets/AnimaController.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include "AnimaController.hpp"
|
||||
|
||||
#include <wx/dcclient.h>
|
||||
#include <wx/dcgraph.h>
|
||||
#ifdef __APPLE__
|
||||
#include "libslic3r/MacUtils.hpp"
|
||||
#endif
|
||||
|
||||
AnimaIcon::AnimaIcon(wxWindow *parent, wxWindowID id, std::vector<std::string> img_list, std::string img_enable, int ivt)
|
||||
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize), m_ivt(ivt)
|
||||
{
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
SetBackgroundColour((wxColour(255, 255, 255)));
|
||||
m_size = 25;
|
||||
|
||||
//add ScalableBitmap
|
||||
for (const auto &filename : img_list) m_images.emplace_back(create_scaled_bitmap(filename, this, m_size));
|
||||
m_image_enable = create_scaled_bitmap(img_enable, this, m_size-8);
|
||||
|
||||
// show first wxStaticBitmap
|
||||
if (!m_images.empty()) m_bitmap = new wxStaticBitmap(this, wxID_ANY, m_images[0], wxDefaultPosition, wxSize(FromDIP(m_size), FromDIP(m_size)));
|
||||
|
||||
|
||||
m_timer = new wxTimer();
|
||||
m_timer->SetOwner(this);
|
||||
|
||||
Bind(wxEVT_TIMER, [this](wxTimerEvent &) {
|
||||
if (m_timer->IsRunning() && !m_images.empty()) {
|
||||
m_current_frame = (m_current_frame + 1) % 4;
|
||||
m_bitmap->SetBitmap(m_images[m_current_frame]);
|
||||
}
|
||||
});
|
||||
|
||||
m_bitmap->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) {
|
||||
wxMouseEvent evt(wxEVT_LEFT_DOWN);
|
||||
evt.SetEventObject(this);
|
||||
wxPostEvent(this, evt);
|
||||
});
|
||||
|
||||
m_bitmap->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) {
|
||||
if (!m_timer->IsRunning())
|
||||
SetCursor(wxCursor(wxCURSOR_HAND));
|
||||
else
|
||||
SetCursor(wxCursor(wxCURSOR_ARROW));
|
||||
e.Skip();
|
||||
});
|
||||
m_bitmap->Bind(wxEVT_LEAVE_WINDOW, [this](auto &e) {
|
||||
SetCursor(wxCursor(wxCURSOR_ARROW));
|
||||
e.Skip();
|
||||
});
|
||||
sizer->Add(m_bitmap, 0, wxALIGN_CENTER, 0);
|
||||
SetSizer(sizer);
|
||||
SetSize(wxSize(FromDIP(m_size), FromDIP(m_size)));
|
||||
SetMaxSize(wxSize(FromDIP(m_size), FromDIP(m_size)));
|
||||
SetMinSize(wxSize(FromDIP(m_size), FromDIP(m_size)));
|
||||
Layout();
|
||||
Fit();
|
||||
Play();
|
||||
}
|
||||
|
||||
|
||||
void AnimaIcon::Play()
|
||||
{
|
||||
if (true)
|
||||
m_timer->Start(m_ivt);
|
||||
|
||||
}
|
||||
|
||||
void AnimaIcon::Stop()
|
||||
{
|
||||
m_timer->Stop();
|
||||
}
|
||||
|
||||
void AnimaIcon::Enable()
|
||||
{
|
||||
if (m_bitmap) { m_bitmap->SetBitmap(m_image_enable); }
|
||||
}
|
||||
28
src/slic3r/GUI/Widgets/AnimaController.hpp
Normal file
28
src/slic3r/GUI/Widgets/AnimaController.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef slic3r_GUI_AnimaController_hpp_
|
||||
#define slic3r_GUI_AnimaController_hpp_
|
||||
|
||||
#include "../wxExtensions.hpp"
|
||||
#include "Label.hpp"
|
||||
|
||||
|
||||
class AnimaIcon : public wxPanel
|
||||
{
|
||||
public:
|
||||
AnimaIcon(wxWindow *parent, wxWindowID id, std::vector<std::string> img_list, std::string img_enable, int ivt = 1000);
|
||||
|
||||
void Play();
|
||||
void Stop();
|
||||
void Enable();
|
||||
bool IsRunning() const;
|
||||
|
||||
private:
|
||||
wxBitmap m_image_enable;
|
||||
wxStaticBitmap * m_bitmap{nullptr};
|
||||
std::vector<wxBitmap> m_images;
|
||||
wxTimer * m_timer;
|
||||
int m_current_frame = 0;
|
||||
int m_ivt;
|
||||
int m_size;
|
||||
};
|
||||
|
||||
#endif // !slic3r_GUI_AnimaController_hpp_
|
||||
@@ -15,7 +15,7 @@ ProgressBar::ProgressBar(wxWindow *parent, wxWindowID id, int max, const wxPoint
|
||||
{
|
||||
m_shownumber = shown;
|
||||
SetBackgroundColour(wxColour(255,255,255));
|
||||
|
||||
|
||||
if (size.y >= miniHeight) {
|
||||
m_miniHeight = size.y;
|
||||
} else {
|
||||
@@ -64,20 +64,20 @@ void ProgressBar::create(wxWindow *parent, wxWindowID id, const wxPoint &pos, w
|
||||
}
|
||||
|
||||
|
||||
void ProgressBar::SetRadius(double radius) {
|
||||
void ProgressBar::SetRadius(double radius) {
|
||||
m_radius = radius;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void ProgressBar::SetProgressForedColour(wxColour colour)
|
||||
void ProgressBar::SetProgressForedColour(wxColour colour)
|
||||
{
|
||||
m_progress_background_colour = colour;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void ProgressBar::SetProgressBackgroundColour(wxColour colour)
|
||||
{
|
||||
m_progress_colour = colour;
|
||||
void ProgressBar::SetProgressBackgroundColour(wxColour colour)
|
||||
{
|
||||
m_progress_colour = colour;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
@@ -86,29 +86,29 @@ void ProgressBar::Rescale()
|
||||
;
|
||||
}
|
||||
|
||||
void ProgressBar::ShowNumber(bool shown)
|
||||
void ProgressBar::ShowNumber(bool shown)
|
||||
{
|
||||
m_shownumber = shown;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void ProgressBar::Disable(wxString text)
|
||||
{
|
||||
void ProgressBar::Disable(wxString text)
|
||||
{
|
||||
if (m_disable) return;
|
||||
m_disable_text = text;
|
||||
m_disable = true;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void ProgressBar::SetValue(int step)
|
||||
{
|
||||
void ProgressBar::SetValue(int step)
|
||||
{
|
||||
m_disable = false;
|
||||
SetProgress(step);
|
||||
}
|
||||
|
||||
void ProgressBar::Reset()
|
||||
{
|
||||
m_step = 0;
|
||||
void ProgressBar::Reset()
|
||||
{
|
||||
m_step = 0;
|
||||
SetValue(0);
|
||||
}
|
||||
|
||||
@@ -122,9 +122,9 @@ void ProgressBar::SetProgress(int step)
|
||||
}
|
||||
|
||||
|
||||
void ProgressBar::SetMinSize(const wxSize &size)
|
||||
{
|
||||
if (size.y >= miniHeight) {
|
||||
void ProgressBar::SetMinSize(const wxSize &size)
|
||||
{
|
||||
if (size.y >= miniHeight) {
|
||||
m_miniHeight = size.y;
|
||||
} else {
|
||||
return;
|
||||
@@ -177,7 +177,7 @@ void ProgressBar::doRender(wxDC &dc)
|
||||
dc.DrawRoundedRectangle(0, 0, size.x, size.y, m_radius);
|
||||
}
|
||||
|
||||
//draw progress
|
||||
//draw progress
|
||||
if (m_disable) {
|
||||
m_proportion = float(size.x * float(this->m_step) / float(this->m_max));
|
||||
if (m_proportion < m_radius * 2 && m_proportion != 0) { m_proportion = m_radius * 2; }
|
||||
@@ -228,11 +228,11 @@ void ProgressBar::doRender(wxDC &dc)
|
||||
dc.DrawText(text + wxString("%"), pt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void ProgressBar::DoSetSize(int x, int y, int width, int height, int sizeFlags)
|
||||
{
|
||||
void ProgressBar::DoSetSize(int x, int y, int width, int height, int sizeFlags)
|
||||
{
|
||||
wxWindow::DoSetSize(x, y, width, height, sizeFlags);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user