ENH:Add thermal precondition

jira:[STUDIO-13970][STUDIO-13904]

Change-Id: I4b4fa27da1a65e0019c5f4c1dcc099c92189bf50
(cherry picked from commit 92dbde8385fec9719e0e9cfde764421793decd4d)
This commit is contained in:
milk 2025-08-30 20:18:58 +08:00 committed by Noisyfox
parent 69ecdeba71
commit ffab04262d
11 changed files with 302 additions and 19 deletions

View file

@ -220,6 +220,8 @@ set(SLIC3R_GUI_SOURCES
GUI/GUI_Utils.hpp
GUI/HintNotification.cpp
GUI/HintNotification.hpp
GUI/ThermalPreconditioningDialog.cpp
GUI/ThermalPreconditioningDialog.hpp
GUI/HMS.cpp
GUI/HMS.hpp
GUI/HMSPanel.cpp

View file

@ -3308,7 +3308,11 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
}
}
if (jj.contains("stg_cur")) {
stage_curr = jj["stg_cur"].get<int>();
stage_curr = jj["stg_cur"].get<int>();
}
if (jj.contains("stg_cd")) {
stage_remaining_seconds = jj["stg_cd"].get<int>();
}
}
catch (...) {

View file

@ -446,6 +446,7 @@ public:
std::vector<int> stage_list_info;
int stage_curr = 0;
int stage_remaining_seconds = 0;
int m_push_count = 0;
int m_full_msg_count = 0; /*the full message count, there are full or diff messages from network*/
bool calibration_done { false };
@ -456,6 +457,7 @@ public:
wxString get_curr_stage();
int get_curr_stage_idx();
int get_stage_remaining_seconds() const { return stage_remaining_seconds; }
bool is_in_calibration();
bool is_calibration_running();

View file

@ -1,5 +1,4 @@
#include "StatusPanel.hpp"
#include "I18N.hpp"
#include "Widgets/Label.hpp"
#include "Widgets/Button.hpp"
@ -40,6 +39,8 @@
#include "PrintOptionsDialog.hpp"
#include "SafetyOptionsDialog.hpp"
#include "ThermalPreconditioningDialog.hpp"
namespace Slic3r { namespace GUI {
@ -508,6 +509,7 @@ PrintingTaskPanel::PrintingTaskPanel(wxWindow* parent, PrintingTaskType type)
: wxPanel(parent, wxID_ANY,wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL)
{
m_type = type;
m_question_button = nullptr;
create_panel(this);
SetBackgroundColour(*wxWHITE);
m_bitmap_background = ScalableBitmap(this, "thumbnail_grid", m_bitmap_thumbnail->GetSize().y);
@ -517,6 +519,10 @@ PrintingTaskPanel::PrintingTaskPanel(wxWindow* parent, PrintingTaskType type)
PrintingTaskPanel::~PrintingTaskPanel()
{
if (m_question_button) {
delete m_question_button;
m_question_button = nullptr;
}
}
void PrintingTaskPanel::create_panel(wxWindow* parent)
@ -589,17 +595,6 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
bSizer_task_name->Add(task_name_panel, 0, wxEXPAND, FromDIP(5));
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));
#ifdef __WXOSX_MAC__
m_printing_stage_value->SetFont(::Label::Body_11);
#else
m_printing_stage_value->SetFont(wxFont(11, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC")));
#endif
m_printing_stage_value->SetForegroundColour(STAGE_TEXT_COL);
m_staticText_profile_value = new wxStaticText(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT | wxST_ELLIPSIZE_END);
m_staticText_profile_value->Wrap(-1);
@ -724,16 +719,86 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
bSizer_text->Add(0, 0, 0, wxLEFT, FromDIP(20));
bSizer_text->Add(m_staticText_progress_left, 0, wxALIGN_CENTER_VERTICAL | wxALL, 0);
m_printing_stage_value = new wxStaticText(penel_finish_time, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT | wxST_ELLIPSIZE_END);
m_printing_stage_value->Wrap(-1);
m_printing_stage_value->SetMaxSize(wxSize(FromDIP(800), -1));
#ifdef __WXOSX_MAC__
m_printing_stage_value->SetFont(::Label::Body_11);
#else
m_printing_stage_value->SetFont(wxFont(11, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC")));
#endif
m_printing_stage_value->SetForegroundColour(STAGE_TEXT_COL);
m_printing_stage_value->Bind(wxEVT_LEFT_UP, &PrintingTaskPanel::on_stage_clicked, this);
m_printing_stage_value->Bind(wxEVT_ENTER_WINDOW, [this](wxMouseEvent &event) {
auto *dev_manager = wxGetApp().getDeviceManager();
MachineObject *obj = dev_manager ? dev_manager->get_selected_machine() : nullptr;
if (obj && obj->stage_curr == 58) {
m_printing_stage_value->SetCursor(wxCursor(wxCURSOR_HAND));
wxFont font = m_printing_stage_value->GetFont();
font.SetUnderlined(true);
m_printing_stage_value->SetFont(font);
}
event.Skip();
});
m_printing_stage_value->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent &event) {
auto *dev_manager = wxGetApp().getDeviceManager();
MachineObject *obj = dev_manager ? dev_manager->get_selected_machine() : nullptr;
if (obj && obj->stage_curr == 58) {
m_printing_stage_value->SetCursor(wxCURSOR_ARROW);
wxFont font = m_printing_stage_value->GetFont();
font.SetUnderlined(false);
m_printing_stage_value->SetFont(font);
}
event.Skip();
});
// penel_text->SetMaxSize(wxSize(FromDIP(600), -1));
penel_text->SetSizer(bSizer_text);
penel_text->Layout();
// Create question button
m_question_button = new ScalableButton(penel_finish_time, wxID_ANY, "thermal_question", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true);
m_question_button->SetToolTip(_L("Click to view thermal preconditioning explanation"));
m_question_button->Hide(); // Hide by default
m_question_button->Bind(wxEVT_LEFT_UP, &PrintingTaskPanel::on_stage_clicked, this);
m_question_button->Bind(wxEVT_ENTER_WINDOW, [this](wxMouseEvent &event) {
auto *dev_manager = wxGetApp().getDeviceManager();
MachineObject *obj = dev_manager ? dev_manager->get_selected_machine() : nullptr;
if (obj && obj->stage_curr == 58) {
m_question_button->SetCursor(wxCursor(wxCURSOR_HAND));
if (m_printing_stage_value) {
wxFont f = m_printing_stage_value->GetFont();
f.SetUnderlined(true);
m_printing_stage_value->SetFont(f);
}
}
event.Skip();
});
m_question_button->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent &event) {
auto *dev_manager = wxGetApp().getDeviceManager();
MachineObject *obj = dev_manager ? dev_manager->get_selected_machine() : nullptr;
if (obj && obj->stage_curr == 58) {
m_question_button->SetCursor(wxCURSOR_ARROW);
if (m_printing_stage_value) {
wxFont f = m_printing_stage_value->GetFont();
f.SetUnderlined(false);
m_printing_stage_value->SetFont(f);
}
event.Skip();
}
});
// 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(m_printing_stage_value, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 0);
bSizer_finish_time->Add(m_question_button, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(5));
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));
@ -746,6 +811,9 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
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));
@ -769,7 +837,6 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
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(progress_lr_panel, 0, wxEXPAND | wxTOP, FromDIP(5));
m_printing_sizer = new wxBoxSizer(wxHORIZONTAL);
@ -1077,6 +1144,39 @@ void PrintingTaskPanel::update_stage_value(wxString stage, int val)
m_gauge_progress->SetValue(val);
}
void PrintingTaskPanel::update_stage_value_with_machine(wxString stage, int val, MachineObject *obj)
{
m_gauge_progress->SetValue(val);
// m_printing_stage_value->SetLabelText(stage);
m_printing_stage_value->SetLabelText(" Thermal Preconditioning for first layeroptimization");
if (obj && obj->stage_curr == 58) {
// Show English text for thermal preconditioning
m_printing_stage_value->SetForegroundColour(wxColour(146, 146, 146)); // Gray color, indicates clickable
m_printing_stage_value->SetCursor(wxCursor(wxCURSOR_HAND));
m_question_button->Show(); // Show question button
} else {
m_printing_stage_value->SetLabelText(stage);
m_printing_stage_value->SetForegroundColour(STAGE_TEXT_COL);
m_printing_stage_value->SetCursor(wxCURSOR_ARROW);
m_question_button->Hide(); // Hide question button
}
}
void PrintingTaskPanel::on_stage_clicked(wxMouseEvent &event)
{
auto *dev_manager = wxGetApp().getDeviceManager();
MachineObject *obj = dev_manager ? dev_manager->get_selected_machine() : nullptr;
if (obj && obj->stage_curr == 58) {
wxWindow *top = wxGetTopLevelParent(this);
ThermalPreconditioningDialog m_thermal_dialog(top ? top : this, obj->get_dev_id() , "Calculating...");
m_thermal_dialog.ShowModal();
}
event.Skip();
}
void PrintingTaskPanel::update_progress_percent(wxString percent, wxString icon)
{
m_staticText_progress_percent->SetLabelText(percent);
@ -3641,7 +3741,7 @@ void StatusPanel::update_subtask(MachineObject *obj)
if (obj->gcode_file_prepare_percent >= 0 && obj->gcode_file_prepare_percent <= 100 && show_percent)
prepare_text += wxString::Format("(%d%%)", obj->gcode_file_prepare_percent);
m_project_task_panel->update_stage_value(prepare_text, 0);
m_project_task_panel->update_stage_value_with_machine(prepare_text, 0, obj);
m_project_task_panel->update_progress_percent(NA_STR, wxEmptyString);
m_project_task_panel->update_left_time(NA_STR);
m_project_task_panel->update_layers_num(true, wxString::Format(_L("Layer: %s"), NA_STR));
@ -3665,12 +3765,12 @@ void StatusPanel::update_subtask(MachineObject *obj)
// update printing stage
m_project_task_panel->update_left_time(obj->mc_left_time);
if (obj->subtask_) {
m_project_task_panel->update_stage_value(obj->get_curr_stage(), obj->subtask_->task_progress);
m_project_task_panel->update_stage_value_with_machine(obj->get_curr_stage(), obj->subtask_->task_progress, obj);
m_project_task_panel->update_progress_percent(wxString::Format("%d", obj->subtask_->task_progress), "%");
m_project_task_panel->update_layers_num(true, wxString::Format(_L("Layer: %d/%d"), obj->curr_layer, obj->total_layers));
} else {
m_project_task_panel->update_stage_value(obj->get_curr_stage(), 0);
m_project_task_panel->update_stage_value_with_machine(obj->get_curr_stage(), 0, obj);
m_project_task_panel->update_progress_percent(NA_STR, wxEmptyString);
m_project_task_panel->update_layers_num(true, wxString::Format(_L("Layer: %s"), NA_STR));
}
@ -3842,7 +3942,9 @@ void StatusPanel::reset_printing_values()
m_project_task_panel->reset_printing_value();
m_project_task_panel->update_subtask_name(NA_STR);
m_project_task_panel->show_profile_info(false);
m_project_task_panel->update_stage_value(wxEmptyString, 0);
// m_project_task_panel->update_stage_value_with_machine(wxEmptyString, 0, obj);
m_project_task_panel->update_stage_value_with_machine(wxEmptyString, 0, obj);
//obj->get_curr_stage()
m_project_task_panel->update_progress_percent(NA_STR, wxEmptyString);
m_project_task_panel->market_scoring_hide();

View file

@ -7,6 +7,7 @@
#include "SliceInfoPanel.hpp"
#include "CameraPopup.hpp"
#include "GUI.hpp"
#include "ThermalPreconditioningDialog.hpp"
#include <wx/panel.h>
#include <wx/bitmap.h>
#include <wx/image.h>
@ -280,6 +281,7 @@ private:
wxStaticText* m_staticText_consumption_of_time;
wxStaticText* m_staticText_consumption_of_weight;
wxStaticText* m_printing_stage_value;
ScalableButton* m_question_button;
wxStaticText* m_staticText_profile_value;
wxStaticText* m_staticText_progress_percent;
wxStaticText* m_staticText_progress_percent_icon;
@ -330,6 +332,11 @@ public:
void enable_abort_button(bool enable);
void update_subtask_name(wxString name);
void update_stage_value(wxString stage, int val);
void update_stage_value_with_machine(wxString stage, int val, MachineObject* obj = nullptr);
void on_stage_clicked(wxMouseEvent& event);
// Public interface to update remaining time text in the thermal dialog
void update_thermal_remaining_time(MachineObject* obj);
void update_progress_percent(wxString percent, wxString icon);
void update_left_time(wxString time);
void update_left_time(int mc_left_time);

View file

@ -0,0 +1,106 @@
#include "ThermalPreconditioningDialog.hpp"
#include "I18N.hpp"
#include "GUI_App.hpp"
#include "wxExtensions.hpp"
#include "DeviceManager.hpp"
#include "DeviceCore/DevManager.h"
namespace Slic3r {
BEGIN_EVENT_TABLE(ThermalPreconditioningDialog, wxDialog)
EVT_BUTTON(wxID_OK, ThermalPreconditioningDialog::on_ok_clicked)
END_EVENT_TABLE()
ThermalPreconditioningDialog::ThermalPreconditioningDialog(wxWindow* parent, std::string dev_id,const wxString& remaining_time)
: wxDialog(parent, wxID_ANY, _L("Thermal Preconditioning for first layer optimization"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
, m_dev_id(dev_id)
, m_refresh_timer(this)
{
wxBitmap bitmap = create_scaled_bitmap("thermal_preconditioning_title", this, 16);
wxIcon icon;
icon.CopyFromBitmap(bitmap);
SetIcon(icon);
create_ui();
Bind(wxEVT_TIMER, &ThermalPreconditioningDialog::on_timer, this);
m_refresh_timer.Start(1000);
// Set remaining time
if (!remaining_time.IsEmpty()) {
m_remaining_time_label->SetLabelText(wxString::Format(_L("Remaining time: %s"), remaining_time));
} else {
m_remaining_time_label->SetLabelText(_L("Remaining time: Calculating..."));
}
// Set dialog size and position
SetSize(wxSize(FromDIP(400), FromDIP(200)));
CentreOnScreen();
}
void ThermalPreconditioningDialog::create_ui()
{
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
// Remaining time label
m_remaining_time_label = new wxStaticText(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
wxFont time_font = m_remaining_time_label->GetFont();
time_font.SetPointSize(14);
time_font.SetWeight(wxFONTWEIGHT_BOLD);
m_remaining_time_label->SetFont(time_font);
m_remaining_time_label->SetForegroundColour(wxColour(50, 58, 61));
// Explanation text
m_explanation_label = new wxStaticText(this, wxID_ANY,
_L("The heated bed's thermal preconditioning helps optimize the first layer print quality. Printing will start once preconditioning is complete."),
wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
m_explanation_label->Wrap(FromDIP(350));
m_explanation_label->SetForegroundColour(wxColour(50, 58, 61));
// OK button
m_ok_button = new wxButton(this, wxID_OK, _L("OK"));
m_ok_button->SetBackgroundColour(wxColour("#B6F34F"));
m_ok_button->SetForegroundColour(wxColour("#000000"));
m_ok_button->SetMinSize(wxSize(FromDIP(80), FromDIP(32)));
// Layout
main_sizer->Add(0, 0, 1, wxEXPAND);
main_sizer->Add(m_remaining_time_label, 0, wxALIGN_CENTER);
main_sizer->Add(m_explanation_label, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, FromDIP(25));
main_sizer->Add(0, 0, 1, wxEXPAND);
main_sizer->Add(m_ok_button, 0, wxALIGN_RIGHT | wxRIGHT | wxBOTTOM, FromDIP(20));
SetSizer(main_sizer);
}
void ThermalPreconditioningDialog::on_ok_clicked(wxCommandEvent& event)
{
EndModal(wxID_OK);
}
void ThermalPreconditioningDialog::update_thermal_remaining_time()
{
DeviceManager *dev = Slic3r::GUI::wxGetApp().getDeviceManager();
if (!dev) return;
MachineObject * m_obj = dev->get_my_machine(m_dev_id);
int remaining_seconds = m_obj->get_stage_remaining_seconds();
wxString remaining_time;
if (remaining_seconds >= 0) {
int minutes = remaining_seconds/60;
int seconds = remaining_seconds % 60;
remaining_time = wxString::Format("Remaining time: %dmin%ds", minutes, seconds);
}
set_remaining_time_text(remaining_time);
}
void ThermalPreconditioningDialog::on_timer(wxTimerEvent &event) {
DeviceManager *dev = Slic3r::GUI::wxGetApp().getDeviceManager();
if (!dev) return;
MachineObject *m_obj = dev->get_my_machine(m_dev_id);
if (IsShown() && m_obj && m_obj->stage_curr == 58) {
update_thermal_remaining_time();
} else {
m_refresh_timer.Stop();
}
}
} // namespace Slic3r

View file

@ -0,0 +1,40 @@
#pragma once
#include <wx/wx.h>
#include <wx/dialog.h>
#include <wx/stattext.h>
#include <wx/button.h>
#include <wx/sizer.h>
#include <wx/statbmp.h>
namespace Slic3r {
class MachineObject;
class ThermalPreconditioningDialog : public wxDialog
{
public:
ThermalPreconditioningDialog(wxWindow *parent, std::string dev_id, const wxString &remaining_time);
~ThermalPreconditioningDialog() = default;
// Allow external updates of remaining time text
void set_remaining_time_text(const wxString& text) { if (m_remaining_time_label) m_remaining_time_label->SetLabelText(text); }
void update_thermal_remaining_time();
private:
void create_ui();
void on_ok_clicked(wxCommandEvent& event);
void on_timer(wxTimerEvent &event);
std::string m_dev_id;
wxTimer m_refresh_timer;
wxStaticText* m_remaining_time_label;
wxStaticText* m_explanation_label;
wxButton* m_ok_button;
wxStaticBitmap* m_title_bitmap;
DECLARE_EVENT_TABLE()
};
} // namespace Slic3r

View file

@ -573,7 +573,6 @@ void MachineInfoPanel::update(MachineObject* obj)
void MachineInfoPanel::update_version_text(MachineObject* obj)
{
if (obj->upgrade_display_state == DevFirmwareUpgradingState::UpgradingInProgress) {
m_staticText_ver_val->SetLabelText("-");
//m_staticText_ams_ver_val->SetLabelText("-");