mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-20 01:12:09 +00:00
Check timelapse storage space before sending a print
This commit is contained in:
@@ -1883,7 +1883,7 @@ bool SelectMachineDialog::check_sdcard_for_timelpase(MachineObject* obj)
|
||||
// must set to a status if return true
|
||||
if (m_checkbox_list["timelapse"]->IsShown() && m_checkbox_list["timelapse"]->getValue() == "on")
|
||||
{
|
||||
if (obj->GetStorage()->get_sdcard_state() == DevStorage::SdcardState::NO_SDCARD) {
|
||||
if (obj->GetStorage()->get_sdcard_state() == DevStorage::SdcardState::NO_SDCARD && !obj->m_has_timelapse_kit && !obj->is_support_internal_timelapse) {
|
||||
show_status(PrintDialogStatus::PrintStatusTimelapseNoSdcard);
|
||||
return true;
|
||||
}
|
||||
@@ -2083,6 +2083,9 @@ void SelectMachineDialog::show_status(PrintDialogStatus status, std::vector<wxSt
|
||||
msg = msg_text;
|
||||
Enable_Refresh_Button(true);
|
||||
Enable_Send_Button(true);
|
||||
} else if (status == PrintDialogStatus::PrintStatusTimelapseStorageLow) {
|
||||
Enable_Refresh_Button(true);
|
||||
Enable_Send_Button(true);
|
||||
} else if (status == PrintStatusToolHeadCoolingFanWarning) {
|
||||
Enable_Refresh_Button(true);
|
||||
Enable_Send_Button(true);
|
||||
@@ -2574,7 +2577,16 @@ void SelectMachineDialog::on_ok_btn(wxCommandEvent &event)
|
||||
}
|
||||
else
|
||||
{
|
||||
this->on_send_print();
|
||||
// if machine supports internal timelapse and timelapse is on, check storage first
|
||||
if (obj_->is_support_internal_timelapse &&
|
||||
m_checkbox_list["timelapse"]->IsShown() &&
|
||||
m_checkbox_list["timelapse"]->getValue() == "on" &&
|
||||
!m_timelapse_storage.empty())
|
||||
{
|
||||
start_timelapse_storage_check(obj_);
|
||||
} else {
|
||||
this->on_send_print();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2881,6 +2893,9 @@ void SelectMachineDialog::show_timelapse_folder_popup()
|
||||
m_timelapse_storage = val;
|
||||
update_timelapse_folder_btn_icon();
|
||||
if (m_timelapse_storage_popup) m_timelapse_storage_popup->Dismiss();
|
||||
DeviceManager* dev = wxGetApp().getDeviceManager();
|
||||
MachineObject* obj = dev ? dev->get_selected_machine() : nullptr;
|
||||
if (obj) check_timelapse_storage_warning(obj);
|
||||
};
|
||||
radio->Bind(wxEVT_LEFT_DOWN, on_select);
|
||||
text->Bind(wxEVT_LEFT_DOWN, on_select);
|
||||
@@ -2919,6 +2934,215 @@ void SelectMachineDialog::show_timelapse_folder_popup()
|
||||
m_timelapse_storage_popup->Popup();
|
||||
}
|
||||
|
||||
void SelectMachineDialog::check_timelapse_storage_warning(MachineObject* obj)
|
||||
{
|
||||
if (!obj || !obj->is_support_internal_timelapse) return;
|
||||
if (!m_checkbox_list["timelapse"]->IsShown()) return;
|
||||
if (m_checkbox_list["timelapse"]->getValue() != "on") return;
|
||||
if (m_timelapse_storage.empty()) return;
|
||||
|
||||
if (obj->is_timelapse_storage_low(m_timelapse_storage)) {
|
||||
wxString storage_name = (m_timelapse_storage == "internal") ? _L("Internal") : _L("External");
|
||||
wxString msg = wxString::Format(
|
||||
_L("%s space less than 20MB. Timelapse may not save properly. You can turn it off or"),
|
||||
storage_name);
|
||||
// show_status sets button state; then directly add with link callback
|
||||
show_status(PrintDialogStatus::PrintStatusTimelapseStorageLow);
|
||||
m_pre_print_checker.add_with_link(
|
||||
PrintDialogStatus::PrintStatusTimelapseStorageLow,
|
||||
msg,
|
||||
_L("Clean up files"),
|
||||
[this]() { navigate_to_timelapse_page(); });
|
||||
}
|
||||
}
|
||||
|
||||
void SelectMachineDialog::start_timelapse_storage_check(MachineObject* obj)
|
||||
{
|
||||
if (!obj) { on_send_print(); return; }
|
||||
|
||||
// get total layer count from the sliced result
|
||||
// Orca: PrintStatistics::Mode has no per-layer time vector (unlike the reference), so
|
||||
// derive the timelapse layer count from the sliced print objects instead.
|
||||
m_timelapse_total_layer = 0;
|
||||
if (m_print_type == PrintFromType::FROM_NORMAL) {
|
||||
PartPlate* plate = m_plater->get_partplate_list().get_curr_plate();
|
||||
if (plate && plate->fff_print()) {
|
||||
for (const PrintObject* po : plate->fff_print()->objects())
|
||||
m_timelapse_total_layer = std::max(m_timelapse_total_layer, (int)po->layer_count());
|
||||
}
|
||||
}
|
||||
|
||||
obj->timelapse_storage_check_done = false;
|
||||
obj->timelapse_storage_check_result = -1;
|
||||
obj->command_ipcam_check_timelapse_storage(m_timelapse_storage, m_timelapse_total_layer);
|
||||
|
||||
// start polling timer
|
||||
m_timelapse_check_elapsed_ms = 0;
|
||||
if (!m_timelapse_check_timer) {
|
||||
m_timelapse_check_timer = new wxTimer(this);
|
||||
Bind(wxEVT_TIMER, &SelectMachineDialog::on_timelapse_storage_check_timer, this, m_timelapse_check_timer->GetId());
|
||||
}
|
||||
m_timelapse_check_timer->Start(m_timelapse_check_interval_ms);
|
||||
}
|
||||
|
||||
void SelectMachineDialog::on_timelapse_storage_check_timer(wxTimerEvent& /*event*/)
|
||||
{
|
||||
m_timelapse_check_elapsed_ms += m_timelapse_check_interval_ms;
|
||||
|
||||
DeviceManager* dev = wxGetApp().getDeviceManager();
|
||||
MachineObject* obj = dev ? dev->get_selected_machine() : nullptr;
|
||||
|
||||
bool timed_out = m_timelapse_check_elapsed_ms >= m_timelapse_check_timeout_ms;
|
||||
bool done = obj && obj->timelapse_storage_check_done.load();
|
||||
|
||||
if (done || timed_out) {
|
||||
m_timelapse_check_timer->Stop();
|
||||
if (timed_out && !done) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "timelapse storage check timed out, proceeding with print";
|
||||
on_send_print();
|
||||
return;
|
||||
}
|
||||
on_timelapse_storage_check_result();
|
||||
}
|
||||
}
|
||||
|
||||
void SelectMachineDialog::on_timelapse_storage_check_result()
|
||||
{
|
||||
DeviceManager* dev = wxGetApp().getDeviceManager();
|
||||
MachineObject* obj = dev ? dev->get_selected_machine() : nullptr;
|
||||
if (!obj) { on_send_print(); return; }
|
||||
|
||||
// query failed -> ignore, proceed with print
|
||||
if (obj->timelapse_storage_check_result != 0) {
|
||||
BOOST_LOG_TRIVIAL(info) << "timelapse storage check failed (result=" << obj->timelapse_storage_check_result << "), proceeding";
|
||||
on_send_print();
|
||||
return;
|
||||
}
|
||||
|
||||
// space is enough -> proceed
|
||||
if (obj->timelapse_storage_is_enough) {
|
||||
on_send_print();
|
||||
return;
|
||||
}
|
||||
|
||||
// space not enough -> show dialog
|
||||
show_timelapse_storage_dialog(obj);
|
||||
}
|
||||
|
||||
void SelectMachineDialog::show_timelapse_storage_dialog(MachineObject* obj)
|
||||
{
|
||||
bool is_internal = (m_timelapse_storage == "internal");
|
||||
bool has_video_files = obj->timelapse_storage_file_count >= 0;
|
||||
// internal: 2 buttons (Confirm & Print, Cancel Timelapse)
|
||||
// external + has files: 3 buttons (Confirm & Print, Cancel Timelapse, Clean Up)
|
||||
// external + no files: 1 button (Cancel Timelapse only)
|
||||
bool show_confirm_btn = is_internal || has_video_files;
|
||||
bool show_cleanup_btn = !is_internal && has_video_files;
|
||||
|
||||
wxString body_text;
|
||||
if (is_internal)
|
||||
body_text = _L("Low internal storage. This timelapse will overwrite the oldest video files.");
|
||||
else if (has_video_files)
|
||||
body_text = _L("Low external storage. This timelapse will overwrite the oldest video files.");
|
||||
else
|
||||
body_text = _L("Insufficient external storage for time-lapse photography. Connect to computer to delete files, or use a larger memory card.");
|
||||
|
||||
wxDialog dlg(this, wxID_ANY, _L("Storage Space Not Enough"),
|
||||
wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE);
|
||||
dlg.SetBackgroundColour(*wxWHITE);
|
||||
|
||||
auto* main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// warning icon + text row
|
||||
auto* msg_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto* warn_bmp = new wxStaticBitmap(&dlg, wxID_ANY,
|
||||
create_scaled_bitmap("obj_warning", &dlg, 16), wxDefaultPosition, wxSize(FromDIP(16), FromDIP(16)));
|
||||
auto* msg_label = new Label(&dlg, body_text);
|
||||
msg_label->SetFont(Label::Body_14);
|
||||
msg_label->SetForegroundColour(wxColour(0x33, 0x33, 0x33));
|
||||
msg_label->Wrap(FromDIP(340));
|
||||
msg_sizer->Add(warn_bmp, 0, wxALIGN_TOP | wxRIGHT, FromDIP(6));
|
||||
msg_sizer->Add(msg_label, 1, wxEXPAND);
|
||||
main_sizer->Add(msg_sizer, 0, wxALL | wxEXPAND, FromDIP(20));
|
||||
|
||||
auto* btn_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
const int ID_CLEANUP = wxID_HIGHEST + 1; // distinct from wxID_CANCEL (X button)
|
||||
|
||||
// use int id to distinguish choices: wxID_OK=confirm, wxID_NO=cancel_tl, ID_CLEANUP=cleanup
|
||||
if (show_confirm_btn) {
|
||||
auto* btn_confirm = new Button(&dlg, _L("Confirm & Print"));
|
||||
// Orca: use the accent green rather than the reference's hard-coded confirm colour.
|
||||
StateColor confirm_bg(std::pair<wxColour, int>(wxColour(0, 150, 136), StateColor::Normal));
|
||||
btn_confirm->SetBackgroundColor(confirm_bg);
|
||||
btn_confirm->SetTextColor(StateColor(std::pair<wxColour, int>(*wxWHITE, StateColor::Normal)));
|
||||
btn_confirm->Bind(wxEVT_BUTTON, [&dlg](wxCommandEvent&) { dlg.EndModal(wxID_OK); });
|
||||
btn_sizer->Add(btn_confirm, 0, wxEXPAND | wxBOTTOM, FromDIP(8));
|
||||
}
|
||||
|
||||
auto* btn_cancel_tl = new Button(&dlg, _L("Cancel Timelapse & Print"));
|
||||
if (!show_confirm_btn) {
|
||||
// Orca: use the accent green rather than the reference's hard-coded confirm colour.
|
||||
StateColor cancel_bg(std::pair<wxColour, int>(wxColour(0, 150, 136), StateColor::Normal));
|
||||
btn_cancel_tl->SetBackgroundColor(cancel_bg);
|
||||
btn_cancel_tl->SetTextColor(StateColor(std::pair<wxColour, int>(*wxWHITE, StateColor::Normal)));
|
||||
}
|
||||
btn_cancel_tl->Bind(wxEVT_BUTTON, [&dlg](wxCommandEvent&) { dlg.EndModal(wxID_NO); });
|
||||
btn_sizer->Add(btn_cancel_tl, 0, wxEXPAND | (show_cleanup_btn ? wxBOTTOM : 0), FromDIP(8));
|
||||
|
||||
if (show_cleanup_btn) {
|
||||
auto* btn_cleanup = new Button(&dlg, _L("Clean Up"));
|
||||
btn_cleanup->Bind(wxEVT_BUTTON, [&dlg, ID_CLEANUP](wxCommandEvent&) { dlg.EndModal(ID_CLEANUP); });
|
||||
btn_sizer->Add(btn_cleanup, 0, wxEXPAND);
|
||||
}
|
||||
|
||||
main_sizer->Add(btn_sizer, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, FromDIP(20));
|
||||
dlg.SetSizer(main_sizer);
|
||||
dlg.Fit();
|
||||
dlg.CenterOnParent();
|
||||
|
||||
// ShowModal returns after the dialog closes; handle the action outside the modal stack.
|
||||
// wxID_CANCEL is returned when the user clicks X (close button) -> do nothing in that case.
|
||||
int result = dlg.ShowModal();
|
||||
|
||||
if (result == wxID_OK) {
|
||||
// Confirm & Print
|
||||
obj->command_ipcam_delete_oldest_timelapse(m_timelapse_storage, m_timelapse_total_layer);
|
||||
on_send_print();
|
||||
} else if (result == wxID_NO) {
|
||||
// Cancel Timelapse & Print
|
||||
m_checkbox_list["timelapse"]->setValue("off");
|
||||
on_send_print();
|
||||
} else if (result == ID_CLEANUP) {
|
||||
// Clean Up: close SelectMachineDialog and navigate
|
||||
navigate_to_timelapse_page();
|
||||
}
|
||||
// wxID_CANCEL (X button): do nothing, user dismissed the dialog
|
||||
}
|
||||
|
||||
void SelectMachineDialog::navigate_to_timelapse_page()
|
||||
{
|
||||
// EndModal closes the dialog; schedule navigation after the dialog is fully destroyed
|
||||
wxGetApp().CallAfter([]() {
|
||||
auto* main_frame = wxGetApp().mainframe;
|
||||
if (!main_frame) return;
|
||||
|
||||
// use existing jump_to_monitor to switch to Monitor tab
|
||||
main_frame->jump_to_monitor();
|
||||
|
||||
// then switch to Storage (Media) tab inside Monitor
|
||||
auto* monitor = dynamic_cast<MonitorPanel*>(main_frame->m_monitor);
|
||||
if (monitor) {
|
||||
auto* tabpanel = monitor->get_tabpanel();
|
||||
if (tabpanel) {
|
||||
tabpanel->SetSelection(MonitorPanel::PT_MEDIA);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this->EndModal(wxID_CANCEL);
|
||||
}
|
||||
|
||||
void SelectMachineDialog::on_send_print()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "print_job: on_ok to send";
|
||||
@@ -4441,6 +4665,7 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_)
|
||||
}
|
||||
}
|
||||
}
|
||||
check_timelapse_storage_warning(obj_);
|
||||
|
||||
// Orca: show warning if external filament does not match
|
||||
for (auto& m : m_ams_mapping_result) {
|
||||
|
||||
Reference in New Issue
Block a user