mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 19:01:02 +00:00
feat(gui): multi-nozzle UI for H2C/A2L
Multi-nozzle sync widget, AMS rack-nozzle mapping popup, calibration rework, send-dialog nozzle mapping and extruder-count UI. Includes the fix to persist the AMS sync badge on filament cards (H2C/A2L and direct-sync printers).
This commit is contained in:
@@ -494,6 +494,8 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/Widgets/AMSControl.hpp
|
||||
GUI/Widgets/AMSItem.cpp
|
||||
GUI/Widgets/AMSItem.hpp
|
||||
GUI/Widgets/MultiNozzleSync.cpp
|
||||
GUI/Widgets/MultiNozzleSync.hpp
|
||||
GUI/Widgets/AxisCtrlButton.cpp
|
||||
GUI/Widgets/AxisCtrlButton.hpp
|
||||
GUI/SafetyOptionsDialog.hpp
|
||||
|
||||
@@ -1024,7 +1024,8 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type,
|
||||
const Transform3d& projection_matrix,
|
||||
const GUI::Size& cnv_size,
|
||||
std::function<bool(const GLVolume &)> filter_func,
|
||||
bool partly_inside_enable) const
|
||||
bool partly_inside_enable,
|
||||
std::vector<double> * printable_heights) const
|
||||
{
|
||||
GLVolumeWithIdAndZList to_render = volumes_to_render(volumes, type, view_matrix, filter_func);
|
||||
if (to_render.empty())
|
||||
@@ -1108,7 +1109,24 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type,
|
||||
//use -1 ad a invalid type
|
||||
shader->set_uniform("print_volume.type", -1);
|
||||
}
|
||||
|
||||
|
||||
// Per-extruder printable-height shading. The flag is set to
|
||||
// 2.0 only for multi-extruder printers (two per-extruder heights); otherwise it is forced to 0.0
|
||||
// on every render so no stale flag survives a multi->single-extruder plate switch, keeping the
|
||||
// shared gouraud shader pixel-identical for single-extruder printers. When active the height
|
||||
// branch reads print_volume.xy_data (the bed rect), so set it explicitly here.
|
||||
std::array<float, 3> extruder_printable_heights = {0.0f, 0.0f, 0.0f};
|
||||
if (printable_heights != nullptr && printable_heights->size() > 1) {
|
||||
extruder_printable_heights[0] = 2.0f;
|
||||
extruder_printable_heights[1] = static_cast<float>((*printable_heights)[0]);
|
||||
extruder_printable_heights[2] = static_cast<float>((*printable_heights)[1]);
|
||||
shader->set_uniform("extruder_printable_heights", extruder_printable_heights);
|
||||
shader->set_uniform("print_volume.xy_data", m_print_volume.data);
|
||||
}
|
||||
else {
|
||||
shader->set_uniform("extruder_printable_heights", extruder_printable_heights);
|
||||
}
|
||||
|
||||
shader->set_uniform("volume_world_matrix", volume.first->world_matrix());
|
||||
shader->set_uniform("slope.actived", m_slope.isGlobalActive && !volume.first->is_modifier && !volume.first->is_wipe_tower);
|
||||
shader->set_uniform("slope.volume_world_normal_matrix", static_cast<Matrix3f>(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>()));
|
||||
|
||||
@@ -497,7 +497,10 @@ public:
|
||||
const Transform3d& projection_matrix,
|
||||
const GUI::Size& cnv_size,
|
||||
std::function<bool(const GLVolume &)> filter_func = std::function<bool(const GLVolume &)>(),
|
||||
bool partly_inside_enable =true
|
||||
bool partly_inside_enable =true,
|
||||
// Per-extruder printable heights (extruder_printable_height); null / size<=1
|
||||
// leaves the shader's extruder_printable_heights flag at 0.0 (single-extruder = inert).
|
||||
std::vector<double> * printable_heights = nullptr
|
||||
) const;
|
||||
|
||||
// Clear the geometry
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
#include "DeviceCore/DevExtruderSystem.h"
|
||||
#include "DeviceCore/DevFilaBlackList.h"
|
||||
#include "DeviceCore/DevFilaSystem.h"
|
||||
#include "DeviceCore/DevFilaSwitch.h"
|
||||
#include "DeviceCore/DevNozzleSystem.h" // DevNozzle / GetNozzleByPosId / GetNozzleRack
|
||||
#include "DeviceCore/DevNozzleRack.h" // DevNozzleRack::IsSupported (rack gating)
|
||||
|
||||
#define FILAMENT_MAX_TEMP 300
|
||||
#define FILAMENT_MIN_TEMP 120
|
||||
@@ -531,56 +534,121 @@ void AMSMaterialsSetting::on_select_reset(wxCommandEvent& event) {
|
||||
Close();
|
||||
}
|
||||
|
||||
// Nozzle-context builder for the AMS-edit blacklist check. Factors the blacklist evaluation out of
|
||||
// on_select_ok and threads rack-gated per-nozzle context (extruder id + nozzle flow + nozzle diameter)
|
||||
// into CheckFilamentInfo, returning the accumulate-all CheckResult. As a side effect it resolves
|
||||
// ams_filament_id/ams_setting_id from the matched preset. fila_name is kept as the preset name (not
|
||||
// the short alias) so the name / name_suffix rules match on the non-rack fleet (no unintended
|
||||
// activation of e.g. the PLA Glow rule).
|
||||
static DevFilaBlacklist::CheckResult
|
||||
sCheckFilamentInfo(PresetBundle* preset_bundle,
|
||||
MachineObject* obj,
|
||||
int ams_id,
|
||||
int slot_id,
|
||||
const std::string& filament_id,
|
||||
std::string& ams_filament_id,
|
||||
std::string& ams_setting_id)
|
||||
{
|
||||
DevFilaBlacklist::CheckResult result;
|
||||
if (!preset_bundle || !obj)
|
||||
return result;
|
||||
|
||||
// Orca: there is no lookup struct that also carries setting_id, so resolve the (root) preset by
|
||||
// scanning the filaments collection for a matching filament_id.
|
||||
Preset* fila_preset = nullptr;
|
||||
for (auto it = preset_bundle->filaments.begin(); it != preset_bundle->filaments.end(); ++it) {
|
||||
if (it->filament_id == filament_id) {
|
||||
fila_preset = &(*it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!fila_preset)
|
||||
return result;
|
||||
|
||||
if (wxGetApp().app_config->get("skip_ams_blacklist_check") != "true") {
|
||||
std::string filamnt_type;
|
||||
fila_preset->get_filament_type(filamnt_type);
|
||||
|
||||
DevFilaBlacklist::CheckFilamentInfo check_info;
|
||||
check_info.dev_id = obj->get_dev_id();
|
||||
check_info.model_id = obj->printer_type;
|
||||
check_info.fila_id = fila_preset->filament_id;
|
||||
check_info.fila_type = filamnt_type;
|
||||
check_info.fila_name = fila_preset->name;
|
||||
check_info.has_filament_switch = obj->GetFilaSwitch()->IsInstalled();
|
||||
check_info.ams_id = ams_id;
|
||||
check_info.slot_id = slot_id;
|
||||
|
||||
if (auto vendor = dynamic_cast<ConfigOptionStrings*>(fila_preset->config.option("filament_vendor"));
|
||||
vendor && !vendor->values.empty()) {
|
||||
check_info.fila_vendor = vendor->values[0];
|
||||
}
|
||||
|
||||
check_info.extruder_id = obj->GetFilaSystem()->GetExtruderIdByAmsId(std::to_string(ams_id));
|
||||
|
||||
// Rack-gated per-nozzle context. For a multi-nozzle main extruder on a rack printer (H2C) the
|
||||
// specific nozzle is resolved later by the print-dispatch rack mapping, so leave
|
||||
// nozzle_flow/nozzle_diameter unset here to keep the high-flow nozzle rules dormant. Every
|
||||
// non-rack printer takes the else branch, threading the reported nozzle flow + diameter and
|
||||
// thereby activating the high-flow blacklist warnings in the AMS-edit dialog for H2D/H2S/X2D/P2S
|
||||
// (and the E3D-kit X1C/P1x).
|
||||
DevNozzleSystem* nozzle_system = obj->GetNozzleSystem();
|
||||
const bool rack_supported = nozzle_system && nozzle_system->GetNozzleRack() &&
|
||||
nozzle_system->GetNozzleRack()->IsSupported();
|
||||
if (check_info.extruder_id == MAIN_EXTRUDER_ID && rack_supported) {
|
||||
; // multi-nozzle main extruder on a rack printer — nozzle resolved later by print dispatch
|
||||
} else {
|
||||
check_info.nozzle_flow = obj->GetFilaSystem()->GetNozzleFlowStringByAmsId(std::to_string(ams_id));
|
||||
if (nozzle_system) {
|
||||
DevNozzle nozzle = nozzle_system->GetNozzleByPosId(check_info.extruder_id.value_or(-1));
|
||||
if (!nozzle.IsEmpty())
|
||||
check_info.nozzle_diameter = nozzle.GetNozzleDiameter();
|
||||
}
|
||||
}
|
||||
|
||||
result = DevFilaBlacklist::check_filaments_in_blacklist(check_info);
|
||||
}
|
||||
|
||||
ams_filament_id = fila_preset->filament_id;
|
||||
ams_setting_id = fila_preset->setting_id;
|
||||
return result;
|
||||
}
|
||||
|
||||
void AMSMaterialsSetting::on_select_ok(wxCommandEvent &event)
|
||||
{
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
//get filament id
|
||||
ams_filament_id = "";
|
||||
ams_setting_id = "";
|
||||
|
||||
// the combobox item
|
||||
auto filament_item = map_filament_items[m_comboBox_filament->GetValue().ToStdString()];
|
||||
|
||||
// check filament info (rack-gated per-nozzle blacklist evaluation)
|
||||
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
|
||||
if (preset_bundle) {
|
||||
for (auto it = preset_bundle->filaments.begin(); it != preset_bundle->filaments.end(); it++) {
|
||||
const auto& fila_check_res = sCheckFilamentInfo(preset_bundle, obj, ams_id, slot_id,
|
||||
filament_item.filament_id, ams_filament_id, ams_setting_id);
|
||||
|
||||
auto filament_item = map_filament_items[m_comboBox_filament->GetValue().ToStdString()];
|
||||
std::string filament_id = filament_item.filament_id;
|
||||
if (it->filament_id.compare(filament_id) == 0) {
|
||||
if (const auto& prohibit_items = fila_check_res.get_items_by_action("prohibition"); !prohibit_items.empty()) {
|
||||
wxString info_msg;
|
||||
for (const auto& item : prohibit_items) { info_msg += item.info_msg + "\n"; }
|
||||
MessageDialog msg_wingow(nullptr, info_msg, _L("Error"), wxICON_WARNING | wxOK);
|
||||
msg_wingow.ShowModal();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//check is it in the filament blacklist
|
||||
if (wxGetApp().app_config->get("skip_ams_blacklist_check") != "true") {
|
||||
bool in_blacklist = false;
|
||||
std::string action;
|
||||
wxString info;
|
||||
std::string filamnt_type;
|
||||
std::string filamnt_name;
|
||||
it->get_filament_type(filamnt_type);
|
||||
|
||||
auto vendor = dynamic_cast<ConfigOptionStrings *>(it->config.option("filament_vendor"));
|
||||
|
||||
if (vendor && (vendor->values.size() > 0)) {
|
||||
std::string vendor_name = vendor->values[0];
|
||||
DevFilaBlacklist::check_filaments_in_blacklist(obj->printer_type, vendor_name, filamnt_type, it->filament_id, ams_id, slot_id, it->name, in_blacklist, action, info);
|
||||
}
|
||||
|
||||
if (in_blacklist) {
|
||||
if (action == "prohibition") {
|
||||
MessageDialog msg_wingow(nullptr, info, _L("Error"), wxICON_WARNING | wxOK);
|
||||
msg_wingow.ShowModal();
|
||||
//m_comboBox_filament->SetSelection(m_filament_selection);
|
||||
return;
|
||||
}
|
||||
else if (action == "warning") {
|
||||
MessageDialog msg_wingow(nullptr, info, _L("Warning"), wxICON_INFORMATION | wxOK);
|
||||
msg_wingow.ShowModal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ams_filament_id = it->filament_id;
|
||||
ams_setting_id = it->setting_id;
|
||||
break;
|
||||
}
|
||||
if (const auto& warning_items = fila_check_res.get_items_by_action("warning"); !warning_items.empty()) {
|
||||
std::vector<FilamentWarningInfo> infos;
|
||||
for (const auto& item : warning_items) {
|
||||
FilamentWarningInfo winfo;
|
||||
winfo.info_msg = item.info_msg;
|
||||
winfo.wiki_url = item.wiki_url;
|
||||
infos.emplace_back(winfo);
|
||||
}
|
||||
FilamentWarningDialog msg_window(nullptr, _L("Warning"), infos);
|
||||
msg_window.ShowModal();
|
||||
}
|
||||
|
||||
wxString nozzle_temp_min = m_input_nozzle_min->GetTextCtrl()->GetValue();
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
#include "BindDialog.hpp"
|
||||
|
||||
#include "DeviceCore/DevFilaSystem.h"
|
||||
#include "DeviceCore/DevNozzleRack.h"
|
||||
#include "DeviceCore/DevMappingNozzle.h"
|
||||
#include "DeviceTab/wgtDeviceNozzleSelect.h"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
#define MATERIAL_ITEM_SIZE wxSize(FromDIP(65), FromDIP(50))
|
||||
@@ -64,6 +67,7 @@ static void _add_containers(const AmsMapingPopup * win,
|
||||
//m_ams_wheel_mitem = ScalableBitmap(this, "ams_wheel", FromDIP(25));
|
||||
m_ams_wheel_mitem = ScalableBitmap(this, "ams_wheel_narrow", 25);
|
||||
m_ams_not_match = ScalableBitmap(this, "filament_not_mactch", 25);
|
||||
m_rack_nozzle_bitmap = ScalableBitmap(this, "dev_rack_nozzle_print_job", 22);
|
||||
|
||||
m_material_coloul = mcolour;
|
||||
m_material_name = mname;
|
||||
@@ -73,9 +77,7 @@ static void _add_containers(const AmsMapingPopup * win,
|
||||
SetDoubleBuffered(true);
|
||||
#endif //__WINDOWS__
|
||||
|
||||
SetSize(MATERIAL_ITEM_SIZE);
|
||||
SetMinSize(MATERIAL_ITEM_SIZE);
|
||||
SetMaxSize(MATERIAL_ITEM_SIZE);
|
||||
messure_size();
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
|
||||
Bind(wxEVT_PAINT, &MaterialItem::paintEvent, this);
|
||||
@@ -89,6 +91,7 @@ void MaterialItem::msw_rescale() {
|
||||
m_arraw_bitmap_white.msw_rescale();
|
||||
m_transparent_mitem.msw_rescale();
|
||||
m_ams_wheel_mitem.msw_rescale();
|
||||
m_rack_nozzle_bitmap.msw_rescale();
|
||||
}
|
||||
|
||||
void MaterialItem::allow_paint_dropdown(bool flag) {
|
||||
@@ -114,11 +117,22 @@ void MaterialItem::set_ams_info(wxColour col, wxString txt, int ctype, std::vect
|
||||
BOOST_LOG_TRIVIAL(info) << "set_ams_info " << m_ams_name;
|
||||
}
|
||||
|
||||
void MaterialItem::set_nozzle_info(const wxString& mapped_nozzle_str)
|
||||
{
|
||||
if (m_mapped_nozzle_str != mapped_nozzle_str) {
|
||||
m_mapped_nozzle_str = mapped_nozzle_str;
|
||||
messure_size(); // a mapped nozzle adds/removes a row, changing the card height
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialItem::reset_ams_info() {
|
||||
m_ams_name = "-";
|
||||
m_ams_coloul = wxColour(0xCE, 0xCE, 0xCE);
|
||||
m_ams_cols.clear();
|
||||
m_ams_ctype = 0;
|
||||
m_mapped_nozzle_str.Clear();
|
||||
messure_size();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
@@ -215,6 +229,11 @@ void MaterialItem::render(wxDC &dc)
|
||||
acolor = wxColour(0x90, 0x90, 0x90);
|
||||
}
|
||||
|
||||
// Header + AMS name keep their fixed-card positions; a mapped nozzle only adds a row below, so
|
||||
// position this text against the base card height, not the grown one (else it slides down onto
|
||||
// the nozzle row). Identical to GetSize().y when no nozzle row is present.
|
||||
const float base_y = m_mapped_nozzle_str.IsEmpty() ? (float) GetSize().y : (float) MATERIAL_ITEM_SIZE.y;
|
||||
|
||||
// materials name
|
||||
dc.SetFont(::Label::Body_13);
|
||||
|
||||
@@ -227,7 +246,7 @@ void MaterialItem::render(wxDC &dc)
|
||||
}
|
||||
|
||||
auto material_txt_size = dc.GetTextExtent(m_material_name);
|
||||
dc.DrawText(m_material_name, wxPoint((GetSize().x - material_txt_size.x) / 2, ((float)GetSize().y * 2 / 5 - material_txt_size.y) / 2));
|
||||
dc.DrawText(m_material_name, wxPoint((GetSize().x - material_txt_size.x) / 2, (base_y * 2 / 5 - material_txt_size.y) / 2));
|
||||
|
||||
|
||||
|
||||
@@ -238,11 +257,11 @@ void MaterialItem::render(wxDC &dc)
|
||||
if (mapping_txt.size() >= 4) {
|
||||
mapping_txt.insert(mapping_txt.size() / 2, "\n");
|
||||
mapping_txt_size = dc.GetTextExtent(mapping_txt);
|
||||
m_text_pos_y = ((float) GetSize().y * 3 / 5 - mapping_txt_size.y) / 2 + (float) GetSize().y * 2 / 5 - mapping_txt_size.y / 2;
|
||||
m_text_pos_y = (base_y * 3 / 5 - mapping_txt_size.y) / 2 + base_y * 2 / 5 - mapping_txt_size.y / 2;
|
||||
m_text_pos_x = mapping_txt_size.x / 4;
|
||||
} else {
|
||||
mapping_txt_size = dc.GetTextExtent(mapping_txt);
|
||||
m_text_pos_y = ((float) GetSize().y * 3 / 5 - mapping_txt_size.y) / 2 + (float) GetSize().y * 2 / 5;
|
||||
m_text_pos_y = (base_y * 3 / 5 - mapping_txt_size.y) / 2 + base_y * 2 / 5;
|
||||
m_text_pos_x = 0;
|
||||
}
|
||||
|
||||
@@ -258,12 +277,73 @@ void MaterialItem::match(bool mat)
|
||||
Refresh();
|
||||
}
|
||||
|
||||
// A filament can map to several rack nozzles; the joined "R1, R2, ..." string is wrapped to a few
|
||||
// slots per line so the label stays readable inside the narrow card.
|
||||
static constexpr size_t MAPPED_NOZZLE_STR_MAX_CHARS_PER_LINE = 7;
|
||||
|
||||
static wxString s_wrap_mapped_nozzle_str(const wxString& text)
|
||||
{
|
||||
if (text.length() <= MAPPED_NOZZLE_STR_MAX_CHARS_PER_LINE)
|
||||
return text;
|
||||
|
||||
wxArrayString parts = wxSplit(text, ',');
|
||||
wxString wrapped;
|
||||
std::vector<wxString> buffers;
|
||||
for (auto& part : parts) {
|
||||
if (!part.empty())
|
||||
buffers.push_back(part.Trim(false).Trim(true));
|
||||
|
||||
if (buffers.size() > 2) {
|
||||
if (!wrapped.empty())
|
||||
wrapped += "\n";
|
||||
wrapped += buffers[0] + " " + buffers[1] + " " + buffers[2];
|
||||
buffers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (!wrapped.empty())
|
||||
wrapped += "\n";
|
||||
if (buffers.size() > 0)
|
||||
wrapped += buffers[0];
|
||||
if (buffers.size() > 1)
|
||||
wrapped += " " + buffers[1];
|
||||
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
static int s_get_mapped_nozzle_str_line_count(const wxString& text)
|
||||
{
|
||||
if (text.empty())
|
||||
return 0;
|
||||
return std::max(1, static_cast<int>(wxSplit(s_wrap_mapped_nozzle_str(text), '\n', '\0').size()));
|
||||
}
|
||||
|
||||
void MaterialItem::messure_size()
|
||||
{
|
||||
// Without a mapped nozzle the card is a fixed swatch + AMS wheel; a mapped nozzle adds a row
|
||||
// below (glyph + slot label), growing the height by one text line per wrapped line.
|
||||
if (m_mapped_nozzle_str.empty()) {
|
||||
SetSize(MATERIAL_ITEM_SIZE);
|
||||
SetMinSize(MATERIAL_ITEM_SIZE);
|
||||
SetMaxSize(MATERIAL_ITEM_SIZE);
|
||||
} else {
|
||||
const int line_count = s_get_mapped_nozzle_str_line_count(m_mapped_nozzle_str);
|
||||
const wxSize item_size(MATERIAL_ITEM_SIZE.x, FromDIP(84 + std::max(0, line_count - 1) * 10));
|
||||
SetSize(item_size);
|
||||
SetMinSize(item_size);
|
||||
SetMaxSize(item_size);
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialItem::doRender(wxDC& dc)
|
||||
{
|
||||
wxSize size = GetSize();
|
||||
auto mcolor = m_material_coloul;
|
||||
auto acolor = m_ams_coloul;
|
||||
change_the_opacity(acolor);
|
||||
// Header + wheel keep their fixed-card geometry; when a nozzle row is present the extra height
|
||||
// below is used for it, so lay out the top of the card against the base height, not the grown one.
|
||||
const int base_y = m_mapped_nozzle_str.IsEmpty() ? size.y : MATERIAL_ITEM_SIZE.y;
|
||||
|
||||
if (mcolor.Alpha() == 0) {
|
||||
dc.DrawBitmap(m_transparent_mitem.bmp(), FromDIP(1), FromDIP(1));
|
||||
@@ -288,7 +368,7 @@ void MaterialItem::doRender(wxDC& dc)
|
||||
|
||||
//bottom rectangle in wheel bitmap, size is MATERIAL_REC_WHEEL_SIZE(22)
|
||||
auto left = (size.x / 2 - MATERIAL_REC_WHEEL_SIZE.x) / 2 + FromDIP(3);
|
||||
auto up = (size.y * 0.4 + (size.y * 0.6 - MATERIAL_REC_WHEEL_SIZE.y) / 2);
|
||||
auto up = (base_y * 0.4 + (base_y * 0.6 - MATERIAL_REC_WHEEL_SIZE.y) / 2);
|
||||
auto right = left + MATERIAL_REC_WHEEL_SIZE.x - FromDIP(3);
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
//bottom
|
||||
@@ -350,12 +430,12 @@ void MaterialItem::doRender(wxDC& dc)
|
||||
}
|
||||
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.DrawRoundedRectangle(FromDIP(0), FromDIP(0), MATERIAL_ITEM_SIZE.x - FromDIP(0), MATERIAL_ITEM_SIZE.y - FromDIP(0), 5);
|
||||
dc.DrawRoundedRectangle(FromDIP(0), FromDIP(0), MATERIAL_ITEM_SIZE.x - FromDIP(0), size.y - FromDIP(0), 5);
|
||||
|
||||
if (m_selected) {
|
||||
dc.SetPen(wxPen(AMS_CONTROL_BRAND_COLOUR, FromDIP(2)));
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.DrawRoundedRectangle(FromDIP(1), FromDIP(1), MATERIAL_ITEM_SIZE.x - FromDIP(1), MATERIAL_ITEM_SIZE.y - FromDIP(1), 5);
|
||||
dc.DrawRoundedRectangle(FromDIP(1), FromDIP(1), MATERIAL_ITEM_SIZE.x - FromDIP(1), size.y - FromDIP(1), 5);
|
||||
}
|
||||
//#endif
|
||||
if (m_text_pos_y > 0 && m_match) {
|
||||
@@ -368,7 +448,7 @@ void MaterialItem::doRender(wxDC& dc)
|
||||
}
|
||||
|
||||
auto wheel_left = (GetSize().x / 2 - m_ams_wheel_mitem.GetBmpSize().x) / 2 + FromDIP(2);
|
||||
auto wheel_top = ((float)GetSize().y * 0.6 - m_ams_wheel_mitem.GetBmpSize().y) / 2 + (float)GetSize().y * 0.4;
|
||||
auto wheel_top = ((float)base_y * 0.6 - m_ams_wheel_mitem.GetBmpSize().y) / 2 + (float)base_y * 0.4;
|
||||
|
||||
if (!m_match) {
|
||||
wheel_left += m_ams_wheel_mitem.GetBmpSize().x;
|
||||
@@ -380,6 +460,29 @@ void MaterialItem::doRender(wxDC& dc)
|
||||
dc.DrawBitmap(m_ams_wheel_mitem.bmp(), wheel_left - FromDIP(LEFT_OFFSET), wheel_top);
|
||||
}
|
||||
}
|
||||
|
||||
// Rack / filament-switcher printers: draw the physical nozzle the print-dispatch mapping assigned
|
||||
// to this filament — a divider, the rack-nozzle glyph, and the slot label ("R1", "L R", ...) in
|
||||
// the extra row below the wheel. Nothing is drawn when no nozzle is mapped (all other printers).
|
||||
if (!m_mapped_nozzle_str.IsEmpty()) {
|
||||
int row_top = wheel_top + m_ams_wheel_mitem.GetBmpSize().y + FromDIP(4);
|
||||
dc.SetPen(wxPen(wxColour("#CECECE"), 1, wxPENSTYLE_SHORT_DASH)); // Orca: WXCOLOUR_GREY400 macro not in scope here
|
||||
dc.DrawLine(FromDIP(1), row_top, FromDIP(size.x), row_top);
|
||||
|
||||
int bitmap_y = row_top + (size.y - row_top - m_rack_nozzle_bitmap.GetBmpHeight()) / 2;
|
||||
int bitmap_l = wheel_left + (m_ams_wheel_mitem.GetBmpWidth() - m_rack_nozzle_bitmap.GetBmpWidth()) / 2 + FromDIP(2);
|
||||
dc.DrawBitmap(m_rack_nozzle_bitmap.bmp(), bitmap_l, bitmap_y);
|
||||
|
||||
// Orca: BBS width-fits this label via a get_suitable_font_size overload Orca lacks; the wrapped
|
||||
// slot string is short, so a fixed label font renders it cleanly without that helper.
|
||||
const wxString wrapped = s_wrap_mapped_nozzle_str(m_mapped_nozzle_str);
|
||||
dc.SetFont(::Label::Head_12);
|
||||
dc.SetTextForeground(StateColor::darkModeColorFor(wxColour(0x26, 0x2E, 0x30)));
|
||||
const wxSize text_size = dc.GetMultiLineTextExtent(wrapped);
|
||||
int text_x = (size.x + bitmap_l + m_rack_nozzle_bitmap.GetBmpWidth() - text_size.x) / 2;
|
||||
int text_y = row_top + (size.y - row_top - text_size.y) / 2;
|
||||
dc.DrawText(wrapped, text_x, text_y);
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialItem::reset_valid_info() {
|
||||
@@ -725,6 +828,16 @@ AmsMapingPopup::AmsMapingPopup(wxWindow *parent, bool use_in_sync_dialog) :
|
||||
m_right_first_text_panel->SetMaxSize(wxSize(-1, FromDIP(same_height)));
|
||||
|
||||
m_sizer_ams_right->Add(m_right_first_text_panel, 0, wxEXPAND | wxBOTTOM | wxTOP, FromDIP(8));
|
||||
|
||||
// Flush-waste warning (rack printers only). Orca: uses a plain Label here.
|
||||
// Hidden by default => zero layout effect for non-rack printers.
|
||||
m_flush_warning_panel = new Label(m_right_marea_panel);
|
||||
m_flush_warning_panel->SetFont(::Label::Body_12);
|
||||
m_flush_warning_panel->SetForegroundColour(StateColor::darkModeColorFor("#F09A17"));
|
||||
m_flush_warning_panel->SetBackgroundColour(StateColor::darkModeColorFor("#FFFFE0"));
|
||||
m_flush_warning_panel->Show(false);
|
||||
m_sizer_ams_right->Add(m_flush_warning_panel, 0, wxBOTTOM | wxALIGN_LEFT, FromDIP(8));
|
||||
|
||||
m_right_split_ams_sizer = create_split_sizer(m_right_marea_panel, _L("Right AMS"));
|
||||
m_sizer_ams_right->Add(m_right_split_ams_sizer, 0, wxEXPAND, 0);
|
||||
m_sizer_ams_right->Add(m_sizer_ams_basket_right, 0, wxEXPAND|wxTOP, FromDIP(8));
|
||||
@@ -740,6 +853,14 @@ AmsMapingPopup::AmsMapingPopup(wxWindow *parent, bool use_in_sync_dialog) :
|
||||
m_sizer_ams->Add(0, 0, 0, wxEXPAND, FromDIP(15));
|
||||
m_sizer_ams->Add(m_right_marea_panel, 1, wxEXPAND, FromDIP(0));
|
||||
|
||||
// Rack nozzle manual-pick, alongside the AMS area (rack printers only). Hidden by default
|
||||
// => contributes nothing to layout for non-rack printers.
|
||||
m_rack_nozzle_select = new wgtDeviceNozzleRackSelect(this);
|
||||
m_rack_nozzle_select->Bind(EVT_NOZZLE_SELECT_CHANGED, &AmsMapingPopup::OnNozzleMappingSelected, this);
|
||||
m_rack_nozzle_select->Bind(EVT_NOZZLE_SELECT_CLICKED, [this](wxCommandEvent& e) { this->Dismiss(); });
|
||||
m_rack_nozzle_select->Show(false);
|
||||
m_sizer_ams->Add(m_rack_nozzle_select, 0, wxEXPAND | wxTOP | wxLEFT, FromDIP(15));
|
||||
|
||||
m_sizer_main->Add(title_panel, 0, wxEXPAND | wxALL, FromDIP(2));
|
||||
m_sizer_main->Add(m_sizer_ams, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(14));
|
||||
m_sizer_main->Add( 0, 0, 0, wxTOP, FromDIP(14));
|
||||
@@ -782,6 +903,7 @@ void AmsMapingPopup::msw_rescale()
|
||||
m_right_extra_slot->msw_rescale();
|
||||
for (auto item : m_mapping_item_list) { item->msw_rescale(); }
|
||||
for (auto container : m_amsmapping_container_list) { container->msw_rescale(); }
|
||||
if (m_rack_nozzle_select) { m_rack_nozzle_select->Rescale(); }
|
||||
|
||||
Fit();
|
||||
Refresh();
|
||||
@@ -1038,7 +1160,7 @@ void AmsMapingPopup::update_items_check_state(const std::vector<FilamentInfo>& a
|
||||
}
|
||||
}
|
||||
|
||||
void AmsMapingPopup::update(MachineObject* obj, const std::vector<FilamentInfo>& ams_mapping_result)
|
||||
void AmsMapingPopup::update(MachineObject* obj, const std::vector<FilamentInfo>& ams_mapping_result, bool use_dynamic_switch, std::optional<PrintFromType> print_type)
|
||||
{
|
||||
//BOOST_LOG_TRIVIAL(info) << "ams_mapping nozzle count " << obj->get_extder_system()->nozzle.size();
|
||||
BOOST_LOG_TRIVIAL(info) << "ams_mapping total count " << obj->GetFilaSystem()->GetAmsCount();
|
||||
@@ -1252,11 +1374,68 @@ void AmsMapingPopup::update(MachineObject* obj, const std::vector<FilamentInfo>&
|
||||
} else {
|
||||
m_right_split_ams_sizer->Show(false);
|
||||
}
|
||||
|
||||
/*rack (manual nozzle pick; rack printers only)*/
|
||||
update_rack_select(obj, use_dynamic_switch, print_type);
|
||||
update_flush_waste(obj);
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
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_flush_waste(MachineObject* obj)
|
||||
{
|
||||
if (!obj) {
|
||||
m_flush_warning_panel->Hide();
|
||||
return;
|
||||
};
|
||||
|
||||
float flush_waste_base = obj->get_nozzle_mapping_result()->GetFlushWeightBase();
|
||||
float flush_waste_current = obj->get_nozzle_mapping_result()->GetFlushWeightCurrent();
|
||||
if ((flush_waste_base != -1) && (flush_waste_current != -1) && flush_waste_current > flush_waste_base) {
|
||||
m_flush_warning_panel->SetLabel(wxString::Format(_L("Printing with the current nozzle may produce an extra %0.2f g of waste."), flush_waste_current - flush_waste_base));
|
||||
m_flush_warning_panel->Show();
|
||||
} else {
|
||||
m_flush_warning_panel->Hide();
|
||||
}
|
||||
}
|
||||
|
||||
void AmsMapingPopup::OnNozzleMappingSelected(wxCommandEvent& evt)
|
||||
{
|
||||
if (auto ptr = m_rack.lock()) {
|
||||
MachineObject* obj = ptr->GetNozzleSystem()->GetOwner();
|
||||
obj->get_nozzle_mapping_result()->SetManualNozzleMappingByFila(m_current_filament_id, m_rack_nozzle_select->GetSelectedNozzlePosID());
|
||||
update_flush_waste(obj);
|
||||
}
|
||||
|
||||
evt.Skip();
|
||||
Dismiss();
|
||||
}
|
||||
|
||||
std::vector<TrayData> AmsMapingPopup::parse_ams_mapping(const std::map<std::string, DevAms*, NumericStrCompare>& amsList)
|
||||
{
|
||||
std::vector<TrayData> m_tray_data;
|
||||
@@ -2305,12 +2484,25 @@ _GetBackupStatus(unsigned int fila_back_group)
|
||||
|
||||
for (int j = 16; j < 32; j++)/* single ams is from 128*/
|
||||
{
|
||||
// N9 / AMS-Lite-mixed reports its four backup slots in bits 24-27, which decode directly to
|
||||
// tray ids 24-27 (handled in the loop below). Keep those bits out of the single-ams (128+)
|
||||
// range; all other bits keep their existing mapping unchanged.
|
||||
if (j >= 24 && j <= 27)
|
||||
continue;
|
||||
if (fila_back_group & (1 << j))
|
||||
{
|
||||
trayid_group[128 + j - 16] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 24; i <= 27; i++)/* ams lite for N9 */
|
||||
{
|
||||
if (fila_back_group & (1 << i))
|
||||
{
|
||||
trayid_group[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return trayid_group;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,8 +41,15 @@
|
||||
|
||||
#include "slic3r/GUI/DeviceCore/DevUtil.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#define MAPPING_ITEM_INVALID_REMAIN -1
|
||||
|
||||
namespace Slic3r {
|
||||
class DevNozzleRack;
|
||||
namespace GUI { class wgtDeviceNozzleRackSelect; }
|
||||
}
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
|
||||
@@ -93,6 +100,9 @@ public:
|
||||
//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
|
||||
@@ -107,6 +117,7 @@ public:
|
||||
ScalableBitmap m_filament_wheel_transparent;
|
||||
ScalableBitmap m_ams_wheel_mitem;
|
||||
ScalableBitmap m_ams_not_match;
|
||||
ScalableBitmap m_rack_nozzle_bitmap;
|
||||
|
||||
bool m_selected {false};
|
||||
bool m_warning{false};
|
||||
@@ -117,6 +128,10 @@ public:
|
||||
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();
|
||||
@@ -259,12 +274,19 @@ public:
|
||||
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;
|
||||
|
||||
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;};
|
||||
void update_materials_list(std::vector<std::string> list);
|
||||
void set_tag_texture(std::string texture);
|
||||
void update(MachineObject* obj, const std::vector<FilamentInfo>& ams_mapping_result);
|
||||
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();
|
||||
@@ -295,6 +317,8 @@ public:
|
||||
void EnableExtMappingFilaTypeCheck(bool to_check = true) { m_ext_mapping_filatype_check = to_check;} ;
|
||||
|
||||
private:
|
||||
void OnNozzleMappingSelected(wxCommandEvent& evt);
|
||||
|
||||
ResetCallback m_reset_callback{nullptr};
|
||||
std::string m_material_index;
|
||||
bool m_only_show_ext_spool{false};
|
||||
|
||||
@@ -241,6 +241,9 @@ void BackgroundSlicingProcess::process_fff()
|
||||
//BBS: add plate index into render params
|
||||
m_temp_output_path = this->get_current_plate()->get_tmp_gcode_path();
|
||||
m_fff_print->export_gcode(m_temp_output_path, m_gcode_result, [this](const ThumbnailsParams& params) { return this->render_thumbnails(params); });
|
||||
// Orca: BBL printers post-process the g-code in place here and never re-parse it into a fresh
|
||||
// GCodeProcessorResult, so m_gcode_result->nozzle_group_result (consumed by the H2C print-dispatch
|
||||
// nozzle mapping) survives post-processing. No preservation guard is needed on this path.
|
||||
if(m_fff_print->is_BBL_printer())
|
||||
run_post_process_scripts(m_temp_output_path, false, "File", m_temp_output_path, m_fff_print->full_print_config());
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
#include "Plater.hpp"
|
||||
#include "DeviceCore/DevExtruderSystem.h"
|
||||
#include "DeviceCore/DevManager.h"
|
||||
#include "DeviceCore/DevConfigUtil.h"
|
||||
#include "DeviceCore/DevNozzleSystem.h"
|
||||
#include "DeviceCore/DevNozzleRack.h"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
@@ -26,6 +29,7 @@ namespace GUI {
|
||||
enum CaliColumnType : int {
|
||||
Cali_Name = 0,
|
||||
Cali_Filament,
|
||||
Cali_Nozzle_ID,
|
||||
Cali_Nozzle,
|
||||
Cali_K_Value,
|
||||
Cali_Delete,
|
||||
@@ -33,6 +37,9 @@ enum CaliColumnType : int {
|
||||
Cali_Type_Count
|
||||
};
|
||||
|
||||
// Orca: derive nozzle-volume support from the machine preset's nozzle_volume array
|
||||
// rather than a live device-capability query, to avoid flipping the Nozzle-Flow column
|
||||
// visibility on shipping printers (H2D/X1/P1/A1).
|
||||
bool support_nozzle_volume(const MachineObject* obj)
|
||||
{
|
||||
if (!obj)
|
||||
@@ -49,14 +56,17 @@ bool support_nozzle_volume(const MachineObject* obj)
|
||||
return false;
|
||||
}
|
||||
|
||||
int get_colume_idx(CaliColumnType type, MachineObject* obj)
|
||||
// Rack hotend position code -> label: 0 = fixed ("R"); >=0x10 = rack slot number; else N/A.
|
||||
static wxString nozzle_id_code_to_string(int code)
|
||||
{
|
||||
if (!support_nozzle_volume(obj)
|
||||
&& (type > CaliColumnType::Cali_Nozzle)) {
|
||||
return type - 1;
|
||||
if (code == 0) {
|
||||
return "R";
|
||||
} else if (code >= 0x10) {
|
||||
return wxString::Format("%d", code - 0x10 + 1);
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << "Nozzle position id is -1 or invalid.";
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static wxString get_preset_name_by_filament_id(std::string filament_id)
|
||||
@@ -244,6 +254,10 @@ void HistoryWindow::on_device_connected(MachineObject* obj)
|
||||
}
|
||||
m_comboBox_nozzle_dia->SetSelection(selection);
|
||||
|
||||
m_extruder_switch_btn->SetLabels(
|
||||
_L(DevPrinterConfigUtil::get_toolhead_display_name(obj->printer_type, DEPUTY_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::TitleCase)),
|
||||
_L(DevPrinterConfigUtil::get_toolhead_display_name(obj->printer_type, MAIN_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::TitleCase)));
|
||||
|
||||
if (obj->is_multi_extruders())
|
||||
m_extruder_switch_btn->Show();
|
||||
else
|
||||
@@ -320,7 +334,21 @@ void HistoryWindow::enbale_action_buttons(bool enable) {
|
||||
}
|
||||
}
|
||||
|
||||
// The main extruder can carry a nozzle rack (H2C); the Nozzle-ID column only shows for it.
|
||||
bool HistoryWindow::support_nozzle_id_column()
|
||||
{
|
||||
if (!curr_obj || get_extruder_id() != MAIN_EXTRUDER_ID)
|
||||
return false;
|
||||
auto ns = curr_obj->GetNozzleSystem();
|
||||
if (!ns)
|
||||
return false;
|
||||
auto rack = ns->GetNozzleRack();
|
||||
return rack && rack->IsSupported();
|
||||
}
|
||||
|
||||
void HistoryWindow::sync_history_data() {
|
||||
int column_idx = 0;
|
||||
|
||||
Freeze();
|
||||
m_history_data_panel->DestroyChildren();
|
||||
m_history_data_panel->Enable();
|
||||
@@ -331,24 +359,35 @@ void HistoryWindow::sync_history_data() {
|
||||
|
||||
m_history_data_panel->SetSizer(gbSizer, true);
|
||||
|
||||
const bool has_nozzle_id = support_nozzle_id_column();
|
||||
|
||||
auto title_name = new Label(m_history_data_panel, _L("Name"));
|
||||
title_name->SetFont(Label::Head_14);
|
||||
gbSizer->Add(title_name, {0, get_colume_idx(CaliColumnType::Cali_Name, curr_obj) }, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
BOOST_LOG_TRIVIAL(info) << "=====================" << title_name->GetLabelText().ToStdString();
|
||||
gbSizer->Add(title_name, {0, column_idx++ }, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
|
||||
auto title_preset_name = new Label(m_history_data_panel, _L("Filament"));
|
||||
title_preset_name->SetFont(Label::Head_14);
|
||||
gbSizer->Add(title_preset_name, { 0, get_colume_idx(CaliColumnType::Cali_Filament, curr_obj) }, { 1, 1 }, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(title_preset_name, { 0, column_idx++ }, { 1, 1 }, wxBOTTOM, FromDIP(15));
|
||||
|
||||
if (has_nozzle_id) {
|
||||
auto nozzle_id_name = new Label(m_history_data_panel, _L("Nozzle ID"));
|
||||
nozzle_id_name->SetFont(Label::Head_14);
|
||||
{
|
||||
wxString chd_ext_name = _L(DevPrinterConfigUtil::get_toolhead_display_name(curr_obj->printer_type, MAIN_EXTRUDER_ID, ToolHeadComponent::Extruder, ToolHeadNameCase::LowerCase));
|
||||
nozzle_id_name->SetToolTip(wxString::Format(_L("Note: The hotend number on the %s is tied to the holder. When the hotend is moved to a new holder, its number will update automatically."), chd_ext_name));
|
||||
}
|
||||
gbSizer->Add(nozzle_id_name, {0, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
}
|
||||
|
||||
if (support_nozzle_volume(curr_obj)) {
|
||||
auto nozzle_name = new Label(m_history_data_panel, _L("Nozzle Flow"));
|
||||
nozzle_name->SetFont(Label::Head_14);
|
||||
gbSizer->Add(nozzle_name, {0, get_colume_idx(CaliColumnType::Cali_Nozzle, curr_obj)}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(nozzle_name, {0, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
}
|
||||
|
||||
auto title_k = new Label(m_history_data_panel, _L("Factor K"));
|
||||
title_k->SetFont(Label::Head_14);
|
||||
gbSizer->Add(title_k, { 0, get_colume_idx(CaliColumnType::Cali_K_Value,curr_obj) }, { 1, 1 }, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(title_k, { 0, column_idx++ }, { 1, 1 }, wxBOTTOM, FromDIP(15));
|
||||
|
||||
// Hide
|
||||
//auto title_n = new Label(m_history_data_panel, wxID_ANY, _L("N"));
|
||||
@@ -357,7 +396,12 @@ void HistoryWindow::sync_history_data() {
|
||||
|
||||
auto title_action = new Label(m_history_data_panel, _L("Action"));
|
||||
title_action->SetFont(Label::Head_14);
|
||||
gbSizer->Add(title_action, {0, get_colume_idx(CaliColumnType::Cali_Delete, curr_obj)}, {1, 1});
|
||||
gbSizer->Add(title_action, {0, column_idx++}, {1, 1});
|
||||
|
||||
// grid item num in row (Delete + Edit share one "Action" header)
|
||||
const int column_count = column_idx + 1;
|
||||
// reset column_idx for the data rows
|
||||
column_idx = 0;
|
||||
|
||||
int i = 1;
|
||||
for (auto& result : m_calib_results_history) {
|
||||
@@ -373,13 +417,13 @@ void HistoryWindow::sync_history_data() {
|
||||
n_value->Hide();
|
||||
auto delete_button = new Button(m_history_data_panel, _L("Delete"));
|
||||
delete_button->SetStyle(ButtonStyle::Alert, ButtonType::Window);
|
||||
delete_button->Bind(wxEVT_BUTTON, [this, gbSizer, i, &result](auto& e) {
|
||||
delete_button->Bind(wxEVT_BUTTON, [this, gbSizer, i, &result, column_count](auto& e) {
|
||||
if (m_ui_op_lock) {
|
||||
return;
|
||||
} else {
|
||||
m_ui_op_lock = true;
|
||||
}
|
||||
for (int j = 0; j < HISTORY_WINDOW_ITEMS_COUNT; j++) {
|
||||
for (int j = 0; j < column_count; j++) {
|
||||
auto item = gbSizer->FindItemAtPosition({ i, j });
|
||||
if (item && item->GetWindow())
|
||||
item->GetWindow()->Hide();
|
||||
@@ -392,6 +436,8 @@ void HistoryWindow::sync_history_data() {
|
||||
cali_info.cali_idx = result.cali_idx;
|
||||
cali_info.nozzle_diameter = result.nozzle_diameter;
|
||||
cali_info.filament_id = result.filament_id;
|
||||
cali_info.nozzle_pos_id = result.nozzle_pos_id;
|
||||
cali_info.nozzle_sn = result.nozzle_sn;
|
||||
CalibUtils::delete_PA_calib_result(cali_info);
|
||||
});
|
||||
|
||||
@@ -418,17 +464,23 @@ void HistoryWindow::sync_history_data() {
|
||||
}
|
||||
});
|
||||
|
||||
gbSizer->Add(name_value, {i, get_colume_idx(CaliColumnType::Cali_Name, curr_obj)}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(preset_name_value, {i, get_colume_idx(CaliColumnType::Cali_Filament, curr_obj)}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(name_value, {i, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(preset_name_value, {i, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
if (has_nozzle_id) {
|
||||
wxString nozzle_id = nozzle_id_code_to_string(result.nozzle_pos_id);
|
||||
auto nozzle_id_label = new Label(m_history_data_panel, nozzle_id);
|
||||
gbSizer->Add(nozzle_id_label, {i, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
}
|
||||
if (support_nozzle_volume(curr_obj)) {
|
||||
wxString nozzle_name = get_nozzle_volume_type_name(result.nozzle_volume_type);
|
||||
auto nozzle_name_label = new Label(m_history_data_panel, nozzle_name);
|
||||
gbSizer->Add(nozzle_name_label, {i, get_colume_idx(CaliColumnType::Cali_Nozzle, curr_obj)}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(nozzle_name_label, {i, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
}
|
||||
gbSizer->Add(k_value, {i, get_colume_idx(CaliColumnType::Cali_K_Value, curr_obj)}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(k_value, {i, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
//gbSizer->Add(n_value, { i, 3 }, { 1, 1 }, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(delete_button, {i, get_colume_idx(CaliColumnType::Cali_Delete, curr_obj)}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(edit_button, {i, get_colume_idx(CaliColumnType::Cali_Edit, curr_obj)}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(delete_button, {i, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(edit_button, {i, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
column_idx = 0;
|
||||
i++;
|
||||
m_ui_op_lock = false;
|
||||
}
|
||||
@@ -523,13 +575,22 @@ EditCalibrationHistoryDialog::EditCalibrationHistoryDialog(wxWindow
|
||||
if (obj && obj->is_multi_extruders()) {
|
||||
|
||||
Label *extruder_name_title = new Label(top_panel, _L("Extruder"));
|
||||
int extruder_index = obj->is_main_extruder_on_left() ? result.extruder_id : 1 - result.extruder_id;
|
||||
wxString extruder_name = extruder_index == 0 ? _L("Left") : _L("Right");
|
||||
wxString extruder_name = _L(DevPrinterConfigUtil::get_toolhead_display_name(
|
||||
obj->printer_type, result.extruder_id, ToolHeadComponent::Extruder, ToolHeadNameCase::TitleCase, true));
|
||||
Label *extruder_name_value = new Label(top_panel, extruder_name);
|
||||
flex_sizer->Add(extruder_name_title);
|
||||
flex_sizer->Add(extruder_name_value);
|
||||
}
|
||||
|
||||
if (obj && obj->GetNozzleSystem() && obj->GetNozzleSystem()->GetNozzleRack()
|
||||
&& obj->GetNozzleSystem()->GetNozzleRack()->IsSupported() && result.extruder_id == MAIN_EXTRUDER_ID) {
|
||||
Label* nozzle_id_title = new Label(top_panel, _L("Nozzle ID"));
|
||||
wxString nozzle_id = nozzle_id_code_to_string(result.nozzle_pos_id);
|
||||
Label* nozzle_id_value = new Label(top_panel, nozzle_id);
|
||||
flex_sizer->Add(nozzle_id_title);
|
||||
flex_sizer->Add(nozzle_id_value);
|
||||
}
|
||||
|
||||
if (support_nozzle_volume(curr_obj)) {
|
||||
Label *nozzle_name_title = new Label(top_panel, _L("Nozzle"));
|
||||
wxString nozzle_name;
|
||||
@@ -768,14 +829,40 @@ NewCalibrationHistoryDialog::NewCalibrationHistoryDialog(wxWindow *parent, const
|
||||
Label *extruder_name_title = new Label(top_panel, _L("Extruder"));
|
||||
m_comboBox_extruder = new ::ComboBox(top_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, NEW_HISTORY_DIALOG_INPUT_SIZE, 0, nullptr, wxCB_READONLY);
|
||||
wxArrayString extruder_items;
|
||||
extruder_items.push_back(_L("Left"));
|
||||
extruder_items.push_back(_L("Right"));
|
||||
extruder_items.push_back(_L(DevPrinterConfigUtil::get_toolhead_display_name(
|
||||
curr_obj->printer_type, DEPUTY_EXTRUDER_ID, ToolHeadComponent::Extruder, ToolHeadNameCase::TitleCase, true)));
|
||||
extruder_items.push_back(_L(DevPrinterConfigUtil::get_toolhead_display_name(
|
||||
curr_obj->printer_type, MAIN_EXTRUDER_ID, ToolHeadComponent::Extruder, ToolHeadNameCase::TitleCase, true)));
|
||||
m_comboBox_extruder->Set(extruder_items);
|
||||
m_comboBox_extruder->SetSelection(-1);
|
||||
flex_sizer->Add(extruder_name_title);
|
||||
flex_sizer->Add(m_comboBox_extruder);
|
||||
}
|
||||
|
||||
// Nozzle ID (rack printers only): choose the hotend position this manual record belongs to.
|
||||
if (curr_obj->GetNozzleSystem() && curr_obj->GetNozzleSystem()->GetNozzleRack()
|
||||
&& curr_obj->GetNozzleSystem()->GetNozzleRack()->IsSupported()) {
|
||||
auto rack = curr_obj->GetNozzleSystem()->GetNozzleRack();
|
||||
Label *nozzle_id_title = new Label(top_panel, _L("Nozzle ID"));
|
||||
m_comboBox_nozzle_id = new ::ComboBox(top_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, NEW_HISTORY_DIALOG_INPUT_SIZE, 0, nullptr, wxCB_READONLY);
|
||||
m_comboBox_nozzle_id->Bind(wxEVT_COMMAND_COMBOBOX_SELECTED, &NewCalibrationHistoryDialog::on_select_nozzle_pos, this);
|
||||
|
||||
auto main_extder = curr_obj->GetExtderSystem()->GetExtderById(MAIN_EXTRUDER_ID);
|
||||
int r_nozzle_id = main_extder ? main_extder->GetNozzleId() : 0;
|
||||
auto r_nozzle = curr_obj->GetNozzleSystem()->GetExtNozzle(r_nozzle_id);
|
||||
if (r_nozzle.IsNormal()) {
|
||||
m_comboBox_nozzle_id->Append("R", wxNullBitmap, new int{0});
|
||||
}
|
||||
for (auto nozzle_item : rack->GetRackNozzles()) {
|
||||
if (nozzle_item.second.IsNormal()) {
|
||||
m_comboBox_nozzle_id->Append(wxString::Format("%d", nozzle_item.first + 1), wxNullBitmap, new int{0x10 | nozzle_item.first});
|
||||
}
|
||||
}
|
||||
m_comboBox_nozzle_id->SetSelection(-1);
|
||||
flex_sizer->Add(nozzle_id_title);
|
||||
flex_sizer->Add(m_comboBox_nozzle_id);
|
||||
}
|
||||
|
||||
if (support_nozzle_volume(curr_obj)) {
|
||||
Label *nozzle_name_title = new Label(top_panel, _L("Nozzle"));
|
||||
m_comboBox_nozzle_type = new ::ComboBox(top_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, NEW_HISTORY_DIALOG_INPUT_SIZE, 0, nullptr, wxCB_READONLY);
|
||||
@@ -839,6 +926,35 @@ int NewCalibrationHistoryDialog::get_extruder_id(int extruder_index)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NewCalibrationHistoryDialog::get_nozzle_combo_id_code() const
|
||||
{
|
||||
if (!m_comboBox_nozzle_id)
|
||||
return -1;
|
||||
|
||||
auto sel = m_comboBox_nozzle_id->GetSelection();
|
||||
if (sel != wxNOT_FOUND && m_comboBox_nozzle_id->GetClientData(sel))
|
||||
return *(reinterpret_cast<int*>(m_comboBox_nozzle_id->GetClientData(sel)));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void NewCalibrationHistoryDialog::on_select_nozzle_pos(wxCommandEvent &event)
|
||||
{
|
||||
// Mirror the picked hotend's flow onto the (Orca index-based) nozzle-type combo.
|
||||
if (!curr_obj || !m_comboBox_nozzle_id || !m_comboBox_nozzle_type || !curr_obj->GetNozzleSystem())
|
||||
return;
|
||||
|
||||
int pos = get_nozzle_combo_id_code();
|
||||
if (pos < 0)
|
||||
return;
|
||||
|
||||
DevNozzle nozzle = curr_obj->GetNozzleSystem()->GetNozzleByPosId(pos);
|
||||
if (nozzle.IsNormal()) {
|
||||
NozzleVolumeType volume_type = DevNozzle::ToNozzleVolumeType(nozzle.GetNozzleFlowType());
|
||||
m_comboBox_nozzle_type->SetSelection(static_cast<int>(volume_type));
|
||||
}
|
||||
}
|
||||
|
||||
void NewCalibrationHistoryDialog::on_ok(wxCommandEvent &event)
|
||||
{
|
||||
wxString name = m_name_value->GetTextCtrl()->GetValue();
|
||||
@@ -897,6 +1013,7 @@ void NewCalibrationHistoryDialog::on_ok(wxCommandEvent &event)
|
||||
m_new_result.nozzle_diameter = nozzle_value;
|
||||
m_new_result.filament_id = filament_id;
|
||||
m_new_result.setting_id = setting_id;
|
||||
m_new_result.nozzle_pos_id = get_nozzle_combo_id_code();
|
||||
|
||||
// Check for duplicate names from history
|
||||
{
|
||||
|
||||
@@ -27,6 +27,7 @@ protected:
|
||||
void enbale_action_buttons(bool enable);
|
||||
float get_nozzle_value();
|
||||
int get_extruder_id();
|
||||
bool support_nozzle_id_column();
|
||||
|
||||
void on_click_new_button(wxCommandEvent &event);
|
||||
|
||||
@@ -76,10 +77,12 @@ public:
|
||||
protected:
|
||||
virtual void on_ok(wxCommandEvent &event);
|
||||
virtual void on_cancel(wxCommandEvent &event);
|
||||
void on_select_nozzle_pos(wxCommandEvent &event);
|
||||
|
||||
|
||||
wxArrayString get_all_filaments(const MachineObject *obj);
|
||||
int get_extruder_id(int extruder_index); // extruder_index 0 : left, 1 : right
|
||||
int get_nozzle_combo_id_code() const; // rack hotend position code, -1 if none
|
||||
|
||||
protected:
|
||||
PACalibResult m_new_result;
|
||||
@@ -93,6 +96,7 @@ protected:
|
||||
ComboBox *m_comboBox_filament;
|
||||
ComboBox *m_comboBox_extruder;
|
||||
ComboBox *m_comboBox_nozzle_type;
|
||||
ComboBox *m_comboBox_nozzle_id{nullptr};
|
||||
|
||||
struct FilamentInfos
|
||||
{
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
#include "DeviceCore/DevExtruderSystem.h"
|
||||
#include "DeviceCore/DevFilaBlackList.h"
|
||||
#include "DeviceCore/DevFilaSystem.h"
|
||||
#include "DeviceCore/DevFilaSwitch.h"
|
||||
#include "DeviceCore/DevManager.h"
|
||||
#include "DeviceCore/DevNozzleSystem.h"
|
||||
#include "DeviceCore/DevStorage.h"
|
||||
|
||||
#define CALIBRATION_LABEL_SIZE wxSize(FromDIP(150), FromDIP(24))
|
||||
@@ -1457,31 +1459,40 @@ bool CalibrationPresetPage::is_filament_in_blacklist(int tray_id, Preset* preset
|
||||
get_tray_ams_and_slot_id(curr_obj, tray_id, ams_id, slot_id, out_tray_id);
|
||||
|
||||
if (wxGetApp().app_config->get("skip_ams_blacklist_check") != "true") {
|
||||
bool in_blacklist = false;
|
||||
std::string action;
|
||||
wxString info;
|
||||
std::string filamnt_type;
|
||||
preset->get_filament_type(filamnt_type);
|
||||
DevFilaBlacklist::CheckFilamentInfo check_info;
|
||||
check_info.dev_id = curr_obj->get_dev_id();
|
||||
check_info.model_id = curr_obj->printer_type;
|
||||
check_info.fila_id = preset->filament_id;
|
||||
preset->get_filament_type(check_info.fila_type);
|
||||
check_info.ams_id = ams_id;
|
||||
check_info.slot_id = slot_id;
|
||||
check_info.has_filament_switch = curr_obj->GetFilaSwitch()->IsInstalled();
|
||||
// fila_name intentionally left empty: the engine recovers it from the selected AMS slot,
|
||||
// preserving the name-match behavior.
|
||||
|
||||
auto vendor = dynamic_cast<ConfigOptionStrings*> (preset->config.option("filament_vendor"));
|
||||
if (vendor && (vendor->values.size() > 0)) {
|
||||
std::string vendor_name = vendor->values[0];
|
||||
DevFilaBlacklist::check_filaments_in_blacklist(curr_obj->printer_type, vendor_name, filamnt_type, preset->filament_id, ams_id, slot_id, "", in_blacklist, action, info);
|
||||
check_info.fila_vendor = vendor->values[0];
|
||||
}
|
||||
|
||||
if (in_blacklist) {
|
||||
error_tips = info.ToUTF8().data();
|
||||
if (action == "prohibition") {
|
||||
return false;
|
||||
}
|
||||
else if (action == "warning") {
|
||||
return true;
|
||||
}
|
||||
const auto &result = DevFilaBlacklist::check_filaments_in_blacklist(check_info);
|
||||
|
||||
if (const auto &prohibition_items = result.get_items_by_action("prohibition"); !prohibition_items.empty()) {
|
||||
wxString combined_msg;
|
||||
for (const auto &item : prohibition_items) { combined_msg += item.info_msg + "\n"; }
|
||||
error_tips = combined_msg.ToUTF8().data();
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
error_tips = "";
|
||||
|
||||
if (const auto &warning_items = result.get_items_by_action("warning"); !warning_items.empty()) {
|
||||
wxString combined_msg;
|
||||
for (const auto &item : warning_items) { combined_msg += item.info_msg + "\n"; }
|
||||
error_tips = combined_msg.ToUTF8().data();
|
||||
return true;
|
||||
}
|
||||
|
||||
error_tips = "";
|
||||
return true;
|
||||
}
|
||||
if (devPrinterUtil::IsVirtualSlot(ams_id)) {
|
||||
if (m_cali_mode == CalibMode::Calib_PA_Line && (m_cali_method == CalibrationMethod::CALI_METHOD_AUTO || m_cali_method == CalibrationMethod::CALI_METHOD_NEW_AUTO)) {
|
||||
@@ -1663,7 +1674,9 @@ bool CalibrationPresetPage::is_nozzle_info_synced() const
|
||||
if (curr_obj->is_nozzle_flow_type_supported()) {
|
||||
if (extruder.GetNozzleFlowType() == NozzleFlowType::NONE_FLOWTYPE)
|
||||
return false;
|
||||
if (int(extruder.GetNozzleFlowType()) - 1 != int(get_nozzle_volume_type(extruder_id)))
|
||||
// Map device flow -> volume type via DevNozzle::ToNozzleVolumeType so U_FLOW resolves to
|
||||
// nvtTPUHighFlow(3); the naive flow-1 would yield nvtHybrid(2). Identical to flow-1 for S/H flow.
|
||||
if (int(DevNozzle::ToNozzleVolumeType(extruder.GetNozzleFlowType())) != int(get_nozzle_volume_type(extruder_id)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2124,7 +2137,7 @@ void CalibrationPresetPage::init_with_machine(MachineObject* obj)
|
||||
}
|
||||
|
||||
if (obj->GetExtderSystem()->GetNozzleFlowType(i) != NozzleFlowType::NONE_FLOWTYPE) {
|
||||
m_left_comboBox_nozzle_volume->SetSelection(obj->GetExtderSystem()->GetNozzleFlowType(i) - 1);
|
||||
m_left_comboBox_nozzle_volume->SetSelection(int(DevNozzle::ToNozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(i))));
|
||||
} else {
|
||||
m_left_comboBox_nozzle_volume->SetSelection(0);
|
||||
}
|
||||
@@ -2144,7 +2157,7 @@ void CalibrationPresetPage::init_with_machine(MachineObject* obj)
|
||||
}
|
||||
|
||||
if (obj->GetExtderSystem()->GetNozzleFlowType(i) != NozzleFlowType::NONE_FLOWTYPE) {
|
||||
m_right_comboBox_nozzle_volume->SetSelection(obj->GetExtderSystem()->GetNozzleFlowType(i) - 1);
|
||||
m_right_comboBox_nozzle_volume->SetSelection(int(DevNozzle::ToNozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(i))));
|
||||
} else {
|
||||
m_right_comboBox_nozzle_volume->SetSelection(0);
|
||||
}
|
||||
@@ -2182,7 +2195,7 @@ void CalibrationPresetPage::init_with_machine(MachineObject* obj)
|
||||
else {
|
||||
if ((obj->GetExtderSystem()->GetTotalExtderCount() > 0) && (obj->GetExtderSystem()->GetNozzleFlowType(0) != NozzleFlowType::NONE_FLOWTYPE))
|
||||
{
|
||||
m_comboBox_nozzle_volume->SetSelection(obj->GetExtderSystem()->GetNozzleFlowType(0) - 1);
|
||||
m_comboBox_nozzle_volume->SetSelection(int(DevNozzle::ToNozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(0))));
|
||||
} else {
|
||||
m_comboBox_nozzle_volume->SetSelection(0);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
#include "I18N.hpp"
|
||||
#include "Widgets/Label.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "DeviceCore/DevConfigUtil.h"
|
||||
#include "DeviceCore/DevNozzleSystem.h"
|
||||
#include "DeviceCore/DevNozzleRack.h"
|
||||
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
@@ -95,35 +98,43 @@ enum class GridTextInputType {
|
||||
class GridTextInput : public TextInput
|
||||
{
|
||||
public:
|
||||
GridTextInput(wxWindow* parent, wxString text, wxString label, wxSize size, int col_idx, GridTextInputType type);
|
||||
GridTextInput(wxWindow* parent, wxString text, wxString label, wxSize size, int col_idx, GridTextInputType type, int extruder_id = MAIN_EXTRUDER_ID);
|
||||
int get_col_idx() { return m_col_idx; }
|
||||
void set_col_idx(int idx) { m_col_idx = idx; }
|
||||
int get_extruder_id() { return m_extruder_id; }
|
||||
void set_extruder_id(int id) { m_extruder_id = id; }
|
||||
GridTextInputType get_type() { return m_type; }
|
||||
void set_type(GridTextInputType type) { m_type = type; }
|
||||
private:
|
||||
int m_col_idx;
|
||||
int m_extruder_id{MAIN_EXTRUDER_ID};
|
||||
GridTextInputType m_type;
|
||||
};
|
||||
|
||||
GridTextInput::GridTextInput(wxWindow* parent, wxString text, wxString label, wxSize size, int col_idx, GridTextInputType type)
|
||||
GridTextInput::GridTextInput(wxWindow* parent, wxString text, wxString label, wxSize size, int col_idx, GridTextInputType type, int extruder_id)
|
||||
: TextInput(parent, text, label, "", wxDefaultPosition, size, wxTE_PROCESS_ENTER)
|
||||
, m_col_idx(col_idx)
|
||||
, m_extruder_id(extruder_id)
|
||||
, m_type(type)
|
||||
{
|
||||
}
|
||||
|
||||
class GridComboBox : public ComboBox {
|
||||
public:
|
||||
GridComboBox(wxWindow* parent, wxSize size, int col_idx);
|
||||
GridComboBox(wxWindow* parent, wxSize size, int col_idx, int extruder_id = MAIN_EXTRUDER_ID);
|
||||
int get_col_idx() { return m_col_idx; }
|
||||
void set_col_idx(int idx) { m_col_idx = idx; }
|
||||
int get_extruder_id() { return m_extruder_id; }
|
||||
void set_extruder_id(int id) { m_extruder_id = id; }
|
||||
private:
|
||||
int m_col_idx;
|
||||
int m_extruder_id{MAIN_EXTRUDER_ID};
|
||||
};
|
||||
|
||||
GridComboBox::GridComboBox(wxWindow* parent, wxSize size, int col_idx)
|
||||
GridComboBox::GridComboBox(wxWindow* parent, wxSize size, int col_idx, int extruder_id)
|
||||
: ComboBox(parent, wxID_ANY, "", wxDefaultPosition, size, 0, nullptr)
|
||||
, m_col_idx(col_idx)
|
||||
, m_extruder_id(extruder_id)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -227,7 +238,7 @@ void CaliPASaveAutoPanel::sync_cali_result(const std::vector<PACalibResult>& cal
|
||||
m_calib_results.clear();
|
||||
for (auto& item : cali_result) {
|
||||
if (item.confidence == 0)
|
||||
m_calib_results[item.tray_id] = item;
|
||||
m_calib_results.push_back(item);
|
||||
}
|
||||
m_grid_panel->DestroyChildren();
|
||||
auto grid_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
@@ -281,14 +292,14 @@ void CaliPASaveAutoPanel::sync_cali_result(const std::vector<PACalibResult>& cal
|
||||
wxString tray_name = get_tray_name_by_tray_id(item.tray_id);
|
||||
tray_title->SetLabel(tray_name);
|
||||
|
||||
auto k_value = new GridTextInput(m_grid_panel, "", "", CALIBRATION_SAVE_INPUT_SIZE, item.tray_id, GridTextInputType::K);
|
||||
auto n_value = new GridTextInput(m_grid_panel, "", "", CALIBRATION_SAVE_INPUT_SIZE, item.tray_id, GridTextInputType::N);
|
||||
auto k_value = new GridTextInput(m_grid_panel, "", "", CALIBRATION_SAVE_INPUT_SIZE, item.tray_id, GridTextInputType::K, item.extruder_id);
|
||||
auto n_value = new GridTextInput(m_grid_panel, "", "", CALIBRATION_SAVE_INPUT_SIZE, item.tray_id, GridTextInputType::N, item.extruder_id);
|
||||
k_value->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
n_value->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
auto k_value_failed = new Label(m_grid_panel, _L("Failed"));
|
||||
auto n_value_failed = new Label(m_grid_panel, _L("Failed"));
|
||||
|
||||
auto comboBox_tray_name = new GridComboBox(m_grid_panel, CALIBRATION_SAVE_INPUT_SIZE, item.tray_id);
|
||||
auto comboBox_tray_name = new GridComboBox(m_grid_panel, CALIBRATION_SAVE_INPUT_SIZE, item.tray_id, item.extruder_id);
|
||||
auto tray_name_failed = new Label(m_grid_panel, " - ");
|
||||
wxArrayString selections;
|
||||
static std::vector<PACalibResult> filtered_results;
|
||||
@@ -395,17 +406,26 @@ void CaliPASaveAutoPanel::save_to_result_from_widgets(wxWindow* window, bool* ou
|
||||
return;
|
||||
|
||||
//operate
|
||||
// The grid can hold two rows with the same tray_id under different extruders
|
||||
// (rack / dual-address configs), so results must be keyed by (tray_id, extruder_id).
|
||||
auto find_result = [this](int tray_id, int extruder_id) -> PACalibResult* {
|
||||
auto it = std::find_if(m_calib_results.begin(), m_calib_results.end(), [tray_id, extruder_id](const PACalibResult& r) {
|
||||
return r.tray_id == tray_id && r.extruder_id == extruder_id;
|
||||
});
|
||||
return it != m_calib_results.end() ? &(*it) : nullptr;
|
||||
};
|
||||
auto input = dynamic_cast<GridTextInput*>(window);
|
||||
if (input && input->IsShown()) {
|
||||
int tray_id = input->get_col_idx();
|
||||
int tray_id = input->get_col_idx();
|
||||
int extruder_id = input->get_extruder_id();
|
||||
if (input->get_type() == GridTextInputType::K) {
|
||||
float k = 0.0f;
|
||||
if (!CalibUtils::validate_input_k_value(input->GetTextCtrl()->GetValue(), &k)) {
|
||||
*out_msg = wxString::Format(_L("Please input a valid value (K in %.1f~%.1f)"), MIN_PA_K_VALUE, MAX_PA_K_VALUE);
|
||||
*out_is_valid = false;
|
||||
}
|
||||
else
|
||||
m_calib_results[tray_id].k_value = k;
|
||||
else if (auto* result = find_result(tray_id, extruder_id))
|
||||
result->k_value = k;
|
||||
}
|
||||
else if (input->get_type() == GridTextInputType::N) {
|
||||
}
|
||||
@@ -413,7 +433,8 @@ void CaliPASaveAutoPanel::save_to_result_from_widgets(wxWindow* window, bool* ou
|
||||
|
||||
auto comboBox = dynamic_cast<GridComboBox*>(window);
|
||||
if (comboBox && comboBox->IsShown()) {
|
||||
int tray_id = comboBox->get_col_idx();
|
||||
int tray_id = comboBox->get_col_idx();
|
||||
int extruder_id = comboBox->get_extruder_id();
|
||||
wxString name = comboBox->GetTextCtrl()->GetValue().ToStdString();
|
||||
if (name.IsEmpty()) {
|
||||
*out_msg = _L("Please enter the name you want to save to printer.");
|
||||
@@ -423,7 +444,8 @@ void CaliPASaveAutoPanel::save_to_result_from_widgets(wxWindow* window, bool* ou
|
||||
*out_msg = _L("The name cannot exceed 40 characters.");
|
||||
*out_is_valid = false;
|
||||
}
|
||||
m_calib_results[tray_id].name = into_u8(name);
|
||||
if (auto* result = find_result(tray_id, extruder_id))
|
||||
result->name = into_u8(name);
|
||||
}
|
||||
|
||||
auto childern = window->GetChildren();
|
||||
@@ -441,52 +463,7 @@ bool CaliPASaveAutoPanel::get_result(std::vector<PACalibResult>& out_result) {
|
||||
else
|
||||
save_to_result_from_widgets(m_grid_panel, &is_valid, &err_msg);
|
||||
if (is_valid) {
|
||||
/*
|
||||
std::vector<PACalibResult> to_save_result;
|
||||
for (auto &result : m_calib_results) {
|
||||
auto iter = std::find_if(to_save_result.begin(), to_save_result.end(), [this, &result](const PACalibResult &item) {
|
||||
bool has_same_name = (item.name == result.second.name && item.filament_id == result.second.filament_id);
|
||||
if (m_obj && m_obj->is_multi_extruders()) {
|
||||
has_same_name &= (item.extruder_id == result.second.extruder_id && item.nozzle_volume_type == result.second.nozzle_volume_type);
|
||||
}
|
||||
return has_same_name;
|
||||
});
|
||||
|
||||
if (iter != to_save_result.end()) {
|
||||
MessageDialog msg_dlg(nullptr, _L("Only one of the results with the same name will be saved. Are you sure you want to overwrite the other results?"),
|
||||
wxEmptyString, wxICON_WARNING | wxYES_NO);
|
||||
if (msg_dlg.ShowModal() != wxID_YES) {
|
||||
return false;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &result : m_history_results) {
|
||||
auto iter = std::find_if(m_history_results.begin(), m_history_results.end(), [this, &result](const PACalibResult &item) {
|
||||
bool has_same_name = (item.name == result.name && item.filament_id == result.filament_id);
|
||||
if (m_obj && m_obj->is_multi_extruders()) {
|
||||
has_same_name &= (item.extruder_id == result.extruder_id && item.nozzle_volume_type == result.nozzle_volume_type);
|
||||
}
|
||||
return has_same_name;
|
||||
});
|
||||
|
||||
if (iter != m_history_results.end()) {
|
||||
MessageDialog msg_dlg(nullptr,
|
||||
wxString::Format(_L("There is already a historical calibration result with the same name: %s. Are you sure you want to override the historical result?"),
|
||||
result.name),
|
||||
wxEmptyString, wxICON_WARNING | wxYES_NO);
|
||||
if (msg_dlg.ShowModal() != wxID_YES) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
for (auto& result : m_calib_results) {
|
||||
out_result.push_back(result.second);
|
||||
}
|
||||
out_result = m_calib_results;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
@@ -517,11 +494,12 @@ void CaliPASaveAutoPanel::sync_cali_result_for_multi_extruder(const std::vector<
|
||||
m_calib_results.clear();
|
||||
for (auto &item : cali_result) {
|
||||
if (item.confidence == 0) {
|
||||
int tray_id = 4 * item.ams_id + item.slot_id;
|
||||
PACalibResult result = item;
|
||||
result.tray_id = 4 * item.ams_id + item.slot_id;
|
||||
if (item.ams_id == VIRTUAL_TRAY_MAIN_ID || item.ams_id == VIRTUAL_TRAY_DEPUTY_ID) {
|
||||
tray_id = item.ams_id;
|
||||
result.tray_id = item.ams_id;
|
||||
}
|
||||
m_calib_results[tray_id] = item;
|
||||
m_calib_results.push_back(result);
|
||||
}
|
||||
}
|
||||
m_multi_extruder_grid_panel->DestroyChildren();
|
||||
@@ -532,14 +510,21 @@ void CaliPASaveAutoPanel::sync_cali_result_for_multi_extruder(const std::vector<
|
||||
m_multi_extruder_grid_panel->SetSizer(grid_sizer, true);
|
||||
m_multi_extruder_grid_panel->Bind(wxEVT_LEFT_DOWN, [this](auto &e) { SetFocusIgnoringChildren(); });
|
||||
|
||||
wxStaticBoxSizer *left_sizer = new wxStaticBoxSizer(wxVERTICAL, m_multi_extruder_grid_panel, _L("Left extruder"));
|
||||
wxStaticBoxSizer *right_sizer = new wxStaticBoxSizer(wxVERTICAL, m_multi_extruder_grid_panel, _L("Right extruder"));
|
||||
const std::string& cwsp_pt = m_obj->printer_type;
|
||||
wxStaticBoxSizer *left_sizer = new wxStaticBoxSizer(wxVERTICAL, m_multi_extruder_grid_panel, _L(DevPrinterConfigUtil::get_toolhead_display_name(cwsp_pt, DEPUTY_EXTRUDER_ID, ToolHeadComponent::Extruder, ToolHeadNameCase::SentenceCase)));
|
||||
wxStaticBoxSizer *right_sizer = new wxStaticBoxSizer(wxVERTICAL, m_multi_extruder_grid_panel, _L(DevPrinterConfigUtil::get_toolhead_display_name(cwsp_pt, MAIN_EXTRUDER_ID, ToolHeadComponent::Extruder, ToolHeadNameCase::SentenceCase)));
|
||||
grid_sizer->Add(left_sizer);
|
||||
grid_sizer->AddSpacer(COLUMN_GAP);
|
||||
grid_sizer->Add(right_sizer);
|
||||
|
||||
wxFlexGridSizer *left_grid_sizer = new wxFlexGridSizer(3, COLUMN_GAP, ROW_GAP);
|
||||
wxFlexGridSizer *right_grid_sizer = new wxFlexGridSizer(3, COLUMN_GAP, ROW_GAP);
|
||||
|
||||
// The main extruder can carry a nozzle rack (H2C); non-rack printers keep the 3-column layout.
|
||||
auto ns = m_obj->GetNozzleSystem();
|
||||
auto rack = ns ? ns->GetNozzleRack() : nullptr;
|
||||
bool has_rack = rack && rack->IsSupported();
|
||||
|
||||
wxFlexGridSizer *right_grid_sizer = new wxFlexGridSizer(has_rack ? 4 : 3, COLUMN_GAP, ROW_GAP);
|
||||
left_sizer->Add(left_grid_sizer);
|
||||
right_sizer->Add(right_grid_sizer);
|
||||
|
||||
@@ -567,29 +552,46 @@ void CaliPASaveAutoPanel::sync_cali_result_for_multi_extruder(const std::vector<
|
||||
auto k_title = new Label(m_multi_extruder_grid_panel, _L("Factor K"), 0, CALIBRATION_SAVE_NUMBER_INPUT_SIZE);
|
||||
k_title->SetFont(Label::Head_14);
|
||||
right_grid_sizer->Add(k_title, 1, wxALIGN_CENTER);
|
||||
|
||||
if (has_rack) {
|
||||
auto nozzle_title = new Label(m_multi_extruder_grid_panel, _L("Nozzle ID"), 0, CALIBRATION_SAVE_NUMBER_INPUT_SIZE);
|
||||
nozzle_title->SetFont(Label::Head_14);
|
||||
right_grid_sizer->Add(nozzle_title, 1, wxALIGN_CENTER);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, std::string>> preset_names;
|
||||
// key: (extruder_id, tray_id)
|
||||
std::vector<std::pair<std::pair<int, int>, std::string>> preset_names;
|
||||
int i = 1;
|
||||
std::unordered_set<std::string> set;
|
||||
for (auto &info : m_obj->selected_cali_preset) {
|
||||
std::string default_name;
|
||||
// extruder _id
|
||||
// Derive extruder_id from the tray so the extruder/tray key stays consistent
|
||||
// with the calibration result's extruder_id.
|
||||
int extruder_id = 0;
|
||||
if (info.tray_id == VIRTUAL_TRAY_MAIN_ID) {
|
||||
extruder_id = 0;
|
||||
} else if (info.tray_id == VIRTUAL_TRAY_DEPUTY_ID) {
|
||||
extruder_id = 1;
|
||||
} else {
|
||||
int ams_id = info.tray_id / 4;
|
||||
extruder_id = m_obj->get_extruder_id_by_ams_id(std::to_string(ams_id));
|
||||
}
|
||||
{
|
||||
int extruder_id = 0;
|
||||
if (info.tray_id == VIRTUAL_TRAY_MAIN_ID) {
|
||||
extruder_id = 0;
|
||||
} else if (info.tray_id == VIRTUAL_TRAY_DEPUTY_ID) {
|
||||
extruder_id = 1;
|
||||
} else {
|
||||
int ams_id = info.tray_id / 4;
|
||||
extruder_id = m_obj->get_extruder_id_by_ams_id(std::to_string(ams_id));
|
||||
}
|
||||
|
||||
if (extruder_id == 0) {
|
||||
default_name += L("Right Nozzle");
|
||||
} else if (extruder_id == 1){
|
||||
default_name += L("Left Nozzle");
|
||||
if (extruder_id == MAIN_EXTRUDER_ID) {
|
||||
default_name += DevPrinterConfigUtil::get_toolhead_display_name(cwsp_pt, MAIN_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::TitleCase);
|
||||
if (has_rack) {
|
||||
default_name += "_";
|
||||
if (info.nozzle_pos_id == 0) {
|
||||
default_name += "R";
|
||||
} else if (info.nozzle_pos_id >= 0x10) {
|
||||
default_name += std::to_string((info.nozzle_pos_id & 0x0f) + 1);
|
||||
} else {
|
||||
default_name += "N/A";
|
||||
}
|
||||
}
|
||||
} else if (extruder_id == DEPUTY_EXTRUDER_ID) {
|
||||
default_name += DevPrinterConfigUtil::get_toolhead_display_name(cwsp_pt, DEPUTY_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::TitleCase);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -614,7 +616,7 @@ void CaliPASaveAutoPanel::sync_cali_result_for_multi_extruder(const std::vector<
|
||||
}
|
||||
}
|
||||
|
||||
preset_names.push_back({info.tray_id, default_name});
|
||||
preset_names.push_back({{extruder_id, info.tray_id}, default_name});
|
||||
}
|
||||
|
||||
bool left_first_add_item = true;
|
||||
@@ -648,14 +650,19 @@ void CaliPASaveAutoPanel::sync_cali_result_for_multi_extruder(const std::vector<
|
||||
tray_title->SetBitmap(*get_extruder_color_icon(full_filament_ams_list[item.tray_id].opt_string("filament_colour", 0u), tray_name.ToStdString(), FromDIP(20), FromDIP(20)));
|
||||
tray_title->SetToolTip("");
|
||||
|
||||
auto k_value = new GridTextInput(m_multi_extruder_grid_panel, "", "", CALIBRATION_SAVE_NUMBER_INPUT_SIZE, item.tray_id, GridTextInputType::K);
|
||||
auto n_value = new GridTextInput(m_multi_extruder_grid_panel, "", "", CALIBRATION_SAVE_NUMBER_INPUT_SIZE, item.tray_id, GridTextInputType::N);
|
||||
auto k_value = new GridTextInput(m_multi_extruder_grid_panel, "", "", CALIBRATION_SAVE_NUMBER_INPUT_SIZE, item.tray_id, GridTextInputType::K, item.extruder_id);
|
||||
auto n_value = new GridTextInput(m_multi_extruder_grid_panel, "", "", CALIBRATION_SAVE_NUMBER_INPUT_SIZE, item.tray_id, GridTextInputType::N, item.extruder_id);
|
||||
k_value->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
n_value->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
auto k_value_failed = new Label(m_multi_extruder_grid_panel, _L("Failed"));
|
||||
auto n_value_failed = new Label(m_multi_extruder_grid_panel, _L("Failed"));
|
||||
|
||||
auto comboBox_tray_name = new GridComboBox(m_multi_extruder_grid_panel, CALIBRATION_SAVE_INPUT_SIZE, item.tray_id);
|
||||
auto nozzle_id_value = new Label(m_multi_extruder_grid_panel, wxEmptyString);
|
||||
nozzle_id_value->SetMinSize(wxSize(FromDIP(180), FromDIP(24)));
|
||||
nozzle_id_value->Wrap(-1);
|
||||
auto nozzle_id_failed = new Label(m_multi_extruder_grid_panel, _L("Failed"));
|
||||
|
||||
auto comboBox_tray_name = new GridComboBox(m_multi_extruder_grid_panel, CALIBRATION_SAVE_INPUT_SIZE, item.tray_id, item.extruder_id);
|
||||
auto tray_name_failed = new Label(m_multi_extruder_grid_panel, " - ");
|
||||
wxArrayString selections;
|
||||
static std::vector<PACalibResult> filtered_results;
|
||||
@@ -671,22 +678,26 @@ void CaliPASaveAutoPanel::sync_cali_result_for_multi_extruder(const std::vector<
|
||||
}
|
||||
comboBox_tray_name->Set(selections);
|
||||
|
||||
auto set_edit_mode = [this, k_value, n_value, k_value_failed, n_value_failed, comboBox_tray_name, tray_name_failed](std::string str) {
|
||||
auto set_edit_mode = [this, k_value, n_value, k_value_failed, n_value_failed, nozzle_id_value, nozzle_id_failed, comboBox_tray_name, tray_name_failed](std::string str, bool display_nozzle) {
|
||||
if (str == "normal") {
|
||||
comboBox_tray_name->Show();
|
||||
tray_name_failed->Show(false);
|
||||
k_value->Show();
|
||||
n_value->Show();
|
||||
nozzle_id_value->Show(display_nozzle);
|
||||
k_value_failed->Show(false);
|
||||
n_value_failed->Show(false);
|
||||
nozzle_id_failed->Show(false);
|
||||
}
|
||||
if (str == "failed") {
|
||||
comboBox_tray_name->Show(false);
|
||||
tray_name_failed->Show();
|
||||
k_value->Show(false);
|
||||
n_value->Show(false);
|
||||
nozzle_id_value->Show(false);
|
||||
k_value_failed->Show();
|
||||
n_value_failed->Show();
|
||||
nozzle_id_failed->Show(display_nozzle);
|
||||
}
|
||||
|
||||
// hide n value
|
||||
@@ -698,15 +709,39 @@ void CaliPASaveAutoPanel::sync_cali_result_for_multi_extruder(const std::vector<
|
||||
};
|
||||
|
||||
if (!result_failed) {
|
||||
set_edit_mode("normal");
|
||||
set_edit_mode("normal", has_rack && item.extruder_id == MAIN_EXTRUDER_ID);
|
||||
|
||||
auto k_str = wxString::Format("%.3f", item.k_value);
|
||||
auto n_str = wxString::Format("%.3f", item.n_coef);
|
||||
k_value->GetTextCtrl()->SetValue(k_str);
|
||||
n_value->GetTextCtrl()->SetValue(n_str);
|
||||
|
||||
if (has_rack && item.extruder_id == MAIN_EXTRUDER_ID) {
|
||||
wxString nozzle_id_str;
|
||||
if (item.nozzle_pos_id == 0) {
|
||||
nozzle_id_str += "R | ";
|
||||
} else if (item.nozzle_pos_id >= 0x10) {
|
||||
nozzle_id_str += wxString::Format("%d | ", (item.nozzle_pos_id & 0x0f) + 1);
|
||||
} else {
|
||||
nozzle_id_str += "N/A | ";
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << "Nozzle position id is -1 or invalid.";
|
||||
}
|
||||
nozzle_id_str += wxString::Format("%.1f mm ", item.nozzle_diameter);
|
||||
switch (item.nozzle_volume_type) {
|
||||
case NozzleVolumeType::nvtStandard: nozzle_id_str += _L("Standard Flow"); break;
|
||||
case NozzleVolumeType::nvtHighFlow: nozzle_id_str += _L("High Flow"); break;
|
||||
case NozzleVolumeType::nvtTPUHighFlow: nozzle_id_str += _L("TPU High Flow"); break;
|
||||
default: break;
|
||||
}
|
||||
nozzle_id_value->SetLabel(nozzle_id_str);
|
||||
}
|
||||
|
||||
for (auto &name : preset_names) {
|
||||
if (item.tray_id == name.first) { comboBox_tray_name->SetValue(from_u8(name.second)); }
|
||||
int extruder_id = name.first.first;
|
||||
int tray_id = name.first.second;
|
||||
if (item.tray_id == tray_id && item.extruder_id == extruder_id) {
|
||||
comboBox_tray_name->SetValue(from_u8(name.second));
|
||||
}
|
||||
}
|
||||
|
||||
comboBox_tray_name->Bind(wxEVT_COMBOBOX, [this, comboBox_tray_name, k_value, n_value](auto &e) {
|
||||
@@ -714,7 +749,7 @@ void CaliPASaveAutoPanel::sync_cali_result_for_multi_extruder(const std::vector<
|
||||
auto history = filtered_results[selection];
|
||||
});
|
||||
} else {
|
||||
set_edit_mode("failed");
|
||||
set_edit_mode("failed", has_rack && item.extruder_id == MAIN_EXTRUDER_ID);
|
||||
}
|
||||
|
||||
if ((m_obj->is_main_extruder_on_left() && item.extruder_id == 0)
|
||||
@@ -762,6 +797,14 @@ void CaliPASaveAutoPanel::sync_cali_result_for_multi_extruder(const std::vector<
|
||||
} else {
|
||||
right_grid_sizer->Add(k_value_failed, 1, wxEXPAND);
|
||||
}
|
||||
|
||||
if (has_rack) {
|
||||
if (nozzle_id_value->IsShown()) {
|
||||
right_grid_sizer->Add(nozzle_id_value, 1, wxEXPAND);
|
||||
} else {
|
||||
right_grid_sizer->Add(nozzle_id_failed, 1, wxEXPAND);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,10 @@ protected:
|
||||
wxPanel* m_part_failed_panel;
|
||||
wxPanel* m_grid_panel{ nullptr };
|
||||
wxPanel* m_multi_extruder_grid_panel{ nullptr };
|
||||
std::map<int, PACalibResult> m_calib_results;// map<tray_id, PACalibResult>
|
||||
// keyed by (tray_id, extruder_id): the same tray_id can appear under two
|
||||
// extruders (rack / dual-address configs), so a tray_id-only map would
|
||||
// silently overwrite one extruder's K/N.
|
||||
std::vector<PACalibResult> m_calib_results;
|
||||
std::vector<PACalibResult> m_history_results;
|
||||
bool m_is_all_failed{ true };
|
||||
MachineObject* m_obj{ nullptr };
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
#include "DeviceCore/DevFilaSystem.h"
|
||||
#include "DeviceCore/DevFilaSwitch.h"
|
||||
#include "DeviceCore/DevExtensionTool.h"
|
||||
#include "DeviceCore/DevExtruderSystem.h"
|
||||
#include "DeviceCore/DevNozzleSystem.h"
|
||||
@@ -39,6 +40,7 @@
|
||||
#include "DeviceCore/DevHMS.h"
|
||||
|
||||
#include "DeviceCore/DevMapping.h"
|
||||
#include "DeviceCore/DevMappingNozzle.h"
|
||||
#include "DeviceCore/DevManager.h"
|
||||
#include "DeviceCore/DevUtil.h"
|
||||
|
||||
@@ -567,11 +569,14 @@ MachineObject::MachineObject(DeviceManager* manager, NetworkAgent* agent, std::s
|
||||
m_extension_tool = DevExtensionTool::Create(this);
|
||||
m_nozzle_system = new DevNozzleSystem(this);
|
||||
m_fila_system = new DevFilaSystem(this);
|
||||
m_fila_switch = new DevFilaSwitch(this);
|
||||
m_hms_system = new DevHMS(this);
|
||||
m_config = new DevConfig(this);
|
||||
|
||||
m_ctrl = new DevCtrl(this);
|
||||
m_print_options = new DevPrintOptions(this);
|
||||
|
||||
m_nozzle_mapping_ptr = std::make_shared<DevNozzleMappingCtrl>(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -615,6 +620,9 @@ MachineObject::~MachineObject()
|
||||
delete m_fila_system;
|
||||
m_fila_system = nullptr;
|
||||
|
||||
delete m_fila_switch;
|
||||
m_fila_switch = nullptr;
|
||||
|
||||
delete m_hms_system;
|
||||
m_hms_system = nullptr;
|
||||
|
||||
@@ -862,6 +870,9 @@ void MachineObject::clear_version_info()
|
||||
cutting_module_version_info = DevFirmwareVersionInfo();
|
||||
extinguish_version_info = DevFirmwareVersionInfo();
|
||||
module_vers.clear();
|
||||
// Drop cached rack-hotend (WTM) firmware alongside the module list.
|
||||
// Inert for non-rack printers (rack firmware map is empty).
|
||||
m_nozzle_system->ClearFirmwareInfoWTM();
|
||||
}
|
||||
|
||||
void MachineObject::store_version_info(const DevFirmwareVersionInfo& info)
|
||||
@@ -874,6 +885,11 @@ void MachineObject::store_version_info(const DevFirmwareVersionInfo& info)
|
||||
cutting_module_version_info = info;
|
||||
} else if (info.isExtinguishSystem()) {
|
||||
extinguish_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);
|
||||
}
|
||||
|
||||
module_vers.emplace(info.name, info);
|
||||
@@ -1976,6 +1992,10 @@ int MachineObject::command_set_pa_calibration(const std::vector<PACalibResult> &
|
||||
j["print"]["filaments"][i]["n_coef"] = std::to_string(pa_calib_values[i].n_coef);
|
||||
else
|
||||
j["print"]["filaments"][i]["n_coef"] = "0.0";
|
||||
if (pa_calib_values[i].nozzle_pos_id >= 0) {
|
||||
j["print"]["filaments"][i]["nozzle_pos"] = pa_calib_values[i].nozzle_pos_id;
|
||||
j["print"]["filaments"][i]["nozzle_sn"] = pa_calib_values[i].nozzle_sn;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "extrusion_cali_set: " << j.dump();
|
||||
@@ -1994,6 +2014,10 @@ int MachineObject::command_delete_pa_calibration(const PACalibIndexInfo& pa_cali
|
||||
j["print"]["nozzle_id"] = _generate_nozzle_id(pa_calib.nozzle_volume_type, to_string_nozzle_diameter(pa_calib.nozzle_diameter)).ToStdString();
|
||||
j["print"]["filament_id"] = pa_calib.filament_id;
|
||||
j["print"]["cali_idx"] = pa_calib.cali_idx;
|
||||
if (pa_calib.nozzle_pos_id >= 0) {
|
||||
j["print"]["nozzle_pos"] = pa_calib.nozzle_pos_id;
|
||||
j["print"]["nozzle_sn"] = pa_calib.nozzle_sn;
|
||||
}
|
||||
j["print"]["nozzle_diameter"] = to_string_nozzle_diameter(pa_calib.nozzle_diameter);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "extrusion_cali_del: " << j.dump();
|
||||
@@ -2014,6 +2038,11 @@ int MachineObject::command_get_pa_calibration_tab(const PACalibExtruderInfo &cal
|
||||
j["print"]["nozzle_id"] = _generate_nozzle_id(calib_info.nozzle_volume_type, to_string_nozzle_diameter(calib_info.nozzle_diameter)).ToStdString();
|
||||
j["print"]["nozzle_diameter"] = to_string_nozzle_diameter(calib_info.nozzle_diameter);
|
||||
|
||||
if (calib_info.nozzle_pos_id >= 0) {
|
||||
j["print"]["nozzle_pos"] = calib_info.nozzle_pos_id;
|
||||
j["print"]["nozzle_sn"] = calib_info.nozzle_sn;
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "extrusion_cali_get: " << j.dump();
|
||||
request_tab_from_bbs = true;
|
||||
return this->publish_json(j);
|
||||
@@ -2040,6 +2069,10 @@ int MachineObject::commnad_select_pa_calibration(const PACalibIndexInfo& pa_cali
|
||||
j["print"]["slot_id"] = pa_calib_info.slot_id;
|
||||
j["print"]["cali_idx"] = pa_calib_info.cali_idx;
|
||||
j["print"]["filament_id"] = pa_calib_info.filament_id;
|
||||
if (pa_calib_info.nozzle_pos_id >= 0) {
|
||||
j["print"]["nozzle_pos"] = pa_calib_info.nozzle_pos_id;
|
||||
j["print"]["nozzle_sn"] = pa_calib_info.nozzle_sn;
|
||||
}
|
||||
j["print"]["nozzle_diameter"] = to_string_nozzle_diameter(pa_calib_info.nozzle_diameter);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "extrusion_cali_sel: " << j.dump();
|
||||
@@ -2379,6 +2412,7 @@ void MachineObject::reset()
|
||||
jobState_ = 0;
|
||||
m_plate_index = -1;
|
||||
device_cert_installed = false;
|
||||
clear_auto_nozzle_mapping();// reset nozzle mapping
|
||||
|
||||
// reset print_json
|
||||
json empty_j;
|
||||
@@ -2896,6 +2930,7 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
}
|
||||
|
||||
m_fan->ParseV2_0(jj);
|
||||
m_fila_switch->ParseFilaSwitchInfo(jj);
|
||||
|
||||
if (jj.contains("support_filament_backup")) {
|
||||
if (jj["support_filament_backup"].is_boolean()) {
|
||||
@@ -3000,6 +3035,8 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
|
||||
|
||||
if (jj.contains("command")) {
|
||||
m_nozzle_mapping_ptr->ParseAutoNozzleMapping(jj);
|
||||
|
||||
if (jj["command"].get<std::string>() == "ams_change_filament") {
|
||||
if (jj.contains("errno")) {
|
||||
if (jj["errno"].is_number()) {
|
||||
@@ -4082,6 +4119,14 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
pa_calib_result.nozzle_volume_type = convert_to_nozzle_type((*it)["nozzle_id"].get<std::string>());
|
||||
}
|
||||
|
||||
if ((*it).contains("nozzle_pos")) {
|
||||
pa_calib_result.nozzle_pos_id = (*it)["nozzle_pos"].get<int>();
|
||||
}
|
||||
|
||||
if ((*it).contains("nozzle_sn")) {
|
||||
pa_calib_result.nozzle_sn = (*it)["nozzle_sn"].get<std::string>();
|
||||
}
|
||||
|
||||
if (jj["nozzle_diameter"].is_number_float()) {
|
||||
pa_calib_result.nozzle_diameter = jj["nozzle_diameter"].get<float>();
|
||||
} else if (jj["nozzle_diameter"].is_string()) {
|
||||
@@ -4179,6 +4224,14 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
pa_calib_result.nozzle_volume_type = NozzleVolumeType::nvtStandard;
|
||||
}
|
||||
|
||||
if (it->contains("nozzle_pos")) {
|
||||
pa_calib_result.nozzle_pos_id = (*it)["nozzle_pos"].get<int>();
|
||||
}
|
||||
|
||||
if (it->contains("nozzle_sn")) {
|
||||
pa_calib_result.nozzle_sn = (*it)["nozzle_sn"].get<std::string>();
|
||||
}
|
||||
|
||||
if ((*it)["k_value"].is_number_float())
|
||||
pa_calib_result.k_value = (*it)["k_value"].get<float>();
|
||||
else if ((*it)["k_value"].is_string())
|
||||
@@ -5004,6 +5057,7 @@ void MachineObject::parse_new_info(json print)
|
||||
is_support_user_preset = get_flag_bits(fun, 11);
|
||||
is_support_door_open_check = get_flag_bits(fun, 12);
|
||||
is_support_nozzle_blob_detection = get_flag_bits(fun, 13);
|
||||
m_nozzle_system->SetSupportNozzleRack(get_flag_bits(fun, 60)); // H2C hotend rack support (device-side gate for the sync dialog)
|
||||
is_support_upgrade_kit = get_flag_bits(fun, 14);
|
||||
is_support_internal_timelapse = get_flag_bits(fun, 28);
|
||||
m_support_mqtt_homing = get_flag_bits(fun, 32);
|
||||
@@ -5066,7 +5120,10 @@ void MachineObject::parse_new_info(json print)
|
||||
|
||||
DevBed::ParseV2_0(device,m_bed);
|
||||
|
||||
if (device.contains("nozzle")) { DevNozzleSystemParser::ParseV2_0(device["nozzle"], m_nozzle_system); }
|
||||
// Pass the whole device json: ParseV2_0 reads ext/rack nozzles from device["nozzle"] and the
|
||||
// hotend-rack state from device["holder"] (rack data lives outside device["nozzle"]). It is a
|
||||
// no-op for devices that report neither key, so non-rack machines are unaffected.
|
||||
DevNozzleSystemParser::ParseV2_0(device, m_nozzle_system);
|
||||
if (device.contains("extruder")) { ExtderSystemParser::ParseV2_0(device["extruder"], m_extder_system);}
|
||||
if (device.contains("ext_tool")) { DevExtensionToolParser::ParseV2_0(device["ext_tool"], m_extension_tool); }
|
||||
|
||||
|
||||
@@ -83,10 +83,12 @@ class DevExtensionTool;
|
||||
class DevExtderSystem;
|
||||
class DevFan;
|
||||
class DevFilaSystem;
|
||||
class DevFilaSwitch;
|
||||
class DevPrintOptions;
|
||||
class DevHMS;
|
||||
class DevLamp;
|
||||
class DevNozzleSystem;
|
||||
class DevNozzleMappingCtrl;
|
||||
class DeviceManager;
|
||||
class DevStorage;
|
||||
struct DevPrintTaskRatingInfo;
|
||||
@@ -115,6 +117,7 @@ private:
|
||||
DevExtderSystem* m_extder_system;
|
||||
DevNozzleSystem* m_nozzle_system;
|
||||
DevFilaSystem* m_fila_system;
|
||||
DevFilaSwitch* m_fila_switch;
|
||||
DevFan* m_fan;
|
||||
DevBed * m_bed;
|
||||
DevStorage* m_storage;
|
||||
@@ -131,6 +134,11 @@ private:
|
||||
/*Config*/
|
||||
DevConfig* m_config;
|
||||
|
||||
/* print-dispatch nozzle mapping (H2C hotend rack). Created unconditionally in the ctor so
|
||||
get_nozzle_mapping_result() is always valid; stays empty (no result attached) for every
|
||||
non-rack printer. */
|
||||
std::shared_ptr<DevNozzleMappingCtrl> m_nozzle_mapping_ptr;
|
||||
|
||||
public:
|
||||
MachineObject(DeviceManager* manager, NetworkAgent* agent, std::string name, std::string id, std::string ip);
|
||||
~MachineObject();
|
||||
@@ -329,7 +337,12 @@ public:
|
||||
|
||||
DevNozzleSystem* GetNozzleSystem() const { return m_nozzle_system;}
|
||||
|
||||
/* print-dispatch nozzle mapping (H2C hotend rack); result stays empty for non-rack printers */
|
||||
std::shared_ptr<DevNozzleMappingCtrl> get_nozzle_mapping_result() const { return m_nozzle_mapping_ptr; }
|
||||
void clear_auto_nozzle_mapping();// defined in DevMappingNozzle.cpp
|
||||
|
||||
DevFilaSystem* GetFilaSystem() const { return m_fila_system;}
|
||||
DevFilaSwitch* GetFilaSwitch() const { return m_fila_switch;}
|
||||
bool HasAms() const;
|
||||
|
||||
DevLamp* GetLamp() const { return m_lamp; }
|
||||
|
||||
@@ -4,5 +4,15 @@ list(APPEND SLIC3R_GUI_SOURCES
|
||||
GUI/DeviceTab/uiAmsHumidityPopup.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
|
||||
GUI/DeviceTab/wgtDeviceNozzleRackUpdate.cpp
|
||||
GUI/DeviceTab/wgtDeviceNozzleSelect.h
|
||||
GUI/DeviceTab/wgtDeviceNozzleSelect.cpp
|
||||
GUI/DeviceTab/wgtMsgBox.h
|
||||
GUI/DeviceTab/wgtMsgBox.cpp
|
||||
)
|
||||
set(SLIC3R_GUI_SOURCES ${SLIC3R_GUI_SOURCES} PARENT_SCOPE)
|
||||
@@ -0,0 +1,700 @@
|
||||
//**********************************************************/
|
||||
/* File: wgtDeviceNozzleRack.cpp
|
||||
* Description: The Device-tab panel with the toolhead nozzle and the H2C induction hotend rack.
|
||||
*
|
||||
* \n class wgtDeviceNozzleRack;
|
||||
* \n class wgtDeviceNozzleRackToolHead;
|
||||
* \n class wgtDeviceNozzleRackArea;
|
||||
* \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"
|
||||
#include "wgtDeviceNozzleRackUpdate.h"
|
||||
|
||||
#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/wxExtensions.hpp"
|
||||
|
||||
#include "slic3r/GUI/Widgets/Button.hpp"
|
||||
#include "slic3r/GUI/Widgets/Label.hpp"
|
||||
|
||||
#define WX_DIP_SIZE(x, y) wxSize(FromDIP(x), FromDIP(y))
|
||||
|
||||
#define L_RAW_A_STR _L("Row A")
|
||||
#define L_RAW_B_STR _L("Row B")
|
||||
|
||||
// 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("#00AE42");
|
||||
|
||||
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;
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
// Orca: StateColor has no gray button style, so define one file-local. This panel is its only
|
||||
// consumer, so keeping it here avoids touching the shared StateColor widget.
|
||||
static StateColor s_button_style_gray()
|
||||
{
|
||||
return StateColor(std::pair<wxColour, int>(wxColour(206, 206, 206), StateColor::Pressed),
|
||||
std::pair<wxColour, int>(*wxWHITE, StateColor::Focused),
|
||||
std::pair<wxColour, int>(wxColour(238, 238, 238), StateColor::Hovered),
|
||||
std::pair<wxColour, int>(*wxWHITE, StateColor::Normal));
|
||||
}
|
||||
|
||||
wgtDeviceNozzleRack::wgtDeviceNozzleRack(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style)
|
||||
: wxPanel(parent, id, pos, size, style)
|
||||
{
|
||||
CreateGui();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRack::CreateGui()
|
||||
{
|
||||
m_toolhead_panel = new wgtDeviceNozzleRackToolHead(this);
|
||||
m_rack_area = new wgtDeviceNozzleRackArea(this);
|
||||
|
||||
wxPanel* separator = new wxPanel(this);
|
||||
separator->SetMaxSize(wxSize(FromDIP(1), -1));
|
||||
separator->SetMinSize(wxSize(FromDIP(1), -1));
|
||||
separator->SetBackgroundColour(WGT_GREY300);
|
||||
|
||||
wxSizer* main_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
main_sizer->AddStretchSpacer();
|
||||
main_sizer->Add(m_toolhead_panel, 0, wxEXPAND);
|
||||
main_sizer->Add(separator, 0, wxEXPAND);
|
||||
main_sizer->Add(m_rack_area, 0, wxEXPAND);
|
||||
main_sizer->AddStretchSpacer();
|
||||
|
||||
SetSizer(main_sizer);
|
||||
SetMaxSize(WX_DIP_SIZE(586, -1));
|
||||
SetMinSize(WX_DIP_SIZE(586, -1));
|
||||
SetSize(WX_DIP_SIZE(586, -1));
|
||||
Layout();
|
||||
|
||||
wxGetApp().UpdateDarkUIWin(this);
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRack::UpdateRackInfo(std::shared_ptr<DevNozzleRack> rack)
|
||||
{
|
||||
if (!rack->IsSupported()) { return; }
|
||||
|
||||
m_nozzle_rack = rack;
|
||||
if (m_nozzle_rack.expired()) { return; }
|
||||
|
||||
DevNozzleSystem* nozzle_system = m_nozzle_rack.lock()->GetNozzleSystem();
|
||||
if (nozzle_system)
|
||||
{
|
||||
m_toolhead_panel->UpdateToolHeadInfo(nozzle_system->GetExtNozzle(MAIN_EXTRUDER_ID));
|
||||
m_rack_area->UpdateRackInfo(m_nozzle_rack);
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRack::Rescale()
|
||||
{
|
||||
m_toolhead_panel->Rescale();
|
||||
m_rack_area->Rescale();
|
||||
Layout();
|
||||
}
|
||||
|
||||
class wgtDeviceNozzleRackTitle : public StaticBox
|
||||
{
|
||||
public:
|
||||
wgtDeviceNozzleRackTitle(wxWindow* parent, const wxString& title) : StaticBox(parent)
|
||||
{
|
||||
SetBackgroundColour(WGT_GREY200);
|
||||
SetBorderColor(*wxWHITE);
|
||||
SetCornerRadius(0);
|
||||
|
||||
m_title_label = new Label(this, title);
|
||||
m_title_label->SetFont(Label::Body_14);
|
||||
m_title_label->SetBackgroundColour(WGT_GREY200);
|
||||
|
||||
wxSizer* title_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
title_sizer->AddStretchSpacer();
|
||||
title_sizer->Add(m_title_label, 0, wxEXPAND | wxALIGN_CENTER | wxTOP | wxBOTTOM, FromDIP(5));
|
||||
title_sizer->AddStretchSpacer();
|
||||
SetSizer(title_sizer);
|
||||
};
|
||||
|
||||
public:
|
||||
void SetLabel(const wxString& new_label) { m_title_label->SetLabel(new_label); }
|
||||
|
||||
private:
|
||||
Label* m_title_label;
|
||||
};
|
||||
|
||||
|
||||
void wgtDeviceNozzleRackToolHead::CreateGui()
|
||||
{
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Create Header
|
||||
wgtDeviceNozzleRackTitle* title_box = new wgtDeviceNozzleRackTitle(this, _L("Toolhead"));
|
||||
mainSizer->Add(title_box, 0, wxEXPAND | wxTOP);
|
||||
mainSizer->AddStretchSpacer();
|
||||
|
||||
// Image
|
||||
m_extruder_nozzle_empty = new ScalableBitmap(this, "dev_rack_toolhead_empty", 98);
|
||||
m_extruder_nozzle_normal = new ScalableBitmap(this, "dev_rack_toolhead_normal", 98);
|
||||
m_toolhead_icon = new wxStaticBitmap(this, wxID_ANY, m_extruder_nozzle_empty->bmp(), wxDefaultPosition, WX_DIP_SIZE(98, 98));
|
||||
mainSizer->Add(m_toolhead_icon, 0, wxALIGN_CENTRE_HORIZONTAL | wxTOP, FromDIP(20));
|
||||
|
||||
// Nozzle info
|
||||
m_nozzle_diamenter_label = new Label(this);
|
||||
m_nozzle_diamenter_label->SetFont(Label::Body_13);
|
||||
m_nozzle_diamenter_label->SetBackgroundColour(*wxWHITE);
|
||||
mainSizer->Add(m_nozzle_diamenter_label, 0, wxALIGN_CENTRE_HORIZONTAL | wxBOTTOM | wxTOP, FromDIP(5));
|
||||
|
||||
m_nozzle_flowtype_label = new Label(this);
|
||||
m_nozzle_flowtype_label->SetFont(Label::Body_13);
|
||||
m_nozzle_flowtype_label->SetBackgroundColour(*wxWHITE);
|
||||
mainSizer->Add(m_nozzle_flowtype_label, 0, wxALIGN_CENTRE_HORIZONTAL);
|
||||
mainSizer->AddStretchSpacer();
|
||||
|
||||
// Set sizer
|
||||
SetSizer(mainSizer);
|
||||
SetMaxSize(WX_DIP_SIZE(132, -1));
|
||||
SetMinSize(WX_DIP_SIZE(132, -1));
|
||||
SetSize(WX_DIP_SIZE(132, -1));
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackToolHead::UpdateToolHeadInfo(const DevNozzle& extruder_nozzle)
|
||||
{
|
||||
/* Labels */
|
||||
if (extruder_nozzle.IsEmpty())
|
||||
{
|
||||
m_nozzle_diamenter_label->Show(false);
|
||||
m_nozzle_flowtype_label->SetLabel(_L("Empty"));
|
||||
}
|
||||
else if (extruder_nozzle.IsUnknown())
|
||||
{
|
||||
m_nozzle_diamenter_label->Show(false);
|
||||
m_nozzle_flowtype_label->SetLabel(_L("Unknown"));
|
||||
}
|
||||
else if (extruder_nozzle.IsAbnormal())
|
||||
{
|
||||
m_nozzle_diamenter_label->Show(false);
|
||||
m_nozzle_flowtype_label->SetLabel(_L("Error"));
|
||||
}
|
||||
else /*extruder_nozzle.IsNormal()*/
|
||||
{
|
||||
m_nozzle_diamenter_label->Show(true);
|
||||
m_nozzle_diamenter_label->SetLabel(extruder_nozzle.GetNozzleDiameterStr());
|
||||
m_nozzle_flowtype_label->SetLabel(extruder_nozzle.GetNozzleFlowTypeStr());
|
||||
}
|
||||
|
||||
/* Icon*/
|
||||
bool extruder_exist = !extruder_nozzle.IsEmpty();
|
||||
if (m_extruder_nozzle_exist != extruder_exist)
|
||||
{
|
||||
m_extruder_nozzle_exist = extruder_exist;
|
||||
m_filament_color = extruder_nozzle.GetFilamentColor();
|
||||
m_toolhead_icon->SetBitmap(m_extruder_nozzle_exist ? SetNozzleBmpColor(m_extruder_nozzle_normal->bmp(), m_filament_color) : m_extruder_nozzle_empty->bmp());
|
||||
m_toolhead_icon->Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackToolHead::Rescale()
|
||||
{
|
||||
m_extruder_nozzle_normal->msw_rescale();
|
||||
m_extruder_nozzle_empty->msw_rescale();
|
||||
m_toolhead_icon->SetBitmap(m_extruder_nozzle_exist ? SetNozzleBmpColor(m_extruder_nozzle_normal->bmp(), m_filament_color) : m_extruder_nozzle_empty->bmp());
|
||||
|
||||
Layout();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackArea::CreateGui()
|
||||
{
|
||||
wxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Create Header
|
||||
m_title_nozzle_rack = new wgtDeviceNozzleRackTitle(this, _L("Induction Hotend Rack"));
|
||||
main_sizer->Add(m_title_nozzle_rack, 0, wxEXPAND | wxTOP);
|
||||
|
||||
// Create Simple Book
|
||||
m_simple_book = new wxSimplebook(this, wxID_ANY);
|
||||
|
||||
wxSizer* content_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_panel_content = new wxPanel(m_simple_book, wxID_ANY);
|
||||
m_panel_refresh = new wxPanel(m_simple_book, wxID_ANY);
|
||||
|
||||
// Create Hotends ans Rack Position Panel
|
||||
wxSizer* hotends_rack_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
// Hotends
|
||||
m_hotends_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_arow_nozzles_box = CreateNozzleBox( { 0, 2, 4});
|
||||
m_brow_nozzles_box = CreateNozzleBox( { 1, 3, 5});
|
||||
m_hotends_sizer->Add(m_arow_nozzles_box);
|
||||
m_hotends_sizer->Add(m_brow_nozzles_box);
|
||||
hotends_rack_sizer->Add(m_hotends_sizer, 0, wxLEFT, FromDIP(8));
|
||||
|
||||
// Rack
|
||||
m_rack_pos_panel = new wgtDeviceNozzleRackPos(m_panel_content);
|
||||
hotends_rack_sizer->Add(m_rack_pos_panel, 0, wxEXPAND);
|
||||
content_sizer->Add(hotends_rack_sizer, 0);
|
||||
|
||||
wxSizer* btn_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_btn_hotends_infos = new Button(m_panel_content, _L("Hotends Info"));
|
||||
m_btn_hotends_infos->SetFont(Label::Body_12);
|
||||
m_btn_hotends_infos->SetBackgroundColor(s_button_style_gray());
|
||||
m_btn_hotends_infos->SetBackgroundColour(*wxWHITE);
|
||||
m_btn_hotends_infos->Bind(wxEVT_BUTTON, &wgtDeviceNozzleRackArea::OnBtnHotendsInfos, this);
|
||||
|
||||
m_btn_read_all = new Button(m_panel_content, _L("Read All"));
|
||||
m_btn_read_all->SetFont(Label::Body_12);
|
||||
m_btn_read_all->SetBackgroundColor(s_button_style_gray());
|
||||
m_btn_read_all->SetBackgroundColour(*wxWHITE);
|
||||
m_btn_read_all->Bind(wxEVT_BUTTON, &wgtDeviceNozzleRackArea::OnBtnReadAll, this);
|
||||
|
||||
btn_sizer->Add(m_btn_hotends_infos, 0, wxLEFT);
|
||||
btn_sizer->Add(m_btn_read_all, 0, wxLEFT, FromDIP(5));
|
||||
content_sizer->Add(btn_sizer, 0, wxLEFT, FromDIP(10));
|
||||
|
||||
/* refresh panel */
|
||||
wxSizer* refresh_sizer = CreateRefreshBook(m_panel_refresh);
|
||||
|
||||
m_panel_content->SetSizer(content_sizer);
|
||||
m_panel_refresh->SetSizer(refresh_sizer);
|
||||
m_simple_book->AddPage(m_panel_content, "Content");
|
||||
m_simple_book->AddPage(m_panel_refresh, "Refresh");
|
||||
main_sizer->Add(m_simple_book, 1, wxEXPAND);
|
||||
|
||||
m_simple_book->SetSelection(0);
|
||||
|
||||
SetSizer(main_sizer);
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
|
||||
wxSizer* wgtDeviceNozzleRackArea::CreateRefreshBook(wxPanel* parent)
|
||||
{
|
||||
wxSizer* refresh_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
std::vector<std::string> list{"ams_rfid_1", "ams_rfid_2", "ams_rfid_3", "ams_rfid_4"};
|
||||
m_refresh_icon = new AnimaIcon(parent, wxID_ANY, list, "refresh_printer", 100);
|
||||
m_refresh_icon->SetMinSize(wxSize(FromDIP(25), FromDIP(25)));
|
||||
|
||||
wxSizer* progress_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
Label* progress_prefix = new Label(parent, _L("Reading "));
|
||||
progress_prefix->SetBackgroundColour(*wxWHITE);
|
||||
m_progress_refresh = new Label(parent, "(1/6)");
|
||||
m_progress_refresh->SetFont(Label::Body_14);
|
||||
m_progress_refresh->SetBackgroundColour(*wxWHITE);
|
||||
m_progress_refresh->SetForegroundColour(*wxGREEN);
|
||||
Label* progress_suffix = new Label(parent, " ...");
|
||||
progress_suffix->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
progress_sizer->Add(progress_prefix, 0, wxLEFT);
|
||||
progress_sizer->Add(m_progress_refresh, 0, wxLEFT);
|
||||
progress_sizer->Add(progress_suffix, 0, wxLEFT);
|
||||
|
||||
Label* refresh_tip = new Label(parent, _L("Please wait"));
|
||||
refresh_tip->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
refresh_sizer->Add(0, 0, 1, wxEXPAND, 0);
|
||||
refresh_sizer->Add(m_refresh_icon, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
refresh_sizer->Add(progress_sizer, 0, wxALIGN_CENTER_HORIZONTAL, FromDIP(0));
|
||||
refresh_sizer->Add(refresh_tip, 0, wxALIGN_CENTER_HORIZONTAL, FromDIP(0));
|
||||
refresh_sizer->Add(0, 0, 1, wxEXPAND, 0);
|
||||
|
||||
return refresh_sizer;
|
||||
}
|
||||
|
||||
StaticBox* wgtDeviceNozzleRackArea::CreateNozzleBox(const std::vector<int> nozzle_idxes)
|
||||
{
|
||||
StaticBox* nozzle_box = new StaticBox(m_panel_content);
|
||||
nozzle_box->SetBackgroundColor(*wxWHITE);
|
||||
nozzle_box->SetBorderColor(*wxWHITE);
|
||||
nozzle_box->SetCornerRadius(0);
|
||||
|
||||
wxSizer* h_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
for (auto start_idx : nozzle_idxes)
|
||||
{
|
||||
wgtDeviceNozzleRackNozzleItem* nozzle_item = new wgtDeviceNozzleRackNozzleItem(nozzle_box, start_idx);
|
||||
m_nozzle_items[start_idx] = nozzle_item;
|
||||
h_sizer->Add(nozzle_item, 0, wxALL, FromDIP(8));
|
||||
}
|
||||
|
||||
nozzle_box->SetSizer(h_sizer);
|
||||
return nozzle_box;
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackArea::UpdateNozzleItems(const std::unordered_map<int, wgtDeviceNozzleRackNozzleItem*>& nozzle_items,
|
||||
std::shared_ptr<DevNozzleRack> nozzle_rack)
|
||||
{
|
||||
for (auto iter : nozzle_items)
|
||||
{
|
||||
iter.second->Update(nozzle_rack);
|
||||
}
|
||||
|
||||
/*update nozzle possition and background*/
|
||||
if (nozzle_rack->GetReadingCount() != 0)
|
||||
{
|
||||
m_progress_refresh->SetLabel(wxString::Format("(%d/%d)", nozzle_rack->GetReadingIdx(), nozzle_rack->GetReadingCount()));
|
||||
if(!m_refresh_icon->IsPlaying()) {
|
||||
m_simple_book->SetSelection(1);
|
||||
m_refresh_icon->Play();
|
||||
}
|
||||
return;
|
||||
} else{
|
||||
m_refresh_icon->Stop();
|
||||
m_simple_book->SetSelection(0);
|
||||
}
|
||||
|
||||
const DevNozzleRack::RackPos new_pos = nozzle_rack->GetPosition();
|
||||
const DevNozzleRack::RackStatus new_status = nozzle_rack->GetStatus();
|
||||
if (m_rack_pos != new_pos || m_rack_status != new_status)
|
||||
{
|
||||
m_rack_pos = new_pos;
|
||||
m_rack_status = new_status;
|
||||
if (m_rack_status == DevNozzleRack::RACK_STATUS_IDLE)
|
||||
{
|
||||
m_hotends_sizer->Clear();
|
||||
if (m_rack_pos == DevNozzleRack::RACK_POS_B_TOP)
|
||||
{
|
||||
m_hotends_sizer->Add(m_brow_nozzles_box);
|
||||
m_hotends_sizer->Add(m_arow_nozzles_box);
|
||||
}
|
||||
else if (m_rack_pos == DevNozzleRack::RACK_POS_A_TOP)
|
||||
{
|
||||
m_hotends_sizer->Add(m_arow_nozzles_box);
|
||||
m_hotends_sizer->Add(m_brow_nozzles_box);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_hotends_sizer->Add(m_arow_nozzles_box);
|
||||
m_hotends_sizer->Add(m_brow_nozzles_box);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackArea::UpdateRackInfo(std::weak_ptr<DevNozzleRack> rack)
|
||||
{
|
||||
m_nozzle_rack = rack;
|
||||
const auto& nozzle_rack = rack.lock();
|
||||
if (nozzle_rack)
|
||||
{
|
||||
UpdateNozzleItems(m_nozzle_items, nozzle_rack);
|
||||
m_rack_pos_panel->UpdateRackPos(nozzle_rack);
|
||||
m_btn_read_all->Enable(nozzle_rack->CtrlCanReadAll());
|
||||
}
|
||||
|
||||
if (m_rack_upgrade_dlg && m_rack_upgrade_dlg->IsShown())
|
||||
{
|
||||
m_rack_upgrade_dlg->UpdateRackInfo(nozzle_rack);
|
||||
}
|
||||
};
|
||||
|
||||
void wgtDeviceNozzleRackArea::OnBtnHotendsInfos(wxCommandEvent& evt)
|
||||
{
|
||||
const auto& nozzle_rack = m_nozzle_rack.lock();
|
||||
if (nozzle_rack)
|
||||
{
|
||||
m_rack_upgrade_dlg = new wgtDeviceNozzleRackUpgradeDlg((wxWindow*)wxGetApp().mainframe, nozzle_rack);
|
||||
m_rack_upgrade_dlg->ShowModal();
|
||||
|
||||
delete m_rack_upgrade_dlg;
|
||||
m_rack_upgrade_dlg = nullptr;
|
||||
}
|
||||
|
||||
evt.Skip();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackArea::OnBtnReadAll(wxCommandEvent& evt)
|
||||
{
|
||||
if (const auto nozzle_rack = m_nozzle_rack.lock())
|
||||
{
|
||||
nozzle_rack->CtrlRackReadAll(true);
|
||||
}
|
||||
|
||||
evt.Skip();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackArea::Rescale()
|
||||
{
|
||||
for (auto item : m_nozzle_items)
|
||||
{
|
||||
item.second->Rescale();
|
||||
}
|
||||
|
||||
m_rack_pos_panel->Rescale();
|
||||
m_btn_hotends_infos->Rescale();
|
||||
m_btn_read_all->Rescale();
|
||||
}
|
||||
|
||||
static void s_set_bg_style(StaticBox* box,
|
||||
ScalableButton* btn,
|
||||
Label* label_row,
|
||||
Label* label_row_status,
|
||||
const wxColour& clr)
|
||||
{
|
||||
box->SetBorderColor(clr);
|
||||
box->SetBackgroundColor(clr);
|
||||
btn->SetBackgroundColour(clr);
|
||||
label_row->SetBackgroundColour(clr);
|
||||
label_row_status->SetBackgroundColour(clr);
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackPos::CreateGui()
|
||||
{
|
||||
// RowA
|
||||
m_rowup_panel = new StaticBox(this, wxID_ANY);
|
||||
m_rowup_panel->SetCornerRadius(0);
|
||||
|
||||
wxBoxSizer* rowa_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
rowa_sizer->AddStretchSpacer();
|
||||
m_btn_rowup = new ScalableButton(m_rowup_panel, wxID_ANY, "dev_rack_row_up", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, false, 25);
|
||||
m_btn_rowup->Bind(wxEVT_ENTER_WINDOW, [this](auto&) { SetCursor(wxCURSOR_HAND); });
|
||||
m_btn_rowup->Bind(wxEVT_LEAVE_WINDOW, [this](auto&) { SetCursor(wxCURSOR_ARROW); });
|
||||
m_btn_rowup->Bind(wxEVT_BUTTON, &wgtDeviceNozzleRackPos::OnMoveRackUp, this);
|
||||
rowa_sizer->Add(m_btn_rowup, 0, wxALIGN_CENTER | wxEXPAND | wxLEFT | wxRIGHT, FromDIP(10));
|
||||
|
||||
m_label_rowup_status = new Label(m_rowup_panel);
|
||||
m_label_rowup_status->SetFont(Label::Body_12);
|
||||
m_label_rowup_status->Show(false);
|
||||
rowa_sizer->Add(m_label_rowup_status, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, FromDIP(10));
|
||||
|
||||
m_label_rowup = new Label(m_rowup_panel);
|
||||
m_label_rowup->SetFont(Label::Body_14);
|
||||
rowa_sizer->Add(m_label_rowup, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, FromDIP(10));
|
||||
rowa_sizer->AddStretchSpacer();
|
||||
|
||||
m_rowup_panel->SetSizer(rowa_sizer);
|
||||
|
||||
// homing
|
||||
m_btn_homing = new ScalableButton(this, wxID_ANY, "dev_rack_home", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, false, 25);
|
||||
m_btn_homing->SetBackgroundColour(WGT_GREY200);
|
||||
m_btn_homing->Bind(wxEVT_ENTER_WINDOW, [this](auto&) { SetCursor(wxCURSOR_HAND); });
|
||||
m_btn_homing->Bind(wxEVT_LEAVE_WINDOW, [this](auto&) { SetCursor(wxCURSOR_ARROW); });
|
||||
m_btn_homing->Bind(wxEVT_BUTTON, &wgtDeviceNozzleRackPos::OnBtnHomingRack, this);
|
||||
|
||||
// Row B
|
||||
m_rowbottom_panel = new StaticBox(this, wxID_ANY);
|
||||
m_rowbottom_panel->SetCornerRadius(0);
|
||||
|
||||
wxBoxSizer* rowb_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
rowb_sizer->AddStretchSpacer();
|
||||
|
||||
m_btn_rowbottom_up = new ScalableButton(m_rowbottom_panel, wxID_ANY, "dev_rack_row_up", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, false, 25);
|
||||
m_btn_rowbottom_up->Bind(wxEVT_BUTTON, &wgtDeviceNozzleRackPos::OnMoveRackDown, this);
|
||||
m_btn_rowbottom_up->Bind(wxEVT_ENTER_WINDOW, [this](auto&) { SetCursor(wxCURSOR_HAND); });
|
||||
m_btn_rowbottom_up->Bind(wxEVT_LEAVE_WINDOW, [this](auto&) { SetCursor(wxCURSOR_ARROW); });
|
||||
rowb_sizer->Add(m_btn_rowbottom_up, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, FromDIP(10));
|
||||
|
||||
m_label_rowbottom_status = new Label(m_rowbottom_panel);
|
||||
m_label_rowbottom_status->SetFont(Label::Body_12);
|
||||
m_label_rowbottom_status->Show(false);
|
||||
rowb_sizer->Add(m_label_rowbottom_status, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, FromDIP(10));
|
||||
|
||||
m_label_rowbottom = new Label(m_rowbottom_panel);
|
||||
m_label_rowbottom->SetFont(Label::Body_14);
|
||||
rowb_sizer->Add(m_label_rowbottom, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, FromDIP(10));
|
||||
rowb_sizer->AddStretchSpacer();
|
||||
|
||||
m_rowbottom_panel->SetSizer(rowb_sizer);
|
||||
|
||||
// bg style
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
s_set_bg_style(m_rowup_panel, m_btn_rowup, m_label_rowup, m_label_rowup_status, *wxWHITE);
|
||||
s_set_bg_style(m_rowbottom_panel, m_btn_rowbottom_up, m_label_rowbottom, m_label_rowbottom_status, *wxWHITE);
|
||||
|
||||
// main sizer
|
||||
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
main_sizer->Add(m_rowup_panel, 1, wxALIGN_TOP | wxEXPAND | wxALIGN_CENTER);
|
||||
main_sizer->Add(m_btn_homing, 0, wxALIGN_CENTER | wxTOP | wxBOTTOM, FromDIP(10));
|
||||
main_sizer->Add(m_rowbottom_panel, 1, wxALIGN_BOTTOM | wxEXPAND | wxALIGN_CENTER);
|
||||
SetSizer(main_sizer);
|
||||
|
||||
SetMinSize(WX_DIP_SIZE(85, -1));
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackPos::UpdateRackPos(const std::shared_ptr<DevNozzleRack>& rack)
|
||||
{
|
||||
m_rack = rack;
|
||||
if (rack)
|
||||
{
|
||||
UpdateRackPos(rack->GetPosition(), rack->GetStatus(), rack->GetReadingCount() > 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void s_show_label(Label* label, const wxString& text)
|
||||
{
|
||||
label->SetLabel(text);
|
||||
label->Show();
|
||||
}
|
||||
|
||||
static void s_show_label(Label* label, const wxString& text, const wxColour& text_color)
|
||||
{
|
||||
label->SetLabel(text);
|
||||
label->SetForegroundColour(StateColor::darkModeColorFor(text_color));
|
||||
label->Show();
|
||||
}
|
||||
|
||||
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.
|
||||
if (is_reading)
|
||||
{
|
||||
s_show_label(m_label_rowup, L_RAW_A_STR, *wxBLACK);
|
||||
s_show_label(m_label_rowup_status, _L("Running..."));
|
||||
|
||||
s_show_label(m_label_rowbottom, L_RAW_B_STR, *wxBLACK);
|
||||
s_show_label(m_label_rowbottom_status, _L("Running..."));
|
||||
|
||||
m_btn_rowup->Show(false);
|
||||
m_btn_rowbottom_up->Show(false);
|
||||
|
||||
m_rack_pos = DevNozzleRack::RACK_POS_UNKNOWN;
|
||||
m_rack_status = DevNozzleRack::RACK_STATUS_UNKNOWN;
|
||||
return;
|
||||
}
|
||||
|
||||
if (new_pos != m_rack_pos || m_rack_status != new_status)
|
||||
{
|
||||
m_rack_pos = new_pos;
|
||||
m_rack_status = new_status;
|
||||
|
||||
if (m_rack_status != DevNozzleRack::RACK_STATUS_IDLE)
|
||||
{
|
||||
s_show_label(m_label_rowup, L_RAW_A_STR, *wxBLACK);
|
||||
s_show_label(m_label_rowup_status, _L("Running..."));
|
||||
|
||||
s_show_label(m_label_rowbottom, L_RAW_B_STR, *wxBLACK);
|
||||
s_show_label(m_label_rowbottom_status, _L("Running..."));
|
||||
|
||||
m_btn_rowup->Show(false);
|
||||
m_btn_rowbottom_up->Show(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (new_pos == DevNozzleRack::RACK_POS_A_TOP)
|
||||
{
|
||||
s_show_label(m_label_rowup, L_RAW_A_STR, s_hgreen_clr);
|
||||
s_show_label(m_label_rowup_status, _L("Raised"));
|
||||
|
||||
m_rowbottom_panel->SetBorderColor(*wxWHITE);
|
||||
m_rowbottom_panel->SetBackgroundColor(*wxWHITE);
|
||||
s_show_label(m_label_rowbottom, L_RAW_B_STR, *wxBLACK);
|
||||
m_label_rowbottom_status->Show(false);
|
||||
|
||||
m_btn_rowup->Show(false);
|
||||
m_btn_rowbottom_up->Show(true);
|
||||
}
|
||||
else if (new_pos == DevNozzleRack::RACK_POS_B_TOP)
|
||||
{
|
||||
s_show_label(m_label_rowup, L_RAW_B_STR, s_hgreen_clr);
|
||||
s_show_label(m_label_rowup_status, _L("Raised"));
|
||||
s_show_label(m_label_rowbottom, L_RAW_A_STR, *wxBLACK);
|
||||
m_label_rowbottom_status->Show(false);
|
||||
|
||||
m_btn_rowup->Show(false);
|
||||
m_btn_rowbottom_up->Show(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
s_show_label(m_label_rowup, L_RAW_A_STR, *wxBLACK);
|
||||
m_label_rowup_status->Show(false);
|
||||
|
||||
s_show_label(m_label_rowbottom, L_RAW_B_STR, *wxBLACK);
|
||||
m_label_rowbottom_status->Show(false);
|
||||
|
||||
m_btn_rowup->Show(true);
|
||||
m_btn_rowbottom_up->Show(true);
|
||||
}
|
||||
}
|
||||
|
||||
Layout();
|
||||
Refresh();
|
||||
}
|
||||
};
|
||||
|
||||
void wgtDeviceNozzleRackPos::OnMoveRackUp(wxCommandEvent& evt)
|
||||
{
|
||||
auto rack = m_rack.lock();
|
||||
if (rack)
|
||||
{
|
||||
if (m_label_rowup->GetLabel() == L_RAW_A_STR)
|
||||
{
|
||||
rack->CtrlRackPosMove(DevNozzleRack::RACK_POS_A_TOP);
|
||||
}
|
||||
else if (m_label_rowup->GetLabel() == L_RAW_B_STR)
|
||||
{
|
||||
rack->CtrlRackPosMove(DevNozzleRack::RACK_POS_B_TOP);
|
||||
}
|
||||
}
|
||||
evt.Skip();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackPos::OnMoveRackDown(wxCommandEvent& evt)
|
||||
{
|
||||
auto rack = m_rack.lock();
|
||||
if (rack)
|
||||
{
|
||||
if (m_label_rowbottom->GetLabel() == L_RAW_A_STR)
|
||||
{
|
||||
rack->CtrlRackPosMove(DevNozzleRack::RACK_POS_A_TOP);
|
||||
}
|
||||
else if (m_label_rowbottom->GetLabel() == L_RAW_B_STR)
|
||||
{
|
||||
rack->CtrlRackPosMove(DevNozzleRack::RACK_POS_B_TOP);
|
||||
}
|
||||
}
|
||||
evt.Skip();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackPos::OnBtnHomingRack(wxCommandEvent& evt)
|
||||
{
|
||||
if (auto rack = m_rack.lock())
|
||||
{
|
||||
rack->CtrlRackPosGoHome();
|
||||
}
|
||||
evt.Skip();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackPos::Rescale()
|
||||
{
|
||||
m_btn_rowup->msw_rescale();
|
||||
m_btn_rowbottom_up->msw_rescale();
|
||||
m_btn_homing->msw_rescale();
|
||||
}
|
||||
|
||||
};// end of namespace Slic3r::GUI
|
||||
@@ -0,0 +1,196 @@
|
||||
//**********************************************************/
|
||||
/* File: wgtDeviceNozzleRack.h
|
||||
* Description: The Device-tab panel with the toolhead nozzle and the H2C induction hotend rack.
|
||||
*
|
||||
* \n class wgtDeviceNozzleRack; // toolhead panel + rack area, side by side
|
||||
* \n class wgtDeviceNozzleRackToolHead;
|
||||
* \n class wgtDeviceNozzleRackArea;
|
||||
* \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;
|
||||
class Label;
|
||||
class ScalableBitmap;
|
||||
class ScalableButton;
|
||||
namespace Slic3r
|
||||
{
|
||||
struct DevNozzle;
|
||||
class DevNozzleRack;
|
||||
namespace GUI
|
||||
{
|
||||
class wgtDeviceNozzleRackArea;
|
||||
class wgtDeviceNozzleRackNozzleItem;
|
||||
class wgtDeviceNozzleRackToolHead;
|
||||
class wgtDeviceNozzleRackPos;
|
||||
class wgtDeviceNozzleRackTitle;
|
||||
class wgtDeviceNozzleRackUpgradeDlg;
|
||||
}
|
||||
};
|
||||
|
||||
namespace Slic3r::GUI
|
||||
{
|
||||
class wgtDeviceNozzleRack : public wxPanel
|
||||
{
|
||||
public:
|
||||
wgtDeviceNozzleRack(wxWindow* parent,
|
||||
wxWindowID id = wxID_ANY,
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize,
|
||||
long style = wxTAB_TRAVERSAL);
|
||||
~wgtDeviceNozzleRack() = default;
|
||||
|
||||
public:
|
||||
void UpdateRackInfo(std::shared_ptr<DevNozzleRack> rack);
|
||||
void Rescale();
|
||||
|
||||
private:
|
||||
void CreateGui();
|
||||
|
||||
private:
|
||||
std::weak_ptr<DevNozzleRack> m_nozzle_rack;
|
||||
|
||||
// GUI
|
||||
wgtDeviceNozzleRackToolHead* m_toolhead_panel{ nullptr };
|
||||
wgtDeviceNozzleRackArea* m_rack_area{ nullptr };
|
||||
};
|
||||
|
||||
|
||||
class wgtDeviceNozzleRackToolHead : public wxPanel
|
||||
{
|
||||
public:
|
||||
wgtDeviceNozzleRackToolHead(wxWindow* parent) : wxPanel(parent) { CreateGui();}
|
||||
|
||||
public:
|
||||
void UpdateToolHeadInfo(const DevNozzle& extruder_nozzle);
|
||||
void Rescale();
|
||||
|
||||
private:
|
||||
void CreateGui();
|
||||
|
||||
private:
|
||||
bool m_extruder_nozzle_exist = false;
|
||||
std::string m_filament_color;
|
||||
|
||||
// GUI
|
||||
ScalableBitmap* m_extruder_nozzle_normal = nullptr;
|
||||
ScalableBitmap* m_extruder_nozzle_empty = nullptr;
|
||||
wxStaticBitmap* m_toolhead_icon;
|
||||
|
||||
Label* m_nozzle_diamenter_label;
|
||||
Label* m_nozzle_flowtype_label;
|
||||
};
|
||||
|
||||
|
||||
class wgtDeviceNozzleRackArea : public wxPanel
|
||||
{
|
||||
public:
|
||||
wgtDeviceNozzleRackArea(wxWindow* parent) : wxPanel(parent) { CreateGui();}
|
||||
|
||||
public:
|
||||
void UpdateRackInfo(std::weak_ptr<DevNozzleRack> rack);
|
||||
void Rescale();
|
||||
|
||||
private:
|
||||
void CreateGui();
|
||||
StaticBox* CreateNozzleBox(const std::vector<int> nozzle_idxes);
|
||||
wxSizer* CreateRefreshBook(wxPanel* parent);
|
||||
|
||||
// updates
|
||||
void UpdateNozzleItems(const std::unordered_map<int, wgtDeviceNozzleRackNozzleItem*>& nozzle_items,
|
||||
std::shared_ptr<DevNozzleRack> nozzle_rack);
|
||||
|
||||
// events
|
||||
void OnBtnHotendsInfos(wxCommandEvent& evt);
|
||||
void OnBtnReadAll(wxCommandEvent& evt);
|
||||
|
||||
private:
|
||||
std::weak_ptr<DevNozzleRack> m_nozzle_rack;
|
||||
DevNozzleRack::RackPos m_rack_pos = DevNozzleRack::RACK_POS_UNKNOWN;
|
||||
DevNozzleRack::RackStatus m_rack_status = DevNozzleRack::RACK_STATUS_UNKNOWN;
|
||||
|
||||
// GUI
|
||||
wxSimplebook* m_simple_book{ nullptr };
|
||||
wxPanel* m_panel_content{ nullptr };
|
||||
wxPanel* m_panel_refresh{ nullptr };
|
||||
|
||||
wgtDeviceNozzleRackTitle* m_title_nozzle_rack;
|
||||
wxBoxSizer* m_hotends_sizer;
|
||||
StaticBox* m_arow_nozzles_box;
|
||||
StaticBox* m_brow_nozzles_box;
|
||||
std::unordered_map<int, wgtDeviceNozzleRackNozzleItem*> m_nozzle_items;
|
||||
|
||||
wgtDeviceNozzleRackPos* m_rack_pos_panel;
|
||||
|
||||
Button* m_btn_hotends_infos;
|
||||
Button* m_btn_read_all;
|
||||
|
||||
/* refresh book */
|
||||
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;
|
||||
};
|
||||
|
||||
class wgtDeviceNozzleRackPos : public wxPanel
|
||||
{
|
||||
public:
|
||||
explicit wgtDeviceNozzleRackPos(wxWindow* parent) : wxPanel(parent) { CreateGui();}
|
||||
|
||||
public:
|
||||
void UpdateRackPos(const std::shared_ptr<DevNozzleRack>& rack);
|
||||
void Rescale();
|
||||
|
||||
private:
|
||||
void CreateGui();
|
||||
|
||||
void UpdateRackPos(DevNozzleRack::RackPos new_pos,
|
||||
DevNozzleRack::RackStatus new_status,
|
||||
bool is_reading);
|
||||
|
||||
// events
|
||||
void OnMoveRackUp(wxCommandEvent& evt);
|
||||
void OnMoveRackDown(wxCommandEvent& evt);
|
||||
void OnBtnHomingRack(wxCommandEvent& evt);
|
||||
|
||||
private:
|
||||
std::weak_ptr<DevNozzleRack> m_rack;
|
||||
DevNozzleRack::RackPos m_rack_pos = DevNozzleRack::RACK_POS_UNKNOWN;
|
||||
DevNozzleRack::RackStatus m_rack_status = DevNozzleRack::RACK_STATUS_UNKNOWN;
|
||||
|
||||
// GUI
|
||||
StaticBox* m_rowup_panel;
|
||||
ScalableButton* m_btn_rowup;
|
||||
Label* m_label_rowup_status{ nullptr };
|
||||
Label* m_label_rowup{ nullptr };
|
||||
|
||||
StaticBox* m_rowbottom_panel;
|
||||
ScalableButton* m_btn_rowbottom_up;
|
||||
Label* m_label_rowbottom_status{ nullptr };
|
||||
Label* m_label_rowbottom{ nullptr };
|
||||
|
||||
ScalableButton* m_btn_homing{ nullptr };
|
||||
};
|
||||
|
||||
};// end of namespace Slic3r::GUI
|
||||
@@ -0,0 +1,347 @@
|
||||
//**********************************************************/
|
||||
/* 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("#00AE42");
|
||||
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
|
||||
@@ -0,0 +1,99 @@
|
||||
//**********************************************************/
|
||||
/* 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
|
||||
@@ -0,0 +1,696 @@
|
||||
//**********************************************************/
|
||||
/* File: wgtDeviceNozzleRackUpdate.cpp
|
||||
* Description: The dialog for reading/upgrading the H2C induction-hotend-rack firmware.
|
||||
*
|
||||
* \n class wgtDeviceNozzleRackUpdate
|
||||
//**********************************************************/
|
||||
|
||||
#include "wgtDeviceNozzleRackUpdate.h"
|
||||
|
||||
#include "slic3r/GUI/DeviceCore/DevNozzleSystem.h"
|
||||
|
||||
#include "slic3r/GUI/MainFrame.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/MsgDialog.hpp"
|
||||
#include "slic3r/GUI/wxExtensions.hpp"
|
||||
|
||||
#include "slic3r/GUI/Widgets/Button.hpp"
|
||||
#include "slic3r/GUI/Widgets/Label.hpp"
|
||||
|
||||
#define WX_DIP_SIZE(x, y) wxSize(FromDIP(x), FromDIP(y))
|
||||
|
||||
static wxColour s_red_clr("#D01B1B");
|
||||
|
||||
namespace Slic3r::GUI
|
||||
{
|
||||
|
||||
wxDEFINE_EVENT(wxEVT_NOZZLE_JUMP_UPGRADE, wxCommandEvent);
|
||||
|
||||
wgtDeviceNozzleRackUpgradeDlg::wgtDeviceNozzleRackUpgradeDlg(wxWindow* parent, const std::shared_ptr<DevNozzleRack> rack)
|
||||
: DPIDialog(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
|
||||
{
|
||||
m_rack_upgrade_panel = new wgtDeviceNozzleRackUprade(this);
|
||||
m_rack_upgrade_panel->UpdateRackInfo(rack);
|
||||
|
||||
Bind(wxEVT_NOZZLE_JUMP_UPGRADE, [this](wxCommandEvent&) {
|
||||
EndModal(wxID_OK);
|
||||
});
|
||||
|
||||
auto main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
main_sizer->Add(m_rack_upgrade_panel, 0, wxEXPAND);
|
||||
SetSizer(main_sizer);
|
||||
Layout();
|
||||
Fit();
|
||||
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackUpgradeDlg::UpdateRackInfo(const std::shared_ptr<DevNozzleRack> rack)
|
||||
{
|
||||
m_rack_upgrade_panel->UpdateRackInfo(rack);
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackUpgradeDlg::on_dpi_changed(const wxRect& suggested_rect)
|
||||
{
|
||||
m_rack_upgrade_panel->Rescale();
|
||||
}
|
||||
|
||||
wgtDeviceNozzleRackUprade::wgtDeviceNozzleRackUprade(wxWindow* parent,
|
||||
wxWindowID id,
|
||||
const wxPoint& pos,
|
||||
const wxSize& size,
|
||||
long style)
|
||||
: wxPanel(parent, id, pos, size, style)
|
||||
{
|
||||
CreateGui();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackUprade::CreateGui()
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
|
||||
// Main vertical sizer
|
||||
auto* main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Header: title + buttons
|
||||
auto* header_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
// Title label
|
||||
auto* title_label = new Label(this, _L("Hotends Info"));
|
||||
title_label->SetFont(Label::Head_14);
|
||||
header_sizer->Add(title_label, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(20));
|
||||
|
||||
// Spacer
|
||||
header_sizer->AddStretchSpacer();
|
||||
main_sizer->Add(header_sizer, 0, wxEXPAND | wxTOP | wxRIGHT, FromDIP(10));
|
||||
|
||||
// "Nozzles"
|
||||
m_extruder_nozzle_item = new wgtDeviceNozzleRackHotendUpdate(this, "R");
|
||||
m_extruder_nozzle_item->UpdateColourStyle(wxColour("#F8F8F8"));
|
||||
m_extruder_nozzle_item->SetExtruderNozzleId(MAIN_EXTRUDER_ID);
|
||||
|
||||
main_sizer->Add(m_extruder_nozzle_item, 0, wxEXPAND | wxALL, FromDIP(12));
|
||||
for (int id = 0; id < 6; id ++)
|
||||
{
|
||||
auto item = new wgtDeviceNozzleRackHotendUpdate(this, wxString::Format("%d", id + 1));
|
||||
item->SetRackNozzleId(id);
|
||||
m_nozzle_items[id] = item;
|
||||
|
||||
main_sizer->Add(item, 0, wxEXPAND | wxALL, FromDIP(12));
|
||||
if (id < 5)
|
||||
{
|
||||
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
|
||||
main_sizer->Add(separator, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(12));
|
||||
}
|
||||
}
|
||||
|
||||
main_sizer->AddSpacer(FromDIP(20));
|
||||
|
||||
// Set sizer
|
||||
this->SetSizer(main_sizer);
|
||||
this->Layout();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackUprade::UpdateRackInfo(const std::shared_ptr<DevNozzleRack> rack)
|
||||
{
|
||||
m_nozzle_rack = rack;
|
||||
if (!rack) { return;}
|
||||
|
||||
// update the nozzles
|
||||
m_extruder_nozzle_item->UpdateExtruderNozzleInfo(rack);
|
||||
for (auto iter : m_nozzle_items)
|
||||
{
|
||||
iter.second->UpdateRackNozzleInfo(rack);
|
||||
}
|
||||
|
||||
// update layout
|
||||
Layout();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackUprade::OnBtnReadAll(wxCommandEvent& e)
|
||||
{
|
||||
if (auto rack = m_nozzle_rack.lock())
|
||||
{
|
||||
rack->CtrlRackReadAll(true);
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackUprade::Rescale()
|
||||
{
|
||||
m_extruder_nozzle_item->Rescale();
|
||||
for (auto& iter : m_nozzle_items)
|
||||
{
|
||||
iter.second->Rescale();
|
||||
}
|
||||
}
|
||||
|
||||
#define WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG *wxWHITE
|
||||
wgtDeviceNozzleRackHotendUpdate::wgtDeviceNozzleRackHotendUpdate(wxWindow* parent, const wxString& idx_text)
|
||||
: StaticBox(parent, wxID_ANY)
|
||||
{
|
||||
CreateGui();
|
||||
|
||||
m_idx_label->SetLabel(idx_text);
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackHotendUpdate::CreateGui()
|
||||
{
|
||||
SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
SetBorderColor(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
SetCornerRadius(0);
|
||||
|
||||
//load nozzle hs image
|
||||
for (int i = 1; i <= 4; i++)
|
||||
{
|
||||
auto normalImage = new ScalableBitmap(this, "Nozzle_HS_01_0" + std::to_string(i * 2), 46);
|
||||
auto bigImage = new ScalableBitmap(this, "Big_Nozzle_HS_01_0" + std::to_string(i * 2), 216);
|
||||
nozzle_hs.push_back({normalImage, bigImage});
|
||||
}
|
||||
for (int i = 2; i <= 4; i++)
|
||||
{
|
||||
auto normalImage = new ScalableBitmap(this, "Nozzle_HH_01_0" + std::to_string(i * 2), 46);
|
||||
auto bigImage = new ScalableBitmap(this, "Big_Nozzle_HH_01_0" + std::to_string(i * 2), 216);
|
||||
nozzle_hh.push_back({normalImage, bigImage});
|
||||
}
|
||||
|
||||
auto* content_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
// Index
|
||||
m_idx_label = new Label(this);
|
||||
m_idx_label->SetFont(Label::Head_14);
|
||||
m_idx_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
content_sizer->Add(m_idx_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(25));
|
||||
|
||||
// Icon
|
||||
wxPanel* imagePanel = new wxPanel(this);
|
||||
imagePanel->SetMaxSize(WX_DIP_SIZE(46, -1));
|
||||
imagePanel->SetMinSize(WX_DIP_SIZE(46, -1));
|
||||
imagePanel->SetSize(wxSize(FromDIP(46), FromDIP(-1)));
|
||||
wxBoxSizer* panelSizer = new wxBoxSizer(wxVERTICAL);
|
||||
imagePanel->SetSizer(panelSizer);
|
||||
|
||||
m_nozzle_empty_image = new ScalableBitmap(imagePanel, "dev_rack_nozzle_empty", 46);
|
||||
m_icon_bitmap = new wxStaticBitmap(imagePanel, wxID_ANY, m_nozzle_empty_image->bmp());
|
||||
panelSizer->Add(m_icon_bitmap, 0, wxALIGN_CENTER);
|
||||
content_sizer->Add(imagePanel, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(25));
|
||||
m_icon_bitmap->Bind(wxEVT_ENTER_WINDOW, &wgtDeviceNozzleRackHotendUpdate::OnBitmapHoverEnter, this);
|
||||
m_icon_bitmap->Bind(wxEVT_LEAVE_WINDOW, &wgtDeviceNozzleRackHotendUpdate::OnBitmapHoverLeave, this);
|
||||
|
||||
// Diameter/type (vertical)
|
||||
wxPanel* type_panel = new wxPanel(this);
|
||||
auto* main_type_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
auto* type_sizer_row_1 = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto* type_sizer_row_2 = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
m_material_label = new Label(type_panel);
|
||||
m_material_label->SetFont(Label::Body_12);
|
||||
m_material_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
|
||||
m_colour_box = new StaticBox(type_panel);
|
||||
m_colour_box->SetMaxSize(WX_DIP_SIZE(16, 16));
|
||||
m_colour_box->SetMinSize(WX_DIP_SIZE(16, 16));
|
||||
m_colour_box->SetCornerRadius(FromDIP(2));
|
||||
m_colour_box->SetSize(wxSize(FromDIP(16), FromDIP(16)));
|
||||
|
||||
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));
|
||||
|
||||
m_diameter_label = new Label(type_panel);
|
||||
m_diameter_label->SetFont(Label::Body_12);
|
||||
m_diameter_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
|
||||
m_flowtype_label = new Label(type_panel);
|
||||
m_flowtype_label->SetFont(Label::Body_12);
|
||||
m_flowtype_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
|
||||
m_type_label = new Label(type_panel);
|
||||
m_type_label->SetFont(Label::Body_12);
|
||||
m_type_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
|
||||
type_sizer_row_2->Add(m_diameter_label, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT );
|
||||
type_sizer_row_2->Add(m_flowtype_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(2));
|
||||
type_sizer_row_2->Add(m_type_label, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(2));
|
||||
|
||||
main_type_sizer->Add(type_sizer_row_1, 0, wxALIGN_LEFT);
|
||||
main_type_sizer->Add(type_sizer_row_2, 1, wxALIGN_LEFT | wxEXPAND | wxTOP, FromDIP(4));
|
||||
type_panel->SetSizer(main_type_sizer);
|
||||
type_panel->SetMaxSize(WX_DIP_SIZE(160, 40));
|
||||
type_panel->SetMinSize(WX_DIP_SIZE(160, 40));
|
||||
type_panel->SetSize(WX_DIP_SIZE(160, 40));
|
||||
|
||||
content_sizer->Add(type_panel, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(12));
|
||||
|
||||
// SN and version (vertical)
|
||||
wxPanel* info_panel = new wxPanel(this);
|
||||
auto* info_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_sn_label = new Label(info_panel);
|
||||
m_sn_label->SetFont(Label::Body_12);
|
||||
m_sn_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
|
||||
auto* version_h_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_version_label = new Label(info_panel);
|
||||
m_version_label->SetFont(Label::Body_12);
|
||||
m_version_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
|
||||
m_version_new_label = new Label(info_panel);
|
||||
m_version_new_label->SetFont(Label::Body_12);
|
||||
m_version_new_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
m_version_new_label->SetForegroundColour(wxColour(0, 168, 84)); // Green
|
||||
|
||||
version_h_sizer->Add(m_version_label, 0, wxALIGN_CENTER_VERTICAL);
|
||||
version_h_sizer->Add(m_version_new_label, 0, wxALIGN_CENTER_VERTICAL);
|
||||
info_sizer->Add(m_sn_label, 0, wxALIGN_LEFT);
|
||||
info_sizer->Add(version_h_sizer, 0, wxALIGN_LEFT | wxTOP, FromDIP(4));
|
||||
info_panel->SetSizer(info_sizer);
|
||||
info_panel->SetMaxSize(WX_DIP_SIZE(183, 40));
|
||||
info_panel->SetMinSize(WX_DIP_SIZE(183, 40));
|
||||
info_panel->SetSize(WX_DIP_SIZE(183, 40));
|
||||
|
||||
//Used Time
|
||||
m_used_time = new Label(this);
|
||||
m_used_time->SetFont(Label::Body_12);
|
||||
m_used_time->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
|
||||
m_refresh_icon = new ScalableBitmap(this, "refresh_printer", 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.
|
||||
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);
|
||||
|
||||
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));
|
||||
content_sizer->Add(m_status_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(20));
|
||||
content_sizer->Add(m_status_bitmap, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(2));
|
||||
content_sizer->Add(m_refreshing_icon, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(2));
|
||||
content_sizer->AddSpacer(FromDIP(25));
|
||||
|
||||
auto* main_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
main_sizer->Add(content_sizer, 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(10));
|
||||
|
||||
SetSizer(main_sizer);
|
||||
Layout();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackHotendUpdate::OnStatusIconClick(wxMouseEvent& event)
|
||||
{
|
||||
if (m_status_label->GetLabel() == _L("Refresh"))
|
||||
{
|
||||
m_status_label->SetForegroundColour(wxColour("#A3A3A3"));
|
||||
m_status_label->SetLabel(_L("Refreshing"));
|
||||
m_status_bitmap->Show(false);
|
||||
if(!m_refreshing_icon->IsPlaying())
|
||||
{
|
||||
m_refreshing_icon->Play();
|
||||
m_refreshing_icon->Show();
|
||||
}
|
||||
if (auto shared = m_nozzle_rack.lock())
|
||||
{
|
||||
shared->CrtlRackReadNozzle(m_rack_nozzle_id);
|
||||
}
|
||||
Layout();
|
||||
}
|
||||
|
||||
if (m_status_label->GetLabel() == _L("Error") /*&& m_nozzle_status == NOZZLE_STATUS_ABNORMAL*/)
|
||||
{
|
||||
if (auto shared = m_nozzle_rack.lock())
|
||||
{
|
||||
MessageDialog dlg(nullptr, _L("Hotend status abnormal, unavailable at present. Please upgrade the firmware"
|
||||
" and try again."), _L("Update"), 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();
|
||||
|
||||
wxCommandEvent evt(wxEVT_NOZZLE_JUMP_UPGRADE, GetId());
|
||||
evt.SetEventObject(this);
|
||||
wxWindow* target = GetParent();
|
||||
if (target)
|
||||
{
|
||||
target = target->GetParent();
|
||||
if (target)
|
||||
{
|
||||
wxPostEvent(target, evt);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackHotendUpdate::OnBitmapHoverEnter(wxMouseEvent& event)
|
||||
{
|
||||
int scaledW = FromDIP(240);
|
||||
int scaledH = FromDIP(240);
|
||||
|
||||
ScalableBitmap* scaledBmp{nullptr};
|
||||
if (m_nozzle_status == NOZZLE_STATUS_EMPTY || m_nozzle_status == NOZZLE_STATUS_UNKNOWN || !findNozzleImage)
|
||||
{
|
||||
if (!m_scaled_nozzle_empty_image) {m_scaled_nozzle_empty_image = new ScalableBitmap(this, "dev_rack_nozzle_empty", 216);}
|
||||
|
||||
scaledBmp = m_scaled_nozzle_empty_image;
|
||||
}
|
||||
else
|
||||
{
|
||||
scaledBmp = m_scaled_nozzle_image;
|
||||
}
|
||||
|
||||
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);
|
||||
wxBoxSizer* frameSizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_hoverFrame->SetSizer(frameSizer);
|
||||
|
||||
wxStaticBitmap* hoverBmp = new wxStaticBitmap(m_hoverFrame, wxID_ANY, scaledBmp->bmp());
|
||||
frameSizer->Add(hoverBmp, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP | wxBOTTOM, FromDIP(12));
|
||||
|
||||
wxPoint mousePos = wxGetMousePosition();
|
||||
m_hoverFrame->SetPosition(wxPoint(mousePos.x + 10, mousePos.y));
|
||||
m_hoverFrame->Layout();
|
||||
m_hoverFrame->Show(true);
|
||||
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackHotendUpdate::OnBitmapHoverLeave(wxMouseEvent& event)
|
||||
{
|
||||
if (m_hoverFrame)
|
||||
{
|
||||
m_hoverFrame->Destroy();
|
||||
m_hoverFrame = nullptr;
|
||||
}
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackHotendUpdate::updateNozzleImage(const DevNozzle& nozzle)
|
||||
{
|
||||
if (m_nozzle_status == NOZZLE_STATUS_UNKNOWN || m_nozzle_status == NOZZLE_STATUS_EMPTY)
|
||||
{
|
||||
if (!m_nozzle_empty_image) {m_nozzle_empty_image = new ScalableBitmap(this, "dev_rack_nozzle_empty", 46);}
|
||||
m_icon_bitmap->SetBitmap(m_nozzle_empty_image->bmp());
|
||||
m_icon_bitmap->Refresh();
|
||||
return;
|
||||
}
|
||||
findNozzleImage = false;
|
||||
int index = -1;
|
||||
switch (nozzle.GetNozzleDiameterType()) {
|
||||
case NOZZLE_DIAMETER_0_2: index = 0; break;
|
||||
case NOZZLE_DIAMETER_0_4: index = 1; break;
|
||||
case NOZZLE_DIAMETER_0_6: index = 2; break;
|
||||
case NOZZLE_DIAMETER_0_8: index = 3; break;
|
||||
default: index = -1; break;
|
||||
}
|
||||
NozzleType nozzleType = nozzle.GetNozzleType();
|
||||
if (nozzleType == ntHardenedSteel || nozzleType == ntStainlessSteel)
|
||||
{
|
||||
if (nozzle.GetNozzleFlowType() == H_FLOW && index >= 1)
|
||||
{
|
||||
m_nozzle_image = nozzle_hh[index - 1][0];
|
||||
m_icon_bitmap->SetBitmap(m_nozzle_image->bmp());
|
||||
m_scaled_nozzle_image = nozzle_hh[index - 1][1];
|
||||
findNozzleImage = true;
|
||||
}
|
||||
else if (nozzle.GetNozzleFlowType() == S_FLOW && index >= 0)
|
||||
{
|
||||
m_nozzle_image = nozzle_hs[index][0];
|
||||
m_icon_bitmap->SetBitmap(m_nozzle_image->bmp());
|
||||
m_scaled_nozzle_image = nozzle_hs[index][1];
|
||||
findNozzleImage = true;
|
||||
}
|
||||
}
|
||||
if (!findNozzleImage)
|
||||
{
|
||||
if (!m_nozzle_empty_image) {m_nozzle_empty_image = new ScalableBitmap(this, "dev_rack_nozzle_empty", 46);}
|
||||
m_icon_bitmap->SetBitmap(m_nozzle_empty_image->bmp());
|
||||
}
|
||||
m_icon_bitmap->Refresh();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackHotendUpdate::UpdateColourStyle(const wxColour& clr)
|
||||
{
|
||||
SetBackgroundColour(clr);
|
||||
SetBorderColor(clr);
|
||||
|
||||
// BFS: Update all children background color
|
||||
auto children = GetChildren();
|
||||
while (!children.IsEmpty())
|
||||
{
|
||||
auto win = children.front();
|
||||
children.pop_front();
|
||||
win->SetBackgroundColour(clr);
|
||||
|
||||
for (auto child : win->GetChildren())
|
||||
{
|
||||
children.push_back(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackHotendUpdate::UpdateExtruderNozzleInfo(const std::shared_ptr<DevNozzleRack> rack)
|
||||
{
|
||||
m_nozzle_rack = rack;
|
||||
if (rack)
|
||||
{
|
||||
DevNozzleSystem* nozzle_system = rack->GetNozzleSystem();
|
||||
if (nozzle_system)
|
||||
{
|
||||
UpdateInfo(nozzle_system->GetExtNozzle(m_ext_nozzle_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackHotendUpdate::UpdateRackNozzleInfo(const std::shared_ptr<DevNozzleRack> rack)
|
||||
{
|
||||
m_nozzle_rack = rack;
|
||||
if (rack)
|
||||
{
|
||||
UpdateInfo(rack->GetNozzle(m_rack_nozzle_id));
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackHotendUpdate::UpdateInfo(const DevNozzle& nozzle)
|
||||
{
|
||||
/*update nozzle possition and background*/
|
||||
if (auto share = m_nozzle_rack.lock())
|
||||
{
|
||||
// if (share->GetReadingCount() > 0 && m_status_label->IsShown())
|
||||
if (share->GetReadingCount() > 0 && m_status_label->IsShown() && m_status_label->GetLabel() == _L("Refreshing"))
|
||||
{
|
||||
m_refreshing_icon->Play();
|
||||
m_refreshing_icon->Show();
|
||||
m_nozzle_status = NOZZLE_STATUS_DC;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_refreshing_icon->Stop();
|
||||
m_refreshing_icon->Show(false);
|
||||
}
|
||||
}
|
||||
|
||||
wxString filamentDisplayName{};
|
||||
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())
|
||||
{
|
||||
filamentDisplayName = wxString(filament_preset.alias);
|
||||
}
|
||||
}
|
||||
|
||||
if (nozzle.IsEmpty() && m_nozzle_status != NOZZLE_STATUS_EMPTY)
|
||||
{
|
||||
m_nozzle_status = NOZZLE_STATUS_EMPTY;
|
||||
|
||||
m_material_label->Show(false);
|
||||
m_colour_box->Show(false);
|
||||
|
||||
m_diameter_label->Show(false);
|
||||
m_flowtype_label->Show(false);
|
||||
m_type_label->SetLabel(_L("Empty"));
|
||||
m_type_label->SetForegroundColour(StateColor::darkModeColorFor(*wxBLACK));
|
||||
|
||||
m_sn_label->Show(false);
|
||||
m_version_label->Show(false);
|
||||
m_version_new_label->Show(false);
|
||||
|
||||
updateNozzleImage(nozzle);
|
||||
|
||||
m_used_time->Show(false);
|
||||
m_status_label->Show(false);
|
||||
m_status_bitmap->Show(false);
|
||||
|
||||
}
|
||||
else if (nozzle.IsNormal() && m_nozzle_status != NOZZLE_STATUS_NORMAL)
|
||||
{
|
||||
m_nozzle_status = NOZZLE_STATUS_NORMAL;
|
||||
|
||||
m_material_label->SetLabel(filamentDisplayName);
|
||||
m_colour_box->SetBackgroundColour(wxColour("#" + nozzle.GetFilamentColor()));
|
||||
m_colour_box->SetBorderColor(wxColour("#" + nozzle.GetFilamentColor()));
|
||||
m_material_label->Show(true);
|
||||
m_colour_box->Show(true);
|
||||
|
||||
m_diameter_label->Show(true);
|
||||
m_flowtype_label->Show(true);
|
||||
m_diameter_label->SetLabel(nozzle.GetNozzleDiameterStr());
|
||||
m_flowtype_label->SetLabel(nozzle.GetNozzleFlowTypeStr());
|
||||
m_type_label->SetLabel(nozzle.GetNozzleTypeStr());
|
||||
m_type_label->SetForegroundColour(StateColor::darkModeColorFor(*wxBLACK));
|
||||
|
||||
m_sn_label->Show(true);
|
||||
m_version_label->Show(true);
|
||||
|
||||
updateNozzleImage(nozzle);
|
||||
|
||||
m_used_time->Show(true);
|
||||
m_status_label->Show(false);
|
||||
m_status_bitmap->Show(false);
|
||||
}
|
||||
else if (nozzle.IsAbnormal() && m_nozzle_status != NOZZLE_STATUS_ABNORMAL)
|
||||
{
|
||||
m_nozzle_status = NOZZLE_STATUS_ABNORMAL;
|
||||
|
||||
m_material_label->SetLabel(filamentDisplayName);
|
||||
m_colour_box->SetBackgroundColour(wxColour("#" + nozzle.GetFilamentColor()));
|
||||
m_colour_box->SetBorderColor(wxColour("#" + nozzle.GetFilamentColor()));
|
||||
m_material_label->Show(true);
|
||||
m_colour_box->Show(true);
|
||||
|
||||
m_diameter_label->Show(true);
|
||||
m_flowtype_label->Show(true);
|
||||
m_diameter_label->SetLabel(nozzle.GetNozzleDiameterStr());
|
||||
m_flowtype_label->SetLabel(nozzle.GetNozzleFlowTypeStr());
|
||||
m_type_label->SetLabel(nozzle.GetNozzleTypeStr());
|
||||
m_type_label->SetForegroundColour(StateColor::darkModeColorFor(*wxBLACK));
|
||||
|
||||
updateNozzleImage(nozzle);
|
||||
|
||||
m_status_label->Show(true);
|
||||
m_status_bitmap->Show(true);
|
||||
m_status_label->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#E14747")));
|
||||
m_status_label->SetLabel(_L("Error"));
|
||||
m_status_bitmap->SetBitmap(m_error_icon->bmp());
|
||||
m_status_bitmap->Refresh();
|
||||
}
|
||||
else if (nozzle.IsUnknown() && m_nozzle_status != NOZZLE_STATUS_UNKNOWN)
|
||||
{
|
||||
m_nozzle_status = NOZZLE_STATUS_UNKNOWN;
|
||||
|
||||
m_colour_box->Show(false);
|
||||
m_material_label->SetLabel(wxString("--"));
|
||||
m_material_label->Show(true);
|
||||
|
||||
m_diameter_label->Show(false);
|
||||
m_flowtype_label->Show(false);
|
||||
m_type_label->SetLabel(_L("Unknown"));
|
||||
m_type_label->SetForegroundColour(StateColor::darkModeColorFor(*wxBLACK));
|
||||
|
||||
m_sn_label->Show(true);
|
||||
m_version_label->Show(true);
|
||||
|
||||
updateNozzleImage(nozzle);
|
||||
|
||||
m_used_time->Show(true);
|
||||
m_status_label->Show(true);
|
||||
m_status_bitmap->Show(true);
|
||||
m_status_label->SetForegroundColour(wxColour("#00AE42"));
|
||||
m_status_label->SetLabel(_L("Refresh"));
|
||||
m_status_bitmap->SetBitmap(m_refresh_icon->bmp());
|
||||
m_status_bitmap->Refresh();
|
||||
}
|
||||
|
||||
// Update firmware info
|
||||
const DevFirmwareVersionInfo& firmware = nozzle.GetFirmwareInfo();
|
||||
if (nozzle.IsUnknown())
|
||||
{
|
||||
m_sn_label->SetLabel(wxString::Format("%s: --", _L("SN")));
|
||||
m_version_label->SetLabel(wxString::Format("%s: --", _L("Version")));
|
||||
m_version_new_label->Show(false);
|
||||
}
|
||||
if (nozzle.IsNormal() || nozzle.IsAbnormal())
|
||||
{
|
||||
if (firmware.isValid())
|
||||
{
|
||||
m_sn_label->SetLabel(wxString::Format("%s: %s", _L("SN"), firmware.sn));
|
||||
|
||||
if (!firmware.sw_new_ver.empty() && firmware.sw_new_ver != firmware.sw_ver)
|
||||
{
|
||||
m_version_label->SetLabel(wxString::Format("%s: %s > ", _L("Version"), firmware.sw_ver));
|
||||
m_version_new_label->SetLabel(wxString::Format("%s", firmware.sw_new_ver));
|
||||
m_version_new_label->Show(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_version_label->SetLabel(wxString::Format("%s:%s", _L("Version"), firmware.sw_ver));
|
||||
m_version_new_label->Show(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sn_label->SetLabel(wxString::Format("%s: --", _L("SN")));
|
||||
m_version_label->SetLabel(wxString::Format("%s: --", _L("Version")));
|
||||
m_version_new_label->Show(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!nozzle.IsEmpty())
|
||||
{
|
||||
if (nozzle.IsUnknown())
|
||||
{
|
||||
m_used_time->SetLabel(wxString::Format(_L("Used Time: %s"), "--h"));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update used time
|
||||
int usedSeconds = nozzle.GetNozzlePrintTime();
|
||||
if (usedSeconds < 60)
|
||||
{
|
||||
m_used_time->SetLabel(wxString::Format(_L("Used Time: %s"), "0 h"));
|
||||
}
|
||||
else
|
||||
{
|
||||
int printTime = (usedSeconds >= 3600 ? usedSeconds / 3600 : usedSeconds / 60);
|
||||
std::string printTimeStr = (usedSeconds >= 3600 ? std::to_string(printTime) + " h" : std::to_string(printTime) + " min");
|
||||
m_used_time->SetLabel(wxString::Format(_L("Used Time: %s"), printTimeStr.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackHotendUpdate::Rescale()
|
||||
{
|
||||
// update images
|
||||
if (m_nozzle_image) { m_nozzle_image->msw_rescale(); }
|
||||
if (m_nozzle_empty_image) { m_nozzle_empty_image->msw_rescale(); }
|
||||
if (m_nozzle_status == NOZZLE_STATUS_EMPTY && m_nozzle_empty_image)
|
||||
{
|
||||
m_icon_bitmap->SetBitmap(m_nozzle_empty_image->bmp());
|
||||
}
|
||||
else if (m_nozzle_image != nullptr)
|
||||
{
|
||||
m_icon_bitmap->SetBitmap(m_nozzle_image->bmp());
|
||||
}
|
||||
m_icon_bitmap->Refresh();
|
||||
}
|
||||
|
||||
};// namespace Slic3r::GUI
|
||||
@@ -0,0 +1,175 @@
|
||||
//**********************************************************/
|
||||
/* File: wgtDeviceNozzleRackUpdate.h
|
||||
* Description: The dialog for reading/upgrading the H2C induction-hotend-rack firmware.
|
||||
*
|
||||
* \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.
|
||||
//**********************************************************/
|
||||
|
||||
#pragma once
|
||||
#include "slic3r/GUI/DeviceCore/DevNozzleRack.h"
|
||||
|
||||
#include "slic3r/GUI/GUI_Utils.hpp"
|
||||
#include "slic3r/GUI/Widgets/StaticBox.hpp"
|
||||
#include "slic3r/GUI/Widgets/AnimaController.hpp"
|
||||
|
||||
#include <wx/panel.h>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
// Previous definitions
|
||||
class Button;
|
||||
class Label;
|
||||
class ScalableBitmap;
|
||||
class ScalableButton;
|
||||
namespace Slic3r
|
||||
{
|
||||
struct DevNozzle;
|
||||
class DevNozzleRack;
|
||||
namespace GUI
|
||||
{
|
||||
class wgtDeviceNozzleRackUprade;
|
||||
class wgtDeviceNozzleRackHotendUpdate;
|
||||
}
|
||||
};
|
||||
|
||||
namespace Slic3r::GUI
|
||||
{
|
||||
class wgtDeviceNozzleRackUpgradeDlg : public DPIDialog
|
||||
{
|
||||
public:
|
||||
wgtDeviceNozzleRackUpgradeDlg(wxWindow* parent, const std::shared_ptr<DevNozzleRack> rack);
|
||||
|
||||
public:
|
||||
void UpdateRackInfo(const std::shared_ptr<DevNozzleRack> rack);
|
||||
|
||||
public:
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||
|
||||
private:
|
||||
wgtDeviceNozzleRackUprade* m_rack_upgrade_panel;
|
||||
};
|
||||
|
||||
|
||||
class wgtDeviceNozzleRackUprade : public wxPanel
|
||||
{
|
||||
public:
|
||||
wgtDeviceNozzleRackUprade(wxWindow* parent,
|
||||
wxWindowID id = wxID_ANY,
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize,
|
||||
long style = wxTAB_TRAVERSAL);
|
||||
~wgtDeviceNozzleRackUprade() = default;
|
||||
|
||||
public:
|
||||
void UpdateRackInfo(const std::shared_ptr<DevNozzleRack> rack);
|
||||
void Rescale();
|
||||
|
||||
private:
|
||||
void CreateGui();
|
||||
|
||||
void OnBtnReadAll(wxCommandEvent& e);
|
||||
|
||||
private:
|
||||
std::weak_ptr<DevNozzleRack> m_nozzle_rack;
|
||||
|
||||
// GUI
|
||||
wgtDeviceNozzleRackHotendUpdate* m_extruder_nozzle_item;
|
||||
std::unordered_map<int, wgtDeviceNozzleRackHotendUpdate*> m_nozzle_items;
|
||||
};
|
||||
|
||||
// Using for rack nozzle id or extruder nozzle id
|
||||
class wgtDeviceNozzleRackHotendUpdate : public StaticBox
|
||||
{
|
||||
public:
|
||||
wgtDeviceNozzleRackHotendUpdate(wxWindow* parent, const wxString& idx_text);
|
||||
|
||||
public:
|
||||
// Color
|
||||
void UpdateColourStyle(const wxColour& clr);
|
||||
|
||||
// extruder nozzle
|
||||
int GetExtruderNozzleId() const { return m_ext_nozzle_id; }
|
||||
void SetExtruderNozzleId(int ext_nozzle_id) { m_ext_nozzle_id = ext_nozzle_id; }
|
||||
void UpdateExtruderNozzleInfo(const std::shared_ptr<DevNozzleRack> rack);
|
||||
|
||||
// rack nozzle
|
||||
void SetRackNozzleId(int rack_nozzle_id) { m_rack_nozzle_id = rack_nozzle_id; }
|
||||
int GetRackNozzleId() const { return m_rack_nozzle_id; }
|
||||
void UpdateRackNozzleInfo(const std::shared_ptr<DevNozzleRack> rack);
|
||||
|
||||
// gui
|
||||
void Rescale();
|
||||
|
||||
private:
|
||||
void CreateGui();
|
||||
|
||||
void UpdateInfo(const DevNozzle& nozzle);
|
||||
|
||||
void OnBitmapHoverEnter(wxMouseEvent& event);
|
||||
void OnBitmapHoverLeave(wxMouseEvent& event);
|
||||
void OnStatusIconClick(wxMouseEvent& event);
|
||||
void updateNozzleImage(const DevNozzle& nozzle);
|
||||
|
||||
private:
|
||||
enum NozzleStatus : int
|
||||
{
|
||||
NOZZLE_STATUS_DC = -1,
|
||||
NOZZLE_STATUS_EMPTY,
|
||||
NOZZLE_STATUS_NORMAL,
|
||||
NOZZLE_STATUS_ABNORMAL,
|
||||
NOZZLE_STATUS_UNKNOWN,
|
||||
};
|
||||
|
||||
private:
|
||||
int m_ext_nozzle_id = -1;
|
||||
int m_rack_nozzle_id = -1;
|
||||
bool m_isRefreshFinish = false;
|
||||
bool findNozzleImage = false;
|
||||
|
||||
NozzleStatus m_nozzle_status = NOZZLE_STATUS_DC;
|
||||
std::weak_ptr<DevNozzleRack> m_nozzle_rack;
|
||||
|
||||
std::vector<std::vector<ScalableBitmap*>> nozzle_hs;
|
||||
std::vector<std::vector<ScalableBitmap*>> nozzle_hh;
|
||||
// GUI
|
||||
ScalableBitmap* m_nozzle_image = nullptr;
|
||||
ScalableBitmap* m_nozzle_empty_image = nullptr;
|
||||
ScalableBitmap* m_scaled_nozzle_image = nullptr;
|
||||
ScalableBitmap* m_scaled_nozzle_empty_image = nullptr;
|
||||
ScalableBitmap* m_refresh_icon = nullptr;
|
||||
AnimaIcon* m_refreshing_icon = nullptr;
|
||||
ScalableBitmap* m_error_icon = nullptr;
|
||||
|
||||
Label* m_idx_label;
|
||||
wxStaticBitmap* m_icon_bitmap{ nullptr };
|
||||
wxStaticBitmap* m_status_bitmap{ nullptr };
|
||||
|
||||
Label* m_material_label{ nullptr };
|
||||
StaticBox* m_colour_box{ nullptr };
|
||||
wxFrame* m_hoverFrame{ nullptr };
|
||||
|
||||
Label* m_status_label{ nullptr };
|
||||
Label* m_used_time{ nullptr };
|
||||
|
||||
Label* m_diameter_label;
|
||||
Label* m_flowtype_label;
|
||||
Label* m_type_label;
|
||||
ScalableButton* m_error_button{ nullptr };
|
||||
|
||||
Label* m_sn_label;
|
||||
Label* m_version_label;
|
||||
Label* m_version_new_label;
|
||||
};
|
||||
|
||||
wxDECLARE_EVENT(wxEVT_NOZZLE_JUMP_UPGRADE, wxCommandEvent);
|
||||
|
||||
};// end of namespace Slic3r::GUI
|
||||
@@ -0,0 +1,289 @@
|
||||
//**********************************************************/
|
||||
/* File: wgtDeviceNozzleSelect.cpp
|
||||
* Description: The panel to select nozzle
|
||||
*
|
||||
* \n class wgtDeviceNozzleSelect;
|
||||
//**********************************************************/
|
||||
|
||||
#include "wgtDeviceNozzleSelect.h"
|
||||
#include "wgtDeviceNozzleRackNozzleItem.h" // the nozzle-item widget lives in its own header
|
||||
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/DeviceTab/wgtMsgBox.h"
|
||||
#include "slic3r/GUI/Widgets/Label.hpp"
|
||||
|
||||
static wxColour s_gray_clr("#B0B0B0");
|
||||
static wxColour s_hgreen_clr("#00AE42");
|
||||
static wxColour s_red_clr("#D01B1B");
|
||||
|
||||
static std::vector<int> a_nozzle_seq = {16, 18, 20, 17, 19, 21};
|
||||
|
||||
wxDEFINE_EVENT(EVT_NOZZLE_SELECT_CHANGED, wxCommandEvent);
|
||||
wxDEFINE_EVENT(EVT_NOZZLE_SELECT_CLICKED, wxCommandEvent);
|
||||
|
||||
namespace Slic3r::GUI {
|
||||
|
||||
wgtDeviceNozzleRackSelect::wgtDeviceNozzleRackSelect(wxWindow *parent) : wxPanel(parent, wxID_ANY) { CreateGui(); }
|
||||
|
||||
static wxPanel* s_create_title(wxWindow *parent, const wxString& text)
|
||||
{
|
||||
wxPanel *panel = new wxPanel(parent, wxID_ANY);
|
||||
|
||||
auto title = new Label(panel, text);
|
||||
title->SetFont(::Label::Body_13);
|
||||
title->SetBackgroundColour(*wxWHITE);
|
||||
title->SetForegroundColour(0x909090);
|
||||
|
||||
auto split_line = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
split_line->SetBackgroundColour(0xeeeeee);
|
||||
split_line->SetMinSize(wxSize(-1, 1));
|
||||
split_line->SetMaxSize(wxSize(-1, 1));
|
||||
|
||||
wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(0, 0, 0, wxEXPAND, 0);
|
||||
sizer->Add(title, 0, wxALIGN_CENTER, 0);
|
||||
sizer->Add(split_line, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND, 0);
|
||||
panel->SetSizer(sizer);
|
||||
panel->Layout();
|
||||
return panel;
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackSelect::CreateGui()
|
||||
{
|
||||
wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxColour tip_bg_clr("#FFF0E0");
|
||||
m_title_tips_dynamic = new wgtMsgBox(this);
|
||||
m_title_tips_dynamic->SetBackgroundColour(tip_bg_clr);
|
||||
m_title_tips_dynamic->SetBorderColor(wxColour("#FF6F00"));
|
||||
m_title_tips_dynamic->SetCornerRadius(1);
|
||||
m_title_tips_dynamic->SetBorderWidth(1);
|
||||
auto text_label = m_title_tips_dynamic->GetTextLabel();
|
||||
text_label->SetFont(::Label::Body_12);
|
||||
text_label->SetForegroundColour("#FF6F00");
|
||||
text_label->SetBackgroundColour(tip_bg_clr);
|
||||
text_label->SetLabel(_L("Dynamic nozzles are allocated on the current plate. Picking hotend is not supported."));
|
||||
text_label->Wrap(FromDIP(300));
|
||||
text_label->Fit();
|
||||
m_title_tips_dynamic->Layout();
|
||||
m_title_tips_dynamic->Refresh();
|
||||
|
||||
// nozzles
|
||||
wxGridSizer *nozzle_sizer = new wxGridSizer(2, 3, FromDIP(10), FromDIP(10));
|
||||
for (auto idx : a_nozzle_seq) {
|
||||
wgtDeviceNozzleRackNozzleItem *nozzle_item = new wgtDeviceNozzleRackNozzleItem(this, idx - 16);
|
||||
nozzle_item->EnableSelect();
|
||||
nozzle_item->Bind(EVT_NOZZLE_RACK_NOZZLE_ITEM_SELECTED, &wgtDeviceNozzleRackSelect::OnNozzleItemSelected, this);
|
||||
m_nozzle_items[idx] = nozzle_item;
|
||||
nozzle_sizer->Add(nozzle_item, 0);
|
||||
}
|
||||
|
||||
// toolhead area
|
||||
m_toolhead_nozzle_l = new wgtDeviceNozzleRackNozzleItem(this, 1);
|
||||
m_toolhead_nozzle_l->EnableSelect();
|
||||
m_toolhead_nozzle_l->SetDisplayIdText("L");
|
||||
m_toolhead_nozzle_l->Bind(EVT_NOZZLE_RACK_NOZZLE_ITEM_SELECTED, &wgtDeviceNozzleRackSelect::OnNozzleItemSelected, this);
|
||||
|
||||
m_toolhead_nozzle_r = new wgtDeviceNozzleRackNozzleItem(this, 0);
|
||||
m_toolhead_nozzle_r->EnableSelect();
|
||||
m_toolhead_nozzle_r->SetDisplayIdText("R");
|
||||
m_toolhead_nozzle_r->Bind(EVT_NOZZLE_RACK_NOZZLE_ITEM_SELECTED, &wgtDeviceNozzleRackSelect::OnNozzleItemSelected, this);
|
||||
|
||||
wxSizer* toolhead_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
toolhead_sizer->Add(m_toolhead_nozzle_l, 0, wxRIGHT, FromDIP(5));
|
||||
toolhead_sizer->Add(m_toolhead_nozzle_r, 0, wxLEFT, FromDIP(5));
|
||||
|
||||
main_sizer->Add(m_title_tips_dynamic, 0, wxALIGN_CENTRE_VERTICAL | wxBOTTOM, FromDIP(10));
|
||||
main_sizer->Add(s_create_title(this, _L("Hotend Rack")), 0, wxEXPAND);
|
||||
main_sizer->Add(nozzle_sizer, 0, wxTOP | wxBOTTOM | wxALIGN_LEFT, FromDIP(10));
|
||||
main_sizer->Add(s_create_title(this, _L("ToolHead")), 0, wxEXPAND);
|
||||
main_sizer->AddSpacer(FromDIP(10));
|
||||
main_sizer->Add(toolhead_sizer, 0, wxALIGN_LEFT);
|
||||
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
SetSizer(main_sizer);
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
|
||||
static void s_update_nozzle_info(wgtDeviceNozzleRackNozzleItem* item,
|
||||
std::shared_ptr<DevNozzleRack> rack,
|
||||
const DevNozzle& nozzle_info)
|
||||
{
|
||||
item->Update(rack, nozzle_info.IsOnRack());
|
||||
if (nozzle_info.IsUnknown()) {
|
||||
if (item->GetToolTipText() != _L("Nozzle information needs to be read")) {
|
||||
item->SetToolTip(_L("Nozzle information needs to be read"));
|
||||
}
|
||||
} else {
|
||||
item->SetToolTip(wxEmptyString);
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackSelect::UpdateNozzleInfos(std::shared_ptr<DevNozzleRack> rack)
|
||||
{
|
||||
m_nozzle_rack = rack;
|
||||
if (rack) {
|
||||
s_update_nozzle_info(m_toolhead_nozzle_l, rack, rack->GetNozzleSystem()->GetNozzleByPosId(DEPUTY_EXTRUDER_ID));
|
||||
s_update_nozzle_info(m_toolhead_nozzle_r, rack, rack->GetNozzleSystem()->GetNozzleByPosId(MAIN_EXTRUDER_ID));
|
||||
for (const auto& item : m_nozzle_items) {
|
||||
s_update_nozzle_info(item.second, rack, rack->GetNozzleSystem()->GetNozzleByPosId(item.first));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void s_enable_item_if_match(wgtDeviceNozzleRackNozzleItem* item,
|
||||
const DevNozzle& nozzle_info,
|
||||
const DevNozzle& selected_nozzle)
|
||||
{
|
||||
if (item) {
|
||||
if (nozzle_info.GetLogicExtruderId() != selected_nozzle.GetLogicExtruderId()) {
|
||||
item->SetDisable(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nozzle_info.IsEmpty() && !nozzle_info.IsAbnormal() && !nozzle_info.IsUnknown() &&
|
||||
nozzle_info.GetNozzleType() == selected_nozzle.GetNozzleType() &&
|
||||
nozzle_info.GetNozzleDiameter() == selected_nozzle.GetNozzleDiameter() &&
|
||||
nozzle_info.GetNozzleFlowType() == selected_nozzle.GetNozzleFlowType()) {
|
||||
item->SetDisable(false);
|
||||
} else {
|
||||
item->SetDisable(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackSelect::UpdatSelectedNozzle(std::shared_ptr<DevNozzleRack> rack, int selected_nozzle_pos_id)
|
||||
{
|
||||
m_nozzle_rack = rack;
|
||||
if (rack) {
|
||||
SetSelectedNozzle(rack->GetNozzleSystem()->GetNozzleByPosId(selected_nozzle_pos_id));
|
||||
if (m_enable_manual_nozzle_pick) {
|
||||
s_enable_item_if_match(m_toolhead_nozzle_r, rack->GetNozzleSystem()->GetNozzleByPosId(MAIN_EXTRUDER_ID), m_selected_nozzle);
|
||||
s_enable_item_if_match(m_toolhead_nozzle_l, rack->GetNozzleSystem()->GetNozzleByPosId(DEPUTY_EXTRUDER_ID), m_selected_nozzle);
|
||||
for (auto& item : m_nozzle_items) {
|
||||
s_enable_item_if_match(item.second, rack->GetNozzleSystem()->GetNozzleByPosId(item.first), m_selected_nozzle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackSelect::UpdatSelectedNozzles(std::shared_ptr<DevNozzleRack> rack,
|
||||
std::vector<int> selected_nozzle_pos_vec,
|
||||
bool use_dynamic_switch,
|
||||
std::optional<PrintFromType> /*print_from_type*/)
|
||||
{
|
||||
m_nozzle_rack = rack;
|
||||
m_enable_manual_nozzle_pick = !use_dynamic_switch;
|
||||
m_title_tips_dynamic->Show(use_dynamic_switch);
|
||||
|
||||
UpdateNozzleInfos(rack);
|
||||
if (!use_dynamic_switch) {
|
||||
if (selected_nozzle_pos_vec.size() > 0) {
|
||||
return UpdatSelectedNozzle(rack, selected_nozzle_pos_vec.at(0));
|
||||
} else {
|
||||
return ClearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
if (rack) {
|
||||
ClearSelection();
|
||||
for (const auto& pos_id : selected_nozzle_pos_vec) {
|
||||
if (pos_id == MAIN_EXTRUDER_ID) {
|
||||
m_toolhead_nozzle_r->SetSelected(true);
|
||||
} else if (pos_id == DEPUTY_EXTRUDER_ID) {
|
||||
m_toolhead_nozzle_l->SetSelected(true);
|
||||
} else if (auto it = m_nozzle_items.find(pos_id); it != m_nozzle_items.end()) {
|
||||
it->second->SetSelected(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_toolhead_nozzle_l->IsSelected()) {
|
||||
m_toolhead_nozzle_l->SetDisable(true);
|
||||
}
|
||||
|
||||
if (!m_toolhead_nozzle_r->IsSelected()) {
|
||||
m_toolhead_nozzle_r->SetDisable(true);
|
||||
}
|
||||
|
||||
for (const auto& item : m_nozzle_items) {
|
||||
if (!item.second->IsSelected()) {
|
||||
item.second->SetDisable(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackSelect::ClearSelection()
|
||||
{
|
||||
m_selected_nozzle = DevNozzle();
|
||||
m_toolhead_nozzle_l->SetSelected(false);
|
||||
m_toolhead_nozzle_r->SetSelected(false);
|
||||
for (auto &item : m_nozzle_items) { item.second->SetSelected(false); }
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackSelect::SetSelectedNozzle(const DevNozzle &nozzle)
|
||||
{
|
||||
int new_selected_pos_id = nozzle.GetNozzlePosId();
|
||||
if (m_selected_nozzle.GetNozzlePosId() != new_selected_pos_id) {
|
||||
ClearSelection();
|
||||
|
||||
m_selected_nozzle = nozzle;
|
||||
if (new_selected_pos_id == MAIN_EXTRUDER_ID) {
|
||||
m_toolhead_nozzle_r->SetSelected(true);
|
||||
} else if (new_selected_pos_id == DEPUTY_EXTRUDER_ID) {
|
||||
m_toolhead_nozzle_l->SetSelected(true);
|
||||
} else if (auto it = m_nozzle_items.find(m_selected_nozzle.GetNozzlePosId()); it != m_nozzle_items.end()) {
|
||||
it->second->SetSelected(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
volatile int sGetNozzlePosId(wgtDeviceNozzleRackNozzleItem* item,
|
||||
wgtDeviceNozzleRackNozzleItem* l_item,
|
||||
wgtDeviceNozzleRackNozzleItem* r_item)
|
||||
{
|
||||
int to_select_pos_id = -1;
|
||||
if (item == l_item) {
|
||||
to_select_pos_id = DEPUTY_EXTRUDER_ID;
|
||||
} else if (item == r_item) {
|
||||
to_select_pos_id = MAIN_EXTRUDER_ID;
|
||||
} else {
|
||||
to_select_pos_id = item->GetNozzleId() + 0x10;
|
||||
}
|
||||
|
||||
return to_select_pos_id;
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackSelect::OnNozzleItemSelected(wxCommandEvent &evt)
|
||||
{
|
||||
if (!m_enable_manual_nozzle_pick) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto *item = dynamic_cast<wgtDeviceNozzleRackNozzleItem *>(evt.GetEventObject());
|
||||
if (item; auto ptr = m_nozzle_rack.lock()) {
|
||||
int to_select_pos_id = sGetNozzlePosId(item, m_toolhead_nozzle_l, m_toolhead_nozzle_r);
|
||||
if (to_select_pos_id > -1 && to_select_pos_id != GetSelectedNozzlePosID()) {
|
||||
SetSelectedNozzle(ptr->GetNozzleSystem()->GetNozzleByPosId(to_select_pos_id));
|
||||
wxCommandEvent change_evt(EVT_NOZZLE_SELECT_CHANGED, GetId());
|
||||
change_evt.SetEventObject(this);
|
||||
ProcessEvent(change_evt);
|
||||
evt.Skip();
|
||||
} else {
|
||||
wxCommandEvent change_evt(EVT_NOZZLE_SELECT_CLICKED, GetId());
|
||||
change_evt.SetEventObject(this);
|
||||
ProcessEvent(change_evt);
|
||||
evt.Skip();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackSelect::Rescale()
|
||||
{
|
||||
m_toolhead_nozzle_l->Rescale();
|
||||
m_toolhead_nozzle_r->Rescale();
|
||||
for (auto &item : m_nozzle_items) { item.second->Rescale(); }
|
||||
}
|
||||
|
||||
}; // namespace Slic3r::GUI
|
||||
@@ -0,0 +1,74 @@
|
||||
//**********************************************************/
|
||||
/* File: wgtDeviceNozzleSelect.h
|
||||
* Description: The panel to select nozzle
|
||||
*
|
||||
* \n class wgtDeviceNozzleSelect;
|
||||
//**********************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "slic3r/GUI/DeviceCore/DevNozzleSystem.h" // DevNozzle (value member) + DevDefs.h (PrintFromType, MAIN/DEPUTY_EXTRUDER_ID)
|
||||
|
||||
#include <wx/panel.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
// Previous definitions
|
||||
class Label;
|
||||
namespace Slic3r
|
||||
{
|
||||
struct DevNozzle;
|
||||
class DevNozzleRack;
|
||||
namespace GUI
|
||||
{
|
||||
class wgtDeviceNozzleRackNozzleItem;
|
||||
class wgtMsgBox;
|
||||
}
|
||||
};
|
||||
|
||||
wxDECLARE_EVENT(EVT_NOZZLE_SELECT_CHANGED, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_NOZZLE_SELECT_CLICKED, wxCommandEvent);
|
||||
namespace Slic3r::GUI
|
||||
{
|
||||
class wgtDeviceNozzleRackSelect : public wxPanel
|
||||
{
|
||||
public:
|
||||
wgtDeviceNozzleRackSelect(wxWindow* parent);
|
||||
~wgtDeviceNozzleRackSelect() = default;
|
||||
|
||||
public:
|
||||
int GetSelectedNozzlePosID() const { return m_selected_nozzle.GetNozzlePosId();}
|
||||
void UpdatSelectedNozzles(std::shared_ptr<DevNozzleRack> rack, std::vector<int> selected_nozzle_pos_vec, bool use_dynamic_switch, std::optional<PrintFromType> print_from_type);// for slicing with dynamic switch
|
||||
void Rescale();
|
||||
|
||||
private:
|
||||
void CreateGui();
|
||||
|
||||
void ClearSelection();
|
||||
void SetSelectedNozzle(const DevNozzle &nozzle);
|
||||
void UpdatSelectedNozzle(std::shared_ptr<DevNozzleRack> rack, int selected_nozzle_pos_id = -1); // for slicing without dynamic switch
|
||||
|
||||
void UpdateNozzleInfos(std::shared_ptr<DevNozzleRack> rack);
|
||||
|
||||
void OnNozzleItemSelected(wxCommandEvent& evt);
|
||||
|
||||
private:
|
||||
DevNozzle m_selected_nozzle;
|
||||
std::weak_ptr<DevNozzleRack> m_nozzle_rack;
|
||||
|
||||
// pick
|
||||
bool m_enable_manual_nozzle_pick = true;
|
||||
|
||||
// Label
|
||||
wgtMsgBox* m_title_tips_dynamic;
|
||||
|
||||
// GUI
|
||||
wgtDeviceNozzleRackNozzleItem * m_toolhead_nozzle_l{nullptr};
|
||||
wgtDeviceNozzleRackNozzleItem * m_toolhead_nozzle_r{nullptr};
|
||||
std::unordered_map<int, wgtDeviceNozzleRackNozzleItem *> m_nozzle_items; // from 16 to 21
|
||||
};
|
||||
|
||||
};// end of namespace Slic3r::GUI
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "wgtMsgBox.h"
|
||||
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/Widgets/Label.hpp"
|
||||
#include "slic3r/GUI/wxExtensions.hpp"
|
||||
|
||||
#include <wx/sizer.h>
|
||||
|
||||
namespace Slic3r::GUI
|
||||
{
|
||||
|
||||
wgtMsgBox::wgtMsgBox(wxWindow* parent)
|
||||
: StaticBox(parent, wxID_ANY)
|
||||
{
|
||||
CreateGUI();
|
||||
}
|
||||
|
||||
void wgtMsgBox::CreateGUI()
|
||||
{
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
m_txt_label = new Label(this);
|
||||
m_txt_label->SetFont(::Label::Body_13);
|
||||
m_txt_label->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
auto padding_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
padding_sizer->Add(m_txt_label, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5));
|
||||
mainSizer->Add(padding_sizer, 0, wxALIGN_CENTER_VERTICAL | wxALL, 1);
|
||||
|
||||
//Button* btnClose = CreateCloseButton();
|
||||
//wxButton* btnClose = CreateCloseButton();
|
||||
//mainSizer->Add(btnClose, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
mainSizer->Fit(this);
|
||||
}
|
||||
|
||||
void wgtMsgBox::OnCloseClicked(wxCommandEvent& evt)
|
||||
{
|
||||
this->Hide();
|
||||
}
|
||||
|
||||
} // namespace Slic3r::GUI
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef WGTMSGBOX_H
|
||||
#define WGTMSGBOX_H
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/button.h>
|
||||
|
||||
#include "slic3r/GUI/Widgets/StaticBox.hpp"
|
||||
|
||||
class Label;
|
||||
namespace Slic3r::GUI
|
||||
{
|
||||
class wgtMsgBox : public StaticBox
|
||||
{
|
||||
public:
|
||||
wgtMsgBox(wxWindow* parent);
|
||||
|
||||
public:
|
||||
Label* GetTextLabel() const { return m_txt_label; }
|
||||
|
||||
private:
|
||||
Label* m_txt_label;
|
||||
|
||||
private:
|
||||
void CreateGUI();
|
||||
void OnCloseClicked(wxCommandEvent& evt);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // WGTMSGBOX_H
|
||||
@@ -138,7 +138,8 @@ FilamentMapDialog::FilamentMapDialog(wxWindow *parent,
|
||||
|
||||
if (mode < fmmManual)
|
||||
m_page_type = PageType::ptAuto;
|
||||
else if (mode == fmmManual)
|
||||
else if (mode == fmmManual || mode == fmmNozzleManual)
|
||||
// Orca: there is no dedicated nozzle-manual page, so treat an fmmNozzleManual plate as a manual variant and show the Custom page instead of misfiling it as "Same as Global".
|
||||
m_page_type = PageType::ptManual;
|
||||
else
|
||||
m_page_type = PageType::ptDefault;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "FilamentMapPanel.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "Widgets/MultiNozzleSync.hpp" // manuallySetNozzleCount producer for extruder_nozzle_stats
|
||||
#include <algorithm>
|
||||
#include <wx/dcbuffer.h>
|
||||
#include <wx/utils.h>
|
||||
#include "wx/graphics.h"
|
||||
@@ -72,6 +74,33 @@ FilamentMapManualPanel::FilamentMapManualPanel(wxWindow *p
|
||||
|
||||
m_switch_btn->Bind(wxEVT_BUTTON, &FilamentMapManualPanel::OnSwitchFilament, this);
|
||||
|
||||
// Multi-nozzle: give the user a reachable way to declare, per extruder, how many
|
||||
// physical nozzles of each volume type a multi-nozzle extruder carries. This is the
|
||||
// fallback "manual" producer of the extruder_nozzle_stats config (the full device nozzle-rack
|
||||
// auto-sync is deferred). Gated on the edited printer preset having an extruder with
|
||||
// extruder_max_nozzle_count > 1, so the trigger is not even created for any single-nozzle or
|
||||
// dual-extruder ({1,1}, H2D) printer - zero UI change for every existing profile.
|
||||
auto *max_nozzle_counts_opt = wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionIntsNullable>("extruder_max_nozzle_count");
|
||||
// Skip nil entries: a nullable-int nil is INT_MAX (> 1) and would otherwise falsely pass the gate.
|
||||
if (max_nozzle_counts_opt &&
|
||||
std::any_of(max_nozzle_counts_opt->values.begin(), max_nozzle_counts_opt->values.end(),
|
||||
[](int v) { return v > 1 && v != ConfigOptionIntsNullable::nil_value(); })) {
|
||||
auto *set_count_link = new Label(this, _L("Set the physical nozzle count..."));
|
||||
set_count_link->SetFont(Label::Body_14);
|
||||
set_count_link->SetForegroundColour(BorderSelectedColor);
|
||||
set_count_link->SetCursor(wxCursor(wxCURSOR_HAND));
|
||||
top_sizer->AddSpacer(FromDIP(8));
|
||||
top_sizer->Add(set_count_link, 0, wxALIGN_LEFT | wxLEFT, FromDIP(15));
|
||||
const std::vector<int> max_counts = max_nozzle_counts_opt->values;
|
||||
set_count_link->Bind(wxEVT_LEFT_DOWN, [max_counts](wxMouseEvent &evt) {
|
||||
for (int extruder_id = 0; extruder_id < (int) max_counts.size(); ++extruder_id) {
|
||||
if (max_counts[extruder_id] > 1)
|
||||
GUI::manuallySetNozzleCount(extruder_id);
|
||||
}
|
||||
evt.Skip();
|
||||
});
|
||||
}
|
||||
|
||||
SetSizer(top_sizer);
|
||||
Layout();
|
||||
Fit();
|
||||
|
||||
@@ -2911,8 +2911,11 @@ void GCodeViewer::render_legend_color_arr_recommen(float window_padding)
|
||||
|
||||
float delta_weight_to_single_ext = stats_by_extruder.stats_by_single_extruder.filament_flush_weight - stats_by_extruder.stats_by_multi_extruder_curr.filament_flush_weight;
|
||||
float delta_weight_to_best = stats_by_extruder.stats_by_multi_extruder_curr.filament_flush_weight - stats_by_extruder.stats_by_multi_extruder_best.filament_flush_weight;
|
||||
int delta_change_to_single_ext = stats_by_extruder.stats_by_single_extruder.filament_change_count - stats_by_extruder.stats_by_multi_extruder_curr.filament_change_count;
|
||||
int delta_change_to_best = stats_by_extruder.stats_by_multi_extruder_curr.filament_change_count - stats_by_extruder.stats_by_multi_extruder_best.filament_change_count;
|
||||
// The displayed "hand changes" delta uses the per-nozzle flush_filament_change_count.
|
||||
// For single-nozzle-per-extruder printers it equals the per-extruder filament_change_count,
|
||||
// so the shown value is unchanged.
|
||||
int delta_change_to_single_ext = stats_by_extruder.stats_by_single_extruder.flush_filament_change_count - stats_by_extruder.stats_by_multi_extruder_curr.flush_filament_change_count;
|
||||
int delta_change_to_best = stats_by_extruder.stats_by_multi_extruder_curr.flush_filament_change_count - stats_by_extruder.stats_by_multi_extruder_best.flush_filament_change_count;
|
||||
|
||||
bool any_less_to_single_ext = delta_weight_to_single_ext > EPSILON || delta_change_to_single_ext > 0;
|
||||
bool any_more_to_best = delta_weight_to_best > EPSILON || delta_change_to_best > 0;
|
||||
|
||||
@@ -8136,6 +8136,11 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
||||
shader = wxGetApp().get_shader("gouraud");
|
||||
ECanvasType canvas_type = this->m_canvas_type;
|
||||
bool partly_inside_enable = canvas_type == ECanvasType::CanvasAssembleView ? false : true;
|
||||
// The edited printer's per-extruder printable heights feed the object shader's
|
||||
// extruder_printable_heights uniform. Empty for single-extruder printers, so the shader flag stays
|
||||
// 0.0 and rendering is pixel-identical there (see GLVolumeCollection::render).
|
||||
auto printable_height_option = wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloatsNullable>("extruder_printable_height");
|
||||
std::vector<double>* printable_heights = printable_height_option ? &printable_height_option->values : nullptr;
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
|
||||
@@ -8183,7 +8188,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
||||
return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
|
||||
}
|
||||
},
|
||||
partly_inside_enable);
|
||||
partly_inside_enable, printable_heights);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -8218,7 +8223,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
||||
return true;
|
||||
}
|
||||
},
|
||||
partly_inside_enable);
|
||||
partly_inside_enable, printable_heights);
|
||||
if (m_canvas_type == CanvasAssembleView && m_gizmos.m_assemble_view_data->model_objects_clipper()->get_position() > 0) {
|
||||
const GLGizmosManager& gm = get_gizmos_manager();
|
||||
shader->stop_using();
|
||||
|
||||
@@ -518,6 +518,16 @@ void MonitorPanel::jump_to_HMS()
|
||||
m_tabpanel->SetSelection(PT_HMS);
|
||||
}
|
||||
|
||||
void MonitorPanel::jump_to_Upgrade()
|
||||
{
|
||||
if (this->IsShown()) {
|
||||
auto page = m_tabpanel->GetCurrentPage();
|
||||
if (page && page != m_upgrade_panel) {
|
||||
m_tabpanel->SetSelection(PT_UPDATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MonitorPanel::jump_to_LiveView()
|
||||
{
|
||||
if (!this->IsShown()) { return; }
|
||||
|
||||
@@ -156,6 +156,7 @@ public:
|
||||
|
||||
|
||||
void jump_to_HMS();
|
||||
void jump_to_Upgrade();
|
||||
void jump_to_LiveView();
|
||||
void update_network_version_footer();
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "I18N.hpp"
|
||||
//#include "ConfigWizard.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "Widgets/Label.hpp"
|
||||
#include "slic3r/GUI/MainFrame.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#define MSG_DLG_MAX_SIZE wxSize(-1, FromDIP(464))//notice:ban setting the maximum width value
|
||||
@@ -750,6 +751,47 @@ NetworkErrorDialog::NetworkErrorDialog(wxWindow* parent)
|
||||
Centre(wxBOTH);
|
||||
}
|
||||
|
||||
|
||||
FilamentWarningDialog::FilamentWarningDialog(wxWindow *parent, const wxString &title, std::vector<FilamentWarningInfo> infos)
|
||||
: MsgDialog(parent, title.IsEmpty() ? wxString::Format(_L("%s warning"), SLIC3R_APP_FULL_NAME) : title, wxEmptyString, wxOK | wxICON_WARNING), m_messages(infos)
|
||||
{
|
||||
BuildContent();
|
||||
finalize();
|
||||
}
|
||||
|
||||
void FilamentWarningDialog::BuildContent()
|
||||
{
|
||||
wxBoxSizer *messages_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
int message_count = 0;
|
||||
for (int i = 0; i < m_messages.size(); i++)
|
||||
{
|
||||
const wxString &message = m_messages[i].info_msg;
|
||||
const wxString &wiki_url = m_messages[i].wiki_url;
|
||||
if (message_count > 0) { messages_sizer->AddSpacer(FromDIP(10)); }
|
||||
|
||||
if (wiki_url.IsEmpty()) {
|
||||
// No wiki link - just display as regular text
|
||||
Label *text = new Label(this, message);
|
||||
text->SetFont(::Label::Body_12);
|
||||
text->Wrap(FromDIP(400));
|
||||
messages_sizer->Add(text, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(5));
|
||||
} else {
|
||||
Label *link = new Label(this, message + " " + _L("Please refer to Wiki before use->"));
|
||||
link->SetForegroundColour(wxColour(8, 153, 46));
|
||||
link->SetFont(::Label::Body_12);
|
||||
link->Wrap(FromDIP(400));
|
||||
link->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { SetCursor(wxCURSOR_HAND); });
|
||||
link->Bind(wxEVT_LEAVE_WINDOW, [this](auto &e) { SetCursor(wxCURSOR_ARROW); });
|
||||
link->Bind(wxEVT_LEFT_DOWN, [wiki_url](auto &event) { wxLaunchDefaultBrowser(wiki_url); });
|
||||
messages_sizer->Add(link, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(5));
|
||||
}
|
||||
message_count++;
|
||||
}
|
||||
|
||||
content_sizer->Add(messages_sizer, 1, wxEXPAND | wxALL, FromDIP(5));
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -67,6 +67,10 @@ struct MsgDialog : DPIDialog
|
||||
bool get_checkbox_state();
|
||||
virtual void on_dpi_changed(const wxRect& suggested_rect);
|
||||
void SetButtonLabel(wxWindowID btn_id, const wxString& label, bool set_focus = false);
|
||||
// Public wrapper around add_button — lets callers append custom-labelled choice buttons to an
|
||||
// already-constructed dialog (used by the H2C rack hotend "Jump to the upgrade page" prompt).
|
||||
// Purely additive; existing dialogs are unaffected.
|
||||
void AddButton(wxWindowID btn_id, const wxString& label, bool set_focus = false) { add_button(btn_id, set_focus, label); }
|
||||
|
||||
protected:
|
||||
enum {
|
||||
@@ -434,6 +438,32 @@ public:
|
||||
bool m_show_again{false};
|
||||
};
|
||||
|
||||
// Multi-item filament-blacklist warning dialog with per-item wiki links. Replaces the plain
|
||||
// MessageDialog used for warnings so stacked accumulate-all warnings render with their own
|
||||
// optional Wiki link.
|
||||
struct FilamentWarningInfo
|
||||
{
|
||||
wxString info_msg;
|
||||
wxString wiki_url;
|
||||
};
|
||||
|
||||
class FilamentWarningDialog : public MsgDialog
|
||||
{
|
||||
public:
|
||||
FilamentWarningDialog(wxWindow *parent, const wxString &title, std::vector<FilamentWarningInfo> infos);
|
||||
FilamentWarningDialog(FilamentWarningDialog &&) = delete;
|
||||
FilamentWarningDialog(const FilamentWarningDialog &) = delete;
|
||||
FilamentWarningDialog &operator=(FilamentWarningDialog &&) = delete;
|
||||
FilamentWarningDialog &operator=(const FilamentWarningDialog &) = delete;
|
||||
virtual ~FilamentWarningDialog() = default;
|
||||
|
||||
private:
|
||||
void BuildContent();
|
||||
|
||||
private:
|
||||
std::vector<FilamentWarningInfo> m_messages;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <regex>
|
||||
#include "libslic3r/MultiNozzleUtils.hpp"
|
||||
#include <future>
|
||||
#include <glad/gl.h>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
@@ -66,6 +68,8 @@ static const int PARTPLATE_PLATENAME_OFFSET_Y = 10;
|
||||
const float WIPE_TOWER_DEFAULT_X_POS = 165.;
|
||||
const float WIPE_TOWER_DEFAULT_Y_POS = 250.; // Max y
|
||||
|
||||
const float N9_WIPE_TOWER_DEFAULT_Y_POS = 160.;
|
||||
|
||||
const float I3_WIPE_TOWER_DEFAULT_X_POS = 0.;
|
||||
const float I3_WIPE_TOWER_DEFAULT_Y_POS = 250.; // Max y
|
||||
|
||||
@@ -4221,6 +4225,12 @@ void PartPlateList::set_default_wipe_tower_pos_for_plate(int plate_idx, bool ini
|
||||
y = I3_WIPE_TOWER_DEFAULT_Y_POS;
|
||||
}
|
||||
|
||||
std::string printer_type = wxGetApp().preset_bundle->printers.get_edited_preset().get_printer_type(wxGetApp().preset_bundle);
|
||||
// Note: printer_type == "N9" and printer_structure_opt->value == PrinterStructure::psI3 can both be true
|
||||
if (printer_type == "N9") {
|
||||
y = N9_WIPE_TOWER_DEFAULT_Y_POS;
|
||||
}
|
||||
|
||||
PartPlate *part_plate = get_plate(plate_idx);
|
||||
Vec3d plate_origin = part_plate->get_origin();
|
||||
BoundingBoxf3 plate_bbox = part_plate->get_bounding_box();
|
||||
@@ -6279,6 +6289,58 @@ int PartPlateList::load_from_3mf_structure(PlateDataPtrs& plate_data_list, int f
|
||||
m_plate_list[index]->slice_filaments_info = plate_data_list[i]->slice_filaments_info;
|
||||
gcode_result->warnings = plate_data_list[i]->warnings;
|
||||
gcode_result->filament_maps = plate_data_list[i]->filament_maps;
|
||||
|
||||
// Reconstruct the device-side nozzle grouping from the loaded 3mf so
|
||||
// the monitor/preview can map filaments to physical nozzles.
|
||||
// load_nozzle_infos_with_compatibility handles older single-nozzle 3mf (no <nozzle> tags) by
|
||||
// falling back to the per-filament group_id, then to the extruder volume types / nozzle diameters.
|
||||
{
|
||||
// nozzle_volume_types is space-separated ints; nozzle_diameters is comma/space-separated floats.
|
||||
auto parse_int_tokens = [](const std::string& str) -> std::vector<int> {
|
||||
std::vector<int> out;
|
||||
std::istringstream iss(str);
|
||||
std::string tok;
|
||||
while (iss >> tok) { try { out.push_back(std::stoi(tok)); } catch (...) {} }
|
||||
return out;
|
||||
};
|
||||
auto parse_double_tokens = [](const std::string& str) -> std::vector<double> {
|
||||
std::vector<double> out;
|
||||
std::string s = str;
|
||||
std::replace(s.begin(), s.end(), ',', ' ');
|
||||
std::istringstream iss(s);
|
||||
std::string tok;
|
||||
while (iss >> tok) { try { out.push_back(std::stod(tok)); } catch (...) {} }
|
||||
return out;
|
||||
};
|
||||
|
||||
std::vector<int> nozzle_volume_type_values = parse_int_tokens(plate_data_list[i]->nozzle_volume_types);
|
||||
std::vector<double> nozzle_diameter_values = parse_double_tokens(plate_data_list[i]->nozzle_diameters);
|
||||
|
||||
std::vector<NozzleVolumeType> extruder_volume_types(nozzle_volume_type_values.size(), NozzleVolumeType::nvtStandard);
|
||||
for (size_t idx = 0; idx < nozzle_volume_type_values.size(); ++idx)
|
||||
if (nozzle_volume_type_values[idx] >= 0 && nozzle_volume_type_values[idx] <= nvtMaxNozzleVolumeType)
|
||||
extruder_volume_types[idx] = static_cast<NozzleVolumeType>(nozzle_volume_type_values[idx]);
|
||||
|
||||
auto nozzle_infos = MultiNozzleUtils::load_nozzle_infos_with_compatibility(
|
||||
plate_data_list[i]->nozzles_info,
|
||||
plate_data_list[i]->slice_filaments_info,
|
||||
plate_data_list[i]->filament_maps,
|
||||
extruder_volume_types,
|
||||
nozzle_diameter_values);
|
||||
|
||||
std::vector<int> fil_seq(plate_data_list[i]->filament_change_sequence.begin(), plate_data_list[i]->filament_change_sequence.end());
|
||||
std::vector<int> noz_seq(plate_data_list[i]->nozzle_change_sequence.begin(), plate_data_list[i]->nozzle_change_sequence.end());
|
||||
bool enable_filament_dynamic_map = false;
|
||||
if (plate_data_list[i]->config.has("enable_filament_dynamic_map"))
|
||||
enable_filament_dynamic_map = plate_data_list[i]->config.option<ConfigOptionBool>("enable_filament_dynamic_map")->value;
|
||||
|
||||
auto group_result = MultiNozzleUtils::StaticNozzleGroupResult::create(
|
||||
plate_data_list[i]->slice_filaments_info, nozzle_infos, fil_seq, noz_seq, enable_filament_dynamic_map);
|
||||
if (group_result)
|
||||
gcode_result->nozzle_group_result = std::make_shared<MultiNozzleUtils::StaticNozzleGroupResult>(group_result.value());
|
||||
else
|
||||
gcode_result->nozzle_group_result = nullptr;
|
||||
}
|
||||
if (m_plater && !plate_data_list[i]->thumbnail_file.empty()) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, load thumbnail from %2%.")%(i+1) %plate_data_list[i]->thumbnail_file;
|
||||
if (boost::filesystem::exists(plate_data_list[i]->thumbnail_file)) {
|
||||
@@ -6438,9 +6500,10 @@ void PartPlateList::init_bed_type_info()
|
||||
auto bed_texture_maps = wxGetApp().plater()->get_bed_texture_maps();
|
||||
std::string bottom_texture_end_name = bed_texture_maps.find("bottom_texture_end_name") != bed_texture_maps.end() ? bed_texture_maps["bottom_texture_end_name"] : "";
|
||||
std::string bottom_texture_rect_str = bed_texture_maps.find("bottom_texture_rect") != bed_texture_maps.end() ? bed_texture_maps["bottom_texture_rect"] : "";
|
||||
std::string bottom_texture_rect_longer_str = bed_texture_maps.find("bottom_texture_rect_longer") != bed_texture_maps.end() ? bed_texture_maps["bottom_texture_rect_longer"] : "";
|
||||
std::string middle_texture_rect_str = bed_texture_maps.find("middle_texture_rect") != bed_texture_maps.end() ? bed_texture_maps["middle_texture_rect"] : "";
|
||||
std::string use_double_extruder_default_texture = bed_texture_maps.find("use_double_extruder_default_texture") != bed_texture_maps.end() ? bed_texture_maps["use_double_extruder_default_texture"] : "";
|
||||
std::array<float, 4> bottom_texture_rect = {0, 0, 0, 0}, middle_texture_rect = {0, 0, 0, 0};
|
||||
std::array<float, 4> bottom_texture_rect = {0, 0, 0, 0}, bottom_texture_rect_longer = {0, 0, 0, 0}, middle_texture_rect = {0, 0, 0, 0};
|
||||
if (bottom_texture_rect_str.size() > 0) {
|
||||
std::vector<std::string> items;
|
||||
boost::algorithm::erase_all(bottom_texture_rect_str, " ");
|
||||
@@ -6451,6 +6514,16 @@ void PartPlateList::init_bed_type_info()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bottom_texture_rect_longer_str.size() > 0) {
|
||||
std::vector<std::string> items;
|
||||
boost::algorithm::erase_all(bottom_texture_rect_longer_str, " ");
|
||||
boost::split(items, bottom_texture_rect_longer_str, boost::is_any_of(","));
|
||||
if (items.size() == 4) {
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
bottom_texture_rect_longer[i] = std::atof(items[i].c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (middle_texture_rect_str.size() > 0) {
|
||||
std::vector<std::string> items;
|
||||
boost::algorithm::erase_all(middle_texture_rect_str, " ");
|
||||
@@ -6471,6 +6544,7 @@ void PartPlateList::init_bed_type_info()
|
||||
}
|
||||
pte_part2 = BedTextureInfo::TexturePart(45, -14.5, 70, 8, "bbl_bed_pte_left_bottom.svg");
|
||||
auto &bottom_rect = bottom_texture_rect;
|
||||
auto &bottom_rect_longer = bottom_texture_rect_longer;
|
||||
if (bottom_texture_end_name.size() > 0 && bottom_rect[2] > 0.f) {
|
||||
std::string pte_part2_name = "bbl_bed_pte_bottom_" + bottom_texture_end_name + ".svg";
|
||||
pte_part2 = BedTextureInfo::TexturePart(bottom_rect[0], bottom_rect[1], bottom_rect[2], bottom_rect[3], pte_part2_name);
|
||||
@@ -6494,6 +6568,9 @@ void PartPlateList::init_bed_type_info()
|
||||
if (bottom_texture_end_name.size() > 0 && bottom_rect[2] > 0.f) {
|
||||
std::string st_part2_name = "bbl_bed_st_bottom_" + bottom_texture_end_name + ".svg";
|
||||
st_part2 = BedTextureInfo::TexturePart(bottom_rect[0], bottom_rect[1], bottom_rect[2], bottom_rect[3], st_part2_name);
|
||||
} else if (bottom_rect_longer[2] > 0.f) {
|
||||
// SuperTack bottom strip uses the wider "longer" rect.
|
||||
st_part2.update_pos(bottom_rect_longer[0], bottom_rect_longer[1], bottom_rect_longer[2], bottom_rect_longer[3]);
|
||||
}
|
||||
|
||||
ep_part1 = BedTextureInfo::TexturePart(57, 300, 236.12f, 10.f, "bbl_bed_ep_middle.svg");
|
||||
@@ -6504,6 +6581,9 @@ void PartPlateList::init_bed_type_info()
|
||||
if (bottom_texture_end_name.size() > 0 && bottom_rect[2] > 0.f) {
|
||||
std::string ep_part2_name = "bbl_bed_ep_bottom_" + bottom_texture_end_name + ".svg";
|
||||
ep_part2 = BedTextureInfo::TexturePart(bottom_rect[0], bottom_rect[1], bottom_rect[2], bottom_rect[3], ep_part2_name);
|
||||
} else if (bottom_rect_longer[2] > 0.f) {
|
||||
// Engineering-plate bottom strip uses the wider "longer" rect.
|
||||
ep_part2.update_pos(bottom_rect_longer[0], bottom_rect_longer[1], bottom_rect_longer[2], bottom_rect_longer[3]);
|
||||
}
|
||||
|
||||
pc_part1 = BedTextureInfo::TexturePart(57, 300, 236.12f, 10.f, "bbl_bed_pc_middle.svg");
|
||||
|
||||
@@ -668,6 +668,12 @@ public:
|
||||
this->filename = part.filename;
|
||||
this->texture = part.texture;
|
||||
}
|
||||
void update_pos(float xx, float yy, float ww, float hh) {
|
||||
x = xx;
|
||||
y = yy;
|
||||
w = ww;
|
||||
h = hh;
|
||||
}
|
||||
void update_file(std::string file) {
|
||||
filename = file;
|
||||
}
|
||||
|
||||
+359
-18
@@ -122,6 +122,8 @@
|
||||
#include "NotificationManager.hpp"
|
||||
#include "PresetComboBoxes.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "Widgets/MultiNozzleSync.hpp" // NozzleOption, tryPopUpMultiNozzleDialog, setExtruderNozzleCount
|
||||
#include "DeviceCore/DevNozzleSystem.h" // DevNozzle, GetExtNozzles / GetRackNozzles
|
||||
#include "ProjectDirtyStateManager.hpp"
|
||||
#include "Gizmos/GLGizmoSimplify.hpp" // create suggestion notification
|
||||
#include "Gizmos/GLGizmoSVG.hpp" // Drop SVG file
|
||||
@@ -584,7 +586,11 @@ struct Sidebar::priv
|
||||
void jump_to_object(ObjectDataViewModelNode* item);
|
||||
void can_search();
|
||||
|
||||
bool sync_extruder_list(bool &only_external_material);
|
||||
bool sync_extruder_list(bool &only_external_material, bool is_manual = false);
|
||||
// Resolve the nozzle option for a multi-nozzle machine. Returns nullopt (and is a no-op) unless
|
||||
// extruder_count >= 2 && support_multi_nozzle. When is_manual, always pops the MultiNozzleSyncDialog;
|
||||
// otherwise reuses the app_config-cached option when the machine's nozzle config is unchanged.
|
||||
std::optional<NozzleOption> get_nozzle_options(MachineObject* obj, int extruder_count, bool support_multi_nozzle, bool is_manual);
|
||||
bool switch_diameter(bool single);
|
||||
void update_sync_status(const MachineObject* obj);
|
||||
|
||||
@@ -1323,7 +1329,288 @@ static bool is_skip_high_flow_printer(const std::string& printer)
|
||||
return invalidate_list.count(printer);
|
||||
};
|
||||
|
||||
bool Sidebar::priv::sync_extruder_list(bool &only_external_material)
|
||||
// ---- Multi-nozzle sync helpers ----------------------------
|
||||
// Serialize/deserialize the machine nozzle config + chosen NozzleOption for the app_config
|
||||
// "sync_extruder" reuse cache, and Sidebar::priv::get_nozzle_options (the nozzle picker).
|
||||
|
||||
static std::string serialize_nozzle_config(const std::map<int, std::vector<DevNozzle>>& nozzle_cfg_map)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
||||
std::vector<DevNozzle> deputy_nozzles;
|
||||
auto deputy_it = nozzle_cfg_map.find(1);
|
||||
if (deputy_it != nozzle_cfg_map.end()) {
|
||||
deputy_nozzles = deputy_it->second;
|
||||
}
|
||||
|
||||
std::vector<DevNozzle> main_nozzles;
|
||||
auto main_it = nozzle_cfg_map.find(0);
|
||||
if (main_it != nozzle_cfg_map.end()) {
|
||||
main_nozzles = main_it->second;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < deputy_nozzles.size(); ++i) {
|
||||
if (i > 0) oss << ";";
|
||||
oss << std::fixed << std::setprecision(1) << deputy_nozzles[i].GetNozzleDiameter() << ","
|
||||
<< static_cast<int>(deputy_nozzles[i].GetNozzleFlowType());
|
||||
}
|
||||
|
||||
oss << "|";
|
||||
|
||||
for (size_t i = 0; i < main_nozzles.size(); ++i) {
|
||||
if (i > 0) oss << ";";
|
||||
oss << std::fixed << std::setprecision(1) << main_nozzles[i].GetNozzleDiameter() << ","
|
||||
<< static_cast<int>(main_nozzles[i].GetNozzleFlowType());
|
||||
}
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
static std::map<int, std::vector<DevNozzle>> deserialize_nozzle_config(const std::string &config_str)
|
||||
{
|
||||
std::map<int, std::vector<DevNozzle>> nozzle_cfg_map;
|
||||
if (config_str.empty()) return nozzle_cfg_map;
|
||||
|
||||
std::vector<std::string> extruder_parts;
|
||||
boost::split(extruder_parts, config_str, boost::is_any_of("|"));
|
||||
|
||||
auto get_nozzles_from_string = [](const std::string& part_str) -> std::vector<DevNozzle> {
|
||||
std::vector<DevNozzle> nozzles;
|
||||
std::vector<std::string> parts;
|
||||
boost::split(parts, part_str, boost::is_any_of(";"));
|
||||
for (const auto &part : parts) {
|
||||
std::vector<std::string> values;
|
||||
boost::split(values, part, boost::is_any_of(","));
|
||||
if (values.size() == 2) {
|
||||
DevNozzle nozzle;
|
||||
nozzle.m_diameter = std::stof(values[0]);
|
||||
nozzle.m_nozzle_flow = static_cast<NozzleFlowType>(std::stoi(values[1]));
|
||||
nozzles.push_back(nozzle);
|
||||
}
|
||||
}
|
||||
return nozzles;
|
||||
};
|
||||
|
||||
if (extruder_parts.size() != 2) {
|
||||
auto nozzles = get_nozzles_from_string(config_str);
|
||||
nozzle_cfg_map[MAIN_EXTRUDER_ID] = nozzles;
|
||||
nozzle_cfg_map[DEPUTY_EXTRUDER_ID] = { DevNozzle() };
|
||||
return nozzle_cfg_map;
|
||||
}
|
||||
|
||||
if (!extruder_parts[0].empty()) {
|
||||
auto nozzles = get_nozzles_from_string(extruder_parts[0]);
|
||||
nozzle_cfg_map[DEPUTY_EXTRUDER_ID] = nozzles;
|
||||
}
|
||||
if (!extruder_parts[1].empty()) {
|
||||
auto nozzles = get_nozzles_from_string(extruder_parts[1]);
|
||||
nozzle_cfg_map[MAIN_EXTRUDER_ID] = nozzles;
|
||||
}
|
||||
|
||||
return nozzle_cfg_map;
|
||||
}
|
||||
|
||||
static bool is_same_nozzle_config(const std::map<int, std::vector<DevNozzle>> &config1, const std::map<int, std::vector<DevNozzle>> &config2)
|
||||
{
|
||||
if (config1.size() != config2.size()) return false;
|
||||
|
||||
for (const auto& [eid, nozzles1] : config1) {
|
||||
auto it = config2.find(eid);
|
||||
if (it == config2.end()) return false;
|
||||
|
||||
const auto &nozzles2 = it->second;
|
||||
if (nozzles1.size() != nozzles2.size()) return false;
|
||||
|
||||
auto sorted_nozzles1 = nozzles1;
|
||||
auto sorted_nozzles2 = nozzles2;
|
||||
|
||||
auto compare_nozzle = [](const DevNozzle &a, const DevNozzle &b) {
|
||||
float dia_a = a.GetNozzleDiameter();
|
||||
float dia_b = b.GetNozzleDiameter();
|
||||
if (std::abs(dia_a - dia_b) > EPSILON) {
|
||||
return dia_a < dia_b;
|
||||
}
|
||||
return static_cast<int>(a.GetNozzleFlowType()) < static_cast<int>(b.GetNozzleFlowType());
|
||||
};
|
||||
|
||||
std::sort(sorted_nozzles1.begin(), sorted_nozzles1.end(), compare_nozzle);
|
||||
std::sort(sorted_nozzles2.begin(), sorted_nozzles2.end(), compare_nozzle);
|
||||
|
||||
for (size_t i = 0; i < sorted_nozzles1.size(); ++i) {
|
||||
if (std::abs(sorted_nozzles1[i].GetNozzleDiameter() - sorted_nozzles2[i].GetNozzleDiameter()) > EPSILON ||
|
||||
sorted_nozzles1[i].GetNozzleFlowType() != sorted_nozzles2[i].GetNozzleFlowType()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string serialize_nozzle_option(const NozzleOption& option) {
|
||||
std::ostringstream oss;
|
||||
oss << option.diameter << "|";
|
||||
|
||||
bool first = true;
|
||||
for (const auto& pair : option.extruder_nozzle_stats) {
|
||||
if (!first) oss << ";";
|
||||
first = false;
|
||||
oss << pair.first << ":";
|
||||
|
||||
bool first_stat = true;
|
||||
for (const auto& stat_pair : pair.second) {
|
||||
if (!first_stat) oss << ",";
|
||||
first_stat = false;
|
||||
oss << static_cast<int>(stat_pair.first) << "#" << stat_pair.second;
|
||||
}
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
static std::optional<NozzleOption> deserialize_nozzle_option(const std::string& option_str) {
|
||||
if (option_str.empty()) return std::nullopt;
|
||||
|
||||
std::vector<std::string> parts;
|
||||
boost::split(parts, option_str, boost::is_any_of("|"));
|
||||
if (parts.size() != 2) return std::nullopt;
|
||||
|
||||
NozzleOption option;
|
||||
option.diameter = parts[0];
|
||||
|
||||
std::vector<std::string> extruder_parts;
|
||||
boost::split(extruder_parts, parts[1], boost::is_any_of(";"));
|
||||
|
||||
for (const auto& extruder_part : extruder_parts) {
|
||||
if (extruder_part.empty()) continue;
|
||||
|
||||
std::vector<std::string> extruder_data;
|
||||
boost::split(extruder_data, extruder_part, boost::is_any_of(":"));
|
||||
if (extruder_data.size() != 2) continue;
|
||||
|
||||
int extruder_id = std::stoi(extruder_data[0]);
|
||||
std::unordered_map<NozzleVolumeType, int> stats;
|
||||
|
||||
std::vector<std::string> stat_parts;
|
||||
boost::split(stat_parts, extruder_data[1], boost::is_any_of(","));
|
||||
|
||||
for (const auto& stat_part : stat_parts) {
|
||||
std::vector<std::string> kv;
|
||||
boost::split(kv, stat_part, boost::is_any_of("#"));
|
||||
if (kv.size() == 2) {
|
||||
NozzleVolumeType type = static_cast<NozzleVolumeType>(std::stoi(kv[0]));
|
||||
int count = std::stoi(kv[1]);
|
||||
stats[type] = count;
|
||||
}
|
||||
}
|
||||
|
||||
option.extruder_nozzle_stats[extruder_id] = stats;
|
||||
}
|
||||
|
||||
return option;
|
||||
}
|
||||
|
||||
std::optional<NozzleOption> Sidebar::priv::get_nozzle_options(MachineObject* obj, int extruder_count, bool support_multi_nozzle, bool is_manual)
|
||||
{
|
||||
if (extruder_count < 2 || !support_multi_nozzle) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!obj || !obj->GetNozzleSystem()) return std::nullopt;
|
||||
auto nozzle_system = obj->GetNozzleSystem();
|
||||
|
||||
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
|
||||
if (!preset_bundle) return std::nullopt;
|
||||
|
||||
std::string curr_dev_id = obj->get_dev_id();
|
||||
std::map<int, std::vector<DevNozzle>> curr_nozzle_cfg;
|
||||
|
||||
for (const auto& ext_nozzle : nozzle_system->GetExtNozzles()) {
|
||||
int extruder_id = ext_nozzle.first;
|
||||
curr_nozzle_cfg[extruder_id].emplace_back(ext_nozzle.second);
|
||||
}
|
||||
|
||||
for (const auto& rack_nozzle : nozzle_system->GetRackNozzles()) {
|
||||
curr_nozzle_cfg[MAIN_EXTRUDER_ID].emplace_back(rack_nozzle.second);
|
||||
}
|
||||
|
||||
AppConfig *app_config = wxGetApp().app_config;
|
||||
std::string saved_dev_id = app_config->get("sync_extruder", "dev_id");
|
||||
std::string saved_nozzle_config_str = app_config->get("sync_extruder", "nozzle_config");
|
||||
std::string saved_nozzle_option_str = app_config->get("sync_extruder", "nozzle_option");
|
||||
std::optional<NozzleOption> nozzle_option;
|
||||
|
||||
if (is_manual) {
|
||||
nozzle_option = tryPopUpMultiNozzleDialog(obj);
|
||||
} else {
|
||||
bool can_reuse_saved_option = false;
|
||||
if (!saved_dev_id.empty() && !saved_nozzle_config_str.empty() && !saved_nozzle_option_str.empty() && saved_dev_id == curr_dev_id) {
|
||||
auto saved_nozzle_config = deserialize_nozzle_config(saved_nozzle_config_str);
|
||||
if (is_same_nozzle_config(saved_nozzle_config, curr_nozzle_cfg)) {
|
||||
nozzle_option = deserialize_nozzle_option(saved_nozzle_option_str);
|
||||
can_reuse_saved_option = nozzle_option.has_value();
|
||||
}
|
||||
if (can_reuse_saved_option) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Reusing saved nozzle option for dev_id: " << curr_dev_id;
|
||||
|
||||
auto &project_config = preset_bundle->project_config;
|
||||
ConfigOptionEnumsGeneric *nozzle_volume_type_opt = project_config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type");
|
||||
|
||||
// Write to preset bundle config
|
||||
for (int extruder_id = 0; extruder_id < extruder_count; ++extruder_id) {
|
||||
NozzleVolumeType volume_type;
|
||||
int nozzle_count;
|
||||
bool clear_all = true;
|
||||
|
||||
if (!nozzle_option->extruder_nozzle_stats.count(extruder_id)) {
|
||||
nozzle_count = 0;
|
||||
// Reset the concrete volume types (Standard/High Flow); Hybrid stays a hidden match-any
|
||||
// sentinel and is left alone. TPU High Flow is a concrete variant shipped on 0.4/0.6
|
||||
// nozzles, so reset it too, but only there (mirrors the High-Flow-skip-for-0.2 guard).
|
||||
std::vector<NozzleVolumeType> reset_types{nvtStandard, nvtHighFlow};
|
||||
if (extruder_supports_tpu_high_flow(preset_bundle, extruder_id))
|
||||
reset_types.push_back(nvtTPUHighFlow);
|
||||
for (NozzleVolumeType vt : reset_types) {
|
||||
volume_type = vt;
|
||||
setExtruderNozzleCount(preset_bundle, extruder_id, volume_type, nozzle_count, clear_all);
|
||||
clear_all = false;
|
||||
}
|
||||
} else {
|
||||
for (auto &stat : nozzle_option->extruder_nozzle_stats[extruder_id]) {
|
||||
volume_type = stat.first;
|
||||
nozzle_count = stat.second;
|
||||
setExtruderNozzleCount(preset_bundle, extruder_id, volume_type, nozzle_count, clear_all);
|
||||
clear_all = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Orca: nozzle stats are persisted directly into the preset's `extruder_nozzle_stats` key;
|
||||
// there is no machine/user data-source flag on the stats.
|
||||
}
|
||||
}
|
||||
|
||||
if (!can_reuse_saved_option) {
|
||||
nozzle_option = tryPopUpMultiNozzleDialog(obj);
|
||||
}
|
||||
}
|
||||
if (nozzle_option) {
|
||||
std::string current_nozzle_config = serialize_nozzle_config(curr_nozzle_cfg);
|
||||
std::string current_nozzle_option = serialize_nozzle_option(*nozzle_option);
|
||||
if (app_config->has_section("sync_extruder")) {
|
||||
app_config->set("sync_extruder", "dev_id", curr_dev_id);
|
||||
app_config->set("sync_extruder", "nozzle_config", current_nozzle_config);
|
||||
app_config->set("sync_extruder", "nozzle_option", current_nozzle_option);
|
||||
} else {
|
||||
std::map<std::string, std::string> data;
|
||||
data["dev_id"] = curr_dev_id;
|
||||
data["nozzle_config"] = current_nozzle_config;
|
||||
data["nozzle_option"] = current_nozzle_option;
|
||||
app_config->set_section("sync_extruder", data);
|
||||
}
|
||||
}
|
||||
|
||||
return nozzle_option;
|
||||
}
|
||||
|
||||
bool Sidebar::priv::sync_extruder_list(bool &only_external_material, bool is_manual)
|
||||
{
|
||||
MachineObject *obj = wxGetApp().getDeviceManager()->get_selected_machine();
|
||||
auto printer_name = plater->get_selected_printer_name_in_combox();
|
||||
@@ -1377,13 +1664,51 @@ bool Sidebar::priv::sync_extruder_list(bool &only_external_material)
|
||||
}
|
||||
assert(obj->GetExtderSystem()->GetTotalExtderCount() == extruder_nums);
|
||||
|
||||
// Multi-nozzle: resolve the nozzle option (pops MultiNozzleSyncDialog when is_manual). No-op for every
|
||||
// existing printer — support_multi_nozzle is false unless some extruder_max_nozzle_count entry is a real
|
||||
// value > 1 (nil-guarded, matching the manual/ToolOrdering gates), which no shipping single-nozzle or
|
||||
// dual-extruder profile sets. When the machine is multi-nozzle but no option resolves (e.g. user cancelled),
|
||||
// abort the sync.
|
||||
const ConfigOptionIntsNullable *extruder_max_nozzle_count = cur_preset.config.option<ConfigOptionIntsNullable>("extruder_max_nozzle_count");
|
||||
bool support_multi_nozzle = extruder_max_nozzle_count != nullptr &&
|
||||
std::any_of(extruder_max_nozzle_count->values.begin(), extruder_max_nozzle_count->values.end(),
|
||||
[](int val) { return val > 1 && val != ConfigOptionIntsNullable::nil_value(); });
|
||||
auto nozzle_option = get_nozzle_options(obj, extruder_nums, support_multi_nozzle, is_manual);
|
||||
if (!nozzle_option && support_multi_nozzle)
|
||||
return false;
|
||||
|
||||
std::vector<float> nozzle_diameters;
|
||||
nozzle_diameters.resize(extruder_nums);
|
||||
std::vector<NozzleVolumeType> target_types(extruder_nums, NozzleVolumeType::nvtStandard);
|
||||
for (size_t index = 0; index < extruder_nums; ++index) {
|
||||
int extruder_id = extruder_map[index];
|
||||
nozzle_diameters[extruder_id] = obj->GetExtderSystem()->GetNozzleDiameter(index);
|
||||
nozzle_diameters[extruder_id] = nozzle_option ? atof(nozzle_option->diameter.c_str()) : obj->GetExtderSystem()->GetNozzleDiameter(index);
|
||||
NozzleVolumeType target_type = NozzleVolumeType::nvtStandard;
|
||||
auto printer_tab = dynamic_cast<TabPrinter *>(wxGetApp().get_tab(Preset::TYPE_PRINTER));
|
||||
std::optional<NozzleVolumeType> select_type;
|
||||
if (nozzle_option && nozzle_option->extruder_nozzle_stats.count(index)) {
|
||||
const auto &stats = nozzle_option->extruder_nozzle_stats[index];
|
||||
if (stats.size() > 1) {
|
||||
// Orca: for a mixed extruder, keep Hybrid out of the (still-deferred) nozzle-centric slicing
|
||||
// pipeline, so collapse to the dominant concrete volume type (tie -> Standard).
|
||||
// TPU High Flow is shipped on 0.4/0.6 nozzles, so only fold it in there (mirrors the
|
||||
// High-Flow-skip-for-0.2 guard). Uses the device-reported diameter, same predicate as the preset path.
|
||||
int std_cnt = 0, hf_cnt = 0, tpu_hf_cnt = 0;
|
||||
for (const auto &kv : stats) {
|
||||
if (kv.first == nvtStandard) std_cnt = kv.second;
|
||||
else if (kv.first == nvtHighFlow) hf_cnt = kv.second;
|
||||
else if (kv.first == nvtTPUHighFlow) tpu_hf_cnt = kv.second;
|
||||
}
|
||||
if (!nozzle_diameter_supports_tpu_high_flow(nozzle_diameters[extruder_id]))
|
||||
tpu_hf_cnt = 0;
|
||||
select_type = nvtStandard;
|
||||
if (hf_cnt > std_cnt && hf_cnt >= tpu_hf_cnt)
|
||||
select_type = nvtHighFlow;
|
||||
else if (tpu_hf_cnt > std_cnt && tpu_hf_cnt > hf_cnt)
|
||||
select_type = nvtTPUHighFlow;
|
||||
} else {
|
||||
select_type = stats.begin()->first;
|
||||
}
|
||||
}
|
||||
if (obj->is_nozzle_flow_type_supported()) {
|
||||
if (obj->GetExtderSystem()->GetNozzleFlowType(index) == NozzleFlowType::NONE_FLOWTYPE) {
|
||||
MessageDialog dlg(this->plater, _L("There are unset nozzle types. Please set the nozzle types of all extruders before synchronizing."),
|
||||
@@ -1393,9 +1718,13 @@ bool Sidebar::priv::sync_extruder_list(bool &only_external_material)
|
||||
}
|
||||
// hack code, only use standard flow for 0.2
|
||||
if (std::fabs(nozzle_diameters[extruder_id] - 0.2) > EPSILON)
|
||||
target_type = NozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(extruder_id) - 1);
|
||||
// Map device flow->volume via the table, not `flowtype - 1`.
|
||||
// The arithmetic only aligns for S_FLOW/H_FLOW; U_FLOW(3)-1 would yield nvtHybrid(2), not nvtTPUHighFlow(3).
|
||||
target_type = DevNozzle::ToNozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(extruder_id));
|
||||
}
|
||||
printer_tab->set_extruder_volume_type(index, target_type);
|
||||
if (select_type)
|
||||
target_type = *select_type;
|
||||
target_types[index] = target_type;
|
||||
}
|
||||
|
||||
int deputy_4 = 0, main_4 = 0, deputy_1 = 0, main_1 = 0;
|
||||
@@ -1439,6 +1768,12 @@ bool Sidebar::priv::sync_extruder_list(bool &only_external_material)
|
||||
is_switching_diameter = false;
|
||||
}
|
||||
|
||||
// set nozzle volume type after switching prset, so this value can override the old value stored in conf
|
||||
auto printer_tab = dynamic_cast<TabPrinter *>(wxGetApp().get_tab(Preset::TYPE_PRINTER));
|
||||
for (size_t idx = 0; idx < target_types.size(); ++idx) {
|
||||
printer_tab->set_extruder_volume_type(idx, target_types[idx]);
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " finish sync_extruder_list";
|
||||
return true;
|
||||
}
|
||||
@@ -3745,15 +4080,13 @@ void Sidebar::sync_ams_list(bool is_from_big_sync_btn)
|
||||
{ // badge ams filament
|
||||
clear_combos_filament_badge();
|
||||
if (sync_result.direct_sync) {
|
||||
auto& ams_list = wxGetApp().preset_bundle->filament_ams_list;
|
||||
size_t tray_idx = 0;
|
||||
for (auto& entry : ams_list) {
|
||||
if (tray_idx >= p->combos_filament.size()) break;
|
||||
auto filament_id = entry.second.opt_string("filament_id", 0u);
|
||||
if (!filament_id.empty()) {
|
||||
badge_combox_filament(p->combos_filament[tray_idx]);
|
||||
}
|
||||
tray_idx++;
|
||||
// Orca: PresetBundle::sync_ams_list rebuilds combos_filament
|
||||
// 1:1 from the AMS trays that produce a combo (loaded trays + placeholders; non-placeholder
|
||||
// empty trays are skipped), so every resulting combo is AMS-sourced and gets a badge. The
|
||||
// previous per-tray index walked the full filament_ams_list (including the skipped empties),
|
||||
// so an empty slot before a loaded one dropped the badge for the trailing filaments.
|
||||
for (auto &c : p->combos_filament) {
|
||||
badge_combox_filament(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3948,7 +4281,8 @@ bool Sidebar::is_multifilament()
|
||||
void Sidebar::deal_btn_sync() {
|
||||
m_begin_sync_printer_status = true;
|
||||
bool only_external_material;
|
||||
auto ok = p->sync_extruder_list(only_external_material);
|
||||
// Manual "sync machine" button: is_manual=true so an H2C pops the MultiNozzleSyncDialog to pick a nozzle option.
|
||||
auto ok = p->sync_extruder_list(only_external_material, true);
|
||||
if (ok) {
|
||||
pop_sync_nozzle_and_ams_dialog();
|
||||
}
|
||||
@@ -5641,6 +5975,9 @@ std::map<std::string, std::string> Plater::get_bed_texture_maps()
|
||||
if (pm->bottom_texture_rect.size() > 0) {
|
||||
maps["bottom_texture_rect"] = pm->bottom_texture_rect;
|
||||
}
|
||||
if (pm->bottom_texture_rect_longer.size() > 0) {
|
||||
maps["bottom_texture_rect_longer"] = pm->bottom_texture_rect_longer;
|
||||
}
|
||||
if (pm->middle_texture_rect.size() > 0) {
|
||||
maps["middle_texture_rect"] = pm->middle_texture_rect;
|
||||
}
|
||||
@@ -11112,8 +11449,9 @@ bool Plater::priv::check_ams_status_impl(bool is_slice_all)
|
||||
auto nozzle_volumes_values = preset_bundle->project_config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type")->values;
|
||||
assert(obj->GetExtderSystem()->GetTotalExtderCount() == 2 && nozzle_volumes_values.size() == 2);
|
||||
if (obj->GetExtderSystem()->GetTotalExtderCount() == 2 && nozzle_volumes_values.size() == 2) {
|
||||
NozzleVolumeType right_nozzle_type = NozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(0) - 1);
|
||||
NozzleVolumeType left_nozzle_type = NozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(1) - 1);
|
||||
// Map device flow->volume via the table, not `flowtype - 1` (which mis-maps U_FLOW to nvtHybrid).
|
||||
NozzleVolumeType right_nozzle_type = DevNozzle::ToNozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(0));
|
||||
NozzleVolumeType left_nozzle_type = DevNozzle::ToNozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(1));
|
||||
NozzleVolumeType preset_left_type = NozzleVolumeType(nozzle_volumes_values[0]);
|
||||
NozzleVolumeType preset_right_type = NozzleVolumeType(nozzle_volumes_values[1]);
|
||||
is_same_as_printer = (left_nozzle_type == preset_left_type && right_nozzle_type == preset_right_type);
|
||||
@@ -17317,6 +17655,9 @@ void Plater::suppress_background_process(const bool stop_background_process)
|
||||
this->p->suppressed_backround_processing_update = true;
|
||||
}
|
||||
|
||||
// Expose the slicing process to the device GUI.
|
||||
BackgroundSlicingProcess& Plater::background_process() { return p->background_process; }
|
||||
|
||||
void Plater::center_selection() { p->center_selection(); }
|
||||
void Plater::drop_selection() { p->drop_selection(); }
|
||||
void Plater::mirror(Axis axis) { p->mirror(axis); }
|
||||
|
||||
@@ -50,6 +50,7 @@ class SLAPrint;
|
||||
//BBS: add partplatelist and SlicingStatusEvent
|
||||
class PartPlateList;
|
||||
class SlicingStatusEvent;
|
||||
class BackgroundSlicingProcess;
|
||||
enum SLAPrintObjectStep : unsigned int;
|
||||
enum class ConversionType : int;
|
||||
class DevAms;
|
||||
@@ -519,6 +520,9 @@ public:
|
||||
void schedule_background_process(bool schedule = true);
|
||||
bool is_background_process_update_scheduled() const;
|
||||
void suppress_background_process(const bool stop_background_process) ;
|
||||
// Expose the slicing process so the device GUI can read the current
|
||||
// GCodeProcessorResult (e.g. the nozzle grouping for print-dispatch mapping).
|
||||
BackgroundSlicingProcess& background_process();
|
||||
/* -1: send current gcode if not specified
|
||||
* -2: send all gcode to target machine */
|
||||
int send_gcode(int plate_idx = -1, Export3mfProgressFn proFn = nullptr);
|
||||
|
||||
@@ -36,6 +36,8 @@ std::string PrePrintChecker::get_print_status_info(PrintDialogStatus status)
|
||||
case PrintStatusNotSupportedPrintAll: return "PrintStatusNotSupportedPrintAll";
|
||||
case PrintStatusBlankPlate: return "PrintStatusBlankPlate";
|
||||
case PrintStatusUnsupportedPrinter: return "PrintStatusUnsupportedPrinter";
|
||||
case PrintStatusRackNozzleMappingWaiting: return "PrintStatusRackNozzleMappingWaiting";
|
||||
case PrintStatusRackNozzleMappingError: return "PrintStatusRackNozzleMappingError";
|
||||
case PrintStatusColorQuantityExceed: return "PrintStatusColorQuantityExceed";
|
||||
// Handle filament errors
|
||||
case PrintStatusAmsOnSettingup: return "PrintStatusAmsOnSettingup";
|
||||
@@ -47,6 +49,7 @@ std::string PrePrintChecker::get_print_status_info(PrintDialogStatus status)
|
||||
case PrintStatusTimelapseNoSdcard: return "PrintStatusTimelapseNoSdcard";
|
||||
case PrintStatusTimelapseWarning: return "PrintStatusTimelapseWarning";
|
||||
case PrintStatusMixAmsAndVtSlotWarning: return "PrintStatusMixAmsAndVtSlotWarning";
|
||||
case PrintStatusRackNozzleMappingWarning: return "PrintStatusRackNozzleMappingWarning";
|
||||
case PrintStatusWarningKvalueNotUsed: return "PrintStatusWarningKvalueNotUsed";
|
||||
case PrintStatusHasFilamentInBlackListWarning: return "PrintStatusHasFilamentInBlackListWarning";
|
||||
case PrintStatusFilamentWarningHighChamberTemp: return "PrintStatusFilamentWarningHighChamberTemp";
|
||||
|
||||
@@ -65,6 +65,8 @@ enum PrintDialogStatus : unsigned int {
|
||||
PrintStatusNotSupportedPrintAll,
|
||||
PrintStatusBlankPlate,
|
||||
PrintStatusUnsupportedPrinter,
|
||||
PrintStatusRackNozzleMappingWaiting,
|
||||
PrintStatusRackNozzleMappingError,
|
||||
PrintStatusPrinterErrorEnd,
|
||||
|
||||
// Errors for filament, Block Print
|
||||
@@ -89,6 +91,7 @@ enum PrintDialogStatus : unsigned int {
|
||||
PrintStatusTimelapseWarning,
|
||||
PrintStatusMixAmsAndVtSlotWarning,
|
||||
PrintStatusToolHeadCoolingFanWarning,
|
||||
PrintStatusRackNozzleMappingWarning,
|
||||
PrintStatusPrinterWarningEnd,
|
||||
|
||||
// Warnings for filament
|
||||
|
||||
@@ -1307,7 +1307,16 @@ void PlaterPresetComboBox::update()
|
||||
bool selected_in_ams = false;
|
||||
if (m_type == Preset::TYPE_FILAMENT) {
|
||||
set_replace_text("Bambu", "BambuStudioBlack");
|
||||
selected_in_ams = add_ams_filaments(into_u8(selected_user_preset.empty() ? selected_system_preset : selected_user_preset), true);
|
||||
// Orca: selected_system/user_preset hold the FULL preset name because Orca keys the maps above by
|
||||
// full name to avoid alias collisions (BBS keys by alias). add_ams_filaments() compares against
|
||||
// get_preset_name() which returns the alias, so resolve the selection back to its alias here.
|
||||
// Without this, e.g. "Bambu PLA Basic @BBL H2C" never equals the AMS tray alias "Bambu PLA Basic",
|
||||
// so the FROM_AMS flag is never set and update_sync_status() wipes the AMS sync check mark on the
|
||||
// filament cards for connected Bambu printers.
|
||||
wxString selected_full = selected_user_preset.empty() ? selected_system_preset : selected_user_preset;
|
||||
auto alias_it = preset_aliases.find(selected_full);
|
||||
wxString selected_alias = alias_it != preset_aliases.end() ? from_u8(alias_it->second) : selected_full;
|
||||
selected_in_ams = add_ams_filaments(into_u8(selected_alias), true);
|
||||
}
|
||||
|
||||
std::vector<std::string> filament_orders = {"Bambu PLA Basic", "Bambu PLA Matte", "Bambu PETG HF", "Bambu ABS", "Bambu PLA Silk", "Bambu PLA-CF",
|
||||
|
||||
@@ -1426,6 +1426,7 @@ wxString PrinterPartsDialog::GetString(NozzleFlowType nozzle_flow_type) const {
|
||||
switch (nozzle_flow_type) {
|
||||
case Slic3r::S_FLOW: return _L("Standard");
|
||||
case Slic3r::H_FLOW: return _L("High flow");
|
||||
case Slic3r::U_FLOW: return _L("TPU High flow");
|
||||
default: break;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,12 +20,16 @@
|
||||
|
||||
#include "DeviceCore/DevConfig.h"
|
||||
#include "DeviceCore/DevNozzleSystem.h"
|
||||
#include "DeviceCore/DevNozzleRack.h"
|
||||
#include "DeviceCore/DevExtensionTool.h"
|
||||
#include "DeviceCore/DevExtruderSystem.h"
|
||||
#include "DeviceCore/DevFilaBlackList.h"
|
||||
#include "DeviceCore/DevFilaSystem.h"
|
||||
#include "DeviceCore/DevFilaSwitch.h"
|
||||
#include "DeviceCore/DevManager.h"
|
||||
#include "DeviceCore/DevMapping.h"
|
||||
#include "DeviceCore/DevUtilBackend.h"
|
||||
#include "DeviceCore/DevMappingNozzle.h"
|
||||
#include "DeviceCore/DevStorage.h"
|
||||
|
||||
#include <wx/progdlg.h>
|
||||
@@ -62,12 +66,22 @@ std::string get_nozzle_volume_type_cloud_string(NozzleVolumeType nozzle_volume_t
|
||||
else if (nozzle_volume_type == NozzleVolumeType::nvtHighFlow) {
|
||||
return "high_flow";
|
||||
}
|
||||
else if (nozzle_volume_type == NozzleVolumeType::nvtTPUHighFlow) {
|
||||
return "tpu_high_flow";
|
||||
}
|
||||
else if (nozzle_volume_type == NozzleVolumeType::nvtHybrid) {
|
||||
// to be supported
|
||||
return "hybrid_flow";
|
||||
}
|
||||
else {
|
||||
assert(false);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
// Throttle so the rack print-dispatch nozzle-mapping request isn't re-sent on every status poll.
|
||||
static int s_nozzle_mapping_last_request_time = 0;
|
||||
|
||||
std::vector<wxString> SelectMachineDialog::MACHINE_BED_TYPE_STRING;
|
||||
std::vector<string> SelectMachineDialog::MachineBedTypeString;
|
||||
void SelectMachineDialog::init_machine_bed_types()
|
||||
@@ -558,7 +572,11 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater)
|
||||
m_checkbox_list["flow_cali"] = option_flow_dynamics_cali;
|
||||
m_checkbox_list["nozzle_offset_cali"] = option_nozzle_offset_cali_cali;
|
||||
for (auto print_opt : m_checkbox_list_order) {
|
||||
print_opt->Bind(EVT_SWITCH_PRINT_OPTION, [this](auto &e) { save_option_vals(); });
|
||||
print_opt->Bind(EVT_SWITCH_PRINT_OPTION, [this, print_opt](auto &e) {
|
||||
save_option_vals();
|
||||
// Flow calibration feeds the printer-side rack nozzle mapping; re-request it on change.
|
||||
if (print_opt == m_checkbox_list["flow_cali"]) on_flow_cali_option_changed();
|
||||
});
|
||||
}
|
||||
|
||||
option_auto_bed_level->Hide();
|
||||
@@ -932,13 +950,30 @@ void SelectMachineDialog::finish_mode()
|
||||
|
||||
void SelectMachineDialog::sync_ams_mapping_result(std::vector<FilamentInfo> &result)
|
||||
{
|
||||
// A rack / filament-switcher printer grows its filament cards to show the mapped-nozzle row.
|
||||
// Reflow the grid so the taller cards aren't clipped; no-op for printers that never show it.
|
||||
auto relayout_nozzle_cards = [this]() {
|
||||
DeviceManager* dev = wxGetApp().getDeviceManager();
|
||||
MachineObject* obj_ = dev ? dev->get_selected_machine() : nullptr;
|
||||
if (!obj_) return;
|
||||
DevNozzleSystem* ns = obj_->GetNozzleSystem();
|
||||
if (!obj_->GetFilaSwitch()->IsInstalled() && !(ns && ns->GetNozzleRack()->IsSupported())) return;
|
||||
if (m_sizer_ams_mapping_left) { m_sizer_ams_mapping_left->Layout(); m_filament_panel_left_sizer->Layout(); m_filament_left_panel->Layout(); }
|
||||
if (m_sizer_ams_mapping_right) { m_sizer_ams_mapping_right->Layout(); m_filament_panel_right_sizer->Layout(); m_filament_right_panel->Layout(); }
|
||||
if (m_sizer_ams_mapping) { m_sizer_ams_mapping->Layout(); m_filament_panel_sizer->Layout(); m_filament_panel->Layout(); }
|
||||
if (m_scroll_area) { m_scroll_area->Layout(); }
|
||||
Layout();
|
||||
};
|
||||
|
||||
if (result.empty()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "ams_mapping result is empty";
|
||||
for (auto it = m_materialList.begin(); it != m_materialList.end(); it++) {
|
||||
wxString ams_id = "Ext";//
|
||||
wxColour ams_col = wxColour(0xCE, 0xCE, 0xCE);
|
||||
it->second->item->set_ams_info(ams_col, ams_id);
|
||||
it->second->item->set_nozzle_info(get_mapped_nozzle_str(it->first));
|
||||
}
|
||||
relayout_nozzle_cards();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -975,11 +1010,13 @@ void SelectMachineDialog::sync_ams_mapping_result(std::vector<FilamentInfo> &res
|
||||
cols.push_back(DevAmsTray::decode_color(col));
|
||||
}
|
||||
m->set_ams_info(ams_col, ams_id,f->ctype, cols);
|
||||
m->set_nozzle_info(get_mapped_nozzle_str(id));
|
||||
break;
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
relayout_nozzle_cards();
|
||||
auto tab_index = (MainFrame::TabPosition) dynamic_cast<Notebook *>(wxGetApp().tab_panel())->GetSelection();
|
||||
if (tab_index == MainFrame::TabPosition::tp3DEditor || tab_index == MainFrame::TabPosition::tpPreview) {
|
||||
updata_thumbnail_data_after_connected_printer();
|
||||
@@ -1378,6 +1415,7 @@ bool SelectMachineDialog::is_nozzle_type_match(DevExtderSystem data, wxString& e
|
||||
if (nozzle_volume_type_opt) {
|
||||
NozzleVolumeType nozzle_volume_type = (NozzleVolumeType) (nozzle_volume_type_opt->get_at(used_extruders[i]));
|
||||
if (nozzle_volume_type == NozzleVolumeType::nvtStandard) { used_extruders_flow[used_extruders[i]] = "Standard";}
|
||||
else if (nozzle_volume_type == NozzleVolumeType::nvtTPUHighFlow) { used_extruders_flow[used_extruders[i]] = "TPU High Flow";}
|
||||
else {used_extruders_flow[used_extruders[i]] = "High Flow";}
|
||||
}
|
||||
}
|
||||
@@ -1397,6 +1435,10 @@ bool SelectMachineDialog::is_nozzle_type_match(DevExtderSystem data, wxString& e
|
||||
{
|
||||
flow_type_of_machine.push_back(L("Standard"));
|
||||
}
|
||||
else if (it.GetNozzleFlowType() == NozzleFlowType::U_FLOW)
|
||||
{
|
||||
flow_type_of_machine.push_back(L("TPU High Flow"));
|
||||
}
|
||||
}
|
||||
|
||||
//Only when all preset nozzle types and machine nozzle types are exactly the same, return true.
|
||||
@@ -1443,6 +1485,279 @@ int SelectMachineDialog::convert_filament_map_nozzle_id_to_task_nozzle_id(int no
|
||||
}
|
||||
}
|
||||
|
||||
// Physical nozzle(s) a mapped filament prints on. key: nozzle pos id, value: nozzle.
|
||||
// (fila_id is the filament index, i.e. FilamentInfo::id.) Non-rack printers resolve from the
|
||||
// filament->extruder map; a rack printer (H2C) resolves from the print-dispatch mapping the printer
|
||||
// returned. Empty when no mapping is available yet — the per-nozzle blacklist check then skips it.
|
||||
std::map<int, DevNozzle> SelectMachineDialog::get_mapped_nozzles(int fila_id) const
|
||||
{
|
||||
std::map<int, DevNozzle> nozzle_map;
|
||||
|
||||
DeviceManager* dev = wxGetApp().getDeviceManager();
|
||||
if (!dev) { return nozzle_map; }
|
||||
MachineObject* obj_ = dev->get_selected_machine();
|
||||
if (!obj_) { return nozzle_map; }
|
||||
|
||||
int total_ext_count = 0;
|
||||
if (m_print_type == FROM_NORMAL) {
|
||||
const auto& full_config = wxGetApp().preset_bundle->full_config();
|
||||
const auto opt = full_config.option<ConfigOptionFloats>("nozzle_diameter");
|
||||
total_ext_count = opt ? opt->values.size() : 0;
|
||||
} else {
|
||||
const auto opt = m_required_data_config.option<ConfigOptionFloats>("nozzle_diameter");
|
||||
total_ext_count = opt ? opt->values.size() : 0;
|
||||
}
|
||||
|
||||
if (total_ext_count != obj_->GetExtderSystem()->GetTotalExtderCount()) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": total_ext_count not match";
|
||||
return nozzle_map;
|
||||
}
|
||||
|
||||
DevNozzleSystem* nozzle_system = obj_->GetNozzleSystem();
|
||||
if (!nozzle_system) { return nozzle_map; }
|
||||
|
||||
if (!nozzle_system->GetNozzleRack()->IsSupported()) {
|
||||
if (total_ext_count == 1) {
|
||||
nozzle_map[MAIN_EXTRUDER_ID] = nozzle_system->GetNozzleByPosId(MAIN_EXTRUDER_ID);
|
||||
} else if (total_ext_count == 2) {
|
||||
if (fila_id >= 0 && (size_t)fila_id < m_filaments_map.size()) {
|
||||
if (m_filaments_map[fila_id] == 1) {
|
||||
nozzle_map[DEPUTY_EXTRUDER_ID] = nozzle_system->GetNozzleByPosId(DEPUTY_EXTRUDER_ID);
|
||||
} else if (m_filaments_map[fila_id] == 2) {
|
||||
nozzle_map[MAIN_EXTRUDER_ID] = nozzle_system->GetNozzleByPosId(MAIN_EXTRUDER_ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (m_print_type == FROM_NORMAL) {
|
||||
// Rack printer (H2C): resolve the physical rack nozzle(s) the print-dispatch mapping assigned
|
||||
// to this filament. Empty until the printer returns the auto-mapping result.
|
||||
for (int nozzle_pos : obj_->get_nozzle_mapping_result()->GetMappedNozzlePosVecByFilaId(fila_id))
|
||||
nozzle_map[nozzle_pos] = nozzle_system->GetNozzleByPosId(nozzle_pos);
|
||||
}
|
||||
|
||||
return nozzle_map;
|
||||
}
|
||||
|
||||
wxString SelectMachineDialog::get_mapped_nozzle_str(int fila_id) const
|
||||
{
|
||||
if (m_print_type != FROM_NORMAL)
|
||||
return wxEmptyString; // no slicing data when printing from sdcard
|
||||
|
||||
DeviceManager* dev = wxGetApp().getDeviceManager();
|
||||
if (!dev) { return wxEmptyString; }
|
||||
MachineObject* obj_ = dev->get_selected_machine();
|
||||
if (!obj_) { return wxEmptyString; }
|
||||
|
||||
DevNozzleSystem* nozzle_system = obj_->GetNozzleSystem();
|
||||
const bool rack_supported = nozzle_system && nozzle_system->GetNozzleRack()->IsSupported();
|
||||
const bool fila_switch_installed = obj_->GetFilaSwitch()->IsInstalled();
|
||||
|
||||
// A dynamic nozzle map routes filaments through the filament switcher; without one it can't map.
|
||||
if (use_dynamic_nozzle_map() && !fila_switch_installed)
|
||||
return "?";
|
||||
|
||||
if (fila_switch_installed || rack_supported) {
|
||||
if (rack_supported) {
|
||||
return obj_->get_nozzle_mapping_result()->GetMappedNozzlePosStrByFilaId(fila_id);
|
||||
} else {
|
||||
const auto& nozzle_map = get_mapped_nozzles(fila_id);
|
||||
if (nozzle_map.count(MAIN_EXTRUDER_ID) != 0 && nozzle_map.count(DEPUTY_EXTRUDER_ID) != 0)
|
||||
return "L R";
|
||||
else if (nozzle_map.count(MAIN_EXTRUDER_ID) != 0)
|
||||
return "R";
|
||||
else if (nozzle_map.count(DEPUTY_EXTRUDER_ID) != 0)
|
||||
return "L";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
return wxEmptyString;
|
||||
}
|
||||
|
||||
bool SelectMachineDialog::use_dynamic_nozzle_map() const
|
||||
{
|
||||
if (m_print_type == FROM_NORMAL) {
|
||||
auto nozzle_group_res = DevUtilBackend::GetNozzleGroupResult(m_plater);
|
||||
if (nozzle_group_res && nozzle_group_res->is_support_dynamic_nozzle_map())
|
||||
return true;
|
||||
} else if (m_print_type == FROM_SDCARD_VIEW) {
|
||||
if (m_required_data_plate_data_list.size() > (size_t) m_print_plate_idx) {
|
||||
auto dynamic_nozzle_map = m_required_data_plate_data_list[m_print_plate_idx]->config.option<ConfigOptionBool>("enable_filament_dynamic_map");
|
||||
if (dynamic_nozzle_map)
|
||||
return dynamic_nozzle_map->value;
|
||||
}
|
||||
auto dynamic_nozzle_map = m_required_data_config.option<ConfigOptionBool>("enable_filament_dynamic_map");
|
||||
if (dynamic_nozzle_map)
|
||||
return dynamic_nozzle_map->value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SelectMachineDialog::clear_nozzle_mapping()
|
||||
{
|
||||
m_nozzle_mapping_result.clear();
|
||||
// Orca: no BBS get_current_machine(); use the selected device (same accessor get_mapped_nozzles uses).
|
||||
DeviceManager* dev = wxGetApp().getDeviceManager();
|
||||
if (MachineObject* obj_ = dev ? dev->get_selected_machine() : nullptr)
|
||||
obj_->get_nozzle_mapping_result()->Clear();
|
||||
}
|
||||
|
||||
void SelectMachineDialog::on_flow_cali_option_changed()
|
||||
{
|
||||
// Flow calibration feeds the printer-side rack nozzle-mapping computation, so a change must
|
||||
// invalidate the cached mapping and let the next status poll re-request it (V0 path).
|
||||
DeviceManager* dev = wxGetApp().getDeviceManager();
|
||||
MachineObject* obj_ = dev ? dev->get_selected_machine() : nullptr;
|
||||
if (!obj_ || !(obj_->GetNozzleSystem() && obj_->GetNozzleSystem()->GetNozzleRack()->IsSupported())) return;
|
||||
if (use_dynamic_nozzle_map()) return;
|
||||
// Orca: BBS also pops a confirmation dialog and re-fires immediately (and handles a PA-value
|
||||
// switch Orca lacks); here we just clear + unthrottle so the next poll re-requests promptly.
|
||||
clear_nozzle_mapping();
|
||||
s_nozzle_mapping_last_request_time = 0;
|
||||
}
|
||||
|
||||
// Rack print-dispatch nozzle mapping, dynamic-map (V1) path. Fires get_auto_nozzle_mapping when no
|
||||
// result is cached, blocks Send while the printer computes, then surfaces error/warning. Returns
|
||||
// false (pre-print check fails / keep waiting) only while blocking; true when the chain may proceed.
|
||||
bool SelectMachineDialog::CheckErrorSyncNozzleMappingResultV1(MachineObject* obj_)
|
||||
{
|
||||
if (m_print_type != FROM_NORMAL)
|
||||
return true; // there is no slicing data when printing from sdcard
|
||||
|
||||
if (!obj_)
|
||||
return true;
|
||||
|
||||
if (!(obj_->GetNozzleSystem() && obj_->GetNozzleSystem()->GetNozzleRack()->IsSupported()))
|
||||
return true; // no need to check if the printer has no nozzle rack
|
||||
|
||||
if (!use_dynamic_nozzle_map())
|
||||
return true;
|
||||
|
||||
const auto& obj_nozzle_mapping_ptr = obj_->get_nozzle_mapping_result();
|
||||
if (!obj_nozzle_mapping_ptr->HasResult()) {
|
||||
if (time(nullptr) - s_nozzle_mapping_last_request_time > 10) { // avoid too many requests
|
||||
int rtn = obj_nozzle_mapping_ptr->CtrlGetAutoNozzleMappingV1(m_plater);
|
||||
if (rtn == 0) {
|
||||
s_nozzle_mapping_last_request_time = time(nullptr);
|
||||
} else {
|
||||
const auto& err_msg = wxString::Format(_L("Failed to send nozzle auto-mapping request to printer { code: %d }. "
|
||||
"Please try to refresh the printer information. "
|
||||
"If it still does not recover, you can try to rebind the printer and check the network connection."), rtn);
|
||||
show_status(PrintDialogStatus::PrintStatusRackNozzleMappingWaiting, {err_msg});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
show_status(PrintDialogStatus::PrintStatusRackNozzleMappingWaiting, {_L("The printer is calculating nozzle mapping.") + " " + _L("Please wait a moment...")});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj_nozzle_mapping_ptr->GetResultStr() == "failed") {
|
||||
const auto& err_msg = wxString::Format(_L("Failed to receive nozzle auto-mapping table from printer { msg: %s }. Please refresh the printer information."), obj_nozzle_mapping_ptr->GetMqttReason());
|
||||
show_status(PrintDialogStatus::PrintStatusRackNozzleMappingError, {err_msg});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj_nozzle_mapping_ptr->GetResultStr() == "fail") {
|
||||
const wxString& err_msg = wxString::Format(_L("The printer failed to build the nozzle auto-mapping table { code: %d }. Please refresh nozzle information."), obj_nozzle_mapping_ptr->GetErrno());
|
||||
show_status(PrintDialogStatus::PrintStatusRackNozzleMappingError, {err_msg});
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& mapping_map = obj_->get_nozzle_mapping_result()->GetNozzleMappingMap();
|
||||
if (!mapping_map.empty()) {
|
||||
if (m_nozzle_mapping_result != mapping_map) {
|
||||
m_nozzle_mapping_result = mapping_map;
|
||||
sync_ams_mapping_result(m_ams_mapping_result);
|
||||
}
|
||||
|
||||
float flush_waste_base = obj_->get_nozzle_mapping_result()->GetFlushWeightBase();
|
||||
float flush_waste_current = obj_->get_nozzle_mapping_result()->GetFlushWeightCurrent();
|
||||
if ((flush_waste_base != -1) && (flush_waste_current != -1) && flush_waste_current > flush_waste_base) {
|
||||
const wxString& warning_msg = wxString::Format(_L("The current nozzle mapping may produce an extra %0.2f g of waste."), flush_waste_current - flush_waste_base);
|
||||
show_status(PrintDialogStatus::PrintStatusRackNozzleMappingWarning, {warning_msg});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const wxString& err_msg = wxString::Format(_L("Failed to receive nozzle auto-mapping table from printer { msg: %s }. Please refresh the printer information."), "empty table");
|
||||
show_status(PrintDialogStatus::PrintStatusRackNozzleMappingError, {err_msg});
|
||||
return true;
|
||||
}
|
||||
|
||||
// Rack print-dispatch nozzle mapping, static-rack (V0) path — see CheckErrorSyncNozzleMappingResultV1.
|
||||
bool SelectMachineDialog::CheckErrorSyncNozzleMappingResultV0(MachineObject* obj_)
|
||||
{
|
||||
if (!obj_)
|
||||
return true;
|
||||
|
||||
if (!(obj_->GetNozzleSystem() && obj_->GetNozzleSystem()->GetNozzleRack()->IsSupported()))
|
||||
return true; // no need to check if the printer has no nozzle rack
|
||||
|
||||
if (m_print_type != FROM_NORMAL)
|
||||
return true; // there is no slicing data when printing from sdcard
|
||||
|
||||
if (use_dynamic_nozzle_map())
|
||||
return true; // handled by the V1 path
|
||||
|
||||
auto nozzle_group_res = DevUtilBackend::GetNozzleGroupResult(m_plater);
|
||||
if (nozzle_group_res && nozzle_group_res->get_used_nozzles_in_extruder(LOGIC_R_EXTRUDER_ID).empty())
|
||||
return true; // no right-extruder nozzles used in slicing -> nothing to map
|
||||
|
||||
const auto& obj_nozzle_mapping_ptr = obj_->get_nozzle_mapping_result();
|
||||
if (!obj_nozzle_mapping_ptr->HasResult()) {
|
||||
if (time(nullptr) - s_nozzle_mapping_last_request_time > 10) { // avoid too many requests
|
||||
// Orca: BBS passes m_pa_value_switch->GetValue() ? 0 : 1; Orca has no PA-value switch UI,
|
||||
// so use the switch-off default (1).
|
||||
int rtn = obj_nozzle_mapping_ptr->CtrlGetAutoNozzleMappingV0(m_plater, m_ams_mapping_result, m_checkbox_list["flow_cali"]->getValueInt(), 1);
|
||||
if (rtn == 0) {
|
||||
s_nozzle_mapping_last_request_time = time(nullptr);
|
||||
} else {
|
||||
const auto& err_msg = wxString::Format(_L("Failed to send nozzle auto-mapping request to printer { code: %d }. "
|
||||
"Please try to refresh the printer information. "
|
||||
"If it still does not recover, you can try to rebind the printer and check the network connection."), rtn);
|
||||
show_status(PrintDialogStatus::PrintStatusRackNozzleMappingWaiting, {err_msg});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
show_status(PrintDialogStatus::PrintStatusRackNozzleMappingWaiting, {_L("The printer is calculating nozzle mapping.") + " " + _L("Please wait a moment...")});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj_nozzle_mapping_ptr->GetResultStr() == "failed") {
|
||||
const auto& err_msg = wxString::Format(_L("Failed to receive nozzle auto-mapping table from printer { msg: %s }. Please refresh the printer information."), obj_nozzle_mapping_ptr->GetMqttReason());
|
||||
show_status(PrintDialogStatus::PrintStatusRackNozzleMappingError, {err_msg});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj_nozzle_mapping_ptr->GetResultStr() == "fail") {
|
||||
const wxString& err_msg = wxString::Format(_L("The printer failed to build the nozzle auto-mapping table { code: %d }. Please refresh nozzle information."), obj_nozzle_mapping_ptr->GetErrno());
|
||||
show_status(PrintDialogStatus::PrintStatusRackNozzleMappingError, {err_msg});
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& mapping_map = obj_->get_nozzle_mapping_result()->GetNozzleMappingMap();
|
||||
if (!mapping_map.empty()) {
|
||||
if (m_nozzle_mapping_result != mapping_map) {
|
||||
m_nozzle_mapping_result = mapping_map;
|
||||
sync_ams_mapping_result(m_ams_mapping_result);
|
||||
}
|
||||
|
||||
float flush_waste_base = obj_->get_nozzle_mapping_result()->GetFlushWeightBase();
|
||||
float flush_waste_current = obj_->get_nozzle_mapping_result()->GetFlushWeightCurrent();
|
||||
if ((flush_waste_base != -1) && (flush_waste_current != -1) && flush_waste_current > flush_waste_base) {
|
||||
const wxString& warning_msg = wxString::Format(_L("The current nozzle mapping may produce an extra %0.2f g of waste."), flush_waste_current - flush_waste_base);
|
||||
show_status(PrintDialogStatus::PrintStatusRackNozzleMappingWarning, {warning_msg});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const wxString& err_msg = wxString::Format(_L("Failed to receive nozzle auto-mapping table from printer { msg: %s }. Please refresh the printer information."), "empty table");
|
||||
show_status(PrintDialogStatus::PrintStatusRackNozzleMappingError, {err_msg});
|
||||
return true;
|
||||
}
|
||||
|
||||
void SelectMachineDialog::prepare(int print_plate_idx)
|
||||
{
|
||||
m_print_plate_idx = print_plate_idx;
|
||||
@@ -1684,6 +1999,17 @@ void SelectMachineDialog::show_status(PrintDialogStatus status, std::vector<wxSt
|
||||
} else if (status == PrintStatusWarningExtFilamentNotMatch) {
|
||||
Enable_Refresh_Button(true);
|
||||
Enable_Send_Button(true);
|
||||
} else if (status == PrintDialogStatus::PrintStatusRackNozzleMappingWaiting) {
|
||||
// Printer is computing the rack nozzle mapping: block Send until it replies.
|
||||
Enable_Refresh_Button(true);
|
||||
Enable_Send_Button(false);
|
||||
} else if (status == PrintDialogStatus::PrintStatusRackNozzleMappingError) {
|
||||
Enable_Refresh_Button(true);
|
||||
Enable_Send_Button(false);
|
||||
} else if (status == PrintDialogStatus::PrintStatusRackNozzleMappingWarning) {
|
||||
// Extra-waste warning: allow Send.
|
||||
Enable_Refresh_Button(true);
|
||||
Enable_Send_Button(true);
|
||||
}
|
||||
|
||||
/*enter perpare mode*/
|
||||
@@ -1983,15 +2309,48 @@ void SelectMachineDialog::on_ok_btn(wxCommandEvent &event)
|
||||
}
|
||||
}
|
||||
|
||||
bool in_blacklist = false;
|
||||
std::string action;
|
||||
wxString info;
|
||||
wxString wiki_url;
|
||||
DevFilaBlacklist::check_filaments_in_blacklist_url(obj_->printer_type, filament_brand, filament_type, m_ams_mapping_result[i].filament_id, ams_id, slot_id, "", in_blacklist,
|
||||
action, info, wiki_url);
|
||||
if (in_blacklist && action == "warning") {
|
||||
confirm_text.push_back(ConfirmBeforeSendInfo(info, wiki_url));
|
||||
has_slice_warnings = true;
|
||||
DevFilaBlacklist::CheckFilamentInfo check_info;
|
||||
check_info.dev_id = obj_->get_dev_id();
|
||||
check_info.model_id = obj_->printer_type;
|
||||
check_info.fila_id = m_ams_mapping_result[i].filament_id;
|
||||
check_info.fila_type = filament_type;
|
||||
check_info.fila_vendor = filament_brand;
|
||||
// Populate fila_name so the high-flow warning strings (blacklist rules 14/15/18/19,
|
||||
// "%s has a risk of nozzle clogging ...") render with the filament name instead of a
|
||||
// leading blank. Resolves the same preset the engine's internal AMS-name recovery uses, so
|
||||
// blacklist name-matching is unchanged.
|
||||
if (auto option = wxGetApp().preset_bundle->get_filament_by_filament_id(check_info.fila_id))
|
||||
check_info.fila_name = option->filament_name;
|
||||
check_info.ams_id = ams_id;
|
||||
check_info.slot_id = slot_id;
|
||||
check_info.has_filament_switch = obj_->GetFilaSwitch()->IsInstalled();
|
||||
|
||||
std::vector<DevNozzle> mapped_nozzles;
|
||||
for (const auto &[pos_id, nozzle] : get_mapped_nozzles(m_ams_mapping_result[i].id)) {
|
||||
if (!nozzle.IsEmpty()) { mapped_nozzles.push_back(nozzle); }
|
||||
}
|
||||
|
||||
// Evaluate the blacklist once per resolved physical nozzle so the high-flow
|
||||
// nozzle_flows/nozzle_diameters rules match against the real nozzle. If no nozzle context is
|
||||
// resolvable (a non-rack printer, or a rack filament not yet mapped by the print-dispatch
|
||||
// mapping), evaluate once with the nozzle fields unset so the non-nozzle rules still fire as
|
||||
// before (empty nozzle_flow never matches a nozzle_flows rule).
|
||||
const size_t iterations = mapped_nozzles.empty() ? 1 : mapped_nozzles.size();
|
||||
for (size_t n = 0; n < iterations; ++n) {
|
||||
if (n < mapped_nozzles.size()) {
|
||||
const DevNozzle &nozzle = mapped_nozzles[n];
|
||||
check_info.extruder_id = nozzle.GetExtruderId();
|
||||
check_info.nozzle_flow = DevNozzle::GetNozzleFlowTypeString(nozzle.GetNozzleFlowType());
|
||||
check_info.nozzle_diameter = nozzle.GetNozzleDiameter();
|
||||
}
|
||||
|
||||
const auto &result = DevFilaBlacklist::check_filaments_in_blacklist(check_info);
|
||||
if (const auto &warning_items = result.get_items_by_action("warning"); !warning_items.empty()) {
|
||||
for (const auto &item : warning_items) {
|
||||
confirm_text.push_back(ConfirmBeforeSendInfo(item.info_msg, item.wiki_url));
|
||||
has_slice_warnings = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2514,6 +2873,12 @@ void SelectMachineDialog::on_send_print()
|
||||
m_print_job->task_ams_mapping = ams_mapping_array;
|
||||
m_print_job->task_ams_mapping2 = ams_mapping_array2;
|
||||
m_print_job->task_ams_mapping_info = ams_mapping_info;
|
||||
// Print-dispatch nozzle mapping (H2C hotend rack): attach ONLY when a mapping result exists.
|
||||
// For every non-rack printer (X1/P1/A1/H2S/H2D) the mapping json is empty, so task_nozzle_mapping
|
||||
// stays absent and the print payload is unchanged.
|
||||
if (!obj_->get_nozzle_mapping_result()->GetNozzleMappingJson().empty()) {
|
||||
m_print_job->task_nozzle_mapping = obj_->get_nozzle_mapping_result()->GetNozzleMappingJson().dump();
|
||||
}
|
||||
|
||||
/* build nozzles info for multi extruders printers */
|
||||
if (build_nozzles_info(m_print_job->task_nozzles_info)) {
|
||||
@@ -2689,6 +3054,7 @@ void SelectMachineDialog::on_set_finish_mapping(wxCommandEvent &evt)
|
||||
if (item->id == m_current_filament_id) {
|
||||
auto ams_colour = wxColour(wxAtoi(selection_data_arr[0]), wxAtoi(selection_data_arr[1]), wxAtoi(selection_data_arr[2]), wxAtoi(selection_data_arr[3]));
|
||||
m->set_ams_info(ams_colour, selection_data_arr[4], ctype, material_cols);
|
||||
m->set_nozzle_info(get_mapped_nozzle_str(item->id));
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
@@ -2993,9 +3359,11 @@ void SelectMachineDialog::on_selection_changed(wxCommandEvent &event)
|
||||
/* reset timeout and reading printer info */
|
||||
m_status_bar->reset();
|
||||
m_timeout_count = 0;
|
||||
s_nozzle_mapping_last_request_time = 0;
|
||||
m_ams_mapping_res = false;
|
||||
m_ams_mapping_valid = false;
|
||||
m_ams_mapping_result.clear();
|
||||
clear_nozzle_mapping();
|
||||
m_pre_print_checker.clear();
|
||||
|
||||
m_link_edit_nozzle->Show(false);
|
||||
@@ -3102,7 +3470,9 @@ void SelectMachineDialog::update_filament_change_count()
|
||||
auto best = stats.stats_by_multi_extruder_best;
|
||||
auto curr = stats.stats_by_multi_extruder_curr;
|
||||
|
||||
int hand_changes_count = curr.filament_change_count - best.filament_change_count;
|
||||
// Per-nozzle flush_filament_change_count. Equals the per-extruder filament_change_count for
|
||||
// single-nozzle-per-extruder printers, so this suggestion's shown value is unchanged.
|
||||
int hand_changes_count = curr.flush_filament_change_count - best.flush_filament_change_count;
|
||||
int saving_weight = curr.filament_flush_weight - best.filament_flush_weight;
|
||||
|
||||
if (obj->GetExtderSystem()->GetTotalExtderCount() > 1) { m_link_edit_nozzle->Show(true); }
|
||||
@@ -3425,6 +3795,10 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_)
|
||||
}
|
||||
}
|
||||
|
||||
// Rack print-dispatch nozzle mapping (dynamic V1): request/await the printer's mapping and block
|
||||
// Send while it computes. No-op for non-rack printers.
|
||||
if (!CheckErrorSyncNozzleMappingResultV1(obj_)) return;
|
||||
|
||||
if (!DevPrinterConfigUtil::support_ams_ext_mix_print(obj_->printer_type)) {
|
||||
bool useAms = _HasAms(m_ams_mapping_result);
|
||||
bool useExt = _HasExt(m_ams_mapping_result);
|
||||
@@ -3444,6 +3818,10 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_)
|
||||
return;
|
||||
}
|
||||
|
||||
// Rack print-dispatch nozzle mapping (static rack V0): consumes the validated AMS mapping, so it
|
||||
// runs after the AMS-validity check and before the per-nozzle blacklist loop (which needs the result).
|
||||
if (!CheckErrorSyncNozzleMappingResultV0(obj_)) return;
|
||||
|
||||
// filaments check for black list
|
||||
for (auto i = 0; i < m_ams_mapping_result.size(); i++) {
|
||||
const auto &ams_id = m_ams_mapping_result[i].get_ams_id();
|
||||
@@ -3458,22 +3836,53 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_)
|
||||
if (fs.id == m_ams_mapping_result[i].id) { filament_brand = m_filaments[i].brand; }
|
||||
}
|
||||
|
||||
bool in_blacklist = false;
|
||||
std::string action;
|
||||
wxString info;
|
||||
wxString wiki_url;
|
||||
DevFilaBlacklist::check_filaments_in_blacklist_url(obj_->printer_type, filament_brand, filament_type, m_ams_mapping_result[i].filament_id, ams_id, slot_id, "", in_blacklist,
|
||||
action, info, wiki_url);
|
||||
if (in_blacklist) {
|
||||
DevFilaBlacklist::CheckFilamentInfo check_info;
|
||||
check_info.dev_id = obj_->get_dev_id();
|
||||
check_info.model_id = obj_->printer_type;
|
||||
check_info.fila_id = m_ams_mapping_result[i].filament_id;
|
||||
check_info.fila_type = filament_type;
|
||||
check_info.fila_vendor = filament_brand;
|
||||
// Populate fila_name so the high-flow warning strings (blacklist rules 14/15/18/19,
|
||||
// "%s has a risk of nozzle clogging ...") render with the filament name instead of a
|
||||
// leading blank. Resolves the same preset the engine's internal AMS-name recovery uses, so
|
||||
// blacklist name-matching is unchanged.
|
||||
if (auto option = wxGetApp().preset_bundle->get_filament_by_filament_id(check_info.fila_id))
|
||||
check_info.fila_name = option->filament_name;
|
||||
check_info.ams_id = ams_id;
|
||||
check_info.slot_id = slot_id;
|
||||
check_info.has_filament_switch = obj_->GetFilaSwitch()->IsInstalled();
|
||||
|
||||
std::vector<wxString> error_msg { info };
|
||||
if (action == "prohibition") {
|
||||
show_status(PrintDialogStatus::PrintStatusHasFilamentInBlackListError, error_msg, wiki_url);
|
||||
std::vector<DevNozzle> mapped_nozzles;
|
||||
for (const auto &[pos_id, nozzle] : get_mapped_nozzles(m_ams_mapping_result[i].id)) {
|
||||
if (!nozzle.IsEmpty()) { mapped_nozzles.push_back(nozzle); }
|
||||
}
|
||||
|
||||
// Per-physical-nozzle blacklist check — thread the nozzle the filament prints on so the
|
||||
// high-flow prohibition/warning rules evaluate against the real nozzle flow/diameter.
|
||||
// With no resolvable nozzle context (a non-rack printer, or a rack filament not yet mapped
|
||||
// by the print-dispatch mapping), evaluate once with the nozzle fields unset so the
|
||||
// non-nozzle prohibitions/warnings still fire as before.
|
||||
const size_t iterations = mapped_nozzles.empty() ? 1 : mapped_nozzles.size();
|
||||
for (size_t n = 0; n < iterations; ++n) {
|
||||
if (n < mapped_nozzles.size()) {
|
||||
const DevNozzle &nozzle = mapped_nozzles[n];
|
||||
check_info.extruder_id = nozzle.GetExtruderId();
|
||||
check_info.nozzle_flow = DevNozzle::GetNozzleFlowTypeString(nozzle.GetNozzleFlowType());
|
||||
check_info.nozzle_diameter = nozzle.GetNozzleDiameter();
|
||||
}
|
||||
|
||||
const auto &result = DevFilaBlacklist::check_filaments_in_blacklist(check_info);
|
||||
if (const auto &prohibition_items = result.get_items_by_action("prohibition"); !prohibition_items.empty()) {
|
||||
for (const auto &item : prohibition_items) {
|
||||
show_status(PrintDialogStatus::PrintStatusHasFilamentInBlackListError, {item.info_msg}, item.wiki_url);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (action == "warning") {
|
||||
show_status(PrintDialogStatus::PrintStatusHasFilamentInBlackListWarning, error_msg, wiki_url);/** warning check **/
|
||||
// return;
|
||||
|
||||
if (const auto &warning_items = result.get_items_by_action("warning"); !warning_items.empty()) {
|
||||
for (const auto &item : warning_items) {
|
||||
show_status(PrintDialogStatus::PrintStatusHasFilamentInBlackListWarning, {item.info_msg}, item.wiki_url);/** warning check **/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3664,6 +4073,7 @@ void SelectMachineDialog::reset_ams_material()
|
||||
wxString ams_id = "-";
|
||||
wxColour ams_col = wxColour(0xEE, 0xEE, 0xEE);
|
||||
m->set_ams_info(ams_col, ams_id);
|
||||
m->set_nozzle_info(get_mapped_nozzle_str(id));
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
@@ -3968,7 +4378,7 @@ void SelectMachineDialog::reset_and_sync_ams_list()
|
||||
m_mapping_popup.set_current_filament_id(extruder);
|
||||
m_mapping_popup.set_tag_texture(materials[extruder]);
|
||||
m_mapping_popup.set_send_win(this);//fix bug:fisrt click is not valid
|
||||
m_mapping_popup.update(obj_, m_ams_mapping_result);
|
||||
m_mapping_popup.update(obj_, m_ams_mapping_result, use_dynamic_nozzle_map(), m_print_type);
|
||||
m_mapping_popup.Popup();
|
||||
}
|
||||
}
|
||||
@@ -4471,7 +4881,7 @@ void SelectMachineDialog::set_default_from_sdcard()
|
||||
m_mapping_popup.set_current_filament_id(fo.id);
|
||||
m_mapping_popup.set_tag_texture(fo.type);
|
||||
m_mapping_popup.set_send_win(this);
|
||||
m_mapping_popup.update(obj_, m_ams_mapping_result);
|
||||
m_mapping_popup.update(obj_, m_ams_mapping_result, use_dynamic_nozzle_map(), m_print_type);
|
||||
m_mapping_popup.Popup();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "DeviceManager.hpp"
|
||||
#include "DeviceCore/DevNozzleSystem.h" // DevNozzle (get_mapped_nozzles return type)
|
||||
#include "Plater.hpp"
|
||||
#include "BBLStatusBar.hpp"
|
||||
#include "BBLStatusBarPrint.hpp"
|
||||
@@ -61,10 +62,8 @@ namespace Slic3r { namespace GUI {
|
||||
|
||||
std::string get_nozzle_volume_type_cloud_string(NozzleVolumeType nozzle_volume_type);
|
||||
void print_ams_mapping_result(std::vector<FilamentInfo> &result);
|
||||
enum PrintFromType {
|
||||
FROM_NORMAL,
|
||||
FROM_SDCARD_VIEW,
|
||||
};
|
||||
// enum PrintFromType moved to DeviceCore/DevDefs.h so shared device-mapping GUI
|
||||
// headers can name it without an include cycle. SelectMachine.hpp still sees it via DeviceManager.hpp.
|
||||
|
||||
enum PrintPageMode {
|
||||
PrintPageModePrepare = 0,
|
||||
@@ -324,6 +323,7 @@ private:
|
||||
std::vector<MachineObject*> m_list;
|
||||
std::vector<FilamentInfo> m_filaments;
|
||||
std::vector<FilamentInfo> m_ams_mapping_result;
|
||||
std::unordered_map<int, int> m_nozzle_mapping_result; // cached rack print-dispatch mapping: filament/group id -> physical nozzle pos
|
||||
std::vector<int> m_filaments_map;
|
||||
std::shared_ptr<BBLStatusBarPrint> m_status_bar;
|
||||
std::unique_ptr<Worker> m_worker;
|
||||
@@ -511,6 +511,23 @@ public:
|
||||
bool is_nozzle_type_match(DevExtderSystem data, wxString& error_message) const;
|
||||
int convert_filament_map_nozzle_id_to_task_nozzle_id(int nozzle_id) const;
|
||||
|
||||
// Physical nozzle(s) a mapped filament (by filament index / FilamentInfo::id) prints on.
|
||||
// key: nozzle pos id, value: nozzle. Used by the per-nozzle filament blacklist check loop.
|
||||
std::map<int, DevNozzle> get_mapped_nozzles(int fila_id) const;
|
||||
|
||||
// Short label of the physical nozzle(s) a filament prints on, shown on its send-dialog card:
|
||||
// rack slots ("R1", "R2 R3") for a nozzle-rack printer, or "L"/"R"/"L R" for a filament-switcher
|
||||
// printer. Empty for printers with neither (the card then shows no nozzle row).
|
||||
wxString get_mapped_nozzle_str(int fila_id) const;
|
||||
|
||||
// Rack print-dispatch nozzle mapping (H2C): request the printer's auto-mapping and consume the
|
||||
// result, gating the Send button while the printer computes. Both are no-ops for non-rack printers.
|
||||
bool CheckErrorSyncNozzleMappingResultV1(MachineObject* obj_);
|
||||
bool CheckErrorSyncNozzleMappingResultV0(MachineObject* obj_);
|
||||
void clear_nozzle_mapping();
|
||||
bool use_dynamic_nozzle_map() const;
|
||||
void on_flow_cali_option_changed();
|
||||
|
||||
PrintFromType get_print_type() {return m_print_type;};
|
||||
wxString format_steel_name(NozzleType type);
|
||||
PrintDialogStatus get_status() { return m_print_status; }
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
#include "DeviceCore/DevManager.h"
|
||||
#include "DeviceCore/DevPrintTaskInfo.h"
|
||||
|
||||
#include "DeviceTab/wgtDeviceNozzleRack.h"
|
||||
|
||||
|
||||
|
||||
#include "PrintOptionsDialog.hpp"
|
||||
@@ -1611,13 +1613,26 @@ wxBoxSizer *StatusBasePanel::create_machine_control_page(wxWindow *parent)
|
||||
wxBoxSizer *bSizer_control = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto temp_axis_ctrl_sizer = create_temp_axis_group(parent);
|
||||
auto m_ams_ctrl_sizer = create_ams_group(parent);
|
||||
auto m_filament_load_sizer = create_filament_group(parent);
|
||||
|
||||
/* ams control box or live nozzle-rack panel (rack printers switch between the two) */
|
||||
wxSizer *ams_rack_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
ams_rack_sizer->Add(create_ams_group(parent), 0, wxEXPAND | wxLEFT);
|
||||
|
||||
m_panel_nozzle_rack = new wgtDeviceNozzleRack(parent);
|
||||
m_panel_nozzle_rack->Show(false);
|
||||
ams_rack_sizer->Add(m_panel_nozzle_rack, 0, wxEXPAND | wxLEFT);
|
||||
|
||||
m_ams_rack_switch = new SwitchBoard(parent, _L("Filament"), _L("Hotends"), wxSize(FromDIP(126), FromDIP(26)));
|
||||
m_ams_rack_switch->updateState("left");
|
||||
m_ams_rack_switch->Hide();
|
||||
m_ams_rack_switch->Bind(wxCUSTOMEVT_SWITCH_POS, &StatusBasePanel::on_ams_rack_switch, this);
|
||||
|
||||
bSizer_control->Add(0, 0, 0, wxTOP, FromDIP(8));
|
||||
bSizer_control->Add(temp_axis_ctrl_sizer, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8));
|
||||
bSizer_control->Add(m_ams_rack_switch, 0, wxALIGN_CENTRE|wxTOP, FromDIP(6));
|
||||
bSizer_control->Add(0, 0, 0, wxTOP, FromDIP(6));
|
||||
bSizer_control->Add(m_ams_ctrl_sizer, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8));
|
||||
bSizer_control->Add(ams_rack_sizer, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8));
|
||||
bSizer_control->Add(0, 0, 0, wxTOP, FromDIP(6));
|
||||
bSizer_control->Add(m_filament_load_sizer, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8));
|
||||
bSizer_control->Add(0, 0, 0, wxTOP, FromDIP(4));
|
||||
@@ -2163,7 +2178,19 @@ void StatusBasePanel::expand_filament_loading(wxMouseEvent& e)
|
||||
else if (obj->is_series_o())
|
||||
{
|
||||
const auto& ext_system = obj->GetExtderSystem();
|
||||
if (ext_system->GetTotalExtderCount() == 2)
|
||||
// Prefer the config-driven filament-load image: H2C ships per-extruder bitmaps, and a printer
|
||||
// fitted with an induction hotend rack picks the rack-specific set (filament_load_image_nozzle_rack).
|
||||
// Printers whose profile carries no such key (e.g. H2D) get an empty name here and fall through to
|
||||
// the generic O-series bitmaps below, so their monitor page stays byte-identical. Rack-driven, so
|
||||
// there is no behavior change for any non-rack printer.
|
||||
int cur_ext_id = (ext_system && ext_system->GetTotalExtderCount() > 1) ? ext_system->GetCurrentExtderId() : 0;
|
||||
bool has_nozzle_rack = obj->GetNozzleSystem()->GetNozzleRack()->IsSupported();
|
||||
std::string img_name = DevPrinterConfigUtil::get_filament_load_img(obj->printer_type, cur_ext_id, has_nozzle_rack);
|
||||
if (!img_name.empty())
|
||||
{
|
||||
m_filament_load_img->SetBitmap(create_scaled_bitmap(img_name, this, load_img_size));
|
||||
}
|
||||
else if (ext_system->GetTotalExtderCount() == 2)
|
||||
{
|
||||
int cur_extder_id = ext_system->GetCurrentExtderId();
|
||||
if (cur_extder_id == MAIN_EXTRUDER_ID)
|
||||
@@ -2202,6 +2229,10 @@ void StatusBasePanel::show_ams_group(bool show)
|
||||
wxGetApp().mainframe->m_monitor->Layout();
|
||||
}
|
||||
|
||||
// On rack printers, don't clobber the rack view when the user has the switch on "Hotends".
|
||||
// Inert for every non-rack printer: the switch stays hidden, so this guard never triggers.
|
||||
if (show && m_ams_rack_switch->IsShown() && (m_ams_rack_switch->switch_left != true)) { return; }
|
||||
|
||||
if (m_ams_control_box->IsShown() != show) {
|
||||
m_ams_control_box->Show(show);
|
||||
m_ams_control->Layout();
|
||||
@@ -2237,6 +2268,31 @@ void StatusBasePanel::show_filament_load_group(bool show)
|
||||
}
|
||||
}
|
||||
|
||||
void StatusBasePanel::jump_to_Rack()
|
||||
{
|
||||
if (obj && obj->GetNozzleSystem()->GetNozzleRack()->IsSupported()) {
|
||||
m_ams_rack_switch->updateState("right");
|
||||
m_ams_control_box->Show(false);
|
||||
m_panel_nozzle_rack->Show(true);
|
||||
Layout();
|
||||
}
|
||||
}
|
||||
|
||||
void StatusBasePanel::on_ams_rack_switch(wxCommandEvent &e)
|
||||
{
|
||||
if (!m_ams_control_box->IsShown() && e.GetInt() == 1) {
|
||||
m_ams_control_box->Show(e.GetInt() == 1);
|
||||
m_panel_nozzle_rack->Show(e.GetInt() == 0);
|
||||
Layout();
|
||||
} else if (!m_panel_nozzle_rack->IsShown() && e.GetInt() == 0) {
|
||||
m_ams_control_box->Show(e.GetInt() == 1);
|
||||
m_panel_nozzle_rack->Show(e.GetInt() == 0);
|
||||
Layout();
|
||||
}
|
||||
|
||||
e.Skip();
|
||||
}
|
||||
|
||||
void StatusPanel::update_camera_state(MachineObject* obj)
|
||||
{
|
||||
if (!obj) return;
|
||||
@@ -2778,6 +2834,8 @@ void StatusPanel::update(MachineObject *obj)
|
||||
update_ams(obj);
|
||||
update_cali(obj);
|
||||
|
||||
update_rack(obj);
|
||||
|
||||
if (obj) {
|
||||
//nozzle ui
|
||||
//m_button_left_of_extruder->SetSelected();
|
||||
@@ -3412,7 +3470,14 @@ void StatusPanel::update_ams(MachineObject *obj)
|
||||
int tray_id_int = atoi(tray_id.c_str());
|
||||
// new protocol
|
||||
if (ams_id_int < 128) {
|
||||
if ((obj->tray_reading_bits & (1 << (ams_id_int * 4 + tray_id_int))) != 0) {
|
||||
if (ams_it->second->IsAmsLiteMixed()) {
|
||||
// Mixed AMS-Lite (A2L / N9) RFID-reading bits occupy positions 24..27.
|
||||
if ((obj->tray_reading_bits & (1 << (AMS_LITE_MIXED_TRAY_INDEX_OFFSET + tray_id_int))) != 0) {
|
||||
m_ams_control->PlayRridLoading(ams_id, tray_id);
|
||||
} else {
|
||||
m_ams_control->StopRridLoading(ams_id, tray_id);
|
||||
}
|
||||
} else if ((obj->tray_reading_bits & (1 << (ams_id_int * 4 + tray_id_int))) != 0) {
|
||||
m_ams_control->PlayRridLoading(ams_id, tray_id);
|
||||
} else {
|
||||
m_ams_control->StopRridLoading(ams_id, tray_id);
|
||||
@@ -5085,6 +5150,9 @@ void StatusPanel::set_default()
|
||||
m_ams_control->Hide();
|
||||
m_ams_control_box->Hide();
|
||||
m_ams_control->Reset();
|
||||
m_ams_rack_switch->updateState("left");
|
||||
m_ams_rack_switch->Hide();
|
||||
m_panel_nozzle_rack->Hide();
|
||||
m_scale_panel->Hide();
|
||||
m_filament_load_box->Hide();
|
||||
m_filament_step->Hide();
|
||||
@@ -5262,6 +5330,7 @@ void StatusPanel::msw_rescale()
|
||||
m_extruder_switching_status->msw_rescale();
|
||||
|
||||
m_ams_control->msw_rescale();
|
||||
m_panel_nozzle_rack->Rescale();
|
||||
// m_filament_step->Rescale();
|
||||
|
||||
|
||||
@@ -5283,6 +5352,20 @@ void StatusPanel::msw_rescale()
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void StatusPanel::update_rack(MachineObject *obj)
|
||||
{
|
||||
// Rack switch + live rack panel are shown only for printers whose nozzle system reports a rack
|
||||
// (H2C). Every other Bambu printer keeps the switch hidden and the panel collapsed -> the AMS
|
||||
// monitor page is unchanged for X1/P1/A1/H2D/H2S.
|
||||
if (obj && obj->GetNozzleSystem()->GetNozzleRack()->IsSupported()) {
|
||||
m_ams_rack_switch->Show();
|
||||
m_panel_nozzle_rack->UpdateRackInfo(obj->GetNozzleSystem()->GetNozzleRack());
|
||||
} else {
|
||||
m_ams_rack_switch->Show(false);
|
||||
m_panel_nozzle_rack->Show(false);
|
||||
}
|
||||
}
|
||||
|
||||
void StatusPanel::update_filament_loading_panel(MachineObject* obj)
|
||||
{
|
||||
if (!obj) {
|
||||
|
||||
@@ -50,6 +50,7 @@ namespace GUI {
|
||||
|
||||
// Previous definitions
|
||||
class MessageDialog;
|
||||
class wgtDeviceNozzleRack;
|
||||
|
||||
enum CameraRecordingStatus {
|
||||
RECORDING_NONE,
|
||||
@@ -518,11 +519,16 @@ protected:
|
||||
bool m_show_ams_group{false};
|
||||
bool m_show_filament_group{ false };
|
||||
|
||||
/* AMS control box <-> live nozzle-rack panel toggle (rack printers only) */
|
||||
SwitchBoard* m_ams_rack_switch{ nullptr };
|
||||
|
||||
AMSControl* m_ams_control;
|
||||
StaticBox* m_ams_control_box;
|
||||
wxStaticBitmap *m_ams_extruder_img;
|
||||
wxStaticBitmap* m_bitmap_extruder_img;
|
||||
|
||||
wgtDeviceNozzleRack* m_panel_nozzle_rack{ nullptr };
|
||||
|
||||
wxPanel * m_panel_separator_right;
|
||||
wxPanel * m_panel_separotor_bottom;
|
||||
wxGridBagSizer *m_tasklist_info_sizer{nullptr};
|
||||
@@ -611,6 +617,11 @@ public:
|
||||
void show_ams_group(bool show = true);
|
||||
void show_filament_load_group(bool show = true);
|
||||
MediaPlayCtrl* get_media_play_ctrl() {return m_media_play_ctrl;};
|
||||
|
||||
void jump_to_Rack();
|
||||
|
||||
private:
|
||||
void on_ams_rack_switch(wxCommandEvent& event);
|
||||
};
|
||||
|
||||
|
||||
@@ -767,6 +778,7 @@ protected:
|
||||
void update_temp_ctrl(MachineObject *obj);
|
||||
void update_misc_ctrl(MachineObject *obj);
|
||||
void update_ams(MachineObject* obj);
|
||||
void update_rack(MachineObject* obj);
|
||||
void update_filament_loading_panel(MachineObject* obj);
|
||||
|
||||
void update_extruder_status(MachineObject* obj);
|
||||
|
||||
@@ -1547,6 +1547,8 @@ bool SyncAmsInfoDialog::is_nozzle_type_match(DevExtderSystem data, wxString &err
|
||||
NozzleVolumeType nozzle_volume_type = (NozzleVolumeType) (nozzle_volume_type_opt->get_at(used_extruders[i]));
|
||||
if (nozzle_volume_type == NozzleVolumeType::nvtStandard) {
|
||||
used_extruders_flow[used_extruders[i]] = "Standard";
|
||||
} else if (nozzle_volume_type == NozzleVolumeType::nvtTPUHighFlow) {
|
||||
used_extruders_flow[used_extruders[i]] = "TPU High Flow";
|
||||
} else {
|
||||
used_extruders_flow[used_extruders[i]] = "High Flow";
|
||||
}
|
||||
@@ -1562,6 +1564,8 @@ bool SyncAmsInfoDialog::is_nozzle_type_match(DevExtderSystem data, wxString &err
|
||||
flow_type_of_machine.push_back("High Flow");
|
||||
} else if (it->GetNozzleFlowType() == NozzleFlowType::S_FLOW) {
|
||||
flow_type_of_machine.push_back("Standard");
|
||||
} else if (it->GetNozzleFlowType() == NozzleFlowType::U_FLOW) {
|
||||
flow_type_of_machine.push_back("TPU High Flow");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1847,6 +1847,27 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
|
||||
if (opt_key == "enable_prime_tower") {
|
||||
auto timelapse_type = m_config->option<ConfigOptionEnum<TimelapseType>>("timelapse_type");
|
||||
bool timelapse_enabled = timelapse_type->value == TimelapseType::tlSmooth;
|
||||
if (!boost::any_cast<bool>(value)) {
|
||||
// Disabling the prime tower on a multi-nozzle printer degrades quality because nozzle changes rely
|
||||
// on it. Gate on any extruder having extruder_max_nozzle_count > 1 so single-nozzle and dual-extruder
|
||||
// (H2D, {1,1}) printers keep their exact existing behavior.
|
||||
// Orca: gate on any_of(count > 1) rather than the sum of counts >= 2. The sum form would also fire for
|
||||
// H2D ({1,1} sums to 2); any_of(>1) preserves H2D's current no-dialog behavior.
|
||||
auto *max_nozzle_counts_opt = wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionIntsNullable>("extruder_max_nozzle_count");
|
||||
const bool has_multiple_nozzle = max_nozzle_counts_opt &&
|
||||
std::any_of(max_nozzle_counts_opt->values.begin(), max_nozzle_counts_opt->values.end(),
|
||||
[](int v) { return v > 1 && v != ConfigOptionIntsNullable::nil_value(); });
|
||||
if (has_multiple_nozzle) {
|
||||
MessageDialog dlg(wxGetApp().plater(),
|
||||
_L("Prime tower is required for nozzle changing. There may be flaws on the model without prime tower. Are you sure you want to disable prime tower?"),
|
||||
_L("Warning"), wxICON_WARNING | wxYES | wxNO);
|
||||
if (dlg.ShowModal() == wxID_NO) {
|
||||
DynamicPrintConfig new_conf = *m_config;
|
||||
new_conf.set_key_value("enable_prime_tower", new ConfigOptionBool(true));
|
||||
m_config_manipulation.apply(m_config, &new_conf);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!boost::any_cast<bool>(value) && timelapse_enabled) {
|
||||
bool set_enable_prime_tower = false;
|
||||
MessageDialog dlg(wxGetApp().plater(), _L("A prime tower is required for smooth timelapse mode. There may be flaws on the model without a prime tower. Are you sure you want to disable the prime tower\?"),
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <slic3r/GUI/Widgets/Label.hpp>
|
||||
#include <slic3r/GUI/I18N.hpp>
|
||||
#include "slic3r/GUI/DeviceTab/uiDeviceUpdateVersion.h"
|
||||
#include "slic3r/GUI/DeviceTab/wgtDeviceNozzleRackUpdate.h"
|
||||
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
@@ -217,6 +218,9 @@ MachineInfoPanel::MachineInfoPanel(wxWindow* parent, wxWindowID id, const wxPoin
|
||||
createAirPumpWidgets(m_main_left_sizer);
|
||||
createExtinguishWidgets(m_main_left_sizer);
|
||||
|
||||
// nozzle rack widgets (H2C induction hotend rack; hidden unless GetNozzleRack()->IsSupported())
|
||||
createNozzleRackWidgets(m_main_left_sizer);
|
||||
|
||||
m_main_sizer->Add(m_main_left_sizer, 1, wxEXPAND, 0);
|
||||
|
||||
wxBoxSizer *m_main_right_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
@@ -431,6 +435,7 @@ void MachineInfoPanel::init_bitmaps()
|
||||
m_img_laser = ScalableBitmap(this, "laser", 160);
|
||||
m_img_cutting = ScalableBitmap(this, "cut", 160);
|
||||
m_img_extinguish = ScalableBitmap(this, "extinguish", 160);
|
||||
m_img_nozzle_rack = ScalableBitmap(this, "nozzle_rack", 160);
|
||||
|
||||
upgrade_green_icon = ScalableBitmap(this, "monitor_upgrade_online", 5);
|
||||
upgrade_gray_icon = ScalableBitmap(this, "monitor_upgrade_offline", 5);
|
||||
@@ -539,6 +544,7 @@ void MachineInfoPanel::update(MachineObject* obj)
|
||||
update_cut(obj);
|
||||
update_laszer(obj);
|
||||
update_extinguish(obj);
|
||||
update_nozzle_rack(obj);
|
||||
|
||||
//update progress
|
||||
int upgrade_percent = obj->get_upgrade_percent();
|
||||
@@ -1257,6 +1263,80 @@ void MachineInfoPanel::show_extinguish(bool show)
|
||||
}
|
||||
}
|
||||
|
||||
void MachineInfoPanel::createNozzleRackWidgets(wxBoxSizer *main_left_sizer)
|
||||
{
|
||||
// horizontal line above
|
||||
m_nozzle_rack_line_above = new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL);
|
||||
m_nozzle_rack_line_above->SetBackgroundColour(wxColour(206, 206, 206));
|
||||
main_left_sizer->Add(m_nozzle_rack_line_above, 0, wxEXPAND | wxLEFT, FromDIP(40));
|
||||
|
||||
m_nozzle_rack_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
// left placeholder icon (keep consistent spacing with others)
|
||||
m_nozzle_rack_img = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize(FromDIP(200), FromDIP(200)));
|
||||
m_nozzle_rack_img->SetBitmap(m_img_nozzle_rack.bmp());
|
||||
m_nozzle_rack_sizer->Add(m_nozzle_rack_img, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5));
|
||||
|
||||
// right content: label + update button
|
||||
auto *content_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_nozzle_rack_text = new wxStaticText(this, wxID_ANY, _L("Hotends on Rack"), wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_nozzle_rack_text->Wrap(-1);
|
||||
m_nozzle_rack_text->SetFont(Label::Head_14);
|
||||
content_sizer->Add(m_nozzle_rack_text, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT | wxLEFT, FromDIP(50));
|
||||
|
||||
m_nozzle_rack_update_btn = new Button(this, _L("Info"));
|
||||
StateColor btn_bg(std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Disabled), std::pair<wxColour, int>(wxColour(200, 200, 200), StateColor::Pressed),
|
||||
std::pair<wxColour, int>(wxColour(240, 240, 240), StateColor::Hovered), std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Enabled),
|
||||
std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Normal));
|
||||
StateColor btn_bd(std::pair<wxColour, int>(wxColour(200, 200, 200), StateColor::Disabled), std::pair<wxColour, int>(wxColour(150, 150, 150), StateColor::Enabled));
|
||||
StateColor btn_text(std::pair<wxColour, int>(wxColour(150, 150, 150), StateColor::Disabled), std::pair<wxColour, int>(wxColour(0, 0, 0), StateColor::Enabled));
|
||||
m_nozzle_rack_update_btn->SetBackgroundColor(btn_bg);
|
||||
m_nozzle_rack_update_btn->SetBorderColor(btn_bd);
|
||||
m_nozzle_rack_update_btn->SetTextColor(btn_text);
|
||||
m_nozzle_rack_update_btn->SetFont(Label::Body_10.Bold());
|
||||
m_nozzle_rack_update_btn->SetMinSize(wxSize(FromDIP(-1), FromDIP(24)));
|
||||
m_nozzle_rack_update_btn->SetCornerRadius(FromDIP(12));
|
||||
m_nozzle_rack_update_btn->Bind(wxEVT_BUTTON, &MachineInfoPanel::on_nozzle_rack_update, this);
|
||||
content_sizer->Add(m_nozzle_rack_update_btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(350));
|
||||
|
||||
m_nozzle_rack_sizer->Add(content_sizer, 1, wxEXPAND, 0);
|
||||
|
||||
main_left_sizer->Add(m_nozzle_rack_sizer, 0, wxEXPAND, 0);
|
||||
}
|
||||
|
||||
void MachineInfoPanel::update_nozzle_rack(MachineObject* obj)
|
||||
{
|
||||
if (obj && obj->GetNozzleSystem()) {
|
||||
auto rack = obj->GetNozzleSystem()->GetNozzleRack();
|
||||
if (rack && rack->IsSupported()) {
|
||||
show_nozzle_rack(true);
|
||||
}
|
||||
else {
|
||||
show_nozzle_rack(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MachineInfoPanel::show_nozzle_rack(bool show)
|
||||
{
|
||||
if (m_nozzle_rack_img->IsShown() != show) {
|
||||
m_nozzle_rack_line_above->Show(show);
|
||||
m_nozzle_rack_update_btn->Show(show);
|
||||
m_nozzle_rack_img->Show(show);
|
||||
m_nozzle_rack_text->Show(show);
|
||||
}
|
||||
}
|
||||
|
||||
void MachineInfoPanel::on_nozzle_rack_update(wxCommandEvent &event)
|
||||
{
|
||||
if (!m_obj || !m_obj->GetNozzleSystem()) return;
|
||||
auto rack = m_obj->GetNozzleSystem()->GetNozzleRack();
|
||||
if (!rack) return;
|
||||
|
||||
wgtDeviceNozzleRackUpgradeDlg dlg(this, rack);
|
||||
dlg.ShowModal();
|
||||
}
|
||||
|
||||
|
||||
void MachineInfoPanel::on_sys_color_changed()
|
||||
{
|
||||
|
||||
@@ -134,6 +134,13 @@ protected:
|
||||
wxStaticLine* m_extinguish_line_above = nullptr;;
|
||||
uiDeviceUpdateVersion* m_extinguish_version = nullptr;
|
||||
|
||||
/* nozzle rack (H2C induction hotend rack) — opens wgtDeviceNozzleRackUpgradeDlg */
|
||||
wxBoxSizer* m_nozzle_rack_sizer = nullptr;
|
||||
wxStaticBitmap* m_nozzle_rack_img = nullptr;
|
||||
wxStaticLine* m_nozzle_rack_line_above = nullptr;
|
||||
wxStaticText* m_nozzle_rack_text = nullptr;
|
||||
Button* m_nozzle_rack_update_btn = nullptr;
|
||||
|
||||
/* upgrade widgets */
|
||||
wxBoxSizer* m_upgrading_sizer;
|
||||
wxStaticText * m_staticText_upgrading_info;
|
||||
@@ -155,6 +162,7 @@ protected:
|
||||
ScalableBitmap m_img_cutting;
|
||||
ScalableBitmap m_img_laser;
|
||||
ScalableBitmap m_img_extinguish;
|
||||
ScalableBitmap m_img_nozzle_rack;
|
||||
ScalableBitmap upgrade_gray_icon;
|
||||
ScalableBitmap upgrade_green_icon;
|
||||
ScalableBitmap upgrade_yellow_icon;
|
||||
@@ -212,16 +220,21 @@ private:
|
||||
void createCuttingWidgets(wxBoxSizer* main_left_sizer);
|
||||
void createLaserWidgets(wxBoxSizer* main_left_sizer);
|
||||
void createExtinguishWidgets(wxBoxSizer* main_left_sizer);
|
||||
void createNozzleRackWidgets(wxBoxSizer* main_left_sizer);
|
||||
|
||||
void update_air_pump(MachineObject* obj);
|
||||
void update_cut(MachineObject* obj);
|
||||
void update_laszer(MachineObject* obj);
|
||||
void update_extinguish(MachineObject* obj);
|
||||
void update_nozzle_rack(MachineObject* obj);
|
||||
|
||||
void show_air_pump(bool show = true);
|
||||
void show_cut(bool show = true);
|
||||
void show_laszer(bool show = true);
|
||||
void show_extinguish(bool show = true);
|
||||
void show_nozzle_rack(bool show = true);
|
||||
|
||||
void on_nozzle_rack_update(wxCommandEvent& event);
|
||||
};
|
||||
|
||||
//enum UpgradeMode {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,239 @@
|
||||
#ifndef slic3r_GUI_MultiNozzleSync_hpp_
|
||||
#define slic3r_GUI_MultiNozzleSync_hpp_
|
||||
|
||||
// Multi-nozzle GUI.
|
||||
//
|
||||
// Two surfaces live here:
|
||||
// - The DEVICE-INDEPENDENT "manual nozzle count" surface (ManualNozzleCountDialog + helpers): lets the
|
||||
// user declare, per extruder, how many physical nozzles of each volume type a multi-nozzle (H2C)
|
||||
// extruder carries. Persisted into the printer preset's `extruder_nozzle_stats` config key, which the
|
||||
// multi-nozzle slicer already consumes (ToolOrdering::build_multi_nozzle_group_result).
|
||||
// - The DEVICE-SYNC surface (MultiNozzleSyncDialog / HotEndTable / NozzleListTable /
|
||||
// tryPopUpMultiNozzleDialog): reads the live nozzle rack from the connected machine via
|
||||
// DevNozzleRack / wgtDeviceNozzleRackNozzleItem and lets the user pick a nozzle option to slice with.
|
||||
// Popped from the sidebar "sync machine" button when the selected printer is an H2C.
|
||||
//
|
||||
// Everything here is inert for existing printers: the manual entry points are gated on the printer having
|
||||
// any extruder with `extruder_max_nozzle_count > 1` (no shipping single-nozzle/dual-extruder profile sets
|
||||
// it), and the device-sync path only runs for a connected machine whose rack reports as supported.
|
||||
|
||||
#include "../GUI_Utils.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/MultiNozzleUtils.hpp"
|
||||
#include "slic3r/GUI/DeviceCore/DevNozzleRack.h"
|
||||
#include "slic3r/GUI/DeviceTab/wgtDeviceNozzleRackNozzleItem.h"
|
||||
|
||||
#include <wx/panel.h>
|
||||
#include <wx/webview.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
class wxChoice;
|
||||
class wxStaticText;
|
||||
class wxStaticBitmap;
|
||||
class Button; // global widget (src/slic3r/GUI/Widgets/Button.hpp), not in the Slic3r::GUI namespace
|
||||
class Label; // global widget (src/slic3r/GUI/Widgets/Label.hpp)
|
||||
class StaticBox;
|
||||
|
||||
namespace Slic3r {
|
||||
class PresetBundle;
|
||||
class MachineObject;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
#define ENABLE_MIX_FLOW_PRINT 1
|
||||
|
||||
#if ENABLE_MIX_FLOW_PRINT
|
||||
struct NozzleOption
|
||||
{
|
||||
std::string diameter;
|
||||
std::unordered_map<int, std::unordered_map<NozzleVolumeType, int>> extruder_nozzle_stats;
|
||||
};
|
||||
#else
|
||||
struct NozzleOption
|
||||
{
|
||||
std::string diameter;
|
||||
std::unordered_map<int, std::pair<NozzleVolumeType, int>> extruder_nozzle_stats;
|
||||
};
|
||||
#endif
|
||||
|
||||
// Dialog to manually set the per-volume-type physical nozzle count of one multi-nozzle extruder.
|
||||
class ManualNozzleCountDialog : public DPIDialog
|
||||
{
|
||||
public:
|
||||
ManualNozzleCountDialog(wxWindow *parent, NozzleVolumeType volume_type, int standard_count, int highflow_count, int max_nozzle_count, bool force_no_zero);
|
||||
~ManualNozzleCountDialog() override = default;
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override {}
|
||||
int GetNozzleCount(NozzleVolumeType volume_type) const;
|
||||
|
||||
private:
|
||||
wxChoice *m_standard_choice{nullptr};
|
||||
wxChoice *m_highflow_choice{nullptr};
|
||||
Button *m_confirm_btn{nullptr};
|
||||
wxStaticText *m_error_label{nullptr};
|
||||
};
|
||||
|
||||
class ExtruderBadge : public wxPanel
|
||||
{
|
||||
public:
|
||||
ExtruderBadge(wxWindow* parent);
|
||||
void SetExtruderInfo(int extruder_id, const std::string& label, const NozzleVolumeType& flow);
|
||||
void UnMarkRelatedItems(const NozzleOption& option);
|
||||
void MarkRelatedItems(const NozzleOption& option);
|
||||
void SetExtruderValid(bool right_on);
|
||||
private:
|
||||
void SetExtruderStatus(bool left_selected, bool right_selected);
|
||||
|
||||
bool m_right_on{ true };
|
||||
wxStaticBitmap* badget;
|
||||
Label* left;
|
||||
Label* right;
|
||||
Label* left_diameter_desp;
|
||||
Label* right_diameter_desp;
|
||||
Label* left_flow_desp;
|
||||
Label* right_flow_desp;
|
||||
|
||||
std::vector<std::string> m_diameter_list;
|
||||
std::vector<NozzleVolumeType> m_volume_type_list;
|
||||
};
|
||||
|
||||
class HotEndTable : public wxPanel
|
||||
{
|
||||
public:
|
||||
HotEndTable(wxWindow* parent);
|
||||
void UpdateRackInfo(std::weak_ptr<DevNozzleRack> rack);
|
||||
void MarkRelatedItems(const NozzleOption& option);
|
||||
void UnMarkRelatedItems(const NozzleOption& option);
|
||||
private:
|
||||
StaticBox* CreateNozzleBox(const std::vector<int>& nozzle_indices);
|
||||
void UpdateNozzleItems(const std::unordered_map<int, wgtDeviceNozzleRackNozzleItem*>& nozzle_items,
|
||||
std::shared_ptr<DevNozzleRack> nozzle_rack);
|
||||
|
||||
private:
|
||||
struct HotEndAttr {
|
||||
std::string diameter;
|
||||
int extruder_id;
|
||||
NozzleVolumeType volume_type;
|
||||
};
|
||||
|
||||
std::vector<int> FilterHotEnds(const NozzleOption& option);
|
||||
|
||||
private:
|
||||
StaticBox* m_arow_nozzle_box{ nullptr };
|
||||
StaticBox* m_brow_nozzle_box{ nullptr };
|
||||
std::unordered_map<int, wgtDeviceNozzleRackNozzleItem*> m_nozzle_items;
|
||||
std::weak_ptr<DevNozzleRack> m_nozzle_rack;
|
||||
void OnPaint(wxPaintEvent& event);
|
||||
};
|
||||
|
||||
|
||||
wxDECLARE_EVENT(EVT_NOZZLE_SELECTED, wxCommandEvent);
|
||||
|
||||
class NozzleListTable : public wxPanel
|
||||
{
|
||||
public:
|
||||
NozzleListTable(wxWindow* parent);
|
||||
int GetSelectIdx();
|
||||
void SetOptions(const std::vector<NozzleOption>& options,int default_select);
|
||||
private:
|
||||
wxString BuildTableObjStr();
|
||||
wxString BuildTextObjStr();
|
||||
std::vector<NozzleOption> m_nozzle_options;
|
||||
|
||||
void SendSelectionChangedEvent();
|
||||
|
||||
wxWebView* m_web_view;
|
||||
|
||||
int m_selected_idx;
|
||||
};
|
||||
|
||||
class MultiNozzleStatusTable : public wxPanel
|
||||
{
|
||||
public:
|
||||
MultiNozzleStatusTable(wxWindow* parent);
|
||||
void UpdateRackInfo(std::weak_ptr<DevNozzleRack> rack);
|
||||
void MarkRelatedItems(const NozzleOption& option);
|
||||
void UnMarkRelatedItems(const NozzleOption& option);
|
||||
private:
|
||||
ExtruderBadge* m_badge;
|
||||
HotEndTable* m_table;
|
||||
};
|
||||
|
||||
|
||||
class MultiNozzleSyncDialog : public DPIDialog
|
||||
{
|
||||
public:
|
||||
MultiNozzleSyncDialog(wxWindow* parent, std::weak_ptr<DevNozzleRack> rack);
|
||||
virtual void on_dpi_changed(const wxRect& suggested_rect) {};
|
||||
std::vector<NozzleOption> GetNozzleOptions(const std::vector<MultiNozzleUtils::NozzleGroupInfo>& group_infos);
|
||||
|
||||
std::optional<NozzleOption> GetSelectedOption() {
|
||||
if (m_nozzle_option_idx < 0 || m_nozzle_option_idx >= m_nozzle_option_values.size())
|
||||
return std::nullopt;
|
||||
return m_nozzle_option_values[m_nozzle_option_idx];
|
||||
}
|
||||
|
||||
int ShowModal() override;
|
||||
~MultiNozzleSyncDialog() override;
|
||||
private:
|
||||
void UpdateRackInfo(std::weak_ptr<DevNozzleRack> rack);
|
||||
|
||||
bool hasMultiDiameters(const std::vector<MultiNozzleUtils::NozzleGroupInfo>& group_infos);
|
||||
void OnSelectRadio(int select_idx);
|
||||
|
||||
bool UpdateUi(std::weak_ptr<DevNozzleRack> rack, bool ignore_unknown=false, bool ignore_unreliable=false);
|
||||
|
||||
bool UpdateOptionList(std::weak_ptr<DevNozzleRack> rack, bool ignore_unknown, bool ignore_unreliable);
|
||||
void UpdateTip(std::weak_ptr<DevNozzleRack> rack, bool ignore_unknown, bool ignore_unreliable);
|
||||
void UpdateButton(std::weak_ptr<DevNozzleRack> rack, bool ignore_unknown, bool ignore_unreliable);
|
||||
void OnRackStatusReadingFinished(wxEvent& evt);
|
||||
void OnRefreshTimer(wxTimerEvent& event);
|
||||
|
||||
private:
|
||||
MultiNozzleStatusTable* m_nozzle_table;
|
||||
NozzleListTable* m_list_table;
|
||||
std::vector<NozzleOption> m_nozzle_option_values;
|
||||
int m_nozzle_option_idx{ -1 };
|
||||
bool m_refreshing{ false };
|
||||
|
||||
std::weak_ptr<DevNozzleRack> m_nozzle_rack;
|
||||
Label* m_tips;
|
||||
Label* m_caution;
|
||||
|
||||
wxTimer* m_refresh_timer {nullptr};
|
||||
size_t m_rack_event_token;
|
||||
Button* m_cancel_btn;
|
||||
Button* m_confirm_btn;
|
||||
};
|
||||
|
||||
|
||||
// Entry point for the sidebar "sync machine" button: pops MultiNozzleSyncDialog for a connected H2C and
|
||||
// returns the chosen nozzle option (nullopt if the machine has no supported rack or the user cancels).
|
||||
std::optional<NozzleOption> tryPopUpMultiNozzleDialog(MachineObject* obj);
|
||||
|
||||
// Persist one extruder's per-volume-type nozzle count into the edited printer preset's `extruder_nozzle_stats`
|
||||
// config key (the value the multi-nozzle slicer reads). When clear_all is true, the extruder's other volume-type
|
||||
// counts are reset first.
|
||||
void setExtruderNozzleCount(PresetBundle *preset_bundle, int extruder_id, NozzleVolumeType type, int nozzle_count, bool clear_all);
|
||||
|
||||
// Refresh the sidebar nozzle-count display for one extruder. Deferred wiring point: Orca's sidebar ExtruderGroup has
|
||||
// no nozzle-count widget yet, so the multi-nozzle sidebar UI is not wired up.
|
||||
void updateNozzleCountDisplay(PresetBundle *preset_bundle, int extruder_id, NozzleVolumeType volume_type);
|
||||
|
||||
// True when a nozzle of this diameter can carry the "Direct Drive TPU High Flow" variant. That variant
|
||||
// exists on the 0.4 and 0.6 nozzle H2D/H2D Pro leaves (not 0.2/0.8), so this is the single source of truth
|
||||
// for the diameter set — the counterpart of the High-Flow-skip-for-0.2 guard.
|
||||
bool nozzle_diameter_supports_tpu_high_flow(double nozzle_diameter);
|
||||
// Same predicate resolved from the edited printer preset's nozzle_diameter for one extruder. False when unknown.
|
||||
bool extruder_supports_tpu_high_flow(PresetBundle *preset_bundle, int extruder_id);
|
||||
|
||||
// Entry point: pop the ManualNozzleCountDialog for one extruder of a multi-nozzle printer and persist the result.
|
||||
// No-op unless the selected printer has an extruder with extruder_max_nozzle_count > 1.
|
||||
void manuallySetNozzleCount(int extruder_id);
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
#endif // slic3r_GUI_MultiNozzleSync_hpp_
|
||||
@@ -94,6 +94,10 @@ wxString get_nozzle_volume_type_name(NozzleVolumeType type)
|
||||
return _L("Standard");
|
||||
} else if (NozzleVolumeType::nvtHighFlow == type) {
|
||||
return _L("High Flow");
|
||||
} else if (NozzleVolumeType::nvtHybrid == type) {
|
||||
return _L("Hybrid");
|
||||
} else if (NozzleVolumeType::nvtTPUHighFlow == type) {
|
||||
return _L("TPU High Flow");
|
||||
}
|
||||
return wxString();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user