NEW: support send to sd card with cloud

JIRA: STUDIO-7378

Change-Id: I95fee50db29825e508d276d52c7a3e85e1347ebd
(cherry picked from commit 13db95ceb4f18b8cbce1e67447eeaa6ee36cc8ad)
This commit is contained in:
Kunlong Ma
2024-09-04 14:09:32 +08:00
committed by Noisyfox
parent dc494da0b3
commit 5a6cbc0a9a
6 changed files with 735 additions and 78 deletions

View File

@@ -40,18 +40,30 @@ 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 {
@@ -118,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)
@@ -249,6 +260,13 @@ 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)
{
@@ -672,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();
@@ -1178,6 +1206,194 @@ void PrinterFileSystem::DumpLog(void * thiz, int, tchar const *msg)
static_cast<PrinterFileSystem*>(thiz)->Bambu_FreeLogMsg(msg);
}
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) {
@@ -1185,7 +1401,7 @@ boost::uint32_t PrinterFileSystem::SendRequest(int type, json const &req, callba
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;
@@ -1199,7 +1415,7 @@ boost::uint32_t PrinterFileSystem::SendRequest(int type, json const &req, callba
}
// OutputDebugStringA(oss.str().c_str());
// OutputDebugStringA("\n");
auto msg = oss.str();
auto msg = oss.str();
boost::unique_lock l(m_mutex);
m_messages.push_back(msg);
m_callbacks.push_back(callback);
@@ -1240,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();
@@ -1273,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());
@@ -1356,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) {
@@ -1369,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);
}
}
}
}
@@ -1459,12 +1721,15 @@ void PrinterFileSystem::Reconnect(boost::unique_lock<boost::mutex> &l, int resul
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
}

View File

@@ -23,39 +23,52 @@ 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,
};
@@ -91,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
@@ -107,6 +136,7 @@ public:
};
struct Download;
struct Upload;
struct File
{
@@ -129,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();
@@ -198,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();
@@ -230,12 +284,14 @@ private:
typedef std::function<int(int, json const &resp, unsigned char const *data)> callback_t2;
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 &param = "")
{
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;
@@ -301,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;
@@ -310,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;
@@ -329,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;
@@ -338,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_