mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-28 05:12:27 +00:00
fix(gui): rack-aware pre-print nozzle checks in the send dialog
A print sliced for a nozzle that sits in the hotend rack (but is not mounted) was blocked by the send dialog's mounted-nozzle diameter check, even though the printer fetches the required nozzle itself (#14685). Consolidate the three mounted-nozzle gates (_is_nozzle_data_valid, is_nozzle_type_match, _is_same_nozzle_diameters) into a single CheckErrorExtruderNozzleWithSlicing fed by s_get_slicing_extuder_nozzles, which collects the plate's per-extruder nozzle requirements (hybrid extruders contribute one entry per used sub-nozzle flow). The rack extruder validates against its whole inventory (mounted + rack) with guidance to calibrate the rack, refresh nozzle info, or re-slice, and blocks when toolhead + rack are full (no free slot to stow a nozzle). Other extruders keep the validity/flow/diameter checks against the mounted nozzle. Also add CheckErrorRackStatus, which holds Send while the printer is still reading the rack hotend information, and judge material hardness for the rack extruder per dispatch-mapped nozzle as a non-blocking caution (mounted nozzles keep the blocking gate).
This commit is contained in:
@@ -18763,8 +18763,26 @@ msgstr ""
|
|||||||
msgid "Both"
|
msgid "Both"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
#, possible-c-format, possible-boost-format
|
||||||
"The printer has no nozzle matching the slicing file (%s). Please install a "
|
msgid "The printer has no nozzle matching the slicing file (%s)."
|
||||||
"matching nozzle on the printer, or set the corresponding printer preset "
|
msgstr ""
|
||||||
"while slicing."
|
|
||||||
|
msgid "Please complete the hotend rack setup and try again."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Please refresh the nozzle information and try again."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Please install a matching nozzle in the hotend rack, or set the corresponding printer preset while slicing."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "The toolhead and hotend rack are full. Please remove at least one hotend before printing."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#, possible-c-format, possible-boost-format
|
||||||
|
msgid "Refreshing information of hotends(%d/%d)."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#, possible-c-format, possible-boost-format
|
||||||
|
msgid "The hardness of current material (%s) exceeds the hardness of %s(%s). It may cause nozzle wear, leading to material leakage and unstable flow. Please exercise caution when using it."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -157,4 +157,27 @@ enum PrintFromType
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
};// namespace Slic3r
|
};// namespace Slic3r
|
||||||
|
|
||||||
|
// A nozzle requirement of the sliced plate (or an installed nozzle's identity), compared in the
|
||||||
|
// pre-print slicing-vs-installed nozzle checks.
|
||||||
|
struct NozzleDef
|
||||||
|
{
|
||||||
|
float nozzle_diameter;
|
||||||
|
Slic3r::NozzleFlowType nozzle_flow_type;
|
||||||
|
|
||||||
|
bool operator==(const NozzleDef& other) const
|
||||||
|
{
|
||||||
|
return nozzle_diameter == other.nozzle_diameter && nozzle_flow_type == other.nozzle_flow_type;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct std::hash<NozzleDef>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const NozzleDef& v) const noexcept
|
||||||
|
{
|
||||||
|
size_t h1 = std::hash<int>{}(v.nozzle_diameter * 1000);
|
||||||
|
size_t h2 = std::hash<int>{}(v.nozzle_flow_type);
|
||||||
|
return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2));
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -15,9 +15,9 @@
|
|||||||
// change_filament_gcode TPU-kit branch can activate. nvtHybrid is a slicer-only sentinel with no
|
// change_filament_gcode TPU-kit branch can activate. nvtHybrid is a slicer-only sentinel with no
|
||||||
// device representation, so it stays out of these device flow-type conversions.
|
// device representation, so it stays out of these device flow-type conversions.
|
||||||
//
|
//
|
||||||
// GetExtruderNozzleInfo (and its ExtruderNozzleInfos / NozzleDef type pair) is intentionally
|
// GetExtruderNozzleInfo (and its ExtruderNozzleInfos aggregate) is intentionally omitted here;
|
||||||
// omitted here; it belongs with the SelectMachine slicing-vs-installed nozzle-info comparison
|
// the SelectMachine slicing-vs-installed nozzle comparison collects its per-extruder NozzleDef
|
||||||
// that is its only consumer.
|
// entries itself (s_get_slicing_extuder_nozzles).
|
||||||
|
|
||||||
namespace Slic3r
|
namespace Slic3r
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1919,6 +1919,17 @@ int PartPlate::get_physical_extruder_by_filament_id(const DynamicConfig& g_confi
|
|||||||
return the_map->values[zero_base_logical_idx];
|
return the_map->values[zero_base_logical_idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int PartPlate::get_logical_extruder_by_filament_id(const DynamicConfig& g_config, int idx) const
|
||||||
|
{
|
||||||
|
const std::vector<int>& filament_map = get_real_filament_maps(g_config);
|
||||||
|
if (idx <= 0 || idx > (int)filament_map.size())
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return filament_map[idx - 1] - 1;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<int> PartPlate::get_used_filaments()
|
std::vector<int> PartPlate::get_used_filaments()
|
||||||
{
|
{
|
||||||
std::vector<int> used_filaments;
|
std::vector<int> used_filaments;
|
||||||
|
|||||||
@@ -350,6 +350,7 @@ public:
|
|||||||
std::vector<int> get_used_filaments();
|
std::vector<int> get_used_filaments();
|
||||||
const std::vector<FilamentInfo>& get_slice_filaments_info() const { return slice_filaments_info; }
|
const std::vector<FilamentInfo>& get_slice_filaments_info() const { return slice_filaments_info; }
|
||||||
int get_physical_extruder_by_filament_id(const DynamicConfig& g_config, int idx) const;
|
int get_physical_extruder_by_filament_id(const DynamicConfig& g_config, int idx) const;
|
||||||
|
int get_logical_extruder_by_filament_id(const DynamicConfig& g_config, int idx) const;
|
||||||
bool check_filament_printable(const DynamicPrintConfig & config, wxString& error_message);
|
bool check_filament_printable(const DynamicPrintConfig & config, wxString& error_message);
|
||||||
bool check_tpu_printable_status(const DynamicPrintConfig & config, const std::vector<int> &tpu_filaments);
|
bool check_tpu_printable_status(const DynamicPrintConfig & config, const std::vector<int> &tpu_filaments);
|
||||||
bool check_mixture_of_pla_and_petg(const DynamicPrintConfig & config);
|
bool check_mixture_of_pla_and_petg(const DynamicPrintConfig & config);
|
||||||
|
|||||||
@@ -58,7 +58,11 @@ std::string PrePrintChecker::get_print_status_info(PrintDialogStatus status)
|
|||||||
case PrintStatusFilamentWarningHighChamberTempCloseDoor: return "PrintStatusFilamentWarningHighChamberTempCloseDoor";
|
case PrintStatusFilamentWarningHighChamberTempCloseDoor: return "PrintStatusFilamentWarningHighChamberTempCloseDoor";
|
||||||
case PrintStatusFilamentWarningHighChamberTempSoft: return "PrintStatusFilamentWarningHighChamberTempSoft";
|
case PrintStatusFilamentWarningHighChamberTempSoft: return "PrintStatusFilamentWarningHighChamberTempSoft";
|
||||||
case PrintStatusFilamentWarningUnknownHighChamberTempSoft: return "PrintStatusFilamentWarningUnknownHighChamberTempSoft";
|
case PrintStatusFilamentWarningUnknownHighChamberTempSoft: return "PrintStatusFilamentWarningUnknownHighChamberTempSoft";
|
||||||
|
case PrintStatusNozzleNoMatchedHotends: return "PrintStatusNozzleNoMatchedHotends";
|
||||||
|
case PrintStatusNozzleRackMaximumInstalled: return "PrintStatusNozzleRackMaximumInstalled";
|
||||||
|
case PrintStatusRackReading: return "PrintStatusRackReading";
|
||||||
case PrintStatusWarningExtFilamentNotMatch: return "PrintStatusWarningExtFilamentNotMatch";
|
case PrintStatusWarningExtFilamentNotMatch: return "PrintStatusWarningExtFilamentNotMatch";
|
||||||
|
case PrintStatusFilamentWarningNozzleHRC: return "PrintStatusFilamentWarningNozzleHRC";
|
||||||
case PrintStatusReadingFinished: return "PrintStatusReadingFinished";
|
case PrintStatusReadingFinished: return "PrintStatusReadingFinished";
|
||||||
case PrintStatusSendingCanceled: return "PrintStatusSendingCanceled";
|
case PrintStatusSendingCanceled: return "PrintStatusSendingCanceled";
|
||||||
case PrintStatusAmsMappingSuccess: return "PrintStatusAmsMappingSuccess";
|
case PrintStatusAmsMappingSuccess: return "PrintStatusAmsMappingSuccess";
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ enum PrintDialogStatus : unsigned int {
|
|||||||
PrintStatusNozzleDataInvalid,
|
PrintStatusNozzleDataInvalid,
|
||||||
PrintStatusNozzleDiameterMismatch,
|
PrintStatusNozzleDiameterMismatch,
|
||||||
PrintStatusNozzleTypeMismatch,
|
PrintStatusNozzleTypeMismatch,
|
||||||
|
PrintStatusNozzleNoMatchedHotends,
|
||||||
|
PrintStatusNozzleRackMaximumInstalled,
|
||||||
PrintStatusRefreshingMachineList,
|
PrintStatusRefreshingMachineList,
|
||||||
PrintStatusSending,
|
PrintStatusSending,
|
||||||
PrintStatusLanModeNoSdcard,
|
PrintStatusLanModeNoSdcard,
|
||||||
@@ -67,6 +69,7 @@ enum PrintDialogStatus : unsigned int {
|
|||||||
PrintStatusUnsupportedPrinter,
|
PrintStatusUnsupportedPrinter,
|
||||||
PrintStatusRackNozzleMappingWaiting,
|
PrintStatusRackNozzleMappingWaiting,
|
||||||
PrintStatusRackNozzleMappingError,
|
PrintStatusRackNozzleMappingError,
|
||||||
|
PrintStatusRackReading,
|
||||||
PrintStatusFilaSwitcherError,
|
PrintStatusFilaSwitcherError,
|
||||||
PrintStatusPrinterErrorEnd,
|
PrintStatusPrinterErrorEnd,
|
||||||
|
|
||||||
@@ -105,6 +108,7 @@ enum PrintDialogStatus : unsigned int {
|
|||||||
PrintStatusFilamentWarningHighChamberTempSoft,
|
PrintStatusFilamentWarningHighChamberTempSoft,
|
||||||
PrintStatusFilamentWarningUnknownHighChamberTempSoft,
|
PrintStatusFilamentWarningUnknownHighChamberTempSoft,
|
||||||
PrintStatusWarningExtFilamentNotMatch,
|
PrintStatusWarningExtFilamentNotMatch,
|
||||||
|
PrintStatusFilamentWarningNozzleHRC,
|
||||||
PrintStatusFilamentWarningEnd,
|
PrintStatusFilamentWarningEnd,
|
||||||
|
|
||||||
PrintStatusWarningEnd,//->end error<-
|
PrintStatusWarningEnd,//->end error<-
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
#include <wx/mstream.h>
|
#include <wx/mstream.h>
|
||||||
#include <miniz.h>
|
#include <miniz.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <unordered_map>
|
||||||
#include "Plater.hpp"
|
#include "Plater.hpp"
|
||||||
#include "Notebook.hpp"
|
#include "Notebook.hpp"
|
||||||
#include "BitmapCache.hpp"
|
#include "BitmapCache.hpp"
|
||||||
@@ -1393,103 +1394,6 @@ void SelectMachineDialog::auto_supply_with_ext(std::vector<DevAmsTray> slots) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SelectMachineDialog::is_nozzle_type_match(DevExtderSystem data, wxString& error_message) const {
|
|
||||||
if (data.GetTotalExtderCount() <= 1 || !wxGetApp().preset_bundle)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const auto& project_config = wxGetApp().preset_bundle->project_config;
|
|
||||||
//check nozzle used
|
|
||||||
auto used_filaments = wxGetApp().plater()->get_partplate_list().get_curr_plate()->get_used_filaments(); // 1 based
|
|
||||||
auto filament_maps = wxGetApp().plater()->get_partplate_list().get_curr_plate()->get_real_filament_maps(project_config); // 1 based
|
|
||||||
// flow type and diameter of every nozzle the sliced plate needs, keyed by logical extruder;
|
|
||||||
// a hybrid extruder contributes one entry per used sub-nozzle flow
|
|
||||||
std::multimap<int, std::pair<NozzleFlowType, float>> used_extruders_flow;
|
|
||||||
std::vector<int> used_extruders; // 0 based
|
|
||||||
for (auto f : used_filaments) {
|
|
||||||
int filament_extruder = filament_maps[f - 1] - 1;
|
|
||||||
if (std::find(used_extruders.begin(), used_extruders.end(), filament_extruder) == used_extruders.end()) used_extruders.emplace_back(filament_extruder);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::sort(used_extruders.begin(), used_extruders.end());
|
|
||||||
|
|
||||||
auto nozzle_volume_type_opt = dynamic_cast<const ConfigOptionEnumsGeneric *>(wxGetApp().preset_bundle->project_config.option("nozzle_volume_type"));
|
|
||||||
auto nozzle_diameter_opt = wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter");
|
|
||||||
for (auto i = 0; i < used_extruders.size(); i++) {
|
|
||||||
if (nozzle_volume_type_opt) {
|
|
||||||
NozzleVolumeType nozzle_volume_type = (NozzleVolumeType) (nozzle_volume_type_opt->get_at(used_extruders[i]));
|
|
||||||
float preset_diameter = nozzle_diameter_opt ? (float) nozzle_diameter_opt->get_at(used_extruders[i]) : -1.0f;
|
|
||||||
if (nozzle_volume_type == NozzleVolumeType::nvtHybrid) {
|
|
||||||
// A hybrid extruder prints with a mix of nozzle flows: collect the flow of each
|
|
||||||
// physically used nozzle instead of forcing a single one.
|
|
||||||
auto nozzle_group_res = DevUtilBackend::GetNozzleGroupResult(m_plater);
|
|
||||||
if (nozzle_group_res) {
|
|
||||||
for (const auto &nozzle_info : nozzle_group_res->get_used_nozzles_in_extruder(used_extruders[i])) {
|
|
||||||
float diameter = preset_diameter;
|
|
||||||
try { diameter = std::stof(nozzle_info.diameter); } catch (const std::exception &) {}
|
|
||||||
used_extruders_flow.insert({used_extruders[i], {DevNozzle::ToNozzleFlowType(nozzle_info.volume_type), diameter}});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// A by-object plate with several objects produces no plate-level nozzle grouping,
|
|
||||||
// so the used flows are unknown; skip the check for this extruder rather than
|
|
||||||
// blocking the print.
|
|
||||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": no nozzle group result, flow check skipped for extruder " << used_extruders[i];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
used_extruders_flow.insert({used_extruders[i], {DevNozzle::ToNozzleFlowType(nozzle_volume_type), preset_diameter}});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<int> map_extruders = {1, 0};
|
|
||||||
|
|
||||||
const DevNozzleSystem* nozzle_sys = data.Owner() ? data.Owner()->GetNozzleSystem() : nullptr;
|
|
||||||
const bool has_nozzle_rack = nozzle_sys && nozzle_sys->GetNozzleRack() && nozzle_sys->GetNozzleRack()->IsSupported();
|
|
||||||
|
|
||||||
//Only when all preset nozzle types and machine nozzle types are exactly the same, return true.
|
|
||||||
for (auto it = used_extruders_flow.begin(); it != used_extruders_flow.end(); it++) {
|
|
||||||
// The default two extruders are left, right, but the order of the extruders on the machine is right, left.
|
|
||||||
int target_machine_nozzle_id = map_extruders[it->first];
|
|
||||||
NozzleFlowType used_flow = it->second.first;
|
|
||||||
|
|
||||||
// A nozzle-rack extruder swaps nozzles during the print, so the mounted nozzle is irrelevant:
|
|
||||||
// the extruder inventory (mounted nozzle + rack) must hold a nozzle of every needed flow instead.
|
|
||||||
if (has_nozzle_rack && target_machine_nozzle_id == MAIN_EXTRUDER_ID) {
|
|
||||||
if (nozzle_sys->CollectNozzles(target_machine_nozzle_id, used_flow, it->second.second).empty()) {
|
|
||||||
error_message = wxString::Format(_L("The printer has no nozzle matching the slicing file (%s). "
|
|
||||||
"Please install a matching nozzle on the printer, "
|
|
||||||
"or set the corresponding printer preset while slicing."),
|
|
||||||
DevNozzle::GetNozzleFlowTypeStr(used_flow));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
NozzleFlowType machine_flow = data.GetNozzleFlowType(target_machine_nozzle_id);
|
|
||||||
if (machine_flow == NozzleFlowType::NONE_FLOWTYPE)
|
|
||||||
continue; // the mounted nozzle is unknown, nothing to compare against
|
|
||||||
|
|
||||||
if (machine_flow != used_flow) {
|
|
||||||
wxString pos;
|
|
||||||
if (target_machine_nozzle_id == DEPUTY_EXTRUDER_ID)
|
|
||||||
{
|
|
||||||
pos = _L("left nozzle");
|
|
||||||
}
|
|
||||||
else if(target_machine_nozzle_id == MAIN_EXTRUDER_ID)
|
|
||||||
{
|
|
||||||
pos = _L("right nozzle");
|
|
||||||
}
|
|
||||||
|
|
||||||
error_message = wxString::Format(_L("The nozzle flow setting of %s(%s) doesn't match with the slicing file(%s). "
|
|
||||||
"Please make sure the nozzle installed matches with settings in printer, "
|
|
||||||
"then set the corresponding printer preset while slicing."), pos,
|
|
||||||
DevNozzle::GetNozzleFlowTypeStr(machine_flow),
|
|
||||||
DevNozzle::GetNozzleFlowTypeStr(used_flow));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SelectMachineDialog::convert_filament_map_nozzle_id_to_task_nozzle_id(int nozzle_id) const
|
int SelectMachineDialog::convert_filament_map_nozzle_id_to_task_nozzle_id(int nozzle_id) const
|
||||||
{
|
{
|
||||||
if (nozzle_id == (int)FilamentMapNozzleId::NOZZLE_LEFT) {
|
if (nozzle_id == (int)FilamentMapNozzleId::NOZZLE_LEFT) {
|
||||||
@@ -2090,6 +1994,20 @@ void SelectMachineDialog::show_status(PrintDialogStatus status, std::vector<wxSt
|
|||||||
// Advisory slicing/hardware mismatch: allow Send.
|
// Advisory slicing/hardware mismatch: allow Send.
|
||||||
Enable_Refresh_Button(true);
|
Enable_Refresh_Button(true);
|
||||||
Enable_Send_Button(true);
|
Enable_Send_Button(true);
|
||||||
|
} else if (status == PrintDialogStatus::PrintStatusFilamentWarningNozzleHRC) {
|
||||||
|
// Hardness caution against a rack-mapped nozzle: allow Send.
|
||||||
|
Enable_Refresh_Button(true);
|
||||||
|
Enable_Send_Button(true);
|
||||||
|
} else if (status == PrintDialogStatus::PrintStatusNozzleNoMatchedHotends) {
|
||||||
|
Enable_Refresh_Button(true);
|
||||||
|
Enable_Send_Button(false);
|
||||||
|
} else if (status == PrintDialogStatus::PrintStatusNozzleRackMaximumInstalled) {
|
||||||
|
Enable_Refresh_Button(true);
|
||||||
|
Enable_Send_Button(false);
|
||||||
|
} else if (status == PrintDialogStatus::PrintStatusRackReading) {
|
||||||
|
// Rack hotend info is being read: block Send until it finishes.
|
||||||
|
Enable_Refresh_Button(true);
|
||||||
|
Enable_Send_Button(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*enter perpare mode*/
|
/*enter perpare mode*/
|
||||||
@@ -2165,84 +2083,17 @@ static std::unordered_set<int> _get_used_nozzle_idxes()
|
|||||||
return used_nozzle_idxes;
|
return used_nozzle_idxes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// On a hotend-rack printer the right extruder swaps to the required nozzle during the print, so
|
||||||
static bool _is_nozzle_data_valid(MachineObject* obj_, const DevExtderSystem &ext_data)
|
// pre-print checks against its currently mounted nozzle don't apply. Availability of a matching
|
||||||
|
// nozzle (flow and diameter) is checked against the whole inventory (mounted + rack) in
|
||||||
|
// CheckErrorExtruderNozzleWithSlicing() instead.
|
||||||
|
static bool _is_rack_managed_nozzle(const MachineObject* obj, int nozzle_idx)
|
||||||
{
|
{
|
||||||
if (obj_ == nullptr) return false;
|
if (nozzle_idx != MAIN_EXTRUDER_ID)
|
||||||
|
|
||||||
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
|
|
||||||
|
|
||||||
try {
|
|
||||||
PartPlate *cur_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate();
|
|
||||||
auto used_filament_idxs = cur_plate->get_used_filaments(); /*the index is started from 1*/
|
|
||||||
for (int used_filament_idx : used_filament_idxs)
|
|
||||||
{
|
|
||||||
int used_nozzle_idx = cur_plate->get_physical_extruder_by_filament_id(preset_bundle->full_config(), used_filament_idx);
|
|
||||||
if (ext_data.GetNozzleType(used_nozzle_idx) == NozzleType::ntUndefine ||
|
|
||||||
ext_data.GetNozzleDiameter(used_nozzle_idx) <= 0.0f ||
|
|
||||||
ext_data.GetNozzleFlowType(used_nozzle_idx) == NozzleFlowType::NONE_FLOWTYPE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (const std::exception &) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
const DevNozzleSystem* nozzle_sys = obj ? obj->GetNozzleSystem() : nullptr;
|
||||||
}
|
return nozzle_sys && nozzle_sys->GetNozzleRack() && nozzle_sys->GetNozzleRack()->IsSupported();
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************//*
|
|
||||||
* @param tag_nozzle_type -- return the mismatch nozzle type
|
|
||||||
* @param tag_nozzle_diameter -- return the target nozzle_diameter but mismatch
|
|
||||||
* @return is same or not
|
|
||||||
/*************************************************************/
|
|
||||||
static bool _is_same_nozzle_diameters(MachineObject* obj, float &tag_nozzle_diameter, int& mismatch_nozzle_id)
|
|
||||||
{
|
|
||||||
if (obj == nullptr) return false;
|
|
||||||
|
|
||||||
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
|
|
||||||
auto opt_nozzle_diameters = preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter");
|
|
||||||
if (!opt_nozzle_diameters)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
PartPlate* cur_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate();
|
|
||||||
auto used_filament_idxs = cur_plate->get_used_filaments();/*the index is started from 1*/
|
|
||||||
for (int used_filament_idx : used_filament_idxs)
|
|
||||||
{
|
|
||||||
int used_nozzle_idx = cur_plate->get_physical_extruder_by_filament_id(preset_bundle->full_config(), used_filament_idx);
|
|
||||||
if (used_nozzle_idx == -1)
|
|
||||||
{
|
|
||||||
assert(0);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
tag_nozzle_diameter = float(opt_nozzle_diameters->get_at(used_nozzle_idx));
|
|
||||||
auto machine_nozzle_diameter = obj->GetExtderSystem()->GetNozzleDiameter(used_nozzle_idx);
|
|
||||||
|
|
||||||
// Assume matching if diameter is unknown
|
|
||||||
if (machine_nozzle_diameter == 0.0f)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tag_nozzle_diameter != machine_nozzle_diameter)
|
|
||||||
{
|
|
||||||
mismatch_nozzle_id = used_nozzle_idx;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception&)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SelectMachineDialog::is_nozzle_hrc_matched(const DevExtder* extruder, std::string& filament_type) const
|
bool SelectMachineDialog::is_nozzle_hrc_matched(const DevExtder* extruder, std::string& filament_type) const
|
||||||
@@ -3608,6 +3459,228 @@ static wxString _get_nozzle_name(int total_ext_count, int ext_id)
|
|||||||
return _L("nozzle");
|
return _L("nozzle");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nozzle requirements of the sliced plate. key -> physical extruder id, value -> nozzle data.
|
||||||
|
// For example:
|
||||||
|
// {0, {0.4, S_FLOW}}, {1, {0.8, H_FLOW}}
|
||||||
|
// {0, {0.4, S_FLOW}}, {0, {0.4, H_FLOW}} // hybrid
|
||||||
|
static std::unordered_multimap<int, NozzleDef> s_get_slicing_extuder_nozzles()
|
||||||
|
{
|
||||||
|
std::unordered_multimap<int, NozzleDef> used_extuder_nozzles;
|
||||||
|
|
||||||
|
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
|
||||||
|
if (!preset_bundle) {
|
||||||
|
return used_extuder_nozzles;
|
||||||
|
}
|
||||||
|
|
||||||
|
PartPlate* cur_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate();
|
||||||
|
if (!cur_plate) {
|
||||||
|
return used_extuder_nozzles;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto opt_nozzle_diameters = preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter");
|
||||||
|
if (!opt_nozzle_diameters) {
|
||||||
|
return used_extuder_nozzles;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto nozzle_volume_type_opt = dynamic_cast<const ConfigOptionEnumsGeneric*>(preset_bundle->project_config.option("nozzle_volume_type"));
|
||||||
|
if (!nozzle_volume_type_opt) {
|
||||||
|
return used_extuder_nozzles;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto nozzle_group_res = DevUtilBackend::GetNozzleGroupResult(wxGetApp().plater());
|
||||||
|
if (nozzle_group_res && nozzle_group_res->is_support_dynamic_nozzle_map() && nozzle_volume_type_opt->values.size() == 2) {
|
||||||
|
const auto& used_nozzles = nozzle_group_res->get_used_nozzles_in_extruder();
|
||||||
|
for (const auto& used_nozzle : used_nozzles) {
|
||||||
|
NozzleDef nozzle_data;
|
||||||
|
nozzle_data.nozzle_diameter = std::stof(used_nozzle.diameter);
|
||||||
|
nozzle_data.nozzle_flow_type = DevNozzle::ToNozzleFlowType(used_nozzle.volume_type);
|
||||||
|
if (used_nozzle.extruder_id == 0) {
|
||||||
|
used_extuder_nozzles.insert({ DEPUTY_EXTRUDER_ID, nozzle_data });
|
||||||
|
} else if (used_nozzle.extruder_id == 1) {
|
||||||
|
used_extuder_nozzles.insert({ MAIN_EXTRUDER_ID, nozzle_data });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return used_extuder_nozzles;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& used_filament_idxs = cur_plate->get_used_filaments(); /*the index is started from 1*/
|
||||||
|
for (int used_filament_idx : used_filament_idxs) {
|
||||||
|
int physical_idx = cur_plate->get_physical_extruder_by_filament_id(preset_bundle->full_config(), used_filament_idx);
|
||||||
|
int logic_extruder_idx = cur_plate->get_logical_extruder_by_filament_id(preset_bundle->full_config(), used_filament_idx);
|
||||||
|
if (physical_idx < 0 || logic_extruder_idx < 0) {
|
||||||
|
assert(0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
NozzleDef nozzle_data;
|
||||||
|
nozzle_data.nozzle_diameter = float(opt_nozzle_diameters->get_at(logic_extruder_idx));
|
||||||
|
nozzle_data.nozzle_flow_type = NozzleFlowType::S_FLOW;// default value
|
||||||
|
|
||||||
|
auto volume_type = (NozzleVolumeType)nozzle_volume_type_opt->get_at(logic_extruder_idx);
|
||||||
|
if (volume_type == NozzleVolumeType::nvtHybrid) {
|
||||||
|
if (used_extuder_nozzles.find(physical_idx) != used_extuder_nozzles.end()) {
|
||||||
|
continue;// already collected
|
||||||
|
}
|
||||||
|
|
||||||
|
// A hybrid extruder prints with a mix of nozzle flows: collect the flow of each
|
||||||
|
// physically used nozzle instead of forcing a single one.
|
||||||
|
if (nozzle_group_res) {
|
||||||
|
for (const auto& nozzle_info : nozzle_group_res->get_used_nozzles_in_extruder(logic_extruder_idx)) {
|
||||||
|
nozzle_data.nozzle_flow_type = DevNozzle::ToNozzleFlowType(nozzle_info.volume_type);
|
||||||
|
used_extuder_nozzles.insert({ physical_idx, nozzle_data });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Orca: a by-object plate with several objects produces no plate-level nozzle
|
||||||
|
// grouping, so the used flows are unknown; skip the check for this extruder
|
||||||
|
// rather than blocking the print.
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": no nozzle group result, nozzle check skipped for extruder " << logic_extruder_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nozzle_data.nozzle_flow_type = DevNozzle::ToNozzleFlowType(volume_type);
|
||||||
|
used_extuder_nozzles.insert({ physical_idx, nozzle_data });
|
||||||
|
};
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "exception: " << e.what();
|
||||||
|
}
|
||||||
|
|
||||||
|
return used_extuder_nozzles;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SelectMachineDialog::CheckErrorRackStatus(MachineObject* obj_)
|
||||||
|
{
|
||||||
|
if (!obj_) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rack = obj_->GetNozzleSystem()->GetNozzleRack();
|
||||||
|
if (!rack->IsSupported()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rack->GetReadingCount() > 0) {
|
||||||
|
const wxString& msg = wxString::Format(_L("Refreshing information of hotends(%d/%d)."), rack->GetReadingIdx(), rack->GetReadingCount());
|
||||||
|
show_status(PrintDialogStatus::PrintStatusRackReading, { msg + " " + _L("Please wait a moment...") });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare the extruder nozzle info between slicing file and installed on printer
|
||||||
|
bool SelectMachineDialog::CheckErrorExtruderNozzleWithSlicing(MachineObject* obj_)
|
||||||
|
{
|
||||||
|
if (!obj_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& ext_sys = obj_->GetExtderSystem();
|
||||||
|
const auto& nozzle_sys = obj_->GetNozzleSystem();
|
||||||
|
if (m_print_type == FROM_NORMAL) {
|
||||||
|
const auto& slicing_ext_nozzles = s_get_slicing_extuder_nozzles();
|
||||||
|
for (auto slicing_ext_nozzle : slicing_ext_nozzles) {
|
||||||
|
int slicing_ext_idx = slicing_ext_nozzle.first;
|
||||||
|
auto slicing_ext = slicing_ext_nozzle.second;
|
||||||
|
auto installed_ext_nozzle = nozzle_sys->GetExtNozzle(slicing_ext_idx);
|
||||||
|
|
||||||
|
// No need to check the right extruder's mounted nozzle when using a nozzle rack: the
|
||||||
|
// extruder swaps nozzles during the print, so the inventory (mounted + rack) must hold
|
||||||
|
// a matching nozzle instead.
|
||||||
|
if (slicing_ext_idx == MAIN_EXTRUDER_ID && nozzle_sys->GetNozzleRack()->IsSupported()) {
|
||||||
|
if (nozzle_sys->CollectNozzles(MAIN_EXTRUDER_ID, slicing_ext.nozzle_flow_type, slicing_ext.nozzle_diameter).empty()) {
|
||||||
|
wxString slicing_nozzle_str = DevNozzle::GetNozzleFlowTypeStr(slicing_ext.nozzle_flow_type);
|
||||||
|
if (slicing_ext.nozzle_diameter > 0.0f)
|
||||||
|
slicing_nozzle_str += wxString::Format(" %.1fmm", slicing_ext.nozzle_diameter);
|
||||||
|
wxString msg = wxString::Format(_L("The printer has no nozzle matching the slicing file (%s)."), slicing_nozzle_str);
|
||||||
|
msg += " ";
|
||||||
|
if (nozzle_sys->GetNozzleRack()->GetCaliStatus() != DevNozzleRack::Rack_CALI_OK)
|
||||||
|
msg += _L("Please complete the hotend rack setup and try again.");
|
||||||
|
else if (nozzle_sys->HasUnknownNozzles() || nozzle_sys->HasUnreliableNozzles())
|
||||||
|
msg += _L("Please refresh the nozzle information and try again.");
|
||||||
|
else
|
||||||
|
msg += _L("Please install a matching nozzle in the hotend rack, or set the corresponding printer preset while slicing.");
|
||||||
|
|
||||||
|
show_status(PrintDialogStatus::PrintStatusNozzleNoMatchedHotends, { msg });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A nozzle swap needs a free rack slot to stow the mounted nozzle first.
|
||||||
|
if (nozzle_sys->IsRackMaximumInstalled()) {
|
||||||
|
show_status(PrintDialogStatus::PrintStatusNozzleRackMaximumInstalled,
|
||||||
|
{ _L("The toolhead and hotend rack are full. Please remove at least one hotend before printing.") });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check nozzle data valid
|
||||||
|
{
|
||||||
|
if (installed_ext_nozzle.GetNozzleType() == NozzleType::ntUndefine ||
|
||||||
|
installed_ext_nozzle.GetNozzleDiameter() <= 0.0f) {
|
||||||
|
show_status(PrintDialogStatus::PrintStatusNozzleDataInvalid);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj_->is_nozzle_flow_type_supported() &&
|
||||||
|
installed_ext_nozzle.GetNozzleFlowType() == NozzleFlowType::NONE_FLOWTYPE) {
|
||||||
|
show_status(PrintDialogStatus::PrintStatusNozzleDataInvalid);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check nozzle flow type
|
||||||
|
{
|
||||||
|
if (obj_->is_nozzle_flow_type_supported() && slicing_ext.nozzle_flow_type != installed_ext_nozzle.GetNozzleFlowType()) {
|
||||||
|
const wxString& pos = _get_nozzle_name(ext_sys->GetTotalExtderCount(), slicing_ext_idx);
|
||||||
|
const wxString& installed_nozzle_str = installed_ext_nozzle.GetNozzleFlowTypeStr();
|
||||||
|
const wxString& slicing_nozzle_str = DevNozzle::GetNozzleFlowTypeStr(slicing_ext.nozzle_flow_type);
|
||||||
|
wxString error_message = wxString::Format(_L("The nozzle flow setting of %s(%s) doesn't match with the slicing file(%s). "
|
||||||
|
"Please make sure the nozzle installed matches with settings in printer, "
|
||||||
|
"then set the corresponding printer preset while slicing."),
|
||||||
|
pos, installed_nozzle_str, slicing_nozzle_str);
|
||||||
|
|
||||||
|
std::vector<wxString> params{ error_message };
|
||||||
|
params.emplace_back(_L("Tips: If you changed your nozzle of your printer lately, Please go to 'Device -> Printer parts' to change your nozzle setting."));
|
||||||
|
show_status(PrintDialogStatus::PrintStatusNozzleMatchInvalid, params);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check nozzle diameter
|
||||||
|
{
|
||||||
|
if (slicing_ext.nozzle_diameter != installed_ext_nozzle.GetNozzleDiameter()) {
|
||||||
|
std::vector<wxString> msg_params;
|
||||||
|
if (ext_sys->GetTotalExtderCount() == 2) {
|
||||||
|
const wxString& mismatch_nozzle_str = _get_nozzle_name(ext_sys->GetTotalExtderCount(), slicing_ext_idx);
|
||||||
|
const wxString& nozzle_message = wxString::Format(_L("The %s diameter(%.1fmm) of current printer doesn't match with the slicing file (%.1fmm). "
|
||||||
|
"Please make sure the nozzle installed matches with settings in printer, then set the "
|
||||||
|
"corresponding printer preset when slicing."),
|
||||||
|
mismatch_nozzle_str, installed_ext_nozzle.GetNozzleDiameter(), slicing_ext.nozzle_diameter);
|
||||||
|
msg_params.emplace_back(nozzle_message);
|
||||||
|
} else {
|
||||||
|
const wxString& nozzle_message = wxString::Format(_L("The current nozzle diameter (%.1fmm) doesn't match with the slicing file (%.1fmm). "
|
||||||
|
"Please make sure the nozzle installed matches with settings in printer, then set the "
|
||||||
|
"corresponding printer preset when slicing."),
|
||||||
|
installed_ext_nozzle.GetNozzleDiameter(), slicing_ext.nozzle_diameter);
|
||||||
|
msg_params.emplace_back(nozzle_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
msg_params.emplace_back(_L("Tips: If you changed your nozzle of your printer lately, Please go to 'Device -> Printer parts' to change your nozzle setting."));
|
||||||
|
show_status(PrintDialogStatus::PrintStatusNozzleDiameterMismatch, msg_params);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static wxString _get_ext_loc_str(const std::unordered_set<int>& extruders, int total_ext_num)
|
static wxString _get_ext_loc_str(const std::unordered_set<int>& extruders, int total_ext_num)
|
||||||
{
|
{
|
||||||
assert(!extruders.empty());
|
assert(!extruders.empty());
|
||||||
@@ -3810,59 +3883,24 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_)
|
|||||||
const auto &full_config = wxGetApp().preset_bundle->full_config();
|
const auto &full_config = wxGetApp().preset_bundle->full_config();
|
||||||
size_t nozzle_nums = full_config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
size_t nozzle_nums = full_config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
||||||
|
|
||||||
/*the nozzle type of preset and machine are different*/
|
// Filament Track Switch: warn on a slicing/hardware mismatch, and block Send when dynamic
|
||||||
if (nozzle_nums > 1 && m_print_type == FROM_NORMAL) {
|
// nozzle mapping needs a switch that isn't installed or set up. No-op for printers without one.
|
||||||
if (!_is_nozzle_data_valid(obj_, *obj_->GetExtderSystem())) {
|
if (!CheckErrorDynamicSwitchNozzle(obj_)) return;
|
||||||
show_status(PrintDialogStatus::PrintStatusNozzleDataInvalid);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wxString error_message;
|
// Block Send while a rack printer is still reading its hotend information.
|
||||||
if (!is_nozzle_type_match(*obj_->GetExtderSystem(), error_message)) {
|
if (!CheckErrorRackStatus(obj_)) return;
|
||||||
std::vector<wxString> params{error_message};
|
|
||||||
params.emplace_back(_L("Tips: If you changed your nozzle of your printer lately, Please go to 'Device -> Printer parts' to change your nozzle setting."));
|
// Compare the slicing file's nozzle requirements (validity, flow, diameter) against the printer.
|
||||||
show_status(PrintDialogStatus::PrintStatusNozzleMatchInvalid, params);
|
if (!CheckErrorExtruderNozzleWithSlicing(obj_)) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check nozzle type and diameter
|
|
||||||
if (m_print_type == PrintFromType::FROM_NORMAL)
|
if (m_print_type == PrintFromType::FROM_NORMAL)
|
||||||
{
|
{
|
||||||
int mismatch_nozzle_id = 0;
|
// Orca: blocking hardness gate on the mounted nozzles; the rack extruder is instead judged
|
||||||
float nozzle_diameter = 0;
|
// per dispatch-mapped nozzle in the blacklist loop below, as a non-blocking caution.
|
||||||
if (!_is_same_nozzle_diameters(obj_, nozzle_diameter, mismatch_nozzle_id))
|
|
||||||
{
|
|
||||||
std::vector<wxString> msg_params;
|
|
||||||
if (obj_->GetExtderSystem()->GetTotalExtderCount() == 2) {
|
|
||||||
wxString mismatch_nozzle_str;
|
|
||||||
if (mismatch_nozzle_id == MAIN_EXTRUDER_ID) {
|
|
||||||
mismatch_nozzle_str = _L("right nozzle");
|
|
||||||
} else {
|
|
||||||
mismatch_nozzle_str = _L("left nozzle");
|
|
||||||
}
|
|
||||||
|
|
||||||
const wxString &nozzle_config = wxString::Format(_L("The %s diameter(%.1fmm) of current printer doesn't match with the slicing file (%.1fmm). "
|
|
||||||
"Please make sure the nozzle installed matches with settings in printer, then set the "
|
|
||||||
"corresponding printer preset when slicing."),
|
|
||||||
mismatch_nozzle_str, obj_->GetExtderSystem()->GetNozzleDiameter(mismatch_nozzle_id), nozzle_diameter);
|
|
||||||
msg_params.emplace_back(nozzle_config);
|
|
||||||
} else {
|
|
||||||
const wxString &nozzle_config = wxString::Format(_L("The current nozzle diameter (%.1fmm) doesn't match with the slicing file (%.1fmm). "
|
|
||||||
"Please make sure the nozzle installed matches with settings in printer, then set the "
|
|
||||||
"corresponding printer preset when slicing."),
|
|
||||||
obj_->GetExtderSystem()->GetNozzleDiameter(0), nozzle_diameter);
|
|
||||||
msg_params.emplace_back(nozzle_config);
|
|
||||||
}
|
|
||||||
|
|
||||||
msg_params.emplace_back(_L("Tips: If you changed your nozzle of your printer lately, Please go to 'Device -> Printer parts' to change your nozzle setting."));
|
|
||||||
show_status(PrintDialogStatus::PrintStatusNozzleDiameterMismatch, msg_params);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto &used_nozzle_idxes = _get_used_nozzle_idxes();
|
const auto &used_nozzle_idxes = _get_used_nozzle_idxes();
|
||||||
for (const auto &extder : obj_->GetExtderSystem()->GetExtruders()) {
|
for (const auto &extder : obj_->GetExtderSystem()->GetExtruders()) {
|
||||||
if (used_nozzle_idxes.count(extder.GetNozzleId()) == 0) { continue; }
|
if (used_nozzle_idxes.count(extder.GetNozzleId()) == 0) { continue; }
|
||||||
|
if (_is_rack_managed_nozzle(obj_, extder.GetNozzleId())) { continue; }
|
||||||
|
|
||||||
std::string filament_type;
|
std::string filament_type;
|
||||||
if (!is_nozzle_hrc_matched(&extder, filament_type)) {
|
if (!is_nozzle_hrc_matched(&extder, filament_type)) {
|
||||||
@@ -3875,10 +3913,6 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filament Track Switch: warn on a slicing/hardware mismatch, and block Send when dynamic
|
|
||||||
// nozzle mapping needs a switch that isn't installed or set up. No-op for printers without one.
|
|
||||||
if (!CheckErrorDynamicSwitchNozzle(obj_)) return;
|
|
||||||
|
|
||||||
// Rack print-dispatch nozzle mapping (dynamic V1): request/await the printer's mapping and block
|
// 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.
|
// Send while it computes. No-op for non-rack printers.
|
||||||
if (!CheckErrorSyncNozzleMappingResultV1(obj_)) return;
|
if (!CheckErrorSyncNozzleMappingResultV1(obj_)) return;
|
||||||
@@ -3968,6 +4002,25 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_)
|
|||||||
show_status(PrintDialogStatus::PrintStatusHasFilamentInBlackListWarning, {item.info_msg}, item.wiki_url);/** warning check **/
|
show_status(PrintDialogStatus::PrintStatusHasFilamentInBlackListWarning, {item.info_msg}, item.wiki_url);/** warning check **/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A rack-managed nozzle skipped the mounted-nozzle hardness gate above; judge the
|
||||||
|
// filament's hardness against the nozzle the print-dispatch mapping assigned. Caution
|
||||||
|
// only: the printer picks the swap target, so Send stays enabled.
|
||||||
|
if (n < mapped_nozzles.size()) {
|
||||||
|
const DevNozzle &nozzle = mapped_nozzles[n];
|
||||||
|
if (_is_rack_managed_nozzle(obj_, nozzle.GetExtruderId()) && nozzle.GetNozzleType() != NozzleType::ntUndefine) {
|
||||||
|
const int nozzle_hrc = Print::get_hrc_by_nozzle_type(nozzle.GetNozzleType());
|
||||||
|
const int filament_hrc = wxGetApp().preset_bundle->get_required_hrc_by_filament_type(filament_type);
|
||||||
|
if (abs(filament_hrc) > abs(nozzle_hrc)) {
|
||||||
|
const int pos_id = nozzle.GetNozzlePosId();
|
||||||
|
const wxString nozzle_str = (pos_id < 0x10) ? wxString("R") : wxString::Format("R%d", pos_id - 0x10 + 1);
|
||||||
|
show_status(PrintDialogStatus::PrintStatusFilamentWarningNozzleHRC,
|
||||||
|
{wxString::Format(_L("The hardness of current material (%s) exceeds the hardness of %s(%s). It may cause nozzle wear, "
|
||||||
|
"leading to material leakage and unstable flow. Please exercise caution when using it."),
|
||||||
|
filament_type, nozzle_str, format_steel_name(nozzle.GetNozzleType()))});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -508,7 +508,6 @@ public:
|
|||||||
bool build_nozzles_info(std::string& nozzles_info);
|
bool build_nozzles_info(std::string& nozzles_info);
|
||||||
bool can_hybrid_mapping(DevExtderSystem data);
|
bool can_hybrid_mapping(DevExtderSystem data);
|
||||||
void auto_supply_with_ext(std::vector<DevAmsTray> slots);
|
void auto_supply_with_ext(std::vector<DevAmsTray> slots);
|
||||||
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;
|
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.
|
// Physical nozzle(s) a mapped filament (by filament index / FilamentInfo::id) prints on.
|
||||||
@@ -520,6 +519,12 @@ public:
|
|||||||
// printer. Empty for printers with neither (the card then shows no nozzle row).
|
// printer. Empty for printers with neither (the card then shows no nozzle row).
|
||||||
wxString get_mapped_nozzle_str(int fila_id) const;
|
wxString get_mapped_nozzle_str(int fila_id) const;
|
||||||
|
|
||||||
|
// Block Send while a rack printer is still reading its hotend information.
|
||||||
|
bool CheckErrorRackStatus(MachineObject* obj_);
|
||||||
|
// Compare the slicing file's nozzle requirements (validity, flow, diameter) against the
|
||||||
|
// printer; the rack extruder is checked against its whole inventory (mounted + rack).
|
||||||
|
bool CheckErrorExtruderNozzleWithSlicing(MachineObject* obj_);//return true if no errors
|
||||||
|
|
||||||
// Rack print-dispatch nozzle mapping (H2C): request the printer's auto-mapping and consume the
|
// 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.
|
// result, gating the Send button while the printer computes. Both are no-ops for non-rack printers.
|
||||||
bool CheckErrorSyncNozzleMappingResultV1(MachineObject* obj_);
|
bool CheckErrorSyncNozzleMappingResultV1(MachineObject* obj_);
|
||||||
|
|||||||
Reference in New Issue
Block a user