Files
OrcaSlicer/src/slic3r/GUI/DeviceCore/DevMappingNozzle.cpp
T
HanifKoh 4895bc03b4 Remove Unused Project Includes and Forward-Declare Where a Type Is Only Referenced (#16099)
* Remove Unused Project Includes and Forward-Declare Where a Type Is Only Referenced

Generated with include-what-you-use and applied conservatively. Only OrcaSlicer's own headers, the ones under src/ and tests/, are removed or forward-declared; standard-library and third-party includes are left alone. An include is removed only when both the Release and the Debug configuration leave it unused, never from inside a conditional block, and never from a file with platform-specific blocks, which only gain includes. Files whose only use of a header sits behind a feature or debug macro (libvgcode's OpenGL ES and marker code, the ARACHNE/TESTS_EXPORT_SVGS debug output) keep their includes.

clonable_ptr.hpp gains #pragma once; it had no include guard and was only safe while Config.hpp was its sole includer.

* Remove Unused Project Includes From Files With Platform-Specific Code

A Linux include-what-you-use run cannot see the code inside _WIN32, __APPLE__ or __linux__ blocks, so its verdict is only taken where nothing the removed header declares, directly or through what it includes, is named inside those blocks. Removals also have to hold in both the Release and Debug configuration and never touch a line inside a conditional block.

* Restore the libslic3r Precompiled Header and Direct Includes Lost in the Platform Pass

The platform-file pass treated pchheader.hpp as an ordinary header and
emptied it, and left GUI_Preview.hpp and 14 other files relying on
headers they no longer reached directly.

* Restore MainFrame.hpp in ParamsDialog.cpp for the Windows-Only Reparent Call

* Include Headers That Files Reached Through Ones the Cleanup Removed

* Drop Includes Duplicated by the Cleanup or by Main's Own Additions

* Leave PreciseSeam.cpp as Main Has It After the Precise Seam Rework
2026-10-05 16:47:17 +08:00

468 lines
18 KiB
C++

