mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-18 06:40:12 +03:00
Compare commits
47 Commits
main
...
feature/ba
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01f2af94a1 | ||
|
|
8d9b08fed9 | ||
|
|
ddfa961111 | ||
|
|
0b4cb89f7c | ||
|
|
4683c17f6f | ||
|
|
12720ccbd8 | ||
|
|
6767ab5ae0 | ||
|
|
9d9bb9cdbd | ||
|
|
79593e07c6 | ||
|
|
5808ae5ab0 | ||
|
|
1b722de6e7 | ||
|
|
38ab986c5f | ||
|
|
37e27a24c4 | ||
|
|
7f8baf46d3 | ||
|
|
d097fbec9a | ||
|
|
2ee0bbde54 | ||
|
|
80d3c3908d | ||
|
|
5e95aca42d | ||
|
|
40a875800d | ||
|
|
50abe02f18 | ||
|
|
7265a2fbfe | ||
|
|
db51d8ab9b | ||
|
|
d4e95e3269 | ||
|
|
0feca8d442 | ||
|
|
8d211ce878 | ||
|
|
8a6609f282 | ||
|
|
4bca7b3008 | ||
|
|
550ab5d438 | ||
|
|
56ac4e1281 | ||
|
|
5fd1aa6e8b | ||
|
|
eeec8ab06b | ||
|
|
28b7fd90d0 | ||
|
|
4a1c53e76e | ||
|
|
3ec074edb2 | ||
|
|
615deefa87 | ||
|
|
ffda4282eb | ||
|
|
5cf7e27c23 | ||
|
|
8bb093b50b | ||
|
|
47f7e09e84 | ||
|
|
e8f8a4bb7d | ||
|
|
c7581e590c | ||
|
|
5f3f892a6b | ||
|
|
0f58f23853 | ||
|
|
7cc572be47 | ||
|
|
d591d50ca0 | ||
|
|
aa44d57eac | ||
|
|
bf4fb64c8a |
18
resources/images/dev_hms_diag_loading.svg
Normal file
18
resources/images/dev_hms_diag_loading.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<svg width="117" height="88" viewBox="0 0 117 88" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_dev_hms_diag_loading)">
|
||||
<ellipse cx="58.5" cy="48.2667" rx="32.175" ry="32.2667" fill="#F2F2F2" stroke="#DBDBDB"/>
|
||||
<rect x="35.175" y="24.8662" width="46.65" height="46.8" rx="5" fill="#F7F7F7" stroke="#EBEBEB" stroke-width="6"/>
|
||||
<path d="M43.875 57.0665L52.4779 46.7998L62.1562 57.0665H43.875Z" fill="#DBDBDB"/>
|
||||
<path d="M51.1875 57.0671L62.8875 42.4004L76.05 57.0671H51.1875Z" fill="#EBEBEB"/>
|
||||
<ellipse cx="49.3595" cy="39.0998" rx="3.29063" ry="3.3" fill="#DBDBDB"/>
|
||||
<rect x="32.175" y="21.8662" width="52.65" height="52.8" rx="8" stroke="#DBDBDB"/>
|
||||
<ellipse cx="17.55" cy="68.7995" rx="3.65625" ry="3.66667" fill="#EBEBEB"/>
|
||||
<ellipse cx="24.1313" cy="28.4666" rx="2.19375" ry="2.2" fill="#EBEBEB"/>
|
||||
<ellipse cx="100.912" cy="35.0662" rx="2.19375" ry="2.2" fill="#EBEBEB"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_dev_hms_diag_loading">
|
||||
<rect width="117" height="88" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
3
resources/images/folder-closed-active.svg
Normal file
3
resources/images/folder-closed-active.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.88802 4.48958C8.16145 4.76302 8.53124 4.91667 8.91927 4.91667H13C13.2292 4.91667 13.4167 5.10417 13.4167 5.33333V6.16667H2.58333V3.66667C2.58333 3.4375 2.77083 3.25 2.99999 3.25H6.47656C6.58593 3.25 6.6927 3.29427 6.77083 3.3724L7.88802 4.48958ZM2.58333 7.41667H13.4167V12C13.4167 12.2292 13.2292 12.4167 13 12.4167H2.99999C2.77083 12.4167 2.58333 12.2292 2.58333 12V7.41667ZM8.77343 3.60677L7.65364 2.48698C7.34114 2.17448 6.91666 2 6.47395 2H2.99999C2.08072 2 1.33333 2.7474 1.33333 3.66667V12C1.33333 12.9193 2.08072 13.6667 2.99999 13.6667H13C13.9193 13.6667 14.6667 12.9193 14.6667 12V5.33333C14.6667 4.41406 13.9193 3.66667 13 3.66667H8.91927C8.86458 3.66667 8.80989 3.64583 8.77083 3.60677H8.77343Z" fill="#009688"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 839 B |
3
resources/images/folder-closed-hover.svg
Normal file
3
resources/images/folder-closed-hover.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.88802 4.48958C8.16145 4.76302 8.53124 4.91667 8.91927 4.91667H13C13.2292 4.91667 13.4167 5.10417 13.4167 5.33333V6.16667H2.58333V3.66667C2.58333 3.4375 2.77083 3.25 2.99999 3.25H6.47656C6.58593 3.25 6.6927 3.29427 6.77083 3.3724L7.88802 4.48958ZM2.58333 7.41667H13.4167V12C13.4167 12.2292 13.2292 12.4167 13 12.4167H2.99999C2.77083 12.4167 2.58333 12.2292 2.58333 12V7.41667ZM8.77343 3.60677L7.65364 2.48698C7.34114 2.17448 6.91666 2 6.47395 2H2.99999C2.08072 2 1.33333 2.7474 1.33333 3.66667V12C1.33333 12.9193 2.08072 13.6667 2.99999 13.6667H13C13.9193 13.6667 14.6667 12.9193 14.6667 12V5.33333C14.6667 4.41406 13.9193 3.66667 13 3.66667H8.91927C8.86458 3.66667 8.80989 3.64583 8.77083 3.60677H8.77343Z" fill="#6B6B6B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 839 B |
3
resources/images/folder-closed.svg
Normal file
3
resources/images/folder-closed.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.88802 4.48958C8.16145 4.76302 8.53124 4.91667 8.91927 4.91667H13C13.2292 4.91667 13.4167 5.10417 13.4167 5.33333V6.16667H2.58333V3.66667C2.58333 3.4375 2.77083 3.25 2.99999 3.25H6.47656C6.58593 3.25 6.6927 3.29427 6.77083 3.3724L7.88802 4.48958ZM2.58333 7.41667H13.4167V12C13.4167 12.2292 13.2292 12.4167 13 12.4167H2.99999C2.77083 12.4167 2.58333 12.2292 2.58333 12V7.41667ZM8.77343 3.60677L7.65364 2.48698C7.34114 2.17448 6.91666 2 6.47395 2H2.99999C2.08072 2 1.33333 2.7474 1.33333 3.66667V12C1.33333 12.9193 2.08072 13.6667 2.99999 13.6667H13C13.9193 13.6667 14.6667 12.9193 14.6667 12V5.33333C14.6667 4.41406 13.9193 3.66667 13 3.66667H8.91927C8.86458 3.66667 8.80989 3.64583 8.77083 3.60677H8.77343Z" fill="#262E30"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 839 B |
@@ -75,7 +75,8 @@
|
||||
"support_extrusion_cali": false,
|
||||
"support_user_preset": false,
|
||||
"support_refresh_nozzle": true,
|
||||
"support_ams_ext_mix_print": true
|
||||
"support_ams_ext_mix_print": true,
|
||||
"support_print_check_firmware_for_tpu_left": true
|
||||
},
|
||||
"model_id": "O1D",
|
||||
"support_wrapping_detection": true,
|
||||
|
||||
@@ -75,7 +75,8 @@
|
||||
"support_extrusion_cali": false,
|
||||
"support_user_preset": false,
|
||||
"support_refresh_nozzle": true,
|
||||
"support_ams_ext_mix_print": true
|
||||
"support_ams_ext_mix_print": true,
|
||||
"support_print_check_firmware_for_tpu_left": true
|
||||
},
|
||||
"model_id": "O1E",
|
||||
"auto_pa_cali_thumbnail_image": "fd_calibration_auto_multi_extruders",
|
||||
|
||||
@@ -23,6 +23,7 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/AboutDialog.cpp
|
||||
GUI/AboutDialog.hpp
|
||||
GUI/AmsMappingPopup.cpp
|
||||
GUI/AmsMappingPopupUpdate.cpp
|
||||
GUI/AmsMappingPopup.hpp
|
||||
GUI/AMSMaterialsSetting.cpp
|
||||
GUI/AMSMaterialsSetting.hpp
|
||||
|
||||
@@ -42,7 +42,7 @@ static std::string get_humidity_level_img_path(int humidity_percent)
|
||||
}
|
||||
|
||||
if (wxGetApp().dark_mode()) {
|
||||
return "hum_level" + std::to_string(hum_level) + "_no_num_dark";
|
||||
return "hum_level" + std::to_string(hum_level) + "_no_num_dark"; // Orca: use the dark-mode humidity glyph in dark mode
|
||||
} else {
|
||||
return "hum_level" + std::to_string(hum_level) + "_no_num_light";
|
||||
}
|
||||
@@ -86,7 +86,7 @@ FilamentItemPanel::FilamentItemPanel(wxWindow* parent, const wxString& text, con
|
||||
: wxPanel(parent, id)
|
||||
, m_icon_name(icon_name)
|
||||
{
|
||||
SetBackgroundColour(wxColour("#F0F0F1")); // Light gray background
|
||||
SetBackgroundColour(wxColour("#F0F0F1")); // Orca: light-gray panel scheme (#F0F0F1 replaces REF #F7F7F7)
|
||||
SetMinSize(wxSize(FromDIP(64), FromDIP(106))); // Width: 64, Height: 106
|
||||
SetSize(wxSize(FromDIP(64), FromDIP(106))); // Fixed size
|
||||
|
||||
@@ -977,7 +977,7 @@ void AMSDryCtrWin::create()
|
||||
// set title icon
|
||||
SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE));
|
||||
this->SetDoubleBuffered(true);
|
||||
std::string icon_path = (boost::format("%1%/images/OrcaSlicerTitle.ico") % resources_dir()).str();
|
||||
std::string icon_path = (boost::format("%1%/images/OrcaSlicerTitle.ico") % resources_dir()).str(); // Orca: app title icon
|
||||
SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO));
|
||||
|
||||
SetSize(wxSize(FromDIP(700), FromDIP(500)));
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "slic3r/GUI/wxExtensions.hpp"
|
||||
#include "slic3r/GUI/DeviceCore/DevFilaSystem.h"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp" // Orca: explicit _L() catalog include
|
||||
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
|
||||
@@ -296,11 +296,8 @@ void AMSMaterialsSetting::create_panel_kn(wxWindow* parent)
|
||||
m_ratio_text->SetForegroundColour(wxColour(50, 58, 61));
|
||||
m_ratio_text->SetFont(Label::Head_14);
|
||||
|
||||
std::string language = wxGetApp().app_config->get("language");
|
||||
wxString region = "en";
|
||||
if (language.find("zh") == 0)
|
||||
region = "zh";
|
||||
wxString link_url = wxString::Format("https://wiki.bambulab.com/%s/software/bambu-studio/calibration_pa", region);
|
||||
// Orca: link to the Orca Slicer pressure-advance wiki (region-agnostic).
|
||||
wxString link_url = "https://www.orcaslicer.com/wiki/pressure_advance_calib";
|
||||
m_wiki_ctrl = new HyperLink(parent, _L("Wiki Guide"), link_url);
|
||||
cali_title_sizer->Add(m_ratio_text, 0, wxALIGN_CENTER_VERTICAL);
|
||||
cali_title_sizer->Add(m_wiki_ctrl, 0, wxALIGN_CENTER_VERTICAL);
|
||||
@@ -449,9 +446,19 @@ void AMSMaterialsSetting::update_filament_editing(bool is_printing)
|
||||
m_tip_readonly->Wrap(FromDIP(380));
|
||||
m_tip_readonly->Show(is_printing);
|
||||
}
|
||||
|
||||
if (m_view_only) { // Orca: view-only (2D laser/cut) — lock every edit control and hide apply/reset
|
||||
m_comboBox_filament->Enable(false);
|
||||
m_comboBox_cali_result->Enable(false);
|
||||
m_input_k_val->Enable(false);
|
||||
m_input_n_val->Enable(false);
|
||||
m_button_confirm->Hide();
|
||||
m_button_reset->Hide();
|
||||
}
|
||||
}
|
||||
|
||||
void AMSMaterialsSetting::on_select_reset(wxCommandEvent& event) {
|
||||
if (m_view_only) return; // Orca: view-only never commits
|
||||
MessageDialog msg_dlg(nullptr, _L("Are you sure you want to clear the filament information?"), wxEmptyString, wxICON_WARNING | wxOK | wxCANCEL);
|
||||
auto result = msg_dlg.ShowModal();
|
||||
if (result != wxID_OK)
|
||||
@@ -619,6 +626,8 @@ void AMSMaterialsSetting::on_select_ok(wxCommandEvent &event)
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
if (m_view_only) return; // Orca: view-only never commits
|
||||
|
||||
//get filament id
|
||||
ams_filament_id = "";
|
||||
ams_setting_id = "";
|
||||
@@ -830,6 +839,7 @@ void AMSMaterialsSetting::on_picker_color(wxCommandEvent& event)
|
||||
|
||||
void AMSMaterialsSetting::on_clr_picker(wxMouseEvent &event)
|
||||
{
|
||||
if (m_view_only) return; // Orca: view-only disables color editing
|
||||
if(!m_is_third)
|
||||
return;
|
||||
|
||||
|
||||
@@ -124,6 +124,9 @@ public:
|
||||
std::string ams_setting_id;
|
||||
|
||||
bool m_is_third;
|
||||
// Orca: view-only mode (laser/cut). When set, the dialog is inspectable but every
|
||||
// editing control is disabled and no command is sent.
|
||||
bool m_view_only = false;
|
||||
wxString m_brand_filament;
|
||||
wxString m_brand_sn;
|
||||
wxString m_brand_tmp;
|
||||
@@ -183,6 +186,7 @@ protected:
|
||||
|
||||
int m_pa_cali_select_id = 0;
|
||||
|
||||
// Orca: native wxComboBox on macOS (the custom ComboBox mis-renders the filament dropdown there)
|
||||
#ifdef __APPLE__
|
||||
wxComboBox *m_comboBox_filament;
|
||||
#else
|
||||
|
||||
@@ -272,7 +272,7 @@ void AMSSetting::create()
|
||||
m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(10));
|
||||
m_sizer_main->Add(m_static_ams_settings, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(24));
|
||||
m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(10));
|
||||
m_sizer_main->Add(m_panel_body, 1, wxBottom | wxLEFT | wxRIGHT | wxEXPAND, FromDIP(24));
|
||||
m_sizer_main->Add(m_panel_body, 1, wxBOTTOM | wxLEFT | wxRIGHT | wxEXPAND, FromDIP(24)); // Orca: wxBOTTOM (REF uses the wxEdge enum by mistake)
|
||||
|
||||
this->SetSizer(m_sizer_main);
|
||||
this->Layout();
|
||||
@@ -644,7 +644,7 @@ void AMSSettingTypePanel::Update(const MachineObject* obj)
|
||||
}
|
||||
|
||||
if (ptr->IsSwitching()) {
|
||||
int display_percent = obj->get_upgrade_percent();
|
||||
int display_percent = obj->get_upgrade_percent(); // Orca: read upgrade progress via the kept MachineObject accessor
|
||||
if (display_percent == 100 || display_percent == 0) {
|
||||
display_percent = 1;// special case, sometimes it's switching but percent is 0 or 100
|
||||
}
|
||||
@@ -735,6 +735,7 @@ void AMSSettingTypePanel::OnAmsTypeChanged(wxCommandEvent& event)
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
// Orca: AMSSettingArrangeAMSOrder impl kept compiled-out (feature intentionally not shipped).
|
||||
#if 0 /*used option*/
|
||||
AMSSettingArrangeAMSOrder::AMSSettingArrangeAMSOrder(wxWindow* parent)
|
||||
: wxPanel(parent)
|
||||
|
||||
@@ -129,6 +129,7 @@ private:
|
||||
AnimaIcon* m_switching_icon;
|
||||
};
|
||||
|
||||
// Orca: the firmware AMS-reorder ("Arrange AMS Order") panel is intentionally not shipped.
|
||||
#if 0
|
||||
class AMSSettingArrangeAMSOrder : public wxPanel
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -34,23 +34,28 @@
|
||||
#include "Widgets/CheckBox.hpp"
|
||||
#include "Widgets/ComboBox.hpp"
|
||||
#include "Widgets/ScrolledWindow.hpp"
|
||||
#include "Widgets/SwitchButton.hpp"
|
||||
#include "Widgets/SwitchButton.hpp" // Orca: SwitchBoard (m_nozzle_btn_panel); not transitively available here
|
||||
#include "Widgets/PopupWindow.hpp"
|
||||
#include <wx/simplebook.h>
|
||||
#include <wx/hashmap.h>
|
||||
|
||||
#include "slic3r/GUI/DeviceCore/DevUtil.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#define MAPPING_ITEM_INVALID_REMAIN -1
|
||||
|
||||
namespace Slic3r {
|
||||
// Previous definitions
|
||||
namespace Slic3r
|
||||
{
|
||||
class DevNozzleRack;
|
||||
namespace GUI { class wgtDeviceNozzleRackSelect; }
|
||||
}
|
||||
namespace GUI
|
||||
{
|
||||
class wgtDeviceNozzleRackSelect;
|
||||
class wgtMsgPanel;
|
||||
};
|
||||
};
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
||||
#define AMS_TOTAL_COUNT 4
|
||||
@@ -64,7 +69,8 @@ enum TrayType {
|
||||
enum ShowType {
|
||||
LEFT, // only show left ams and left ext
|
||||
RIGHT, //only show right ams and right ext
|
||||
LEFT_AND_RIGHT //show left and right ams at the same time
|
||||
LEFT_AND_RIGHT, //show left and right ams at the same time
|
||||
LEFT_AND_RIGHT_DYNAMIC //show all left and right at one panel when use_dynamic_switch
|
||||
};
|
||||
|
||||
struct TrayData
|
||||
@@ -85,26 +91,59 @@ struct TrayData
|
||||
class MaterialItem: public wxPanel
|
||||
{
|
||||
protected:
|
||||
int m_text_pos_x = 0;
|
||||
int m_text_pos_y = -1;
|
||||
bool m_dropdown_allow_painted = true;
|
||||
wxString m_mapping_text;
|
||||
|
||||
public:
|
||||
MaterialItem(wxWindow *parent, wxColour mcolour, wxString mname);
|
||||
// Orca: filament_id defaults to empty so the existing 3-arg callers keep compiling;
|
||||
// m_filament_id is otherwise unused here.
|
||||
MaterialItem(wxWindow *parent, wxColour mcolour, wxString mname, std::string filament_id = std::string());
|
||||
~MaterialItem();
|
||||
|
||||
wxPanel* m_main_panel;
|
||||
void allow_paint_dropdown(bool flag);
|
||||
|
||||
void set_ams_text(const wxString& txt);
|
||||
void set_ams_info(wxColour col, wxString txt,
|
||||
int ctype = 0, std::vector<wxColour> cols = std::vector<wxColour>(),
|
||||
bool record_back_info = false);
|
||||
|
||||
void set_material_cols(int ctype, const std::vector<wxColour>& cols);
|
||||
|
||||
void reset_ams_info();
|
||||
virtual void reset_valid_info();
|
||||
|
||||
void set_nozzle_info(const wxString& mapped_nozzle_str);
|
||||
|
||||
void disable();
|
||||
void enable();
|
||||
void on_normal();
|
||||
void on_selected();
|
||||
void on_warning();
|
||||
|
||||
bool is_selected() const { return m_selected;}
|
||||
bool is_warning() const { return m_warning;}
|
||||
|
||||
void msw_rescale();
|
||||
|
||||
protected:
|
||||
void messure_size();
|
||||
|
||||
public:
|
||||
std::string m_filament_id;
|
||||
|
||||
wxColour m_material_coloul;
|
||||
int m_material_ctype = 0;
|
||||
std::vector<wxColour> m_material_cols;
|
||||
wxString m_material_name;
|
||||
wxString m_mapped_nozzle_str;
|
||||
|
||||
//info
|
||||
wxColour m_ams_coloul;
|
||||
wxString m_ams_name;
|
||||
// Physical nozzle(s) the print-dispatch mapping assigned to this filament (e.g. "R1", "L R").
|
||||
// Empty on printers without a nozzle rack or filament switcher; when set, the card grows a row.
|
||||
wxString m_mapped_nozzle_str;
|
||||
int m_ams_ctype = 0;
|
||||
std::vector<wxColour> m_ams_cols = std::vector<wxColour>();
|
||||
|
||||
//reset
|
||||
wxColour m_back_ams_coloul;
|
||||
wxString m_back_ams_name;
|
||||
@@ -124,32 +163,17 @@ public:
|
||||
bool m_match {true};
|
||||
bool m_enable {true};
|
||||
|
||||
void msw_rescale();
|
||||
void allow_paint_dropdown(bool flag);
|
||||
void set_ams_info(wxColour col, wxString txt, int ctype=0, std::vector<wxColour> cols= std::vector<wxColour>(),bool record_back_info = false);
|
||||
void reset_ams_info();
|
||||
// Set the mapped-nozzle label ("R1", "L R", ...); grows/shrinks the card as the row appears/clears.
|
||||
void set_nozzle_info(const wxString& mapped_nozzle_str);
|
||||
// Size the card: base swatch height, plus one row when a nozzle label is present.
|
||||
void messure_size();
|
||||
|
||||
void disable();
|
||||
void enable();
|
||||
void on_normal();
|
||||
void on_selected();
|
||||
void on_warning();
|
||||
|
||||
void paintEvent(wxPaintEvent &evt);
|
||||
virtual void render(wxDC &dc);
|
||||
void match(bool mat);
|
||||
virtual void doRender(wxDC &dc);
|
||||
virtual void reset_valid_info();
|
||||
};
|
||||
|
||||
class MaterialSyncItem : public MaterialItem
|
||||
{
|
||||
public:
|
||||
MaterialSyncItem(wxWindow *parent, wxColour mcolour, wxString mname);
|
||||
// Orca: filament_id defaults to empty for existing 3-arg callers (see MaterialItem).
|
||||
MaterialSyncItem(wxWindow *parent, wxColour mcolour, wxString mname, std::string filament_id = std::string());
|
||||
~MaterialSyncItem();
|
||||
int get_real_offset();
|
||||
void render(wxDC &dc) override;
|
||||
@@ -181,7 +205,7 @@ public:
|
||||
public:
|
||||
void update_data(TrayData data);
|
||||
void send_event(int fliament_id);
|
||||
void set_data(const wxString& tag_name, wxColour colour, wxString name, bool remain_detect, TrayData data, bool unmatch = false);
|
||||
void set_data(const wxString& tag_name, wxColour colour, wxString name, bool remain_detect, TrayData data, bool unmatch = false, std::optional<wxString> tooltip_opt = std::nullopt);
|
||||
void set_checked(bool checked);
|
||||
void set_tray_index(wxString t_index) { m_tray_index = t_index; };
|
||||
|
||||
@@ -222,13 +246,38 @@ protected:
|
||||
void doRender(wxDC& dc);
|
||||
};
|
||||
|
||||
class DevIconLabel : public wxPanel
|
||||
{
|
||||
public:
|
||||
DevIconLabel(wxWindow* parent, const wxString& icon, const wxString& label);
|
||||
|
||||
public:
|
||||
void SetAllBackgroundColor(const wxColour& color);
|
||||
|
||||
Label* GetLabelItem() const { return m_label; }
|
||||
void SetLabel(const wxString& label);
|
||||
void SetIcon(const wxString& icon);
|
||||
|
||||
void Rescale();
|
||||
|
||||
private:
|
||||
void CreateGui();
|
||||
|
||||
private:
|
||||
Label* m_label{ nullptr };
|
||||
wxString m_icon_str;
|
||||
wxStaticBitmap* m_icon{ nullptr };
|
||||
};
|
||||
|
||||
class AmsMapingPopup : public PopupWindow
|
||||
{
|
||||
bool m_use_in_sync_dialog = false;
|
||||
bool m_ams_remain_detect_flag = false;
|
||||
bool m_ext_mapping_filatype_check = true;
|
||||
wxStaticText* m_title_text{ nullptr };
|
||||
|
||||
wgtDeviceNozzleRackSelect *m_rack_nozzle_select{nullptr};
|
||||
DevIconLabel* m_flush_warning_panel;
|
||||
|
||||
public:
|
||||
AmsMapingPopup(wxWindow *parent,bool use_in_sync_dialog = false);
|
||||
~AmsMapingPopup() {};
|
||||
@@ -244,11 +293,11 @@ public:
|
||||
bool m_has_unmatch_filament {false};
|
||||
int m_current_filament_id;
|
||||
ShowType m_show_type{ShowType::RIGHT};
|
||||
// Orca: set from update() — true when a Filament Track Switch is installed, which makes the
|
||||
// external-spool slots un-pickable (false on any printer without a switch, so behavior is unchanged).
|
||||
bool m_fila_switch_installed{false};
|
||||
std::string m_tag_material;
|
||||
wxScrolledWindow *m_scrolled_window{nullptr};
|
||||
wxBoxSizer *m_sizer_main{nullptr};
|
||||
wxBoxSizer *m_sizer_main_h{nullptr};
|
||||
wxBoxSizer *m_sizer_ams_v{nullptr};
|
||||
wxBoxSizer *m_sizer_ams{nullptr};
|
||||
wxBoxSizer *m_sizer_ams_left{nullptr};
|
||||
wxBoxSizer *m_sizer_ams_right{nullptr};
|
||||
@@ -262,26 +311,27 @@ public:
|
||||
MappingItem* m_right_extra_slot{nullptr};
|
||||
|
||||
wxPanel * m_left_marea_panel{nullptr};
|
||||
wxPanel * m_right_marea_panel{nullptr};
|
||||
wxPanel* m_right_marea_panel{ nullptr }; // used as right if both left and right sides shown. used as single panel if only one side shown.
|
||||
wxPanel * m_left_first_text_panel{nullptr};
|
||||
wxPanel * m_right_first_text_panel{nullptr};
|
||||
wgtMsgPanel* m_ams_tips_msg_panel{nullptr};
|
||||
wxPanel * m_split_line_panel{nullptr};
|
||||
wxBoxSizer * m_left_split_ams_sizer{nullptr};
|
||||
wxBoxSizer * m_right_split_ams_sizer{nullptr};
|
||||
wxBoxSizer * m_right_split_ext_sizer{ nullptr };
|
||||
Label * m_left_tips{nullptr};
|
||||
Label * m_right_tips{nullptr};
|
||||
|
||||
ScalableButton* m_reset_btn{nullptr};
|
||||
wxString m_single_tip_text;
|
||||
wxString m_left_tip_text;
|
||||
wxString m_right_tip_text;
|
||||
wxString m_ams_tips_panel_text;
|
||||
wxBoxSizer* m_sizer_split_ams_left;
|
||||
wxBoxSizer* m_sizer_split_ams_right;
|
||||
bool m_mapping_from_multi_machines {false};
|
||||
|
||||
// Rack nozzle manual-pick (rack printers only; hidden by default via Show(false)).
|
||||
wgtDeviceNozzleRackSelect* m_rack_nozzle_select{nullptr};
|
||||
Label* m_flush_warning_panel{nullptr}; // Orca: uses a plain Label for the flush warning
|
||||
std::weak_ptr<DevNozzleRack> m_rack;
|
||||
|
||||
bool get_use_in_sync_dialog() { return m_use_in_sync_dialog; }
|
||||
void set_sizer_title(wxBoxSizer *sizer, wxString text);
|
||||
wxBoxSizer* create_split_sizer(wxWindow* parent, wxString text);
|
||||
void set_send_win(wxWindow* win) {send_win = win;};
|
||||
@@ -289,8 +339,6 @@ public:
|
||||
void set_tag_texture(std::string texture);
|
||||
void update(MachineObject* obj, const std::vector<FilamentInfo>& ams_mapping_result, bool use_dynamic_switch = false, std::optional<PrintFromType> print_type = std::nullopt);
|
||||
void update_rack_select(MachineObject* obj, bool use_dynamic_switch, std::optional<PrintFromType> print_type);
|
||||
void update_flush_waste(MachineObject* obj);
|
||||
void update_title(MachineObject* obj);
|
||||
void update_items_check_state(const std::vector<FilamentInfo>& ams_mapping_result);
|
||||
void update_ams_data_multi_machines();
|
||||
void add_ams_mapping(std::vector<TrayData> tray_data, bool remain_detect_flag, wxWindow *container, wxBoxSizer *sizer);
|
||||
@@ -304,15 +352,24 @@ public:
|
||||
void paintEvent(wxPaintEvent &evt);
|
||||
void set_parent_item(MaterialItem* item) {m_parent_item = item;};
|
||||
void set_show_type(ShowType type) { m_show_type = type; };
|
||||
// Orca: kept as a no-op for SelectMachine — its only caller passes false, which
|
||||
// equals the update path's default (all slots shown).
|
||||
void set_only_show_ext_spool(bool /*flag*/) {}
|
||||
// Orca: dropped by the reference but still consumed by SelectMachine.
|
||||
std::vector<TrayData> parse_ams_mapping(const std::map<std::string, DevAms*, NumericStrCompare>& amsList);
|
||||
|
||||
#ifdef __APPLE__
|
||||
void on_mouse_move(wxMouseEvent &evt);
|
||||
wxPopupWindow * m_tip_popup{nullptr};
|
||||
Label* m_tip_label{nullptr};
|
||||
#endif
|
||||
|
||||
using ResetCallback = std::function<void(const std::string&)>;
|
||||
void reset_ams_info();
|
||||
void set_reset_callback(ResetCallback callback);
|
||||
void show_reset_button();
|
||||
void set_material_index_str(std::string str) { m_material_index = str; }
|
||||
const std::string &get_material_index_str() { return m_material_index; }
|
||||
void set_only_show_ext_spool(bool flag);
|
||||
|
||||
public:
|
||||
void msw_rescale();
|
||||
@@ -320,11 +377,20 @@ public:
|
||||
void EnableExtMappingFilaTypeCheck(bool to_check = true) { m_ext_mapping_filatype_check = to_check;} ;
|
||||
|
||||
private:
|
||||
// update
|
||||
void update_title(MachineObject* obj);
|
||||
void update_ams_tips(MachineObject* obj);
|
||||
void update_mapping_items(MachineObject* obj, const std::vector<FilamentInfo>& ams_mapping_result, bool use_dynamic_switch);
|
||||
|
||||
// events
|
||||
void OnNozzleMappingSelected(wxCommandEvent& evt);
|
||||
void update_flush_waste(MachineObject* obj);
|
||||
|
||||
private:
|
||||
std::weak_ptr<DevNozzleRack> m_rack;
|
||||
|
||||
ResetCallback m_reset_callback{nullptr};
|
||||
std::string m_material_index;
|
||||
bool m_only_show_ext_spool{false};
|
||||
};
|
||||
|
||||
class AmsMapingTipPopup : public PopupWindow
|
||||
|
||||
708
src/slic3r/GUI/AmsMappingPopupUpdate.cpp
Normal file
708
src/slic3r/GUI/AmsMappingPopupUpdate.cpp
Normal file
@@ -0,0 +1,708 @@
|
||||
/*****************************************************************//**
|
||||
* \file AmsMappingPopupUpdate.cpp
|
||||
* \brief do update for AmsMappingPopup
|
||||
* \note moved from AmsMappingPopup.cpp to here to reduce the code size of AmsMappingPopup.cpp
|
||||
*
|
||||
* \author xin.zhang
|
||||
* \date February 2026
|
||||
*********************************************************************/
|
||||
#include "AmsMappingPopup.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "DeviceCore/DevConfigUtil.h"
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "slic3r/Utils/WxFontUtils.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI_Preview.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "format.hpp"
|
||||
#include "Widgets/ProgressDialog.hpp"
|
||||
#include "Widgets/RoundedRectangle.hpp"
|
||||
#include "Widgets/StaticBox.hpp"
|
||||
|
||||
#include <wx/progdlg.h>
|
||||
#include <wx/clipbrd.h>
|
||||
#include <wx/dcgraph.h>
|
||||
#include <wx/graphics.h>
|
||||
#include <miniz.h>
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include "Plater.hpp"
|
||||
#include "BitmapCache.hpp"
|
||||
|
||||
#include "DeviceCore/DevFilaSystem.h"
|
||||
#include "DeviceCore/DevFilaSwitch.h"
|
||||
#include "DeviceCore/DevMappingNozzle.h"
|
||||
#include "DeviceCore/DevNozzleRack.h" // Orca: full type for GetNozzleSystem()->GetNozzleRack()->IsSupported()
|
||||
|
||||
#include "DeviceTab/wgtDeviceNozzleSelect.h"
|
||||
#include "DeviceTab/wgtMsgPanel.h"
|
||||
|
||||
namespace Slic3r::GUI {
|
||||
|
||||
|
||||
static void _add_containers(const AmsMapingPopup* win,
|
||||
std::list<MappingContainer*>& one_slot_containers,
|
||||
const std::vector<MappingContainer*>& four_slots_containers,
|
||||
wxBoxSizer* target_sizer)
|
||||
{
|
||||
for (auto container : four_slots_containers) { target_sizer->Add(container, 0, wxTOP, win->FromDIP(5)); }
|
||||
|
||||
while (!one_slot_containers.empty()) {
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (one_slot_containers.empty()) { break; }
|
||||
|
||||
sizer->Add(one_slot_containers.front(), 0, wxLEFT, (i == 0) ? 0 : win->FromDIP(5));
|
||||
one_slot_containers.pop_front();
|
||||
}
|
||||
|
||||
target_sizer->Add(sizer, 0, wxTOP, win->FromDIP(5));
|
||||
}
|
||||
}
|
||||
|
||||
void AmsMapingPopup::update(MachineObject* obj,
|
||||
const std::vector<FilamentInfo>& ams_mapping_result,
|
||||
bool use_dynamic_switch,
|
||||
std::optional<PrintFromType> print_type)
|
||||
{
|
||||
if (!obj) { return; } // Orca: guard before use (REF logs before its null check)
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "ams_mapping total count " << obj->GetFilaSystem()->GetAmsCount();
|
||||
|
||||
for (auto& ams_container : m_amsmapping_container_list) {
|
||||
ams_container->Destroy();
|
||||
}
|
||||
|
||||
m_amsmapping_container_list.clear();
|
||||
m_amsmapping_container_sizer_list.clear();
|
||||
m_mapping_item_list.clear();
|
||||
|
||||
/*title*/
|
||||
update_title(obj);
|
||||
|
||||
/*ams mapping*/
|
||||
update_ams_tips(obj);
|
||||
update_mapping_items(obj, ams_mapping_result, use_dynamic_switch);
|
||||
|
||||
/*rack*/
|
||||
update_rack_select(obj, use_dynamic_switch, print_type);
|
||||
update_flush_waste(obj);
|
||||
|
||||
if (wxGetApp().dark_mode() && m_reset_btn->GetName() != "erase_dark") {
|
||||
m_reset_btn->SetName("erase_dark");
|
||||
m_reset_btn->SetBitmap(ScalableBitmap(m_right_first_text_panel, "erase_dark", 14).bmp());
|
||||
} else if (!wxGetApp().dark_mode() && m_reset_btn->GetName() != "erase") {
|
||||
m_reset_btn->SetName("erase");
|
||||
m_reset_btn->SetBitmap(ScalableBitmap(m_right_first_text_panel, "erase", 14).bmp());
|
||||
}
|
||||
|
||||
size_t extruder_num = obj->GetExtderSystem()->GetTotalExtderCount();
|
||||
if (extruder_num == 1) {
|
||||
m_left_marea_panel->Hide();
|
||||
m_left_extra_slot->Hide();
|
||||
m_split_line_panel->Hide();
|
||||
m_right_marea_panel->Show();
|
||||
m_right_marea_panel->Enable(true);
|
||||
m_right_extra_slot->Show();
|
||||
m_right_extra_slot->Enable(true);
|
||||
m_right_split_ext_sizer->Show(true);
|
||||
set_sizer_title(m_right_split_ams_sizer, _L("AMS"));
|
||||
} else if (extruder_num > 1) {
|
||||
if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) {
|
||||
m_left_marea_panel->Show(false);
|
||||
m_left_extra_slot->Show(false);
|
||||
m_right_extra_slot->Show(false);
|
||||
m_left_split_ams_sizer->Show(false);
|
||||
m_right_split_ams_sizer->Show(false);
|
||||
m_right_split_ext_sizer->Show(false);
|
||||
m_split_line_panel->Hide();
|
||||
|
||||
m_right_marea_panel->Show();
|
||||
m_right_marea_panel->Enable(true);
|
||||
} else {
|
||||
|
||||
m_left_marea_panel->Show();
|
||||
m_left_extra_slot->Show();
|
||||
m_split_line_panel->Show();
|
||||
m_right_marea_panel->Show();
|
||||
m_right_extra_slot->Show();
|
||||
m_right_split_ext_sizer->Show(true);
|
||||
m_left_tips->SetLabel(m_left_tip_text);
|
||||
m_right_tips->SetLabel(m_right_tip_text);
|
||||
set_sizer_title(m_left_split_ams_sizer, _L("Left AMS"));
|
||||
set_sizer_title(m_right_split_ams_sizer, _L("Right AMS"));
|
||||
if (m_show_type == ShowType::LEFT) {
|
||||
m_left_marea_panel->Enable(true);
|
||||
m_left_extra_slot->Enable(true);
|
||||
m_right_marea_panel->Enable(false);
|
||||
m_right_extra_slot->Enable(false);
|
||||
if (m_use_in_sync_dialog) {
|
||||
m_left_tips->SetLabel(m_single_tip_text);
|
||||
m_right_tips->SetLabel("");
|
||||
}
|
||||
} else if (m_show_type == ShowType::RIGHT) {
|
||||
m_left_marea_panel->Enable(false);
|
||||
m_left_extra_slot->Enable(false);
|
||||
m_right_marea_panel->Enable(true);
|
||||
m_right_extra_slot->Enable(true);
|
||||
if (m_use_in_sync_dialog) {
|
||||
// m_right_tips->SetLabel(m_single_tip_text);
|
||||
m_left_tips->SetLabel("");
|
||||
}
|
||||
} else if (m_show_type == ShowType::LEFT_AND_RIGHT) {
|
||||
m_left_marea_panel->Enable(true);
|
||||
m_left_extra_slot->Enable(true);
|
||||
m_right_marea_panel->Enable(true);
|
||||
m_right_extra_slot->Enable(true);
|
||||
if (m_use_in_sync_dialog) {
|
||||
m_left_tips->SetLabel(m_single_tip_text);
|
||||
m_right_tips->SetLabel("");
|
||||
}
|
||||
} else if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) {
|
||||
m_left_marea_panel->Show(false);
|
||||
m_left_extra_slot->Show(false);
|
||||
m_right_extra_slot->Show(false);
|
||||
m_left_split_ams_sizer->Show(false);
|
||||
m_right_split_ams_sizer->Show(false);
|
||||
m_right_split_ext_sizer->Show(false);
|
||||
m_split_line_panel->Hide();
|
||||
|
||||
m_right_marea_panel->Show();
|
||||
m_right_marea_panel->Enable(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
// Orca: the kept fila model has no tray-level ams_type; the owning AMS type is passed in
|
||||
// (DevAms::GetAmsType() for AMS trays, DevAmsType::EXT_SPOOL for the virtual/ext slots).
|
||||
static std::optional<TrayData> sGetTrayData(DevAmsTray* tray,
|
||||
const std::string& ams_id_str,
|
||||
DevAmsType ams_type)
|
||||
{
|
||||
int ams_id = 0;
|
||||
int tray_id = 0;
|
||||
try {
|
||||
ams_id = std::stoi(ams_id_str);
|
||||
tray_id = atoi(tray->id.c_str());
|
||||
} catch (const std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":[error] invalid ams_id or tray_id: " << e.what();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
TrayData td;
|
||||
td.ams_id = ams_id;
|
||||
if (ams_type != DevAmsType::EXT_SPOOL) {
|
||||
td.slot_id = tray_id;
|
||||
} else {
|
||||
td.slot_id = 0;
|
||||
}
|
||||
|
||||
if (ams_type == DevAmsType::AMS ||
|
||||
ams_type == DevAmsType::AMS_LITE ||
|
||||
ams_type == DevAmsType::N3F) {
|
||||
td.id = ams_id * 4 + tray_id;
|
||||
} else if (ams_type == DevAmsType::N3S) {
|
||||
td.id = ams_id + tray_id;
|
||||
} else if(ams_type == DevAmsType::EXT_SPOOL){
|
||||
td.id = tray_id;
|
||||
}
|
||||
|
||||
if (ams_type != EXT_SPOOL && !tray->is_exists) {
|
||||
td.type = EMPTY;
|
||||
} else {
|
||||
if (!tray->is_tray_info_ready()) {
|
||||
td.type = THIRD;
|
||||
} else {
|
||||
td.type = NORMAL;
|
||||
td.remain = tray->remain;
|
||||
td.colour = DevAmsTray::decode_color(tray->color);
|
||||
td.name = tray->get_display_filament_type();
|
||||
td.filament_type = tray->get_filament_type();
|
||||
td.ctype = tray->ctype;
|
||||
for (const auto& col : tray->cols) {
|
||||
td.material_cols.push_back(DevAmsTray::decode_color(col));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return td;
|
||||
}
|
||||
|
||||
void AmsMapingPopup::update_mapping_items(MachineObject* obj, const std::vector<FilamentInfo>& ams_mapping_result, bool use_dynamic_switch)
|
||||
{
|
||||
std::list<MappingContainer*> left_one_slot_containers;
|
||||
std::list<MappingContainer*> right_one_slot_containers;
|
||||
std::vector<MappingContainer*> left_four_slots_containers;
|
||||
std::vector<MappingContainer*> right_four_slot_containers;
|
||||
|
||||
const auto& ams_list = obj->GetFilaSystem()->GetAmsList();
|
||||
for (auto ams_iter = ams_list.begin(); ams_iter != ams_list.end(); ams_iter++) {
|
||||
const auto& extruder_set = ams_iter->second->GetBindedExtruderSet();
|
||||
if (extruder_set.size() <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
wxPanel* target_panel = nullptr;
|
||||
if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) {
|
||||
target_panel = m_right_marea_panel;
|
||||
} else {
|
||||
int extruder_id = *extruder_set.begin();
|
||||
if (extruder_id == MAIN_EXTRUDER_ID) {
|
||||
target_panel = m_right_marea_panel;
|
||||
} else if (extruder_id == DEPUTY_EXTRUDER_ID) {
|
||||
target_panel = m_left_marea_panel;
|
||||
}
|
||||
}
|
||||
|
||||
if (!target_panel) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto sizer_mapping_list = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto ams_mapping_item_container = new MappingContainer(target_panel, ams_iter->second->GetDisplayName(), ams_iter->second->GetSlotCount());
|
||||
ams_mapping_item_container->SetName(target_panel->GetName());
|
||||
ams_mapping_item_container->SetSizer(sizer_mapping_list);
|
||||
ams_mapping_item_container->Layout();
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "ams_mapping ams id " << ams_iter->first.c_str();
|
||||
|
||||
DevAms* ams_group = ams_iter->second;
|
||||
auto ams_type = ams_group->GetAmsType();
|
||||
std::vector<TrayData> tray_datas;
|
||||
std::map<std::string, DevAmsTray*>::const_iterator tray_iter;
|
||||
for (tray_iter = ams_group->GetTrays().cbegin(); tray_iter != ams_group->GetTrays().cend(); tray_iter++) {
|
||||
const auto& td_opt = sGetTrayData(tray_iter->second, ams_group->GetAmsId(), ams_type);
|
||||
if (td_opt.has_value()) {
|
||||
tray_datas.push_back(td_opt.value());
|
||||
}
|
||||
}
|
||||
|
||||
ams_mapping_item_container->Show();
|
||||
add_ams_mapping(tray_datas, obj->GetFilaSystem()->IsDetectRemainEnabled(), ams_mapping_item_container, sizer_mapping_list);
|
||||
m_amsmapping_container_sizer_list.push_back(sizer_mapping_list);
|
||||
m_amsmapping_container_list.push_back(ams_mapping_item_container);
|
||||
|
||||
if (target_panel == m_right_marea_panel) {
|
||||
if (ams_mapping_item_container->get_slots_num() == 1) {
|
||||
right_one_slot_containers.push_back(ams_mapping_item_container);
|
||||
} else {
|
||||
right_four_slot_containers.push_back(ams_mapping_item_container);
|
||||
}
|
||||
} else if (target_panel == m_left_marea_panel) {
|
||||
if (ams_mapping_item_container->get_slots_num() == 1) {
|
||||
left_one_slot_containers.push_back(ams_mapping_item_container);
|
||||
} else {
|
||||
left_four_slots_containers.push_back(ams_mapping_item_container);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = obj->vt_slot.size() - 1; i >= 0; i--) {
|
||||
DevAmsTray* tray_data = &obj->vt_slot[i];
|
||||
const auto& td_opt = sGetTrayData(tray_data, tray_data->id, DevAmsType::EXT_SPOOL);
|
||||
if (!td_opt.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) {
|
||||
auto sizer_mapping_list = new wxBoxSizer(wxHORIZONTAL);
|
||||
const auto& shown_name = td_opt->ams_id == VIRTUAL_TRAY_MAIN_ID ? "Ext-R" : "Ext-L";
|
||||
auto ams_mapping_item_container = new MappingContainer(m_right_marea_panel, shown_name, 1);
|
||||
ams_mapping_item_container->SetName(m_right_marea_panel->GetName());
|
||||
ams_mapping_item_container->SetSizer(sizer_mapping_list);
|
||||
ams_mapping_item_container->Layout();
|
||||
|
||||
std::vector<TrayData> tray_datas;
|
||||
tray_datas.push_back(td_opt.value());
|
||||
add_ams_mapping(tray_datas, false, ams_mapping_item_container, sizer_mapping_list);
|
||||
m_amsmapping_container_sizer_list.push_back(sizer_mapping_list);
|
||||
m_amsmapping_container_list.push_back(ams_mapping_item_container);
|
||||
right_one_slot_containers.push_back(ams_mapping_item_container);
|
||||
} else {
|
||||
if (td_opt->ams_id == VIRTUAL_TRAY_MAIN_ID) {
|
||||
m_right_extra_slot->send_win = send_win;
|
||||
add_ext_ams_mapping(td_opt.value(), m_right_extra_slot);
|
||||
} else if (td_opt->ams_id == VIRTUAL_TRAY_DEPUTY_ID) {
|
||||
m_left_extra_slot->send_win = send_win;
|
||||
add_ext_ams_mapping(td_opt.value(), m_left_extra_slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_add_containers(this, left_one_slot_containers, left_four_slots_containers, m_sizer_ams_basket_left);
|
||||
_add_containers(this, right_one_slot_containers, right_four_slot_containers, m_sizer_ams_basket_right);
|
||||
if (m_show_type != ShowType::LEFT_AND_RIGHT_DYNAMIC){
|
||||
m_left_split_ams_sizer->Show(!left_one_slot_containers.empty() || !left_four_slots_containers.empty());
|
||||
m_right_split_ams_sizer->Show(!right_one_slot_containers.empty() || !right_four_slot_containers.empty());
|
||||
} else {
|
||||
m_left_split_ams_sizer->Show(false);
|
||||
m_right_split_ams_sizer->Show(false);
|
||||
}
|
||||
|
||||
update_items_check_state(ams_mapping_result);
|
||||
}
|
||||
|
||||
void AmsMapingPopup::update_ams_data_multi_machines()
|
||||
{
|
||||
m_mapping_from_multi_machines = true;
|
||||
|
||||
std::vector<TrayData> tray_datas;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
TrayData td;
|
||||
td.id = i;
|
||||
td.type = EMPTY;
|
||||
td.colour = wxColour(166, 169, 170);
|
||||
td.name = "";
|
||||
td.filament_type = "";
|
||||
td.ctype = 0;
|
||||
tray_datas.push_back(td);
|
||||
}
|
||||
|
||||
for (auto& ams_container : m_amsmapping_container_list) {
|
||||
ams_container->Destroy();
|
||||
}
|
||||
|
||||
m_amsmapping_container_list.clear();
|
||||
m_amsmapping_container_sizer_list.clear();
|
||||
m_mapping_item_list.clear();
|
||||
|
||||
if (wxGetApp().dark_mode() && m_reset_btn->GetName() != "erase_dark") {
|
||||
m_reset_btn->SetName("erase_dark");
|
||||
m_reset_btn->SetBitmap(ScalableBitmap(m_right_first_text_panel, "erase_dark", 14).bmp());
|
||||
} else if (!wxGetApp().dark_mode() && m_reset_btn->GetName() != "erase") {
|
||||
m_reset_btn->SetName("erase");
|
||||
m_reset_btn->SetBitmap(ScalableBitmap(m_right_first_text_panel, "erase", 14).bmp());
|
||||
}
|
||||
|
||||
size_t nozzle_nums = 1;
|
||||
m_show_type = ShowType::RIGHT;
|
||||
|
||||
m_left_marea_panel->Hide();
|
||||
m_left_extra_slot->Hide();
|
||||
// m_left_marea_panel->Show();
|
||||
m_right_marea_panel->Show();
|
||||
set_sizer_title(m_right_split_ams_sizer, _L("AMS"));
|
||||
// m_right_tips->SetLabel(m_single_tip_text);
|
||||
m_right_extra_slot->Hide();
|
||||
m_left_extra_slot->Hide();
|
||||
|
||||
|
||||
|
||||
/*ams*/
|
||||
bool has_left_ams = false, has_right_ams = false;
|
||||
std::list<MappingContainer*> left_one_slot_containers;
|
||||
std::list<MappingContainer*> right_one_slot_containers;
|
||||
std::vector<MappingContainer*> left_four_slots_containers;
|
||||
std::vector<MappingContainer*> right_four_slot_containers;
|
||||
for (int i = 0; i < 1; i++) {
|
||||
int ams_indx = 0;
|
||||
int ams_type = 1;
|
||||
int nozzle_id = 0;
|
||||
|
||||
if (ams_type >= 1 || ams_type <= 3) { // 1:ams 2:ams-lite 3:n3f
|
||||
|
||||
auto sizer_mapping_list = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto ams_mapping_item_container = new MappingContainer(nozzle_id == 0 ? m_right_marea_panel : m_left_marea_panel, "AMS-1", 4);
|
||||
ams_mapping_item_container->SetName(nozzle_id == 0 ? m_right_marea_panel->GetName() : m_left_marea_panel->GetName());
|
||||
ams_mapping_item_container->SetSizer(sizer_mapping_list);
|
||||
ams_mapping_item_container->Layout();
|
||||
|
||||
m_has_unmatch_filament = false;
|
||||
ams_mapping_item_container->Show();
|
||||
add_ams_mapping(tray_datas, false, ams_mapping_item_container, sizer_mapping_list);
|
||||
m_amsmapping_container_sizer_list.push_back(sizer_mapping_list);
|
||||
m_amsmapping_container_list.push_back(ams_mapping_item_container);
|
||||
|
||||
if (nozzle_id == 0) {
|
||||
has_right_ams = true;
|
||||
if (ams_mapping_item_container->get_slots_num() == 1) {
|
||||
right_one_slot_containers.push_back(ams_mapping_item_container);
|
||||
} else {
|
||||
right_four_slot_containers.push_back(ams_mapping_item_container);
|
||||
}
|
||||
} else if (nozzle_id == 1) {
|
||||
has_left_ams = true;
|
||||
if (ams_mapping_item_container->get_slots_num() == 1) {
|
||||
left_one_slot_containers.push_back(ams_mapping_item_container);
|
||||
} else {
|
||||
left_four_slots_containers.push_back(ams_mapping_item_container);
|
||||
}
|
||||
}
|
||||
} else if (ams_type == 4) { // 4:n3s
|
||||
}
|
||||
}
|
||||
|
||||
_add_containers(this, left_one_slot_containers, left_four_slots_containers, m_sizer_ams_basket_left);
|
||||
_add_containers(this, right_one_slot_containers, right_four_slot_containers, m_sizer_ams_basket_right);
|
||||
m_left_split_ams_sizer->Show(has_left_ams);
|
||||
m_right_split_ams_sizer->Show(has_right_ams);
|
||||
//update_items_check_state(ams_mapping_result);
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
|
||||
|
||||
void AmsMapingPopup::update_title(MachineObject* obj)
|
||||
{
|
||||
const auto& full_config = wxGetApp().preset_bundle->full_config();
|
||||
size_t nozzle_nums = full_config.option<ConfigOptionFloatsNullable>("nozzle_diameter")->values.size();
|
||||
if (nozzle_nums > 1) {
|
||||
m_split_line_panel->Show();
|
||||
if (m_show_type == ShowType::LEFT) {
|
||||
wxString nozzle_name = obj ? _L(DevPrinterConfigUtil::get_toolhead_display_name(
|
||||
obj->printer_type, DEPUTY_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::LowerCase)) : _L("left nozzle");
|
||||
m_title_text->SetLabelText(wxString::Format(_L("Please select the filament installed on the %s."), nozzle_name));
|
||||
return;
|
||||
} else if (m_show_type == ShowType::RIGHT) {
|
||||
wxString nozzle_name = obj ? _L(DevPrinterConfigUtil::get_toolhead_display_name(
|
||||
obj->printer_type, MAIN_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::LowerCase)) : _L("right nozzle");
|
||||
m_title_text->SetLabelText(wxString::Format(_L("Please select the filament installed on the %s."), nozzle_name));
|
||||
return;
|
||||
} else if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) {
|
||||
m_title_text->SetLabelText(_L("Please select the filament."));
|
||||
return;
|
||||
}
|
||||
} else if (nozzle_nums == 1) {
|
||||
m_split_line_panel->Hide();
|
||||
}
|
||||
|
||||
m_title_text->SetLabelText(_L("Nozzle"));
|
||||
}
|
||||
|
||||
|
||||
void AmsMapingPopup::update_ams_tips(MachineObject* obj)
|
||||
{
|
||||
const auto& full_config = wxGetApp().preset_bundle->full_config();
|
||||
size_t nozzle_nums = full_config.option<ConfigOptionFloatsNullable>("nozzle_diameter")->values.size();
|
||||
if (m_ams_tips_msg_panel) {
|
||||
m_ams_tips_msg_panel->Clear();
|
||||
if (nozzle_nums == 2 && m_show_type != ShowType::LEFT_AND_RIGHT) {
|
||||
m_ams_tips_msg_panel->AddMessage(_L("To learn about the filaments matching rules."),
|
||||
"#FF6F00", ""); // Orca: dropped vendor help link
|
||||
}
|
||||
|
||||
if (obj && obj->GetFilaSwitch()->IsInstalled()) {
|
||||
const auto& msg = _L("External spools is not supported since Filament Track Switch has been installed. If you want to use external spool, please uninstall it.");
|
||||
m_ams_tips_msg_panel->AddMessage(msg, "#FF6F00", "");
|
||||
}
|
||||
|
||||
m_ams_tips_msg_panel->Layout();
|
||||
m_ams_tips_msg_panel->Fit();
|
||||
m_ams_tips_msg_panel->Show(m_ams_tips_msg_panel->GetMessageCount() > 0);
|
||||
}
|
||||
}
|
||||
|
||||
void AmsMapingPopup::update_rack_select(MachineObject* obj, bool use_dynamic_switch, std::optional<PrintFromType> print_from_type)
|
||||
{
|
||||
// Orca: MachineObject has no GetNozzleRack() convenience; route through the nozzle system instead.
|
||||
m_rack = obj ? obj->GetNozzleSystem()->GetNozzleRack() : nullptr;
|
||||
|
||||
bool show_rack_select_area = false;
|
||||
if (!m_mapping_from_multi_machines && !m_use_in_sync_dialog &&
|
||||
obj && obj->GetNozzleSystem()->GetNozzleRack()->IsSupported() &&
|
||||
print_from_type.has_value() && print_from_type.value() == PrintFromType::FROM_NORMAL) {
|
||||
const auto& nozzle_pos_vec = obj->get_nozzle_mapping_result()->GetMappedNozzlePosVecByFilaId(m_current_filament_id);
|
||||
m_rack_nozzle_select->UpdatSelectedNozzles(obj->GetNozzleSystem()->GetNozzleRack(), nozzle_pos_vec, use_dynamic_switch, print_from_type);
|
||||
show_rack_select_area = true;
|
||||
}
|
||||
|
||||
if (show_rack_select_area != m_rack_nozzle_select->IsShown()) {
|
||||
m_right_tip_text = show_rack_select_area ? _L("Select Filament && Hotends") : _L("Select Filament");
|
||||
m_right_tips->SetLabel(m_right_tip_text);
|
||||
m_rack_nozzle_select->Show(show_rack_select_area);
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
}
|
||||
|
||||
void AmsMapingPopup::update_items_check_state(const std::vector<FilamentInfo>& ams_mapping_result)
|
||||
{
|
||||
/*update check states*/
|
||||
if (m_parent_item) {
|
||||
auto update_item_check_state = [&ams_mapping_result, this](MappingItem* item) {
|
||||
if (item) {
|
||||
for (const auto& mapping_res : ams_mapping_result) {
|
||||
if (mapping_res.id == this->m_current_filament_id) {
|
||||
if (mapping_res.ams_id == std::to_string(item->m_ams_id) &&
|
||||
mapping_res.slot_id == std::to_string(item->m_slot_id)) {
|
||||
item->set_checked(true);
|
||||
} else {
|
||||
item->set_checked(false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
item->set_checked(false);
|
||||
}
|
||||
};
|
||||
|
||||
update_item_check_state(m_left_extra_slot);
|
||||
update_item_check_state(m_right_extra_slot);
|
||||
for (auto mapping_item : m_mapping_item_list) {
|
||||
update_item_check_state(mapping_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AmsMapingPopup::add_ams_mapping(std::vector<TrayData> tray_data,
|
||||
bool remain_detect_flag,
|
||||
wxWindow* container,
|
||||
wxBoxSizer* sizer)
|
||||
{
|
||||
sizer->Add(0, 0, 0, wxLEFT, FromDIP(6));
|
||||
|
||||
for (auto i = 0; i < tray_data.size(); i++) {
|
||||
|
||||
// set button
|
||||
MappingItem* m_mapping_item = new MappingItem(container);
|
||||
m_mapping_item->send_win = send_win;
|
||||
m_mapping_item->m_ams_id = tray_data[i].ams_id;
|
||||
m_mapping_item->m_slot_id = tray_data[i].slot_id;
|
||||
// Orca: transition_tridid here has no total-extruder-count overload; label the virtual (ext)
|
||||
// slots Ext-R / Ext-L inline in the two-nozzle left+right views (matches that overload's result).
|
||||
if ((m_show_type == ShowType::LEFT_AND_RIGHT || m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC)
|
||||
&& (tray_data[i].id == VIRTUAL_TRAY_MAIN_ID || tray_data[i].id == VIRTUAL_TRAY_DEPUTY_ID)) {
|
||||
m_mapping_item->set_tray_index(tray_data[i].id == VIRTUAL_TRAY_MAIN_ID ? wxString("Ext-R") : wxString("Ext-L"));
|
||||
} else {
|
||||
m_mapping_item->set_tray_index(wxGetApp().transition_tridid(tray_data[i].id));
|
||||
}
|
||||
|
||||
m_mapping_item->SetSize(wxSize(FromDIP(48), FromDIP(60)));
|
||||
m_mapping_item->SetMinSize(wxSize(FromDIP(48), FromDIP(60)));
|
||||
m_mapping_item->SetMaxSize(wxSize(FromDIP(48), FromDIP(60)));
|
||||
|
||||
// traversal if can pick the item or not
|
||||
bool can_pick_the_item = true;
|
||||
std::optional<wxString> item_tooltip_msg;
|
||||
// check filament area
|
||||
if (m_use_in_sync_dialog) {
|
||||
if (can_pick_the_item) {
|
||||
if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) {
|
||||
can_pick_the_item = !devPrinterUtil::IsVirtualSlot(m_mapping_item->m_ams_id);
|
||||
if (!can_pick_the_item) {
|
||||
item_tooltip_msg = _L(
|
||||
"External spools is not supported since Filament Track Switch has been installed. If you want to use external spool, please uninstall it.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (can_pick_the_item) {
|
||||
// LEFT_AND_RIGHT — the fallback when a filament has no entry in the
|
||||
// nozzle map — keeps both panels pickable.
|
||||
auto parent = container->GetParent();
|
||||
if (parent == m_left_marea_panel) {
|
||||
can_pick_the_item = (m_show_type == ShowType::LEFT || m_show_type == ShowType::LEFT_AND_RIGHT);
|
||||
} else if (parent == m_right_marea_panel) {
|
||||
if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) {
|
||||
can_pick_the_item = !devPrinterUtil::IsVirtualSlot(m_mapping_item->m_ams_id);
|
||||
if (!can_pick_the_item) {
|
||||
item_tooltip_msg = _L(
|
||||
"External spools is not supported since Filament Track Switch has been installed. If you want to use external spool, please uninstall it.");
|
||||
}
|
||||
} else if (m_show_type != ShowType::RIGHT && m_show_type != ShowType::LEFT_AND_RIGHT) {
|
||||
can_pick_the_item = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check filament type
|
||||
if (can_pick_the_item) {
|
||||
if (tray_data[i].type == NORMAL && !is_match_material(tray_data[i].filament_type)){
|
||||
can_pick_the_item = false;
|
||||
} else if(tray_data[i].type == EMPTY){
|
||||
// Mapping to an empty slot is only allowed from the multi-machine send page,
|
||||
// which maps onto EMPTY placeholder trays.
|
||||
can_pick_the_item = m_mapping_from_multi_machines;
|
||||
}
|
||||
}
|
||||
|
||||
wxColour display_color;
|
||||
if (can_pick_the_item) {
|
||||
display_color = tray_data[i].type == NORMAL ? tray_data[i].colour : wxColour(0xCE, 0xCE, 0xCE);
|
||||
} else {
|
||||
display_color = wxColour(0xEE, 0xEE, 0xEE);
|
||||
}
|
||||
|
||||
wxString display_name;
|
||||
if (tray_data[i].type == THIRD) {
|
||||
display_name = "?";
|
||||
} else if (tray_data[i].type == EMPTY) {
|
||||
display_name = "-";
|
||||
} else {
|
||||
display_name = tray_data[i].name;
|
||||
}
|
||||
|
||||
m_mapping_item->set_data(m_tag_material, display_color, display_name, remain_detect_flag, tray_data[i], !can_pick_the_item, item_tooltip_msg);
|
||||
m_mapping_item->Bind(wxEVT_LEFT_DOWN, [this, can_pick_the_item, m_mapping_item](wxMouseEvent& e) {
|
||||
if (can_pick_the_item) {
|
||||
m_mapping_item->send_event(m_current_filament_id);
|
||||
Dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
sizer->Add(0, 0, 0, wxRIGHT, FromDIP(6));
|
||||
sizer->Add(m_mapping_item, 0, wxTOP, FromDIP(1));
|
||||
m_mapping_item_list.push_back(m_mapping_item);
|
||||
}
|
||||
}
|
||||
|
||||
void AmsMapingPopup::add_ext_ams_mapping(TrayData tray_data, MappingItem* item)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
m_mapping_item_list.push_back(item);
|
||||
#endif
|
||||
// set button
|
||||
if (tray_data.type == NORMAL) {
|
||||
if (is_match_material(tray_data.filament_type)) {
|
||||
item->set_data(m_tag_material, tray_data.colour, tray_data.name, false, tray_data);
|
||||
} else {
|
||||
item->set_data(m_tag_material, m_ext_mapping_filatype_check ? wxColour(0xEE, 0xEE, 0xEE) : tray_data.colour, tray_data.name, false, tray_data, true);
|
||||
m_has_unmatch_filament = true;
|
||||
}
|
||||
|
||||
item->Bind(wxEVT_LEFT_DOWN, [this, tray_data, item](wxMouseEvent& e) {
|
||||
if (!item->GetParent() || !item->GetParent()->IsEnabled()) return;
|
||||
if (m_ext_mapping_filatype_check && !is_match_material(tray_data.filament_type)) return;
|
||||
item->send_event(m_current_filament_id);
|
||||
Dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// temp
|
||||
if (tray_data.type == EMPTY) {
|
||||
item->set_data(m_tag_material, wxColour(0xCE, 0xCE, 0xCE), "-", false, tray_data);
|
||||
item->Bind(wxEVT_LEFT_DOWN, [this, tray_data, item](wxMouseEvent& e) {
|
||||
if (!item->GetParent() || !item->GetParent()->IsEnabled()) return;
|
||||
item->send_event(m_current_filament_id);
|
||||
Dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
// third party
|
||||
if (tray_data.type == THIRD) {
|
||||
item->set_data(m_tag_material, tray_data.colour, "?", false, tray_data);
|
||||
//item->set_data(wxColour(0xCE, 0xCE, 0xCE), "?", tray_data);
|
||||
item->Bind(wxEVT_LEFT_DOWN, [this, tray_data, item](wxMouseEvent& e) {
|
||||
if (!item->GetParent() || !item->GetParent()->IsEnabled()) return;
|
||||
item->send_event(m_current_filament_id);
|
||||
Dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
item->set_tray_index("Ext");
|
||||
}
|
||||
|
||||
} // namespace Slic3r::GUI
|
||||
@@ -813,7 +813,7 @@ PingCodeBindDialog::~PingCodeBindDialog() {
|
||||
|
||||
m_simplebook->SetSelection(0);
|
||||
auto m_bind_job = std::make_unique<BindJob>(
|
||||
m_machine_info->get_dev_id(), m_machine_info->get_dev_ip(), m_machine_info->bind_sec_link, m_machine_info->bind_ssdp_version);
|
||||
m_machine_info->get_dev_id(), m_machine_info->get_dev_ip(), m_machine_info->get_show_printer_type(), m_machine_info->bind_sec_link, m_machine_info->bind_ssdp_version);
|
||||
|
||||
if (m_machine_info && (m_machine_info->get_printer_series() == PrinterSeries::SERIES_X1)) {
|
||||
m_bind_job->set_improved(false);
|
||||
|
||||
@@ -102,7 +102,7 @@ CameraPopup::CameraPopup(wxWindow *parent)
|
||||
top_sizer->Add(0, 0, wxALL, 0);
|
||||
}
|
||||
|
||||
// custom IP camera
|
||||
// Orca: custom IP camera source — lets the user point Live Video at any camera URL (Orca feature; not in the reference)
|
||||
m_custom_camera_input_confirm = new Button(m_panel, _L("Enable"));
|
||||
m_custom_camera_input_confirm->SetBackgroundColor(wxColour(38, 166, 154));
|
||||
m_custom_camera_input_confirm->SetBorderColor(wxColour(38, 166, 154));
|
||||
@@ -132,7 +132,7 @@ CameraPopup::CameraPopup(wxWindow *parent)
|
||||
top_sizer->Add(m_custom_camera_input_confirm, 1, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, FromDIP(5));
|
||||
main_sizer->Add(top_sizer, 0, wxALL, FromDIP(10));
|
||||
|
||||
auto url = wxString::Format(L"https://wiki.bambulab.com/%s/software/bambu-studio/virtual-camera", L"en");
|
||||
auto url = wxString(L"https://www.orcaslicer.com/wiki/"); // Orca: neutral wiki link (vendor URL removed)
|
||||
auto text = _L("Show \"Live Video\" guide page.");
|
||||
|
||||
wxBoxSizer* link_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
@@ -479,7 +479,7 @@ void CameraPopup::OnLeftUp(wxMouseEvent &event)
|
||||
//hyper link
|
||||
auto h_rect = vcamera_guide_link->ClientToScreen(wxPoint(0, 0));
|
||||
if (mouse_pos.x > h_rect.x && mouse_pos.y > h_rect.y && mouse_pos.x < (h_rect.x + vcamera_guide_link->GetSize().x) && mouse_pos.y < (h_rect.y + vcamera_guide_link->GetSize().y)) {
|
||||
auto url = wxString::Format(L"https://wiki.bambulab.com/%s/software/bambu-studio/virtual-camera", L"en");
|
||||
auto url = wxString(L"https://www.orcaslicer.com/wiki/"); // Orca: neutral wiki link (vendor URL removed)
|
||||
wxLaunchDefaultBrowser(url);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,19 @@ list(APPEND SLIC3R_GUI_SOURCES
|
||||
GUI/DeviceCore/DevUtil.cpp
|
||||
GUI/DeviceCore/DevUtilBackend.h
|
||||
GUI/DeviceCore/DevUtilBackend.cpp
|
||||
GUI/DeviceCore/DevAxis.h
|
||||
GUI/DeviceCore/DevAxis.cpp
|
||||
GUI/DeviceCore/DevAxisCtrl.cpp
|
||||
GUI/DeviceCore/DevCalib.h
|
||||
GUI/DeviceCore/DevCalib.cpp
|
||||
GUI/DeviceCore/DevChamber.h
|
||||
GUI/DeviceCore/DevChamber.cpp
|
||||
GUI/DeviceCore/DevChamberCtrl.cpp
|
||||
GUI/DeviceCore/DevStatus.h
|
||||
GUI/DeviceCore/DevStatus.cpp
|
||||
GUI/DeviceCore/DevUpgrade.h
|
||||
GUI/DeviceCore/DevUpgrade.cpp
|
||||
GUI/DeviceCore/DevUpgradeCtrl.cpp
|
||||
|
||||
)
|
||||
set(SLIC3R_GUI_SOURCES ${SLIC3R_GUI_SOURCES} PARENT_SCOPE)
|
||||
25
src/slic3r/GUI/DeviceCore/DevAxis.cpp
Normal file
25
src/slic3r/GUI/DeviceCore/DevAxis.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "DevAxis.h"
|
||||
#include "DevUtil.h"
|
||||
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
void DevAxis::ParseAxis(const json& print_json)
|
||||
{
|
||||
DevJsonValParser::ParseVal(print_json, "home_flag", m_home_flag);
|
||||
|
||||
if (print_json.contains("fun")) {
|
||||
const std::string& fun = print_json["fun"].get<std::string>();
|
||||
m_is_support_mqtt_homing = DevUtil::get_flag_bits(fun, 32);
|
||||
m_is_support_mqtt_axis_ctrl = DevUtil::get_flag_bits(fun, 38);
|
||||
}
|
||||
}
|
||||
|
||||
bool DevAxis::IsArchCoreXY() const
|
||||
{
|
||||
return m_owner->get_printer_arch() == PrinterArch::ARCH_CORE_XY;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
39
src/slic3r/GUI/DeviceCore/DevAxis.h
Normal file
39
src/slic3r/GUI/DeviceCore/DevAxis.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class MachineObject;
|
||||
|
||||
class DevAxis
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<DevAxis> Create(MachineObject *obj) { return std::shared_ptr<DevAxis>( new DevAxis(obj)); }
|
||||
virtual ~DevAxis() = default;
|
||||
|
||||
public:
|
||||
bool IsAxisAtHomeX() const { return m_home_flag == 0 ? true : (m_home_flag & 1) == 1; }
|
||||
bool IsAxisAtHomeY() const { return m_home_flag == 0 ? true : ((m_home_flag >> 1) & 1) == 1; }
|
||||
bool IsAxisAtHomeZ() const { return m_home_flag == 0 ? true : ((m_home_flag >> 2) & 1) == 1; }
|
||||
|
||||
bool IsArchCoreXY() const;
|
||||
|
||||
public:
|
||||
void ParseAxis(const json &print_json);
|
||||
|
||||
int Ctrl_GoHome();
|
||||
int Ctrl_Axis(std::string axis, double unit = 1.0f, double input_val = 1.0f, int speed = 3000); // xyz e
|
||||
|
||||
protected:
|
||||
DevAxis(MachineObject *obj) noexcept : m_owner(obj) {}
|
||||
|
||||
private:
|
||||
MachineObject *m_owner = nullptr;
|
||||
|
||||
int m_home_flag = 0; // bit0:X, bit1:Y, bit2:Z
|
||||
bool m_is_support_mqtt_axis_ctrl = false;
|
||||
bool m_is_support_mqtt_homing = false;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
60
src/slic3r/GUI/DeviceCore/DevAxisCtrl.cpp
Normal file
60
src/slic3r/GUI/DeviceCore/DevAxisCtrl.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#include "DevAxis.h"
|
||||
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
#include "slic3r/Utils/NetworkAgent.hpp"
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
int DevAxis::Ctrl_GoHome()
|
||||
{
|
||||
if (m_is_support_mqtt_homing) {
|
||||
json j;
|
||||
j["print"]["command"] = "back_to_center";
|
||||
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
return m_owner->publish_json(j);
|
||||
}
|
||||
|
||||
// gcode command
|
||||
return m_owner->is_in_printing() ? m_owner->publish_gcode("G28 X\n") : m_owner->publish_gcode("G28 \n");
|
||||
}
|
||||
|
||||
int DevAxis::Ctrl_Axis(std::string axis, double unit, double input_val, int speed)
|
||||
{
|
||||
if (m_is_support_mqtt_axis_ctrl) {
|
||||
int dir = input_val > 0 ? 1 : -1;
|
||||
if (!IsArchCoreXY()) {
|
||||
if (axis.compare("Y") == 0 || axis.compare("Z") == 0) {
|
||||
dir = -dir;
|
||||
}
|
||||
}
|
||||
json j;
|
||||
j["print"]["command"] = "xyz_ctrl";
|
||||
j["print"]["axis"] = axis;
|
||||
j["print"]["dir"] = dir;
|
||||
j["print"]["mode"] = (std::abs(input_val) >= 10) ? 1 : 0;
|
||||
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
return m_owner->publish_json(j);
|
||||
}
|
||||
|
||||
double value = input_val;
|
||||
if (!IsArchCoreXY()) {
|
||||
if (axis.compare("Y") == 0 || axis.compare("Z") == 0) {
|
||||
value = -1.0 * input_val;
|
||||
}
|
||||
}
|
||||
|
||||
char cmd[256];
|
||||
if (axis.compare("X") == 0 || axis.compare("Y") == 0 || axis.compare("Z") == 0) {
|
||||
sprintf(cmd, "M211 S \nM211 X1 Y1 Z1\nM1002 push_ref_mode\nG91 \nG1 %s%0.1f F%d\nM1002 pop_ref_mode\nM211 R\n", axis.c_str(), value * unit, speed);
|
||||
} else if (axis.compare("E") == 0) {
|
||||
sprintf(cmd, "M83 \nG0 %s%0.1f F%d\n", axis.c_str(), value * unit, speed);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Orca: strip analytics telemetry (matches MachineObject::command_axis_control)
|
||||
return m_owner->publish_gcode(cmd);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
337
src/slic3r/GUI/DeviceCore/DevCalib.cpp
Normal file
337
src/slic3r/GUI/DeviceCore/DevCalib.cpp
Normal file
@@ -0,0 +1,337 @@
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
|
||||
#include "slic3r/GUI/UserNotification.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
|
||||
#include <wx/dir.h>
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
#include "DevCalib.h"
|
||||
#include "DevDefs.h"
|
||||
#include "DevFilaSystem.h"
|
||||
#include "DevConfig.h"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
static float string_to_float(const std::string &str_value)
|
||||
{
|
||||
float value = 0.0;
|
||||
fast_float::from_chars(str_value.c_str(), str_value.c_str() + str_value.size(), value);
|
||||
return value;
|
||||
}
|
||||
|
||||
static NozzleVolumeType convert_to_nozzle_type(const std::string &str)
|
||||
{
|
||||
if (str.size() < 8) {
|
||||
assert(false && "invalid nozzle info");
|
||||
return NozzleVolumeType::nvtStandard;
|
||||
}
|
||||
|
||||
if (str[1] == 'S')
|
||||
return NozzleVolumeType::nvtStandard;
|
||||
else if (str[1] == 'H')
|
||||
return NozzleVolumeType::nvtHighFlow;
|
||||
else if (str[1] == 'U')
|
||||
return NozzleVolumeType::nvtTPUHighFlow;
|
||||
// Orca: no nvtE3DHighFlow in Orca's NozzleVolumeType; map 'B' to Standard
|
||||
else
|
||||
return NozzleVolumeType::nvtStandard;
|
||||
}
|
||||
|
||||
static float get_number_flexible(const json& j, const std::string& key, float def = 0.0f)
|
||||
{
|
||||
if (!j.contains(key)) return def;
|
||||
|
||||
const auto& v = j[key];
|
||||
|
||||
if (v.is_number_float()) return v.get<float>();
|
||||
if (v.is_number_integer()) return static_cast<float>(v.get<int>());
|
||||
if (v.is_string()) return string_to_float(v.get<std::string>());
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
void from_json(const json& j, PACalibResult& cali) {
|
||||
cali.extruder_id = j.value("extruder_id", 0);
|
||||
cali.nozzle_volume_type = convert_to_nozzle_type(j.value("nozzle_id", "HS00-0.4"));
|
||||
cali.tray_id = j.value("tray_id",0);
|
||||
cali.ams_id = j.value("ams_id",0);
|
||||
cali.slot_id = j.value("slot_id",0);
|
||||
cali.cali_idx = j.value("cali_idx",-1);
|
||||
cali.nozzle_pos_id = j.value("nozzle_pos",-1);
|
||||
cali.nozzle_diameter = get_number_flexible(j, "nozzle_diameter", 0.4f);
|
||||
cali.nozzle_sn = j.value("nozzle_sn","");
|
||||
cali.filament_id = j.value("filament_id","");
|
||||
cali.setting_id = j.value("setting_id","");
|
||||
cali.name = j.value("name","");
|
||||
cali.k_value = get_number_flexible(j, "k_value", 0.0f);
|
||||
cali.n_coef = get_number_flexible(j, "n_coef", 0.0f);
|
||||
cali.confidence = j.value("confidence", 0);
|
||||
}
|
||||
|
||||
void from_json(const json& j, FlowRatioCalibResult& cali)
|
||||
{
|
||||
cali.tray_id = j.value("tray_id", 0);
|
||||
cali.nozzle_diameter = string_to_float(j.value("nozzle_diameter", ""));
|
||||
cali.filament_id = j.value("filament_id", "");
|
||||
cali.setting_id = j.value("setting_id", "");
|
||||
cali.flow_ratio = string_to_float(j.value("flow_ratio", ""));
|
||||
cali.confidence = j.value("confidence", 0);
|
||||
}
|
||||
|
||||
void DevCalib::ParseCalibVersion(const json& j, DevCalib* system)
|
||||
{
|
||||
if(system) system->m_calib_version = j.value("cali_version", -1);
|
||||
}
|
||||
|
||||
bool DevCalib::IsVersionExpired() const
|
||||
{
|
||||
if (m_last_calib_version.has_value())
|
||||
return m_last_calib_version.value() != m_calib_version;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
void DevCalib::ParseSupportNewAutoCalib(int flag, DevCalib* system)
|
||||
{
|
||||
if(system) system->m_support_new_auto_cali = flag;
|
||||
}
|
||||
|
||||
void DevCalib::RequestPAResult()
|
||||
{
|
||||
m_pa_results_status = CalibStatus::REQUEST;
|
||||
}
|
||||
|
||||
void DevCalib::ResetPAResult()
|
||||
{
|
||||
m_pa_calib_results.clear();
|
||||
m_pa_results_status = CalibStatus::IDLE;
|
||||
}
|
||||
|
||||
int DevCalib::RequestPAHistory(const PACalibExtruderInfo &calib_info)
|
||||
{
|
||||
m_pa_calib_tab.clear();
|
||||
m_pa_table_status = CalibStatus::REQUEST;
|
||||
|
||||
return GetOwner()->command_get_pa_calibration_tab(calib_info);
|
||||
}
|
||||
|
||||
void DevCalib::ResetPAHistory()
|
||||
{
|
||||
m_pa_calib_tab.clear();
|
||||
m_pa_table_status = CalibStatus::IDLE;
|
||||
}
|
||||
|
||||
void DevCalib::RequestFlowRateResult()
|
||||
{
|
||||
m_flow_results_status = CalibStatus::REQUEST;
|
||||
}
|
||||
|
||||
void DevCalib::ResetFlowRateResult()
|
||||
{
|
||||
m_flow_ratio_results.clear();
|
||||
m_flow_results_status = CalibStatus::IDLE;
|
||||
}
|
||||
|
||||
void calib_fail_message(MachineObject* obj, std::string cali_mode, std::string reason){
|
||||
wxString info;
|
||||
if (reason == "invalid nozzle_diameter" || reason == "nozzle_diameter is not supported") {
|
||||
info = _L("This calibration does not support the currently selected nozzle diameter");
|
||||
} else if (reason == "invalid handle_flowrate_cali param") {
|
||||
info = _L("Current flowrate cali param is invalid");
|
||||
} else if (reason == "nozzle_diameter is not matched") {
|
||||
info = _L("Selected diameter and machine diameter do not match");
|
||||
} else if (reason == "generate auto filament cali gcode failure") {
|
||||
info = _L("Failed to generate cali gcode");
|
||||
} else {
|
||||
info = wxString(reason);
|
||||
}
|
||||
|
||||
GUI::wxGetApp().push_notification(obj, info, _L("Calibration error"), UserNotificationStyle::UNS_WARNING_CONFIRM);
|
||||
BOOST_LOG_TRIVIAL(info) << cali_mode << " result fail, reason = " << reason;
|
||||
}
|
||||
|
||||
void DevCalib::ExtrusionCalibSetParse(const json & jj){
|
||||
int tray_id = jj.value("tray_id", -1);
|
||||
|
||||
auto tray_ams_slot_map = GetOwner()->GetFilaSystem()->GetTrayIndexMap();
|
||||
int ams_id = tray_ams_slot_map.find(tray_id) != tray_ams_slot_map.end() ? tray_ams_slot_map[tray_id].first : -1;
|
||||
int slot_id = tray_ams_slot_map.find(tray_id) != tray_ams_slot_map.end() ? tray_ams_slot_map[tray_id].second : -1;
|
||||
|
||||
if(tray_id == VIRTUAL_TRAY_MAIN_ID) {
|
||||
GetOwner()->vt_slot[MAIN_EXTRUDER_ID].k = jj.value("k_value", GetOwner()->vt_slot[MAIN_EXTRUDER_ID].k);
|
||||
GetOwner()->vt_slot[MAIN_EXTRUDER_ID].n = jj.value("n_value", GetOwner()->vt_slot[MAIN_EXTRUDER_ID].n);
|
||||
}else{
|
||||
auto tray_item = GetOwner()->GetFilaSystem()->GetAmsTray(std::to_string(ams_id), std::to_string(slot_id));
|
||||
if (tray_item) {
|
||||
tray_item->k = jj.value("k_value", tray_item->k);
|
||||
tray_item->n = jj.value("n_coef", tray_item->n);
|
||||
}
|
||||
}
|
||||
|
||||
GetOwner()->extrusion_cali_set_tray_id = tray_id;
|
||||
GetOwner()->extrusion_cali_set_hold_start = std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
/* calib select ack parse */
|
||||
void DevCalib::ExtrusionCalibSelectParse(const json &jj){
|
||||
try{
|
||||
int tray_id = jj.value("tray_id", -1);
|
||||
|
||||
auto tray_ams_slot_map = GetOwner()->GetFilaSystem()->GetTrayIndexMap();
|
||||
int default_ams_id = tray_ams_slot_map.find(tray_id) != tray_ams_slot_map.end() ? tray_ams_slot_map[tray_id].first : -1;
|
||||
int default_slot_id = tray_ams_slot_map.find(tray_id) != tray_ams_slot_map.end() ? tray_ams_slot_map[tray_id].second : -1;
|
||||
|
||||
int ams_id = jj.value("ams_id", default_ams_id);
|
||||
int slot_id = jj.value("slot_id", default_slot_id);
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "extrusion_cali_sel: illegal ams_id = " << ams_id << "slot_id = " << slot_id;
|
||||
|
||||
std::vector<DevAmsTray> &vt_slot = GetOwner()->vt_slot;
|
||||
if (ams_id == VIRTUAL_TRAY_MAIN_ID && vt_slot.size() > 0) {
|
||||
vt_slot[MAIN_EXTRUDER_ID].cali_idx = jj.value("cali_idx", vt_slot[MAIN_EXTRUDER_ID].cali_idx);
|
||||
vt_slot[MAIN_EXTRUDER_ID].set_hold_count();
|
||||
} else if (ams_id == VIRTUAL_TRAY_DEPUTY_ID && vt_slot.size() > 1) {
|
||||
vt_slot[DEPUTY_EXTRUDER_ID].cali_idx = jj.value("cali_idx", vt_slot[DEPUTY_EXTRUDER_ID].cali_idx);
|
||||
vt_slot[DEPUTY_EXTRUDER_ID].set_hold_count();
|
||||
} else {
|
||||
auto tray_item = GetOwner()->GetFilaSystem()->GetAmsTray(std::to_string(ams_id), std::to_string(slot_id));
|
||||
if (tray_item) {
|
||||
tray_item->cali_idx = jj.value("cali_idx", tray_item->cali_idx);
|
||||
tray_item->set_hold_count();
|
||||
}
|
||||
}
|
||||
} catch(...){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void DevCalib::ExtrusionCalibGetTableParse(const json &jj){
|
||||
if (GetPAHistoryStatus() == CalibStatus::REQUEST ) {
|
||||
m_pa_table_status = CalibStatus::WAITING;
|
||||
|
||||
/* request success */
|
||||
if (!(jj.contains("result") && jj.contains("reason") && jj["result"].get<std::string>() == "fail")) {
|
||||
SyncCalibVersion();
|
||||
m_pa_table_status = CalibStatus::FINISHED;
|
||||
}
|
||||
|
||||
try{
|
||||
json filaments_json;
|
||||
if(jj.contains("filaments"))
|
||||
{
|
||||
/* fill item->nozzle_diameter with command->nozzle_diameter */
|
||||
filaments_json = jj["filaments"];
|
||||
for (auto &f : filaments_json) {
|
||||
if (!f.contains("nozzle_diameter") && jj.contains("nozzle_diameter")) {
|
||||
f["nozzle_diameter"] = jj["nozzle_diameter"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_pa_calib_tab = filaments_json.get<std::vector<PACalibResult>>();
|
||||
|
||||
/* filter invalid pa_calib_tab */
|
||||
m_pa_calib_tab.erase(std::remove_if(m_pa_calib_tab.begin(), m_pa_calib_tab.end(), [](auto &res) { return res.k_value < 0.0f || res.k_value > 10.0f; }), m_pa_calib_tab.end());
|
||||
|
||||
if (m_pa_calib_tab.empty()) { BOOST_LOG_TRIVIAL(info) << "empty pa calib history"; }
|
||||
}catch(...){
|
||||
m_pa_calib_tab.clear();
|
||||
BOOST_LOG_TRIVIAL(error) << "pa calib history missing fields, current json:\n "<< jj.dump();
|
||||
}
|
||||
// notify cali history to update
|
||||
}
|
||||
}
|
||||
|
||||
void DevCalib::ExtrusionCalibGetResultParse(const json &jj)
|
||||
{
|
||||
m_pa_results_status = CalibStatus::WAITING;
|
||||
|
||||
if (!(jj.contains("result") && jj.contains("reason") && jj["result"].get<std::string>() == "fail" && jj.contains("err_code"))) {
|
||||
m_pa_results_status = CalibStatus::FINISHED;
|
||||
}
|
||||
|
||||
try {
|
||||
json filaments_json;
|
||||
if(jj.contains("filaments"))
|
||||
{
|
||||
/* fill item->nozzle_diameter with command->nozzle_diameter */
|
||||
filaments_json = jj["filaments"];
|
||||
for (auto &f : filaments_json) {
|
||||
if (!f.contains("nozzle_diameter") && jj.contains("nozzle_diameter") ) {
|
||||
f["nozzle_diameter"] = jj["nozzle_diameter"];
|
||||
}
|
||||
|
||||
if (IsSupportNewAutoCali()) {
|
||||
auto ams_id = f.value("ams_id", 0);
|
||||
auto slot_id = f.value("slot_id", 0);
|
||||
if(f.contains("tray_id")){
|
||||
// Orca: Orca's DevFilaSystem has no GetTrayIdByAmsSlotId; mirror its
|
||||
// semantics via a reverse lookup over GetTrayIndexMap() (correct for
|
||||
// non-4-slot AMS layouts, unlike a fixed ams_id*4+slot_id formula).
|
||||
int mapped_tray_id = -1;
|
||||
const auto tray_ams_slot_map = GetOwner()->GetFilaSystem()->GetTrayIndexMap();
|
||||
for (const auto& item : tray_ams_slot_map) {
|
||||
if (item.second.first == ams_id && item.second.second == slot_id) {
|
||||
mapped_tray_id = item.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
f["tray_id"] = mapped_tray_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_pa_calib_results = filaments_json.get<std::vector<PACalibResult>>();
|
||||
|
||||
m_pa_calib_results.erase(std::remove_if(m_pa_calib_results.begin(), m_pa_calib_results.end(), [](auto &res) { return res.k_value < 0.0f || res.k_value > 10.0f; }), m_pa_calib_results.end());
|
||||
|
||||
if (m_pa_calib_results.empty()) { BOOST_LOG_TRIVIAL(info) << "empty pa calib result"; }
|
||||
} catch (...) {
|
||||
m_pa_calib_results.clear();
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "pa calibration results missing fileds, current json: \n"<<jj.dump();
|
||||
}
|
||||
}
|
||||
|
||||
void DevCalib::FlowrateGetResultParse(const json &jj){
|
||||
m_flow_results_status = CalibStatus::FINISHED;
|
||||
m_flow_ratio_results.clear();
|
||||
|
||||
if(!jj.contains("filaments")) return;
|
||||
|
||||
try {
|
||||
m_flow_ratio_results = jj["filaments"].get<std::vector<FlowRatioCalibResult>>();
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "flow ratio calibration results missing fileds, current json:\n"<<jj.dump();
|
||||
}
|
||||
}
|
||||
|
||||
void DevCalib::ParseV1_0(const json &jj, DevCalib *system, bool key_field_only)
|
||||
{
|
||||
if(!jj.contains("command")) return;
|
||||
|
||||
if (jj["command"].get<std::string>() == "extrusion_cali" || jj["command"].get<std::string>() == "flowrate_cali") {
|
||||
if (jj.contains("result")) {
|
||||
if (jj["result"].get<std::string>() == "success") {
|
||||
} else if (jj["result"].get<std::string>() == "fail") {
|
||||
std::string cali_mode = jj["command"].get<std::string>();
|
||||
std::string reason = jj["reason"].get<std::string>();
|
||||
calib_fail_message(system->GetOwner(), cali_mode, reason);
|
||||
}
|
||||
}
|
||||
} else if (jj["command"].get<std::string>() == "extrusion_cali_set") {
|
||||
system->ExtrusionCalibSetParse(jj);
|
||||
} else if (jj["command"].get<std::string>() == "extrusion_cali_sel") {
|
||||
system->ExtrusionCalibSelectParse(jj);
|
||||
} else if (jj["command"].get<std::string>() == "extrusion_cali_get") {
|
||||
system->ExtrusionCalibGetTableParse(jj);
|
||||
} else if (jj["command"].get<std::string>() == "extrusion_cali_get_result") {
|
||||
system->ExtrusionCalibGetResultParse(jj);
|
||||
} else if (jj["command"].get<std::string>() == "flowrate_get_result" && !key_field_only) {
|
||||
system->FlowrateGetResultParse(jj);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
127
src/slic3r/GUI/DeviceCore/DevCalib.h
Normal file
127
src/slic3r/GUI/DeviceCore/DevCalib.h
Normal file
@@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
|
||||
|
||||
#include "DevDefs.h"
|
||||
#include "libslic3r/calib.hpp" // Orca: lowercase filename (Linux case-sensitive)
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class MachineObject;
|
||||
|
||||
|
||||
enum class CalibStatus{
|
||||
IDLE = 0,
|
||||
REQUEST,
|
||||
WAITING,
|
||||
FINISHED,
|
||||
};
|
||||
|
||||
enum class ManualPaCaliMethod {
|
||||
PA_LINE = 0,
|
||||
PA_PATTERN,
|
||||
};
|
||||
|
||||
class DevCalib
|
||||
{
|
||||
protected:
|
||||
bool m_support_new_auto_cali{false};
|
||||
int m_calib_version {-1};
|
||||
std::optional<int> m_last_calib_version;
|
||||
|
||||
public:
|
||||
DevCalib(MachineObject *obj) : m_owner(obj){};
|
||||
MachineObject* GetOwner() const {return m_owner; };
|
||||
|
||||
void RequestPAResult();
|
||||
CalibStatus GetPAResultStatus() const {return m_pa_results_status;}
|
||||
bool IsPAResultReady() const { return m_pa_results_status == CalibStatus::FINISHED;}
|
||||
void ResetPAResult();
|
||||
|
||||
/* calib history */
|
||||
int RequestPAHistory(const PACalibExtruderInfo &calib_info);
|
||||
CalibStatus GetPAHistoryStatus() const {return m_pa_table_status;}
|
||||
bool IsPAHistoryReady() const { return m_pa_table_status == CalibStatus::FINISHED;}
|
||||
void ResetPAHistory();
|
||||
|
||||
void RequestFlowRateResult();
|
||||
CalibStatus GetFlowRateResultStatus() const {return m_flow_results_status;}
|
||||
bool IsFlowRateReady() const { return m_flow_results_status == CalibStatus::FINISHED;}
|
||||
void ResetFlowRateResult();
|
||||
|
||||
int GetCalibVersion() const {return m_calib_version;}
|
||||
void SyncCalibVersion() { if (IsVersionInited()) { m_last_calib_version = m_calib_version; } }
|
||||
void ResetCalibVersion() {m_last_calib_version.reset();}
|
||||
bool IsVersionExpired() const;
|
||||
bool IsVersionInited() const { return m_calib_version > -1;}
|
||||
|
||||
bool IsSupportNewAutoCali() const {return m_support_new_auto_cali;}
|
||||
|
||||
public:
|
||||
void SetStashCalibFinished(bool finished) {m_calib_finished = finished;}
|
||||
bool GetStashCalibFinished() { return m_calib_finished;}
|
||||
|
||||
void SetStashFlowRatio(float ratio) { m_flow_ratio = ratio;}
|
||||
float GetStashFlowRatio() const {return m_flow_ratio;}
|
||||
|
||||
FlowRatioCalibrationType GetFlowRatioCalibType() {return m_flow_ratio_calibration_type;}
|
||||
void SetFlowRatioCalibType(const FlowRatioCalibrationType &type) { m_flow_ratio_calibration_type = type; }
|
||||
|
||||
ManualPaCaliMethod GetManualPaCalibMethod() {return m_manual_pa_cali_method;}
|
||||
void SetManualPaCalibMethod(const ManualPaCaliMethod& method) { m_manual_pa_cali_method = method;}
|
||||
|
||||
NozzleDiameterType GetSelectedNozzleDiameter() {return m_selected_nozzle_diameter;}
|
||||
void SetSelectedNozzleDiameter(const NozzleDiameterType& diameter) { m_selected_nozzle_diameter = diameter;}
|
||||
|
||||
std::vector<CaliPresetInfo> GetSelectedCalibPreset() {return m_selected_calib_preset;}
|
||||
void ResetSelectedCalibPreset() { m_selected_calib_preset.clear();}
|
||||
void SetSelectedCalibPreset(const std::vector<CaliPresetInfo>& preset) { m_selected_calib_preset = preset;}
|
||||
|
||||
std::vector<PACalibResult> GetPAHistory() const {return m_pa_calib_tab; }
|
||||
std::vector<PACalibResult> GetPAResult() const {return m_pa_calib_results; }
|
||||
std::vector<FlowRatioCalibResult> GetFlowRatioResult() const {return m_flow_ratio_results; }
|
||||
|
||||
protected:
|
||||
void ExtrusionCalibSetParse(const json &jj);
|
||||
void ExtrusionCalibSelectParse(const json &jj);
|
||||
void ExtrusionCalibGetTableParse(const json &jj);
|
||||
void ExtrusionCalibGetResultParse(const json &jj);
|
||||
void FlowrateGetResultParse(const json &jj);
|
||||
|
||||
private:
|
||||
MachineObject* m_owner{nullptr};
|
||||
|
||||
std::vector<PACalibResult> m_pa_calib_tab;
|
||||
std::vector<PACalibResult> m_pa_calib_results;
|
||||
std::vector<FlowRatioCalibResult> m_flow_ratio_results;
|
||||
|
||||
CalibStatus m_pa_results_status{CalibStatus::IDLE};
|
||||
CalibStatus m_pa_table_status{CalibStatus::IDLE};
|
||||
CalibStatus m_flow_results_status{CalibStatus::IDLE};
|
||||
|
||||
FlowRatioCalibrationType m_flow_ratio_calibration_type{FlowRatioCalibrationType::COMPLETE_CALIBRATION};
|
||||
ManualPaCaliMethod m_manual_pa_cali_method{ManualPaCaliMethod::PA_LINE};
|
||||
|
||||
// calibration page selected info
|
||||
// 1: record when start calibration in preset page
|
||||
// 2: reset when start calibration in start page
|
||||
// 3: save tray_id, filament_id, setting_id, and name, nozzle_dia
|
||||
// std::vector<CaliPresetInfo> selected_cali_preset;
|
||||
NozzleDiameterType m_selected_nozzle_diameter{NozzleDiameterType::NONE_DIAMETER_TYPE};
|
||||
std::vector<CaliPresetInfo> m_selected_calib_preset;
|
||||
|
||||
// stash calibrating info
|
||||
float m_flow_ratio { 0.0 };
|
||||
bool m_calib_finished{false};
|
||||
|
||||
public:
|
||||
static void ParseCalibVersion(const json& j, DevCalib* system);
|
||||
|
||||
static void ParseSupportNewAutoCalib(int flag, DevCalib* system);
|
||||
|
||||
static void ParseV1_0(const json& print_json, DevCalib* system, bool key_field_only);
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
50
src/slic3r/GUI/DeviceCore/DevChamber.cpp
Normal file
50
src/slic3r/GUI/DeviceCore/DevChamber.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "DevChamber.h"
|
||||
|
||||
#include "DevConfig.h"
|
||||
#include "DevUtil.h"
|
||||
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
bool DevChamber::HasChamber() const { return m_owner->GetConfig()->HasChamber(); }
|
||||
|
||||
bool DevChamber::SupportChamberTempDisplay() const { return m_owner->GetConfig()->SupportChamberTempDisplay();}
|
||||
|
||||
bool DevChamber::SupportChamberEdit() const { return m_owner->GetConfig()->SupportChamberEdit(); }
|
||||
|
||||
int DevChamber::GetChamberTempEditMin() const { return m_owner->GetConfig()->GetChamberTempEditMin(); }
|
||||
|
||||
int DevChamber::GetChamberTempEditMax() const { return m_owner->GetConfig()->GetChamberTempEditMax(); }
|
||||
|
||||
int DevChamber::GetChamberTempSwitchHeat() const { return m_owner->GetConfig()->GetChamberTempSwitchHeat(); }
|
||||
|
||||
void DevChamber::ParseChamber(const json &print_json)
|
||||
{
|
||||
ParseChamberV1_0(print_json);
|
||||
ParseChamberV2_0(print_json);
|
||||
}
|
||||
|
||||
void DevChamber::ParseChamberV1_0(const json &print_json)
|
||||
{
|
||||
DevJsonValParser::ParseVal(print_json, "chamber_temper", m_temp);
|
||||
DevJsonValParser::ParseVal(print_json, "ctt", m_temp_target);
|
||||
}
|
||||
|
||||
void DevChamber::ParseChamberV2_0(const json &print_json)
|
||||
{
|
||||
if (print_json.contains("device")) {
|
||||
const json &device_jj = print_json["device"];
|
||||
if (device_jj.contains("ctc")) {
|
||||
const json &ctc = device_jj["ctc"];
|
||||
int state = DevUtil::get_flag_bits(ctc["state"].get<int>(), 0, 4);
|
||||
if (ctc.contains("info")) {
|
||||
const json &info = ctc["info"];
|
||||
m_temp = DevUtil::get_flag_bits(info["temp"].get<int>(), 0, 16);
|
||||
m_temp_target = DevUtil::get_flag_bits(info["temp"].get<int>(), 16, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
44
src/slic3r/GUI/DeviceCore/DevChamber.h
Normal file
44
src/slic3r/GUI/DeviceCore/DevChamber.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class MachineObject;
|
||||
|
||||
class DevChamber
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<DevChamber> Create(MachineObject *obj) { return std::shared_ptr<DevChamber>( new DevChamber(obj)); }
|
||||
|
||||
public: // getter
|
||||
bool HasChamber() const;
|
||||
bool SupportChamberTempDisplay() const;
|
||||
bool SupportChamberEdit() const;
|
||||
int GetChamberTempEditMin() const;
|
||||
int GetChamberTempEditMax() const;
|
||||
int GetChamberTempSwitchHeat() const;
|
||||
|
||||
float GetChamberTemp() const { return m_temp; };
|
||||
float GetChamberTempTarget() const { return m_temp_target; };
|
||||
|
||||
public:
|
||||
// setter
|
||||
void ParseChamber(const json &print_json);
|
||||
|
||||
void ParseChamberV1_0(const json& print_json);
|
||||
void ParseChamberV2_0(const json& print_json);
|
||||
|
||||
// control
|
||||
int CtrlSetChamberTemp(int temp);
|
||||
|
||||
protected:
|
||||
DevChamber(MachineObject *obj) : m_owner(obj) {}
|
||||
|
||||
private:
|
||||
float m_temp = 0.0f;
|
||||
float m_temp_target = 0.0f;
|
||||
MachineObject* m_owner = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
16
src/slic3r/GUI/DeviceCore/DevChamberCtrl.cpp
Normal file
16
src/slic3r/GUI/DeviceCore/DevChamberCtrl.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "DevChamber.h"
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
int DevChamber::CtrlSetChamberTemp(int temp)
|
||||
{
|
||||
json j;
|
||||
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["print"]["command"] = "set_ctt";
|
||||
j["print"]["ctt_val"] = temp;
|
||||
|
||||
return m_owner->publish_json(j, 1);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -8,17 +8,30 @@ using namespace nlohmann;
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
bool DevConfig::SupportChamberTempDisplay() const
|
||||
{
|
||||
if (!m_support_chamber_temp_display.has_value()) {
|
||||
return m_has_chamber;
|
||||
}
|
||||
|
||||
return m_support_chamber_temp_display.value();
|
||||
}
|
||||
|
||||
void DevConfig::ParseConfig(const json& print_json)
|
||||
{
|
||||
ParseChamberConfig(print_json);
|
||||
ParsePrintOptionsConfig(print_json);
|
||||
ParseCalibrationConfig(print_json);
|
||||
|
||||
}
|
||||
|
||||
void DevConfig::ParseChamberConfig(const json& print_json)
|
||||
{
|
||||
DevJsonValParser::ParseVal(print_json, "support_chamber", m_has_chamber);
|
||||
|
||||
if (print_json.contains("support_chamber_temp_display")) {
|
||||
m_support_chamber_temp_display = DevJsonValParser::GetVal<bool>(print_json, "support_chamber_temp_display");
|
||||
}
|
||||
|
||||
DevJsonValParser::ParseVal(print_json, "support_chamber_temp_edit", m_support_chamber_edit);
|
||||
if (m_support_chamber_edit)
|
||||
{
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
#include <wx/string.h>
|
||||
#include <limits>
|
||||
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
@@ -22,6 +23,7 @@ public:
|
||||
public:
|
||||
// chamber
|
||||
bool HasChamber() const { return m_has_chamber; }
|
||||
bool SupportChamberTempDisplay() const;
|
||||
bool SupportChamberEdit() const { return m_support_chamber_edit; }
|
||||
int GetChamberTempEditMin() const { return m_chamber_temp_edit_min; }
|
||||
int GetChamberTempEditMax() const { return m_chamber_temp_edit_max; }
|
||||
@@ -57,6 +59,7 @@ private:
|
||||
/*configure vals*/
|
||||
// chamber
|
||||
bool m_has_chamber = false; // whether the machine has a chamber
|
||||
std::optional<bool> m_support_chamber_temp_display;
|
||||
bool m_support_chamber_edit = false;
|
||||
int m_chamber_temp_edit_min = 0;
|
||||
int m_chamber_temp_edit_max = 60;
|
||||
|
||||
@@ -1,16 +1,41 @@
|
||||
#include "DevConfigUtil.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
#include <wx/dir.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include "../I18N.hpp"
|
||||
|
||||
using namespace nlohmann;
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
// Translation markers for xgettext extraction.
|
||||
// These strings are configured in printers/*.json (tool_head_display_names)
|
||||
// and passed to _L() at runtime. xgettext cannot scan dynamic strings,
|
||||
// so we mark them here with L() for extraction into .pot file.
|
||||
// This block is never executed at runtime.
|
||||
static void _toolhead_translation_markers()
|
||||
{
|
||||
// Dynamic toolhead display names from JSON config — xgettext cannot scan these
|
||||
L("Main Extruder"); L("Main extruder"); L("main extruder");
|
||||
L("Auxiliary Extruder"); L("Auxiliary extruder"); L("auxiliary extruder");
|
||||
L("Left Extruder"); L("Left extruder"); L("left extruder");
|
||||
L("Right Extruder"); L("Right extruder"); L("right extruder");
|
||||
L("Main Nozzle"); L("Main nozzle"); L("main nozzle");
|
||||
L("Auxiliary Nozzle"); L("Auxiliary nozzle"); L("auxiliary nozzle");
|
||||
L("Left Nozzle"); L("Left nozzle"); L("left nozzle");
|
||||
L("Right Nozzle"); L("Right nozzle"); L("right nozzle");
|
||||
L("Main Hotend"); L("Main hotend"); L("main hotend");
|
||||
L("Auxiliary Hotend"); L("Auxiliary hotend"); L("auxiliary hotend");
|
||||
L("Left Hotend"); L("Left hotend"); L("left hotend");
|
||||
L("Right Hotend"); L("Right hotend"); L("right hotend");
|
||||
// standalone position words (short_name=true runtime results)
|
||||
L("main"); L("auxiliary");
|
||||
L("Main"); L("Auxiliary");
|
||||
L("left"); L("right");
|
||||
L("Left"); L("Right");
|
||||
}
|
||||
|
||||
std::string DevPrinterConfigUtil::m_resource_file_path = "";
|
||||
|
||||
|
||||
@@ -91,7 +116,7 @@ std::string DevPrinterConfigUtil::get_filament_load_img(const std::string &type_
|
||||
{
|
||||
if (has_nozzle_rack)
|
||||
{
|
||||
const auto &rack_vec = get_value_from_config<std::vector<std::string>>(type_str, "filament_load_image_nozzle_rack");
|
||||
const auto &rack_vec = get_value_from_config<std::vector<std::string>>(type_str, "filament_load_image_nozzle_rack") ;
|
||||
if (!rack_vec.empty())
|
||||
{
|
||||
if (ext_id >= 0 && static_cast<size_t>(ext_id) < rack_vec.size())
|
||||
@@ -111,7 +136,6 @@ std::string DevPrinterConfigUtil::get_filament_load_img(const std::string &type_
|
||||
|
||||
std::string DevPrinterConfigUtil::get_fan_text(const std::string& type_str, const std::string& key)
|
||||
{
|
||||
std::vector<std::string> filaments;
|
||||
std::string config_file = m_resource_file_path + "/printers/" + type_str + ".json";
|
||||
boost::nowide::ifstream json_file(config_file.c_str());
|
||||
try
|
||||
@@ -134,6 +158,25 @@ std::string DevPrinterConfigUtil::get_fan_text(const std::string& type_str, cons
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::vector<std::string> DevPrinterConfigUtil::get_fan_text_params(const std::string& type_str, const std::string& key)
|
||||
{
|
||||
std::string config_file = m_resource_file_path + "/printers/" + type_str + ".json";
|
||||
boost::nowide::ifstream json_file(config_file.c_str());
|
||||
try {
|
||||
json jj;
|
||||
if (json_file.is_open()) {
|
||||
json_file >> jj;
|
||||
if (jj.contains("00.00.00.00")) {
|
||||
json const& printer = jj["00.00.00.00"];
|
||||
if (printer.contains("fan") && printer["fan"].contains(key)) {
|
||||
return printer["fan"][key].get<std::vector<std::string>>();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (...) {}
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
|
||||
std::string DevPrinterConfigUtil::get_fan_text(const std::string& type_str, int airduct_mode, int airduct_func, int submode)
|
||||
{
|
||||
std::vector<std::string> filaments;
|
||||
@@ -181,6 +224,36 @@ std::string DevPrinterConfigUtil::get_fan_text(const std::string& type_str, int
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string DevPrinterConfigUtil::get_fan_mode_text(const std::string& type_str, int airduct_mode, const std::string& key)
|
||||
{
|
||||
std::vector<std::string> filaments;
|
||||
std::string config_file = m_resource_file_path + "/printers/" + type_str + ".json";
|
||||
boost::nowide::ifstream json_file(config_file.c_str());
|
||||
try {
|
||||
json jj;
|
||||
if (json_file.is_open()) {
|
||||
json_file >> jj;
|
||||
if (jj.contains("00.00.00.00")) {
|
||||
json const& printer = jj["00.00.00.00"];
|
||||
if (!printer.contains("fan")) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
json const& fan_item = printer["fan"];
|
||||
const auto& airduct_mode_str = std::to_string(airduct_mode);
|
||||
if (!fan_item.contains(airduct_mode_str)) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
if (fan_item[airduct_mode_str].contains(key)) {
|
||||
return fan_item[airduct_mode_str][key].get<std::string>();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (...) {}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::map<std::string, std::vector<std::string>> DevPrinterConfigUtil::get_all_subseries(std::string type_str)
|
||||
{
|
||||
std::map<std::string, std::vector<std::string>> subseries;
|
||||
@@ -284,18 +357,29 @@ std::string DevPrinterConfigUtil::get_toolhead_display_name(
|
||||
{ ToolHeadComponent::Hotend, "hotend" }
|
||||
};
|
||||
|
||||
const int case_index = static_cast<int>(name_case);
|
||||
const std::string role_key = std::to_string(ext_id);
|
||||
int case_index = static_cast<int>(name_case); // 0, 1, 2
|
||||
|
||||
// Try to read from printer config json
|
||||
auto names_json = get_value_from_config<json>(type_str, "tool_head_display_names");
|
||||
std::string role_key = std::to_string(ext_id); // "0" or "1"
|
||||
const std::string& comp_key = comp_keys.at(component);
|
||||
|
||||
std::string result;
|
||||
auto names_json = get_value_from_config<json>(type_str, "tool_head_display_names");
|
||||
if (!names_json.is_null() && names_json.contains(role_key) && names_json[role_key].contains(comp_key)) {
|
||||
|
||||
if (!names_json.is_null()
|
||||
&& names_json.contains(role_key)
|
||||
&& names_json[role_key].contains(comp_key))
|
||||
{
|
||||
auto& arr = names_json[role_key][comp_key];
|
||||
if (arr.is_array() && case_index < static_cast<int>(arr.size()))
|
||||
if (arr.is_array() && case_index < static_cast<int>(arr.size())) {
|
||||
result = arr[case_index].get<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
// Orca: models that ship no tool_head_display_names (e.g. H2D/H2S) must still get distinct
|
||||
// per-extruder labels, so fall back to the previous programmatic Left/Right construction.
|
||||
// Engages only when the config lookup above yields nothing, so models that ship the key
|
||||
// behave exactly like the reference.
|
||||
if (result.empty()) {
|
||||
const std::string side = ext_id == DEPUTY_EXTRUDER_ID ? "Left" : "Right";
|
||||
const std::string component_name = component == ToolHeadComponent::Extruder ? "Extruder" :
|
||||
@@ -307,13 +391,15 @@ std::string DevPrinterConfigUtil::get_toolhead_display_name(
|
||||
std::transform(result.begin(), result.end(), result.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
}
|
||||
|
||||
// short_name: return only the role prefix (e.g. "Main" from "Main Nozzle")
|
||||
if (short_name) {
|
||||
auto sp = result.find(' ');
|
||||
if (sp != std::string::npos)
|
||||
if (sp != std::string::npos) {
|
||||
result = result.substr(0, sp);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
@@ -11,6 +11,8 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
@@ -23,6 +25,20 @@
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
/// Toolhead component type (extruder / nozzle / hotend)
|
||||
enum class ToolHeadComponent {
|
||||
Extruder,
|
||||
Nozzle,
|
||||
Hotend
|
||||
};
|
||||
|
||||
/// Name case style for toolhead display names
|
||||
enum class ToolHeadNameCase {
|
||||
TitleCase = 0, // [0] "Main Extruder" — panel titles, section headers
|
||||
SentenceCase = 1, // [1] "Main extruder" — static box labels
|
||||
LowerCase = 2 // [2] "main extruder" — inline text, sentence concatenation
|
||||
};
|
||||
|
||||
class dePrinterConfigFactory
|
||||
{
|
||||
public:
|
||||
@@ -30,17 +46,6 @@ public:
|
||||
~dePrinterConfigFactory() = default;
|
||||
};
|
||||
|
||||
enum class ToolHeadComponent {
|
||||
Extruder,
|
||||
Nozzle,
|
||||
Hotend
|
||||
};
|
||||
|
||||
enum class ToolHeadNameCase {
|
||||
TitleCase = 0,
|
||||
SentenceCase = 1,
|
||||
LowerCase = 2
|
||||
};
|
||||
|
||||
class DevPrinterConfigUtil
|
||||
{
|
||||
@@ -74,14 +79,19 @@ public:
|
||||
static std::string get_printer_use_ams_type(std::string type_str) { return get_value_from_config<std::string>(type_str, "use_ams_type"); }
|
||||
static std::string get_printer_ams_img(const std::string& type_str) { return get_value_from_config<std::string>(type_str, "printer_use_ams_image"); }
|
||||
static std::string get_printer_ext_img(const std::string& type_str, int pos);//printer_ext_image
|
||||
static bool support_ams_fila_change_abort(std::string type_str) { return get_value_from_config<bool>(type_str, "print", "support_ams_filament_change_abort"); }
|
||||
static std::string get_filament_load_img(const std::string &type_str, int ext_id, bool has_nozzle_rack = false);
|
||||
|
||||
/*fan*/
|
||||
static std::string get_fan_text(const std::string& type_str, const std::string& key);
|
||||
static std::string get_fan_text(const std::string& type_str, const std::string& key);
|
||||
static std::vector<std::string> get_fan_text_params(const std::string& type_str, const std::string& key);
|
||||
static std::string get_fan_text(const std::string& type_str, int airduct_mode, int airduct_func, int submode);
|
||||
static std::string get_fan_mode_text(const std::string& type_str, int airduct_mode, const std::string& key);
|
||||
|
||||
/*extruder*/
|
||||
static bool get_printer_can_set_nozzle(std::string type_str) { return get_value_from_config<bool>(type_str, "enable_set_nozzle_info"); }// can set nozzle from studio
|
||||
|
||||
/*toolhead display names (extruder / nozzle / hotend)*/
|
||||
static std::string get_toolhead_display_name(
|
||||
const std::string& type_str,
|
||||
int ext_id,
|
||||
@@ -90,10 +100,15 @@ public:
|
||||
bool short_name = false);
|
||||
|
||||
/*print job*/
|
||||
static bool support_print_check_firmware_for_tpu_left(std::string type_str){ return get_value_from_config<bool>(type_str, "print", "support_print_check_firmware_for_tpu_left"); }
|
||||
static bool support_user_first_setup_tpu_check(std::string type_str){ return get_value_from_config<bool>(type_str, "print", "support_user_first_setup_tpu_check"); }
|
||||
static std::string support_user_first_setup_tpu_check_url(std::string type_str){ return get_value_from_config<std::string>(type_str, "print", "support_user_first_setup_tpu_check_url"); }
|
||||
static bool support_ams_ext_mix_print(std::string type_str) { return get_value_from_config<bool>(type_str, "print", "support_ams_ext_mix_print"); }
|
||||
static bool support_print_time_estimate_warning(std::string type_str) { return get_value_from_config<bool>(type_str, "print", "support_print_time_estimate_warning"); }
|
||||
|
||||
/*calibration*/
|
||||
static std::vector<std::string> get_unsupport_auto_cali_filaments(std::string type_str) { return get_value_from_config<std::vector<std::string>>(type_str, "auto_cali_not_support_filaments"); }
|
||||
static bool support_disable_cali_flow_type(std::string type_str) { return get_value_from_config<bool>(type_str,"support_disable_cali_flow_type"); }
|
||||
|
||||
/*detection*/
|
||||
static bool support_wrapping_detection(const std::string& type_str) { return get_value_from_config<bool>(type_str, "support_wrapping_detection"); }
|
||||
@@ -103,6 +118,10 @@ public:
|
||||
|
||||
/*print check*/
|
||||
static bool support_print_check_extension_fan_f000_mounted(const std::string& type_str) { return get_value_from_config<bool>(type_str, "print", "support_print_check_extension_fan_f000_mounted"); }
|
||||
static std::string air_print_detection_position(const std::string &type_str) { return get_value_from_config<std::string>(type_str, "air_print_detection_position"); }
|
||||
|
||||
/*bed*/
|
||||
static int get_bed_temperature_limit(const std::string &type_str) { return get_value_from_config<int>(type_str, "print", "bed_temperature_limit"); }
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
@@ -126,7 +145,7 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...) { assert(0 && "get_value_from_config failed"); BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " failed"; }// there are file errors
|
||||
catch (...) { assert(0 && "get_value_from_config failed"); BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " failed"; }// there are file errors
|
||||
return T();
|
||||
};
|
||||
|
||||
@@ -178,7 +197,7 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...) { assert(0 && "get_json_from_config failed"); BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " failed"; }// there are file errors
|
||||
catch (...) { assert(0 && "get_json_from_config failed"); BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " failed"; }// there are file errors
|
||||
return nlohmann::json();
|
||||
}
|
||||
|
||||
@@ -187,35 +206,25 @@ private:
|
||||
};
|
||||
|
||||
/*special transform*/
|
||||
static std::string _parse_printer_type(const std::string& type_str)
|
||||
static std::string _parse_printer_type(const std::string &type_str)
|
||||
{
|
||||
if (type_str.compare("3DPrinter-X1") == 0)
|
||||
{
|
||||
return "BL-P002";
|
||||
}
|
||||
else if (type_str.compare("3DPrinter-X1-Carbon") == 0)
|
||||
{
|
||||
return "BL-P001";
|
||||
}
|
||||
else if (type_str.compare("BL-P001") == 0)
|
||||
{
|
||||
return type_str;
|
||||
}
|
||||
else if (type_str.compare("BL-P002") == 0)
|
||||
{
|
||||
return type_str;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string result = DevPrinterConfigUtil::get_printer_type(type_str);
|
||||
if (!result.empty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
static std::unordered_map<std::string, std::string> s_printer_type_lazy_cache;
|
||||
|
||||
if (type_str == "3DPrinter-X1") return "BL-P002";
|
||||
if (type_str == "3DPrinter-X1-Carbon") return "BL-P001";
|
||||
if (type_str == "BL-P001" || type_str == "BL-P002") return type_str;
|
||||
|
||||
auto cache_it = s_printer_type_lazy_cache.find(type_str);
|
||||
if (cache_it != s_printer_type_lazy_cache.end()) {
|
||||
return cache_it->second;
|
||||
}
|
||||
std::string result = DevPrinterConfigUtil::get_printer_type(type_str);
|
||||
if (!result.empty()) {
|
||||
s_printer_type_lazy_cache[type_str] = result;
|
||||
return result;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " Unsupported printer type: " << type_str;
|
||||
return type_str;
|
||||
}
|
||||
|
||||
};// namespace Slic3r
|
||||
};// namespace Slic3r
|
||||
@@ -10,12 +10,21 @@
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
// Reserved for future usage
|
||||
#define DEV_RESERVED_FOR_FUTURE(...) /* stripped */
|
||||
|
||||
// Previous definitions
|
||||
namespace Slic3r
|
||||
{
|
||||
class MachineObject;
|
||||
}
|
||||
|
||||
enum PrinterArch
|
||||
{
|
||||
ARCH_CORE_XY,
|
||||
ARCH_I3,
|
||||
ARCH_CORE_XY,// move hotbed
|
||||
ARCH_I3,//move z
|
||||
};
|
||||
|
||||
enum PrinterSeries
|
||||
@@ -36,6 +45,7 @@ enum AmsStatusMain
|
||||
AMS_STATUS_MAIN_RFID_IDENTIFYING = 0x02,
|
||||
AMS_STATUS_MAIN_ASSIST = 0x03,
|
||||
AMS_STATUS_MAIN_CALIBRATION = 0x04,
|
||||
AMS_STATUS_MAIN_COLD_PULL = 0x07,
|
||||
AMS_STATUS_MAIN_SELF_CHECK = 0x10,
|
||||
AMS_STATUS_MAIN_DEBUG = 0x20,
|
||||
AMS_STATUS_MAIN_UNKNOWN = 0xFF,
|
||||
@@ -51,10 +61,6 @@ enum DevAmsType : int
|
||||
AMS_LITE_MIXED = 5, // AMS-Lite for N9
|
||||
};
|
||||
|
||||
// Device-numbered filament-change step codes. Newer firmware reports the exact change-step
|
||||
// sequence through the AMS (ams.cfs), keyed by these codes, instead of the client hardcoding
|
||||
// steps per model. Distinct from the legacy GUI-side Slic3r::GUI::FilamentStep enum.
|
||||
// STEP_CHECK_POSITION and STEP_CONFIRM_EXTRUDED share code 0x08 by device design.
|
||||
enum DevFilamentStep
|
||||
{
|
||||
STEP_IDLE = 0x00,
|
||||
@@ -83,37 +89,35 @@ enum DevFilamentStep
|
||||
#define VIRTUAL_AMS_MAIN_ID_STR "255"
|
||||
#define VIRTUAL_AMS_DEPUTY_ID_STR "254"
|
||||
|
||||
// Tray index offset for the AMS-Lite-mixed unit (A2L / N9). Its 4 trays occupy
|
||||
// global tray indices 24..27.
|
||||
#define AMS_LITE_MIXED_TRAY_INDEX_OFFSET 24
|
||||
// Tray index offsets for different AMS types
|
||||
#define AMS_LITE_MIXED_TRAY_INDEX_OFFSET 24 // Offset for AMS_LITE_MIXED tray index (24)
|
||||
|
||||
#define INVALID_AMS_TEMPERATURE std::numeric_limits<float>::min()
|
||||
|
||||
// (ams_id, slot_id) pair.
|
||||
using DevAmsSlotId = std::pair<int, int>;
|
||||
|
||||
/* Extruder*/
|
||||
#define MAIN_EXTRUDER_ID 0
|
||||
#define DEPUTY_EXTRUDER_ID 1
|
||||
#define UNIQUE_EXTRUDER_ID MAIN_EXTRUDER_ID
|
||||
#define INVALID_EXTRUDER_ID -1
|
||||
|
||||
/* Logical extruder ids (multi-nozzle). */
|
||||
// see PartPlate::get_physical_extruder_by_logical_extruder
|
||||
#define LOGIC_UNIQUE_EXTRUDER_ID 0
|
||||
#define LOGIC_L_EXTRUDER_ID 0
|
||||
#define LOGIC_R_EXTRUDER_ID 1
|
||||
|
||||
// <ams_id, slot_id>
|
||||
using DevAmsSlotId = std::pair<int, int>;
|
||||
|
||||
/* Nozzle*/
|
||||
enum NozzleFlowType
|
||||
enum NozzleFlowType : int
|
||||
{
|
||||
NONE_FLOWTYPE,
|
||||
S_FLOW,
|
||||
H_FLOW,
|
||||
U_FLOW, // TPU 1.75 High Flow (device-reported; maps to nvtTPUHighFlow)
|
||||
U_FLOW, // TPU 1.75 High Flow
|
||||
E_FLOW, // E3D High Flow
|
||||
};
|
||||
|
||||
// Discrete nozzle diameters reported by the device.
|
||||
/* 0.2mm 0.4mm 0.6mm 0.8mm */
|
||||
enum NozzleDiameterType : int
|
||||
{
|
||||
NONE_DIAMETER_TYPE,
|
||||
@@ -135,7 +139,7 @@ enum DevPrintingSpeedLevel
|
||||
};
|
||||
|
||||
/*Upgrade*/
|
||||
enum class DevFirmwareUpgradingState : int
|
||||
enum class DevFirmwareUpgradeState : int
|
||||
{
|
||||
DC = -1,
|
||||
UpgradingUnavaliable = 0,
|
||||
@@ -144,6 +148,10 @@ enum class DevFirmwareUpgradingState : int
|
||||
UpgradingFinished = 3
|
||||
};
|
||||
|
||||
// Orca: DeviceManager/UpgradePanel keep the Orca spelling of this enum; the alias to the
|
||||
// reference name is a permanent compatibility surface.
|
||||
using DevFirmwareUpgradingState = DevFirmwareUpgradeState;
|
||||
|
||||
class devPrinterUtil
|
||||
{
|
||||
public:
|
||||
@@ -157,9 +165,6 @@ public:
|
||||
|
||||
namespace GUI
|
||||
{
|
||||
// Print source. Defined here (rather than in SelectMachine.hpp) so the shared device-mapping
|
||||
// GUI headers — AmsMappingPopup, wgtDeviceNozzleSelect — can name it without pulling in
|
||||
// SelectMachine.hpp, which would create an include cycle.
|
||||
enum PrintFromType
|
||||
{
|
||||
FROM_NORMAL,
|
||||
@@ -169,8 +174,6 @@ enum PrintFromType
|
||||
|
||||
};// namespace Slic3r
|
||||
|
||||
// A nozzle requirement of the sliced plate (or an installed nozzle's identity), compared in the
|
||||
// pre-print slicing-vs-installed nozzle checks.
|
||||
struct NozzleDef
|
||||
{
|
||||
float nozzle_diameter;
|
||||
@@ -190,4 +193,7 @@ template<> struct std::hash<NozzleDef>
|
||||
size_t h2 = std::hash<int>{}(v.nozzle_flow_type);
|
||||
return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2));
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// key(extruder_id) -> { key1(nozzle type info), val1( number of the nozzle type)}
|
||||
using ExtruderNozzleInfos = std::unordered_map<int, std::unordered_map<NozzleDef, int>>;
|
||||
@@ -271,7 +271,7 @@ namespace Slic3r
|
||||
system->m_current_loading_extder_id = INVALID_EXTRUDER_ID;
|
||||
if (!system->m_extders[MAIN_EXTRUDER_ID].m_star.ams_id.empty())
|
||||
{
|
||||
system->m_current_busy_for_loading = (system->m_extders[MAIN_EXTRUDER_ID].m_snow == system->m_extders[MAIN_EXTRUDER_ID].m_star);
|
||||
system->m_current_busy_for_loading = (system->m_extders[MAIN_EXTRUDER_ID].m_snow != system->m_extders[MAIN_EXTRUDER_ID].m_star);
|
||||
if (system->m_current_busy_for_loading)
|
||||
{
|
||||
system->m_current_loading_extder_id = MAIN_EXTRUDER_ID;
|
||||
|
||||
@@ -22,6 +22,7 @@ struct DevAmsSlotInfo
|
||||
std::string slot_id;
|
||||
|
||||
public:
|
||||
bool operator!=(const DevAmsSlotInfo& other) const { return !(*this == other); }
|
||||
bool operator==(const DevAmsSlotInfo& other) const { return ams_id == other.ams_id && slot_id == other.slot_id;}
|
||||
};
|
||||
|
||||
|
||||
@@ -46,20 +46,10 @@ void Slic3r::DevFan::converse_to_duct(bool is_suppt_part_fun, bool is_suppt_aux_
|
||||
}
|
||||
}
|
||||
|
||||
static std::string _get_string_from_fantype(int type)
|
||||
{
|
||||
switch (type) {
|
||||
case 1: return "cooling_fan";
|
||||
case 2: return "big_cooling_fan";
|
||||
case 3: return "chamber_fan";
|
||||
default: return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Old protocol
|
||||
int Slic3r::DevFan::command_control_fan(int fan_type, int val)
|
||||
{
|
||||
// Orca: strip analytics telemetry
|
||||
std::string gcode = (boost::format("M106 P%1% S%2% \n") % (int) fan_type % (val)).str();
|
||||
return m_owner->publish_gcode(gcode);
|
||||
}
|
||||
@@ -166,8 +156,6 @@ void Slic3r::DevFan::ParseV3_0(const json &device)
|
||||
m_air_duct_data.modes.clear();
|
||||
m_air_duct_data.parts.clear();
|
||||
|
||||
m_air_duct_data.curren_mode = device["airduct"]["modeCur"].get<int>();
|
||||
|
||||
const json &airduct = device["airduct"];
|
||||
if (airduct.contains("modeCur")) { m_air_duct_data.curren_mode = airduct["modeCur"].get<int>(); }
|
||||
if (airduct.contains("subMode")) { m_air_duct_data.m_sub_mode = airduct["subMode"].get<int>(); }
|
||||
@@ -193,7 +181,7 @@ void Slic3r::DevFan::ParseV3_0(const json &device)
|
||||
}
|
||||
}
|
||||
|
||||
if (AIR_DUCT(mode.id) == AIR_DUCT::AIR_DUCT_EXHAUST) { continue; } /*STUDIO-12796*/
|
||||
if (AIR_DUCT(mode.id) == AIR_DUCT::AIR_DUCT_EXHAUST) { continue; }
|
||||
m_air_duct_data.modes[mode.id] = mode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,6 @@ class MachineObject;
|
||||
enum AirDuctType { AIR_FAN_TYPE, AIR_DOOR_TYPE };
|
||||
typedef std::function<void(const json &)> CommandCallBack;
|
||||
|
||||
|
||||
|
||||
enum AIR_FUN : int {
|
||||
FAN_HEAT_BREAK_0_IDX = 0,
|
||||
FAN_COOLING_0_AIRDOOR = 1,
|
||||
@@ -91,6 +89,13 @@ public:
|
||||
|
||||
bool IsSupportCoolingFilter() const { return m_support_cooling_filter; }
|
||||
bool IsCoolingFilerOn() const { return m_sub_mode == 1; }
|
||||
bool IsExaustFanExit() const
|
||||
{
|
||||
for (auto &p : parts) {
|
||||
if (p.id == int(AIR_FUN::FAN_CHAMBER_0_IDX)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class DevFan
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <optional>
|
||||
#include <unordered_map> // Orca: for m_firmwares / GetSuppotedFirmwares() — the unordered_map AMSSetting.cpp mirrors and iterates
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "DevCtrl.h"
|
||||
|
||||
@@ -75,7 +76,7 @@ public:
|
||||
|
||||
DevAmsSystemIdx GetCurrentFirmwareIdxSel() const { return m_current_firmware_sel.m_firmare_idx; };
|
||||
DevAmsSystemIdx GetCurrentFirmwareIdxRun() const { return m_current_firmware_run.m_firmare_idx; };
|
||||
std::unordered_map<int, DevAmsSystemFirmware> GetSuppotedFirmwares() const { return m_firmwares;};
|
||||
std::unordered_map<int, DevAmsSystemFirmware> GetSuppotedFirmwares() const { return m_firmwares;}; // Orca: unordered (mirrored by AMSSetting.cpp)
|
||||
|
||||
bool IsSwitching() const { return m_status == "SWITCHING";};
|
||||
bool IsIdle() const { return m_status == "IDLE";};
|
||||
@@ -94,7 +95,7 @@ private:
|
||||
|
||||
DevAmsSystemFirmware m_current_firmware_run;
|
||||
DevAmsSystemFirmware m_current_firmware_sel;
|
||||
std::unordered_map<int, DevAmsSystemFirmware> m_firmwares;
|
||||
std::unordered_map<int, DevAmsSystemFirmware> m_firmwares; // Orca: unordered (mirrored by AMSSetting.cpp)
|
||||
|
||||
DevCtrlInfo m_ctrl_switching;
|
||||
};
|
||||
|
||||
@@ -9,19 +9,6 @@ namespace Slic3r {
|
||||
|
||||
DevFilaSwitch::DevFilaSwitch(MachineObject *owner) { m_owner = owner; }
|
||||
|
||||
bool DevFilaSwitch::IsReady() const
|
||||
{
|
||||
if (!m_is_installed) { return false; }
|
||||
|
||||
const auto& ams_list = m_owner->GetFilaSystem()->GetAmsList();
|
||||
for (const auto& ams_item : ams_list) {
|
||||
if (ams_item.second->GetBindedExtruderSet().empty()) { return false; }
|
||||
if (!ams_item.second->GetSwitcherPos().has_value()) { return false; }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DevFilaSwitch::Reset()
|
||||
{
|
||||
m_is_installed = false;
|
||||
@@ -34,6 +21,24 @@ void DevFilaSwitch::Reset()
|
||||
m_cali_status = CaliStatus::CALI_IDLE;
|
||||
}
|
||||
|
||||
bool DevFilaSwitch::IsReady() const
|
||||
{
|
||||
if (!m_is_installed) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "[FilaSwitch] IsReady: not installed";
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& ams_list = m_owner->GetFilaSystem()->GetAmsList();
|
||||
for (const auto& ams_item : ams_list) {
|
||||
if (ams_item.second->GetBindedExtruderSet().size() == 0) { return false; }
|
||||
if (!ams_item.second->GetSwitcherPos().has_value()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<DevAmsTray> DevFilaSwitch::GetInA_Slot() const
|
||||
{
|
||||
auto ams_id_opt = GetInA_SlotId();
|
||||
@@ -128,4 +133,4 @@ void DevFilaSwitch::ParseFilaSwitchInfo(const nlohmann::json &print_jj)
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
}; // namespace Slic3r
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
#include "DevDefs.h"
|
||||
#include <optional>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
@@ -10,16 +9,11 @@ class MachineObject;
|
||||
class DevExtder;
|
||||
class DevAmsTray;
|
||||
|
||||
// Filament Track Switch (H2-family hardware).
|
||||
// The filament blacklist consumes IsInstalled() for the `has_filament_switch`
|
||||
// rule (e.g. PLA Silk). m_is_installed defaults to false and is only raised when
|
||||
// the device reports the switch over MQTT (aux bit 29), so the rule stays inert on
|
||||
// every printer that does not report an installed switch.
|
||||
class DevFilaSwitch
|
||||
{
|
||||
public:
|
||||
enum class CaliStatus : int
|
||||
{
|
||||
{
|
||||
CALI_IDLE = 0,
|
||||
CALI_STEPING = 1,
|
||||
};
|
||||
@@ -48,10 +42,6 @@ public:
|
||||
|
||||
public:
|
||||
bool IsInstalled() const { return m_is_installed; };
|
||||
|
||||
// Ready once installed and every AMS has resolved both its extruder binding and its
|
||||
// switcher track position from the device. The send dialog and sidebar sync UX only
|
||||
// treat the switch as usable when this is true.
|
||||
bool IsReady() const;
|
||||
|
||||
std::optional<bool> IsInA_HasFilament() const { return m_in_a_has_filament; };
|
||||
@@ -88,4 +78,4 @@ private:
|
||||
|
||||
CaliStatus m_cali_status = CaliStatus::CALI_IDLE;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -270,10 +270,10 @@ private:
|
||||
AmsType m_ams_type = AmsType::AMS;
|
||||
std::string m_ams_id;
|
||||
int m_ext_id;//extruder id
|
||||
// Orca: keeps the legacy single m_ext_id for existing consumers and carries the
|
||||
// switch-aware binding alongside it (BambuStudio stores only the set). Without a Filament
|
||||
// Track Switch the set is {m_ext_id}; with one installed the device binds both extruders and
|
||||
// records which input track (A/B) feeds this AMS.
|
||||
// Orca: pull-mode / multi-vendor agent model — keeps the legacy single m_ext_id for existing
|
||||
// consumers and carries the switch-aware binding alongside it. Without a Filament Track Switch
|
||||
// the set is {m_ext_id}; with one installed the device binds both extruders and records which
|
||||
// input track (A/B) feeds this AMS.
|
||||
std::set<int> m_binded_extruder_set;
|
||||
std::optional<DevFilaSwitch::SwitchPos> m_binded_switcher_pos;
|
||||
bool m_exist = false;
|
||||
|
||||
@@ -28,8 +28,8 @@ int DevFilaSystem::CtrlAmsStartDryingHour(int ams_id,
|
||||
jj_command["print"]["command"] = "ams_filament_drying";
|
||||
jj_command["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
jj_command["print"]["ams_id"] = ams_id;
|
||||
jj_command["print"]["mode"] = static_cast<int>(DevAms::DryCtrlMode::OnTime);
|
||||
jj_command["print"]["filament"] = filament_type;
|
||||
jj_command["print"]["mode"] = static_cast<int>(DevAms::DryCtrlMode::OnTime); // Orca: cast scoped enum for json
|
||||
jj_command["print"]["filament"] = filament_type; // Orca: fix comma-operator typo
|
||||
jj_command["print"]["temp"] = tag_temp;
|
||||
jj_command["print"]["duration"] = tag_duration_hour;
|
||||
jj_command["print"]["humidity"] = 0;
|
||||
@@ -45,8 +45,8 @@ int DevFilaSystem::CtrlAmsStopDrying(int ams_id) const
|
||||
jj_command["print"]["command"] = "ams_filament_drying";
|
||||
jj_command["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
jj_command["print"]["ams_id"] = ams_id;
|
||||
jj_command["print"]["mode"] = static_cast<int>(DevAms::DryCtrlMode::Off);
|
||||
jj_command["print"]["filament"] = "";
|
||||
jj_command["print"]["mode"] = static_cast<int>(DevAms::DryCtrlMode::Off); // Orca: cast scoped enum for json
|
||||
jj_command["print"]["filament"] = ""; // Orca: fix comma-operator typo
|
||||
jj_command["print"]["temp"] = 0;
|
||||
jj_command["print"]["duration"] = 0;
|
||||
jj_command["print"]["humidity"] = 0;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#pragma once
|
||||
#include "DevFirmware.h"
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <wx/string.h>
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
@@ -8,6 +11,7 @@ namespace Slic3r {
|
||||
//Previous definitions
|
||||
class MachineObject;
|
||||
|
||||
// Orca: firmware-type enum consumed by DeviceManager/UpgradePanel; no reference-side equivalent yet
|
||||
enum PrinterFirmwareType
|
||||
{
|
||||
FIRMWARE_TYPE_ENGINEER = 0,
|
||||
@@ -15,7 +19,6 @@ enum PrinterFirmwareType
|
||||
FIRMEARE_TYPE_UKNOWN,
|
||||
};
|
||||
|
||||
|
||||
class FirmwareInfo
|
||||
{
|
||||
public:
|
||||
@@ -40,12 +43,18 @@ public:
|
||||
|
||||
public:
|
||||
bool isValid() const { return !sn.empty(); }
|
||||
|
||||
/*type check*/
|
||||
bool isAirPump() const { return product_name.Contains("Air Pump"); }
|
||||
bool isLaszer() const { return product_name.Contains("Laser"); }
|
||||
bool isCuttingModule() const { return product_name.Contains("Cutting Module"); }
|
||||
bool isExtinguishSystem() const { return product_name.Contains("Extinguishing System"); }
|
||||
bool isWTM() const { return name.find("wtm") != std::string::npos; } // nozzle (rack hotend) module firmware
|
||||
bool isFilaTrackSwitch() const { return product_name.Contains("Filament Track"); }
|
||||
bool isRotary() const { return product_name.Contains("Rotary"); }// Rotary Attachment
|
||||
bool isExtinguishSystem() const { return product_name.Contains("Extinguishing System"); }// Auto Fire Extinguishing System
|
||||
bool isWTM() const { return name.find("wtm") != string::npos; } // nozzle
|
||||
bool isExhaustFan() const { return product_name.Contains("Exhaust Fan"); }
|
||||
bool isHmshub() const { return product_name.find("Filament Buffer") != string::npos; }
|
||||
bool isFilaTrackSwitch() const { return product_name.find("Filament Track") != string::npos; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ bool DevHMSItem::parse_hms_info(unsigned attr, unsigned code)
|
||||
std::string DevHMSItem::get_long_error_code() const
|
||||
{
|
||||
char buf[64];
|
||||
// Orca: HMS long-error-code format excludes the reserved byte and keys the Orca HMS wiki lookups
|
||||
::sprintf(buf, "%02X%02X%02X00000%1X%04X",
|
||||
this->m_module_id,
|
||||
this->m_module_num,
|
||||
|
||||
@@ -8,6 +8,9 @@ namespace Slic3r {
|
||||
class MachineObject;
|
||||
|
||||
/* some static info of machine*/ /*TODO*/
|
||||
// Orca: kept as a stub — connection_type/is_lan_mode_printer/is_cloud_mode_printer live inline on
|
||||
// MachineObject (DeviceManager.hpp). Adopting the reference's full DevInfo would duplicate those
|
||||
// definitions and require an m_dev_info member MachineObject does not have.
|
||||
class DevInfo
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -162,8 +162,8 @@ namespace Slic3r
|
||||
}
|
||||
|
||||
//first: left,nozzle=1,map=1 second: right,nozzle=0,map=2
|
||||
bool right_ams_valid = ams->second->GetExtruderId() == 0 && map_opt[MappingOption::USE_RIGHT_AMS];
|
||||
bool left_ams_valid = ams->second->GetExtruderId() == 1 && map_opt[MappingOption::USE_LEFT_AMS];
|
||||
bool right_ams_valid = (ams->second->GetBindedExtruderSet().count(MAIN_EXTRUDER_ID) != 0) && map_opt[MappingOption::USE_RIGHT_AMS];
|
||||
bool left_ams_valid = (ams->second->GetBindedExtruderSet().count(DEPUTY_EXTRUDER_ID) != 0) && map_opt[MappingOption::USE_LEFT_AMS];
|
||||
if (right_ams_valid || left_ams_valid)
|
||||
{
|
||||
tray_filaments.emplace(std::make_pair(tray_index, info));
|
||||
|
||||
@@ -16,10 +16,6 @@
|
||||
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
using namespace nlohmann;
|
||||
@@ -33,30 +29,6 @@ void MachineObject::clear_auto_nozzle_mapping()
|
||||
}
|
||||
}
|
||||
|
||||
static std::string s_get_diameter_str(float diameter)
|
||||
{
|
||||
return (boost::format("%.2f") % diameter).str();
|
||||
}
|
||||
|
||||
static std::string s_get_diameter_str(const std::string& diameter)
|
||||
{
|
||||
try {
|
||||
float dia = boost::lexical_cast<float>(diameter);
|
||||
return s_get_diameter_str(dia);
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " failed to boost::lexical_cast: " << diameter;
|
||||
return diameter;
|
||||
}
|
||||
|
||||
try {
|
||||
float dia = std::stof(diameter);
|
||||
return s_get_diameter_str(dia);
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " std::stof: " << diameter;
|
||||
return diameter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// get auto nozzle mapping through AP
|
||||
// warnings:
|
||||
@@ -268,7 +240,7 @@ int DevNozzleMappingCtrl::CtrlGetAutoNozzleMappingV1(Slic3r::GUI::Plater* plater
|
||||
|
||||
void DevNozzleMappingCtrl::ParseAutoNozzleMapping(const json& print_jj)
|
||||
{
|
||||
if (print_jj.contains("command") && print_jj["command"].get<std::string>() == "get_auto_nozzle_mapping") {
|
||||
if (print_jj.contains("command") && print_jj["command"].get<string>() == "get_auto_nozzle_mapping") {
|
||||
if (print_jj.contains("sequence_id") && print_jj["sequence_id"] == m_sequence_id) {
|
||||
Clear();
|
||||
DevJsonValParser::ParseVal(print_jj, "result", m_result);
|
||||
@@ -482,4 +454,4 @@ float DevNozzleMappingCtrl::GetFlushWeight(Slic3r::MachineObject* obj) const
|
||||
return total_flush_volume * 1.26 * 0.001;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
} // namespace Slic3r
|
||||
@@ -18,7 +18,7 @@ namespace GUI
|
||||
class Plater;
|
||||
}
|
||||
|
||||
struct FilamentInfo; // libslic3r/ProjectTask.hpp
|
||||
struct FilamentInfo; // Orca: libslic3r/ProjectTask.hpp — forward-decl for CtrlGetAutoNozzleMappingV0 signature
|
||||
|
||||
class MachineObject;
|
||||
class DevNozzleMappingCtrl
|
||||
|
||||
@@ -1,11 +1,32 @@
|
||||
#include "DevPrintOptions.h"
|
||||
#include "DevUtil.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
DevPrintOptions::DevPrintOptions(MachineObject* obj) : m_obj(obj)
|
||||
{
|
||||
m_detection_list = {
|
||||
{PrintOptionEnum::AI_Monitoring, &m_ai_monitoring_detection},
|
||||
{PrintOptionEnum::First_Layer_Detection, &m_first_layer_detection},
|
||||
{PrintOptionEnum::Buildplate_Mark_Detection, &m_buildplate_mark_detection},
|
||||
{PrintOptionEnum::Buildplate_Align_Detection, &m_buildplate_align_detection},
|
||||
{PrintOptionEnum::Auto_Recovery_Detection, &m_auto_recovery_detection},
|
||||
{PrintOptionEnum::Allow_Prompt_Sound_Detection, &m_allow_prompt_sound_detection},
|
||||
{PrintOptionEnum::Filament_Tangle_Detection, &m_filament_tangle_detection},
|
||||
{PrintOptionEnum::Idle_Heating_Protect_Detection, &m_idel_heating_protect_detection},
|
||||
{PrintOptionEnum::Purify_Air_At_Print_End, &m_purify_air_at_print_end},
|
||||
{PrintOptionEnum::Snapshot_Detection, &m_snapshot_detection},
|
||||
{PrintOptionEnum::FOD_Check_Detection, &m_fod_check_detection},
|
||||
{PrintOptionEnum::Displacement_Detection, &m_displacement_detection},
|
||||
{PrintOptionEnum::Smart_Nozzle_Blob_Detection, &m_smart_nozzle_blob_detection},
|
||||
};
|
||||
}
|
||||
|
||||
void DevPrintOptionsParser::Parse(DevPrintOptions* opts, const nlohmann::json& print_json)
|
||||
{
|
||||
try
|
||||
@@ -20,6 +41,31 @@ void DevPrintOptionsParser::Parse(DevPrintOptions* opts, const nlohmann::json& p
|
||||
const std::string& cfg = print_json["cfg"].get<std::string>();
|
||||
opts->m_speed_level = (DevPrintingSpeedLevel)DevUtil::get_flag_bits(cfg, 8, 3);
|
||||
}
|
||||
|
||||
// Orca: xcam.cfg is an integer bitfield (distinct from the top-level cfg string) that
|
||||
// carries the plate-align/FOD/displacement current values.
|
||||
if (print_json.contains("xcam") && print_json["xcam"].contains("cfg")) {
|
||||
int xcam_cfg = print_json["xcam"]["cfg"].get<int>();
|
||||
if (time(nullptr) - opts->m_buildplate_align_detection.detect_hold_start > HOLD_TIME_3SEC)
|
||||
opts->m_buildplate_align_detection.current_detect_value = DevUtil::get_flag_bits(xcam_cfg, 20);
|
||||
if (time(nullptr) - opts->m_fod_check_detection.detect_hold_start > HOLD_TIME_3SEC)
|
||||
opts->m_fod_check_detection.current_detect_value = DevUtil::get_flag_bits(xcam_cfg, 21);
|
||||
if (time(nullptr) - opts->m_displacement_detection.detect_hold_start > HOLD_TIME_3SEC)
|
||||
opts->m_displacement_detection.current_detect_value = DevUtil::get_flag_bits(xcam_cfg, 22);
|
||||
}
|
||||
|
||||
// Orca: fun2 support bits (distinct from the top-level cfg/fun fields). ORCA's DevUtil has no
|
||||
// no-border extractor, so the MachineObject helper is used here instead.
|
||||
if (print_json.contains("fun2") && print_json["fun2"].is_string()) {
|
||||
std::string fun2 = print_json["fun2"].get<std::string>();
|
||||
if (!fun2.empty()) {
|
||||
opts->m_buildplate_align_detection.is_support_detect = opts->m_obj->get_flag_bits_no_border(fun2, 2) == 1;
|
||||
opts->m_purify_air_at_print_end.is_support_detect = opts->m_obj->get_flag_bits_no_border(fun2, 4);
|
||||
opts->m_fod_check_detection.is_support_detect = opts->m_obj->get_flag_bits_no_border(fun2, 13);
|
||||
opts->m_displacement_detection.is_support_detect = opts->m_obj->get_flag_bits_no_border(fun2, 14);
|
||||
opts->m_smart_nozzle_blob_detection.is_support_detect = opts->m_obj->get_flag_bits_no_border(fun2, 15);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
@@ -31,32 +77,32 @@ void DevPrintOptionsParser::ParseDetectionV1_0(DevPrintOptions *opts, MachineObj
|
||||
{
|
||||
try {
|
||||
if (print_json.contains("xcam")) {
|
||||
if (time(nullptr) - opts->xcam_ai_monitoring_hold_start > HOLD_TIME_3SEC) {
|
||||
if (time(nullptr) - opts->m_ai_monitoring_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
if (print_json["xcam"].contains("printing_monitor")) {
|
||||
// new protocol
|
||||
opts->xcam_ai_monitoring = print_json["xcam"]["printing_monitor"].get<bool>();
|
||||
opts->m_ai_monitoring_detection.current_detect_value = print_json["xcam"]["printing_monitor"].get<bool>();
|
||||
} else {
|
||||
// old version protocol
|
||||
if (print_json["xcam"].contains("spaghetti_detector")) {
|
||||
opts->xcam_ai_monitoring = print_json["xcam"]["spaghetti_detector"].get<bool>();
|
||||
opts->m_ai_monitoring_detection.current_detect_value = print_json["xcam"]["spaghetti_detector"].get<bool>();
|
||||
if (print_json["xcam"].contains("print_halt")) {
|
||||
bool print_halt = print_json["xcam"]["print_halt"].get<bool>();
|
||||
if (print_halt) { opts->xcam_ai_monitoring_sensitivity = "medium"; }
|
||||
if (print_halt) { opts->m_ai_monitoring_detection.current_detect_sensitivity_value = "medium"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (print_json["xcam"].contains("halt_print_sensitivity")) {
|
||||
opts->xcam_ai_monitoring_sensitivity = print_json["xcam"]["halt_print_sensitivity"].get<std::string>();
|
||||
opts->m_ai_monitoring_detection.current_detect_sensitivity_value = print_json["xcam"]["halt_print_sensitivity"].get<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
if (time(nullptr) - opts->xcam_first_layer_hold_start > HOLD_TIME_3SEC) {
|
||||
if (print_json["xcam"].contains("first_layer_inspector")) { opts->xcam_first_layer_inspector = print_json["xcam"]["first_layer_inspector"].get<bool>(); }
|
||||
if (time(nullptr) - opts->m_first_layer_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
if (print_json["xcam"].contains("first_layer_inspector")) { opts->m_first_layer_detection.current_detect_value = print_json["xcam"]["first_layer_inspector"].get<bool>(); }
|
||||
}
|
||||
|
||||
if (time(nullptr) - opts->xcam_buildplate_marker_hold_start > HOLD_TIME_3SEC) {
|
||||
if (time(nullptr) - opts->m_buildplate_mark_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
if (print_json["xcam"].contains("buildplate_marker_detector")) {
|
||||
opts->xcam_buildplate_marker_detector = print_json["xcam"]["buildplate_marker_detector"].get<bool>();
|
||||
opts->m_buildplate_mark_detection.current_detect_value = print_json["xcam"]["buildplate_marker_detector"].get<bool>();
|
||||
obj->is_support_build_plate_marker_detect = true;
|
||||
} else {
|
||||
obj->is_support_build_plate_marker_detect = false;
|
||||
@@ -66,32 +112,31 @@ void DevPrintOptionsParser::ParseDetectionV1_0(DevPrintOptions *opts, MachineObj
|
||||
} catch (...) {
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void DevPrintOptionsParser::ParseDetectionV1_1(DevPrintOptions *opts, MachineObject *obj, const nlohmann::json &print_json,bool enable)
|
||||
{
|
||||
if (print_json["module_name"].get<std::string>() == "first_layer_inspector") {
|
||||
if (time(nullptr) - opts->xcam_first_layer_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->xcam_first_layer_inspector = enable;
|
||||
if (time(nullptr) - opts->m_first_layer_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->m_first_layer_detection.current_detect_value = enable;
|
||||
}
|
||||
} else if (print_json["module_name"].get<std::string>() == "buildplate_marker_detector") {
|
||||
if (time(nullptr) - opts->xcam_buildplate_marker_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->xcam_buildplate_marker_detector = enable;
|
||||
if (time(nullptr) - opts->m_buildplate_mark_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->m_buildplate_mark_detection.current_detect_value = enable;
|
||||
}
|
||||
} else if (print_json["module_name"].get<std::string>() == "printing_monitor") {
|
||||
if (time(nullptr) - opts->xcam_ai_monitoring_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->xcam_ai_monitoring = enable;
|
||||
if (time(nullptr) - opts->m_ai_monitoring_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->m_ai_monitoring_detection.current_detect_value = enable;
|
||||
if (print_json.contains("halt_print_sensitivity")) {
|
||||
opts->xcam_ai_monitoring_sensitivity = print_json["halt_print_sensitivity"].get<std::string>();
|
||||
opts->m_ai_monitoring_detection.current_detect_sensitivity_value = print_json["halt_print_sensitivity"].get<std::string>();
|
||||
}
|
||||
}
|
||||
} else if (print_json["module_name"].get<std::string>() == "spaghetti_detector") {
|
||||
if (time(nullptr) - opts->xcam_ai_monitoring_hold_start > HOLD_TIME_3SEC) {
|
||||
if (time(nullptr) - opts->m_ai_monitoring_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
// old protocol
|
||||
opts->xcam_ai_monitoring = enable;
|
||||
opts->m_ai_monitoring_detection.current_detect_value = enable;
|
||||
if (print_json.contains("print_halt")) {
|
||||
if (print_json["print_halt"].get<bool>()) { opts->xcam_ai_monitoring_sensitivity = "medium"; }
|
||||
if (print_json["print_halt"].get<bool>()) { opts->m_ai_monitoring_detection.current_detect_sensitivity_value = "medium"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,12 +148,12 @@ void DevPrintOptionsParser::ParseDetectionV1_2(DevPrintOptions *opts, MachineObj
|
||||
if (print_json.contains("option")) {
|
||||
if (print_json["option"].is_number()) {
|
||||
int option = print_json["option"].get<int>();
|
||||
if (time(nullptr) - opts->xcam_auto_recovery_hold_start > HOLD_TIME_3SEC) { opts->xcam_auto_recovery_step_loss = ((option & 0x01) != 0); }
|
||||
if (time(nullptr) - opts->m_auto_recovery_detection.detect_hold_start > HOLD_TIME_3SEC) { opts->m_auto_recovery_detection.current_detect_value = ((option & 0x01) != 0); }
|
||||
}
|
||||
}
|
||||
|
||||
if (time(nullptr) - opts->xcam_auto_recovery_hold_start > HOLD_TIME_3SEC) {
|
||||
if (print_json.contains("auto_recovery")) { opts->xcam_auto_recovery_step_loss = print_json["auto_recovery"].get<bool>(); }
|
||||
if (time(nullptr) - opts->m_auto_recovery_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
if (print_json.contains("auto_recovery")) { opts->m_auto_recovery_detection.current_detect_value = print_json["auto_recovery"].get<bool>(); }
|
||||
}
|
||||
} catch (...) {}
|
||||
|
||||
@@ -117,38 +162,52 @@ void DevPrintOptionsParser::ParseDetectionV1_2(DevPrintOptions *opts, MachineObj
|
||||
void DevPrintOptionsParser::ParseDetectionV2_0(DevPrintOptions *opts, std::string print_json)
|
||||
{
|
||||
|
||||
if (time(nullptr) - opts->xcam_first_layer_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->xcam_first_layer_inspector = DevUtil::get_flag_bits(print_json, 12);
|
||||
if (time(nullptr) - opts->m_first_layer_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->m_first_layer_detection.current_detect_value = DevUtil::get_flag_bits(print_json, 12);
|
||||
}
|
||||
|
||||
if (time(nullptr) - opts->xcam_ai_monitoring_hold_start > HOLD_COUNT_MAX) {
|
||||
opts->xcam_ai_monitoring = DevUtil::get_flag_bits(print_json, 15);
|
||||
if (time(nullptr) - opts->m_ai_monitoring_detection.detect_hold_start > HOLD_COUNT_MAX) {
|
||||
opts->m_ai_monitoring_detection.current_detect_value = DevUtil::get_flag_bits(print_json, 15);
|
||||
|
||||
switch (DevUtil::get_flag_bits(print_json, 13, 2)) {
|
||||
case 0: opts->xcam_ai_monitoring_sensitivity = "never_halt"; break;
|
||||
case 1: opts->xcam_ai_monitoring_sensitivity = "low"; break;
|
||||
case 2: opts->xcam_ai_monitoring_sensitivity = "medium"; break;
|
||||
case 3: opts->xcam_ai_monitoring_sensitivity = "high"; break;
|
||||
case 0: opts->m_ai_monitoring_detection.current_detect_sensitivity_value = "never_halt"; break;
|
||||
case 1: opts->m_ai_monitoring_detection.current_detect_sensitivity_value = "low"; break;
|
||||
case 2: opts->m_ai_monitoring_detection.current_detect_sensitivity_value = "medium"; break;
|
||||
case 3: opts->m_ai_monitoring_detection.current_detect_sensitivity_value = "high"; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (time(nullptr) - opts->xcam_auto_recovery_hold_start > HOLD_COUNT_MAX){
|
||||
opts->xcam_auto_recovery_step_loss =DevUtil::get_flag_bits(print_json, 16);
|
||||
if (time(nullptr) - opts->m_auto_recovery_detection.detect_hold_start > HOLD_COUNT_MAX){
|
||||
opts->m_auto_recovery_detection.current_detect_value = DevUtil::get_flag_bits(print_json, 16);
|
||||
}
|
||||
|
||||
if (time(nullptr) - opts->xcam_prompt_sound_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->xcam_allow_prompt_sound = DevUtil::get_flag_bits(print_json, 22);
|
||||
if (time(nullptr) - opts->m_allow_prompt_sound_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->m_allow_prompt_sound_detection.current_detect_value = DevUtil::get_flag_bits(print_json, 22);
|
||||
}
|
||||
|
||||
if (time(nullptr) - opts->xcam_filament_tangle_detect_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->xcam_filament_tangle_detect = DevUtil::get_flag_bits(print_json, 23);
|
||||
if (time(nullptr) - opts->m_filament_tangle_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->m_filament_tangle_detection.current_detect_value = DevUtil::get_flag_bits(print_json, 23);
|
||||
}
|
||||
}
|
||||
|
||||
void DevPrintOptionsParser::ParseDetectionV2_1(DevPrintOptions *opts, std::string cfg) {
|
||||
if (time(nullptr) - opts->idel_heating_protect_hold_strat > HOLD_TIME_3SEC)
|
||||
opts->idel_heating_protect_enabled = DevUtil::get_flag_bits(cfg, 32, 2);
|
||||
if (time(nullptr) - opts->m_idel_heating_protect_detection.detect_hold_start > HOLD_TIME_3SEC)
|
||||
opts->m_idel_heating_protect_detection.current_detect_value = DevUtil::get_flag_bits(cfg, 32, 2);
|
||||
|
||||
// Orca: further top-level cfg-string detection bits, parsed here alongside idle-heating.
|
||||
if (time(nullptr) - opts->m_purify_air_at_print_end.detect_hold_start > HOLD_TIME_3SEC)
|
||||
opts->m_purify_air_at_print_end.current_detect_value = DevUtil::get_flag_bits(cfg, 36, 2);
|
||||
|
||||
// Smart nozzle blob detection mode: cfg bits 43-44 (2 bits), 0=off, 1=on, 2=auto
|
||||
if (time(nullptr) - opts->m_smart_nozzle_blob_detection.detect_hold_start > HOLD_TIME_3SEC)
|
||||
opts->m_smart_nozzle_blob_detection.current_detect_value = DevUtil::get_flag_bits(cfg, 43, 2);
|
||||
|
||||
if (time(nullptr) - opts->m_snapshot_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->m_snapshot_detection.current_detect_value = DevUtil::get_flag_bits(cfg, 38, 2);
|
||||
opts->m_snapshot_detection.is_support_detect =
|
||||
opts->m_snapshot_detection.current_detect_value != 0 && opts->m_snapshot_detection.current_detect_value != 3;
|
||||
}
|
||||
}
|
||||
|
||||
void DevPrintOptions::SetPrintingSpeedLevel(DevPrintingSpeedLevel speed_level)
|
||||
@@ -163,75 +222,85 @@ void DevPrintOptions::SetPrintingSpeedLevel(DevPrintingSpeedLevel speed_level)
|
||||
}
|
||||
}
|
||||
|
||||
PrintOptionData* DevPrintOptions::GetDetectionOption(PrintOptionEnum print_option)
|
||||
{
|
||||
auto it = m_detection_list.find(print_option);
|
||||
if (it != m_detection_list.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_xcam_control_ai_monitoring(bool on_off, std::string lvl)
|
||||
{
|
||||
bool print_halt = (lvl == "never_halt") ? false : true;
|
||||
|
||||
xcam_ai_monitoring = on_off;
|
||||
xcam_ai_monitoring_hold_start = time(nullptr);
|
||||
xcam_ai_monitoring_sensitivity = lvl;
|
||||
m_ai_monitoring_detection.current_detect_value = on_off;
|
||||
m_ai_monitoring_detection.detect_hold_start = time(nullptr);
|
||||
m_ai_monitoring_detection.current_detect_sensitivity_value = lvl;
|
||||
return command_xcam_control("printing_monitor", on_off, m_obj, lvl);
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_xcam_control_idelheatingprotect_detector(bool on_off)
|
||||
{
|
||||
idel_heating_protect_enabled = on_off;
|
||||
idel_heating_protect_hold_strat = time(nullptr);
|
||||
m_idel_heating_protect_detection.current_detect_value = on_off;
|
||||
m_idel_heating_protect_detection.detect_hold_start = time(nullptr);
|
||||
return command_set_against_continued_heating_mode(on_off);
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_xcam_control_buildplate_marker_detector(bool on_off)
|
||||
{
|
||||
xcam_buildplate_marker_detector = on_off;
|
||||
xcam_buildplate_marker_hold_start = time(nullptr);
|
||||
m_buildplate_mark_detection.current_detect_value = on_off;
|
||||
m_buildplate_mark_detection.detect_hold_start = time(nullptr);
|
||||
return command_xcam_control("buildplate_marker_detector", on_off ,m_obj);
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_xcam_control_first_layer_inspector(bool on_off, bool print_halt)
|
||||
{
|
||||
xcam_first_layer_inspector = on_off;
|
||||
xcam_first_layer_hold_start = time(nullptr);
|
||||
m_first_layer_detection.current_detect_value = on_off;
|
||||
m_first_layer_detection.detect_hold_start = time(nullptr);
|
||||
return command_xcam_control("first_layer_inspector", on_off, m_obj);
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_xcam_control_auto_recovery_step_loss(bool on_off)
|
||||
{
|
||||
xcam_auto_recovery_step_loss = on_off;
|
||||
xcam_auto_recovery_hold_start = time(nullptr);
|
||||
m_auto_recovery_detection.current_detect_value = on_off;
|
||||
m_auto_recovery_detection.detect_hold_start = time(nullptr);
|
||||
return command_set_printing_option(on_off, m_obj);
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_xcam_control_allow_prompt_sound(bool on_off)
|
||||
{
|
||||
xcam_allow_prompt_sound = on_off;
|
||||
xcam_prompt_sound_hold_start = time(nullptr);
|
||||
m_allow_prompt_sound_detection.current_detect_value = on_off;
|
||||
m_allow_prompt_sound_detection.detect_hold_start = time(nullptr);
|
||||
return command_set_prompt_sound(on_off, m_obj);
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_xcam_control_filament_tangle_detect(bool on_off)
|
||||
{
|
||||
xcam_filament_tangle_detect = on_off;
|
||||
xcam_filament_tangle_detect_hold_start = time(nullptr);
|
||||
m_filament_tangle_detection.current_detect_value = on_off;
|
||||
m_filament_tangle_detection.detect_hold_start = time(nullptr);
|
||||
return command_set_filament_tangle_detect(on_off, m_obj);
|
||||
}
|
||||
|
||||
void DevPrintOptions::parse_auto_recovery_step_loss_status(int flag) {
|
||||
if (time(nullptr) - xcam_auto_recovery_hold_start > HOLD_TIME_3SEC) {
|
||||
xcam_auto_recovery_step_loss = ((flag >> 4) & 0x1) != 0;
|
||||
if (time(nullptr) - m_auto_recovery_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
m_auto_recovery_detection.current_detect_value = ((flag >> 4) & 0x1) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
void DevPrintOptions::parse_allow_prompt_sound_status(int flag)
|
||||
{
|
||||
if (time(nullptr) - xcam_prompt_sound_hold_start > HOLD_TIME_3SEC) {
|
||||
xcam_allow_prompt_sound = ((flag >> 17) & 0x1) != 0;
|
||||
if (time(nullptr) - m_allow_prompt_sound_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
m_allow_prompt_sound_detection.current_detect_value = ((flag >> 17) & 0x1) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
void DevPrintOptions::parse_filament_tangle_detect_status(int flag)
|
||||
{
|
||||
if (time(nullptr) - xcam_filament_tangle_detect_hold_start > HOLD_TIME_3SEC) {
|
||||
xcam_filament_tangle_detect = ((flag >> 20) & 0x1) != 0;
|
||||
if (time(nullptr) - m_filament_tangle_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
m_filament_tangle_detection.current_detect_value = ((flag >> 20) & 0x1) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,11 +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
|
||||
// namespace Slic3r
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
#include <wx/string.h>
|
||||
@@ -9,17 +10,53 @@ namespace Slic3r {
|
||||
|
||||
class MachineObject;
|
||||
|
||||
// Identifies a print-detection option tracked in DevPrintOptions::m_detection_list.
|
||||
enum class PrintOptionEnum
|
||||
{
|
||||
AI_Monitoring,
|
||||
First_Layer_Detection,
|
||||
Buildplate_Mark_Detection,
|
||||
Buildplate_Align_Detection,
|
||||
Auto_Recovery_Detection,
|
||||
Allow_Prompt_Sound_Detection,
|
||||
Filament_Tangle_Detection,
|
||||
Idle_Heating_Protect_Detection,
|
||||
Purify_Air_At_Print_End,
|
||||
Snapshot_Detection,
|
||||
FOD_Check_Detection,
|
||||
Displacement_Detection,
|
||||
Smart_Nozzle_Blob_Detection,
|
||||
};
|
||||
|
||||
// State of a single print-detection option.
|
||||
struct PrintOptionData
|
||||
{
|
||||
bool is_support_detect{false}; // some detections have no supporting field
|
||||
int current_detect_value{-1}; // -1 until parsed; otherwise the reported value
|
||||
std::string current_detect_sensitivity_value;
|
||||
time_t detect_hold_start{0}; // suppresses parsed overwrites for a short hold
|
||||
};
|
||||
|
||||
class DevPrintOptions
|
||||
{
|
||||
friend class DevPrintOptionsParser;
|
||||
public:
|
||||
DevPrintOptions(MachineObject* obj): m_obj(obj) {}
|
||||
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);
|
||||
DevPrintingSpeedLevel GetPrintingSpeedLevel() const { return m_speed_level;}
|
||||
|
||||
// Returns the holder for a tracked detection option, or nullptr if not tracked.
|
||||
PrintOptionData* GetDetectionOption(PrintOptionEnum print_option);
|
||||
|
||||
// detect options
|
||||
int command_xcam_control_ai_monitoring(bool on_off, std::string lvl);
|
||||
int command_xcam_control_first_layer_inspector(bool on_off, bool print_halt);
|
||||
@@ -29,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
|
||||
@@ -39,42 +83,44 @@ 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);
|
||||
void parse_filament_tangle_detect_status(int flag);
|
||||
|
||||
bool GetAiMonitoring() const { return xcam_ai_monitoring; }
|
||||
bool GetFirstLayerInspector() const{ return xcam_first_layer_inspector; }
|
||||
bool GetBuildplateMarkerDetector() const { return xcam_buildplate_marker_detector; }
|
||||
bool GetAutoRecoveryStepLoss() const { return xcam_auto_recovery_step_loss; }
|
||||
bool GetAllowPromptSound() const { return xcam_allow_prompt_sound; }
|
||||
bool GetFilamentTangleDetect() const { return xcam_filament_tangle_detect; }
|
||||
int GetIdelHeatingProtectEenabled() const { return idel_heating_protect_enabled; }
|
||||
string GetAiMonitoringSensitivity() const { return xcam_ai_monitoring_sensitivity; }
|
||||
// Thin wrappers preserved for existing callers; read from the detection-option map.
|
||||
bool GetAiMonitoring() const { return m_ai_monitoring_detection.current_detect_value == 1; }
|
||||
bool GetFirstLayerInspector() const{ return m_first_layer_detection.current_detect_value == 1; }
|
||||
bool GetBuildplateMarkerDetector() const { return m_buildplate_mark_detection.current_detect_value == 1; }
|
||||
bool GetAutoRecoveryStepLoss() const { return m_auto_recovery_detection.current_detect_value == 1; }
|
||||
bool GetAllowPromptSound() const { return m_allow_prompt_sound_detection.current_detect_value == 1; }
|
||||
bool GetFilamentTangleDetect() const { return m_filament_tangle_detection.current_detect_value == 1; }
|
||||
int GetIdelHeatingProtectEenabled() const { return m_idel_heating_protect_detection.current_detect_value; }
|
||||
std::string GetAiMonitoringSensitivity() const { return m_ai_monitoring_detection.current_detect_sensitivity_value; }
|
||||
|
||||
|
||||
private:
|
||||
// print option
|
||||
DevPrintingSpeedLevel m_speed_level = SPEED_LEVEL_INVALID;
|
||||
|
||||
// detect options
|
||||
bool xcam_ai_monitoring{false};
|
||||
// detection options
|
||||
PrintOptionData m_ai_monitoring_detection;
|
||||
PrintOptionData m_first_layer_detection;
|
||||
PrintOptionData m_buildplate_mark_detection;
|
||||
PrintOptionData m_buildplate_align_detection;
|
||||
PrintOptionData m_auto_recovery_detection;
|
||||
PrintOptionData m_allow_prompt_sound_detection;
|
||||
PrintOptionData m_filament_tangle_detection;
|
||||
PrintOptionData m_idel_heating_protect_detection;
|
||||
PrintOptionData m_purify_air_at_print_end;
|
||||
PrintOptionData m_snapshot_detection;
|
||||
PrintOptionData m_fod_check_detection;
|
||||
PrintOptionData m_displacement_detection;
|
||||
PrintOptionData m_smart_nozzle_blob_detection;
|
||||
|
||||
std::string xcam_ai_monitoring_sensitivity;
|
||||
bool xcam_buildplate_marker_detector{false};
|
||||
bool xcam_first_layer_inspector{false};
|
||||
bool xcam_auto_recovery_step_loss{false};
|
||||
bool xcam_allow_prompt_sound{false};
|
||||
bool xcam_filament_tangle_detect{false};
|
||||
int idel_heating_protect_enabled = -1;
|
||||
time_t xcam_ai_monitoring_hold_start = 0;
|
||||
time_t xcam_buildplate_marker_hold_start = 0;
|
||||
time_t xcam_first_layer_hold_start = 0;
|
||||
time_t xcam_auto_recovery_hold_start = 0;
|
||||
time_t xcam_prompt_sound_hold_start = 0;
|
||||
time_t xcam_filament_tangle_detect_hold_start = 0;
|
||||
time_t idel_heating_protect_hold_strat = 0;
|
||||
std::map<PrintOptionEnum, PrintOptionData*> m_detection_list;
|
||||
|
||||
MachineObject* m_obj;/*owner*/
|
||||
};
|
||||
@@ -93,4 +139,4 @@ public:
|
||||
static void ParseDetectionV2_1(DevPrintOptions *opts, std::string cfg);
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
} // namespace Slic3r
|
||||
|
||||
33
src/slic3r/GUI/DeviceCore/DevStatus.cpp
Normal file
33
src/slic3r/GUI/DeviceCore/DevStatus.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "DevStatus.h"
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void DevStatus::ParseStatus(const nlohmann::json& print_jj)
|
||||
{
|
||||
try {
|
||||
if (print_jj.contains("job")) {
|
||||
const nlohmann::json& job_jj = print_jj.at("job");
|
||||
if (job_jj.contains("job_state")) {
|
||||
auto new_state = (DevJobState)job_jj.at("job_state").get<int>();
|
||||
if (m_job_state != new_state) {
|
||||
m_job_state = new_state;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": job_state-> " << (int)new_state;
|
||||
}
|
||||
|
||||
if (m_job_state == DevJobState::JobStateFinishing) {
|
||||
m_job_state = DevJobState::JobStateFinish;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
#if BBL_RELEASE_TO_PUBLIC
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": get exception";
|
||||
#else
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": get exception=" << e.what();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
43
src/slic3r/GUI/DeviceCore/DevStatus.h
Normal file
43
src/slic3r/GUI/DeviceCore/DevStatus.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
#include <optional>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
|
||||
#include "DevDefs.h"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
enum DevJobState
|
||||
{
|
||||
JobStateIdle = 0,
|
||||
JobStateSlicing = 1,
|
||||
JobStatePrepare = 2,
|
||||
JobStateStarting = 3,
|
||||
JobStateRunning = 4,
|
||||
JobStatePause = 5,
|
||||
JobStatePausing = 6,
|
||||
JobStateResuming = 7,
|
||||
JobStateFinish = 8,
|
||||
JobStateFailed = 9,
|
||||
JobStateFinishing = 10,
|
||||
JobStateStoppping = 11,
|
||||
};
|
||||
|
||||
class MachineObject;
|
||||
class DevStatus
|
||||
{
|
||||
public:
|
||||
DevStatus(MachineObject* owner) : m_owner(owner) {};
|
||||
|
||||
public:
|
||||
std::optional<DevJobState> GetJobState() const { return m_job_state; }
|
||||
|
||||
// Parse
|
||||
void ParseStatus(const nlohmann::json& print_jj);
|
||||
|
||||
private:
|
||||
MachineObject *m_owner = nullptr;
|
||||
std::optional<DevJobState> m_job_state; // could be nullopt for some old firmware
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -19,16 +19,42 @@ DevStorage::SdcardState Slic3r::DevStorage::set_sdcard_state(int state)
|
||||
{
|
||||
if (system)
|
||||
{
|
||||
if (print_json.contains("sdcard")) {
|
||||
if (print_json["sdcard"].get<bool>())
|
||||
system->m_sdcard_state = DevStorage::SdcardState::HAS_SDCARD_NORMAL;
|
||||
else
|
||||
try {
|
||||
if (print_json.contains("sdcard")) {
|
||||
if (print_json["sdcard"].get<bool>())
|
||||
system->m_sdcard_state = DevStorage::SdcardState::HAS_SDCARD_NORMAL;
|
||||
else
|
||||
system->m_sdcard_state = DevStorage::SdcardState::NO_SDCARD;
|
||||
} else {
|
||||
system->m_sdcard_state = DevStorage::SdcardState::NO_SDCARD;
|
||||
} else {
|
||||
system->m_sdcard_state = DevStorage::SdcardState::NO_SDCARD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parse timelapse storage space from cam push data
|
||||
if (print_json.contains("device") && print_json["device"].is_object()) {
|
||||
const auto& device_json = print_json["device"];
|
||||
if (device_json.contains("cam") && device_json["cam"].is_object()) {
|
||||
const auto& cam_data = device_json["cam"];
|
||||
if (cam_data.contains("tl_internal_free_kb"))
|
||||
system->tl_internal_free_kb = cam_data["tl_internal_free_kb"].get<int>();
|
||||
if (cam_data.contains("tl_internal_total_kb"))
|
||||
system->tl_internal_total_kb = cam_data["tl_internal_total_kb"].get<int>();
|
||||
if (cam_data.contains("tl_external_free_kb"))
|
||||
system->tl_external_free_kb = cam_data["tl_external_free_kb"].get<int>();
|
||||
if (cam_data.contains("tl_external_total_kb"))
|
||||
system->tl_external_total_kb = cam_data["tl_external_total_kb"].get<int>();
|
||||
}
|
||||
}
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
bool DevStorage::is_timelapse_storage_low(const std::string& storage) const
|
||||
{
|
||||
const int THRESHOLD_KB = 20480; // Orca: 20MB (20480 KB; corrected mislabeled comment)
|
||||
if (storage == "internal")
|
||||
return tl_internal_free_kb >= 0 && tl_internal_free_kb < THRESHOLD_KB;
|
||||
else
|
||||
return tl_external_free_kb >= 0 && tl_external_free_kb < THRESHOLD_KB;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -26,11 +26,21 @@ public:
|
||||
SdcardState get_sdcard_state() const { return m_sdcard_state; };
|
||||
SdcardState set_sdcard_state(int state);
|
||||
|
||||
static void ParseV1_0(const json &print_json, DevStorage *system);
|
||||
static void ParseV1_0(const json &print_json, DevStorage *system);
|
||||
|
||||
bool is_timelapse_storage_low(const std::string& storage) const;
|
||||
|
||||
private:
|
||||
MachineObject *m_owner;
|
||||
SdcardState m_sdcard_state { NO_SDCARD };
|
||||
// timelapse storage space info (from device push cam data)
|
||||
int tl_internal_free_kb{-1};
|
||||
int tl_internal_total_kb{-1};
|
||||
int tl_external_free_kb{-1};
|
||||
int tl_external_total_kb{-1};
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
130
src/slic3r/GUI/DeviceCore/DevUpgrade.cpp
Normal file
130
src/slic3r/GUI/DeviceCore/DevUpgrade.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
#include "DevUpgrade.h"
|
||||
#include "DevUtil.h"
|
||||
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
int DevUpgrade::GetUpgradeProgressInt() const
|
||||
{
|
||||
try {
|
||||
return std::stoi(m_upgrade_progress);
|
||||
} catch (const std::exception &e) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": " << e.what();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DevUpgrade::ParseUpgrade_V1_0(const json &print_jj)
|
||||
{
|
||||
if (print_jj.contains("upgrade_state")) {
|
||||
const auto &upgrade_jj = print_jj["upgrade_state"];
|
||||
|
||||
// status and steps
|
||||
DevJsonValParser::ParseVal(upgrade_jj, "status", m_upgrade_status);
|
||||
DevJsonValParser::ParseVal(upgrade_jj, "progress", m_upgrade_progress);
|
||||
DevJsonValParser::ParseVal(upgrade_jj, "message", m_upgrade_message);
|
||||
DevJsonValParser::ParseVal(upgrade_jj, "module", m_upgrade_module);
|
||||
DevJsonValParser::ParseVal(upgrade_jj, "err_code", m_upgrade_err_code);
|
||||
|
||||
// version requested
|
||||
DevJsonValParser::ParseVal(upgrade_jj, "new_version_state", m_upgrade_new_version_state);
|
||||
DevJsonValParser::ParseVal(upgrade_jj, "consistency_request", m_upgrade_consistency_request);
|
||||
DevJsonValParser::ParseVal(upgrade_jj, "force_upgrade", m_upgrade_force_upgrade);
|
||||
|
||||
// version info
|
||||
DevJsonValParser::ParseVal(upgrade_jj, "ota_new_version_number", m_ota_new_version_number);
|
||||
|
||||
// version list
|
||||
if (upgrade_jj.contains("new_ver_list")) {
|
||||
m_new_ver_list.clear();
|
||||
for (auto ver_item = upgrade_jj["new_ver_list"].begin(); ver_item != upgrade_jj["new_ver_list"].end(); ver_item++) {
|
||||
DevFirmwareVersionInfo ver_info;
|
||||
DevJsonValParser::ParseVal(*ver_item, "name", ver_info.name);
|
||||
DevJsonValParser::ParseVal(*ver_item, "cur_ver", ver_info.sw_ver);
|
||||
DevJsonValParser::ParseVal(*ver_item, "new_ver", ver_info.sw_new_ver);
|
||||
m_new_ver_list[ver_info.name] = ver_info;
|
||||
if (ver_info.name == "ota" && m_ota_new_version_number.empty()) { m_ota_new_version_number = ver_info.sw_new_ver; }
|
||||
}
|
||||
} else {
|
||||
m_new_ver_list.clear();
|
||||
}
|
||||
|
||||
// try to parse display state
|
||||
ParseUpgradeDisplayState(upgrade_jj);
|
||||
}
|
||||
|
||||
if (print_jj.contains("cfg")) {
|
||||
const std::string &cfg = print_jj["cfg"].get<std::string>();
|
||||
if (!cfg.empty()) { m_upgrade_force_upgrade = DevUtil::get_flag_bits(cfg, 2); }
|
||||
}
|
||||
}
|
||||
|
||||
void DevUpgrade::ParseUpgradeDisplayState(const json &upgrade_state_jj)
|
||||
{
|
||||
if (upgrade_state_jj.contains("dis_state")) {
|
||||
if ((int) m_upgrade_display_state != upgrade_state_jj["dis_state"].get<int>()) {
|
||||
DevJsonValParser::ParseVal(upgrade_state_jj, "dis_state", m_upgrade_display_state);
|
||||
|
||||
// update the version after upgrade finished
|
||||
if (m_upgrade_display_state == DevFirmwareUpgradeState::UpgradingFinished) {
|
||||
Slic3r::GUI::wxGetApp().CallAfter([this] {
|
||||
m_owner->command_get_version();
|
||||
});
|
||||
}
|
||||
|
||||
// lan mode printer, hide upgrade avaliable state
|
||||
if (m_upgrade_display_state == DevFirmwareUpgradeState::UpgradingAvaliable &&
|
||||
m_owner->is_lan_mode_printer()) {
|
||||
m_upgrade_display_state = DevFirmwareUpgradeState::UpgradingUnavaliable;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// BBS compatibility with old version
|
||||
if (m_upgrade_status == "DOWNLOADING" ||
|
||||
m_upgrade_status == "FLASHING" ||
|
||||
m_upgrade_status == "UPGRADE_REQUEST" ||
|
||||
m_upgrade_status == "PRE_FLASH_START" ||
|
||||
m_upgrade_status == "PRE_FLASH_SUCCESS") {
|
||||
m_upgrade_display_state = DevFirmwareUpgradeState::UpgradingInProgress;
|
||||
} else if (m_upgrade_status == "UPGRADE_SUCCESS" ||
|
||||
m_upgrade_status == "DOWNLOAD_FAIL" ||
|
||||
m_upgrade_status == "FLASH_FAIL" ||
|
||||
m_upgrade_status == "PRE_FLASH_FAIL" ||
|
||||
m_upgrade_status == "UPGRADE_FAIL") {
|
||||
m_upgrade_display_state = DevFirmwareUpgradeState::UpgradingFinished;
|
||||
} else {
|
||||
if (HasNewVersion()) {
|
||||
m_upgrade_display_state = DevFirmwareUpgradeState::UpgradingAvaliable;
|
||||
} else {
|
||||
m_upgrade_display_state = DevFirmwareUpgradeState::UpgradingUnavaliable;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define UpgradeNoError 0
|
||||
#define UpgradeDownloadFailed -1
|
||||
#define UpgradeVerfifyFailed -2
|
||||
#define UpgradeFlashFailed -3
|
||||
#define UpgradePrinting -4
|
||||
|
||||
wxString DevUpgrade::GetUpgradeErrCodeStr() const
|
||||
{
|
||||
switch (m_upgrade_err_code) {
|
||||
case UpgradeNoError: return _L("Update successful.");
|
||||
case UpgradeDownloadFailed: return _L("Downloading failed.");
|
||||
case UpgradeVerfifyFailed: return _L("Verification failed.");
|
||||
case UpgradeFlashFailed: return _L("Update failed.");
|
||||
case UpgradePrinting: return _L("Update failed.");
|
||||
default: return _L("Update failed.");
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
108
src/slic3r/GUI/DeviceCore/DevUpgrade.h
Normal file
108
src/slic3r/GUI/DeviceCore/DevUpgrade.h
Normal file
@@ -0,0 +1,108 @@
|
||||
#pragma once
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
|
||||
#include "DevDefs.h"
|
||||
#include "DevFirmware.h"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class MachineObject;
|
||||
class DevUpgrade
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<DevUpgrade> Create(MachineObject *owner) { return std::shared_ptr<DevUpgrade>(new DevUpgrade(owner)); }
|
||||
|
||||
public:
|
||||
// upgrade status
|
||||
bool IsUpgrading() const { return m_upgrade_display_state == DevFirmwareUpgradeState::UpgradingInProgress; }
|
||||
bool IsUpgradeAvaliable() const { return m_upgrade_display_state == DevFirmwareUpgradeState::UpgradingAvaliable; }
|
||||
DevFirmwareUpgradeState GetUpgradeState() const { return m_upgrade_display_state; }
|
||||
std::string GetUpgradeStatusStr() const { return m_upgrade_status; }
|
||||
|
||||
// upgrade request
|
||||
bool IsUpgradeConsistencyRequest() const { return m_upgrade_consistency_request; }
|
||||
bool IsUpgradeForceUpgrade() const { return m_upgrade_force_upgrade; }
|
||||
|
||||
bool HasNewVersion() const { return m_upgrade_new_version_state == 1; }
|
||||
std::string GetOtaNewVersion() const { return m_ota_new_version_number; }
|
||||
std::map<std::string, DevFirmwareVersionInfo> GetNewVersionList() const { return m_new_ver_list; }
|
||||
|
||||
// upgrading
|
||||
std::string GetUpgradeModuleStr() const { return m_upgrade_module; }
|
||||
std::string GetUpgradeProgressStr() const { return m_upgrade_progress; }
|
||||
int GetUpgradeProgressInt() const;
|
||||
std::string GetUpgradeMessageStr() const { return m_upgrade_message; }
|
||||
wxString GetUpgradeErrCodeStr() const;
|
||||
|
||||
public:
|
||||
// ctrls
|
||||
int CtrlUpgradeConfirm();
|
||||
int CtrlUpgradeConsistencyConfirm();
|
||||
int CtrlUpgradeFirmware(FirmwareInfo info);
|
||||
int CtrlUpgradeModule(std::string url, std::string module_type, std::string version);
|
||||
|
||||
// parser
|
||||
void ParseUpgrade_V1_0(const json &print_jj);
|
||||
void ParseUpgradeDisplayState(const json &upgrade_state_jj);
|
||||
|
||||
protected:
|
||||
DevUpgrade(MachineObject *owner) : m_owner(owner) {}
|
||||
|
||||
private:
|
||||
MachineObject *m_owner = nullptr;
|
||||
|
||||
int m_upgrade_new_version_state = 0; // 0: invalid version, 1: new version available, 2: no new version
|
||||
bool m_upgrade_consistency_request = false;
|
||||
bool m_upgrade_force_upgrade = false;
|
||||
|
||||
std::string m_ota_new_version_number;
|
||||
std::map<std::string, DevFirmwareVersionInfo> m_new_ver_list; // some protcols have version list
|
||||
DevFirmwareUpgradeState m_upgrade_display_state = DevFirmwareUpgradeState::DC;
|
||||
|
||||
int m_upgrade_err_code = 0;
|
||||
std::string m_upgrade_status;
|
||||
std::string m_upgrade_progress;
|
||||
std::string m_upgrade_message;
|
||||
std::string m_upgrade_module;
|
||||
};
|
||||
|
||||
#if 0 /*TODO*/
|
||||
class DevUpgradeGetVersionInfo
|
||||
{
|
||||
public:
|
||||
DevUpgradeGetVersionInfo(MachineObject* obj) : m_owner(obj) {} ;
|
||||
|
||||
public:
|
||||
bool IsEmpty() const { m_module_version_map.empty();}
|
||||
|
||||
DevFirmwareVersionInfo GetAirPumpVersionInfo() const { return m_air_pump_version_info; }
|
||||
DevFirmwareVersionInfo GetLaserVersionInfo() const { return m_laser_version_info; }
|
||||
DevFirmwareVersionInfo GetCuttingModuleVersionInfo() const { return m_cutting_module_version_info; }
|
||||
DevFirmwareVersionInfo GetExtinguishVersionInfo() const { return m_extinguish_version_info; }
|
||||
DevFirmwareVersionInfo GetRotaryVersionInfo() const { return m_rotary_version_info; }
|
||||
std::map<std::string, DevFirmwareVersionInfo> GetModuleVersionInfoMap() const { return m_module_version_map; }
|
||||
|
||||
public:
|
||||
int CtrlGetVersion(bool with_retry = true);
|
||||
|
||||
void ParseGetVersion(const json &print_jj);
|
||||
|
||||
private:
|
||||
MachineObject *m_owner;
|
||||
|
||||
// some version info
|
||||
DevFirmwareVersionInfo m_air_pump_version_info;
|
||||
DevFirmwareVersionInfo m_laser_version_info;
|
||||
DevFirmwareVersionInfo m_cutting_module_version_info;
|
||||
DevFirmwareVersionInfo m_extinguish_version_info;
|
||||
DevFirmwareVersionInfo m_rotary_version_info;
|
||||
std::map<std::string, DevFirmwareVersionInfo> m_module_version_map;
|
||||
|
||||
// ctrl retry
|
||||
int m_retry_count = 0;
|
||||
int m_max_retry = 3;
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace Slic3r
|
||||
56
src/slic3r/GUI/DeviceCore/DevUpgradeCtrl.cpp
Normal file
56
src/slic3r/GUI/DeviceCore/DevUpgradeCtrl.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "DevUpgrade.h"
|
||||
#include "DevUtil.h"
|
||||
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
int DevUpgrade::CtrlUpgradeConfirm()
|
||||
{
|
||||
json j;
|
||||
j["upgrade"]["command"] = "upgrade_confirm";
|
||||
j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["upgrade"]["src_id"] = 1; // 1 for slicer
|
||||
return m_owner->publish_json(j);
|
||||
}
|
||||
|
||||
int DevUpgrade::CtrlUpgradeConsistencyConfirm()
|
||||
{
|
||||
json j;
|
||||
j["upgrade"]["command"] = "consistency_confirm";
|
||||
j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["upgrade"]["src_id"] = 1; // 1 for slicer
|
||||
return m_owner->publish_json(j);
|
||||
}
|
||||
|
||||
int DevUpgrade::CtrlUpgradeFirmware(FirmwareInfo info)
|
||||
{
|
||||
std::string version = info.version;
|
||||
std::string dst_url = info.url;
|
||||
std::string module_name = info.module_type;
|
||||
|
||||
json j;
|
||||
j["upgrade"]["command"] = "start";
|
||||
j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["upgrade"]["url"] = info.url;
|
||||
j["upgrade"]["module"] = info.module_type;
|
||||
j["upgrade"]["version"] = info.version;
|
||||
j["upgrade"]["src_id"] = 1;
|
||||
|
||||
return m_owner->publish_json(j);
|
||||
}
|
||||
|
||||
int DevUpgrade::CtrlUpgradeModule(std::string url, std::string module_type, std::string version)
|
||||
{
|
||||
json j;
|
||||
j["upgrade"]["command"] = "start";
|
||||
j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["upgrade"]["url"] = url;
|
||||
j["upgrade"]["module"] = module_type;
|
||||
j["upgrade"]["version"] = version;
|
||||
j["upgrade"]["src_id"] = 1;
|
||||
|
||||
return m_owner->publish_json(j);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -24,6 +24,79 @@ int DevUtil::get_flag_bits(std::string str, int start, int count)
|
||||
|
||||
return 0;
|
||||
}
|
||||
uint32_t DevUtil::get_flag_bits_no_border(std::string str, int start_idx, int count)
|
||||
{
|
||||
if (start_idx < 0 || count <= 0) return 0;
|
||||
|
||||
try {
|
||||
// --- 1) trim ---
|
||||
auto ltrim = [](std::string &s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); })); };
|
||||
auto rtrim = [](std::string &s) { s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end()); };
|
||||
ltrim(str);
|
||||
rtrim(str);
|
||||
|
||||
// --- 2) remove 0x/0X prefix ---
|
||||
if (str.size() >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { str.erase(0, 2); }
|
||||
|
||||
// --- 3) keep only hex digits ---
|
||||
std::string hex;
|
||||
hex.reserve(str.size());
|
||||
for (char c : str) {
|
||||
if (std::isxdigit(static_cast<unsigned char>(c))) hex.push_back(c);
|
||||
}
|
||||
if (hex.empty()) return 0;
|
||||
|
||||
// --- 4) use size_t for all index/bit math ---
|
||||
const size_t total_bits = hex.size() * 4ULL;
|
||||
|
||||
const size_t ustart = static_cast<size_t>(start_idx);
|
||||
if (ustart >= total_bits) return 0;
|
||||
|
||||
const int int_bits = std::numeric_limits<uint32_t>::digits; // typically 32
|
||||
const size_t need_bits = static_cast<size_t>(std::min(count, int_bits));
|
||||
|
||||
// [first_bit, last_bit]
|
||||
const size_t first_bit = ustart;
|
||||
const size_t last_bit = std::min(ustart + need_bits, total_bits) - 1ULL;
|
||||
if (last_bit < first_bit) return 0;
|
||||
|
||||
const size_t right_index = hex.size() - 1ULL;
|
||||
|
||||
const size_t first_nibble = first_bit / 4ULL;
|
||||
const size_t last_nibble = last_bit / 4ULL;
|
||||
|
||||
const size_t start_idx = right_index - last_nibble;
|
||||
const size_t end_idx = right_index - first_nibble;
|
||||
if (end_idx < start_idx) return 0;
|
||||
|
||||
const size_t sub_len = end_idx - start_idx + 1ULL;
|
||||
if (end_idx >= hex.size()) return 0;
|
||||
|
||||
const std::string sub_hex = hex.substr(start_idx, sub_len);
|
||||
|
||||
unsigned long long chunk = std::stoull(sub_hex, nullptr, 16);
|
||||
|
||||
const unsigned nibble_offset = static_cast<unsigned>(first_bit % 4ULL);
|
||||
const unsigned long long shifted = (nibble_offset == 0U) ? chunk : (chunk >> nibble_offset);
|
||||
|
||||
uint32_t mask;
|
||||
if (need_bits >= static_cast<size_t>(std::numeric_limits<uint32_t>::digits)) {
|
||||
mask = std::numeric_limits<uint32_t>::max();
|
||||
} else {
|
||||
mask = static_cast<uint32_t>((1ULL << need_bits) - 1ULL);
|
||||
}
|
||||
|
||||
const uint32_t val = static_cast<uint32_t>(shifted & mask);
|
||||
return val;
|
||||
} catch (const std::invalid_argument &) {
|
||||
return 0;
|
||||
} catch (const std::out_of_range &) {
|
||||
return 0;
|
||||
} catch (...) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int DevUtil::get_flag_bits(int num, int start, int count, int base)
|
||||
{
|
||||
|
||||
@@ -12,8 +12,15 @@
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
/* Sequence Id*/
|
||||
#define STUDIO_START_SEQ_ID 20000
|
||||
#define STUDIO_END_SEQ_ID 30000
|
||||
#define CLOUD_SEQ_ID 0
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
@@ -27,14 +34,18 @@ public:
|
||||
public:
|
||||
static int get_flag_bits(std::string str, int start, int count = 1);
|
||||
static int get_flag_bits(int num, int start, int count = 1, int base = 10);
|
||||
static uint32_t get_flag_bits_no_border(std::string str, int start_idx, int count = 1);
|
||||
|
||||
// Extract the hex digit at position `pos` (0 = least-significant).
|
||||
// eg. get_hex_bits(16, 1, 10) = 1
|
||||
static int get_hex_bits(int num, int pos, int input_num_base = 10) { return get_flag_bits(num, pos * 4, 4, input_num_base); }
|
||||
static int get_hex_bits(int num, int pos, int input_num_base = 10) { return get_flag_bits(num, pos * 4, 4, input_num_base);};
|
||||
|
||||
static float string_to_float(const std::string& str_value);
|
||||
|
||||
static std::string convertToIp(long long ip);
|
||||
|
||||
// sequence id check
|
||||
static bool is_studio_cmd(int seq) { return seq >= STUDIO_START_SEQ_ID && seq < STUDIO_END_SEQ_ID;};
|
||||
static bool is_cloud_cmd(int seq) { return seq == CLOUD_SEQ_ID;};
|
||||
};
|
||||
|
||||
|
||||
@@ -108,4 +119,94 @@ struct NumericStrCompare
|
||||
}
|
||||
};
|
||||
|
||||
enum class DirtyMode{
|
||||
COUNTER,
|
||||
TIMER
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class DevDirtyHandler{
|
||||
public:
|
||||
DevDirtyHandler(T init_value, int setting_threshold, DirtyMode mode): m_value(init_value), m_setting_threshold(setting_threshold), m_mode(mode)
|
||||
{
|
||||
m_threshold = setting_threshold;
|
||||
}
|
||||
~DevDirtyHandler(){};
|
||||
|
||||
T GetValue() const { return m_value; };
|
||||
|
||||
void SetOptimisticValue(const T& data)
|
||||
{
|
||||
m_value = data;
|
||||
m_start_time = time(nullptr);
|
||||
m_threshold = m_setting_threshold;
|
||||
}
|
||||
|
||||
void UpdateValue(const T& data)
|
||||
{
|
||||
if (m_mode == DirtyMode::COUNTER)
|
||||
{
|
||||
if (m_threshold > 0)
|
||||
m_threshold--;
|
||||
else
|
||||
m_value = data;
|
||||
}
|
||||
else if (m_mode == DirtyMode::TIMER)
|
||||
{
|
||||
if (time(nullptr) - m_start_time > m_threshold)
|
||||
m_value = data;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
T m_value;
|
||||
DirtyMode m_mode;
|
||||
int m_setting_threshold{0};
|
||||
|
||||
int m_start_time{0};
|
||||
int m_threshold{0};
|
||||
};
|
||||
|
||||
|
||||
static std::string s_get_diameter_str(float diameter)
|
||||
{
|
||||
return (boost::format("%.2f") % diameter).str();
|
||||
}
|
||||
|
||||
static std::string s_get_diameter_str(const std::string& diameter)
|
||||
{
|
||||
try {
|
||||
float dia = boost::lexical_cast<float>(diameter);
|
||||
return s_get_diameter_str(dia);
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " failed to boost::lexical_cast: " << diameter;
|
||||
return diameter;
|
||||
}
|
||||
|
||||
try {
|
||||
float dia = std::stof(diameter);
|
||||
return s_get_diameter_str(dia);
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " std::stof: " << diameter;
|
||||
return diameter;
|
||||
}
|
||||
}
|
||||
|
||||
static float s_get_diameter(const std::string& diameter)
|
||||
{
|
||||
try {
|
||||
return boost::lexical_cast<float>(diameter);
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " failed to boost::lexical_cast: " << diameter;
|
||||
}
|
||||
|
||||
try {
|
||||
return std::stof(diameter);
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " std::stof: " << diameter;
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
@@ -2,15 +2,28 @@
|
||||
|
||||
#include "slic3r/GUI/BackgroundSlicingProcess.hpp"
|
||||
|
||||
#include "slic3r/GUI/PartPlate.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <wx/string.h>
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
std::shared_ptr<MultiNozzleUtils::NozzleGroupResultBase> DevUtilBackend::GetNozzleGroupResult(Slic3r::GUI::Plater* plater)
|
||||
|
||||
Slic3r::MultiNozzleUtils::NozzleInfo DevUtilBackend::GetNozzleInfo(const DevNozzle& dev_nozzle)
|
||||
{
|
||||
MultiNozzleUtils::NozzleInfo info;
|
||||
info.diameter = dev_nozzle.GetNozzleDiameterStr().ToStdString();
|
||||
info.volume_type = DevNozzle::ToNozzleVolumeType(dev_nozzle.GetNozzleFlowType());
|
||||
info.extruder_id = dev_nozzle.GetLogicExtruderId();
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
std::shared_ptr<Slic3r::MultiNozzleUtils::NozzleGroupResultBase> DevUtilBackend::GetNozzleGroupResult(Slic3r::GUI::Plater *plater)
|
||||
{
|
||||
if (plater && plater->background_process().get_current_gcode_result()) {
|
||||
return plater->background_process().get_current_gcode_result()->nozzle_group_result;
|
||||
@@ -19,6 +32,29 @@ std::shared_ptr<MultiNozzleUtils::NozzleGroupResultBase> DevUtilBackend::GetNozz
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unordered_map<NozzleDef, int> DevUtilBackend::CollectNozzleInfo(MultiNozzleUtils::NozzleGroupResultBase *nozzle_group_res, int logic_ext_id)
|
||||
{
|
||||
std::unordered_map<NozzleDef, int> need_nozzle_map;
|
||||
if (!nozzle_group_res) {
|
||||
return need_nozzle_map;
|
||||
}
|
||||
|
||||
const std::vector<Slic3r::MultiNozzleUtils::NozzleInfo>& nozzle_vec = nozzle_group_res->get_used_nozzles_in_extruder(logic_ext_id);
|
||||
for (auto slicing_nozzle : nozzle_vec) {
|
||||
try {
|
||||
NozzleDef data;
|
||||
data.nozzle_diameter = boost::lexical_cast<float>(slicing_nozzle.diameter);
|
||||
data.nozzle_flow_type = DevNozzle::ToNozzleFlowType(slicing_nozzle.volume_type);
|
||||
need_nozzle_map[data]++;
|
||||
} catch (const std::exception& e) {
|
||||
assert(0);
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "exception: " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
return need_nozzle_map;
|
||||
}
|
||||
|
||||
static std::unordered_map<std::string, DevAmsType> s_ams_type_map = {
|
||||
{"0", DevAmsType::N3F},
|
||||
{"1", DevAmsType::N3S},
|
||||
@@ -41,6 +77,7 @@ std::optional<Slic3r::DevFilamentDryingPreset> DevUtilBackend::GetFilamentDrying
|
||||
std::vector<std::string> types = config.option<ConfigOptionStrings>("filament_dev_ams_drying_ams_limitations")->values;
|
||||
for (auto type : types) {
|
||||
if (s_ams_type_map.count(type) == 0) {
|
||||
// assert(0);
|
||||
continue;
|
||||
}
|
||||
info.ams_limitations.insert(s_ams_type_map[type]);
|
||||
@@ -62,10 +99,10 @@ std::optional<Slic3r::DevFilamentDryingPreset> DevUtilBackend::GetFilamentDrying
|
||||
}
|
||||
|
||||
if (config.has("filament_dev_drying_softening_temperature")) {
|
||||
info.filament_dev_drying_softening_temperature = config.option<ConfigOptionFloats>("filament_dev_drying_softening_temperature")->get_at(0);
|
||||
info.filament_dev_drying_softening_temperature = config.option<ConfigOptionFloats>("filament_dev_drying_softening_temperature")->get_at(0);
|
||||
}
|
||||
|
||||
if (config.has("filament_dev_ams_drying_heat_distortion_temperature")) {
|
||||
if (config.has("filament_dev_ams_drying_heat_distortion_temperature")){
|
||||
info.filament_dev_ams_drying_heat_distortion_temperature = config.option<ConfigOptionFloats>("filament_dev_ams_drying_heat_distortion_temperature")->get_at(0);
|
||||
}
|
||||
|
||||
@@ -83,4 +120,4 @@ std::optional<Slic3r::DevFilamentDryingPreset> DevUtilBackend::GetFilamentDrying
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
};// namespace Slic3r
|
||||
@@ -1,19 +1,32 @@
|
||||
/**
|
||||
* @file DevUtilBackend.h
|
||||
* @brief Provides common static utility methods for backend (preset/slicing).
|
||||
*
|
||||
* This class offers a collection of static helper functions such as string manipulation,
|
||||
* file operations, and other frequently used utilities.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "DevDefs.h"
|
||||
#include "DevNozzleSystem.h"
|
||||
#include "DevFilaSystem.h"
|
||||
|
||||
#include "libslic3r/MultiNozzleUtils.hpp"
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
// Forward declarations
|
||||
namespace Slic3r
|
||||
{
|
||||
class MachineObject;
|
||||
|
||||
namespace GUI
|
||||
{
|
||||
class Plater;
|
||||
}
|
||||
}; // namespace Slic3r::GUI
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
namespace GUI { class Plater; }
|
||||
|
||||
class DevUtilBackend
|
||||
{
|
||||
@@ -21,14 +34,14 @@ public:
|
||||
DevUtilBackend() = delete;
|
||||
|
||||
public:
|
||||
static MultiNozzleUtils::NozzleInfo GetNozzleInfo(const DevNozzle& dev_nozzle);
|
||||
|
||||
// for rack: the slicer's per-filament -> logical-nozzle grouping for the current plate, read off
|
||||
// the post-slice GCodeProcessorResult (plater->background_process().get_current_gcode_result()).
|
||||
// Returns nullptr when there is no plater / no current result.
|
||||
static std::shared_ptr<MultiNozzleUtils::NozzleGroupResultBase> GetNozzleGroupResult(Slic3r::GUI::Plater* plater);
|
||||
// for rack
|
||||
static std::shared_ptr<MultiNozzleUtils::NozzleGroupResultBase> GetNozzleGroupResult(Slic3r::GUI::Plater *plater);
|
||||
static std::unordered_map<NozzleDef, int> CollectNozzleInfo(MultiNozzleUtils::NozzleGroupResultBase *nozzle_group_res, int logic_ext_id);
|
||||
|
||||
// for filament preset
|
||||
static std::optional<DevFilamentDryingPreset> GetFilamentDryingPreset(const std::string& fila_id);
|
||||
};
|
||||
|
||||
}; // namespace Slic3r
|
||||
}; // namespace Slic3r
|
||||
@@ -5,6 +5,10 @@
|
||||
#include "GUI_App.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "ReleaseNote.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
#include <wx/mstream.h>
|
||||
#include <wx/dcmemory.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI
|
||||
@@ -88,10 +92,22 @@ DeviceErrorDialog::DeviceErrorDialog(MachineObject* obj, wxWindow* parent, wxWin
|
||||
if (m_obj) { m_obj->command_clean_print_error_uiop(m_obj->print_error); }
|
||||
e.Skip();
|
||||
});
|
||||
|
||||
m_request_timer = new wxTimer(this);
|
||||
Bind(wxEVT_TIMER, &DeviceErrorDialog::on_request_timeout, this, m_request_timer->GetId());
|
||||
}
|
||||
|
||||
DeviceErrorDialog::~DeviceErrorDialog()
|
||||
{
|
||||
// Orca: invalidate any in-flight cloud snapshot callback (the request has no cancel handle)
|
||||
*m_alive = false;
|
||||
|
||||
if (m_request_timer) {
|
||||
m_request_timer->Stop();
|
||||
delete m_request_timer;
|
||||
m_request_timer = nullptr;
|
||||
}
|
||||
|
||||
if (web_request.IsOk() && web_request.GetState() == wxWebRequest::State_Active)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "web_request: cancelled";
|
||||
@@ -103,6 +119,11 @@ DeviceErrorDialog::~DeviceErrorDialog()
|
||||
void DeviceErrorDialog::on_webrequest_state(wxWebRequestEvent& evt)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "monitor: monitor_panel web request state = " << evt.GetState();
|
||||
|
||||
// A local/HTTP illustration resolved (or the cloud path fell back to it): stop the watchdog.
|
||||
m_request_cancelled.store(true);
|
||||
clear_request_timer();
|
||||
|
||||
switch (evt.GetState())
|
||||
{
|
||||
case wxWebRequest::State_Completed:
|
||||
@@ -120,7 +141,9 @@ void DeviceErrorDialog::on_webrequest_state(wxWebRequestEvent& evt)
|
||||
case wxWebRequest::State_Cancelled:
|
||||
case wxWebRequest::State_Unauthorized:
|
||||
{
|
||||
m_error_picture->SetBitmap(wxBitmap());
|
||||
m_error_picture->SetBitmap(make_placeholder_image(_L("Network unavailable")));
|
||||
Layout();
|
||||
Fit();
|
||||
break;
|
||||
}
|
||||
case wxWebRequest::State_Active:
|
||||
@@ -129,6 +152,123 @@ void DeviceErrorDialog::on_webrequest_state(wxWebRequestEvent& evt)
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceErrorDialog::on_request_timeout(wxTimerEvent& event)
|
||||
{
|
||||
if (m_request_cancelled.load()) { return; }
|
||||
// Same fallback order as the cloud callback: local/HTTP illustration first, placeholder last.
|
||||
if (!get_fail_snapshot_from_local(m_local_img_url)) {
|
||||
m_error_picture->SetBitmap(make_placeholder_image(_L("Network unavailable")));
|
||||
}
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
|
||||
void DeviceErrorDialog::clear_request_timer()
|
||||
{
|
||||
if (m_request_timer && m_request_timer->IsRunning()) {
|
||||
m_request_timer->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
wxBitmap DeviceErrorDialog::make_placeholder_image(const wxString& text)
|
||||
{
|
||||
const int w = FromDIP(320);
|
||||
const int h = FromDIP(180);
|
||||
|
||||
wxBitmap bmp(wxSize(w, h));
|
||||
wxMemoryDC dc(bmp);
|
||||
dc.SetBackground(wxBrush(wxColour(238, 238, 238)));
|
||||
dc.Clear();
|
||||
|
||||
ScalableBitmap icon = ScalableBitmap(this, "dev_hms_diag_loading", 80);
|
||||
wxBitmap icon_bmp = icon.bmp();
|
||||
if (icon_bmp.IsOk()) {
|
||||
int ix = (w - icon_bmp.GetWidth()) / 2;
|
||||
int iy = (h - icon_bmp.GetHeight()) / 3;
|
||||
dc.DrawBitmap(icon_bmp, ix, iy, true);
|
||||
}
|
||||
|
||||
dc.SetTextForeground(wxColour(158, 158, 158));
|
||||
dc.SetFont(Label::Body_14);
|
||||
wxSize txtSize = dc.GetTextExtent(text);
|
||||
int tx = (w - txtSize.GetWidth()) / 2;
|
||||
int ty = h - txtSize.GetHeight() - FromDIP(45);
|
||||
dc.DrawText(text, tx, ty);
|
||||
|
||||
dc.SelectObject(wxNullBitmap);
|
||||
return bmp;
|
||||
}
|
||||
|
||||
bool DeviceErrorDialog::get_fail_snapshot_from_cloud()
|
||||
{
|
||||
if (!m_obj || m_obj->m_print_error_img_id.empty()) { return false; }
|
||||
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
if (!agent) { return false; }
|
||||
|
||||
// Orca: the cloud request has no cancel handle, so guard the callback against dialog
|
||||
// destruction with a liveness token (m_alive, cleared in the dtor) and marshal onto the UI
|
||||
// thread via the always-valid app handler instead of this->CallAfter. The raw bytes are
|
||||
// decoded on the UI thread so no wxImage (non-atomic refcount) is shared across threads.
|
||||
// m_request_seq guards against a callback from a previous error painting over the
|
||||
// current one when the dialog is reused by a later show_error_code().
|
||||
auto alive = m_alive;
|
||||
const int seq = m_request_seq;
|
||||
int ret = agent->get_hms_snapshot(m_obj->get_dev_id(), m_obj->m_print_error_img_id,
|
||||
[this, alive, seq](std::string body, int status) {
|
||||
if (!alive->load()) { return; }
|
||||
wxGetApp().CallAfter([this, alive, seq, body = std::move(body), status]() {
|
||||
if (!alive->load()) { return; }
|
||||
if (seq != m_request_seq) { return; }
|
||||
m_request_cancelled.store(true);
|
||||
clear_request_timer();
|
||||
|
||||
wxImage image;
|
||||
bool ok = false;
|
||||
if (status == 200) {
|
||||
wxMemoryInputStream stream(body.data(), body.size());
|
||||
ok = image.LoadFile(stream, wxBITMAP_TYPE_ANY);
|
||||
if (!ok) { BOOST_LOG_TRIVIAL(error) << "get_fail_snapshot_from_cloud: failed to resolve stream"; }
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(error) << "get_fail_snapshot_from_cloud: status = " << status;
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
wxImage resize_img = image.Scale(FromDIP(320), FromDIP(180), wxIMAGE_QUALITY_HIGH);
|
||||
m_error_picture->SetBitmap(wxBitmap(resize_img));
|
||||
} else if (!get_fail_snapshot_from_local(m_local_img_url)) {
|
||||
m_error_picture->SetBitmap(make_placeholder_image(_L("Network unavailable")));
|
||||
}
|
||||
Layout();
|
||||
Fit();
|
||||
});
|
||||
});
|
||||
|
||||
return ret == 0; // dispatched; the frame arrives later via the callback
|
||||
}
|
||||
|
||||
bool DeviceErrorDialog::get_fail_snapshot_from_local(const wxString& image_url)
|
||||
{
|
||||
if (image_url.empty()) { return false; }
|
||||
|
||||
const wxImage& img = wxGetApp().get_hms_query()->query_image_from_local(image_url);
|
||||
if (!img.IsOk() && image_url.Contains("http"))
|
||||
{
|
||||
web_request = wxWebSession::GetDefault().CreateRequest(this, image_url);
|
||||
BOOST_LOG_TRIVIAL(trace) << "monitor: create new webrequest, state = " << web_request.GetState();
|
||||
if (web_request.GetState() == wxWebRequest::State_Idle) web_request.Start();
|
||||
BOOST_LOG_TRIVIAL(trace) << "monitor: start new webrequest, state = " << web_request.GetState();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_request_cancelled.store(true);
|
||||
clear_request_timer();
|
||||
const wxImage& resize_img = img.Scale(FromDIP(320), FromDIP(180), wxIMAGE_QUALITY_HIGH);
|
||||
m_error_picture->SetBitmap(wxBitmap(resize_img));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeviceErrorDialog::init_button(ActionButton style, wxString buton_text)
|
||||
{
|
||||
if (btn_bg_white.count() == 0)
|
||||
@@ -170,6 +310,7 @@ void DeviceErrorDialog::init_button_list()
|
||||
init_button(FILAMENT_LOAD_RESUME, _L("Filament Loaded, Resume"));
|
||||
init_button(JUMP_TO_LIVEVIEW, _L("View Liveview"));
|
||||
init_button(NO_REMINDER_NEXT_TIME, _L("No Reminder Next Time"));
|
||||
init_button(REFRESH_NOZZLE, _L("Recheck"));
|
||||
init_button(IGNORE_NO_REMINDER_NEXT_TIME, _L("Ignore. Don't Remind Next Time"));
|
||||
init_button(IGNORE_RESUME, _L("Ignore this and Resume"));
|
||||
init_button(PROBLEM_SOLVED_RESUME, _L("Problem Solved and Resume"));
|
||||
@@ -178,6 +319,10 @@ void DeviceErrorDialog::init_button_list()
|
||||
init_button(CANCEL, _L("Cancel"));
|
||||
init_button(STOP_DRYING, _L("Stop Drying"));
|
||||
init_button(PROCEED, _L("Proceed"));
|
||||
init_button(OK_JUMP_RACK, _L("OK"));
|
||||
init_button(ABORT, _L("Abort"));
|
||||
init_button(DISABLE_PURIFICATION, _L("Disable Purification for This Print"));
|
||||
init_button(DONT_REMIND_NEXT_TIME, _L("Don't Remind Me"));
|
||||
init_button(DBL_CHECK_CANCEL, _L("Cancel"));
|
||||
init_button(DBL_CHECK_DONE, _L("Done"));
|
||||
init_button(DBL_CHECK_RETRY, _L("Retry"));
|
||||
@@ -241,6 +386,7 @@ wxString DeviceErrorDialog::show_error_code(int error_code)
|
||||
Raise();
|
||||
|
||||
#ifndef __linux__
|
||||
// Orca: skip RequestUserAttention on Linux/Wayland (urgency-hint deadlock).
|
||||
// On Linux (especially Wayland) RequestUserAttention(wxUSER_ATTENTION_ERROR) maps to
|
||||
// gtk_window_set_urgency_hint(TRUE) which can leave the window in an urgent-but-unfocused
|
||||
// state — clicks no longer reach any widget in the app and the user has to kill the
|
||||
@@ -305,22 +451,23 @@ void DeviceErrorDialog::update_contents(const wxString& title, const wxString& t
|
||||
}
|
||||
|
||||
/* image */
|
||||
if (!image_url.empty())
|
||||
// Orca: tiered image slot — prefer the printer's captured camera frame of the failure
|
||||
// (cloud, keyed by m_print_error_img_id), else the local/HTTP HMS illustration, else hide.
|
||||
// Reuses m_error_picture + on_webrequest_state (single widget, no second request). The
|
||||
// loading placeholder and 10s watchdog are armed only for the async cloud fetch; the local
|
||||
// path keeps its existing behavior.
|
||||
m_local_img_url = image_url;
|
||||
m_request_cancelled.store(false);
|
||||
++m_request_seq; // invalidate any snapshot fetch still in flight for the previous error
|
||||
clear_request_timer();
|
||||
if (get_fail_snapshot_from_cloud())
|
||||
{
|
||||
m_error_picture->SetBitmap(make_placeholder_image(_L("Loading ...")));
|
||||
m_request_timer->StartOnce(10000);
|
||||
m_error_picture->Show();
|
||||
}
|
||||
else if (get_fail_snapshot_from_local(image_url))
|
||||
{
|
||||
const wxImage& img = wxGetApp().get_hms_query()->query_image_from_local(image_url);
|
||||
if (!img.IsOk() && image_url.Contains("http"))
|
||||
{
|
||||
web_request = wxWebSession::GetDefault().CreateRequest(this, image_url);
|
||||
BOOST_LOG_TRIVIAL(trace) << "monitor: create new webrequest, state = " << web_request.GetState();
|
||||
if (web_request.GetState() == wxWebRequest::State_Idle) web_request.Start();
|
||||
BOOST_LOG_TRIVIAL(trace) << "monitor: start new webrequest, state = " << web_request.GetState();
|
||||
}
|
||||
else
|
||||
{
|
||||
const wxImage& resize_img = img.Scale(FromDIP(320), FromDIP(180), wxIMAGE_QUALITY_HIGH);
|
||||
m_error_picture->SetBitmap(wxBitmap(resize_img));
|
||||
}
|
||||
|
||||
m_error_picture->Show();
|
||||
}
|
||||
else
|
||||
@@ -349,7 +496,10 @@ void DeviceErrorDialog::update_contents(const wxString& title, const wxString& t
|
||||
auto text_size = m_error_msg_label->GetBestSize();
|
||||
if (text_size.y < FromDIP(360))
|
||||
{
|
||||
if (!image_url.empty())
|
||||
// Orca: reserve the image area exactly when an image is coming (cloud snapshot,
|
||||
// local illustration, or the loading placeholder) - m_error_picture's visibility
|
||||
// was decided above, so no blank slot is reserved when neither source is usable.
|
||||
if (m_error_picture->IsShown())
|
||||
{
|
||||
m_scroll_area->SetMinSize(wxSize(FromDIP(320), text_size.y + FromDIP(220)));
|
||||
}
|
||||
@@ -462,6 +612,29 @@ void DeviceErrorDialog::on_button_click(ActionButton btn_id)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::REFRESH_NOZZLE: {
|
||||
m_obj->command_refresh_nozzle();
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::OK_JUMP_RACK: {
|
||||
Slic3r::GUI::wxGetApp().mainframe->jump_to_monitor();
|
||||
Slic3r::GUI::wxGetApp().mainframe->m_monitor->jump_to_Rack();
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::ABORT: {
|
||||
m_obj->command_ams_control("abort");
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::DISABLE_PURIFICATION: {
|
||||
m_obj->command_purification_disable();
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::DONT_REMIND_NEXT_TIME: {
|
||||
if (!m_action_json.is_null()) {
|
||||
m_obj->command_dont_remind_next_time(m_action_json);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::ERROR_BUTTON_COUNT: break;
|
||||
|
||||
case DeviceErrorDialog::DBL_CHECK_CANCEL: {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_set>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <wx/statbmp.h>
|
||||
#include <wx/timer.h>
|
||||
#include <wx/webrequest.h>
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
@@ -35,6 +38,7 @@ public:
|
||||
JUMP_TO_LIVEVIEW,
|
||||
|
||||
NO_REMINDER_NEXT_TIME = 23,
|
||||
REFRESH_NOZZLE = 24,
|
||||
IGNORE_NO_REMINDER_NEXT_TIME = 25,
|
||||
//LOAD_FILAMENT = 26*/
|
||||
IGNORE_RESUME = 27,
|
||||
@@ -46,6 +50,10 @@ public:
|
||||
CANCEL = 37,
|
||||
REMOVE_CLOSE_BTN = 39, // special case, do not show close button
|
||||
PROCEED = 41,
|
||||
OK_JUMP_RACK = 49,
|
||||
ABORT = 51,
|
||||
DISABLE_PURIFICATION = 54,
|
||||
DONT_REMIND_NEXT_TIME = 57,
|
||||
|
||||
ERROR_BUTTON_COUNT,
|
||||
|
||||
@@ -84,6 +92,11 @@ protected:
|
||||
|
||||
void on_button_click(ActionButton btn_id);
|
||||
void on_webrequest_state(wxWebRequestEvent& evt);
|
||||
void on_request_timeout(wxTimerEvent& event);
|
||||
void clear_request_timer();
|
||||
wxBitmap make_placeholder_image(const wxString& text);
|
||||
bool get_fail_snapshot_from_cloud();
|
||||
bool get_fail_snapshot_from_local(const wxString& image_url);
|
||||
void on_dpi_changed(const wxRect& suggested_rect);
|
||||
|
||||
private:
|
||||
@@ -93,6 +106,14 @@ private:
|
||||
std::unordered_set<Button*> m_used_button;
|
||||
|
||||
wxWebRequest web_request;
|
||||
wxTimer* m_request_timer{ nullptr };
|
||||
std::atomic<bool> m_request_cancelled{ false };
|
||||
// UI-thread-only generation counter: bumped on every update_contents() so a cloud
|
||||
// snapshot callback from a previous error cannot paint over the current one.
|
||||
int m_request_seq{ 0 };
|
||||
wxString m_local_img_url;
|
||||
// Orca: liveness token for the async cloud snapshot callback (the request has no cancel handle)
|
||||
std::shared_ptr<std::atomic_bool> m_alive{ std::make_shared<std::atomic_bool>(true) };
|
||||
wxStaticBitmap* m_error_picture;
|
||||
Label* m_error_msg_label{ nullptr };
|
||||
Label* m_error_code_label{ nullptr };
|
||||
|
||||
@@ -44,6 +44,12 @@
|
||||
#include "DeviceCore/DevManager.h"
|
||||
#include "DeviceCore/DevUtil.h"
|
||||
|
||||
// Orca: adopt DeviceCore split — axis/calib/chamber/status/upgrade modules
|
||||
#include "DeviceCore/DevAxis.h"
|
||||
#include "DeviceCore/DevChamber.h"
|
||||
#include "DeviceCore/DevStatus.h"
|
||||
#include "DeviceCore/DevUpgrade.h"
|
||||
|
||||
|
||||
#define CALI_DEBUG
|
||||
#define MINUTE_30 1800000 //ms
|
||||
@@ -68,6 +74,44 @@ int get_tray_id_by_ams_id_and_slot_id(int ams_id, int slot_id)
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Stringing-prone filament IDs per nozzle-diameter bucket.
|
||||
// Mirrors the printer firmware tables (see g_leak_pron_idx_for_0_4 / _0_6_0_8).
|
||||
// Keep these in sync with firmware when new stringing-prone filaments are added.
|
||||
const std::unordered_set<std::string> g_stringing_prone_for_0_4 = {
|
||||
"GFA11", // PLA Aero
|
||||
"GFU90", // TPU 90A
|
||||
"GFU00", // TPU 95A HF
|
||||
"GFU02", // Generic TPU for AMS
|
||||
"GFU98", // TPU for AMS
|
||||
};
|
||||
|
||||
const std::unordered_set<std::string> g_stringing_prone_for_0_6_0_8 = {
|
||||
"GFA11", // PLA Aero
|
||||
"GFU00", // TPU 95A HF
|
||||
};
|
||||
|
||||
// Pick the right table for the given nozzle diameter; returns nullptr if the
|
||||
// nozzle bucket has no entries (e.g. 0.2 mm) or the diameter is invalid.
|
||||
const std::unordered_set<std::string>* pick_stringing_set(float nozzle_diameter)
|
||||
{
|
||||
if (!(nozzle_diameter > 0.f)) return nullptr;
|
||||
if (nozzle_diameter < 0.3f) return nullptr; // 0.2 nozzle: empty
|
||||
if (nozzle_diameter < 0.5f) return &g_stringing_prone_for_0_4;
|
||||
return &g_stringing_prone_for_0_6_0_8; // 0.6 / 0.8 nozzles
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool Slic3r::is_stringing_prone_filament(const std::string& filament_id, float nozzle_diameter)
|
||||
{
|
||||
if (filament_id.empty()) return false;
|
||||
const auto* set = pick_stringing_set(nozzle_diameter);
|
||||
if (!set) return false;
|
||||
return set->count(filament_id) > 0;
|
||||
}
|
||||
|
||||
wxString Slic3r::get_stage_string(int stage)
|
||||
{
|
||||
switch(stage) {
|
||||
@@ -553,6 +597,7 @@ MachineObject::MachineObject(DeviceManager* manager, NetworkAgent* agent, std::s
|
||||
mc_print_sub_stage = 0;
|
||||
mc_left_time = 0;
|
||||
hw_switch_state = 0;
|
||||
m_print_error_img_id = "";
|
||||
|
||||
has_ipcam = true; // default true
|
||||
|
||||
@@ -577,6 +622,13 @@ MachineObject::MachineObject(DeviceManager* manager, NetworkAgent* agent, std::s
|
||||
m_print_options = new DevPrintOptions(this);
|
||||
|
||||
m_nozzle_mapping_ptr = std::make_shared<DevNozzleMappingCtrl>(this);
|
||||
|
||||
// Orca: adopt DeviceCore split — axis/calib/chamber/status/upgrade modules
|
||||
m_axis = DevAxis::Create(this);
|
||||
m_chamber = DevChamber::Create(this);
|
||||
m_upgrade = DevUpgrade::Create(this);
|
||||
m_status = new DevStatus(this);
|
||||
m_calib = new DevCalib(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -629,6 +681,13 @@ MachineObject::~MachineObject()
|
||||
|
||||
delete m_print_options;
|
||||
m_print_options = nullptr;
|
||||
|
||||
// Orca: adopt DeviceCore split
|
||||
delete m_calib;
|
||||
m_calib = nullptr;
|
||||
|
||||
delete m_status;
|
||||
m_status = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -698,6 +757,33 @@ std::string MachineObject::get_filament_display_type(const std::string& ams_id,
|
||||
return this->get_tray(ams_id, tray_id).get_display_filament_type();
|
||||
}
|
||||
|
||||
bool MachineObject::any_loaded_filament_is_stringing_prone() const
|
||||
{
|
||||
if (print_job_filament_mapping.empty()) return false;
|
||||
|
||||
std::vector<float> nozzle_diameters;
|
||||
if (m_extder_system) {
|
||||
for (const auto& ext : m_extder_system->GetExtruders()) {
|
||||
const float d = ext.GetNozzleDiameter();
|
||||
if (d > 0.f) nozzle_diameters.push_back(d);
|
||||
}
|
||||
}
|
||||
if (nozzle_diameters.empty()) return false;
|
||||
|
||||
for (uint16_t v : print_job_filament_mapping) {
|
||||
if (v == 0xFFFF) continue;
|
||||
const int ams_id = (v >> 8) & 0xFF;
|
||||
const int slot_id = v & 0xFF;
|
||||
const std::string fid = this->get_filament_id(std::to_string(ams_id), std::to_string(slot_id));
|
||||
if (fid.empty()) continue;
|
||||
for (float d : nozzle_diameters) {
|
||||
if (Slic3r::is_stringing_prone_filament(fid, d))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MachineObject::_parse_ams_status(int ams_status)
|
||||
{
|
||||
ams_status_sub = ams_status & 0xFF;
|
||||
@@ -716,6 +802,8 @@ void MachineObject::_parse_ams_status(int ams_status)
|
||||
ams_status_main = AmsStatusMain::AMS_STATUS_MAIN_SELF_CHECK;
|
||||
} else if (ams_status_main_int == (int) AmsStatusMain::AMS_STATUS_MAIN_DEBUG) {
|
||||
ams_status_main = AmsStatusMain::AMS_STATUS_MAIN_DEBUG;
|
||||
} else if (ams_status_main_int == (int) AmsStatusMain::AMS_STATUS_MAIN_COLD_PULL) {
|
||||
ams_status_main = AmsStatusMain::AMS_STATUS_MAIN_COLD_PULL;
|
||||
} else {
|
||||
ams_status_main = AmsStatusMain::AMS_STATUS_MAIN_UNKNOWN;
|
||||
}
|
||||
@@ -867,6 +955,9 @@ void MachineObject::clear_version_info()
|
||||
laser_version_info = DevFirmwareVersionInfo();
|
||||
cutting_module_version_info = DevFirmwareVersionInfo();
|
||||
extinguish_version_info = DevFirmwareVersionInfo();
|
||||
rotary_version_info = DevFirmwareVersionInfo();
|
||||
exhaustfan_version_info = DevFirmwareVersionInfo();
|
||||
amshub_version_info = DevFirmwareVersionInfo();
|
||||
filatrack_version_info = DevFirmwareVersionInfo();
|
||||
module_vers.clear();
|
||||
// Drop cached rack-hotend (WTM) firmware alongside the module list.
|
||||
@@ -884,11 +975,17 @@ void MachineObject::store_version_info(const DevFirmwareVersionInfo& info)
|
||||
cutting_module_version_info = info;
|
||||
} else if (info.isExtinguishSystem()) {
|
||||
extinguish_version_info = info;
|
||||
} else if (info.isRotary()) {
|
||||
rotary_version_info = info;
|
||||
} else if (info.isWTM()) {
|
||||
// Route rack-hotend / extruder-nozzle firmware into the nozzle system so
|
||||
// the rack upgrade UI can read per-nozzle versions. isWTM() is false for every non-rack
|
||||
// printer's modules, so this branch never fires outside H2C.
|
||||
m_nozzle_system->AddFirmwareInfoWTM(info);
|
||||
} else if (info.isExhaustFan()) {
|
||||
exhaustfan_version_info = info;
|
||||
} else if (info.isHmshub()) {
|
||||
amshub_version_info = info;
|
||||
} else if (info.isFilaTrackSwitch()) {
|
||||
filatrack_version_info = info;
|
||||
}
|
||||
@@ -1207,7 +1304,7 @@ bool MachineObject::canEnableTimelapse(wxString &error_message) const
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_storage->get_sdcard_state() != DevStorage::SdcardState::HAS_SDCARD_NORMAL) {
|
||||
if (m_storage->get_sdcard_state() != DevStorage::SdcardState::HAS_SDCARD_NORMAL && !m_has_timelapse_kit) {
|
||||
if (m_storage->get_sdcard_state() == DevStorage::SdcardState::NO_SDCARD) {
|
||||
error_message = _L("Timelapse is not supported while the storage does not exist.");
|
||||
} else if (m_storage->get_sdcard_state() == DevStorage::SdcardState::HAS_SDCARD_ABNORMAL) {
|
||||
@@ -1857,10 +1954,17 @@ int MachineObject::command_axis_control(std::string axis, double unit, double in
|
||||
{
|
||||
if (m_support_mqtt_axis_control)
|
||||
{
|
||||
int dir = input_val > 0 ? 1 : -1;
|
||||
// i3-arch printers move the bed for Y/Z, so the on-screen direction is
|
||||
// reversed — same negation the g-code fallback below applies.
|
||||
if (!is_core_xy() && (axis.compare("Y") == 0 || axis.compare("Z") == 0)) {
|
||||
dir = -dir;
|
||||
}
|
||||
|
||||
json j;
|
||||
j["print"]["command"] = "xyz_ctrl";
|
||||
j["print"]["axis"] = axis;
|
||||
j["print"]["dir"] = input_val > 0 ? 1 : -1;
|
||||
j["print"]["dir"] = dir;
|
||||
j["print"]["mode"] = (std::abs(input_val) >= 10) ? 1 : 0;
|
||||
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
return this->publish_json(j);
|
||||
@@ -2199,6 +2303,14 @@ int MachineObject::command_ack_proceed(json& proceed) {
|
||||
} else {
|
||||
proceed["err_ignored"] = std::vector<int>{proceed["err_index"]};
|
||||
}
|
||||
|
||||
for (auto& item : proceed["err_ignored"]) {
|
||||
json error_item;
|
||||
error_item["idx"] = item.get<int>();
|
||||
error_item["mode"] = 0;
|
||||
proceed["rm_idx"].push_back(error_item);
|
||||
}
|
||||
|
||||
proceed["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
|
||||
json j;
|
||||
@@ -2206,6 +2318,78 @@ int MachineObject::command_ack_proceed(json& proceed) {
|
||||
return this->publish_json(j);
|
||||
}
|
||||
|
||||
bool MachineObject::is_timelapse_storage_low(const std::string& storage) const
|
||||
{
|
||||
return m_storage && m_storage->is_timelapse_storage_low(storage);
|
||||
}
|
||||
|
||||
int MachineObject::command_ipcam_check_timelapse_storage(const std::string& storage, int total_layer)
|
||||
{
|
||||
json j;
|
||||
j["camera"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["camera"]["command"] = "ipcam_get_media_info";
|
||||
j["camera"]["sub_command"] = "is_timelapse_storage_enough";
|
||||
j["camera"]["storage"] = storage;
|
||||
j["camera"]["total_layer"] = total_layer;
|
||||
return this->publish_json(j);
|
||||
}
|
||||
|
||||
int MachineObject::command_ipcam_delete_oldest_timelapse(const std::string& storage, int total_layer)
|
||||
{
|
||||
json j;
|
||||
j["camera"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["camera"]["command"] = "ipcam_delete_oldest_timelapse";
|
||||
j["camera"]["storage"] = storage;
|
||||
j["camera"]["total_layer"] = total_layer;
|
||||
return this->publish_json(j);
|
||||
}
|
||||
|
||||
int MachineObject::command_purification_disable()
|
||||
{
|
||||
json j;
|
||||
j["print"]["command"] = "close_air_filt";
|
||||
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
|
||||
return this->publish_json(j, 1);
|
||||
}
|
||||
|
||||
int MachineObject::command_dont_remind_next_time(json& mqtt_guard_json)
|
||||
{
|
||||
if (!mqtt_guard_json.contains("command") ||
|
||||
!mqtt_guard_json.contains("err_index") ||
|
||||
mqtt_guard_json["err_index"].empty()) return -1;
|
||||
|
||||
json j;
|
||||
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
|
||||
try {
|
||||
j["print"]["command"] = mqtt_guard_json["command"].get<std::string>();
|
||||
int err_index = mqtt_guard_json["err_index"].get<int>();
|
||||
|
||||
if (mqtt_guard_json.contains("err_ignored") &&
|
||||
mqtt_guard_json["err_ignored"].is_array()) {
|
||||
j["print"]["err_ignored"] = mqtt_guard_json["err_ignored"];
|
||||
j["print"]["err_ignored"].push_back(err_index);
|
||||
} else {
|
||||
j["print"]["err_ignored"] = std::vector<int>{err_index};
|
||||
}
|
||||
|
||||
for (auto& item : j["print"]["err_ignored"]) {
|
||||
if (!item.is_number_integer()) continue;
|
||||
|
||||
json item_json;
|
||||
item_json["idx"] = item.get<int>();
|
||||
item_json["mode"] = 1; // 1-next time ignore, 2-always ignore
|
||||
j["print"]["rm_idx"].push_back(item_json);
|
||||
}
|
||||
} catch (const json::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << "JSON parsing error in command_dont_remind_next_time: " << e.what();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return this->publish_json(j, 1);
|
||||
}
|
||||
|
||||
int MachineObject::command_xcam_control_ai_monitoring(bool on_off, std::string lvl)
|
||||
{
|
||||
bool print_halt = (lvl == "never_halt") ? false:true;
|
||||
@@ -2915,6 +3099,7 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
|
||||
//supported function
|
||||
m_config->ParseConfig(jj);
|
||||
m_status->ParseStatus(jj); // Orca: adopt DeviceCore split — populate DevStatus module
|
||||
|
||||
if (jj.contains("support_build_plate_marker_detect")) {
|
||||
if (jj["support_build_plate_marker_detect"].is_boolean()) {
|
||||
@@ -3142,6 +3327,17 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
if (jj["print_error"].is_number())
|
||||
print_error = jj["print_error"].get<int>();
|
||||
}
|
||||
// Orca: keep the failure-snapshot id only while an error is active, so a stale
|
||||
// id can't leak into a later unrelated error dialog once the failure clears.
|
||||
if (print_error <= 0) {
|
||||
m_print_error_img_id.clear();
|
||||
}
|
||||
else if (jj.contains("err2") && jj["err2"].is_object()) {
|
||||
json err2 = jj["err2"];
|
||||
if (err2.contains("img_id") && err2["img_id"].is_string()) {
|
||||
m_print_error_img_id = err2["img_id"].get<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
DevStorage::ParseV1_0(jj, m_storage);
|
||||
|
||||
@@ -3163,6 +3359,9 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
int flag3 = jj["flag3"].get<int>();
|
||||
is_support_filament_setting_inprinting = get_flag_bits(flag3, 3);
|
||||
is_enable_ams_np = get_flag_bits(flag3, 9);
|
||||
is_support_fila_change_abort = get_flag_bits(flag3, 13); // filament-change Stop button
|
||||
is_support_ext_change_assist_old = get_flag_bits(flag3, 16); // A/P-series multi-color external change assist
|
||||
is_support_filament_32_colors = get_flag_bits(flag3, 17);
|
||||
}
|
||||
}
|
||||
if (!key_field_only) {
|
||||
@@ -3328,6 +3527,16 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
if (!key_field_only) {
|
||||
/* temperature */
|
||||
|
||||
// Orca: adopt DeviceCore split — populate DevAxis/DevChamber modules
|
||||
// alongside the inline handling (side-effect-free; inline stays authoritative).
|
||||
// Contained locally so a malformed field cannot abort the whole status parse.
|
||||
try {
|
||||
m_axis->ParseAxis(jj);
|
||||
m_chamber->ParseChamber(jj);
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "parse_json: DevAxis/DevChamber parse failed";
|
||||
}
|
||||
|
||||
DevBed::ParseV1_0(jj,m_bed);
|
||||
|
||||
if (jj.contains("frame_temper")) {
|
||||
@@ -4336,6 +4545,17 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
this->camera_resolution = j["camera"]["resolution"].get<std::string>();
|
||||
BOOST_LOG_TRIVIAL(info) << "ack of resolution = " << camera_resolution;
|
||||
}
|
||||
} else if (j["camera"]["command"].get<std::string>() == "ipcam_get_media_info") {
|
||||
if (j["camera"].contains("sub_command") &&
|
||||
j["camera"]["sub_command"].get<std::string>() == "is_timelapse_storage_enough") {
|
||||
timelapse_storage_check_result = j["camera"].value("result", -1);
|
||||
timelapse_storage_is_enough = j["camera"].value("is_enough", true);
|
||||
timelapse_storage_file_count = j["camera"].value("file_count", 0);
|
||||
timelapse_storage_check_done = true;
|
||||
BOOST_LOG_TRIVIAL(info) << "timelapse storage check: result=" << timelapse_storage_check_result
|
||||
<< " is_enough=" << timelapse_storage_is_enough
|
||||
<< " file_count=" << timelapse_storage_file_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4940,6 +5160,15 @@ bool MachineObject::check_enable_np(const json& print) const
|
||||
return false;
|
||||
}
|
||||
|
||||
// Max filament color count for the send gate; 0 = no explicit upper bound.
|
||||
int MachineObject::get_max_filament_color_count() const
|
||||
{
|
||||
if (is_support_filament_32_colors) return 32;
|
||||
if (is_enable_ams_np && !is_series_x()) return 20;
|
||||
if (!is_series_x() && !is_series_o()) return 16;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MachineObject::parse_new_info(json print)
|
||||
{
|
||||
is_enable_np = check_enable_np(print);
|
||||
@@ -5093,8 +5322,23 @@ void MachineObject::parse_new_info(json print)
|
||||
// fun2 may have infinite length, use get_flag_bits_no_border
|
||||
if (!fun2.empty()) {
|
||||
is_support_print_with_emmc = get_flag_bits_no_border(fun2, 0) == 1;
|
||||
is_support_pa_mode = (get_flag_bits_no_border(fun2, 3) == 1);
|
||||
is_support_remote_dry = (get_flag_bits_no_border(fun2, 5) == 1);
|
||||
is_support_check_track_switch_match_slice_printer = get_flag_bits_no_border(fun2, 19) == 1;
|
||||
|
||||
if (DevPrinterConfigUtil::support_print_check_firmware_for_tpu_left(printer_type)) {
|
||||
m_firmware_support_print_tpu_left = get_flag_bits_no_border(fun2, 7) == 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Per-filament-index AMS slot mapping reported by the printer (task-level state) */
|
||||
if (print.contains("mapping") && print["mapping"].is_array()) {
|
||||
std::vector<uint16_t> new_mapping;
|
||||
new_mapping.reserve(print["mapping"].size());
|
||||
for (const auto& v : print["mapping"]) {
|
||||
new_mapping.push_back(static_cast<uint16_t>(v.get<unsigned>()));
|
||||
}
|
||||
print_job_filament_mapping = std::move(new_mapping);
|
||||
}
|
||||
|
||||
/*aux*/
|
||||
@@ -5104,6 +5348,7 @@ void MachineObject::parse_new_info(json print)
|
||||
|
||||
if (!aux.empty()) {
|
||||
m_storage->set_sdcard_state(get_flag_bits(aux, 12, 2));
|
||||
m_has_timelapse_kit = (get_flag_bits(aux, 26, 1) == 1);
|
||||
}
|
||||
|
||||
/*stat*/
|
||||
@@ -5564,7 +5809,9 @@ wxString MachineObject::get_nozzle_replace_url() const
|
||||
return link_map["en"].get<wxString>();
|
||||
}/*retry with en*/
|
||||
|
||||
return "https://wiki.bambulab.com/en/h2/maintenance/replace-hotend";
|
||||
// Orca: no neutral wiki equivalent for this fallback — return empty so the caller hides the link
|
||||
// (PrinterPartsDialog::OnWikiClicked reports "No wiki link available" instead of opening a browser)
|
||||
return wxEmptyString;
|
||||
}
|
||||
|
||||
std::string MachineObject::get_error_code_str(int error_code)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
@@ -21,6 +22,7 @@
|
||||
#include "DeviceCore/DevDefs.h"
|
||||
#include "DeviceCore/DevConfigUtil.h"
|
||||
#include "DeviceCore/DevFirmware.h"
|
||||
#include "DeviceCore/DevCalib.h" // Orca: adopt DeviceCore split (defines DevCalib, CalibStatus, ManualPaCaliMethod)
|
||||
#include "DeviceErrorDialog.hpp"
|
||||
|
||||
#include <wx/object.h>
|
||||
@@ -62,11 +64,7 @@ class DeviceErrorDialog; // Previous definitions
|
||||
}
|
||||
|
||||
class NetworkAgent;
|
||||
enum ManualPaCaliMethod {
|
||||
PA_LINE = 0,
|
||||
PA_PATTERN,
|
||||
};
|
||||
|
||||
// Orca: ManualPaCaliMethod now provided by DeviceCore/DevCalib.h (enum class)
|
||||
|
||||
#define UpgradeNoError 0
|
||||
#define UpgradeDownloadFailed -1
|
||||
@@ -77,7 +75,9 @@ enum ManualPaCaliMethod {
|
||||
// Previous definitions
|
||||
class DevAms;
|
||||
class DevAmsTray;
|
||||
class DevAxis; // Orca: adopt DeviceCore split
|
||||
class DevBed;
|
||||
class DevChamber; // Orca: adopt DeviceCore split
|
||||
class DevConfig;
|
||||
class DevCtrl;
|
||||
class DevExtensionTool;
|
||||
@@ -91,9 +91,15 @@ class DevLamp;
|
||||
class DevNozzleSystem;
|
||||
class DevNozzleMappingCtrl;
|
||||
class DeviceManager;
|
||||
class DevStatus; // Orca: adopt DeviceCore split
|
||||
class DevStorage;
|
||||
class DevUpgrade; // Orca: adopt DeviceCore split
|
||||
struct DevPrintTaskRatingInfo;
|
||||
|
||||
// Returns true when filament_id (e.g. "GFA11", "GFU00") is on the stringing-prone list for the
|
||||
// given nozzle diameter (mm), bucketed per nozzle size to mirror the printer firmware.
|
||||
bool is_stringing_prone_filament(const std::string& filament_id, float nozzle_diameter);
|
||||
|
||||
|
||||
class MachineObject
|
||||
{
|
||||
@@ -123,6 +129,15 @@ private:
|
||||
DevBed * m_bed;
|
||||
DevStorage* m_storage;
|
||||
|
||||
/* Orca: adopt DeviceCore split — axis/calib/chamber/status/upgrade modules alongside
|
||||
MachineObject's inline handling of these concerns, which stays authoritative here (Orca keeps
|
||||
the inline model permanently; the split modules coexist with it via accessors). */
|
||||
std::shared_ptr<DevAxis> m_axis;
|
||||
std::shared_ptr<DevChamber> m_chamber;
|
||||
DevCalib* m_calib{ nullptr };
|
||||
DevStatus* m_status{ nullptr };
|
||||
std::shared_ptr<DevUpgrade> m_upgrade;
|
||||
|
||||
/*Ctrl*/
|
||||
DevCtrl* m_ctrl;
|
||||
|
||||
@@ -145,6 +160,18 @@ public:
|
||||
~MachineObject();
|
||||
|
||||
void set_agent(NetworkAgent* agent) { m_agent = agent; }
|
||||
NetworkAgent* get_agent() const { return m_agent; } // Orca: needed by DeviceCore modules (DevAxisCtrl)
|
||||
|
||||
// Orca: these DeviceCore module accessors are unwired on the read side — axis/chamber/status
|
||||
// are fed every MQTT push but no GUI consumer reads them yet, and for calib/upgrade the inline
|
||||
// parse in DeviceManager.cpp remains authoritative. Do not wire DevUpgrade naively: its
|
||||
// ParseUpgradeDisplayState would duplicate the inline block's dis_state==3 ->
|
||||
// command_get_version CallAfter side effect.
|
||||
std::shared_ptr<DevAxis> GetAxis() const { return m_axis; }
|
||||
std::shared_ptr<DevChamber> GetChamber() const { return m_chamber; }
|
||||
DevCalib* GetCalib() const { return m_calib; }
|
||||
DevStatus* GetStatus() const { return m_status; }
|
||||
std::weak_ptr<DevUpgrade> GetUpgrade() const { return m_upgrade; }
|
||||
|
||||
public:
|
||||
enum ActiveState {
|
||||
@@ -379,6 +406,9 @@ public:
|
||||
DevFirmwareVersionInfo laser_version_info;
|
||||
DevFirmwareVersionInfo cutting_module_version_info;
|
||||
DevFirmwareVersionInfo extinguish_version_info;
|
||||
DevFirmwareVersionInfo rotary_version_info;
|
||||
DevFirmwareVersionInfo exhaustfan_version_info;
|
||||
DevFirmwareVersionInfo amshub_version_info;
|
||||
DevFirmwareVersionInfo filatrack_version_info;
|
||||
std::map<std::string, DevFirmwareVersionInfo> module_vers;
|
||||
std::map<std::string, DevFirmwareVersionInfo> new_ver_list;
|
||||
@@ -417,6 +447,7 @@ public:
|
||||
bool is_system_printing();
|
||||
|
||||
int print_error;
|
||||
std::string m_print_error_img_id;
|
||||
static std::string get_error_code_str(int error_code);
|
||||
std::string get_print_error_str() const { return MachineObject::get_error_code_str(this->print_error); }
|
||||
|
||||
@@ -557,6 +588,7 @@ public:
|
||||
|
||||
bool file_model_download{false};
|
||||
bool virtual_camera{false};
|
||||
bool m_has_timelapse_kit{false};
|
||||
|
||||
bool xcam_ai_monitoring{ false };
|
||||
bool xcam_disable_ai_detection_display{false};
|
||||
@@ -633,10 +665,18 @@ public:
|
||||
bool is_support_airprinting_detection{false};
|
||||
bool is_support_idelheadingprotect_detection{false};
|
||||
|
||||
// timelapse storage check result (temp state from MQTT response)
|
||||
std::atomic<bool> timelapse_storage_check_done { false };
|
||||
int timelapse_storage_check_result { -1 };
|
||||
bool timelapse_storage_is_enough { true };
|
||||
int timelapse_storage_file_count { 0 };
|
||||
|
||||
// fun2
|
||||
bool is_support_print_with_emmc{false};
|
||||
bool is_support_remote_dry = false;
|
||||
bool is_support_check_track_switch_match_slice_printer{false};
|
||||
bool is_support_pa_mode{false};
|
||||
std::optional<bool> m_firmware_support_print_tpu_left;
|
||||
|
||||
bool installed_upgrade_kit{false};
|
||||
int bed_temperature_limit = -1;
|
||||
@@ -666,6 +706,11 @@ public:
|
||||
boost::thread* get_slice_info_thread { nullptr };
|
||||
boost::thread* get_model_task_thread { nullptr };
|
||||
|
||||
// Per-filament-index AMS slot mapping reported by the printer in print.mapping. Up to
|
||||
// 32 entries, each value packs (ams_id << 8) | slot_id; 0xFFFF means unused.
|
||||
std::vector<uint16_t> print_job_filament_mapping;
|
||||
bool any_loaded_filament_is_stringing_prone() const;
|
||||
|
||||
/* job attr */
|
||||
int jobState_ = 0;
|
||||
|
||||
@@ -703,6 +748,7 @@ public:
|
||||
|
||||
/* quick check*/
|
||||
bool canEnableTimelapse(wxString& error_message) const;
|
||||
bool is_timelapse_storage_low(const std::string& storage) const;
|
||||
|
||||
/* command commands */
|
||||
int command_get_version(bool with_retry = true);
|
||||
@@ -714,6 +760,8 @@ public:
|
||||
int command_set_printer_nozzle2(int id, std::string nozzle_type, float diameter);
|
||||
int command_get_access_code();
|
||||
int command_ack_proceed(json& proceed);
|
||||
int command_purification_disable();
|
||||
int command_dont_remind_next_time(json& mqtt_guard_json);
|
||||
|
||||
/* command upgrade */
|
||||
int command_upgrade_confirm();
|
||||
@@ -805,6 +853,8 @@ public:
|
||||
int command_ipcam_record(bool on_off);
|
||||
int command_ipcam_timelapse(bool on_off);
|
||||
int command_ipcam_resolution_set(std::string resolution);
|
||||
int command_ipcam_check_timelapse_storage(const std::string& storage, int total_layer);
|
||||
int command_ipcam_delete_oldest_timelapse(const std::string& storage, int total_layer);
|
||||
int command_xcam_control(std::string module_name, bool on_off, std::string lvl = "");
|
||||
|
||||
//refine printer
|
||||
@@ -878,6 +928,12 @@ public:
|
||||
/*for more extruder*/
|
||||
bool is_enable_np{ false };
|
||||
bool is_enable_ams_np{ false };
|
||||
bool is_support_filament_32_colors{ false };
|
||||
bool is_support_fila_change_abort{ false }; // filament-change Stop button
|
||||
bool is_support_ext_change_assist_old{ false }; // A/P-series multi-color external change assist
|
||||
|
||||
// Max filament color count for the send gate; returns 0 when there is no explicit upper bound.
|
||||
int get_max_filament_color_count() const;
|
||||
|
||||
/**
|
||||
* Virtual Tray (vt_slot) - External/manual filament loading slots.
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
list(APPEND SLIC3R_GUI_SOURCES
|
||||
GUI/DeviceTab/uiAmsHumidityPopup.h
|
||||
GUI/DeviceTab/uiAmsHumidityPopup.cpp
|
||||
GUI/DeviceTab/uiAMSBestPositionPopup.hpp
|
||||
GUI/DeviceTab/uiAMSBestPositionPopup.cpp
|
||||
GUI/DeviceTab/uiDeviceUpdateVersion.h
|
||||
GUI/DeviceTab/uiDeviceUpdateVersion.cpp
|
||||
GUI/DeviceTab/wgtDeviceNozzleRackNozzleItem.h
|
||||
GUI/DeviceTab/wgtDeviceNozzleRackNozzleItem.cpp
|
||||
GUI/DeviceTab/wgtDeviceNozzleRack.h
|
||||
GUI/DeviceTab/wgtDeviceNozzleRack.cpp
|
||||
GUI/DeviceTab/wgtDeviceNozzleRackUpdate.h
|
||||
@@ -14,5 +14,7 @@ list(APPEND SLIC3R_GUI_SOURCES
|
||||
GUI/DeviceTab/wgtDeviceNozzleSelect.cpp
|
||||
GUI/DeviceTab/wgtMsgBox.h
|
||||
GUI/DeviceTab/wgtMsgBox.cpp
|
||||
GUI/DeviceTab/wgtMsgPanel.h
|
||||
GUI/DeviceTab/wgtMsgPanel.cpp
|
||||
)
|
||||
set(SLIC3R_GUI_SOURCES ${SLIC3R_GUI_SOURCES} PARENT_SCOPE)
|
||||
1046
src/slic3r/GUI/DeviceTab/uiAMSBestPositionPopup.cpp
Normal file
1046
src/slic3r/GUI/DeviceTab/uiAMSBestPositionPopup.cpp
Normal file
File diff suppressed because it is too large
Load Diff
226
src/slic3r/GUI/DeviceTab/uiAMSBestPositionPopup.hpp
Normal file
226
src/slic3r/GUI/DeviceTab/uiAMSBestPositionPopup.hpp
Normal file
@@ -0,0 +1,226 @@
|
||||
//**********************************************************/
|
||||
/* File: uiAMSBestPositionPopup.hpp
|
||||
* Description: The popup with suggest best ams position
|
||||
*
|
||||
//**********************************************************/
|
||||
|
||||
#pragma once
|
||||
#include "slic3r/GUI/Widgets/AMSItem.hpp"
|
||||
#include "slic3r/GUI/Widgets/Label.hpp"
|
||||
#include "slic3r/GUI/Widgets/PopupWindow.hpp"
|
||||
#include "slic3r/GUI/wxExtensions.hpp"
|
||||
#include "slic3r/GUI/Widgets/Button.hpp"
|
||||
#include "slic3r/GUI/DeviceCore/DevFilaSystem.h"
|
||||
#include "slic3r/GUI/DeviceCore/DevFilaSwitch.h"
|
||||
#include <algorithm>
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
|
||||
class UiStyledAMSPanel : public wxPanel
|
||||
{
|
||||
public:
|
||||
UiStyledAMSPanel(wxWindow* parent,
|
||||
wxWindowID id = wxID_ANY,
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize,
|
||||
const wxColour& borderColor = wxColour(200, 200, 200),
|
||||
const wxColour& bgColor = wxColour(255, 255, 255),
|
||||
bool borderDashed = true,
|
||||
wxString name = "",
|
||||
bool isTop = false);
|
||||
|
||||
protected:
|
||||
void OnPaint(wxPaintEvent& event);
|
||||
|
||||
private:
|
||||
|
||||
bool m_borderDashed;
|
||||
int m_borderWidth;
|
||||
int m_radius;
|
||||
wxColour m_borderColor;
|
||||
wxColour m_bgColor;
|
||||
wxString m_name;
|
||||
bool m_isTop;
|
||||
};
|
||||
|
||||
class UiStyledSwitchPanel : public wxPanel
|
||||
{
|
||||
public:
|
||||
UiStyledSwitchPanel(wxWindow* parent,
|
||||
wxWindowID id,
|
||||
const wxPoint& pos,
|
||||
const wxSize& size,
|
||||
const wxColour& borderColor,
|
||||
const wxColour& bgColor,
|
||||
bool borderDashed,
|
||||
int borderWidth,
|
||||
int radius,
|
||||
bool isTop);
|
||||
|
||||
|
||||
void AddToLeft(wxWindow* window, int proportion = 0, int flag = wxEXPAND, int border = 0);
|
||||
void AddToRight(wxWindow* window, int proportion = 0, int flag = wxEXPAND, int border = 0);
|
||||
void Clear(bool deleteWindows);
|
||||
|
||||
wxSizer* GetLeftSizer() { return m_leftSizer; }
|
||||
wxSizer* GetRightSizer() { return m_rightSizer; }
|
||||
void LayoutAndFit()
|
||||
{
|
||||
m_leftSizer->Layout();
|
||||
// m_leftSizer->Fit();
|
||||
m_rightSizer->Layout();
|
||||
// m_rightSizer->Fit();
|
||||
m_splitSizer->Layout();
|
||||
// m_splitSizer->Fit();
|
||||
m_contentSizer->Layout();
|
||||
// m_contentSizer->Fit();
|
||||
m_mainSizer->Layout();
|
||||
// m_mainSizer->Fit();
|
||||
Fit();
|
||||
}
|
||||
protected:
|
||||
void OnPaint(wxPaintEvent& event);
|
||||
private:
|
||||
|
||||
bool m_borderDashed;
|
||||
int m_borderWidth;
|
||||
int m_radius;
|
||||
wxColour m_borderColor;
|
||||
wxColour m_bgColor;
|
||||
bool m_isTop;
|
||||
|
||||
wxBoxSizer* m_mainSizer{nullptr};
|
||||
wxBoxSizer* m_contentSizer{nullptr};
|
||||
wxBoxSizer* m_splitSizer{nullptr};
|
||||
wxSizer* m_leftSizer{nullptr};
|
||||
wxSizer* m_rightSizer{nullptr};
|
||||
|
||||
static constexpr int labelHeight = 30;
|
||||
|
||||
};
|
||||
|
||||
|
||||
enum DataStatusType {
|
||||
ADJUST,
|
||||
OK,
|
||||
UNMATCHED
|
||||
};
|
||||
|
||||
class UiAMSSlot : public wxPanel
|
||||
{
|
||||
public:
|
||||
UiAMSSlot(wxWindow* parent,
|
||||
const std::vector<wxColour>& bgColours,
|
||||
const wxString& text,
|
||||
DataStatusType status,
|
||||
wxWindowID id = wxID_ANY,
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize,
|
||||
double colourFactor = 1.0,
|
||||
double scaleFactor = 1.0);
|
||||
|
||||
|
||||
private:
|
||||
void OnPaint(wxPaintEvent&);
|
||||
void DrawRectangle(wxPaintDC& dc, const wxSize& cli);
|
||||
void DrawLine(wxPaintDC& dc, const wxSize& cli);
|
||||
wxColour LightenColour(const wxColour& original);
|
||||
bool IsDark(const wxColour& c)
|
||||
{
|
||||
int brightness = (c.Red() * 299 + c.Green() * 587 + c.Blue() * 114) / 1000;
|
||||
return brightness < 128; // 0-255 range, 128 mid
|
||||
}
|
||||
private:
|
||||
// Copied, not referenced: the slot repaints long after construction, so it must not
|
||||
// depend on the caller's vector staying alive.
|
||||
std::vector<wxColour> m_bgColours;
|
||||
wxString m_text;
|
||||
DataStatusType m_status;
|
||||
wxSize m_size;
|
||||
ScalableBitmap *m_ams_slot_readonly{nullptr};
|
||||
int m_rectangleW = 44;
|
||||
int m_rectangleH = 62;
|
||||
int rectangleW = 44;
|
||||
int rectangleH = 62;
|
||||
double m_colourFactor = 1.0f;
|
||||
double m_scaleFactor = 1.0f;
|
||||
// wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
struct DataAmsSlotInfo
|
||||
{
|
||||
wxString amsName;
|
||||
wxString name;
|
||||
std::vector<wxColour> colours;
|
||||
double colourFactor;
|
||||
double scaleFactor;
|
||||
DataStatusType status;
|
||||
};
|
||||
|
||||
class UiAMS : public UiStyledAMSPanel
|
||||
{
|
||||
public:
|
||||
UiAMS(wxWindow* parent,
|
||||
const std::vector<DataAmsSlotInfo>& amsInfo,
|
||||
wxWindowID id,
|
||||
const wxPoint& pos,
|
||||
const wxSize& minSize);
|
||||
private:
|
||||
void init();
|
||||
std::vector<DataAmsSlotInfo> m_amsInfo;
|
||||
wxString m_amsTitle;
|
||||
wxSize m_minSize;
|
||||
};
|
||||
|
||||
|
||||
struct DataStatusParam {
|
||||
// int count = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
std::vector<DataAmsSlotInfo> slots;
|
||||
};
|
||||
|
||||
class ReselectMachineDialog : public wxDialog
|
||||
{
|
||||
public:
|
||||
ReselectMachineDialog(wxWindow* parent);
|
||||
~ReselectMachineDialog();
|
||||
void Update(MachineObject* obj,
|
||||
const std::map<int, int>& best_pos_map,
|
||||
const std::vector<FilamentInfo>& ams_mapping,
|
||||
wxString save_time);
|
||||
|
||||
private:
|
||||
int CaculateSwitcherDistribution(MachineObject* obj, const std::map<int, int>& best_pos_map, const std::vector<FilamentInfo>& ams_mapping);
|
||||
wxString getTrayID(MachineObject* obj, const std::string& amsID, const std::string& slotID);
|
||||
void OnRefreshButton(wxCommandEvent& event);
|
||||
|
||||
private:
|
||||
int saveTimes{0};
|
||||
wxBoxSizer* mainSizer{nullptr};
|
||||
wxPanel* textPanel{nullptr};
|
||||
wxBoxSizer* textSizer{nullptr};
|
||||
Label* suggestText{nullptr};
|
||||
// wxHyperlinkCtrl* linkwiki{nullptr};
|
||||
Label* linkwiki{nullptr};
|
||||
Label* summaryText{nullptr};
|
||||
UiStyledSwitchPanel* filamentSwitch{nullptr};
|
||||
wxStaticText* filamentTips{nullptr};
|
||||
std::vector<std::vector<DataAmsSlotInfo>> inAAMS{};
|
||||
std::vector<std::vector<DataAmsSlotInfo>> inBAMS{};
|
||||
wxPanel* statusBar{nullptr};
|
||||
wxBoxSizer* btnSizer{nullptr};
|
||||
Button* m_buttonClose{nullptr };
|
||||
Button* m_buttonRefresh{ nullptr };
|
||||
std::vector<wxColour>colourAdjust{wxColour("#675AFF")};
|
||||
std::vector<wxColour>colourOK{wxColour("#FF8181")};
|
||||
std::vector<wxColour>colourUnused{wxColour("#FF818140")};
|
||||
// wxStaticBitmap* m_bitmapSelectMachine{nullptr};
|
||||
};
|
||||
|
||||
wxDECLARE_EVENT(wxEVT_REFRESH_DATA, wxCommandEvent);
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -23,16 +23,16 @@ uiAmsPercentHumidityDryPopup::uiAmsPercentHumidityDryPopup(wxWindow *parent)
|
||||
: wxDialog(parent, wxID_ANY, "")
|
||||
{
|
||||
Create();
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
wxGetApp().UpdateDlgDarkUI(this); // Orca: apply dark-mode theming to the popup
|
||||
}
|
||||
|
||||
void uiAmsPercentHumidityDryPopup::Create()
|
||||
{
|
||||
// create images
|
||||
idle_img = ScalableBitmap(this, "ams_drying", 16);
|
||||
drying_img = ScalableBitmap(this, "ams_is_drying", 16);
|
||||
drying_img = ScalableBitmap(this, "ams_is_drying", 16); // Orca: use the drying-state icon
|
||||
|
||||
// background
|
||||
// background
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
|
||||
// create title sizer
|
||||
@@ -147,7 +147,8 @@ void uiAmsPercentHumidityDryPopup::UpdateContents()
|
||||
// table grid
|
||||
const wxString& humidity_str = wxString::Format("%d%%", m_humidity_percent);
|
||||
m_humidity_label->SetLabel(humidity_str);
|
||||
const wxString& temp_str = wxString::Format(wxString::FromUTF8(u8"%d\u2103" /* °C */), (int)std::round(m_current_temperature));
|
||||
// Orca: format the temperature with an explicit UTF-8 \u2103 (no translation of a units-only format)
|
||||
const wxString& temp_str = wxString::Format(wxString::FromUTF8(u8"%d\u2103" /* \u00b0C */), (int)std::round(m_current_temperature));
|
||||
m_temperature_label->SetLabel(temp_str);
|
||||
|
||||
if (m_left_dry_time > 0)
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Slic3r { namespace GUI {
|
||||
struct uiAmsHumidityInfo
|
||||
{
|
||||
std::string ams_id;
|
||||
AMSModel ams_type;
|
||||
AMSModel ams_type; // Orca: GUI-layer AMS enum; consumer (AMSItem.cpp) assigns an AMSModel
|
||||
int humidity_display_idx = -1;
|
||||
int humidity_percent = -1;
|
||||
float current_temperature;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/wxExtensions.hpp"
|
||||
#include "slic3r/GUI/Widgets/Label.hpp"
|
||||
#include "slic3r/GUI/Widgets/Label.hpp" // Orca: explicit Label include
|
||||
|
||||
#include <wx/stattext.h>
|
||||
|
||||
@@ -81,7 +81,6 @@ void uiDeviceUpdateVersion::CreateWidgets()
|
||||
serial_text->SetFont(font);
|
||||
version_text->SetFont(font);
|
||||
model_text->SetFont(font);
|
||||
|
||||
// The grid sizer
|
||||
wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(0, 2, 0, 0);
|
||||
//grid_sizer->AddGrowableCol(1);
|
||||
@@ -103,10 +102,10 @@ void uiDeviceUpdateVersion::CreateWidgets()
|
||||
|
||||
grid_sizer->Add(version_hsizer, 0, wxEXPAND, 0);
|
||||
grid_sizer->Add(m_dev_version, 0, wxEXPAND | wxALL, FromDIP(5));
|
||||
|
||||
|
||||
// Updating
|
||||
wxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
main_sizer->AddSpacer(FromDIP(40));
|
||||
main_sizer->AddSpacer(FromDIP(40)); // Orca: top spacing above the firmware-info grid
|
||||
main_sizer->Add(grid_sizer, 0, wxALIGN_LEFT, FromDIP(5));
|
||||
|
||||
SetSizer(main_sizer);
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
//**********************************************************/
|
||||
/* File: wgtDeviceNozzleRack.cpp
|
||||
* Description: The Device-tab panel with the toolhead nozzle and the H2C induction hotend rack.
|
||||
* Description: The panel with rack info
|
||||
*
|
||||
* \n class wgtDeviceNozzleRack;
|
||||
* \n class wgtDeviceNozzleRackToolHead;
|
||||
* \n class wgtDeviceNozzleRackArea;
|
||||
* \n class wgtDeviceNozzleRackNozzleItem;
|
||||
* \n class wgtDeviceNozzleRackToolHead;
|
||||
* \n class wgtDeviceNozzleRackPos;
|
||||
*
|
||||
* The wgtDeviceNozzleRackNozzleItem tile and the EVT_NOZZLE_RACK_NOZZLE_ITEM_SELECTED event live in
|
||||
* wgtDeviceNozzleRackNozzleItem.{h,cpp}, so they are not redefined here.
|
||||
//**********************************************************/
|
||||
|
||||
#include "wgtDeviceNozzleRack.h"
|
||||
@@ -17,29 +14,41 @@
|
||||
#include "slic3r/GUI/DeviceCore/DevNozzleSystem.h"
|
||||
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/MainFrame.hpp"
|
||||
#include "slic3r/GUI/MsgDialog.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/wxExtensions.hpp"
|
||||
|
||||
#include "slic3r/GUI/Widgets/Button.hpp"
|
||||
#include "slic3r/GUI/Widgets/Label.hpp"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
#define WX_DIP_SIZE_18 wxSize(FromDIP(18), FromDIP(18))
|
||||
#define WX_DIP_SIZE_46 wxSize(FromDIP(46), FromDIP(46))
|
||||
#define WX_DIP_SIZE(x, y) wxSize(FromDIP(x), FromDIP(y))
|
||||
|
||||
#define WGT_RACK_NOZZLE_SIZE WX_DIP_SIZE(88, 100)
|
||||
|
||||
#define L_RAW_A_STR _L("Row A")
|
||||
#define L_RAW_B_STR _L("Row B")
|
||||
|
||||
static wxColour s_gray_clr("#B0B0B0");
|
||||
static wxColour s_hgreen_clr("#009688"); // Orca: accent green
|
||||
static wxColour s_red_clr("#D01B1B");
|
||||
|
||||
// Orca: StateColor lacks these grey constants, so mirror the values here.
|
||||
static const wxColour WGT_GREY200 = wxColour(248, 248, 248);
|
||||
static const wxColour WGT_GREY300 = wxColour(238, 238, 238);
|
||||
|
||||
static wxColour s_hgreen_clr("#009688");
|
||||
static std::vector<int> a_nozzle_seq = { 0, 2, 4, 1, 3, 5 };
|
||||
static std::vector<int> b_nozzle_seq = { 1, 3, 5, 0, 2, 4 };
|
||||
|
||||
wxDEFINE_EVENT(EVT_NOZZLE_RACK_NOZZLE_ITEM_SELECTED, wxCommandEvent);
|
||||
|
||||
namespace Slic3r::GUI
|
||||
{
|
||||
|
||||
// Tints the "yellow" template pixels of a nozzle bitmap with the loaded filament colour.
|
||||
// Duplicates the file-local helper of the same purpose in wgtDeviceNozzleRackNozzleItem.cpp.
|
||||
static wxBitmap SetNozzleBmpColor(const wxBitmap& bmp, const std::string& color_str) {
|
||||
if(color_str.empty()) return bmp;
|
||||
|
||||
@@ -353,6 +362,36 @@ StaticBox* wgtDeviceNozzleRackArea::CreateNozzleBox(const std::vector<int> nozzl
|
||||
return nozzle_box;
|
||||
}
|
||||
|
||||
static void s_update_title(const std::shared_ptr<DevNozzleRack> rack, wgtDeviceNozzleRackTitle* title_label)
|
||||
{
|
||||
wxString title = _L("Induction Hotend Rack");
|
||||
if (rack && (rack->GetReadingCount() > 0))
|
||||
{
|
||||
wxString pending = ": " + _L("Reading") + wxString::Format(" %d/%d", rack->GetReadingIdx(), rack->GetReadingCount());
|
||||
title += pending;
|
||||
}
|
||||
|
||||
title_label->SetLabel(title);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void s_update_readall_btn(const std::shared_ptr<DevNozzleRack> rack, Button* btn)
|
||||
{
|
||||
wxString label = _L("Read All");
|
||||
if (rack && rack->HasUnreliableNozzles())
|
||||
{
|
||||
label += "*";
|
||||
}
|
||||
|
||||
if (btn->GetLabel() != label)
|
||||
{
|
||||
btn->SetLabel(label);
|
||||
}
|
||||
|
||||
btn->Enable(rack->CtrlCanReadAll());
|
||||
};
|
||||
#endif
|
||||
|
||||
void wgtDeviceNozzleRackArea::UpdateNozzleItems(const std::unordered_map<int, wgtDeviceNozzleRackNozzleItem*>& nozzle_items,
|
||||
std::shared_ptr<DevNozzleRack> nozzle_rack)
|
||||
{
|
||||
@@ -409,6 +448,7 @@ void wgtDeviceNozzleRackArea::UpdateRackInfo(std::weak_ptr<DevNozzleRack> rack)
|
||||
const auto& nozzle_rack = rack.lock();
|
||||
if (nozzle_rack)
|
||||
{
|
||||
// s_update_title(nozzle_rack, m_title_nozzle_rack);
|
||||
UpdateNozzleItems(m_nozzle_items, nozzle_rack);
|
||||
m_rack_pos_panel->UpdateRackPos(nozzle_rack);
|
||||
m_btn_read_all->Enable(nozzle_rack->CtrlCanReadAll());
|
||||
@@ -571,7 +611,7 @@ static void s_show_label(Label* label, const wxString& text, const wxColour& tex
|
||||
void wgtDeviceNozzleRackPos::UpdateRackPos(DevNozzleRack::RackPos new_pos,
|
||||
DevNozzleRack::RackStatus new_status, bool is_reading)
|
||||
{
|
||||
// While reading, both rows show a "Running..." status and the move buttons are hidden.
|
||||
/* di*/
|
||||
if (is_reading)
|
||||
{
|
||||
s_show_label(m_label_rowup, L_RAW_A_STR, *wxBLACK);
|
||||
@@ -697,4 +737,298 @@ void wgtDeviceNozzleRackPos::Rescale()
|
||||
m_btn_homing->msw_rescale();
|
||||
}
|
||||
|
||||
};// end of namespace Slic3r::GUI
|
||||
wgtDeviceNozzleRackNozzleItem::wgtDeviceNozzleRackNozzleItem(wxWindow* parent, int nozzle_id)
|
||||
: StaticBox(parent, wxID_ANY), m_nozzle_id(nozzle_id)
|
||||
{
|
||||
CreateGui();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackNozzleItem::CreateGui()
|
||||
{
|
||||
// Background
|
||||
SetCornerRadius(FromDIP(5));
|
||||
SetBackgroundColor(*wxWHITE);
|
||||
|
||||
// Top H
|
||||
wxSizer *top_h_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
m_nozzle_label_id = new Label(this);
|
||||
m_nozzle_label_id->SetFont(Label::Body_12);
|
||||
m_nozzle_label_id->SetBackgroundColour(*wxWHITE);
|
||||
m_nozzle_label_id->SetLabel(wxString::Format("%d", m_nozzle_id + 1));
|
||||
|
||||
m_status = NOZZLE_STATUS::NOZZLE_EMPTY;
|
||||
m_nozzle_empty_image = new ScalableBitmap(this, "dev_rack_nozzle_empty", 46);
|
||||
m_nozzle_icon = new wxStaticBitmap(this, wxID_ANY, m_nozzle_empty_image->bmp(), wxDefaultPosition, WX_DIP_SIZE_46);
|
||||
m_nozzle_icon->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
m_nozzle_selected_bitmap = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap, wxDefaultPosition, WX_DIP_SIZE(20, 20));
|
||||
m_nozzle_selected_bitmap->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
top_h_sizer->Add(m_nozzle_label_id, 0, wxTOP | wxLEFT, FromDIP(6));
|
||||
top_h_sizer->AddStretchSpacer(1);
|
||||
top_h_sizer->Add(m_nozzle_icon, 0, wxTOP, FromDIP(10));
|
||||
top_h_sizer->AddStretchSpacer(1);
|
||||
top_h_sizer->Add(m_nozzle_selected_bitmap, 0, wxTOP | wxRIGHT, FromDIP(2));
|
||||
|
||||
// Bottom V
|
||||
wxBoxSizer* bottom_v = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxSizer* label_h_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_nozzle_label_1 = new Label(this);
|
||||
m_nozzle_label_1->SetFont(Label::Body_12);
|
||||
m_nozzle_label_1->SetBackgroundColour(*wxWHITE);
|
||||
m_nozzle_label_1->SetLabel(_L("Empty"));
|
||||
|
||||
label_h_sizer->Add(m_nozzle_label_1, 0, wxALIGN_LEFT);
|
||||
|
||||
auto status_icon = create_scaled_bitmap("dev_rack_nozzle_error_icon", this, 14);
|
||||
m_nozzle_status_icon = new wxStaticBitmap(this, wxID_ANY, status_icon, wxDefaultPosition, WX_DIP_SIZE(14, 14));
|
||||
m_nozzle_status_icon->Bind(wxEVT_LEFT_DOWN, &wgtDeviceNozzleRackNozzleItem::OnBtnNozzleStatus, this);
|
||||
m_nozzle_status_icon->Bind(wxEVT_ENTER_WINDOW, [this](auto&) { SetCursor(wxCURSOR_HAND); });
|
||||
m_nozzle_status_icon->Bind(wxEVT_LEAVE_WINDOW, [this](auto&) { SetCursor(wxCURSOR_ARROW); });
|
||||
m_nozzle_status_icon->SetBackgroundColour(*wxWHITE);
|
||||
m_nozzle_status_icon->Show(false);
|
||||
|
||||
label_h_sizer->Add(m_nozzle_status_icon, 0, wxALIGN_CENTER | wxLEFT, FromDIP(2));
|
||||
bottom_v->Add(label_h_sizer, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, FromDIP(2));
|
||||
|
||||
m_nozzle_label_2 = new Label(this);
|
||||
m_nozzle_label_2->SetFont(Label::Body_12);
|
||||
m_nozzle_label_2->SetBackgroundColour(*wxWHITE);
|
||||
bottom_v->Add(m_nozzle_label_2, 0, wxALIGN_CENTER_HORIZONTAL);
|
||||
|
||||
// Main sizer
|
||||
wxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
main_sizer->Add(top_h_sizer, 0, wxEXPAND);
|
||||
main_sizer->Add(bottom_v, 0, wxALIGN_CENTER_HORIZONTAL);
|
||||
SetSizer(main_sizer);
|
||||
|
||||
SetMinSize(WGT_RACK_NOZZLE_SIZE);
|
||||
SetMaxSize(WGT_RACK_NOZZLE_SIZE);
|
||||
SetSize(WGT_RACK_NOZZLE_SIZE);
|
||||
Layout();
|
||||
};
|
||||
|
||||
void wgtDeviceNozzleRackNozzleItem::SetSelected(bool selected)
|
||||
{
|
||||
if (!m_enable_select){
|
||||
assert(false && "not support select");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_is_selected != selected) {
|
||||
m_is_selected = selected;
|
||||
if (selected) {
|
||||
if (!m_nozzle_selected_image) {
|
||||
m_nozzle_selected_image = new ScalableBitmap(this, "dev_rack_nozzle_selected", 20);
|
||||
}
|
||||
|
||||
m_nozzle_selected_bitmap->SetBitmap(m_nozzle_selected_image->bmp());
|
||||
SetBorderColor(StateColor::darkModeColorFor(s_hgreen_clr));
|
||||
} else {
|
||||
m_nozzle_selected_bitmap->SetBitmap(wxNullBitmap);
|
||||
SetBorderColor(StateColor::darkModeColorFor(s_gray_clr));
|
||||
}
|
||||
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackNozzleItem::Update(const std::shared_ptr<DevNozzleRack> rack, bool on_rack /*= true*/)
|
||||
{
|
||||
m_rack = rack;
|
||||
|
||||
if (rack) {
|
||||
const auto &nozzle_info = on_rack ? rack->GetNozzle(m_nozzle_id) : rack->GetNozzleSystem()->GetExtNozzle(m_nozzle_id);
|
||||
const wxString &diameter_str = nozzle_info.GetNozzleDiameterStr();
|
||||
const wxString &flowtype_str = nozzle_info.GetNozzleFlowTypeStr();
|
||||
const std::string &color = nozzle_info.GetFilamentColor();
|
||||
|
||||
/*check empty first*/
|
||||
if (nozzle_info.IsEmpty()) {
|
||||
SetNozzleStatus(NOZZLE_STATUS::NOZZLE_EMPTY, _L("Empty"), wxEmptyString, color);
|
||||
} else if (nozzle_info.IsNormal()) {
|
||||
SetNozzleStatus(NOZZLE_STATUS::NOZZLE_NORMAL, diameter_str, flowtype_str, color);
|
||||
} else if (nozzle_info.IsAbnormal()) {
|
||||
SetNozzleStatus(NOZZLE_STATUS::NOZZLE_ERROR, _L("Error"), wxEmptyString, color);
|
||||
} else if (nozzle_info.IsUnknown()) {
|
||||
SetNozzleStatus(NOZZLE_STATUS::NOZZLE_UNKNOWN, _L("Unknown"), wxEmptyString, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackNozzleItem::SetNozzleStatus(NOZZLE_STATUS status, const wxString& str1, const wxString& str2, const std::string& color)
|
||||
{
|
||||
if (m_status != status || m_filament_color != color)
|
||||
{
|
||||
m_status = status;
|
||||
m_filament_color = color;
|
||||
switch (status)
|
||||
{
|
||||
case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_EMPTY:
|
||||
{
|
||||
if (!m_nozzle_empty_image) { m_nozzle_empty_image = new ScalableBitmap(this, "dev_rack_nozzle_empty", 46);}
|
||||
m_nozzle_icon->SetBitmap(m_nozzle_empty_image->bmp());
|
||||
break;
|
||||
}
|
||||
case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_NORMAL:
|
||||
{
|
||||
if (!m_nozzle_normal_image) { m_nozzle_normal_image = new ScalableBitmap(this, "dev_rack_nozzle_normal", 46);}
|
||||
m_nozzle_icon->SetBitmap(SetNozzleBmpColor(m_nozzle_normal_image->bmp(), m_filament_color));
|
||||
break;
|
||||
}
|
||||
case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_UNKNOWN:
|
||||
{
|
||||
if (!m_nozzle_unknown_image) { m_nozzle_unknown_image = new ScalableBitmap(this, "dev_rack_nozzle_unknown", 46);}
|
||||
m_nozzle_icon->SetBitmap(m_nozzle_unknown_image->bmp());
|
||||
break;
|
||||
}
|
||||
case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_ERROR:
|
||||
{
|
||||
if (!m_nozzle_error_image) { m_nozzle_error_image = new ScalableBitmap(this, "dev_rack_nozzle_error", 46);}
|
||||
m_nozzle_icon->SetBitmap(m_nozzle_error_image->bmp());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (status == wgtDeviceNozzleRackNozzleItem::NOZZLE_ERROR)
|
||||
{
|
||||
m_nozzle_label_1->SetForegroundColour(StateColor::darkModeColorFor(s_red_clr));
|
||||
m_nozzle_status_icon->Show(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_nozzle_label_1->SetForegroundColour(StateColor::darkModeColorFor(*wxBLACK));
|
||||
m_nozzle_status_icon->Show(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool update_layout = (m_nozzle_label_1->GetLabel() != str1 || m_nozzle_label_2->GetLabel() != str2);
|
||||
m_nozzle_label_1->SetLabel(str1);
|
||||
m_nozzle_label_2->SetLabel(str2);
|
||||
|
||||
if (update_layout) {
|
||||
Layout();
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackNozzleItem::OnBtnNozzleStatus(wxMouseEvent& evt)
|
||||
{
|
||||
if (m_is_disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto rack = m_rack.lock();
|
||||
if (rack && m_status == wgtDeviceNozzleRackNozzleItem::NOZZLE_ERROR)
|
||||
{
|
||||
MessageDialog dlg(nullptr, _L("The hotend is in an abnormal state and currently unavailable. "
|
||||
"Please go to 'Device -> Upgrade' to upgrade firmware."), _L("Abnormal Hotend"), wxICON_WARNING);
|
||||
dlg.AddButton(wxID_CANCEL, _L("Cancel"), false);
|
||||
dlg.AddButton(wxID_OK,_L("Jump to the upgrade page"), true);
|
||||
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
wxGetApp().mainframe->m_monitor->jump_to_Upgrade();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackNozzleItem::Rescale()
|
||||
{
|
||||
if (m_nozzle_normal_image) { m_nozzle_normal_image->msw_rescale(); }
|
||||
if (m_nozzle_empty_image) { m_nozzle_empty_image->msw_rescale(); }
|
||||
if (m_nozzle_unknown_image) { m_nozzle_unknown_image->msw_rescale(); }
|
||||
if (m_nozzle_error_image) { m_nozzle_error_image->msw_rescale(); }
|
||||
|
||||
auto status_icon = create_scaled_bitmap("dev_rack_nozzle_error_icon", this, 14);
|
||||
m_nozzle_status_icon->SetBitmap(status_icon);
|
||||
m_nozzle_status_icon->Refresh();
|
||||
|
||||
if (m_nozzle_selected_image) {
|
||||
m_nozzle_selected_image->msw_rescale();
|
||||
if (m_is_selected) {
|
||||
m_nozzle_selected_bitmap->SetBitmap(m_nozzle_selected_image->bmp());
|
||||
}
|
||||
};
|
||||
|
||||
switch (m_status)
|
||||
{
|
||||
case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_EMPTY:
|
||||
{
|
||||
m_nozzle_icon->SetBitmap(m_nozzle_empty_image->bmp());
|
||||
break;
|
||||
}
|
||||
case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_NORMAL:
|
||||
{
|
||||
m_nozzle_icon->SetBitmap(SetNozzleBmpColor(m_nozzle_normal_image->bmp(), m_filament_color));
|
||||
break;
|
||||
}
|
||||
case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_UNKNOWN:
|
||||
{
|
||||
m_nozzle_icon->SetBitmap(m_nozzle_unknown_image->bmp());
|
||||
break;
|
||||
}
|
||||
case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_ERROR:
|
||||
{
|
||||
m_nozzle_icon->SetBitmap(m_nozzle_error_image->bmp());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
void wgtDeviceNozzleRackNozzleItem::EnableSelect()
|
||||
{
|
||||
if (m_enable_select == true) {
|
||||
return;
|
||||
};
|
||||
|
||||
m_enable_select = true;
|
||||
m_nozzle_icon->Bind(wxEVT_LEFT_DOWN, [this](auto& evt) { OnItemSelected(evt); });
|
||||
m_nozzle_label_id->Bind(wxEVT_LEFT_DOWN, [this](auto& evt) { OnItemSelected(evt); });
|
||||
m_nozzle_label_1->Bind(wxEVT_LEFT_DOWN, [this](auto& evt) { OnItemSelected(evt); });
|
||||
m_nozzle_label_2->Bind(wxEVT_LEFT_DOWN, [this](auto& evt) { OnItemSelected(evt); });
|
||||
Bind(wxEVT_LEFT_DOWN, [this](auto& evt) { OnItemSelected(evt); });
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackNozzleItem::OnItemSelected(wxMouseEvent& evt)
|
||||
{
|
||||
if (m_enable_select && !m_is_disabled){
|
||||
SetSelected(true);
|
||||
wxCommandEvent command_evt(EVT_NOZZLE_RACK_NOZZLE_ITEM_SELECTED, GetId());
|
||||
command_evt.SetEventObject(this);
|
||||
ProcessEvent(command_evt);
|
||||
}
|
||||
|
||||
evt.Skip();
|
||||
}
|
||||
|
||||
|
||||
void wgtDeviceNozzleRackNozzleItem::SetDisable(bool disabled)
|
||||
{
|
||||
if (m_is_disabled == disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_is_disabled = disabled;
|
||||
|
||||
auto bg_clr = disabled ? StateColor::darkModeColorFor("#E5E7EB") : StateColor::darkModeColorFor(*wxWHITE);
|
||||
m_nozzle_icon->SetBackgroundColour(bg_clr);
|
||||
m_nozzle_label_id->SetBackgroundColour(bg_clr);
|
||||
m_nozzle_label_1->SetBackgroundColour(bg_clr);
|
||||
m_nozzle_status_icon->SetBackgroundColour(bg_clr);
|
||||
m_nozzle_label_2->SetBackgroundColour(bg_clr);
|
||||
m_nozzle_selected_bitmap->SetBackgroundColour(bg_clr);
|
||||
|
||||
SetBackgroundColor(bg_clr);
|
||||
Refresh();
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
@@ -1,31 +1,22 @@
|
||||
//**********************************************************/
|
||||
/* File: wgtDeviceNozzleRack.h
|
||||
* Description: The Device-tab panel with the toolhead nozzle and the H2C induction hotend rack.
|
||||
* Description: The panel with rack and nozzles
|
||||
*
|
||||
* \n class wgtDeviceNozzleRack; // toolhead panel + rack area, side by side
|
||||
* \n class wgtDeviceNozzleRackToolHead;
|
||||
* \n class wgtDeviceNozzleRackArea;
|
||||
* \n class wgtDeviceNozzleRackNozzleItem;
|
||||
* \n class wgtDeviceNozzleRackToolHead;
|
||||
* \n class wgtDeviceNozzleRackPos;
|
||||
*
|
||||
* The single-nozzle tile class wgtDeviceNozzleRackNozzleItem lives in its own translation unit
|
||||
* (wgtDeviceNozzleRackNozzleItem.{h,cpp}), so it is included here rather than redeclared.
|
||||
* The Hotends-Info upgrade dialog (wgtDeviceNozzleRackUpgradeDlg) is intentionally not wired yet
|
||||
* (see the TODO markers in the .cpp).
|
||||
//**********************************************************/
|
||||
|
||||
#pragma once
|
||||
#include "slic3r/GUI/DeviceCore/DevNozzleRack.h"
|
||||
|
||||
#include "wgtDeviceNozzleRackNozzleItem.h"
|
||||
|
||||
#include "slic3r/GUI/Widgets/StaticBox.hpp"
|
||||
#include "slic3r/GUI/Widgets/AnimaController.hpp"
|
||||
|
||||
#include <wx/panel.h>
|
||||
#include <wx/simplebook.h>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
// Previous definitions
|
||||
class Button;
|
||||
@@ -47,6 +38,9 @@ namespace GUI
|
||||
}
|
||||
};
|
||||
|
||||
// Events
|
||||
wxDECLARE_EVENT(EVT_NOZZLE_RACK_NOZZLE_ITEM_SELECTED, wxCommandEvent);
|
||||
|
||||
namespace Slic3r::GUI
|
||||
{
|
||||
class wgtDeviceNozzleRack : public wxPanel
|
||||
@@ -148,8 +142,6 @@ private:
|
||||
Label* m_progress_refresh{ nullptr };
|
||||
AnimaIcon* m_refresh_icon{ nullptr };
|
||||
|
||||
// "Hotends Info" upgrade dialog. Owned for its ShowModal lifetime by
|
||||
// OnBtnHotendsInfos; UpdateRackInfo forwards live device pushes to it while it is shown.
|
||||
wgtDeviceNozzleRackUpgradeDlg* m_rack_upgrade_dlg = nullptr;
|
||||
};
|
||||
|
||||
@@ -193,4 +185,71 @@ private:
|
||||
ScalableButton* m_btn_homing{ nullptr };
|
||||
};
|
||||
|
||||
};// end of namespace Slic3r::GUI
|
||||
class wgtDeviceNozzleRackNozzleItem : public StaticBox
|
||||
{
|
||||
public:
|
||||
enum NOZZLE_STATUS
|
||||
{
|
||||
NOZZLE_EMPTY,
|
||||
NOZZLE_NORMAL,
|
||||
NOZZLE_UNKNOWN,
|
||||
NOZZLE_ERROR
|
||||
};
|
||||
|
||||
public:
|
||||
wgtDeviceNozzleRackNozzleItem(wxWindow* parent, int nozzle_id);
|
||||
|
||||
public:
|
||||
void Update(const std::shared_ptr<DevNozzleRack> rack, bool on_rack = true); // on_rack is false means extruder nozzle
|
||||
|
||||
int GetNozzleId() const { return m_nozzle_id; }
|
||||
void SetDisplayIdText(const wxString& text) { m_nozzle_label_id->SetLabel(text);};
|
||||
|
||||
void EnableSelect();;
|
||||
void SetSelected(bool selected);
|
||||
bool IsSelected() const { return m_is_selected; }
|
||||
|
||||
bool IsDisabled() const { return m_is_disabled; }
|
||||
void SetDisable(bool disabled);
|
||||
|
||||
void Rescale();
|
||||
|
||||
private:
|
||||
void CreateGui();
|
||||
|
||||
void SetNozzleStatus(NOZZLE_STATUS status, const wxString& str1, const wxString& str2, const std::string& color);
|
||||
|
||||
void OnBtnNozzleStatus(wxMouseEvent& evt);
|
||||
void OnItemSelected(wxMouseEvent& evt);
|
||||
|
||||
private:
|
||||
std::weak_ptr<DevNozzleRack> m_rack;
|
||||
|
||||
int m_nozzle_id; // internal id, from 0 to 5
|
||||
std::string m_filament_color;
|
||||
NOZZLE_STATUS m_status = NOZZLE_STATUS::NOZZLE_EMPTY;
|
||||
|
||||
// select
|
||||
bool m_is_selected = false;
|
||||
bool m_enable_select = false;
|
||||
ScalableBitmap* m_nozzle_selected_image{ nullptr };
|
||||
wxStaticBitmap* m_nozzle_selected_bitmap{ nullptr };
|
||||
|
||||
// enable or disable
|
||||
bool m_is_disabled = false;
|
||||
|
||||
// Images
|
||||
ScalableBitmap* m_nozzle_normal_image{ nullptr };
|
||||
ScalableBitmap* m_nozzle_empty_image{ nullptr };
|
||||
ScalableBitmap* m_nozzle_unknown_image{ nullptr };
|
||||
ScalableBitmap* m_nozzle_error_image{ nullptr };
|
||||
|
||||
// GUI
|
||||
wxStaticBitmap* m_nozzle_icon{ nullptr };
|
||||
Label* m_nozzle_label_id { nullptr };
|
||||
Label* m_nozzle_label_1{ nullptr };
|
||||
wxStaticBitmap* m_nozzle_status_icon = nullptr;
|
||||
Label* m_nozzle_label_2{ nullptr };
|
||||
};
|
||||
|
||||
};// end of namespace Slic3r::GUI
|
||||
@@ -1,347 +0,0 @@
|
||||
//**********************************************************/
|
||||
/* File: wgtDeviceNozzleRackNozzleItem.cpp
|
||||
* Description: One nozzle cell of the H2C hotend rack view. Holds the
|
||||
* wgtDeviceNozzleRackNozzleItem widget, the SetNozzleBmpColor helper and the layout
|
||||
* constants it needs.
|
||||
//**********************************************************/
|
||||
|
||||
#include "wgtDeviceNozzleRackNozzleItem.h"
|
||||
|
||||
#include "slic3r/GUI/DeviceCore/DevNozzleSystem.h"
|
||||
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/MsgDialog.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/wxExtensions.hpp"
|
||||
|
||||
#include "slic3r/GUI/Widgets/Label.hpp"
|
||||
|
||||
#define WX_DIP_SIZE_46 wxSize(FromDIP(46), FromDIP(46))
|
||||
#define WX_DIP_SIZE(x, y) wxSize(FromDIP(x), FromDIP(y))
|
||||
|
||||
#define WGT_RACK_NOZZLE_SIZE WX_DIP_SIZE(88, 100)
|
||||
|
||||
static wxColour s_gray_clr("#B0B0B0");
|
||||
static wxColour s_hgreen_clr("#009688");
|
||||
static wxColour s_red_clr("#D01B1B");
|
||||
|
||||
wxDEFINE_EVENT(EVT_NOZZLE_RACK_NOZZLE_ITEM_SELECTED, wxCommandEvent);
|
||||
|
||||
namespace Slic3r::GUI
|
||||
{
|
||||
|
||||
static wxBitmap SetNozzleBmpColor(const wxBitmap& bmp, const std::string& color_str) {
|
||||
if(color_str.empty()) return bmp;
|
||||
|
||||
wxImage img = bmp.ConvertToImage();
|
||||
wxColour color("#" + color_str);
|
||||
|
||||
for (int y = 0; y < img.GetHeight(); ++y) {
|
||||
for (int x = 0; x < img.GetWidth(); ++x) {
|
||||
unsigned char r = img.GetRed(x, y);
|
||||
unsigned char g = img.GetGreen(x, y);
|
||||
unsigned char b = img.GetBlue(x, y);
|
||||
|
||||
/*replace yellow with color*/
|
||||
if ( r >= 180 && g >= 180 && b <= 150) {
|
||||
img.SetRGB(x, y, color.Red(), color.Green(), color.Blue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return wxBitmap(img, -1, bmp.GetScaleFactor());
|
||||
}
|
||||
|
||||
wgtDeviceNozzleRackNozzleItem::wgtDeviceNozzleRackNozzleItem(wxWindow* parent, int nozzle_id)
|
||||
: StaticBox(parent, wxID_ANY), m_nozzle_id(nozzle_id)
|
||||
{
|
||||
CreateGui();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackNozzleItem::CreateGui()
|
||||
{
|
||||
// Background
|
||||
SetCornerRadius(FromDIP(5));
|
||||
SetBackgroundColor(*wxWHITE);
|
||||
|
||||
// Top H
|
||||
wxSizer *top_h_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
m_nozzle_label_id = new Label(this);
|
||||
m_nozzle_label_id->SetFont(Label::Body_12);
|
||||
m_nozzle_label_id->SetBackgroundColour(*wxWHITE);
|
||||
m_nozzle_label_id->SetLabel(wxString::Format("%d", m_nozzle_id + 1));
|
||||
|
||||
m_status = NOZZLE_STATUS::NOZZLE_EMPTY;
|
||||
m_nozzle_empty_image = new ScalableBitmap(this, "dev_rack_nozzle_empty", 46);
|
||||
m_nozzle_icon = new wxStaticBitmap(this, wxID_ANY, m_nozzle_empty_image->bmp(), wxDefaultPosition, WX_DIP_SIZE_46);
|
||||
m_nozzle_icon->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
m_nozzle_selected_bitmap = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap, wxDefaultPosition, WX_DIP_SIZE(20, 20));
|
||||
m_nozzle_selected_bitmap->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
top_h_sizer->Add(m_nozzle_label_id, 0, wxTOP | wxLEFT, FromDIP(6));
|
||||
top_h_sizer->AddStretchSpacer(1);
|
||||
top_h_sizer->Add(m_nozzle_icon, 0, wxTOP, FromDIP(10));
|
||||
top_h_sizer->AddStretchSpacer(1);
|
||||
top_h_sizer->Add(m_nozzle_selected_bitmap, 0, wxTOP | wxRIGHT, FromDIP(2));
|
||||
|
||||
// Bottom V
|
||||
wxBoxSizer* bottom_v = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxSizer* label_h_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_nozzle_label_1 = new Label(this);
|
||||
m_nozzle_label_1->SetFont(Label::Body_12);
|
||||
m_nozzle_label_1->SetBackgroundColour(*wxWHITE);
|
||||
m_nozzle_label_1->SetLabel(_L("Empty"));
|
||||
|
||||
label_h_sizer->Add(m_nozzle_label_1, 0, wxALIGN_LEFT);
|
||||
|
||||
auto status_icon = create_scaled_bitmap("dev_rack_nozzle_error_icon", this, 14);
|
||||
m_nozzle_status_icon = new wxStaticBitmap(this, wxID_ANY, status_icon, wxDefaultPosition, WX_DIP_SIZE(14, 14));
|
||||
m_nozzle_status_icon->Bind(wxEVT_LEFT_DOWN, &wgtDeviceNozzleRackNozzleItem::OnBtnNozzleStatus, this);
|
||||
m_nozzle_status_icon->Bind(wxEVT_ENTER_WINDOW, [this](auto&) { SetCursor(wxCURSOR_HAND); });
|
||||
m_nozzle_status_icon->Bind(wxEVT_LEAVE_WINDOW, [this](auto&) { SetCursor(wxCURSOR_ARROW); });
|
||||
m_nozzle_status_icon->SetBackgroundColour(*wxWHITE);
|
||||
m_nozzle_status_icon->Show(false);
|
||||
|
||||
label_h_sizer->Add(m_nozzle_status_icon, 0, wxALIGN_CENTER | wxLEFT, FromDIP(2));
|
||||
bottom_v->Add(label_h_sizer, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, FromDIP(2));
|
||||
|
||||
m_nozzle_label_2 = new Label(this);
|
||||
m_nozzle_label_2->SetFont(Label::Body_12);
|
||||
m_nozzle_label_2->SetBackgroundColour(*wxWHITE);
|
||||
bottom_v->Add(m_nozzle_label_2, 0, wxALIGN_CENTER_HORIZONTAL);
|
||||
|
||||
// Main sizer
|
||||
wxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
main_sizer->Add(top_h_sizer, 0, wxEXPAND);
|
||||
main_sizer->Add(bottom_v, 0, wxALIGN_CENTER_HORIZONTAL);
|
||||
SetSizer(main_sizer);
|
||||
|
||||
SetMinSize(WGT_RACK_NOZZLE_SIZE);
|
||||
SetMaxSize(WGT_RACK_NOZZLE_SIZE);
|
||||
SetSize(WGT_RACK_NOZZLE_SIZE);
|
||||
Layout();
|
||||
};
|
||||
|
||||
void wgtDeviceNozzleRackNozzleItem::SetSelected(bool selected)
|
||||
{
|
||||
if (!m_enable_select){
|
||||
assert(false && "not support select");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_is_selected != selected) {
|
||||
m_is_selected = selected;
|
||||
if (selected) {
|
||||
if (!m_nozzle_selected_image) {
|
||||
m_nozzle_selected_image = new ScalableBitmap(this, "dev_rack_nozzle_selected", 20);
|
||||
}
|
||||
|
||||
m_nozzle_selected_bitmap->SetBitmap(m_nozzle_selected_image->bmp());
|
||||
SetBorderColor(StateColor::darkModeColorFor(s_hgreen_clr));
|
||||
} else {
|
||||
m_nozzle_selected_bitmap->SetBitmap(wxNullBitmap);
|
||||
SetBorderColor(StateColor::darkModeColorFor(s_gray_clr));
|
||||
}
|
||||
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackNozzleItem::Update(const std::shared_ptr<DevNozzleRack> rack, bool on_rack /*= true*/)
|
||||
{
|
||||
m_rack = rack;
|
||||
|
||||
if (rack) {
|
||||
const auto &nozzle_info = on_rack ? rack->GetNozzle(m_nozzle_id) : rack->GetNozzleSystem()->GetExtNozzle(m_nozzle_id);
|
||||
const wxString &diameter_str = nozzle_info.GetNozzleDiameterStr();
|
||||
const wxString &flowtype_str = nozzle_info.GetNozzleFlowTypeStr();
|
||||
const std::string &color = nozzle_info.GetFilamentColor();
|
||||
|
||||
/*check empty first*/
|
||||
if (nozzle_info.IsEmpty()) {
|
||||
SetNozzleStatus(NOZZLE_STATUS::NOZZLE_EMPTY, _L("Empty"), wxEmptyString, color);
|
||||
} else if (nozzle_info.IsNormal()) {
|
||||
SetNozzleStatus(NOZZLE_STATUS::NOZZLE_NORMAL, diameter_str, flowtype_str, color);
|
||||
} else if (nozzle_info.IsAbnormal()) {
|
||||
SetNozzleStatus(NOZZLE_STATUS::NOZZLE_ERROR, _L("Error"), wxEmptyString, color);
|
||||
} else if (nozzle_info.IsUnknown()) {
|
||||
SetNozzleStatus(NOZZLE_STATUS::NOZZLE_UNKNOWN, _L("Unknown"), wxEmptyString, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackNozzleItem::SetNozzleStatus(NOZZLE_STATUS status, const wxString& str1, const wxString& str2, const std::string& color)
|
||||
{
|
||||
if (m_status != status || m_filament_color != color)
|
||||
{
|
||||
m_status = status;
|
||||
m_filament_color = color;
|
||||
switch (status)
|
||||
{
|
||||
case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_EMPTY:
|
||||
{
|
||||
if (!m_nozzle_empty_image) { m_nozzle_empty_image = new ScalableBitmap(this, "dev_rack_nozzle_empty", 46);}
|
||||
m_nozzle_icon->SetBitmap(m_nozzle_empty_image->bmp());
|
||||
break;
|
||||
}
|
||||
case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_NORMAL:
|
||||
{
|
||||
if (!m_nozzle_normal_image) { m_nozzle_normal_image = new ScalableBitmap(this, "dev_rack_nozzle_normal", 46);}
|
||||
m_nozzle_icon->SetBitmap(SetNozzleBmpColor(m_nozzle_normal_image->bmp(), m_filament_color));
|
||||
break;
|
||||
}
|
||||
case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_UNKNOWN:
|
||||
{
|
||||
if (!m_nozzle_unknown_image) { m_nozzle_unknown_image = new ScalableBitmap(this, "dev_rack_nozzle_unknown", 46);}
|
||||
m_nozzle_icon->SetBitmap(m_nozzle_unknown_image->bmp());
|
||||
break;
|
||||
}
|
||||
case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_ERROR:
|
||||
{
|
||||
if (!m_nozzle_error_image) { m_nozzle_error_image = new ScalableBitmap(this, "dev_rack_nozzle_error", 46);}
|
||||
m_nozzle_icon->SetBitmap(m_nozzle_error_image->bmp());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (status == wgtDeviceNozzleRackNozzleItem::NOZZLE_ERROR)
|
||||
{
|
||||
m_nozzle_label_1->SetForegroundColour(StateColor::darkModeColorFor(s_red_clr));
|
||||
m_nozzle_status_icon->Show(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_nozzle_label_1->SetForegroundColour(StateColor::darkModeColorFor(*wxBLACK));
|
||||
m_nozzle_status_icon->Show(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool update_layout = (m_nozzle_label_1->GetLabel() != str1 || m_nozzle_label_2->GetLabel() != str2);
|
||||
m_nozzle_label_1->SetLabel(str1);
|
||||
m_nozzle_label_2->SetLabel(str2);
|
||||
|
||||
if (update_layout) {
|
||||
Layout();
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackNozzleItem::OnBtnNozzleStatus(wxMouseEvent& evt)
|
||||
{
|
||||
if (m_is_disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto rack = m_rack.lock();
|
||||
if (rack && m_status == wgtDeviceNozzleRackNozzleItem::NOZZLE_ERROR)
|
||||
{
|
||||
// Orca: show the abnormal-hotend warning as an informational dialog only, with no
|
||||
// "Jump to the upgrade page" button, since Orca's MessageDialog and device Upgrade UI
|
||||
// have no such entry point. Fires when tapping an error-state rack nozzle's status icon.
|
||||
MessageDialog dlg(nullptr, _L("The hotend is in an abnormal state and currently unavailable. "
|
||||
"Please go to 'Device -> Upgrade' to upgrade firmware."), _L("Abnormal Hotend"), wxICON_WARNING | wxOK);
|
||||
dlg.ShowModal();
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackNozzleItem::Rescale()
|
||||
{
|
||||
if (m_nozzle_normal_image) { m_nozzle_normal_image->msw_rescale(); }
|
||||
if (m_nozzle_empty_image) { m_nozzle_empty_image->msw_rescale(); }
|
||||
if (m_nozzle_unknown_image) { m_nozzle_unknown_image->msw_rescale(); }
|
||||
if (m_nozzle_error_image) { m_nozzle_error_image->msw_rescale(); }
|
||||
|
||||
auto status_icon = create_scaled_bitmap("dev_rack_nozzle_error_icon", this, 14);
|
||||
m_nozzle_status_icon->SetBitmap(status_icon);
|
||||
m_nozzle_status_icon->Refresh();
|
||||
|
||||
if (m_nozzle_selected_image) {
|
||||
m_nozzle_selected_image->msw_rescale();
|
||||
if (m_is_selected) {
|
||||
m_nozzle_selected_bitmap->SetBitmap(m_nozzle_selected_image->bmp());
|
||||
}
|
||||
};
|
||||
|
||||
switch (m_status)
|
||||
{
|
||||
case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_EMPTY:
|
||||
{
|
||||
m_nozzle_icon->SetBitmap(m_nozzle_empty_image->bmp());
|
||||
break;
|
||||
}
|
||||
case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_NORMAL:
|
||||
{
|
||||
m_nozzle_icon->SetBitmap(SetNozzleBmpColor(m_nozzle_normal_image->bmp(), m_filament_color));
|
||||
break;
|
||||
}
|
||||
case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_UNKNOWN:
|
||||
{
|
||||
m_nozzle_icon->SetBitmap(m_nozzle_unknown_image->bmp());
|
||||
break;
|
||||
}
|
||||
case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_ERROR:
|
||||
{
|
||||
m_nozzle_icon->SetBitmap(m_nozzle_error_image->bmp());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
void wgtDeviceNozzleRackNozzleItem::EnableSelect()
|
||||
{
|
||||
if (m_enable_select == true) {
|
||||
return;
|
||||
};
|
||||
|
||||
m_enable_select = true;
|
||||
m_nozzle_icon->Bind(wxEVT_LEFT_DOWN, [this](auto& evt) { OnItemSelected(evt); });
|
||||
m_nozzle_label_id->Bind(wxEVT_LEFT_DOWN, [this](auto& evt) { OnItemSelected(evt); });
|
||||
m_nozzle_label_1->Bind(wxEVT_LEFT_DOWN, [this](auto& evt) { OnItemSelected(evt); });
|
||||
m_nozzle_label_2->Bind(wxEVT_LEFT_DOWN, [this](auto& evt) { OnItemSelected(evt); });
|
||||
Bind(wxEVT_LEFT_DOWN, [this](auto& evt) { OnItemSelected(evt); });
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackNozzleItem::OnItemSelected(wxMouseEvent& evt)
|
||||
{
|
||||
if (m_enable_select && !m_is_disabled){
|
||||
SetSelected(true);
|
||||
wxCommandEvent command_evt(EVT_NOZZLE_RACK_NOZZLE_ITEM_SELECTED, GetId());
|
||||
command_evt.SetEventObject(this);
|
||||
ProcessEvent(command_evt);
|
||||
}
|
||||
|
||||
evt.Skip();
|
||||
}
|
||||
|
||||
|
||||
void wgtDeviceNozzleRackNozzleItem::SetDisable(bool disabled)
|
||||
{
|
||||
if (m_is_disabled == disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_is_disabled = disabled;
|
||||
|
||||
auto bg_clr = disabled ? StateColor::darkModeColorFor("#E5E7EB") : StateColor::darkModeColorFor(*wxWHITE);
|
||||
m_nozzle_icon->SetBackgroundColour(bg_clr);
|
||||
m_nozzle_label_id->SetBackgroundColour(bg_clr);
|
||||
m_nozzle_label_1->SetBackgroundColour(bg_clr);
|
||||
m_nozzle_status_icon->SetBackgroundColour(bg_clr);
|
||||
m_nozzle_label_2->SetBackgroundColour(bg_clr);
|
||||
m_nozzle_selected_bitmap->SetBackgroundColour(bg_clr);
|
||||
|
||||
SetBackgroundColor(bg_clr);
|
||||
Refresh();
|
||||
};
|
||||
|
||||
};// end of namespace Slic3r::GUI
|
||||
@@ -1,99 +0,0 @@
|
||||
//**********************************************************/
|
||||
/* File: wgtDeviceNozzleRackNozzleItem.h
|
||||
* Description: One nozzle cell of the H2C hotend rack view.
|
||||
*
|
||||
* This header defines only the single-nozzle-cell widget (and the selection event it emits), which
|
||||
* is the one dependency MultiNozzleSync's HotEndTable needs. The rest of the Device-tab rack panel
|
||||
* (wgtDeviceNozzleRack / ...Area / ...ToolHead / ...Pos) is not provided here.
|
||||
//**********************************************************/
|
||||
|
||||
#pragma once
|
||||
#include "slic3r/GUI/DeviceCore/DevNozzleRack.h"
|
||||
|
||||
#include "slic3r/GUI/Widgets/StaticBox.hpp"
|
||||
#include "slic3r/GUI/Widgets/Label.hpp" // complete type required by the inline SetDisplayIdText below
|
||||
|
||||
#include <wx/panel.h>
|
||||
#include <memory>
|
||||
|
||||
// Previous definitions
|
||||
class ScalableBitmap;
|
||||
namespace Slic3r
|
||||
{
|
||||
struct DevNozzle;
|
||||
class DevNozzleRack;
|
||||
};
|
||||
|
||||
// Events
|
||||
wxDECLARE_EVENT(EVT_NOZZLE_RACK_NOZZLE_ITEM_SELECTED, wxCommandEvent);
|
||||
|
||||
namespace Slic3r::GUI
|
||||
{
|
||||
class wgtDeviceNozzleRackNozzleItem : public StaticBox
|
||||
{
|
||||
public:
|
||||
enum NOZZLE_STATUS
|
||||
{
|
||||
NOZZLE_EMPTY,
|
||||
NOZZLE_NORMAL,
|
||||
NOZZLE_UNKNOWN,
|
||||
NOZZLE_ERROR
|
||||
};
|
||||
|
||||
public:
|
||||
wgtDeviceNozzleRackNozzleItem(wxWindow* parent, int nozzle_id);
|
||||
|
||||
public:
|
||||
void Update(const std::shared_ptr<DevNozzleRack> rack, bool on_rack = true); // on_rack is false means extruder nozzle
|
||||
|
||||
int GetNozzleId() const { return m_nozzle_id; }
|
||||
void SetDisplayIdText(const wxString& text) { m_nozzle_label_id->SetLabel(text);};
|
||||
|
||||
void EnableSelect();;
|
||||
void SetSelected(bool selected);
|
||||
bool IsSelected() const { return m_is_selected; }
|
||||
|
||||
bool IsDisabled() const { return m_is_disabled; }
|
||||
void SetDisable(bool disabled);
|
||||
|
||||
void Rescale();
|
||||
|
||||
private:
|
||||
void CreateGui();
|
||||
|
||||
void SetNozzleStatus(NOZZLE_STATUS status, const wxString& str1, const wxString& str2, const std::string& color);
|
||||
|
||||
void OnBtnNozzleStatus(wxMouseEvent& evt);
|
||||
void OnItemSelected(wxMouseEvent& evt);
|
||||
|
||||
private:
|
||||
std::weak_ptr<DevNozzleRack> m_rack;
|
||||
|
||||
int m_nozzle_id; // internal id, from 0 to 5
|
||||
std::string m_filament_color;
|
||||
NOZZLE_STATUS m_status = NOZZLE_STATUS::NOZZLE_EMPTY;
|
||||
|
||||
// select
|
||||
bool m_is_selected = false;
|
||||
bool m_enable_select = false;
|
||||
ScalableBitmap* m_nozzle_selected_image{ nullptr };
|
||||
wxStaticBitmap* m_nozzle_selected_bitmap{ nullptr };
|
||||
|
||||
// enable or disable
|
||||
bool m_is_disabled = false;
|
||||
|
||||
// Images
|
||||
ScalableBitmap* m_nozzle_normal_image{ nullptr };
|
||||
ScalableBitmap* m_nozzle_empty_image{ nullptr };
|
||||
ScalableBitmap* m_nozzle_unknown_image{ nullptr };
|
||||
ScalableBitmap* m_nozzle_error_image{ nullptr };
|
||||
|
||||
// GUI
|
||||
wxStaticBitmap* m_nozzle_icon{ nullptr };
|
||||
Label* m_nozzle_label_id { nullptr };
|
||||
Label* m_nozzle_label_1{ nullptr };
|
||||
wxStaticBitmap* m_nozzle_status_icon = nullptr;
|
||||
Label* m_nozzle_label_2{ nullptr };
|
||||
};
|
||||
|
||||
};// end of namespace Slic3r::GUI
|
||||
@@ -1,6 +1,6 @@
|
||||
//**********************************************************/
|
||||
/* File: wgtDeviceNozzleRackUpdate.cpp
|
||||
* Description: The dialog for reading/upgrading the H2C induction-hotend-rack firmware.
|
||||
* Description: The panel with rack updating
|
||||
*
|
||||
* \n class wgtDeviceNozzleRackUpdate
|
||||
//**********************************************************/
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "wgtDeviceNozzleRackUpdate.h"
|
||||
|
||||
#include "slic3r/GUI/DeviceCore/DevNozzleSystem.h"
|
||||
#include "slic3r/GUI/DeviceCore/DevUpgrade.h"
|
||||
|
||||
#include "slic3r/GUI/MainFrame.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
@@ -103,7 +104,7 @@ void wgtDeviceNozzleRackUprade::CreateGui()
|
||||
wxPanel* separator = new wxPanel(this);
|
||||
separator->SetMaxSize(wxSize(-1, FromDIP(1)));
|
||||
separator->SetMinSize(wxSize(-1, FromDIP(1)));
|
||||
separator->SetBackgroundColour(wxColour(238, 238, 238));// Orca: grey300 spelled out as a literal RGB, since Orca has no equivalent grey-300 colour macro
|
||||
separator->SetBackgroundColour(wxColour(238, 238, 238)); // Orca: grey300 as a literal, no equivalent colour macro in Orca
|
||||
main_sizer->Add(separator, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(12));
|
||||
}
|
||||
}
|
||||
@@ -215,10 +216,13 @@ void wgtDeviceNozzleRackHotendUpdate::CreateGui()
|
||||
m_colour_box->SetMinSize(WX_DIP_SIZE(16, 16));
|
||||
m_colour_box->SetCornerRadius(FromDIP(2));
|
||||
m_colour_box->SetSize(wxSize(FromDIP(16), FromDIP(16)));
|
||||
// m_colour_box->SetBackgroundColour(*wxRED);
|
||||
|
||||
// type_sizer_row_1->AddStretchSpacer();
|
||||
type_sizer_row_1->Add(m_colour_box, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
|
||||
type_sizer_row_1->AddStretchSpacer(1);
|
||||
type_sizer_row_1->Add(m_material_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(2));
|
||||
// type_sizer_row_1->AddStretchSpacer();
|
||||
|
||||
m_diameter_label = new Label(type_panel);
|
||||
m_diameter_label->SetFont(Label::Body_12);
|
||||
@@ -277,19 +281,20 @@ void wgtDeviceNozzleRackHotendUpdate::CreateGui()
|
||||
m_used_time->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
|
||||
m_refresh_icon = new ScalableBitmap(this, "refresh_printer", 12);
|
||||
// m_in_refreh_icon = new ScalableBitmap(this, "refresh_nozzle", 12);
|
||||
m_error_icon = new ScalableBitmap(this, "error", 14);
|
||||
m_status_bitmap = new wxStaticBitmap(this, wxID_ANY, m_refresh_icon->bmp());
|
||||
m_status_bitmap->Bind(wxEVT_LEFT_UP, &wgtDeviceNozzleRackHotendUpdate::OnStatusIconClick, this);
|
||||
|
||||
std::vector<std::string> list{"refresh_nozzle_1", "refresh_nozzle_2", "refresh_nozzle_3", "refresh_nozzle_4"};
|
||||
// Orca: AnimaIcon has no per-instance size argument (it fixes 25px internally), so no size is
|
||||
// passed here.
|
||||
// Orca: AnimaIcon has no per-instance size argument (it fixes the size internally), so none is passed.
|
||||
m_refreshing_icon = new AnimaIcon(this, wxID_ANY, list, "refresh_nozzle", 100);
|
||||
m_refreshing_icon->Show(false);
|
||||
|
||||
|
||||
m_status_label = new Label(this);
|
||||
m_status_label->SetFont(Label::Body_12);
|
||||
// m_status_label->SetForegroundColour(wxColour("#00AE42"));
|
||||
|
||||
content_sizer->Add(info_panel, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(20));
|
||||
content_sizer->Add(m_used_time, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(20));
|
||||
@@ -312,7 +317,8 @@ void wgtDeviceNozzleRackHotendUpdate::OnStatusIconClick(wxMouseEvent& event)
|
||||
m_status_label->SetForegroundColour(wxColour("#A3A3A3"));
|
||||
m_status_label->SetLabel(_L("Refreshing"));
|
||||
m_status_bitmap->Show(false);
|
||||
if(!m_refreshing_icon->IsPlaying())
|
||||
// m_status_bitmap->Refresh();
|
||||
if(!m_refreshing_icon->IsPlaying())
|
||||
{
|
||||
m_refreshing_icon->Play();
|
||||
m_refreshing_icon->Show();
|
||||
@@ -333,17 +339,17 @@ void wgtDeviceNozzleRackHotendUpdate::OnStatusIconClick(wxMouseEvent& event)
|
||||
dlg.AddButton(wxID_CANCEL, _L("Cancel"), false);
|
||||
dlg.AddButton(wxID_OK,_L("Jump to the upgrade page"), true);
|
||||
|
||||
if (dlg.ShowModal() == wxID_OK)
|
||||
if (dlg.ShowModal() == wxID_OK)
|
||||
{
|
||||
wxGetApp().mainframe->m_monitor->jump_to_Upgrade();
|
||||
|
||||
wxCommandEvent evt(wxEVT_NOZZLE_JUMP_UPGRADE, GetId());
|
||||
evt.SetEventObject(this);
|
||||
wxWindow* target = GetParent();
|
||||
if (target)
|
||||
if (target)
|
||||
{
|
||||
target = target->GetParent();
|
||||
if (target)
|
||||
if (target)
|
||||
{
|
||||
wxPostEvent(target, evt);
|
||||
}
|
||||
@@ -370,8 +376,8 @@ void wgtDeviceNozzleRackHotendUpdate::OnBitmapHoverEnter(wxMouseEvent& event)
|
||||
scaledBmp = m_scaled_nozzle_image;
|
||||
}
|
||||
|
||||
m_hoverFrame = new wxFrame(nullptr, wxID_ANY, "",
|
||||
wxDefaultPosition, wxDefaultSize,
|
||||
m_hoverFrame = new wxFrame(nullptr, wxID_ANY, "",
|
||||
wxDefaultPosition, wxDefaultSize,
|
||||
wxFRAME_NO_TASKBAR | wxBORDER_NONE | wxTRANSPARENT_WINDOW);
|
||||
m_hoverFrame->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
m_hoverFrame->SetSize(scaledW, scaledH);
|
||||
@@ -506,11 +512,11 @@ void wgtDeviceNozzleRackHotendUpdate::UpdateInfo(const DevNozzle& nozzle)
|
||||
}
|
||||
|
||||
wxString filamentDisplayName{};
|
||||
for (auto iter = GUI::wxGetApp().preset_bundle->filaments.begin(); iter != GUI::wxGetApp().preset_bundle->filaments.end(); ++iter)
|
||||
for (auto iter = GUI::wxGetApp().preset_bundle->filaments.begin(); iter != GUI::wxGetApp().preset_bundle->filaments.end(); ++iter)
|
||||
{
|
||||
const Preset& filament_preset = *iter;
|
||||
// const auto& config = filament_preset.config;
|
||||
if (filament_preset.filament_id == nozzle.GetFilamentId())
|
||||
if (filament_preset.filament_id == nozzle.GetFilamentId())
|
||||
{
|
||||
filamentDisplayName = wxString(filament_preset.alias);
|
||||
}
|
||||
@@ -612,7 +618,7 @@ void wgtDeviceNozzleRackHotendUpdate::UpdateInfo(const DevNozzle& nozzle)
|
||||
m_used_time->Show(true);
|
||||
m_status_label->Show(true);
|
||||
m_status_bitmap->Show(true);
|
||||
m_status_label->SetForegroundColour(wxColour("#009688"));
|
||||
m_status_label->SetForegroundColour(wxColour("#009688")); // Orca: accent green
|
||||
m_status_label->SetLabel(_L("Refresh"));
|
||||
m_status_bitmap->SetBitmap(m_refresh_icon->bmp());
|
||||
m_status_bitmap->Refresh();
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
//**********************************************************/
|
||||
/* File: wgtDeviceNozzleRackUpdate.h
|
||||
* Description: The dialog for reading/upgrading the H2C induction-hotend-rack firmware.
|
||||
* Description: The panel for updating hotends
|
||||
*
|
||||
* \n class wgtDeviceNozzleRackUpgradeDlg; // modal "Hotends Info" dialog
|
||||
* \n class wgtDeviceNozzleRackUprade; // the dialog's content panel (per-nozzle rows)
|
||||
* \n class wgtDeviceNozzleRackHotendUpdate; // one hotend row (toolhead "R" + rack 1..6)
|
||||
*
|
||||
* Opened from the Device-tab rack panel's "Hotends Info" button (wgtDeviceNozzleRack.cpp) and
|
||||
* reused by the Device->Upgrade page "Hotends on Rack" section
|
||||
* (UpgradePanel::createNozzleRackWidgets). All device data + MQTT commands live in
|
||||
* DevNozzleRack / DevNozzleSystem, so this is additive GUI gated behind
|
||||
* DevNozzleRack::IsSupported() — dormant for every non-rack printer.
|
||||
* \n class wgtDeviceNozzleRackUpdate
|
||||
//**********************************************************/
|
||||
|
||||
#pragma once
|
||||
@@ -22,8 +14,6 @@
|
||||
|
||||
#include <wx/panel.h>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
// Previous definitions
|
||||
class Button;
|
||||
@@ -49,7 +39,7 @@ public:
|
||||
wgtDeviceNozzleRackUpgradeDlg(wxWindow* parent, const std::shared_ptr<DevNozzleRack> rack);
|
||||
|
||||
public:
|
||||
void UpdateRackInfo(const std::shared_ptr<DevNozzleRack> rack);
|
||||
void UpdateRackInfo(const std::shared_ptr<DevNozzleRack> rack);;
|
||||
|
||||
public:
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||
@@ -146,6 +136,7 @@ private:
|
||||
ScalableBitmap* m_scaled_nozzle_image = nullptr;
|
||||
ScalableBitmap* m_scaled_nozzle_empty_image = nullptr;
|
||||
ScalableBitmap* m_refresh_icon = nullptr;
|
||||
// ScalableBitmap* m_in_refreh_icon = nullptr;
|
||||
AnimaIcon* m_refreshing_icon = nullptr;
|
||||
ScalableBitmap* m_error_icon = nullptr;
|
||||
|
||||
@@ -159,7 +150,7 @@ private:
|
||||
|
||||
Label* m_status_label{ nullptr };
|
||||
Label* m_used_time{ nullptr };
|
||||
|
||||
|
||||
Label* m_diameter_label;
|
||||
Label* m_flowtype_label;
|
||||
Label* m_type_label;
|
||||
@@ -172,4 +163,4 @@ private:
|
||||
|
||||
wxDECLARE_EVENT(wxEVT_NOZZLE_JUMP_UPGRADE, wxCommandEvent);
|
||||
|
||||
};// end of namespace Slic3r::GUI
|
||||
};// end of namespace Slic3r::GUI
|
||||
@@ -6,14 +6,14 @@
|
||||
//**********************************************************/
|
||||
|
||||
#include "wgtDeviceNozzleSelect.h"
|
||||
#include "wgtDeviceNozzleRackNozzleItem.h" // the nozzle-item widget lives in its own header
|
||||
#include "wgtDeviceNozzleRack.h"
|
||||
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/DeviceTab/wgtMsgBox.h"
|
||||
#include "slic3r/GUI/Widgets/Label.hpp"
|
||||
#include "slic3r/GUI/Widgets/Label.hpp" // Orca: explicit Label include
|
||||
|
||||
static wxColour s_gray_clr("#B0B0B0");
|
||||
static wxColour s_hgreen_clr("#009688");
|
||||
static wxColour s_hgreen_clr("#009688"); // Orca: accent green
|
||||
static wxColour s_red_clr("#D01B1B");
|
||||
|
||||
static std::vector<int> a_nozzle_seq = {16, 18, 20, 17, 19, 21};
|
||||
@@ -132,7 +132,7 @@ void wgtDeviceNozzleRackSelect::UpdateNozzleInfos(std::shared_ptr<DevNozzleRack>
|
||||
}
|
||||
}
|
||||
|
||||
static void s_enable_item_if_match(wgtDeviceNozzleRackNozzleItem* item,
|
||||
static void s_enable_item_if_match(wgtDeviceNozzleRackNozzleItem* item,
|
||||
const DevNozzle& nozzle_info,
|
||||
const DevNozzle& selected_nozzle)
|
||||
{
|
||||
@@ -214,7 +214,7 @@ void wgtDeviceNozzleRackSelect::UpdatSelectedNozzles(std::shared_ptr<DevNozzleRa
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackSelect::ClearSelection()
|
||||
void wgtDeviceNozzleRackSelect::ClearSelection()
|
||||
{
|
||||
m_selected_nozzle = DevNozzle();
|
||||
m_toolhead_nozzle_l->SetSelected(false);
|
||||
|
||||
@@ -7,14 +7,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "slic3r/GUI/DeviceCore/DevNozzleSystem.h" // DevNozzle (value member) + DevDefs.h (PrintFromType, MAIN/DEPUTY_EXTRUDER_ID)
|
||||
#include "slic3r/GUI/DeviceCore/DevNozzleSystem.h"
|
||||
|
||||
#include <wx/panel.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
// Previous definitions
|
||||
class Label;
|
||||
@@ -71,4 +68,4 @@ private:
|
||||
std::unordered_map<int, wgtDeviceNozzleRackNozzleItem *> m_nozzle_items; // from 16 to 21
|
||||
};
|
||||
|
||||
};// end of namespace Slic3r::GUI
|
||||
};// end of namespace Slic3r::GUI
|
||||
@@ -28,4 +28,4 @@ private:
|
||||
};
|
||||
}
|
||||
|
||||
#endif // WGTMSGBOX_H
|
||||
#endif // WGTMSGBOX_H
|
||||
163
src/slic3r/GUI/DeviceTab/wgtMsgPanel.cpp
Normal file
163
src/slic3r/GUI/DeviceTab/wgtMsgPanel.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
#include "wgtMsgPanel.h"
|
||||
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/Widgets/Label.hpp"
|
||||
#include "slic3r/GUI/Widgets/StateColor.hpp"
|
||||
#include "slic3r/GUI/wxExtensions.hpp"
|
||||
|
||||
#include <wx/sizer.h>
|
||||
|
||||
namespace Slic3r::GUI
|
||||
{
|
||||
|
||||
// ===== wgtMsgPanelItem ======================================================
|
||||
|
||||
wgtMsgPanelItem::wgtMsgPanelItem(wxWindow* parent,
|
||||
const wxColour& colour,
|
||||
const wxString& text,
|
||||
int max_width,
|
||||
const wxString& wiki_url)
|
||||
: wxPanel(parent, wxID_ANY)
|
||||
, m_max_width(max_width)
|
||||
, m_colour(colour)
|
||||
, m_text(text)
|
||||
, m_wiki_url(wiki_url)
|
||||
{
|
||||
CreateGui();
|
||||
}
|
||||
|
||||
void wgtMsgPanelItem::CreateGui()
|
||||
{
|
||||
SetBackgroundColour(StateColor::darkModeColorFor(wxColour(*wxWHITE)));
|
||||
|
||||
wxBoxSizer* main_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
// Color bar on the left
|
||||
//wxPanel* color_bar = new wxPanel(this, wxID_ANY);
|
||||
//color_bar->SetMinSize(wxSize(FromDIP(4), -1));
|
||||
//color_bar->SetBackgroundColour(m_colour);
|
||||
//main_sizer->Add(color_bar, 0, wxEXPAND | wxRIGHT, FromDIP(4));
|
||||
|
||||
// Text and optional wiki link
|
||||
wxBoxSizer* text_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
m_text_label = new ::Label(this, m_text);
|
||||
m_text_label->SetFont(::Label::Body_13);
|
||||
m_text_label->SetBackgroundColour(StateColor::darkModeColorFor(wxColour(*wxWHITE)));
|
||||
m_text_label->SetForegroundColour(m_colour);
|
||||
if (!m_wiki_url.IsEmpty()) {
|
||||
m_text_label->Wrap(m_max_width - FromDIP(20)); // Wrap text to fit within a reasonable width
|
||||
} else {
|
||||
m_text_label->Wrap(m_max_width - FromDIP(10)); // Wrap text to fit within a reasonable width
|
||||
}
|
||||
|
||||
text_sizer->Add(m_text_label, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
if (!m_wiki_url.IsEmpty()) {
|
||||
m_wiki_link = new wxHyperlinkCtrl(this, wxID_ANY, "Wiki->", m_wiki_url);
|
||||
m_wiki_link->SetNormalColour(m_colour);
|
||||
m_wiki_link->SetHoverColour(wxColour(0, 0, 200));
|
||||
m_wiki_link->SetVisitedColour(*wxBLUE);
|
||||
Bind(wxEVT_HYPERLINK, &wgtMsgPanelItem::OnClickWiki, this, m_wiki_link->GetId());
|
||||
text_sizer->AddSpacer(FromDIP(4));
|
||||
text_sizer->Add(m_wiki_link, 0, wxALIGN_CENTER_VERTICAL | wxEXPAND);
|
||||
}
|
||||
|
||||
main_sizer->Add(text_sizer, 0, wxALIGN_LEFT | wxEXPAND);
|
||||
|
||||
SetSizer(main_sizer);
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
|
||||
void wgtMsgPanelItem::SetColour(const wxColour& colour)
|
||||
{
|
||||
m_colour = colour;
|
||||
// Simple handling: refresh the whole control to redraw the color bar
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void wgtMsgPanelItem::SetText(const wxString& text)
|
||||
{
|
||||
m_text = text;
|
||||
if (m_text_label) {
|
||||
m_text_label->SetLabel(m_text);
|
||||
}
|
||||
}
|
||||
|
||||
void wgtMsgPanelItem::SetWiki(const wxString& wiki_url)
|
||||
{
|
||||
m_wiki_url = wiki_url;
|
||||
if (m_wiki_link) {
|
||||
m_wiki_link->SetURL(m_wiki_url);
|
||||
}
|
||||
}
|
||||
|
||||
void wgtMsgPanelItem::OnClickWiki(wxHyperlinkEvent& evt)
|
||||
{
|
||||
if (!m_wiki_url.IsEmpty()) {
|
||||
wxLaunchDefaultBrowser(m_wiki_url);
|
||||
}
|
||||
}
|
||||
|
||||
// ===== wgtMsgPanel ==========================================================
|
||||
|
||||
wgtMsgPanel::wgtMsgPanel(wxWindow* parent)
|
||||
: wxPanel(parent, wxID_ANY)
|
||||
{
|
||||
CreateGui();
|
||||
}
|
||||
|
||||
void wgtMsgPanel::CreateGui()
|
||||
{
|
||||
SetBackgroundColour(StateColor::darkModeColorFor(wxColour(*wxWHITE)));
|
||||
|
||||
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_label_title = new Label(this, _L("Note:"));
|
||||
m_label_title->SetFont(::Label::Body_14);
|
||||
main_sizer->Add(m_label_title, 0, wxEXPAND | wxALL, FromDIP(4));
|
||||
|
||||
m_list_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
main_sizer->Add(m_list_sizer, 1, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(4));
|
||||
|
||||
SetSizer(main_sizer);
|
||||
Layout();
|
||||
}
|
||||
|
||||
void wgtMsgPanel::AddMessage(const wxString& text,
|
||||
const wxColour& colour,
|
||||
const wxString& wiki_url)
|
||||
{
|
||||
if (!m_list_sizer) return;
|
||||
|
||||
auto* label = new ::Label(this, wxString::Format("%d. ", GetMessageCount() + 1));
|
||||
label->SetFont(::Label::Body_14);
|
||||
label->SetBackgroundColour(StateColor::darkModeColorFor(wxColour(*wxWHITE)));
|
||||
label->SetForegroundColour(colour);
|
||||
|
||||
auto* item = new wgtMsgPanelItem(this, colour, text, GetSize().GetWidth(), wiki_url);
|
||||
auto h_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
h_sizer->Add(label, 0, wxEXPAND);
|
||||
h_sizer->Add(item, 0, wxEXPAND);
|
||||
m_list_sizer->Add(h_sizer, 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(2));
|
||||
Layout();
|
||||
}
|
||||
|
||||
void wgtMsgPanel::Clear()
|
||||
{
|
||||
if (!m_list_sizer) return;
|
||||
|
||||
Freeze();
|
||||
wxSizerItemList children = m_list_sizer->GetChildren();
|
||||
for (auto it = children.begin(); it != children.end(); ++it) {
|
||||
if (wxWindow* win = (*it)->GetWindow()) {
|
||||
win->Destroy();
|
||||
}
|
||||
}
|
||||
m_list_sizer->Clear(true);
|
||||
Thaw();
|
||||
Layout();
|
||||
}
|
||||
|
||||
} // namespace Slic3r::GUI
|
||||
69
src/slic3r/GUI/DeviceTab/wgtMsgPanel.h
Normal file
69
src/slic3r/GUI/DeviceTab/wgtMsgPanel.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include <wx/panel.h>
|
||||
#include <wx/hyperlink.h>
|
||||
#include <wx/sizer.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
class Label;
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
namespace GUI
|
||||
{
|
||||
|
||||
// Single message item, including color, text and optional wiki link
|
||||
class wgtMsgPanelItem : public wxPanel
|
||||
{
|
||||
public:
|
||||
wgtMsgPanelItem(wxWindow* parent,
|
||||
const wxColour& colour,
|
||||
const wxString& text,
|
||||
int max_width,
|
||||
const wxString& wiki_url = wxEmptyString);
|
||||
|
||||
void SetColour(const wxColour& colour);
|
||||
void SetText(const wxString& text);
|
||||
void SetWiki(const wxString& wiki_url);
|
||||
|
||||
private:
|
||||
void CreateGui();
|
||||
void OnClickWiki(wxHyperlinkEvent& evt);
|
||||
|
||||
private:
|
||||
int m_max_width;
|
||||
wxColour m_colour;
|
||||
wxString m_text;
|
||||
wxString m_wiki_url;
|
||||
|
||||
Label* m_text_label{ nullptr };
|
||||
wxHyperlinkCtrl* m_wiki_link{ nullptr };
|
||||
};
|
||||
|
||||
// Message panel based on wxWidget, shows wgtMsgPanelItem in a list
|
||||
class wgtMsgPanel : public wxPanel
|
||||
{
|
||||
public:
|
||||
explicit wgtMsgPanel(wxWindow* parent);
|
||||
~wgtMsgPanel() override = default;
|
||||
|
||||
// Add a message (preferred API)
|
||||
void AddMessage(const wxString& text,
|
||||
const wxColour& colour,
|
||||
const wxString& wiki_url = wxEmptyString);
|
||||
void Clear();
|
||||
|
||||
// Const API
|
||||
int GetMessageCount() const { return m_list_sizer ? m_list_sizer->GetItemCount() : 0; }
|
||||
|
||||
private:
|
||||
void CreateGui();
|
||||
|
||||
private:
|
||||
Label* m_label_title{ nullptr };
|
||||
wxBoxSizer* m_list_sizer{ nullptr };
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace Slic3r::GUI
|
||||
@@ -8,6 +8,26 @@
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
void fill_gradient_rect_east(wxDC& dc, const wxRect& rect, const wxColour& from, const wxColour& to)
|
||||
{
|
||||
if (rect.width <= 0 || rect.height <= 0) return;
|
||||
|
||||
auto mix_channel = [](unsigned char a, unsigned char b, double t) {
|
||||
return static_cast<unsigned char>(a + (b - a) * t + 0.5);
|
||||
};
|
||||
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
for (int x = 0; x < rect.width; ++x) {
|
||||
const double t = rect.width > 1 ? static_cast<double>(x) / (rect.width - 1) : 0.0;
|
||||
const wxColour col(mix_channel(from.Red(), to.Red(), t),
|
||||
mix_channel(from.Green(), to.Green(), t),
|
||||
mix_channel(from.Blue(), to.Blue(), t),
|
||||
mix_channel(from.Alpha(), to.Alpha(), t));
|
||||
dc.SetBrush(wxBrush(col));
|
||||
dc.DrawRectangle(rect.x + x, rect.y, 1, rect.height);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper struct to hold bitmap and DC
|
||||
struct BitmapDC {
|
||||
wxBitmap bitmap;
|
||||
|
||||
@@ -3,10 +3,17 @@
|
||||
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/dc.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <vector>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
// Fills a rect with a west->east linear gradient by drawing solid 1px columns.
|
||||
// Use instead of wxDC::GradientFillLinear, whose CoreGraphics (CGShading) backend
|
||||
// fails to render on some macOS builds; solid fills are unaffected.
|
||||
void fill_gradient_rect_east(wxDC& dc, const wxRect& rect, const wxColour& from, const wxColour& to);
|
||||
|
||||
enum class FilamentRenderMode {
|
||||
Single,
|
||||
Dual,
|
||||
|
||||
@@ -1256,13 +1256,14 @@ int GUI_App::download_plugin(std::string name, std::string package_name, Install
|
||||
// get_url
|
||||
std::string url = get_plugin_url(name, app_config->get_country_code());
|
||||
std::string download_url;
|
||||
std::string online_version;
|
||||
Slic3r::Http http_url = Slic3r::Http::get(url);
|
||||
BOOST_LOG_TRIVIAL(info) << "[download_plugin]: check the plugin from " << url;
|
||||
http_url.timeout_connect(TIMEOUT_CONNECT)
|
||||
.timeout_max(TIMEOUT_RESPONSE)
|
||||
.header("X-BBL-OS-Type", os_type)
|
||||
.on_complete(
|
||||
[&download_url](std::string body, unsigned status) {
|
||||
[&download_url, &online_version](std::string body, unsigned status) {
|
||||
try {
|
||||
json j = json::parse(body);
|
||||
std::string message = j["message"].get<std::string>();
|
||||
@@ -1272,6 +1273,7 @@ int GUI_App::download_plugin(std::string name, std::string package_name, Install
|
||||
if (resource.is_array()) {
|
||||
for (auto iter = resource.begin(); iter != resource.end(); iter++) {
|
||||
Semver version;
|
||||
std::string version_str;
|
||||
std::string url;
|
||||
std::string type;
|
||||
std::string vendor;
|
||||
@@ -1282,7 +1284,8 @@ int GUI_App::download_plugin(std::string name, std::string package_name, Install
|
||||
BOOST_LOG_TRIVIAL(info) << "[download_plugin]: get version of settings's type, " << sub_iter.value();
|
||||
}
|
||||
else if (boost::iequals(sub_iter.key(), "version")) {
|
||||
version = *(Semver::parse(sub_iter.value()));
|
||||
version_str = sub_iter.value();
|
||||
version = *(Semver::parse(version_str));
|
||||
}
|
||||
else if (boost::iequals(sub_iter.key(), "description")) {
|
||||
description = sub_iter.value();
|
||||
@@ -1293,6 +1296,7 @@ int GUI_App::download_plugin(std::string name, std::string package_name, Install
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << "[download_plugin 1]: get type " << type << ", version " << version.to_string() << ", url " << url;
|
||||
download_url = url;
|
||||
online_version = version_str;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1382,6 +1386,26 @@ int GUI_App::download_plugin(std::string name, std::string package_name, Install
|
||||
result = -1;
|
||||
});
|
||||
http.perform_sync();
|
||||
|
||||
// The cloud endpoint serves the newest build of the requested series, which may be
|
||||
// newer than the configured version. Adopt the actual downloaded version so that
|
||||
// install_plugin() names the library after what it really contains - otherwise the
|
||||
// post-load config sync (on_init_network) points at a file name that does not exist
|
||||
// and the plugin is "lost" on the following launch. Legacy mode is left untouched:
|
||||
// is_legacy_version() matches exactly and drives the legacy struct layout.
|
||||
if (result >= 0 && name == "plugins" && !online_version.empty() && !use_legacy_network_plugin()) {
|
||||
std::string configured = app_config->get_network_plugin_version();
|
||||
if (configured.empty())
|
||||
configured = get_latest_network_version();
|
||||
if (configured != online_version
|
||||
&& configured.size() >= 8 && online_version.size() >= 8
|
||||
&& configured.compare(0, 8, online_version, 0, 8) == 0) {
|
||||
BOOST_LOG_TRIVIAL(info) << "[download_plugin] server returned " << online_version
|
||||
<< " for configured " << configured << ", adopting it";
|
||||
app_config->set_network_plugin_version(online_version);
|
||||
}
|
||||
}
|
||||
|
||||
j["result"] = result < 0 ? "failed" : "success";
|
||||
j["error_msg"] = err_msg;
|
||||
return result;
|
||||
@@ -1958,6 +1982,13 @@ bool GUI_App::check_networking_version()
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "check_networking_version: network_ver=" << network_ver << ", expected=" << studio_ver;
|
||||
|
||||
// A configured version outside the whitelisted series must never pass as compatible,
|
||||
// even if it matches the loaded library - its ABI does not match this build.
|
||||
if (!use_legacy_network_plugin() && !is_supported_network_version(studio_ver)) {
|
||||
m_networking_compatible = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (network_ver.length() >= 8 && studio_ver.length() >= 8) {
|
||||
if (network_ver.substr(0,8) == studio_ver.substr(0,8)) {
|
||||
m_networking_compatible = true;
|
||||
@@ -3396,10 +3427,26 @@ void GUI_App::copy_network_if_available()
|
||||
if (app_config->get("update_network_plugin") != "true")
|
||||
return;
|
||||
|
||||
bool had_cache = false;
|
||||
bool installed = install_network_plugin_from_ota(had_cache);
|
||||
// Success consumes the cache and a missing cache leaves nothing to do; only a
|
||||
// failed copy keeps the flag so the install is retried on the next launch.
|
||||
if (installed || !had_cache)
|
||||
app_config->set("update_network_plugin", "false");
|
||||
}
|
||||
|
||||
// Installs the OTA-downloaded plug-in files from ota/plugins into the plugins folder
|
||||
// (network library under its versioned name, and the configured version updated to
|
||||
// match). Returns true when everything was installed; had_cache reports whether a
|
||||
// usable download was present at all.
|
||||
bool GUI_App::install_network_plugin_from_ota(bool& had_cache)
|
||||
{
|
||||
had_cache = false;
|
||||
|
||||
std::string data_dir_str = data_dir();
|
||||
boost::filesystem::path data_dir_path(data_dir_str);
|
||||
auto plugin_folder = data_dir_path / "plugins";
|
||||
auto cache_folder = data_dir_path / "ota";
|
||||
auto cache_folder = data_dir_path / "ota" / "plugins";
|
||||
std::string changelog_file = cache_folder.string() + "/network_plugins.json";
|
||||
|
||||
std::string cached_version;
|
||||
@@ -3417,10 +3464,10 @@ void GUI_App::copy_network_if_available()
|
||||
}
|
||||
|
||||
if (cached_version.empty()) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": no version found in changelog, aborting copy";
|
||||
app_config->set("update_network_plugin", "false");
|
||||
return;
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": no version found in changelog, nothing to install";
|
||||
return false;
|
||||
}
|
||||
had_cache = true;
|
||||
|
||||
std::string network_library, player_library, live555_library, network_library_dst, player_library_dst, live555_library_dst;
|
||||
#if defined(_MSC_VER) || defined(_WIN32)
|
||||
@@ -3451,17 +3498,41 @@ void GUI_App::copy_network_if_available()
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": create directory " << plugin_folder.string();
|
||||
boost::filesystem::create_directory(plugin_folder);
|
||||
}
|
||||
std::string error_message;
|
||||
if (boost::filesystem::exists(network_library)) {
|
||||
CopyFileResult cfr = copy_file(network_library, network_library_dst, error_message, false);
|
||||
if (cfr != CopyFileResult::SUCCESS) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": Copying failed(" << cfr << "): " << error_message;
|
||||
return;
|
||||
}
|
||||
|
||||
// Replace a destination even while the running process still maps it: an in-use
|
||||
// file cannot be deleted or overwritten on Windows, but it can be renamed aside;
|
||||
// the stale ".old" copy is swept on the next launch (see on_init_network).
|
||||
auto install_file = [](const std::string& src, const std::string& dst) -> bool {
|
||||
boost::system::error_code ec;
|
||||
if (boost::filesystem::exists(dst, ec)) {
|
||||
boost::filesystem::remove(dst, ec);
|
||||
if (ec) {
|
||||
boost::filesystem::path aside(dst);
|
||||
aside += ".old";
|
||||
boost::system::error_code ec2;
|
||||
boost::filesystem::remove(aside, ec2);
|
||||
boost::filesystem::rename(dst, aside, ec2);
|
||||
if (ec2) {
|
||||
BOOST_LOG_TRIVIAL(error) << "install_network_plugin_from_ota: cannot replace in-use file " << dst << ": " << ec2.message();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::string error_message;
|
||||
CopyFileResult cfr = copy_file(src, dst, error_message, false);
|
||||
if (cfr != CopyFileResult::SUCCESS) {
|
||||
BOOST_LOG_TRIVIAL(error) << "install_network_plugin_from_ota: copying " << src << " failed(" << cfr << "): " << error_message;
|
||||
return false;
|
||||
}
|
||||
static constexpr const auto perms = fs::owner_read | fs::owner_write | fs::group_read | fs::others_read;
|
||||
fs::permissions(network_library_dst, perms);
|
||||
fs::remove(network_library);
|
||||
fs::permissions(dst, perms);
|
||||
boost::filesystem::remove(src, ec);
|
||||
return true;
|
||||
};
|
||||
|
||||
if (boost::filesystem::exists(network_library)) {
|
||||
if (!install_file(network_library, network_library_dst))
|
||||
return false;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Copying network library from " << network_library << " to " << network_library_dst << " successfully.";
|
||||
|
||||
app_config->set_network_plugin_version(cached_version);
|
||||
@@ -3469,33 +3540,24 @@ void GUI_App::copy_network_if_available()
|
||||
}
|
||||
|
||||
if (boost::filesystem::exists(player_library)) {
|
||||
CopyFileResult cfr = copy_file(player_library, player_library_dst, error_message, false);
|
||||
if (cfr != CopyFileResult::SUCCESS) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": Copying failed(" << cfr << "): " << error_message;
|
||||
return;
|
||||
}
|
||||
|
||||
static constexpr const auto perms = fs::owner_read | fs::owner_write | fs::group_read | fs::others_read;
|
||||
fs::permissions(player_library_dst, perms);
|
||||
fs::remove(player_library);
|
||||
if (!install_file(player_library, player_library_dst))
|
||||
return false;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Copying player library from " << player_library << " to " << player_library_dst << " successfully.";
|
||||
}
|
||||
|
||||
if (boost::filesystem::exists(live555_library)) {
|
||||
CopyFileResult cfr = copy_file(live555_library, live555_library_dst, error_message, false);
|
||||
if (cfr != CopyFileResult::SUCCESS) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": Copying failed(" << cfr << "): " << error_message;
|
||||
return;
|
||||
}
|
||||
|
||||
static constexpr const auto perms = fs::owner_read | fs::owner_write | fs::group_read | fs::others_read;
|
||||
fs::permissions(live555_library_dst, perms);
|
||||
fs::remove(live555_library);
|
||||
if (!install_file(live555_library, live555_library_dst))
|
||||
return false;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Copying live555 library from " << live555_library << " to " << live555_library_dst << " successfully.";
|
||||
}
|
||||
if (boost::filesystem::exists(changelog_file))
|
||||
fs::remove(changelog_file);
|
||||
app_config->set("update_network_plugin", "false");
|
||||
// All cached files consumed - drop the whole ota/plugins cache folder.
|
||||
try {
|
||||
if (boost::filesystem::exists(cache_folder))
|
||||
fs::remove_all(cache_folder);
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": failed to remove the plugin cache folder " << cache_folder.string();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GUI_App::on_init_network(bool try_backup)
|
||||
@@ -3525,6 +3587,21 @@ bool GUI_App::on_init_network(bool try_backup)
|
||||
std::string config_version = app_config->get_network_plugin_version();
|
||||
|
||||
if (should_load_networking_plugin) {
|
||||
// A version outside the whitelisted series (e.g. 02.03.00.62 configured by an older
|
||||
// Orca release) must not be loaded - its ABI no longer matches this build. Fall back
|
||||
// to the latest supported build if it is already on disk; otherwise clear the
|
||||
// configured version so the normal empty-version download flow takes over (the
|
||||
// download URL and install adoption both derive from the configured version, so it
|
||||
// must not keep pointing at the unsupported build).
|
||||
if (!config_version.empty() && !is_supported_network_version(config_version)) {
|
||||
std::string latest = get_latest_network_version();
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": configured plugin version " << config_version
|
||||
<< " is no longer supported, falling back to " << latest;
|
||||
config_version = BBLNetworkPlugin::versioned_library_exists(latest) ? latest : "";
|
||||
app_config->set_network_plugin_version(config_version);
|
||||
app_config->save();
|
||||
}
|
||||
|
||||
if (config_version.empty()) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": no version configured, need to download";
|
||||
m_networking_need_update = true;
|
||||
@@ -3579,6 +3656,20 @@ bool GUI_App::on_init_network(bool try_backup)
|
||||
}
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": on_init_network, load dll failed";
|
||||
// A failed install can leave the config naming a build that never made it to
|
||||
// disk (download_plugin() adopts the downloaded version up front so that
|
||||
// install_plugin() can name the library after it). If the whitelisted latest
|
||||
// is still installed, fall back to it instead of dropping the user into the
|
||||
// re-download flow without networking.
|
||||
std::string latest = get_latest_network_version();
|
||||
if (config_version != latest && BBLNetworkPlugin::versioned_library_exists(latest)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": falling back to installed " << latest;
|
||||
config_version = latest;
|
||||
app_config->set_network_plugin_version(latest);
|
||||
app_config->save();
|
||||
load_agent_dll = Slic3r::NetworkAgent::initialize_network_module(false, config_version);
|
||||
goto __retry;
|
||||
}
|
||||
if (should_load_networking_plugin) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": on_init_network, need upload network module";
|
||||
m_networking_need_update = true;
|
||||
|
||||
@@ -764,6 +764,7 @@ public:
|
||||
|
||||
void show_network_plugin_download_dialog(bool is_update = false);
|
||||
bool hot_reload_network_plugin();
|
||||
bool install_network_plugin_from_ota(bool& had_cache);
|
||||
std::string get_latest_network_version() const;
|
||||
bool has_network_update_available() const;
|
||||
// Orca: return the client version to report to Bambu servers. Pinned to
|
||||
|
||||
@@ -12,9 +12,10 @@ static const char* HMS_PATH = "hms";
|
||||
static const char* HMS_LOCAL_IMG_PATH = "hms/local_image";
|
||||
|
||||
// the local HMS info
|
||||
// Orca: dev-id-type set trimmed to the devices Orca ships local HMS images for
|
||||
static unordered_set<string> package_dev_id_types {"094", "239", "093", "22E"};
|
||||
|
||||
// HMS should be disabled when stealth mode is on or networking is not installed
|
||||
// Orca: HMS should be disabled when stealth mode is on or networking is not installed
|
||||
static bool should_disable_hms()
|
||||
{
|
||||
Slic3r::AppConfig* config = Slic3r::GUI::wxGetApp().app_config;
|
||||
@@ -251,7 +252,7 @@ int HMSQuery::save_to_local(std::string lang, std::string hms_type, std::string
|
||||
std::string dir_str = (hms_folder / filename).make_preferred().string();
|
||||
std::ofstream json_file(encode_path(dir_str.c_str()));
|
||||
if (json_file.is_open()) {
|
||||
json_file << save_json.dump(1, '\t') << std::endl;
|
||||
json_file << save_json.dump(1, '\t') << std::endl; // Orca: tab-indented dump
|
||||
json_file.close();
|
||||
return 0;
|
||||
}
|
||||
@@ -266,9 +267,15 @@ std::string HMSQuery::hms_language_code()
|
||||
// set language code to en by default
|
||||
return "en";
|
||||
std::string lang_code = wxGetApp().app_config->get_language_code();
|
||||
// Orca: the HMS host ships no catalog for these locales, so fall back to english to avoid empty texts + retries
|
||||
if (lang_code.compare("uk") == 0
|
||||
|| lang_code.compare("cs") == 0
|
||||
|| lang_code.compare("ru") == 0) {
|
||||
|| lang_code.compare("ru") == 0
|
||||
|| lang_code.compare("tr") == 0
|
||||
|| lang_code.compare("pt") == 0
|
||||
|| lang_code.compare("ko") == 0
|
||||
)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "HMS: using english for lang_code = " << lang_code;
|
||||
return "en";
|
||||
}
|
||||
@@ -577,6 +584,7 @@ wxImage HMSQuery::query_image_from_local(const wxString& image_name)
|
||||
{
|
||||
const fs::path& image_path = entry.path();
|
||||
const fs::path& image_name = fs::relative(image_path, local_img_dir);
|
||||
// Orca: from_path() for correct UTF-8 path encoding
|
||||
m_hms_local_images[from_path(image_name)] = wxImage(from_path(image_path));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,10 +18,11 @@ static auto waiting_auth_str = _u8L("Logging in");
|
||||
static auto login_failed_str = _u8L("Login failed");
|
||||
|
||||
|
||||
BindJob::BindJob(std::string dev_id, std::string dev_ip, std::string sec_link, std::string ssdp_version)
|
||||
BindJob::BindJob(std::string dev_id, std::string dev_ip, std::string dev_model, std::string sec_link, std::string ssdp_version)
|
||||
:
|
||||
m_dev_id(dev_id),
|
||||
m_dev_ip(dev_ip),
|
||||
m_dev_model(dev_model),
|
||||
m_sec_link(sec_link),
|
||||
m_ssdp_version(ssdp_version)
|
||||
{
|
||||
@@ -60,7 +61,7 @@ void BindJob::process(Ctl &ctl)
|
||||
std::string timezone = get_timezone_utc_hm(offset);
|
||||
|
||||
m_agent->track_update_property("ssdp_version", m_ssdp_version, "string");
|
||||
int result = m_agent->bind(m_dev_ip, m_dev_id, m_sec_link, timezone, m_improved,
|
||||
int result = m_agent->bind(m_dev_ip, m_dev_id, m_dev_model, m_sec_link, timezone, m_improved,
|
||||
[this, &ctl, &curr_percent, &msg, &result_code, &result_info](int stage, int code, std::string info) {
|
||||
|
||||
result_code = code;
|
||||
|
||||
@@ -16,6 +16,7 @@ class BindJob : public Job
|
||||
std::function<void()> m_success_fun{nullptr};
|
||||
std::string m_dev_id;
|
||||
std::string m_dev_ip;
|
||||
std::string m_dev_model;
|
||||
std::string m_sec_link;
|
||||
std::string m_ssdp_version;
|
||||
bool m_job_finished{ false };
|
||||
@@ -23,7 +24,7 @@ class BindJob : public Job
|
||||
bool m_improved{false};
|
||||
|
||||
public:
|
||||
BindJob(std::string dev_id, std::string dev_ip, std::string sec_link, std::string ssdp_version);
|
||||
BindJob(std::string dev_id, std::string dev_ip, std::string dev_model, std::string sec_link, std::string ssdp_version);
|
||||
|
||||
int status_range() const
|
||||
{
|
||||
|
||||
@@ -262,6 +262,7 @@ void PrintJob::process(Ctl &ctl)
|
||||
params.task_vibration_cali = this->task_vibration_cali;
|
||||
params.task_layer_inspect = this->task_layer_inspect;
|
||||
params.task_record_timelapse= this->task_record_timelapse;
|
||||
params.task_timelapse_use_internal = this->task_timelapse_use_internal;
|
||||
params.nozzle_mapping = this->task_nozzle_mapping;
|
||||
params.ams_mapping = this->task_ams_mapping;
|
||||
params.ams_mapping2 = this->task_ams_mapping2;
|
||||
@@ -274,6 +275,7 @@ void PrintJob::process(Ctl &ctl)
|
||||
params.auto_bed_leveling = this->auto_bed_leveling;
|
||||
params.auto_flow_cali = this->auto_flow_cali;
|
||||
params.auto_offset_cali = this->auto_offset_cali;
|
||||
params.extruder_cali_manual_mode = this->extruder_cali_manual_mode;
|
||||
params.task_ext_change_assist = this->task_ext_change_assist;
|
||||
// Allow disabling the eMMC print path via AppConfig. Plugin 02.03.00.62's
|
||||
// eMMC tunnel code hangs indefinitely at the upload phase with some
|
||||
@@ -489,7 +491,7 @@ void PrintJob::process(Ctl &ctl)
|
||||
DeviceManager* dev = wxGetApp().getDeviceManager();
|
||||
MachineObject* obj = dev->get_selected_machine();
|
||||
|
||||
auto wait_fn = [this, curr_percent, &obj](int state, std::string job_info) {
|
||||
auto wait_fn = [this, &ctl, curr_percent, &obj](int state, std::string job_info) {
|
||||
BOOST_LOG_TRIVIAL(info) << "print_job: get_job_info = " << job_info;
|
||||
|
||||
if (!obj->is_support_wait_sending_finish) {
|
||||
@@ -521,6 +523,11 @@ void PrintJob::process(Ctl &ctl)
|
||||
BOOST_LOG_TRIVIAL(info) << "print_job: printer has enter printing status, s = " << obj->print_status;
|
||||
return true;
|
||||
}
|
||||
// Break the wait-for-print-start loop on user cancel.
|
||||
if (ctl.was_canceled()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "print_job: user cancel the job " << obj->job_id_;
|
||||
return true;
|
||||
}
|
||||
time_out++;
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
|
||||
}
|
||||
@@ -637,7 +644,11 @@ void PrintJob::process(Ctl &ctl)
|
||||
if (result < 0) {
|
||||
curr_percent = -1;
|
||||
|
||||
if (result == BAMBU_NETWORK_ERR_PRINT_WR_FILE_NOT_EXIST || result == BAMBU_NETWORK_ERR_PRINT_SP_FILE_NOT_EXIST) {
|
||||
// The printer is still fetching its encryption flag (a transient state), so ask the
|
||||
// user to retry rather than showing a generic error.
|
||||
if (result == BAMBU_NETOWRK_ERR_PRINT_SP_ENC_FLAG_NOT_READY) {
|
||||
msg_text = _u8L("Retrieving printer information, please try again later.");
|
||||
} else if (result == BAMBU_NETWORK_ERR_PRINT_WR_FILE_NOT_EXIST || result == BAMBU_NETWORK_ERR_PRINT_SP_FILE_NOT_EXIST) {
|
||||
msg_text = file_is_not_exists_str;
|
||||
} else if (result == BAMBU_NETWORK_ERR_PRINT_SP_FILE_OVER_SIZE || result == BAMBU_NETWORK_ERR_PRINT_WR_FILE_OVER_SIZE) {
|
||||
msg_text = file_over_size_str;
|
||||
|
||||
@@ -80,6 +80,7 @@ public:
|
||||
bool task_flow_cali;
|
||||
bool task_vibration_cali;
|
||||
bool task_record_timelapse;
|
||||
bool task_timelapse_use_internal { false };
|
||||
bool task_layer_inspect;
|
||||
bool cloud_print_only { false };
|
||||
bool has_sdcard { false };
|
||||
@@ -92,11 +93,13 @@ public:
|
||||
int auto_bed_leveling{0};
|
||||
int auto_flow_cali{0};
|
||||
int auto_offset_cali{0};
|
||||
int extruder_cali_manual_mode{-1};
|
||||
|
||||
void set_print_config(std::string bed_type, bool bed_leveling, bool flow_cali, bool vabration_cali, bool record_timelapse, bool layer_inspect, bool ext_change_assist,
|
||||
int auto_bed_levelingt,
|
||||
int auto_flow_calit,
|
||||
int auto_offset_calit)
|
||||
int auto_offset_calit,
|
||||
int extruder_cali_manual_modet = -1)
|
||||
{
|
||||
task_bed_type = bed_type;
|
||||
task_bed_leveling = bed_leveling;
|
||||
@@ -109,6 +112,7 @@ public:
|
||||
auto_bed_leveling = auto_bed_levelingt;
|
||||
auto_flow_cali = auto_flow_calit;
|
||||
auto_offset_cali = auto_offset_calit;
|
||||
extruder_cali_manual_mode = extruder_cali_manual_modet;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -117,6 +117,35 @@ void SendJob::process(Ctl &ctl)
|
||||
|
||||
ctl.call_on_main_thread([this] { prepare(); }).wait();
|
||||
ctl.update_status(0, msg);
|
||||
|
||||
// In check mode (InputIpAddressDialog / lan-mode send) verify the connection with a dummy
|
||||
// "verify_job" upload and fire the caller's success/fail callback; when not continuing, stop
|
||||
// here. Cloud sends skip this.
|
||||
if (m_is_check_mode) {
|
||||
PrintParams verify_params;
|
||||
verify_params.dev_ip = m_dev_ip;
|
||||
verify_params.username = "bblp";
|
||||
verify_params.password = m_access_code;
|
||||
verify_params.use_ssl_for_ftp = m_local_use_ssl_for_ftp;
|
||||
verify_params.use_ssl_for_mqtt = m_local_use_ssl;
|
||||
verify_params.dev_id = m_dev_id;
|
||||
verify_params.project_name = "verify_job";
|
||||
verify_params.filename = job_data._temp_path.string();
|
||||
verify_params.connection_type = this->connection_type;
|
||||
|
||||
int verify_result = agent->start_send_gcode_to_sdcard(verify_params, nullptr, nullptr, nullptr);
|
||||
if (verify_result != 0) {
|
||||
BOOST_LOG_TRIVIAL(error) << "send_job: access code / ip verification failed, result = " << verify_result;
|
||||
if (m_enter_ip_address_fun_fail) m_enter_ip_address_fun_fail(verify_result);
|
||||
m_job_finished = true;
|
||||
return;
|
||||
} else if (!m_check_and_continue) {
|
||||
if (m_enter_ip_address_fun_success) m_enter_ip_address_fun_success();
|
||||
m_job_finished = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int total_plate_num = m_plater->get_partplate_list().get_plate_count();
|
||||
|
||||
PartPlate* plate = m_plater->get_partplate_list().get_plate(job_data.plate_idx);
|
||||
|
||||
@@ -88,10 +88,7 @@ MediaPlayCtrl::MediaPlayCtrl(wxWindow *parent, wxMediaCtrl2 *media_ctrl, const w
|
||||
|
||||
m_button_play->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](auto &e) { TogglePlay(); });
|
||||
m_button_play->Bind(wxEVT_RIGHT_UP, [this](auto & e) { m_media_ctrl->Play(); });
|
||||
// m_label_status->Bind(wxEVT_LEFT_UP, [this](auto &e) {
|
||||
// auto url = wxString::Format(L"https://wiki.bambulab.com/%s/software/bambu-studio/faq/live-view", L"en");
|
||||
// wxLaunchDefaultBrowser(url);
|
||||
// });
|
||||
// Orca: live-view FAQ link binding removed (vendor URL)
|
||||
|
||||
Bind(wxEVT_RIGHT_UP, [this](auto & e) {
|
||||
wxClipboard & c = *wxTheClipboard;
|
||||
|
||||
@@ -541,6 +541,20 @@ void MonitorPanel::jump_to_LiveView()
|
||||
m_status_info_panel->get_media_play_ctrl()->jump_to_play();
|
||||
}
|
||||
|
||||
void MonitorPanel::jump_to_Rack()
|
||||
{
|
||||
if (!this->IsShown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto page = m_tabpanel->GetCurrentPage();
|
||||
if (page && page != m_status_info_panel) {
|
||||
m_tabpanel->SetSelection(PT_STATUS);
|
||||
}
|
||||
|
||||
m_status_info_panel->jump_to_Rack();
|
||||
}
|
||||
|
||||
void MonitorPanel::update_network_version_footer()
|
||||
{
|
||||
std::string binary_version = Slic3r::NetworkAgent::get_version();
|
||||
|
||||
@@ -158,6 +158,7 @@ public:
|
||||
void jump_to_HMS();
|
||||
void jump_to_Upgrade();
|
||||
void jump_to_LiveView();
|
||||
void jump_to_Rack();
|
||||
void update_network_version_footer();
|
||||
};
|
||||
|
||||
|
||||
@@ -219,9 +219,14 @@ void NetworkPluginDownloadDialog::setup_version_selector()
|
||||
label = wxString::FromUTF8("\xE2\x94\x94 ") + wxString::FromUTF8(ver.display_name);
|
||||
} else {
|
||||
label = wxString::FromUTF8(ver.display_name);
|
||||
if (ver.is_latest) {
|
||||
label += wxString(" ") + _L("(Latest)");
|
||||
}
|
||||
}
|
||||
// Same labeling as the Preferences selector: "(Latest)" = highest listed
|
||||
// version, "(installed)" = library already on disk; both can apply.
|
||||
if (ver.is_latest) {
|
||||
label += wxString(" ") + _L("(Latest)");
|
||||
}
|
||||
if (ver.is_installed) {
|
||||
label += wxString(" ") + _L("(installed)");
|
||||
}
|
||||
m_version_combo->Append(label);
|
||||
}
|
||||
|
||||
@@ -11402,7 +11402,23 @@ void Plater::priv::update_plugin_when_launch(wxCommandEvent &event)
|
||||
if (!app_config) return;
|
||||
|
||||
if (result == wxID_OK) {
|
||||
app_config->set("update_network_plugin", "true");
|
||||
// Apply the downloaded update right away and hot-reload the plug-in, the same
|
||||
// way a manual version switch in Preferences behaves. When a file is still in
|
||||
// use and cannot be replaced, fall back to installing on the next launch.
|
||||
bool had_cache = false;
|
||||
if (wxGetApp().install_network_plugin_from_ota(had_cache)) {
|
||||
notification_manager->close_notification_of_type(NotificationType::BBLPluginUpdateAvailable);
|
||||
app_config->set("update_network_plugin", "false");
|
||||
if (wxGetApp().hot_reload_network_plugin()) {
|
||||
MessageDialog dlg_ok(wxGetApp().mainframe, _L("Network plug-in switched successfully."), _L("Success"), wxOK | wxICON_INFORMATION);
|
||||
dlg_ok.ShowModal();
|
||||
} else {
|
||||
MessageDialog dlg_fail(wxGetApp().mainframe, _L("Failed to load network plug-in. Please restart the application."), _L("Restart Required"), wxOK | wxICON_WARNING);
|
||||
dlg_fail.ShowModal();
|
||||
}
|
||||
} else {
|
||||
app_config->set("update_network_plugin", had_cache ? "true" : "false");
|
||||
}
|
||||
}
|
||||
else if (result == wxID_NO) {
|
||||
app_config->set("update_network_plugin", "false");
|
||||
|
||||
@@ -65,6 +65,11 @@ std::string PrePrintChecker::get_print_status_info(PrintDialogStatus status)
|
||||
case PrintStatusHasUnreliableNozzleWarning: return "PrintStatusHasUnreliableNozzleWarning";
|
||||
case PrintStatusWarningExtFilamentNotMatch: return "PrintStatusWarningExtFilamentNotMatch";
|
||||
case PrintStatusFilamentWarningNozzleHRC: return "PrintStatusFilamentWarningNozzleHRC";
|
||||
case PrintStatusTPUUnsupportCaliOn: return "PrintStatusTPUUnsupportCaliOn";
|
||||
case PrintStatusTPUUnsuggestCali: return "PrintStatusTPUUnsuggestCali";
|
||||
case PrintStatusSmartNozzleBlobNeedAuto: return "PrintStatusSmartNozzleBlobNeedAuto";
|
||||
case PrintStatusFilamentWarningRemainNotEnough: return "PrintStatusFilamentWarningRemainNotEnough";
|
||||
case PrintStatusPrintTimeEstimateWarning: return "PrintStatusPrintTimeEstimateWarning";
|
||||
case PrintStatusReadingFinished: return "PrintStatusReadingFinished";
|
||||
case PrintStatusSendingCanceled: return "PrintStatusSendingCanceled";
|
||||
case PrintStatusAmsMappingSuccess: return "PrintStatusAmsMappingSuccess";
|
||||
@@ -103,8 +108,16 @@ wxString PrePrintChecker::get_pre_state_msg(PrintDialogStatus status)
|
||||
case PrintStatusTPUUnsupportAutoCali: return _L("TPU 90A/TPU 85A is too soft and does not support automatic Flow Dynamics calibration.");
|
||||
case PrintStatusWarningKvalueNotUsed: return _L("Set dynamic flow calibration to 'OFF' to enable custom dynamic flow value.");
|
||||
case PrintStatusNotSupportedPrintAll: return _L("This printer does not support printing all plates.");
|
||||
case PrintStatusColorQuantityExceed: return _L("The current firmware supports a maximum of 16 materials. You can either reduce the number of materials to 16 or fewer on the Preparation Page, or try updating the firmware. If you are still restricted after the update, please wait for subsequent firmware support.");
|
||||
// Orca: %s-parameterized so the real per-printer max color count can be filled in by the caller
|
||||
// (SelectMachine formats it via get_pre_state_msg before add()); default templates keep the wording.
|
||||
case PrintStatusColorQuantityExceed: return _L("The current firmware supports a maximum of %s materials. You can either reduce the number of materials to %s or fewer on the Preparation Page, or try updating the firmware. If you are still restricted after the update, please wait for subsequent firmware support.");
|
||||
case PrintStatusWarningExtFilamentNotMatch: return _L("The type of external filament is unknown or does not match with the filament type in the slicing file. Please make sure you have installed the correct filament in the external spool.");
|
||||
// Same text as the blocking "auto" case (PrintStatusTPUUnsupportAutoCali); CaliOn is a
|
||||
// non-blocking advisory (Send stays enabled).
|
||||
case PrintStatusTPUUnsupportCaliOn: return _L("TPU 90A/TPU 85A are too soft. It is recommended to perform manual flow calibration on the 'Calibration' page. If 'Dynamic Flow Calibration' is set to auto/on, the system will use the previous calibration value and skip the flow calibration process.");
|
||||
case PrintStatusFilamentWarningRemainNotEnough: return _L("The filament in the AMS may be insufficient for this print. Please refill or replace it.");
|
||||
// SmartNozzleBlobNeedAuto text is passed via add_with_link (clickable "Switch"), not here.
|
||||
// PrintTimeEstimateWarning has no call site and no message string.
|
||||
}
|
||||
return wxEmptyString;
|
||||
}
|
||||
@@ -162,6 +175,49 @@ void PrePrintChecker::add(PrintDialogStatus state, wxString msg, wxString tip, c
|
||||
}
|
||||
}
|
||||
|
||||
// Orca: minimal callback-link variant of add(): stores an internal click action
|
||||
// (link_callback) rendered as a trailing link instead of the wiki-url launcher.
|
||||
void PrePrintChecker::add_with_link(PrintDialogStatus state, wxString msg, wxString link_label, std::function<void()> link_callback)
|
||||
{
|
||||
prePrintInfo info;
|
||||
|
||||
if (is_error(state)) {
|
||||
info.level = prePrintInfoLevel::Error;
|
||||
} else if (is_warning(state)) {
|
||||
info.level = prePrintInfoLevel::Warning;
|
||||
} else {
|
||||
info.level = prePrintInfoLevel::Normal;
|
||||
}
|
||||
|
||||
if (is_error_printer(state)) {
|
||||
info.type = prePrintInfoType::Printer;
|
||||
} else if (is_error_filament(state)) {
|
||||
info.type = prePrintInfoType::Filament;
|
||||
} else if (is_warning_printer(state)) {
|
||||
info.type = prePrintInfoType::Printer;
|
||||
} else if (is_warning_filament(state)) {
|
||||
info.type = prePrintInfoType::Filament;
|
||||
}
|
||||
|
||||
info.msg = msg;
|
||||
info.link_label = link_label;
|
||||
info.link_callback = link_callback;
|
||||
|
||||
switch (info.type) {
|
||||
case prePrintInfoType::Filament:
|
||||
if (std::find(filamentList.begin(), filamentList.end(), info) == filamentList.end()) {
|
||||
filamentList.push_back(info);
|
||||
}
|
||||
break;
|
||||
case prePrintInfoType::Printer:
|
||||
if (std::find(printerList.begin(), printerList.end(), info) == printerList.end()) {
|
||||
printerList.push_back(info);
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//void PrePrintMsgBoard::add(const wxString &msg, const wxString &tips, bool is_error)
|
||||
//{
|
||||
@@ -230,7 +286,15 @@ bool PrinterMsgPanel::UpdateInfos(const std::vector<prePrintInfo>& infos)
|
||||
label->SetForegroundColour(_GetLabelColour(info));
|
||||
|
||||
|
||||
if (info.wiki_url.empty())
|
||||
if (!info.link_label.empty())
|
||||
{
|
||||
// Orca: internal callback link (e.g. "Clean up files") instead of a wiki url
|
||||
label->SetLabel(info.msg + " " + info.link_label);
|
||||
label->Bind(wxEVT_ENTER_WINDOW, [this](auto& e) { SetCursor(wxCURSOR_HAND); });
|
||||
label->Bind(wxEVT_LEAVE_WINDOW, [this](auto& e) { SetCursor(wxCURSOR_ARROW); });
|
||||
label->Bind(wxEVT_LEFT_DOWN, [info](wxMouseEvent& event) { if (info.link_callback) info.link_callback(); });
|
||||
}
|
||||
else if (info.wiki_url.empty())
|
||||
{
|
||||
label->SetLabel(info.msg);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef slic3r_GUI_PRE_PRINT_CHECK_hpp_
|
||||
#define slic3r_GUI_PRE_PRINT_CHECK_hpp_
|
||||
|
||||
#include <functional>
|
||||
#include <wx/wx.h>
|
||||
#include "Widgets/Label.hpp"
|
||||
namespace Slic3r { namespace GUI {
|
||||
@@ -23,13 +24,17 @@ struct prePrintInfo
|
||||
wxString msg;
|
||||
wxString tips;
|
||||
wxString wiki_url;
|
||||
int index;
|
||||
wxString link_label; // optional: clickable text appended after msg
|
||||
std::function<void()> link_callback; // optional: internal action for link_label click
|
||||
int index{0};
|
||||
|
||||
public:
|
||||
bool operator==(const prePrintInfo& other) const {
|
||||
return level == other.level && type == other.type &&
|
||||
msg == other.msg && tips == other.tips &&
|
||||
wiki_url == other.wiki_url && index == other.index;
|
||||
wiki_url == other.wiki_url && link_label == other.link_label &&
|
||||
index == other.index;
|
||||
// link_callback excluded: std::function is not comparable
|
||||
}
|
||||
};
|
||||
|
||||
@@ -48,6 +53,7 @@ enum PrintDialogStatus : unsigned int {
|
||||
PrintStatusConnecting,
|
||||
PrintStatusReconnecting,
|
||||
PrintStatusInUpgrading,
|
||||
PrintStatusFirmwareNotSupportTpuAtLeft,
|
||||
PrintStatusModeNotFDM,
|
||||
PrintStatusInSystemPrinting,
|
||||
PrintStatusInPrinting,
|
||||
@@ -93,6 +99,7 @@ enum PrintDialogStatus : unsigned int {
|
||||
PrintStatusPrinterWarningBegin,
|
||||
PrintStatusTimelapseNoSdcard,
|
||||
PrintStatusTimelapseWarning,
|
||||
PrintStatusTimelapseStorageLow,
|
||||
PrintStatusMixAmsAndVtSlotWarning,
|
||||
PrintStatusToolHeadCoolingFanWarning,
|
||||
PrintStatusRackNozzleMappingWarning,
|
||||
@@ -111,6 +118,15 @@ enum PrintDialogStatus : unsigned int {
|
||||
PrintStatusFilamentWarningUnknownHighChamberTempSoft,
|
||||
PrintStatusWarningExtFilamentNotMatch,
|
||||
PrintStatusFilamentWarningNozzleHRC,
|
||||
PrintStatusFilamentCrossExtruderWarning,
|
||||
// Non-blocking advisories. FilamentWarningRemainNotEnough is declared but not wired (missing
|
||||
// device-model surface); PrintTimeEstimateWarning has no call site or message and is kept only
|
||||
// to match the reference enum table.
|
||||
PrintStatusTPUUnsupportCaliOn,
|
||||
PrintStatusTPUUnsuggestCali,
|
||||
PrintStatusSmartNozzleBlobNeedAuto,
|
||||
PrintStatusFilamentWarningRemainNotEnough,
|
||||
PrintStatusPrintTimeEstimateWarning,
|
||||
PrintStatusFilamentWarningEnd,
|
||||
|
||||
PrintStatusWarningEnd,//->end error<-
|
||||
@@ -141,6 +157,8 @@ public:
|
||||
void clear();
|
||||
/*auto merge*/
|
||||
void add(PrintDialogStatus state, wxString msg, wxString tip, const wxString& wiki_url);
|
||||
// Orca: minimal callback-link render path instead of the full style-bitmask machinery.
|
||||
void add_with_link(PrintDialogStatus state, wxString msg, wxString link_label, std::function<void()> link_callback);
|
||||
static ::std::string get_print_status_info(PrintDialogStatus status);
|
||||
|
||||
wxString get_pre_state_msg(PrintDialogStatus status);
|
||||
|
||||
@@ -1270,9 +1270,14 @@ wxBoxSizer *PreferencesDialog::create_item_network_plugin_version(wxString title
|
||||
label = wxString::FromUTF8(ver.display_name);
|
||||
}
|
||||
|
||||
// "(Latest)" marks the highest listed version; "(installed)" marks versions
|
||||
// whose library is already on disk. One entry can carry both.
|
||||
if (ver.is_latest) {
|
||||
label += " " + _L("(Latest)");
|
||||
}
|
||||
if (ver.is_installed) {
|
||||
label += " " + _L("(installed)");
|
||||
}
|
||||
m_network_version_combo->Append(label);
|
||||
if (current_version == ver.version) {
|
||||
current_selection = i;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "PrintOptionsDialog.hpp"
|
||||
#include <initializer_list>
|
||||
#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,92 @@ 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
|
||||
int mode_map[] = {2, 1, 0};
|
||||
if (sel < 0 || sel > 2) { evt.Skip(); return; }
|
||||
|
||||
// If currently in Auto (current_detect_value == 2), a print is running, and a loaded
|
||||
// filament is stringing-prone, confirm before enabling — turning blob detection on
|
||||
// mid-print with such filament can degrade quality.
|
||||
const auto* blob_opt = obj->GetPrintOptions()->GetDetectionOption(PrintOptionEnum::Smart_Nozzle_Blob_Detection);
|
||||
const bool was_auto = blob_opt && blob_opt->current_detect_value == 2;
|
||||
if (sel == 1 /*On*/ && was_auto && obj->is_in_printing()
|
||||
&& obj->any_loaded_filament_is_stringing_prone()) {
|
||||
wxString message = _L("There is stringing-prone filament in the current print job. "
|
||||
"Enabling nozzle clumping detection now may degrade print quality. "
|
||||
"Are you sure you want to enable it?");
|
||||
wxString caption = _L("Enable Nozzle Clumping Detection");
|
||||
// Themed warning dialog with Cancel as the highlighted default, so the safe choice (leave
|
||||
// detection off) is the default and Confirm must be actively picked.
|
||||
MessageDialog dialog(this, message, caption, wxICON_WARNING);
|
||||
dialog.AddButton(wxID_CANCEL, _L("Cancel"), true);
|
||||
dialog.AddButton(wxID_OK, _L("Confirm"), false);
|
||||
if (dialog.ShowModal() != wxID_OK) {
|
||||
// User cancelled: roll the switch back to Auto without sending the command.
|
||||
m_smart_nozzle_blob_mode_switch->SetSelection(0);
|
||||
update_smart_nozzle_blob_mode_desc(0);
|
||||
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 +526,44 @@ void PrintOptionsDialog::update_options(MachineObject* obj_)
|
||||
line7->Hide();
|
||||
}
|
||||
|
||||
// Orca: firmware print-option toggles, each shown only when its fun2 capability bit is
|
||||
// reported. A printer reporting none of these bits renders the dialog as before.
|
||||
auto* print_opts = obj_->GetPrintOptions();
|
||||
auto show_row = [](bool support, std::initializer_list<wxWindow*> 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 +619,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 +685,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);
|
||||
@@ -875,7 +1101,7 @@ wxBoxSizer* PrintOptionsDialog::create_settings_group(wxWindow* parent)
|
||||
line_sizer->Add(FromDIP(5), 0, 0, 0);
|
||||
line_sizer->Add(m_cb_save_remote_print_file_to_storage, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5));
|
||||
line_sizer->Add(text_save_remote_print_file_to_storage, 1, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5));
|
||||
text_save_remote_print_file_to_storage_explain = new Label(parent, _L("Save the printing files initiated from Bambu Studio, Bambu Handy and MakerWorld on External Storage"));
|
||||
text_save_remote_print_file_to_storage_explain = new Label(parent, _L("Save the printing files sent from the slicer and other apps on External Storage")); // Orca: neutral wording (vendor app names removed)
|
||||
text_save_remote_print_file_to_storage_explain->SetForegroundColour(STATIC_TEXT_EXPLAIN_COL);
|
||||
text_save_remote_print_file_to_storage_explain->SetFont(Label::Body_12);
|
||||
text_save_remote_print_file_to_storage_explain->Wrap(FromDIP(400));
|
||||
@@ -951,6 +1177,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 +1311,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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user