From 5fd1aa6e8b4fcc897a5191c25623201bbb20fc6a Mon Sep 17 00:00:00 2001 From: SoftFever Date: Thu, 16 Jul 2026 19:28:50 +0800 Subject: [PATCH] Add firmware print-option toggles to the print options dialog --- src/slic3r/GUI/DeviceCore/DevPrintOptions.cpp | 72 ++++ src/slic3r/GUI/DeviceCore/DevPrintOptions.h | 15 + src/slic3r/GUI/PrintOptionsDialog.cpp | 344 ++++++++++++++++++ src/slic3r/GUI/PrintOptionsDialog.hpp | 30 ++ 4 files changed, 461 insertions(+) diff --git a/src/slic3r/GUI/DeviceCore/DevPrintOptions.cpp b/src/slic3r/GUI/DeviceCore/DevPrintOptions.cpp index 7c1cf2ab15..1ba22b1091 100644 --- a/src/slic3r/GUI/DeviceCore/DevPrintOptions.cpp +++ b/src/slic3r/GUI/DeviceCore/DevPrintOptions.cpp @@ -1,6 +1,8 @@ #include "DevPrintOptions.h" #include "DevUtil.h" +#include + #include "slic3r/GUI/DeviceManager.hpp" namespace Slic3r @@ -356,5 +358,75 @@ int DevPrintOptions::command_set_filament_tangle_detect(bool filament_tangle_det return obj->publish_json(j); } +int DevPrintOptions::command_xcam_control_build_plate_align_detector(bool on_off) +{ + m_buildplate_align_detection.current_detect_value = on_off; + m_buildplate_align_detection.detect_hold_start = time(nullptr); + return command_xcam_control("plate_offset_switch", on_off, m_obj); +} + +int DevPrintOptions::command_xcam_control_fod_check(bool on_off) +{ + m_fod_check_detection.current_detect_value = on_off; + m_fod_check_detection.detect_hold_start = time(nullptr); + return command_xcam_control("fod_check", on_off, m_obj); +} + +int DevPrintOptions::command_xcam_control_displacement_detection(bool on_off) +{ + m_displacement_detection.current_detect_value = on_off; + m_displacement_detection.detect_hold_start = time(nullptr); + return command_xcam_control("model_movement_check", on_off, m_obj); +} + +int DevPrintOptions::command_xcam_control_purify_air_at_print_end(int on_off) +{ + m_purify_air_at_print_end.current_detect_value = on_off; + m_purify_air_at_print_end.detect_hold_start = time(nullptr); + return command_set_purify_air_at_print_end((PurifyAirAtPrintEndState) on_off, m_obj); +} + +int DevPrintOptions::command_set_purify_air_at_print_end(PurifyAirAtPrintEndState state, MachineObject *obj) +{ + json j; + j["print"]["command"] = "print_option"; + j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + switch (state) + { + case PurifyAirAtPrintEndState::PurifyAirDisable: j["print"]["air_purification"] = 0; break; + case PurifyAirAtPrintEndState::PurifyAirByInside: j["print"]["air_purification"] = 1; break; + case PurifyAirAtPrintEndState::PurifyAirByOutside: j["print"]["air_purification"] = 2; break; + default: assert(0); + } + return obj->publish_json(j); +} + +int DevPrintOptions::command_snapshot_control(int on_off) +{ + m_snapshot_detection.current_detect_value = on_off; + m_snapshot_detection.detect_hold_start = time(nullptr); + return command_set_snapshot_control(on_off, m_obj); +} + +int DevPrintOptions::command_set_snapshot_control(int on_off, MachineObject *obj) +{ + json j; + j["camera"]["command"] = "ipcam_cap_pic_set"; + j["camera"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + j["camera"]["control"] = on_off ? "enable" : "disable"; + return obj->publish_json(j); +} + +int DevPrintOptions::command_smart_nozzle_blob_detect_mode(int mode) +{ + json j; + j["print"]["command"] = "print_option"; + j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + j["print"]["nozzle_blob_detect_v2"] = mode; // int: 0=off, 1=on, 2=auto + m_smart_nozzle_blob_detection.current_detect_value = mode; + m_smart_nozzle_blob_detection.detect_hold_start = time(nullptr); + return m_obj->publish_json(j); +} + } // namespace Slic3r diff --git a/src/slic3r/GUI/DeviceCore/DevPrintOptions.h b/src/slic3r/GUI/DeviceCore/DevPrintOptions.h index fce5fcd456..dacaf6547e 100644 --- a/src/slic3r/GUI/DeviceCore/DevPrintOptions.h +++ b/src/slic3r/GUI/DeviceCore/DevPrintOptions.h @@ -43,6 +43,12 @@ class DevPrintOptions public: DevPrintOptions(MachineObject* obj); + // Tri-state for the purify-air-at-print-end firmware option (matches the MQTT air_purification value). + enum class PurifyAirAtPrintEndState : int { + PurifyAirDisable = 0, + PurifyAirByInside = 1, + PurifyAirByOutside = 2, + }; public: void SetPrintingSpeedLevel(DevPrintingSpeedLevel speed_level); @@ -60,6 +66,13 @@ public: int command_xcam_control_filament_tangle_detect(bool on_off); int command_xcam_control_idelheatingprotect_detector(bool on_off); + // Firmware print-option toggles (each gated on a fun2 capability bit). + int command_xcam_control_build_plate_align_detector(bool on_off); + int command_xcam_control_fod_check(bool on_off); + int command_xcam_control_displacement_detection(bool on_off); + int command_xcam_control_purify_air_at_print_end(int on_off); + int command_smart_nozzle_blob_detect_mode(int mode); // 0=off, 1=on, 2=auto + int command_snapshot_control(int on_off); int command_xcam_control(std::string module_name, bool on_off, MachineObject *obj ,std::string lvl = ""); // set print option @@ -70,6 +83,8 @@ public: int command_set_filament_tangle_detect(bool fliament_tangle_detect, MachineObject *obj); int command_set_against_continued_heating_mode(bool on_off); + int command_set_purify_air_at_print_end(PurifyAirAtPrintEndState state, MachineObject *obj); + int command_set_snapshot_control(int on_off, MachineObject *obj); void parse_auto_recovery_step_loss_status(int flag); void parse_allow_prompt_sound_status(int flag); diff --git a/src/slic3r/GUI/PrintOptionsDialog.cpp b/src/slic3r/GUI/PrintOptionsDialog.cpp index 39f115ed89..fa3e6e6dac 100644 --- a/src/slic3r/GUI/PrintOptionsDialog.cpp +++ b/src/slic3r/GUI/PrintOptionsDialog.cpp @@ -1,4 +1,5 @@ #include "PrintOptionsDialog.hpp" +#include #include "I18N.hpp" #include "GUI_App.hpp" #include "libslic3r/Utils.hpp" @@ -8,6 +9,8 @@ #include "DeviceCore/DevConfig.h" #include "DeviceCore/DevExtruderSystem.h" #include "DeviceCore/DevNozzleSystem.h" +#include "DeviceCore/DevFan.h" +#include "DeviceCore/DevPrintOptions.h" static const wxColour STATIC_BOX_LINE_COL = wxColour(238, 238, 238); static const wxColour STATIC_TEXT_CAPTION_COL = wxColour(100, 100, 100); @@ -183,6 +186,68 @@ PrintOptionsDialog::PrintOptionsDialog(wxWindow* parent) evt.Skip(); }); + // Firmware print-option toggles (each gated on a fun2 capability bit, published via DevPrintOptions). + m_cb_plate_align->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent& evt) { + if (obj) { obj->GetPrintOptions()->command_xcam_control_build_plate_align_detector(m_cb_plate_align->GetValue()); } + evt.Skip(); + }); + + m_cb_fod_check->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent& evt) { + if (obj) { obj->GetPrintOptions()->command_xcam_control_fod_check(m_cb_fod_check->GetValue()); } + evt.Skip(); + }); + + m_cb_displacement_detection->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent& evt) { + if (obj) { obj->GetPrintOptions()->command_xcam_control_displacement_detection(m_cb_displacement_detection->GetValue()); } + evt.Skip(); + }); + + m_cb_purify_air_at_print_end->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent& evt) { + if (obj) { obj->GetPrintOptions()->command_xcam_control_purify_air_at_print_end(m_cb_purify_air_at_print_end->GetValue()); } + evt.Skip(); + }); + + purify_air_switch_board->Bind(wxCUSTOMEVT_SWITCH_POS, [this](wxCommandEvent& evt) { + if (evt.GetInt() == 0) { + if (obj) { obj->GetPrintOptions()->command_xcam_control_purify_air_at_print_end((int) DevPrintOptions::PurifyAirAtPrintEndState::PurifyAirByOutside); } + } else if (evt.GetInt() == 1) { + if (obj) { obj->GetPrintOptions()->command_xcam_control_purify_air_at_print_end((int) DevPrintOptions::PurifyAirAtPrintEndState::PurifyAirByInside); } + } + evt.Skip(); + }); + + m_smart_nozzle_blob_mode_switch->Bind(wxCUSTOMEVT_MULTISWITCH_SELECTION, [this](wxCommandEvent& evt) { + if (!obj || !obj->GetPrintOptions()) { evt.Skip(); return; } + int sel = m_smart_nozzle_blob_mode_switch->GetSelection(); + // UI: 0=Auto, 1=On, 2=Off -> protocol: 0=off, 1=on, 2=auto + // Orca: the reference's Auto->On in-print confirmation is omitted here, as its + // stringing-prone helper is not available in this codebase. + int mode_map[] = {2, 1, 0}; + if (sel < 0 || sel > 2) { evt.Skip(); return; } + obj->GetPrintOptions()->command_smart_nozzle_blob_detect_mode(mode_map[sel]); + update_smart_nozzle_blob_mode_desc(sel); + evt.Skip(); + }); + + m_cb_snapshot_enable->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent& evt) { + if (!obj || !obj->GetPrintOptions()) { evt.Skip(); return; } + if (m_cb_snapshot_enable->GetValue()) { + wxString message = _L("When enabled, the printer will automatically capture photos of printed parts and upload them to the cloud. Would you like to enable this option?"); + wxString caption = _L("Confirm Enable Print Status Snapshot"); + wxMessageDialog dialog(this, message, caption, wxYES_NO | wxICON_QUESTION); + dialog.SetYesNoLabels(_L("Confirm"), _L("Cancel")); + if (dialog.ShowModal() == wxID_YES) { + obj->GetPrintOptions()->command_snapshot_control(true); + m_cb_snapshot_enable->SetValue(true); + } else { + m_cb_snapshot_enable->SetValue(false); + } + } else { + obj->GetPrintOptions()->command_snapshot_control(false); + } + evt.Skip(); + }); + wxGetApp().UpdateDlgDarkUI(this); } @@ -437,11 +502,45 @@ void PrintOptionsDialog::update_options(MachineObject* obj_) line7->Hide(); } + // Orca: firmware print-option toggles, each shown only when its fun2 capability bit is + // reported (Task 6 detection map). A printer reporting none of these bits renders + // the dialog exactly as before. + auto* print_opts = obj_->GetPrintOptions(); + auto show_row = [](bool support, std::initializer_list widgets, wxSizerItem* bottom_space) { + for (auto* w : widgets) { if (w) w->Show(support); } + if (bottom_space) bottom_space->Show(support); + }; + + auto* plate_align_opt = print_opts ? print_opts->GetDetectionOption(PrintOptionEnum::Buildplate_Align_Detection) : nullptr; + show_row(plate_align_opt && plate_align_opt->is_support_detect, {m_cb_plate_align, text_plate_align, text_plate_align_caption}, plate_align_bottom_space); + + auto* fod_opt = print_opts ? print_opts->GetDetectionOption(PrintOptionEnum::FOD_Check_Detection) : nullptr; + show_row(fod_opt && fod_opt->is_support_detect, {m_cb_fod_check, text_fod_check, text_fod_check_caption}, fod_check_bottom_space); + + auto* displacement_opt = print_opts ? print_opts->GetDetectionOption(PrintOptionEnum::Displacement_Detection) : nullptr; + show_row(displacement_opt && displacement_opt->is_support_detect, {m_cb_displacement_detection, text_displacement_detection, text_displacement_detection_caption}, displacement_bottom_space); + + auto* smart_blob_opt = print_opts ? print_opts->GetDetectionOption(PrintOptionEnum::Smart_Nozzle_Blob_Detection) : nullptr; + show_row(smart_blob_opt && smart_blob_opt->is_support_detect, {m_smart_nozzle_blob_mode_switch, text_smart_nozzle_blob, text_smart_nozzle_blob_mode_desc}, smart_nozzle_blob_bottom_space); + UpdateOptionSavePrintFileToStorage(obj_); UpdateOptionOpenDoorCheck(obj_); + UpdateOptionSnapshot(obj_); this->Freeze(); + if (plate_align_opt) m_cb_plate_align->SetValue(plate_align_opt->current_detect_value == 1); + if (fod_opt) m_cb_fod_check->SetValue(fod_opt->current_detect_value == 1); + if (displacement_opt) m_cb_displacement_detection->SetValue(displacement_opt->current_detect_value == 1); + if (smart_blob_opt && smart_blob_opt->is_support_detect) { + // Protocol: 0=off, 1=on, 2=auto -> UI: 0=Auto, 1=On, 2=Off + int mode = smart_blob_opt->current_detect_value; + int ui_map[] = {2, 1, 0}; + int ui_sel = (mode >= 0 && mode <= 2) ? ui_map[mode] : 0; + m_smart_nozzle_blob_mode_switch->SetSelection(ui_sel); + update_smart_nozzle_blob_mode_desc(ui_sel); + } + m_cb_first_layer->SetValue(obj_->xcam_first_layer_inspector); m_cb_plate_mark->SetValue(obj_->xcam_buildplate_marker_detector); m_cb_auto_recovery->SetValue(obj_->xcam_auto_recovery_step_loss); @@ -497,6 +596,7 @@ void PrintOptionsDialog::update_options(MachineObject* obj_) update_purgechutepileup_detection_status(); update_nozzleclumping_detection_status(); update_airprinting_detection_status(); + update_purify_air_at_print_end(obj_); this->Thaw(); @@ -562,6 +662,109 @@ void PrintOptionsDialog::UpdateOptionOpenDoorCheck(MachineObject *obj) open_door_switch_board->Show(); } +void PrintOptionsDialog::update_purify_air_at_print_end(MachineObject *obj_) +{ + if (!obj_ || !obj_->GetPrintOptions()) return; + + auto* opt = obj_->GetPrintOptions()->GetDetectionOption(PrintOptionEnum::Purify_Air_At_Print_End); + if (!opt || !opt->is_support_detect) { + purify_air_switch_board->Disable(); + m_cb_purify_air_at_print_end->Hide(); + text_purify_air->Hide(); + text_purify_air_context->Hide(); + purify_air_switch_board->Hide(); + if (purify_air_bottom_space) purify_air_bottom_space->Show(false); + return; + } + + if (purify_air_bottom_space) purify_air_bottom_space->Show(true); + m_cb_purify_air_at_print_end->Show(); + text_purify_air->Show(); + m_cb_purify_air_at_print_end->Enable(); + purify_air_switch_board->Enable(); + text_purify_air_context->SetForegroundColour(STATIC_TEXT_CAPTION_COL); + text_purify_air->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#262E30"))); + + // Orca: this codebase's AirDuctData has no IsExaustFanExit() helper, so the exhaust/chamber + // fan is detected inline by scanning the air-duct parts for the chamber-fan id. + bool has_exhaust_fan = false; + for (const auto& part : obj_->GetFan()->GetAirDuctData().parts) { + if (part.id == int(AIR_FUN::FAN_CHAMBER_0_IDX)) { has_exhaust_fan = true; break; } + } + + if (has_exhaust_fan) { + text_purify_air_context->SetLabel(_L("Purifies the chamber air as the print finishes, based on the selected mode.")); + purify_air_switch_board->Show(); + if (opt->current_detect_value != 0) { + m_cb_purify_air_at_print_end->SetValue(true); + purify_air_switch_board->updateState(opt->current_detect_value == 1 ? "left" : "right"); + purify_air_switch_board->Refresh(); + } else { + purify_air_switch_board->Disable(); + m_cb_purify_air_at_print_end->SetValue(false); + } + } else { + text_purify_air_context->SetLabel(_L("Purifies the chamber air through internal circulation as each print finishes.")); + purify_air_switch_board->Hide(); + m_cb_purify_air_at_print_end->SetValue(opt->current_detect_value != 0); + } + text_purify_air_context->Show(); + text_purify_air_context->Wrap(FromDIP(400)); + + // Orca: disabled while a print is running (matches the reference); the reference's + // "unavailable during the task" toast is not ported. + if (obj_->is_in_printing()) { + m_cb_purify_air_at_print_end->Disable(); + purify_air_switch_board->Disable(); + text_purify_air_context->SetForegroundColour(wxColour(170, 170, 170)); + text_purify_air->SetForegroundColour(wxColour(170, 170, 170)); + } +} + +void PrintOptionsDialog::update_smart_nozzle_blob_mode_desc(int selection) +{ + wxString desc; + switch (selection) { + case 0: // Auto + desc = _L("Automatically match the corresponding switch strategy for leak-prone filaments (disable blob detection) and regular filaments (enable blob detection)."); + break; + case 1: // On + desc = _L("Detect whether the nozzle is wrapped by filament or other foreign matter."); + break; + case 2: // Off + desc = _L("After disabling, nozzle wrapping cannot be detected, which may lead to print failure or nozzle damage."); + break; + default: + desc = _L("Detect whether the nozzle is wrapped by filament or other foreign matter."); + break; + } + text_smart_nozzle_blob_mode_desc->SetLabel(desc); + text_smart_nozzle_blob_mode_desc->Wrap(FromDIP(400)); +} + +void PrintOptionsDialog::UpdateOptionSnapshot(MachineObject *obj) +{ + if (!IsShown()) { return; } + + auto* opt = (obj && obj->GetPrintOptions()) ? obj->GetPrintOptions()->GetDetectionOption(PrintOptionEnum::Snapshot_Detection) : nullptr; + if (!opt || !opt->is_support_detect) { + m_cb_snapshot_enable->Show(false); + m_snapshot_sizer->Show(false); + Layout(); + return; + } + + if (!m_cb_snapshot_enable->IsShown()) { + m_cb_snapshot_enable->Show(true); + m_snapshot_sizer->Show(true); + Layout(); + } + + if (time(nullptr) - opt->detect_hold_start > HOLD_TIME_6SEC) { + m_cb_snapshot_enable->SetValue(opt->current_detect_value == 2); + } +} + wxBoxSizer* PrintOptionsDialog::create_settings_group(wxWindow* parent) { wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); @@ -951,6 +1154,125 @@ wxBoxSizer* PrintOptionsDialog::create_settings_group(wxWindow* parent) text_nozzle_blob_caption->Hide(); line7->Hide(); + // ---- Firmware print-option toggles (hidden until their fun2 capability bit is reported) ---- + + // Purify Air at Print End (tri-state: internal circulation / exhaust) + line_sizer = new wxBoxSizer(wxHORIZONTAL); + m_cb_purify_air_at_print_end = new CheckBox(parent); + text_purify_air = new Label(parent, _L("Purify Air at Print End")); + text_purify_air->SetFont(Label::Body_14); + text_purify_air_context = new Label(parent, wxEmptyString); + text_purify_air_context->SetFont(Label::Body_12); + text_purify_air_context->SetForegroundColour(STATIC_TEXT_CAPTION_COL); + purify_air_switch_board = new SwitchBoard(parent, _L("Internal Circulation"), _L("Exhaust"), wxSize(FromDIP(300), FromDIP(26))); + purify_air_switch_board->Disable(); + line_sizer->Add(FromDIP(5), 0, 0, 0); + line_sizer->Add(m_cb_purify_air_at_print_end, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + line_sizer->Add(text_purify_air, 1, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18)); + sizer->Add(text_purify_air_context, 0, wxLEFT, FromDIP(58)); + sizer->Add(purify_air_switch_board, 0, wxLEFT, FromDIP(58)); + purify_air_bottom_space = sizer->Add(0, 0, 0, wxTOP, FromDIP(15)); + m_cb_purify_air_at_print_end->Hide(); + text_purify_air->Hide(); + text_purify_air_context->Hide(); + purify_air_switch_board->Hide(); + purify_air_bottom_space->Show(false); + + // Build plate alignment detection + line_sizer = new wxBoxSizer(wxHORIZONTAL); + m_cb_plate_align = new CheckBox(parent); + text_plate_align = new Label(parent, _L("Alignment Detection")); + text_plate_align->SetFont(Label::Body_14); + line_sizer->Add(FromDIP(5), 0, 0, 0); + line_sizer->Add(m_cb_plate_align, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + line_sizer->Add(text_plate_align, 1, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18)); + line_sizer = new wxBoxSizer(wxHORIZONTAL); + text_plate_align_caption = new Label(parent, _L("Pauses printing when build plate misalignment is detected.")); + text_plate_align_caption->Wrap(FromDIP(400)); + text_plate_align_caption->SetFont(Label::Body_12); + text_plate_align_caption->SetForegroundColour(STATIC_TEXT_CAPTION_COL); + line_sizer->Add(FromDIP(38), 0, 0, 0); + line_sizer->Add(text_plate_align_caption, 1, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0)); + sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18)); + plate_align_bottom_space = sizer->Add(0, 0, 0, wxTOP, FromDIP(15)); + m_cb_plate_align->Hide(); + text_plate_align->Hide(); + text_plate_align_caption->Hide(); + plate_align_bottom_space->Show(false); + + // Foreign Object Detection + line_sizer = new wxBoxSizer(wxHORIZONTAL); + m_cb_fod_check = new CheckBox(parent); + text_fod_check = new Label(parent, _L("Foreign Object Detection")); + text_fod_check->SetFont(Label::Body_14); + line_sizer->Add(FromDIP(5), 0, 0, 0); + line_sizer->Add(m_cb_fod_check, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + line_sizer->Add(text_fod_check, 1, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18)); + line_sizer = new wxBoxSizer(wxHORIZONTAL); + text_fod_check_caption = new Label(parent, _L("Checks for any objects on the build plate at the start of a print to avoid collisions.")); + text_fod_check_caption->Wrap(FromDIP(400)); + text_fod_check_caption->SetFont(Label::Body_12); + text_fod_check_caption->SetForegroundColour(STATIC_TEXT_CAPTION_COL); + line_sizer->Add(FromDIP(38), 0, 0, 0); + line_sizer->Add(text_fod_check_caption, 1, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0)); + sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18)); + fod_check_bottom_space = sizer->Add(0, 0, 0, wxTOP, FromDIP(15)); + m_cb_fod_check->Hide(); + text_fod_check->Hide(); + text_fod_check_caption->Hide(); + fod_check_bottom_space->Show(false); + + // Printed Part Displacement Detection + line_sizer = new wxBoxSizer(wxHORIZONTAL); + m_cb_displacement_detection = new CheckBox(parent); + text_displacement_detection = new Label(parent, _L("Printed Part Displacement Detection")); + text_displacement_detection->SetFont(Label::Body_14); + line_sizer->Add(FromDIP(5), 0, 0, 0); + line_sizer->Add(m_cb_displacement_detection, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + line_sizer->Add(text_displacement_detection, 1, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18)); + line_sizer = new wxBoxSizer(wxHORIZONTAL); + text_displacement_detection_caption = new Label(parent, _L("Monitors the printed part during printing and alerts immediately if it shifts or collapses.")); + text_displacement_detection_caption->Wrap(FromDIP(400)); + text_displacement_detection_caption->SetFont(Label::Body_12); + text_displacement_detection_caption->SetForegroundColour(STATIC_TEXT_CAPTION_COL); + line_sizer->Add(FromDIP(38), 0, 0, 0); + line_sizer->Add(text_displacement_detection_caption, 1, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0)); + sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18)); + displacement_bottom_space = sizer->Add(0, 0, 0, wxTOP, FromDIP(15)); + m_cb_displacement_detection->Hide(); + text_displacement_detection->Hide(); + text_displacement_detection_caption->Hide(); + displacement_bottom_space->Show(false); + + // Smart nozzle clumping detection (tri-mode: Auto / On / Off) + line_sizer = new wxBoxSizer(wxHORIZONTAL); + text_smart_nozzle_blob = new Label(parent, _L("Nozzle Clumping Detection")); + text_smart_nozzle_blob->SetFont(Label::Body_14); + line_sizer->Add(FromDIP(5), 0, 0, 0); + line_sizer->Add(text_smart_nozzle_blob, 1, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18)); + line_sizer = new wxBoxSizer(wxHORIZONTAL); + text_smart_nozzle_blob_mode_desc = new Label(parent, _L("Checks if the nozzle is clumping by filament or other foreign objects.")); + text_smart_nozzle_blob_mode_desc->SetFont(Label::Body_12); + text_smart_nozzle_blob_mode_desc->Wrap(FromDIP(400)); + text_smart_nozzle_blob_mode_desc->SetForegroundColour(STATIC_TEXT_CAPTION_COL); + line_sizer->Add(FromDIP(5), 0, 0, 0); + line_sizer->Add(text_smart_nozzle_blob_mode_desc, 1, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18)); + m_smart_nozzle_blob_mode_switch = new MultiSwitchButton(parent); + m_smart_nozzle_blob_mode_switch->SetOptions({_L("Auto"), _L("On"), _L("Off")}); + m_smart_nozzle_blob_mode_switch->SetSelection(0); + sizer->Add(m_smart_nozzle_blob_mode_switch, 0, wxLEFT, FromDIP(30)); + smart_nozzle_blob_bottom_space = sizer->Add(0, 0, 0, wxTOP, FromDIP(15)); + text_smart_nozzle_blob->Hide(); + m_smart_nozzle_blob_mode_switch->Hide(); + text_smart_nozzle_blob_mode_desc->Hide(); + smart_nozzle_blob_bottom_space->Show(false); + //Open Door Detection line_sizer = new wxBoxSizer(wxHORIZONTAL); m_cb_open_door = new CheckBox(parent); @@ -966,6 +1288,28 @@ wxBoxSizer* PrintOptionsDialog::create_settings_group(wxWindow* parent) sizer->Add(open_door_switch_board, 0, wxLEFT, FromDIP(58)); sizer->Add(0, 0, 0, wxTOP, FromDIP(15)); + // Print status snapshot (enable path shows a confirm prompt; hidden until supported) + m_snapshot_sizer = new wxBoxSizer(wxVERTICAL); + line_sizer = new wxBoxSizer(wxHORIZONTAL); + m_cb_snapshot_enable = new CheckBox(parent); + Label* text_snapshot = new Label(parent, _L("Print Status Snapshot")); + text_snapshot->SetFont(Label::Body_14); + line_sizer->Add(FromDIP(5), 0, 0, 0); + line_sizer->Add(m_cb_snapshot_enable, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + line_sizer->Add(text_snapshot, 1, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + m_snapshot_sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18)); + line_sizer = new wxBoxSizer(wxHORIZONTAL); + Label* text_snapshot_caption = new Label(parent, _L("Automatically capture and upload print photos, showing defects during printing and the final result for remote viewing.")); + text_snapshot_caption->Wrap(FromDIP(400)); + text_snapshot_caption->SetFont(Label::Body_12); + text_snapshot_caption->SetForegroundColour(STATIC_TEXT_CAPTION_COL); + line_sizer->Add(FromDIP(38), 0, 0, 0); + line_sizer->Add(text_snapshot_caption, 1, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0)); + m_snapshot_sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18)); + sizer->Add(m_snapshot_sizer, 0, wxEXPAND | wxRIGHT, FromDIP(18)); + m_cb_snapshot_enable->Hide(); + m_snapshot_sizer->Show(false); + ai_monitoring_level_list->Connect(wxEVT_COMBOBOX, wxCommandEventHandler(PrintOptionsDialog::set_ai_monitor_sensitivity), NULL, this); // refine printer function options diff --git a/src/slic3r/GUI/PrintOptionsDialog.hpp b/src/slic3r/GUI/PrintOptionsDialog.hpp index 9fd6a5c83d..48f9f25b48 100644 --- a/src/slic3r/GUI/PrintOptionsDialog.hpp +++ b/src/slic3r/GUI/PrintOptionsDialog.hpp @@ -20,6 +20,7 @@ // Previous definitions class SwitchBoard; +class MultiSwitchButton; namespace Slic3r { namespace GUI { @@ -89,6 +90,12 @@ protected: CheckBox* m_cb_filament_tangle; CheckBox* m_cb_nozzle_blob; CheckBox* m_cb_open_door; + // Firmware print-option toggles (each gated on a fun2 capability bit). + CheckBox* m_cb_plate_align{nullptr}; + CheckBox* m_cb_fod_check{nullptr}; + CheckBox* m_cb_displacement_detection{nullptr}; + CheckBox* m_cb_purify_air_at_print_end{nullptr}; + CheckBox* m_cb_snapshot_enable{nullptr}; Label* text_first_layer; Label* text_ai_detections; Label* text_ai_detections_caption; @@ -99,6 +106,13 @@ protected: wxSizerItem *purgechutepileup_bottom_space; wxSizerItem *nozzleclumping_bottom_space; wxSizerItem *airprinting_bottom_space; + // Trailing spacers for the firmware print-option rows, toggled with their rows so a hidden + // row reserves no space (dialog renders as before when the option is unsupported). + wxSizerItem *purify_air_bottom_space{nullptr}; + wxSizerItem *plate_align_bottom_space{nullptr}; + wxSizerItem *fod_check_bottom_space{nullptr}; + wxSizerItem *displacement_bottom_space{nullptr}; + wxSizerItem *smart_nozzle_blob_bottom_space{nullptr}; Label * text_ai_monitoring; Label * text_ai_monitoring_caption; @@ -132,6 +146,16 @@ protected: Label* text_nozzle_blob; Label* text_nozzle_blob_caption; Label* text_open_door; + Label* text_plate_align{nullptr}; + Label* text_plate_align_caption{nullptr}; + Label* text_fod_check{nullptr}; + Label* text_fod_check_caption{nullptr}; + Label* text_displacement_detection{nullptr}; + Label* text_displacement_detection_caption{nullptr}; + Label* text_purify_air{nullptr}; + Label* text_purify_air_context{nullptr}; + Label* text_smart_nozzle_blob{nullptr}; + Label* text_smart_nozzle_blob_mode_desc{nullptr}; StaticLine* line1; StaticLine* line2; StaticLine* line3; @@ -140,6 +164,9 @@ protected: StaticLine* line6; StaticLine* line7; SwitchBoard* open_door_switch_board; + SwitchBoard* purify_air_switch_board{nullptr}; + MultiSwitchButton* m_smart_nozzle_blob_mode_switch{nullptr}; + wxBoxSizer* m_snapshot_sizer{nullptr}; wxBoxSizer* create_settings_group(wxWindow* parent); wxPanel *m_line; @@ -157,6 +184,8 @@ public: void update_purgechutepileup_detection_status(); void update_nozzleclumping_detection_status(); void update_airprinting_detection_status(); + void update_purify_air_at_print_end(MachineObject *obj_); + void update_smart_nozzle_blob_mode_desc(int selection); MachineObject *obj { nullptr }; @@ -184,6 +213,7 @@ public: private: void UpdateOptionSavePrintFileToStorage(MachineObject *obj); void UpdateOptionOpenDoorCheck(MachineObject *obj); + void UpdateOptionSnapshot(MachineObject *obj); }; }} // namespace Slic3r::GUI