#include "DevMappingNozzle.h"
#include "DevNozzleRack.h"
#include "DevNozzleSystem.h"
#include "DevUtil.h"
#include "DevUtilBackend.h"
#include "json_diff.hpp"
#include "libslic3r/MultiNozzleUtils.hpp"
#include "slic3r/GUI/DeviceManager.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/BackgroundSlicingProcess.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include <algorithm>
#include <boost/lexical_cast.hpp>
#include <boost/log/trivial.hpp>
#include "libslic3r/GCode/GCodeProcessor.hpp"
#include <cassert>
#include <exception>
#include "libslic3r/PrintConfig.hpp"
#include <nlohmann/json.hpp>
#include <vector>
#include <string>
#include <unordered_set>
#include "slic3r/GUI/DeviceCore/DevDefs.h"
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/ProjectTask.hpp"
using namespace nlohmann;
namespace Slic3r {
void MachineObject::clear_auto_nozzle_mapping()
{
if (m_nozzle_mapping_ptr) {
m_nozzle_mapping_ptr->Clear();
}
}
// get auto nozzle mapping through AP
// warnings:
// (1) the fila_id here is 1 based index
// (2) the AP wants the diameter string with 2 decimal places
int DevNozzleMappingCtrl::CtrlGetAutoNozzleMappingV0(Slic3r::GUI::Plater* plater,
const std::vector<FilamentInfo>& ams_mapping,
int flow_cali_opt, int pa_value)
{
Clear();
m_plater = plater;
if (!plater) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": plater is nullptr";
return -1;
}
GCodeProcessorResult* gcode_result = plater->background_process().get_current_gcode_result();
if (!gcode_result) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": gcode_result is nullptr";
return -1;
}
const auto& result = gcode_result->nozzle_group_result;
if (!result) {
assert(false && "gcode_result->nozzle_group_result");
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": gcode_result->nozzle_group_result is NULL";
return -1;
}
if (ams_mapping.empty()) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": the ams mapping is empty";
return -1;// the ams mapping is empty
}
json command_jj;
command_jj["print"]["command"] = "get_auto_nozzle_mapping";
m_sequence_id = std::to_string(MachineObject::m_sequence_id++);
command_jj["print"]["sequence_id"] = m_sequence_id;
command_jj["print"]["calibration"] = flow_cali_opt;
command_jj["print"]["extrude_cali_manual_mode"] = pa_value;
// filament seq
json filament_seq_jj;
int max_fila_id = 0;
std::unordered_set<int> filaid_set;
for (int idx = 0; idx < gcode_result->filament_change_sequence.size(); idx++) {
int fila_id = gcode_result->filament_change_sequence[idx];
if (filaid_set.count(fila_id) == 0) {
filaid_set.insert(fila_id);
filament_seq_jj[fila_id + 1] = idx;
max_fila_id = std::max(max_fila_id, fila_id + 1);
}
}
for (int fila_id = 0; fila_id <= max_fila_id; fila_id++) {
if (filament_seq_jj[fila_id].is_null()) {
filament_seq_jj[fila_id] = -1;// fill the used fila_id with -1
}
}
command_jj["print"]["filament_seq"] = filament_seq_jj;
// ams mapping
std::vector<int> ams_mapping_vec(33, 0xFFFF);/* AP ask to fill them*/
for (auto item : ams_mapping) {
try {
int ams_id = stoi(item.ams_id);
int slot_id = !item.slot_id.empty() ? stoi(item.slot_id) : 0;
ams_mapping_vec[item.id + 1] = (ams_id << 8) | slot_id;/*using ams_id << 8 | slot_id*/
} catch (std::exception& e) {
assert(false && "invalid ams_id or slot_id");
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " exception: " << e.what();
ams_mapping_vec[item.id + 1] = item.tray_id;
}
}
command_jj["print"]["ams_mapping"] = ams_mapping_vec;
// filament info
json filament_info_jj;
for (int fila_id = 0; fila_id < ams_mapping.size(); fila_id++) {
const auto& fila = ams_mapping[fila_id];
const auto& nozzle_list = result->get_nozzles_for_filament(fila.id);
if (nozzle_list.empty()) {
assert(false && "nozzle_list should not be empty");
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "nozzle_list should not be nullptr";
continue;
}
for (const auto& nozzle_info : nozzle_list) {
json fila_item_jj;
fila_item_jj["id"] = (fila.id + 1);
fila_item_jj["direction"] = (nozzle_info.extruder_id == LOGIC_L_EXTRUDER_ID ? 1 : 2);
fila_item_jj["group"] = nozzle_info.group_id;
fila_item_jj["nozzle_d"] = s_get_diameter_str(nozzle_info.diameter);
fila_item_jj["nozzle_v"] = (nozzle_info.volume_type == NozzleVolumeType::nvtHighFlow) ? "High Flow" : "Standard";
fila_item_jj["cate"] = fila.filament_id;
fila_item_jj["color"] = fila.color;
filament_info_jj.push_back(fila_item_jj);
}
}
command_jj["print"]["fila_info"] = filament_info_jj;
// nozzle info
json nozzle_info_jj;
const auto& nozzle_system = m_obj->GetNozzleSystem();
const auto& extruder_nozzles = nozzle_system->GetExtNozzles();
for (const auto& nozzle : extruder_nozzles) {
if (nozzle.second.IsNormal()) {
json nozzle_item_jj;
nozzle_item_jj["pos"] = nozzle.second.GetNozzleId();
if (nozzle_system->GetReplaceNozzleTar().has_value() && nozzle_item_jj["pos"] == MAIN_EXTRUDER_ID) {
nozzle_item_jj["pos"] = nozzle_system->GetReplaceNozzleTar().value();// special case of tar_id. see protocol definition
}
nozzle_item_jj["nozzle_d"] = s_get_diameter_str(nozzle.second.GetNozzleDiameter());
nozzle_item_jj["nozzle_v"] = DevNozzle::ToNozzleFlowString(nozzle.second.GetNozzleFlowType());
nozzle_item_jj["wear"] = nozzle.second.GetNozzleWear();
nozzle_item_jj["cate"] = nozzle.second.GetFilamentId();
nozzle_item_jj["color"] = nozzle.second.GetFilamentColor();
nozzle_info_jj.push_back(nozzle_item_jj);
}
}
const auto& rack_nozzles = nozzle_system->GetNozzleRack()->GetRackNozzles();
for (const auto& nozzle : rack_nozzles) {
if (nozzle.second.IsNormal()) {
json nozzle_item_jj;
nozzle_item_jj["pos"] = (nozzle.second.GetNozzleId() + 0x10);
if (nozzle_system->GetReplaceNozzleTar().has_value() && nozzle_item_jj["pos"] == MAIN_EXTRUDER_ID) {
nozzle_item_jj["pos"] = nozzle_system->GetReplaceNozzleTar().value();// special case of tar_id. see protocol definition
}
nozzle_item_jj["nozzle_d"] = s_get_diameter_str(nozzle.second.GetNozzleDiameter());
nozzle_item_jj["nozzle_v"] = DevNozzle::ToNozzleFlowString(nozzle.second.GetNozzleFlowType());
nozzle_item_jj["wear"] = nozzle.second.GetNozzleWear();
nozzle_item_jj["cate"] = nozzle.second.GetFilamentId();
nozzle_item_jj["color"] = nozzle.second.GetFilamentColor();
nozzle_info_jj.push_back(nozzle_item_jj);
}
}
command_jj["print"]["nozzle_info"] = nozzle_info_jj;
m_req_version = NozzleMappingVersion::Version_V0;
return m_obj->publish_json(command_jj);
}
int DevNozzleMappingCtrl::CtrlGetAutoNozzleMappingV1(Slic3r::GUI::Plater* plater)
{
Clear();
m_plater = plater;
if (!m_plater) {
return -1;
}
auto nozzle_group_res = DevUtilBackend::GetNozzleGroupResult(m_plater);
if (!nozzle_group_res) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": gcode_result->nozzle_group_result is NULL";
return -1;
}
json command_jj;
command_jj["print"]["command"] = "get_auto_nozzle_mapping";
m_sequence_id = std::to_string(MachineObject::m_sequence_id++);
command_jj["print"]["sequence_id"] = m_sequence_id;
json nozzle_group_info_jj;
std::unordered_set<int> used_logic_groups;
const auto& used_logic_nozzles = nozzle_group_res->get_used_nozzles_in_extruder();
for (const auto& used_logic_nozzle : used_logic_nozzles) {
if (used_logic_groups.count(used_logic_nozzle.group_id) != 0) {
continue;
}
used_logic_groups.insert(used_logic_nozzle.group_id);
json nozzle_info;
nozzle_info["id"] = used_logic_nozzle.group_id;
nozzle_info["ext"] = (used_logic_nozzle.extruder_id + 1);
try {
nozzle_info["dia"] = std::stof(used_logic_nozzle.diameter);
} catch(const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": e=" << e.what();
continue;
}
if (used_logic_nozzle.volume_type == NozzleVolumeType::nvtHighFlow) {
nozzle_info["vol"] = "High Flow";
} else if(used_logic_nozzle.volume_type == NozzleVolumeType::nvtStandard){
nozzle_info["vol"] = "Standard";
} else if(used_logic_nozzle.volume_type == NozzleVolumeType::nvtTPUHighFlow){
nozzle_info["vol"] = "TPU Flow";
} else {
assert(0);
continue;
}
nozzle_group_info_jj.push_back(nozzle_info);
}
command_jj["print"]["group_info"] = nozzle_group_info_jj;
m_req_version = NozzleMappingVersion::Version_V1;
command_jj["print"]["version"] = (int)m_req_version;
return m_obj->publish_json(command_jj);
}
void DevNozzleMappingCtrl::ParseAutoNozzleMapping(const json& print_jj)
{
if (print_jj.contains("command") && print_jj["command"].get<string>() == "get_auto_nozzle_mapping") {
if (print_jj.contains("sequence_id") && print_jj["sequence_id"] == m_sequence_id) {
Clear();
DevJsonValParser::ParseVal(print_jj, "result", m_result);
DevJsonValParser::ParseVal(print_jj, "reason", m_mqtt_reason);
DevJsonValParser::ParseVal(print_jj, "errno", m_errno);
DevJsonValParser::ParseVal(print_jj, "detail", m_detail_json);
DevJsonValParser::ParseVal(print_jj, "type", m_type);
DevJsonValParser::ParseVal(print_jj, "version", m_ack_version);
if (print_jj.contains("mapping")) {
m_nozzle_mapping_json = print_jj["mapping"];
const auto& mapping = print_jj["mapping"].get<std::vector<int>>();
for (int fila_id = 0; fila_id < mapping.size(); ++fila_id) {
m_nozzle_mapping[fila_id] = mapping[fila_id];
}
}
m_flush_weight_base = GetFlushWeight(m_obj);
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": get_auto_nozzle_mapping: " << m_result;
}
}
}
void DevNozzleMappingCtrl::Clear()
{
m_sequence_id.clear();
m_result.clear();
m_mqtt_reason.clear();
m_type.clear();
m_errno = 0;
m_detail_msg.clear();
m_detail_json.clear();
m_nozzle_mapping.clear();
m_nozzle_mapping_json.clear();
m_flush_weight_base = -1;
m_flush_weight_current = -1;
m_ack_version = NozzleMappingVersion::Version_V0;
}
void DevNozzleMappingCtrl::SetManualNozzleMappingByFila(int fila_id, int nozzle_pos_id)
{
if (nozzle_pos_id == MAIN_EXTRUDER_ID && m_obj->GetNozzleSystem()->GetReplaceNozzleTar().has_value()){
nozzle_pos_id = m_obj->GetNozzleSystem()->GetReplaceNozzleTar().value();// special case of tar_id. see protocol definition
}
if (m_nozzle_mapping[fila_id] != nozzle_pos_id) {
m_nozzle_mapping[fila_id] = nozzle_pos_id;
m_flush_weight_current = GetFlushWeight(m_obj);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": fila_id=" << fila_id << ", nozzle_pos_id=" << nozzle_pos_id;
}
try {
auto mapping = m_nozzle_mapping_json.get<std::vector<int>>();
if (mapping.at(fila_id) != nozzle_pos_id) {
mapping[fila_id] = nozzle_pos_id;
m_nozzle_mapping_json = mapping;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": updated to " << m_nozzle_mapping_json.dump();
}
} catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": exception: " << e.what();
};
}
static std::unordered_set<int> sGetLogicExtruders(const std::vector<MultiNozzleUtils::NozzleInfo>& nozzle_infos)
{
std::unordered_set<int> extruder_ids;
for (const auto& nozzle : nozzle_infos) {
if (nozzle.extruder_id >= 0) {
extruder_ids.insert(nozzle.extruder_id);
}
}
return extruder_ids;
};
std::vector<int> DevNozzleMappingCtrl::GetMappedNozzlePosVecByFilaId(int fila_id) const
{
std::vector<int> physic_nozzle_pos_vec;
auto nozzle_group_res = DevUtilBackend::GetNozzleGroupResult(m_plater);
if (!nozzle_group_res) {
return physic_nozzle_pos_vec;
}
const auto& used_logic_nozzles = nozzle_group_res->get_nozzles_for_filament(fila_id);
if (m_ack_version == NozzleMappingVersion::Version_V0) {
if (auto iter = m_nozzle_mapping.find(fila_id); iter != m_nozzle_mapping.end()) {
if (m_obj->GetNozzleSystem()->GetReplaceNozzleTar().has_value() &&
iter->second == m_obj->GetNozzleSystem()->GetReplaceNozzleTar().value()) {
physic_nozzle_pos_vec.push_back(MAIN_EXTRUDER_ID);
} else {
physic_nozzle_pos_vec.push_back(iter->second);
}
} else {
const auto& used_logic_extruders = sGetLogicExtruders(used_logic_nozzles);
if (used_logic_extruders.size() == 1 && *used_logic_extruders.begin() == LOGIC_L_EXTRUDER_ID) {
physic_nozzle_pos_vec.push_back(1);
return physic_nozzle_pos_vec; // only left extruder is used
}
}
} else if (m_ack_version == NozzleMappingVersion::Version_V1) {
for (const auto& used_logic_nozzle : used_logic_nozzles) {
if (auto iter = m_nozzle_mapping.find(used_logic_nozzle.group_id); iter != m_nozzle_mapping.end()) {
if (m_obj->GetNozzleSystem()->GetReplaceNozzleTar().has_value() &&
iter->second == m_obj->GetNozzleSystem()->GetReplaceNozzleTar().value()) {
physic_nozzle_pos_vec.push_back(MAIN_EXTRUDER_ID);
} else {
physic_nozzle_pos_vec.push_back(iter->second);
}
}
}
}
return physic_nozzle_pos_vec;
}
wxString DevNozzleMappingCtrl::GetMappedNozzlePosStrByFilaId(int fila_id, const wxString& default_str) const
{
wxString display_str;
const auto& physic_nozzle_pos_vec = GetMappedNozzlePosVecByFilaId(fila_id);
for (int idx = 0; idx < physic_nozzle_pos_vec.size(); idx++) {
int pos_id = physic_nozzle_pos_vec[idx];
if (pos_id == MAIN_EXTRUDER_ID) {
display_str += "R";
} else if (pos_id == DEPUTY_EXTRUDER_ID) {
display_str += "L";
} else if (pos_id >= 0x10) {
display_str += wxString::Format("R%d", pos_id - 0x10 + 1);// display 1~n for rack hotends
} else {
continue;
}
if (idx < physic_nozzle_pos_vec.size() - 1) {
display_str += " ";
}
}
return !display_str.empty() ? display_str : default_str;
}
// flush weight for multi-nozzle printers: total flush volume across all filament changes
float DevNozzleMappingCtrl::GetFlushWeight(Slic3r::MachineObject* obj) const
{
auto plater = Slic3r::GUI::wxGetApp().plater();
if (!plater) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": plater is nullptr";
return -1;
}
GCodeProcessorResult* gcode_result = plater->background_process().get_current_gcode_result();
if (!gcode_result) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": gcode_result is nullptr";
return -1;
}
if (gcode_result->filament_change_sequence.empty()) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": filament_change_sequence is empty";
return -1;
}
if (!Slic3r::GUI::wxGetApp().preset_bundle) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": preset_bundle is nullptr";
return -1;
};
const std::vector<std::vector<std::vector<float>>>& flush_matrix = Slic3r::GUI::wxGetApp().preset_bundle->get_full_flush_matrix();
float total_flush_volume = 0;
MultiNozzleUtils::NozzleStatusRecorder recorder;
for (auto filament : gcode_result->filament_change_sequence) {
const auto& nozzle_pos_vec = GetMappedNozzlePosVecByFilaId(filament);
if (nozzle_pos_vec.empty()) {
continue;
}
auto nozzle_pos = nozzle_pos_vec.at(0);
if (nozzle_pos == -1){
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": nozzle_pos is -1 for fila_id=" << filament;
continue;
}
auto nozzle_info = obj->GetNozzleSystem()->GetNozzleByPosId(nozzle_pos);
if (nozzle_info.IsEmpty()) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": nozzle_info IsEmpty for fila_id=" << filament << ", nozzle_pos=" << nozzle_pos;
continue;
}
int extruder_id = nozzle_info.GetLogicExtruderId();
int nozzle_id = nozzle_info.GetNozzlePosId();
int last_filament = recorder.get_filament_in_nozzle(nozzle_id);
if (last_filament != -1 && last_filament != filament) {
if (flush_matrix.size() > extruder_id &&
flush_matrix[extruder_id].size() > last_filament &&
flush_matrix[extruder_id][last_filament].size() > filament){
total_flush_volume += flush_matrix[extruder_id][last_filament][filament];
}
else{
assert(false && "missing flush volume");
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": missing flush volume for extruder_id=" << extruder_id
<< ", last_filament=" << last_filament << ", filament=" << filament;
}
}
recorder.set_nozzle_status(nozzle_id, filament);
}
// estimate the flush weight: total_flush_volume * 1.26 * 0.001
return total_flush_volume * 1.26 * 0.001;
}
} // namespace Slic3r