diff --git a/src/libslic3r/Format/bbs_3mf.cpp b/src/libslic3r/Format/bbs_3mf.cpp index 2b98426f45..5a72510a3a 100644 --- a/src/libslic3r/Format/bbs_3mf.cpp +++ b/src/libslic3r/Format/bbs_3mf.cpp @@ -1421,30 +1421,36 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) add_error("invalid plate index"); return false; } - plate_data_list[it->first-1]->locked = it->second->locked; - plate_data_list[it->first-1]->plate_index = it->second->plate_index-1; - plate_data_list[it->first-1]->obj_inst_map = it->second->obj_inst_map; - plate_data_list[it->first-1]->gcode_file = (m_load_restore || it->second->gcode_file.empty()) ? it->second->gcode_file : m_backup_path + "/" + it->second->gcode_file; - plate_data_list[it->first-1]->gcode_prediction = it->second->gcode_prediction; - plate_data_list[it->first-1]->gcode_weight = it->second->gcode_weight; - plate_data_list[it->first-1]->toolpath_outside = it->second->toolpath_outside; - plate_data_list[it->first-1]->is_support_used = it->second->is_support_used; - plate_data_list[it->first-1]->slice_filaments_info = it->second->slice_filaments_info; - plate_data_list[it->first-1]->warnings = it->second->warnings; - plate_data_list[it->first-1]->thumbnail_file = it->second->thumbnail_file; - //plate_data_list[it->first-1]->pattern_file = (m_load_restore || it->second->pattern_file.empty()) ? it->second->pattern_file : m_backup_path + "/" + it->second->pattern_file; - plate_data_list[it->first-1]->top_file = it->second->top_file; - plate_data_list[it->first-1]->pick_file = it->second->pick_file.empty(); - plate_data_list[it->first-1]->pattern_bbox_file = it->second->pattern_bbox_file.empty(); - plate_data_list[it->first-1]->config = it->second->config; + PlateData * plate = plate_data_list[it->first-1]; + plate->locked = it->second->locked; + plate->plate_index = it->second->plate_index-1; + plate->obj_inst_map = it->second->obj_inst_map; + plate->gcode_file = it->second->gcode_file; + plate->gcode_prediction = it->second->gcode_prediction; + plate->gcode_weight = it->second->gcode_weight; + plate->toolpath_outside = it->second->toolpath_outside; + plate->is_support_used = it->second->is_support_used; + plate->slice_filaments_info = it->second->slice_filaments_info; + plate->warnings = it->second->warnings; + plate->thumbnail_file = it->second->thumbnail_file; + if (plate->thumbnail_file.empty()) { + plate->thumbnail_file = plate->gcode_file; + boost::algorithm::replace_all(plate->thumbnail_file, ".gcode", ".png"); + } + //plate->pattern_file = it->second->pattern_file; + plate->top_file = it->second->top_file; + plate->pick_file = it->second->pick_file.empty(); + plate->pattern_bbox_file = it->second->pattern_bbox_file.empty(); + plate->config = it->second->config; - _extract_from_archive(archive, m_thumbnail_path, [&pixels = plate_data_list[it->first - 1]->plate_thumbnail.pixels](auto &archive, auto const &stat) -> bool { - pixels.resize(stat.m_uncomp_size); - return mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, pixels.data(), pixels.size(), 0); - }); + if (!plate->thumbnail_file.empty()) + _extract_from_archive(archive, plate->thumbnail_file, [&pixels = plate_data_list[it->first - 1]->plate_thumbnail.pixels](auto &archive, auto const &stat) -> bool { + pixels.resize(stat.m_uncomp_size); + return mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, pixels.data(), pixels.size(), 0); + }); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", plate %1%, thumbnail_file=%2%")%it->first %plate_data_list[it->first-1]->thumbnail_file; - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", top_thumbnail_file=%1%, pick_thumbnail_file=%2%")%plate_data_list[it->first-1]->top_file %plate_data_list[it->first-1]->pick_file; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", plate %1%, thumbnail_file=%2%")%it->first %plate->thumbnail_file; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", top_thumbnail_file=%1%, pick_thumbnail_file=%2%")%plate->top_file %plate->pick_file; it++; } @@ -4186,7 +4192,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) if (m_start_part_path.empty()) m_start_part_path = path; else m_sub_model_paths.push_back(path); } else if (boost::starts_with(type, "http://schemas.openxmlformats.org/") && boost::ends_with(type, "thumbnail")) { - m_thumbnail_path = path; + if (boost::algorithm::ends_with(path, ".png")) + m_thumbnail_path = path; } else if (boost::starts_with(type, "http://schemas.bambulab.com/") && boost::ends_with(type, "cover-thumbnail-middle")) { m_thumbnail_middle = path; } else if (boost::starts_with(type, "http://schemas.bambulab.com/") && boost::ends_with(type, "cover-thumbnail-small")) { diff --git a/src/slic3r/GUI/ImageGrid.cpp b/src/slic3r/GUI/ImageGrid.cpp index 8645a2afa1..3ba5103208 100644 --- a/src/slic3r/GUI/ImageGrid.cpp +++ b/src/slic3r/GUI/ImageGrid.cpp @@ -276,7 +276,7 @@ void ImageGrid::mouseMoved(wxMouseEvent& event) m_hit_type = hit.first; m_hit_item = hit.second; if (hit.first == HIT_ITEM) - SetToolTip(m_file_sys->GetFile(hit.second).name); + SetToolTip(from_u8(m_file_sys->GetFile(hit.second).Title())); else SetToolTip({}); Refresh(); @@ -616,7 +616,7 @@ void Slic3r::GUI::ImageGrid::renderContent1(wxDC &dc, wxPoint const &pt, int ind secondAction = _L("Print"); } // Draw buttons on hovered item - wxRect rect{pt.x, pt.y + m_content_rect.GetHeight() - m_buttons_background.GetHeight(), m_content_rect.GetWidth(), m_buttons_background.GetHeight()}; + wxRect rect{pt.x, pt.y + m_content_rect.GetBottom() - m_buttons_background.GetHeight(), m_content_rect.GetWidth(), m_buttons_background.GetHeight()}; if (hit) { renderButtons(dc, {_L("Delete"), (wxChar const *) secondAction, thirdAction.IsEmpty() ? nullptr : (wxChar const *) thirdAction, nullptr}, rect, m_hit_type == HIT_ACTION ? m_hit_item & 3 : -1, states); @@ -651,7 +651,7 @@ void Slic3r::GUI::ImageGrid::renderContent2(wxDC &dc, wxPoint const &pt, int ind auto em = em_unit(this); wxRect rect{pt.x, pt.y + m_content_rect.GetHeight() - h, m_content_rect.GetWidth(), h / 2}; rect.Deflate(em, 0); - renderText2(dc, from_u8(file.Metadata("Title", file.name)), rect); + renderText2(dc, from_u8(file.name), rect); rect.Offset(0, h / 2); rect.SetWidth(rect.GetWidth() / 2 - em); dc.SetFont(Label::Body_13); diff --git a/src/slic3r/GUI/MediaFilePanel.cpp b/src/slic3r/GUI/MediaFilePanel.cpp index dc23159a03..a12044f4e6 100644 --- a/src/slic3r/GUI/MediaFilePanel.cpp +++ b/src/slic3r/GUI/MediaFilePanel.cpp @@ -8,6 +8,8 @@ #include "Widgets/Label.hpp" #include "Printer/PrinterFileSystem.h" #include "MsgDialog.hpp" +#include +#include #ifdef __WXMSW__ #include @@ -34,7 +36,8 @@ MediaFilePanel::MediaFilePanel(wxWindow * parent) top_sizer->SetMinSize({-1, 75 * em_unit(this) / 10}); // Time group - m_time_panel = new ::StaticBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); + auto time_panel = new wxWindow(this, wxID_ANY); + m_time_panel = new ::StaticBox(time_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); m_time_panel->SetBackgroundColor(StateColor()); m_button_year = new ::Button(m_time_panel, _L("Year"), "", wxBORDER_NONE); m_button_month = new ::Button(m_time_panel, _L("Month"), "", wxBORDER_NONE); @@ -57,7 +60,10 @@ MediaFilePanel::MediaFilePanel(wxWindow * parent) time_sizer->Add(m_button_month, 0, wxALIGN_CENTER_VERTICAL); time_sizer->Add(m_button_all, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, 24); m_time_panel->SetSizer(time_sizer); - top_sizer->Add(m_time_panel, 1, wxEXPAND); + wxBoxSizer *time_sizer2 = new wxBoxSizer(wxHORIZONTAL); + time_sizer2->Add(m_time_panel, 1, wxEXPAND); + time_panel->SetSizer(time_sizer2); + top_sizer->Add(time_panel, 1, wxEXPAND); // File type StateColor background( @@ -197,16 +203,21 @@ void MediaFilePanel::SetMachineObject(MachineObject* obj) m_lan_mode = obj->is_lan_mode_printer(); m_lan_ip = obj->is_function_supported(PrinterFunction::FUNC_LOCAL_TUNNEL) ? obj->dev_ip : ""; m_lan_passwd = obj->get_access_code(); - m_tutk_support = obj->is_function_supported(PrinterFunction::FUNC_REMOTE_TUNNEL); + m_remote_support = obj->is_function_supported(PrinterFunction::FUNC_REMOTE_TUNNEL); } else { m_supported = false; m_lan_mode = false; m_lan_ip.clear(); m_lan_passwd.clear(); - m_tutk_support = false; + m_remote_support = false; } - if (machine == m_machine) + if (machine == m_machine) { + if (m_waiting_enable && IsEnabled()) { + auto fs = m_image_grid->GetFileSystem(); + if (fs) fs->Retry(); + } return; + } m_machine = machine; m_last_errors.clear(); auto fs = m_image_grid->GetFileSystem(); @@ -257,23 +268,24 @@ void MediaFilePanel::SetMachineObject(MachineObject* obj) switch (status) { 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; msg = _L("Connect failed [%d]!"); break; + case PrinterFileSystem::Failed: icon = m_bmp_failed; if (e.GetExtraLong() != 1) msg = _L("Connect failed [%d]!"); break; case PrinterFileSystem::ListSyncing: icon = m_bmp_loading; msg = _L("Loading file list..."); break; - case PrinterFileSystem::ListReady: icon = m_bmp_empty; msg = _L("No files"); break; + case PrinterFileSystem::ListReady: icon = m_bmp_empty; msg = _L("No files [%d]"); break; } - if (fs->GetCount() == 0) + if (fs->GetCount() == 0 && !msg.empty()) m_image_grid->SetStatus(icon, msg); if (e.GetInt() == PrinterFileSystem::Initializing) fetchUrl(boost::weak_ptr(fs)); - if ((status == PrinterFileSystem::Failed && m_last_errors.find(fs->GetLastError()) == m_last_errors.end()) - || status == PrinterFileSystem::ListReady) { + int err = fs->GetLastError(); + if ((status == PrinterFileSystem::Failed && m_last_errors.find(err) == m_last_errors.end()) || + status == PrinterFileSystem::ListReady) { json j; - j["code"] = fs->GetLastError(); + j["code"] = err; j["dev_id"] = m_machine; j["dev_ip"] = m_lan_ip; NetworkAgent* agent = wxGetApp().getAgent(); - if (status == PrinterFileSystem::Failed) { + if (status == PrinterFileSystem::Failed && err != 0) { j["result"] = "failed"; if (agent) agent->track_event("download_video_conn", j.dump()); @@ -390,17 +402,26 @@ void MediaFilePanel::fetchUrl(boost::weak_ptr wfs) { boost::shared_ptr fs(wfs.lock()); if (!fs || fs != m_image_grid->GetFileSystem()) return; - if (!m_lan_ip.empty()) { + if (!IsEnabled()) { + m_waiting_enable = true; + m_image_grid->SetStatus(m_bmp_failed, _L("Initialize failed (Device connection not ready)!")); + fs->SetUrl("0"); + return; + } + m_waiting_enable = false; + if (m_local_support && !m_lan_ip.empty()) { std::string url = "bambu:///local/" + m_lan_ip + ".?port=6000&user=" + m_lan_user + "&passwd=" + m_lan_passwd; fs->SetUrl(url); return; } - if (m_lan_mode ) { // not support tutk - m_image_grid->SetStatus(m_bmp_failed, _L("Not accessible in LAN-only mode!")); + if (m_lan_mode) { + m_image_grid->SetStatus(m_bmp_failed, _L("Initialize failed (Not accessible in LAN-only mode)!")); + fs->SetUrl("0"); return; } - if (!m_tutk_support) { // not support tutk - m_image_grid->SetStatus(m_bmp_failed, _L("Missing LAN ip of printer!")); + if (!m_remote_support && m_local_support) { // not support tutk + m_image_grid->SetStatus(m_bmp_failed, _L("Initialize failed (Missing LAN ip of printer)!")); + fs->SetUrl("1"); return; } NetworkAgent *agent = wxGetApp().getAgent(); @@ -411,10 +432,12 @@ void MediaFilePanel::fetchUrl(boost::weak_ptr wfs) CallAfter([this, url, wfs] { boost::shared_ptr fs(wfs.lock()); if (!fs || fs != m_image_grid->GetFileSystem()) return; - if (boost::algorithm::starts_with(url, "bambu:///")) + if (boost::algorithm::starts_with(url, "bambu:///")) { fs->SetUrl(url); - else - m_image_grid->SetStatus(m_bmp_failed, url.empty() ? _L("Network unreachable") : from_u8(url)); + } else { + m_image_grid->SetStatus(m_bmp_failed, wxString::Format(_L("Initialize failed (%s)!"), url.empty() ? _L("Network unreachable") : from_u8(url))); + fs->SetUrl("3"); + } }); }); } @@ -442,6 +465,13 @@ void Slic3r::GUI::MediaFilePanel::doAction(size_t index, int action) if (fs->GetFileType() == PrinterFileSystem::F_MODEL) { if (index != -1) { fs->FetchModel(index, [](std::string const & data) { + Slic3r::DynamicPrintConfig config; + Slic3r::Model model; + Slic3r::PlateDataPtrs plate_data_list; + Slic3r::Semver file_version; + std::istringstream is(data); + if (!Slic3r::load_gcode_3mf_from_stream(is, &config, &model, &plate_data_list, &file_version)) + return; // TODO: print gcode 3mf }); } diff --git a/src/slic3r/GUI/MediaFilePanel.h b/src/slic3r/GUI/MediaFilePanel.h index 666d70cb65..7e4e6451fb 100644 --- a/src/slic3r/GUI/MediaFilePanel.h +++ b/src/slic3r/GUI/MediaFilePanel.h @@ -81,7 +81,9 @@ private: std::string m_lan_passwd; bool m_supported = false; bool m_lan_mode = false; - bool m_tutk_support = false; + bool m_local_support = false; + bool m_remote_support = false; + bool m_waiting_enable = false; int m_last_mode = 0; int m_last_type = 0; diff --git a/src/slic3r/GUI/Monitor.cpp b/src/slic3r/GUI/Monitor.cpp index 16c74d6523..5596e4049a 100644 --- a/src/slic3r/GUI/Monitor.cpp +++ b/src/slic3r/GUI/Monitor.cpp @@ -210,7 +210,7 @@ MonitorPanel::~MonitorPanel() m_media_file_panel = new MediaFilePanel(m_tabpanel); m_tabpanel->AddPage(m_media_file_panel, _L("SD Card"), "", false); - m_tabpanel->AddPage(m_media_file_panel, _L("Internal Storage"), "", false); + //m_tabpanel->AddPage(m_media_file_panel, _L("Internal Storage"), "", false); m_upgrade_panel = new UpgradePanel(m_tabpanel); m_tabpanel->AddPage(m_upgrade_panel, _L("Update"), "", false); diff --git a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp index 45d4052b58..006f9ceecf 100644 --- a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp +++ b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp @@ -87,6 +87,8 @@ void PrinterFileSystem::SetFileType(FileType type, std::string const &storage) SendChangedEvent(EVT_FILE_CHANGED); if (type == F_INVALID_TYPE) return; + if (m_session.tunnel == nullptr) + return; m_status = Status::ListSyncing; SendChangedEvent(EVT_STATUS_CHANGED, m_status); ListAllFiles(); @@ -118,22 +120,33 @@ void PrinterFileSystem::ListAllFiles() req["type"] = types[m_file_type]; if (!m_file_storage.empty()) req["storage"] = m_file_storage; + req["api_version"] = 2; req["notify"] = "DETAIL"; - SendRequest(LIST_INFO, req, [this](json const& resp, FileList & list, auto) { + SendRequest(LIST_INFO, req, [this, type = m_file_type](json const& resp, FileList & list, auto) -> int { json files = resp["file_lists"]; for (auto& f : files) { std::string name = f["name"]; std::string path = f.value("path", ""); time_t time = f.value("time", 0); boost::uint64_t size = f["size"]; - File ff = {name, path, time, size, 0, default_thumbnail}; + if (type > F_TIMELAPSE && path.empty()) // Fix old printer that always return timelapses + return FILE_TYPE_ERR; + File ff = {name, path, time, size, 0}; list.push_back(ff); } return 0; }, [this, type = m_file_type](int result, FileList list) { + if (result != 0) { + m_last_error = result; + m_status = Status::ListReady; + SendChangedEvent(EVT_STATUS_CHANGED, m_status, "", result); + return 0; + } if (type != m_file_type) return 0; m_file_list.swap(list); + for (auto & file : m_file_list) + file.thumbnail = default_thumbnail; std::sort(m_file_list.begin(), m_file_list.end()); auto iter1 = m_file_list.begin(); auto end1 = m_file_list.end(); @@ -269,7 +282,7 @@ void PrinterFileSystem::FetchModel(size_t index, std::function= m_file_list.size()) return; auto &file = m_file_list[index]; - arr.push_back(file.path + "#_rel/.rels"); + arr.push_back(file.path + "#_rels/.rels"); arr.push_back(file.path + "#3D/3dmodel.model"); arr.push_back(file.path + "#Metadata/model_settings.config"); arr.push_back(file.path + "#Metadata/slice_info.config"); @@ -279,6 +292,7 @@ void PrinterFileSystem::FetchModel(size_t index, std::function( SUB_FILE, req, [](json const &resp, File &file, unsigned char const *data) -> int { @@ -302,7 +316,9 @@ size_t PrinterFileSystem::GetCount() const return m_group_mode == G_YEAR ? m_group_year.size() : m_group_month.size(); } -std::string PrinterFileSystem::File::Metadata(std::string const &key, std::string const & dflt) const +std::string PrinterFileSystem::File::Title() const { return Metadata("Title", name); } + +std::string PrinterFileSystem::File::Metadata(std::string const &key, std::string const &dflt) const { auto iter = metadata.find(key); return iter == metadata.end() || iter->second.empty() ? dflt : iter->second; @@ -662,7 +678,8 @@ void PrinterFileSystem::UpdateFocusThumbnail() if (names.empty() && paths.empty()) return; m_task_flags |= FF_THUMNAIL; - UpdateFocusThumbnail2(std::make_shared>(paths), paths.empty() ? OldThumbnail : m_file_type == F_MODEL ? ModelMetadata : VideoThumbnail); + UpdateFocusThumbnail2(std::make_shared>(paths.empty() ? names : paths), + paths.empty() ? OldThumbnail : m_file_type == F_MODEL ? ModelMetadata : VideoThumbnail); } bool PrinterFileSystem::ParseThumbnail(File &file) @@ -691,7 +708,8 @@ bool PrinterFileSystem::ParseThumbnail(File &file, std::istream &is) for (auto &plate : plate_data_list) { time += atof(plate->gcode_prediction.c_str()); weight += atof(plate->gcode_weight.c_str()); - file.metadata.emplace("plate_thumbnail_" + std::to_string(plate->plate_index), plate->thumbnail_file); + if (!plate->thumbnail_file.empty()) + file.metadata.emplace("plate_thumbnail_" + std::to_string(plate->plate_index), plate->thumbnail_file); } file.metadata.emplace("Title", model.model_info->model_name); file.metadata.emplace("Time", durationString(round(time))); @@ -699,10 +717,6 @@ bool PrinterFileSystem::ParseThumbnail(File &file, std::istream &is) auto thumbnail = model.model_info->metadata_items["Thumbnail"]; if (thumbnail.empty() && !plate_data_list.empty()) { thumbnail = plate_data_list.front()->thumbnail_file; - if (thumbnail.empty()) { - thumbnail = plate_data_list.front()->gcode_file; - boost::algorithm::replace_all(thumbnail, ".gcode", ".png"); - } } file.metadata.emplace("Thumbnail", thumbnail); return true; @@ -1082,8 +1096,8 @@ void PrinterFileSystem::Reconnect(boost::unique_lock &l, int resul if (m_stopped || m_messages.empty()) continue; std::string url = m_messages.front(); m_messages.clear(); - if (url.empty()) { - m_last_error = 1; + if (url.size() < 2) { + m_last_error = atoi(url.c_str()); } else { l.unlock(); m_status = Status::Connecting; @@ -1108,7 +1122,7 @@ void PrinterFileSystem::Reconnect(boost::unique_lock &l, int resul m_last_error = ret; } m_status = Status::Failed; - SendChangedEvent(EVT_STATUS_CHANGED, m_status); + SendChangedEvent(EVT_STATUS_CHANGED, m_status, "", url.size() < 2 ? 1 : 0); m_cond.timed_wait(l, boost::posix_time::seconds(10)); } m_status = Status::ListSyncing; diff --git a/src/slic3r/GUI/Printer/PrinterFileSystem.h b/src/slic3r/GUI/Printer/PrinterFileSystem.h index f6dfe0c1ce..255e2e3785 100644 --- a/src/slic3r/GUI/Printer/PrinterFileSystem.h +++ b/src/slic3r/GUI/Printer/PrinterFileSystem.h @@ -49,6 +49,7 @@ class PrinterFileSystem : public wxEvtHandler, public boost::enable_shared_from_ FILE_OPEN_ERR = 13, FILE_READ_ERR = 14, FILE_CHECK_ERR = 15, + FILE_TYPE_ERR = 16, }; @@ -104,7 +105,8 @@ public: bool IsSelect() const { return flags & FF_SELECT; } bool IsDownload() const { return flags & FF_DOWNLOAD; } - std::string Metadata(std::string const & key, std::string const & dflt) const; + std::string Title() const; + std::string Metadata(std::string const &key, std::string const &dflt) const; friend bool operator<(File const & l, File const & r) { return l.time > r.time; } };