add filament_extruder_map (#148)

This commit is contained in:
xiaoyeliu
2026-02-04 09:28:34 +08:00
committed by GitHub
parent 705909d54e
commit 9d423d0714
19 changed files with 6062 additions and 3615 deletions

2
.gitignore vendored
View File

@@ -40,3 +40,5 @@ resources/profiles/user/default
*.code-workspace *.code-workspace
deps_src/build/ deps_src/build/
.claude/ .claude/
.hive-mind/
nul

File diff suppressed because it is too large Load Diff

View File

@@ -344,6 +344,9 @@ public:
// Set a single vector item from either a scalar option or the first value of a vector option.vector of ConfigOptions. // Set a single vector item from either a scalar option or the first value of a vector option.vector of ConfigOptions.
// This function is useful to split values from multiple extrder / filament settings into separate configurations. // This function is useful to split values from multiple extrder / filament settings into separate configurations.
virtual void set_at(const ConfigOption *rhs, size_t i, size_t j) = 0; virtual void set_at(const ConfigOption *rhs, size_t i, size_t j) = 0;
// SM Orca: Copy a single element from source vector at src_idx to this vector at dst_idx
// This function is useful for applying physical extruder mapping to filament parameters
virtual void set_at(const ConfigOptionVectorBase* source, size_t dst_idx, size_t src_idx) = 0;
// Resize the vector of values, copy the newly added values from opt_default if provided. // Resize the vector of values, copy the newly added values from opt_default if provided.
virtual void resize(size_t n, const ConfigOption *opt_default = nullptr) = 0; virtual void resize(size_t n, const ConfigOption *opt_default = nullptr) = 0;
// Clear the values vector. // Clear the values vector.
@@ -431,6 +434,26 @@ public:
throw ConfigurationError("ConfigOptionVector::set_at(): Assigning an incompatible type"); throw ConfigurationError("ConfigOptionVector::set_at(): Assigning an incompatible type");
} }
// SM Orca: Copy a single element from source vector at src_idx to this vector at dst_idx
// Used for applying physical extruder mapping to filament parameters
void set_at(const ConfigOptionVectorBase* source, size_t dst_idx, size_t src_idx) override
{
auto* src_typed = dynamic_cast<const ConfigOptionVector<T>*>(source);
if (!src_typed || src_idx >= src_typed->size() || dst_idx >= this->size())
return;
// Handle nullable vectors - only copy if source value is not nil
if (this->nullable() && src_typed->nullable()) {
if (!src_typed->is_nil(src_idx)) {
this->values[dst_idx] = src_typed->values[src_idx];
}
} else if (!src_typed->nullable()) {
// Source is not nullable, always copy
this->values[dst_idx] = src_typed->values[src_idx];
}
// If source is nullable and value is nil, don't copy (keep existing value)
}
const T& get_at(size_t i) const const T& get_at(size_t i) const
{ {
assert(! this->values.empty()); assert(! this->values.empty());

View File

@@ -6,8 +6,9 @@ namespace Slic3r {
double Extruder::m_share_E = 0.; double Extruder::m_share_E = 0.;
double Extruder::m_share_retracted = 0.; double Extruder::m_share_retracted = 0.;
Extruder::Extruder(unsigned int id, GCodeConfig *config, bool share_extruder) : Extruder::Extruder(unsigned int id, unsigned int physical_extruder_id, GCodeConfig *config, bool share_extruder) :
m_id(id), m_id(id),
m_physical_extruder_id(physical_extruder_id),
m_config(config), m_config(config),
m_share_extruder(share_extruder) m_share_extruder(share_extruder)
{ {
@@ -157,24 +158,25 @@ double Extruder::filament_flow_ratio() const
} }
// Return a "retract_before_wipe" percentage as a factor clamped to <0, 1> // Return a "retract_before_wipe" percentage as a factor clamped to <0, 1>
// SM Orca: 回抽相关参数是挤出机属性,使用 m_physical_extruder_id
double Extruder::retract_before_wipe() const double Extruder::retract_before_wipe() const
{ {
return std::min(1., std::max(0., m_config->retract_before_wipe.get_at(m_id) * 0.01)); return std::min(1., std::max(0., m_config->retract_before_wipe.get_at(m_physical_extruder_id) * 0.01));
} }
double Extruder::retraction_length() const double Extruder::retraction_length() const
{ {
return m_config->retraction_length.get_at(m_id); return m_config->retraction_length.get_at(m_physical_extruder_id);
} }
double Extruder::retract_lift() const double Extruder::retract_lift() const
{ {
return m_config->z_hop.get_at(m_id); return m_config->z_hop.get_at(m_physical_extruder_id);
} }
int Extruder::retract_speed() const int Extruder::retract_speed() const
{ {
return int(floor(m_config->retraction_speed.get_at(m_id)+0.5)); return int(floor(m_config->retraction_speed.get_at(m_physical_extruder_id)+0.5));
} }
bool Extruder::use_firmware_retraction() const bool Extruder::use_firmware_retraction() const
@@ -184,28 +186,28 @@ bool Extruder::use_firmware_retraction() const
int Extruder::deretract_speed() const int Extruder::deretract_speed() const
{ {
int speed = int(floor(m_config->deretraction_speed.get_at(m_id)+0.5)); int speed = int(floor(m_config->deretraction_speed.get_at(m_physical_extruder_id)+0.5));
return (speed > 0) ? speed : this->retract_speed(); return (speed > 0) ? speed : this->retract_speed();
} }
double Extruder::retract_restart_extra() const double Extruder::retract_restart_extra() const
{ {
return m_config->retract_restart_extra.get_at(m_id); return m_config->retract_restart_extra.get_at(m_physical_extruder_id);
} }
double Extruder::retract_length_toolchange() const double Extruder::retract_length_toolchange() const
{ {
return m_config->retract_length_toolchange.get_at(m_id); return m_config->retract_length_toolchange.get_at(m_physical_extruder_id);
} }
double Extruder::retract_restart_extra_toolchange() const double Extruder::retract_restart_extra_toolchange() const
{ {
return m_config->retract_restart_extra_toolchange.get_at(m_id); return m_config->retract_restart_extra_toolchange.get_at(m_physical_extruder_id);
} }
double Extruder::travel_slope() const double Extruder::travel_slope() const
{ {
return m_config->travel_slope.get_at(m_id) * PI / 180; return m_config->travel_slope.get_at(m_physical_extruder_id) * PI / 180;
} }
} }

View File

@@ -11,7 +11,8 @@ class GCodeConfig;
class Extruder class Extruder
{ {
public: public:
Extruder(unsigned int id, GCodeConfig *config, bool share_extruder); // SM Orca: 添加 physical_extruder_id 参数用于支持耗材-挤出机映射
Extruder(unsigned int id, unsigned int physical_extruder_id, GCodeConfig *config, bool share_extruder);
virtual ~Extruder() {} virtual ~Extruder() {}
void reset() { void reset() {
@@ -28,6 +29,8 @@ public:
} }
unsigned int id() const { return m_id; } unsigned int id() const { return m_id; }
// SM Orca: 获取物理挤出机ID
unsigned int physical_extruder_id() const { return m_physical_extruder_id; }
double extrude(double dE); double extrude(double dE);
double retract(double length, double restart_extra); double retract(double length, double restart_extra);
@@ -75,12 +78,14 @@ public:
private: private:
// Private constructor to create a key for a search in std::set. // Private constructor to create a key for a search in std::set.
Extruder(unsigned int id) : m_id(id) {} Extruder(unsigned int id) : m_id(id), m_physical_extruder_id(id) {}
// Reference to GCodeWriter instance owned by GCodeWriter. // Reference to GCodeWriter instance owned by GCodeWriter.
GCodeConfig *m_config; GCodeConfig *m_config;
// Print-wide global ID of this extruder. // Print-wide global ID of this extruder (filament index).
unsigned int m_id; unsigned int m_id;
// SM Orca: 物理挤出机ID用于查询挤出机属性温度、回抽等
unsigned int m_physical_extruder_id;
// Current state of the extruder axis, may be resetted if use_relative_e_distances. // Current state of the extruder axis, may be resetted if use_relative_e_distances.
double m_E; double m_E;
// Current state of the extruder tachometer, used to output the extruded_volume() and used_filament() statistics. // Current state of the extruder tachometer, used to output the extruded_volume() and used_filament() statistics.

View File

@@ -213,42 +213,93 @@ double Flow::mm3_per_mm() const
Flow support_material_flow(const PrintObject *object, float layer_height) Flow support_material_flow(const PrintObject *object, float layer_height)
{ {
// SM Orca: 使用物理挤出机的喷嘴直径
int filament_idx = object->config().support_filament - 1;
int physical_extruder = object->print()->get_physical_extruder(filament_idx);
// SM Orca: 日志 - 配置数组访问边界检查
const auto& nozzle_diameter_config = object->print()->config().nozzle_diameter;
size_t array_size = nozzle_diameter_config.values.size();
BOOST_LOG_TRIVIAL(info) << "Flow::support_material_flow: filament_idx=" << filament_idx
<< " physical_extruder=" << physical_extruder
<< " nozzle_diameter array_size=" << array_size
<< " access_index=" << physical_extruder
<< (physical_extruder >= (int)array_size ? " [POTENTIAL_OUT_OF_BOUNDS!]" : " [OK]");
return Flow::new_from_config_width( return Flow::new_from_config_width(
frSupportMaterial, frSupportMaterial,
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
(object->config().support_line_width.value > 0) ? object->config().support_line_width : object->config().line_width, (object->config().support_line_width.value > 0) ? object->config().support_line_width : object->config().line_width,
// if object->config().support_filament == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. // if object->config().support_filament == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
float(object->print()->config().nozzle_diameter.get_at(object->config().support_filament-1)), float(object->print()->config().nozzle_diameter.get_at(physical_extruder)),
(layer_height > 0.f) ? layer_height : float(object->config().layer_height.value)); (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value));
} }
//BBS //BBS
Flow support_transition_flow(const PrintObject* object) Flow support_transition_flow(const PrintObject* object)
{ {
//BBS: support transition of tree support is bridge flow //BBS: support transition of tree support is bridge flow
float dmr = float(object->print()->config().nozzle_diameter.get_at(object->config().support_filament - 1)); // SM Orca: 使用物理挤出机的喷嘴直径
int filament_idx = object->config().support_filament - 1;
int physical_extruder = object->print()->get_physical_extruder(filament_idx);
// SM Orca: 日志 - 配置数组访问边界检查
const auto& nozzle_diameter_config = object->print()->config().nozzle_diameter;
size_t array_size = nozzle_diameter_config.values.size();
BOOST_LOG_TRIVIAL(info) << "Flow::support_transition_flow: filament_idx=" << filament_idx
<< " physical_extruder=" << physical_extruder
<< " nozzle_diameter array_size=" << array_size
<< " access_index=" << physical_extruder
<< (physical_extruder >= (int)array_size ? " [POTENTIAL_OUT_OF_BOUNDS!]" : " [OK]");
float dmr = float(object->print()->config().nozzle_diameter.get_at(physical_extruder));
return Flow::bridging_flow(dmr, dmr); return Flow::bridging_flow(dmr, dmr);
} }
Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height) Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height)
{ {
// SM Orca: 使用物理挤出机的喷嘴直径
int filament_idx = object->config().support_filament - 1;
int physical_extruder = object->print()->get_physical_extruder(filament_idx);
const PrintConfig &print_config = object->print()->config(); const PrintConfig &print_config = object->print()->config();
// SM Orca: 日志 - 配置数组访问边界检查
size_t array_size = print_config.nozzle_diameter.values.size();
BOOST_LOG_TRIVIAL(info) << "Flow::support_material_1st_layer_flow: filament_idx=" << filament_idx
<< " physical_extruder=" << physical_extruder
<< " nozzle_diameter array_size=" << array_size
<< " access_index=" << physical_extruder
<< (physical_extruder >= (int)array_size ? " [POTENTIAL_OUT_OF_BOUNDS!]" : " [OK]");
const auto &width = (print_config.initial_layer_line_width.value > 0) ? print_config.initial_layer_line_width : object->config().support_line_width; const auto &width = (print_config.initial_layer_line_width.value > 0) ? print_config.initial_layer_line_width : object->config().support_line_width;
return Flow::new_from_config_width( return Flow::new_from_config_width(
frSupportMaterial, frSupportMaterial,
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
(width.value > 0) ? width : object->config().line_width, (width.value > 0) ? width : object->config().line_width,
float(print_config.nozzle_diameter.get_at(object->config().support_filament-1)), float(print_config.nozzle_diameter.get_at(physical_extruder)),
(layer_height > 0.f) ? layer_height : float(print_config.initial_layer_print_height.value)); (layer_height > 0.f) ? layer_height : float(print_config.initial_layer_print_height.value));
} }
Flow support_material_interface_flow(const PrintObject *object, float layer_height) Flow support_material_interface_flow(const PrintObject *object, float layer_height)
{ {
// SM Orca: 使用物理挤出机的喷嘴直径
int filament_idx = object->config().support_interface_filament - 1;
int physical_extruder = object->print()->get_physical_extruder(filament_idx);
// SM Orca: 日志 - 配置数组访问边界检查
const auto& nozzle_diameter_config = object->print()->config().nozzle_diameter;
size_t array_size = nozzle_diameter_config.values.size();
BOOST_LOG_TRIVIAL(info) << "Flow::support_material_interface_flow: filament_idx=" << filament_idx
<< " physical_extruder=" << physical_extruder
<< " nozzle_diameter array_size=" << array_size
<< " access_index=" << physical_extruder
<< (physical_extruder >= (int)array_size ? " [POTENTIAL_OUT_OF_BOUNDS!]" : " [OK]");
return Flow::new_from_config_width( return Flow::new_from_config_width(
frSupportMaterialInterface, frSupportMaterialInterface,
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
(object->config().support_line_width > 0) ? object->config().support_line_width : object->config().line_width, (object->config().support_line_width > 0) ? object->config().support_line_width : object->config().line_width,
// if object->config().support_interface_filament == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. // if object->config().support_interface_filament == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
float(object->print()->config().nozzle_diameter.get_at(object->config().support_interface_filament-1)), float(object->print()->config().nozzle_diameter.get_at(physical_extruder)),
(layer_height > 0.f) ? layer_height : float(object->config().layer_height.value)); (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value));
} }

File diff suppressed because it is too large Load Diff

View File

@@ -114,7 +114,7 @@ private:
std::string append_tcr2(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z = -1.) const; std::string append_tcr2(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z = -1.) const;
// Postprocesses gcode: rotates and moves G1 extrusions and returns result // Postprocesses gcode: rotates and moves G1 extrusions and returns result
std::string post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const; std::string post_process_wipe_tower_moves(GCode& gcodegen, const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const;
// Left / right edges of the wipe tower, for the planning of wipe moves. // Left / right edges of the wipe tower, for the planning of wipe moves.
const float m_left; const float m_left;
const float m_right; const float m_right;
@@ -197,6 +197,9 @@ public:
//BBS: set offset for gcode writer //BBS: set offset for gcode writer
void set_gcode_offset(double x, double y) { m_writer.set_xy_offset(x, y); m_processor.set_xy_offset(x, y);} void set_gcode_offset(double x, double y) { m_writer.set_xy_offset(x, y); m_processor.set_xy_offset(x, y);}
// SM Orca: Set filament-extruder mapping
void set_filament_extruder_map(const std::unordered_map<int, int>& map) { m_writer.set_filament_extruder_map(map); m_processor.set_filament_extruder_map(map); }
// Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests. // Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
const Vec2d& origin() const { return m_origin; } const Vec2d& origin() const { return m_origin; }
void set_origin(const Vec2d &pointf); void set_origin(const Vec2d &pointf);

View File

@@ -526,9 +526,18 @@ void GCodeProcessor::UsedFilaments::process_role_cache(GCodeProcessor* processor
if (role_cache != 0.0f) { if (role_cache != 0.0f) {
std::pair<double, double> filament = { 0.0f, 0.0f }; std::pair<double, double> filament = { 0.0f, 0.0f };
double s = PI * sqr(0.5 * processor->m_result.filament_diameters[processor->m_extruder_id]); // SM Orca: 添加边界检查,与其他地方保持一致
float diameter = (static_cast<size_t>(processor->m_extruder_id) < processor->m_result.filament_diameters.size())
? processor->m_result.filament_diameters[processor->m_extruder_id]
: processor->m_result.filament_diameters.back();
float density = (static_cast<size_t>(processor->m_extruder_id) < processor->m_result.filament_densities.size())
? processor->m_result.filament_densities[processor->m_extruder_id]
: processor->m_result.filament_densities.back();
double s = PI * sqr(0.5 * diameter);
filament.first = role_cache / s * 0.001; filament.first = role_cache / s * 0.001;
filament.second = role_cache * processor->m_result.filament_densities[processor->m_extruder_id] * 0.001; filament.second = role_cache * density * 0.001;
ExtrusionRole active_role = processor->m_extrusion_role; ExtrusionRole active_role = processor->m_extrusion_role;
if (filaments_per_role.find(active_role) != filaments_per_role.end()) { if (filaments_per_role.find(active_role) != filaments_per_role.end()) {
@@ -556,6 +565,25 @@ void GCodeProcessorResult::reset() {
//BBS: add mutex for protection of gcode result //BBS: add mutex for protection of gcode result
lock(); lock();
// SM Orca: 保存当前的extruders_count并尝试从已有数组推断
size_t saved_count = extruders_count;
// SM Orca: 如果extruders_count不合理尝试从已有数组大小推断
if (saved_count == 0 || saved_count > 256) {
// 尝试从已有数组大小推断优先使用filament_diameters的大小
if (!filament_diameters.empty() && filament_diameters.size() <= 256) {
saved_count = filament_diameters.size();
BOOST_LOG_TRIVIAL(info) << "SM Orca: GCodeProcessorResult::reset() - Inferred extruders_count from existing array: "
<< saved_count;
} else {
// SM Orca: 使用16作为默认值覆盖U1等多耗材机型
// 对于只有少量耗材的用户,稍微多分配一些内存影响很小
saved_count = 16;
BOOST_LOG_TRIVIAL(info) << "SM Orca: GCodeProcessorResult::reset() - Using default extruders_count: "
<< saved_count << " (was " << extruders_count << ", array size was " << filament_diameters.size() << ")";
}
}
moves = std::vector<GCodeProcessorResult::MoveVertex>(); moves = std::vector<GCodeProcessorResult::MoveVertex>();
printable_area = Pointfs(); printable_area = Pointfs();
//BBS: add bed exclude area //BBS: add bed exclude area
@@ -567,10 +595,22 @@ void GCodeProcessorResult::reset() {
timelapse_warning_code = 0; timelapse_warning_code = 0;
printable_height = 0.0f; printable_height = 0.0f;
settings_ids.reset(); settings_ids.reset();
extruders_count = 0;
// SM Orca: 恢复extruders_count为合理的值
extruders_count = saved_count;
extruder_colors = std::vector<std::string>(); extruder_colors = std::vector<std::string>();
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY); // SM Orca: 使用推断的大小初始化所有耗材相关数组
filament_diameters = std::vector<float>(saved_count, DEFAULT_FILAMENT_DIAMETER);
filament_densities = std::vector<float>(saved_count, DEFAULT_FILAMENT_DENSITY);
filament_costs = std::vector<float>(saved_count, DEFAULT_FILAMENT_COST);
required_nozzle_HRC = std::vector<int>(saved_count, DEFAULT_FILAMENT_HRC);
filament_vitrification_temperature = std::vector<int>(saved_count, DEFAULT_FILAMENT_VITRIFICATION_TEMPERATURE);
BOOST_LOG_TRIVIAL(info) << "SM Orca: GCodeProcessorResult::reset() - Resized arrays to " << saved_count
<< " (original extruders_count=" << (saved_count == extruders_count ? "preserved" : "inferred")
<< ", this=" << this << ")";
custom_gcode_per_print_z = std::vector<CustomGCode::Item>(); custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
spiral_vase_layers = std::vector<std::pair<float, std::pair<size_t, size_t>>>(); spiral_vase_layers = std::vector<std::pair<float, std::pair<size_t, size_t>>>();
time = 0; time = 0;
@@ -583,6 +623,25 @@ void GCodeProcessorResult::reset() {
//BBS: add mutex for protection of gcode result //BBS: add mutex for protection of gcode result
lock(); lock();
// SM Orca: 保存当前的extruders_count并尝试从已有数组推断
size_t saved_count = extruders_count;
// SM Orca: 如果extruders_count不合理尝试从已有数组大小推断
if (saved_count == 0 || saved_count > 256) {
// 尝试从已有数组大小推断优先使用filament_diameters的大小
if (!filament_diameters.empty() && filament_diameters.size() <= 256) {
saved_count = filament_diameters.size();
BOOST_LOG_TRIVIAL(info) << "SM Orca: GCodeProcessorResult::reset() - Inferred extruders_count from existing array: "
<< saved_count;
} else {
// SM Orca: 使用16作为默认值覆盖U1等多耗材机型
// 对于只有少量耗材的用户,稍微多分配一些内存影响很小
saved_count = 16;
BOOST_LOG_TRIVIAL(info) << "SM Orca: GCodeProcessorResult::reset() - Using default extruders_count: "
<< saved_count << " (was " << extruders_count << ", array size was " << filament_diameters.size() << ")";
}
}
moves.clear(); moves.clear();
lines_ends.clear(); lines_ends.clear();
printable_area = Pointfs(); printable_area = Pointfs();
@@ -596,13 +655,23 @@ void GCodeProcessorResult::reset() {
timelapse_warning_code = 0; timelapse_warning_code = 0;
printable_height = 0.0f; printable_height = 0.0f;
settings_ids.reset(); settings_ids.reset();
extruders_count = 0;
backtrace_enabled = false; backtrace_enabled = false;
extruder_colors = std::vector<std::string>(); extruder_colors = std::vector<std::string>();
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
required_nozzle_HRC = std::vector<int>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_HRC); // SM Orca: 恢复extruders_count为合理的值
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY); extruders_count = saved_count;
filament_costs = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_COST);
// SM Orca: 使用推断的大小初始化所有耗材相关数组
filament_diameters = std::vector<float>(saved_count, DEFAULT_FILAMENT_DIAMETER);
required_nozzle_HRC = std::vector<int>(saved_count, DEFAULT_FILAMENT_HRC);
filament_densities = std::vector<float>(saved_count, DEFAULT_FILAMENT_DENSITY);
filament_costs = std::vector<float>(saved_count, DEFAULT_FILAMENT_COST);
filament_vitrification_temperature = std::vector<int>(saved_count, DEFAULT_FILAMENT_VITRIFICATION_TEMPERATURE);
BOOST_LOG_TRIVIAL(info) << "SM Orca: GCodeProcessorResult::reset() - Resized arrays to " << saved_count
<< " (original extruders_count=" << (saved_count == extruders_count ? "preserved" : "inferred")
<< ", this=" << this << ")";
custom_gcode_per_print_z = std::vector<CustomGCode::Item>(); custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
spiral_vase_layers = std::vector<std::pair<float, std::pair<size_t, size_t>>>(); spiral_vase_layers = std::vector<std::pair<float, std::pair<size_t, size_t>>>();
bed_match_result = BedMatchResult(true); bed_match_result = BedMatchResult(true);
@@ -719,6 +788,45 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_preheat_steps = 1; m_preheat_steps = 1;
m_result.backtrace_enabled = m_preheat_time > 0 && (m_is_XL_printer || (!m_single_extruder_multi_material && extruders_count > 1)); m_result.backtrace_enabled = m_preheat_time > 0 && (m_is_XL_printer || (!m_single_extruder_multi_material && extruders_count > 1));
// SM Orca: 配置验证 - 在resize之前检查配置完整性
size_t physical_extruder_count = config.extruder_offset.values.size();
BOOST_LOG_TRIVIAL(info) << "SM Orca: Configuration validation:"
<< " filaments=" << extruders_count
<< ", physical_extruders=" << physical_extruder_count
<< ", mappings=" << m_filament_extruder_map.size();
// 验证各配置数组大小
if (config.filament_density.values.size() < extruders_count) {
BOOST_LOG_TRIVIAL(warning) << "SM Orca: filament_density has "
<< config.filament_density.values.size() << " values, expected " << extruders_count
<< " (will use fallback values)";
}
if (config.filament_cost.values.size() < extruders_count) {
BOOST_LOG_TRIVIAL(warning) << "SM Orca: filament_cost has "
<< config.filament_cost.values.size() << " values, expected " << extruders_count
<< " (will use fallback values)";
}
if (config.nozzle_temperature.values.size() < extruders_count) {
BOOST_LOG_TRIVIAL(warning) << "SM Orca: nozzle_temperature has "
<< config.nozzle_temperature.values.size() << " values, expected " << extruders_count
<< " (will use fallback values)";
}
// 验证映射表有效性
if (!m_filament_extruder_map.empty()) {
for (size_t i = 0; i < extruders_count; ++i) {
int physical_extruder = get_physical_extruder(i);
if (physical_extruder < 0 ||
physical_extruder >= static_cast<int>(physical_extruder_count)) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: Filament " << i
<< " maps to invalid physical extruder " << physical_extruder
<< " (valid range: 0-" << (physical_extruder_count - 1) << ")";
}
}
}
m_extruder_offsets.resize(extruders_count); m_extruder_offsets.resize(extruders_count);
m_extruder_colors.resize(extruders_count); m_extruder_colors.resize(extruders_count);
m_result.filament_diameters.resize(extruders_count); m_result.filament_diameters.resize(extruders_count);
@@ -731,20 +839,103 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_extruder_temps_first_layer_config.resize(extruders_count); m_extruder_temps_first_layer_config.resize(extruders_count);
m_result.nozzle_hrc = static_cast<int>(config.nozzle_hrc.getInt()); m_result.nozzle_hrc = static_cast<int>(config.nozzle_hrc.getInt());
m_result.nozzle_type = config.nozzle_type; m_result.nozzle_type = config.nozzle_type;
// SM Orca: 获取各配置数组的大小,用于边界检查
size_t diameter_count = config.filament_diameter.values.size();
size_t density_count = config.filament_density.values.size();
size_t cost_count = config.filament_cost.values.size();
size_t temp_initial_count = config.nozzle_temperature_initial_layer.values.size();
size_t temp_count = config.nozzle_temperature.values.size();
size_t hrc_count = config.required_nozzle_HRC.values.size();
size_t vitrification_count = config.temperature_vitrification.values.size();
for (size_t i = 0; i < extruders_count; ++ i) { for (size_t i = 0; i < extruders_count; ++ i) {
m_extruder_offsets[i] = to_3d(config.extruder_offset.get_at(i).cast<float>().eval(), 0.f); // SM Orca: 使用物理挤出机ID来访问offset并确保不越界
int physical_extruder = get_physical_extruder(i);
// SM Orca: 边界检查 - 如果映射的物理挤出机超出范围回退到1:1映射
if (physical_extruder < 0 || physical_extruder >= static_cast<int>(physical_extruder_count)) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: Filament " << i
<< " maps to invalid physical extruder " << physical_extruder
<< " (valid range: 0-" << (physical_extruder_count - 1) << ")";
// 使用filament index作为fallback1:1映射如果也越界则使用0
physical_extruder = (i < physical_extruder_count) ? static_cast<int>(i) : 0;
BOOST_LOG_TRIVIAL(error) << " SM Orca: Using fallback physical extruder " << physical_extruder;
}
m_extruder_offsets[i] = to_3d(config.extruder_offset.get_at(physical_extruder).cast<float>().eval(), 0.f);
m_extruder_colors[i] = static_cast<unsigned char>(i); m_extruder_colors[i] = static_cast<unsigned char>(i);
m_extruder_temps_first_layer_config[i] = static_cast<int>(config.nozzle_temperature_initial_layer.get_at(i));
m_extruder_temps_config[i] = static_cast<int>(config.nozzle_temperature.get_at(i)); // SM Orca: 安全读取温度配置(使用最后有效值作为回退)
// 温度是挤出机属性,使用 physical_extruder 而不是 filament index
if (physical_extruder < static_cast<int>(temp_initial_count)) {
m_extruder_temps_first_layer_config[i] = static_cast<int>(config.nozzle_temperature_initial_layer.get_at(physical_extruder));
} else {
int fallback = temp_initial_count > 0 ?
static_cast<int>(config.nozzle_temperature_initial_layer.get_at(temp_initial_count - 1)) : 210;
m_extruder_temps_first_layer_config[i] = fallback;
BOOST_LOG_TRIVIAL(warning) << "SM Orca: Filament " << i << " (physical extruder " << physical_extruder << ") initial layer temperature not configured, using " << fallback;
}
if (physical_extruder < static_cast<int>(temp_count)) {
m_extruder_temps_config[i] = static_cast<int>(config.nozzle_temperature.get_at(physical_extruder));
} else {
int fallback = temp_count > 0 ?
static_cast<int>(config.nozzle_temperature.get_at(temp_count - 1)) : 210;
m_extruder_temps_config[i] = fallback;
BOOST_LOG_TRIVIAL(warning) << "SM Orca: Filament " << i << " (physical extruder " << physical_extruder << ") temperature not configured, using " << fallback;
}
if (m_extruder_temps_config[i] == 0) { if (m_extruder_temps_config[i] == 0) {
// This means the value should be ignored and first layer temp should be used. // This means the value should be ignored and first layer temp should be used.
m_extruder_temps_config[i] = m_extruder_temps_first_layer_config[i]; m_extruder_temps_config[i] = m_extruder_temps_first_layer_config[i];
} }
m_result.filament_diameters[i] = static_cast<float>(config.filament_diameter.get_at(i));
m_result.required_nozzle_HRC[i] = static_cast<int>(config.required_nozzle_HRC.get_at(i)); // SM Orca: 安全读取直径(使用最后有效值作为回退)
m_result.filament_densities[i] = static_cast<float>(config.filament_density.get_at(i)); if (i < diameter_count) {
m_result.filament_vitrification_temperature[i] = static_cast<float>(config.temperature_vitrification.get_at(i)); m_result.filament_diameters[i] = static_cast<float>(config.filament_diameter.get_at(i));
m_result.filament_costs[i] = static_cast<float>(config.filament_cost.get_at(i)); } else {
float fallback = diameter_count > 0 ?
static_cast<float>(config.filament_diameter.get_at(diameter_count - 1)) : 1.75f;
m_result.filament_diameters[i] = fallback;
BOOST_LOG_TRIVIAL(warning) << "SM Orca: Filament " << i << " diameter not configured, using " << fallback << "mm";
}
// SM Orca: 安全读取HRC
if (i < hrc_count) {
m_result.required_nozzle_HRC[i] = static_cast<int>(config.required_nozzle_HRC.get_at(i));
} else {
int fallback = hrc_count > 0 ?
static_cast<int>(config.required_nozzle_HRC.get_at(hrc_count - 1)) : 0;
m_result.required_nozzle_HRC[i] = fallback;
}
// SM Orca: 安全读取密度(使用最后有效值作为回退)
if (i < density_count) {
m_result.filament_densities[i] = static_cast<float>(config.filament_density.get_at(i));
} else {
float fallback = density_count > 0 ?
static_cast<float>(config.filament_density.get_at(density_count - 1)) : 1.25f;
m_result.filament_densities[i] = fallback;
BOOST_LOG_TRIVIAL(warning) << "SM Orca: Filament " << i << " density not configured, using " << fallback << " g/cm³";
}
// SM Orca: 安全读取玻璃化温度
if (i < vitrification_count) {
m_result.filament_vitrification_temperature[i] = static_cast<int>(config.temperature_vitrification.get_at(i));
} else {
int fallback = vitrification_count > 0 ?
static_cast<int>(config.temperature_vitrification.get_at(vitrification_count - 1)) : 0;
m_result.filament_vitrification_temperature[i] = fallback;
}
// SM Orca: 安全读取成本使用0作为默认值
if (i < cost_count) {
m_result.filament_costs[i] = static_cast<float>(config.filament_cost.get_at(i));
} else {
m_result.filament_costs[i] = 0.0f;
BOOST_LOG_TRIVIAL(warning) << "SM Orca: Filament " << i << " cost not configured, using 0.0";
}
} }
if (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfKlipper || m_flavor == gcfRepRapFirmware) { if (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfKlipper || m_flavor == gcfRepRapFirmware) {
@@ -858,24 +1049,39 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
const ConfigOptionFloats* filament_diameters = config.option<ConfigOptionFloats>("filament_diameter"); const ConfigOptionFloats* filament_diameters = config.option<ConfigOptionFloats>("filament_diameter");
if (filament_diameters != nullptr) { if (filament_diameters != nullptr) {
m_result.filament_diameters.clear(); size_t config_size = filament_diameters->values.size();
m_result.filament_diameters.resize(filament_diameters->values.size());
for (size_t i = 0; i < filament_diameters->values.size(); ++i) { // SM Orca: 确保数组大小等于extruders_count不要clear()
if (m_result.filament_diameters.size() < m_result.extruders_count) {
m_result.filament_diameters.resize(m_result.extruders_count, DEFAULT_FILAMENT_DIAMETER);
}
// SM Orca: 只更新配置中有的值
for (size_t i = 0; i < config_size && i < m_result.extruders_count; ++i) {
m_result.filament_diameters[i] = static_cast<float>(filament_diameters->values[i]); m_result.filament_diameters[i] = static_cast<float>(filament_diameters->values[i]);
} }
// SM Orca: 对于配置中没有的值,使用最后一个有效值
if (config_size > 0 && config_size < m_result.extruders_count) {
float last_value = static_cast<float>(filament_diameters->values[config_size - 1]);
for (size_t i = config_size; i < m_result.extruders_count; ++i) {
m_result.filament_diameters[i] = last_value;
BOOST_LOG_TRIVIAL(debug) << "SM Orca: Filament " << i
<< " diameter not in config, using last value " << last_value << "mm";
}
}
} }
// SM Orca: 确保数组大小正确(移除原有的回退逻辑,因为已经在上面处理了)
if (m_result.filament_diameters.size() < m_result.extruders_count) { if (m_result.filament_diameters.size() < m_result.extruders_count) {
for (size_t i = m_result.filament_diameters.size(); i < m_result.extruders_count; ++i) { m_result.filament_diameters.resize(m_result.extruders_count, DEFAULT_FILAMENT_DIAMETER);
m_result.filament_diameters.emplace_back(DEFAULT_FILAMENT_DIAMETER);
}
} }
const ConfigOptionInts *filament_HRC = config.option<ConfigOptionInts>("required_nozzle_HRC"); const ConfigOptionInts *filament_HRC = config.option<ConfigOptionInts>("required_nozzle_HRC");
if (filament_HRC != nullptr) { if (filament_HRC != nullptr) {
m_result.required_nozzle_HRC.clear(); m_result.required_nozzle_HRC.clear();
m_result.required_nozzle_HRC.resize(filament_HRC->values.size()); m_result.required_nozzle_HRC.resize(filament_HRC->values.size());
for (size_t i = 0; i < filament_HRC->values.size(); ++i) { m_result.required_nozzle_HRC[i] = static_cast<float>(filament_HRC->values[i]); } for (size_t i = 0; i < filament_HRC->values.size(); ++i) { m_result.required_nozzle_HRC[i] = static_cast<int>(filament_HRC->values[i]); }
} }
if (m_result.required_nozzle_HRC.size() < m_result.extruders_count) { if (m_result.required_nozzle_HRC.size() < m_result.extruders_count) {
@@ -885,43 +1091,86 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
const ConfigOptionFloats* filament_densities = config.option<ConfigOptionFloats>("filament_density"); const ConfigOptionFloats* filament_densities = config.option<ConfigOptionFloats>("filament_density");
if (filament_densities != nullptr) { if (filament_densities != nullptr) {
m_result.filament_densities.clear(); size_t config_size = filament_densities->values.size();
m_result.filament_densities.resize(filament_densities->values.size());
for (size_t i = 0; i < filament_densities->values.size(); ++i) { // SM Orca: 确保数组大小等于extruders_count不要clear()
if (m_result.filament_densities.size() < m_result.extruders_count) {
m_result.filament_densities.resize(m_result.extruders_count, DEFAULT_FILAMENT_DENSITY);
}
// SM Orca: 只更新配置中有的值
for (size_t i = 0; i < config_size && i < m_result.extruders_count; ++i) {
m_result.filament_densities[i] = static_cast<float>(filament_densities->values[i]); m_result.filament_densities[i] = static_cast<float>(filament_densities->values[i]);
} }
// SM Orca: 对于配置中没有的值,使用最后一个有效值
if (config_size > 0 && config_size < m_result.extruders_count) {
float last_value = static_cast<float>(filament_densities->values[config_size - 1]);
for (size_t i = config_size; i < m_result.extruders_count; ++i) {
m_result.filament_densities[i] = last_value;
BOOST_LOG_TRIVIAL(debug) << "SM Orca: Filament " << i
<< " density not in config, using last value " << last_value << " g/cm³";
}
}
} }
// SM Orca: 确保数组大小正确
if (m_result.filament_densities.size() < m_result.extruders_count) { if (m_result.filament_densities.size() < m_result.extruders_count) {
for (size_t i = m_result.filament_densities.size(); i < m_result.extruders_count; ++i) { m_result.filament_densities.resize(m_result.extruders_count, DEFAULT_FILAMENT_DENSITY);
m_result.filament_densities.emplace_back(DEFAULT_FILAMENT_DENSITY);
}
} }
//BBS //BBS
const ConfigOptionFloats* filament_costs = config.option<ConfigOptionFloats>("filament_cost"); const ConfigOptionFloats* filament_costs = config.option<ConfigOptionFloats>("filament_cost");
if (filament_costs != nullptr) { if (filament_costs != nullptr) {
m_result.filament_costs.clear(); size_t config_size = filament_costs->values.size();
m_result.filament_costs.resize(filament_costs->values.size());
for (size_t i = 0; i < filament_costs->values.size(); ++i) // SM Orca: 确保数组大小等于extruders_count不要clear()
m_result.filament_costs[i]=static_cast<float>(filament_costs->values[i]); if (m_result.filament_costs.size() < m_result.extruders_count) {
m_result.filament_costs.resize(m_result.extruders_count, DEFAULT_FILAMENT_COST);
}
// SM Orca: 只更新配置中有的值
for (size_t i = 0; i < config_size && i < m_result.extruders_count; ++i)
m_result.filament_costs[i] = static_cast<float>(filament_costs->values[i]);
// SM Orca: 对于配置中没有的值使用0成本默认值
if (config_size < m_result.extruders_count) {
for (size_t i = config_size; i < m_result.extruders_count; ++i) {
m_result.filament_costs[i] = DEFAULT_FILAMENT_COST;
BOOST_LOG_TRIVIAL(debug) << "SM Orca: Filament " << i
<< " cost not in config, using default " << DEFAULT_FILAMENT_COST;
}
}
} }
for (size_t i = m_result.filament_costs.size(); i < m_result.extruders_count; ++i) {
m_result.filament_costs.emplace_back(DEFAULT_FILAMENT_COST); // SM Orca: 确保数组大小正确
if (m_result.filament_costs.size() < m_result.extruders_count) {
m_result.filament_costs.resize(m_result.extruders_count, DEFAULT_FILAMENT_COST);
} }
//BBS //BBS
const ConfigOptionInts* filament_vitrification_temperature = config.option<ConfigOptionInts>("temperature_vitrification"); const ConfigOptionInts* filament_vitrification_temperature = config.option<ConfigOptionInts>("temperature_vitrification");
if (filament_vitrification_temperature != nullptr) { if (filament_vitrification_temperature != nullptr) {
m_result.filament_vitrification_temperature.clear(); size_t config_size = filament_vitrification_temperature->values.size();
m_result.filament_vitrification_temperature.resize(filament_vitrification_temperature->values.size());
for (size_t i = 0; i < filament_vitrification_temperature->values.size(); ++i) { // SM Orca: 确保数组大小等于extruders_count不要clear()
if (m_result.filament_vitrification_temperature.size() < m_result.extruders_count) {
m_result.filament_vitrification_temperature.resize(m_result.extruders_count, DEFAULT_FILAMENT_VITRIFICATION_TEMPERATURE);
}
// SM Orca: 只更新配置中有的值
for (size_t i = 0; i < config_size && i < m_result.extruders_count; ++i) {
m_result.filament_vitrification_temperature[i] = static_cast<int>(filament_vitrification_temperature->values[i]); m_result.filament_vitrification_temperature[i] = static_cast<int>(filament_vitrification_temperature->values[i]);
} }
}
if (m_result.filament_vitrification_temperature.size() < m_result.extruders_count) { // SM Orca: 对于配置中没有的值,使用最后一个有效值
for (size_t i = m_result.filament_vitrification_temperature.size(); i < m_result.extruders_count; ++i) { if (config_size > 0 && config_size < m_result.extruders_count) {
m_result.filament_vitrification_temperature.emplace_back(DEFAULT_FILAMENT_VITRIFICATION_TEMPERATURE); int last_value = static_cast<int>(filament_vitrification_temperature->values[config_size - 1]);
for (size_t i = config_size; i < m_result.extruders_count; ++i) {
m_result.filament_vitrification_temperature[i] = last_value;
BOOST_LOG_TRIVIAL(debug) << "SM Orca: Filament " << i
<< " vitrification temperature not in config, using last value " << last_value;
}
} }
} }
@@ -937,8 +1186,14 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
} }
} }
else { else {
m_extruder_offsets.resize(extruder_offset->values.size()); // SM Orca: 先确保数组足够大,不要缩小已有的数组
for (size_t i = 0; i < extruder_offset->values.size(); ++i) { size_t physical_count = extruder_offset->values.size();
if (m_extruder_offsets.size() < m_result.extruders_count) {
m_extruder_offsets.resize(m_result.extruders_count, DEFAULT_EXTRUDER_OFFSET);
}
// 只更新物理挤出机的offset
for (size_t i = 0; i < physical_count && i < m_extruder_offsets.size(); ++i) {
Vec2f offset = extruder_offset->values[i].cast<float>(); Vec2f offset = extruder_offset->values[i].cast<float>();
m_extruder_offsets[i] = { offset(0), offset(1), 0.0f }; m_extruder_offsets[i] = { offset(0), offset(1), 0.0f };
} }
@@ -946,8 +1201,19 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
} }
if (m_extruder_offsets.size() < m_result.extruders_count) { if (m_extruder_offsets.size() < m_result.extruders_count) {
// SM Orca: 使用映射来填充剩余耗材的offset而不是使用默认值
size_t physical_count = m_extruder_offsets.size();
for (size_t i = m_extruder_offsets.size(); i < m_result.extruders_count; ++i) { for (size_t i = m_extruder_offsets.size(); i < m_result.extruders_count; ++i) {
m_extruder_offsets.emplace_back(DEFAULT_EXTRUDER_OFFSET); int physical_extruder = get_physical_extruder(i);
// 如果映射的物理挤出机索引在有效范围内复用它的offset
if (physical_extruder >= 0 && physical_extruder < static_cast<int>(physical_count)) {
m_extruder_offsets.emplace_back(m_extruder_offsets[physical_extruder]);
BOOST_LOG_TRIVIAL(debug) << "Filament " << i << " using offset from physical extruder " << physical_extruder;
} else {
// 否则使用默认offset
m_extruder_offsets.emplace_back(DEFAULT_EXTRUDER_OFFSET);
BOOST_LOG_TRIVIAL(warning) << "Filament " << i << " using default offset (physical extruder " << physical_extruder << " out of range)";
}
} }
} }
@@ -1117,6 +1383,34 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
const ConfigOptionFloat* z_offset = config.option<ConfigOptionFloat>("z_offset"); const ConfigOptionFloat* z_offset = config.option<ConfigOptionFloat>("z_offset");
if (z_offset != nullptr) if (z_offset != nullptr)
m_z_offset = z_offset->value; m_z_offset = z_offset->value;
// SM Orca: 验证所有数组大小是否正确
bool arrays_valid = true;
if (m_result.filament_diameters.size() != m_result.extruders_count) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: CRITICAL - filament_diameters size mismatch: "
<< m_result.filament_diameters.size() << " != " << m_result.extruders_count;
arrays_valid = false;
}
if (m_result.filament_densities.size() != m_result.extruders_count) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: CRITICAL - filament_densities size mismatch: "
<< m_result.filament_densities.size() << " != " << m_result.extruders_count;
arrays_valid = false;
}
if (m_result.filament_costs.size() != m_result.extruders_count) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: CRITICAL - filament_costs size mismatch: "
<< m_result.filament_costs.size() << " != " << m_result.extruders_count;
arrays_valid = false;
}
if (m_result.filament_vitrification_temperature.size() != m_result.extruders_count) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: CRITICAL - filament_vitrification_temperature size mismatch: "
<< m_result.filament_vitrification_temperature.size() << " != " << m_result.extruders_count;
arrays_valid = false;
}
if (arrays_valid) {
BOOST_LOG_TRIVIAL(info) << "SM Orca: DynamicPrintConfig array validation passed - all arrays correctly sized to "
<< m_result.extruders_count;
}
} }
void GCodeProcessor::enable_stealth_time_estimator(bool enabled) void GCodeProcessor::enable_stealth_time_estimator(bool enabled)
@@ -1556,6 +1850,23 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line, bool
// update start position // update start position
m_start_position = m_end_position; m_start_position = m_end_position;
// SM Orca: 防止NaN/inf从m_end_position传播到m_start_position
if (std::isnan(m_start_position[X]) || std::isinf(m_start_position[X]) ||
std::isnan(m_start_position[Y]) || std::isinf(m_start_position[Y]) ||
std::isnan(m_start_position[Z]) || std::isinf(m_start_position[Z])) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: Detected invalid m_start_position at line " << m_line_id
<< " extruder=" << static_cast<int>(m_extruder_id)
<< " m_start_position=(" << m_start_position[X] << ", " << m_start_position[Y] << ", " << m_start_position[Z] << ")"
<< " m_end_position=(" << m_end_position[X] << ", " << m_end_position[Y] << ", " << m_end_position[Z] << ")";
// 重置为原点,防止污染继续传播
m_start_position[X] = std::isnan(m_start_position[X]) || std::isinf(m_start_position[X]) ? 0.0f : m_start_position[X];
m_start_position[Y] = std::isnan(m_start_position[Y]) || std::isinf(m_start_position[Y]) ? 0.0f : m_start_position[Y];
m_start_position[Z] = std::isnan(m_start_position[Z]) || std::isinf(m_start_position[Z]) ? 0.0f : m_start_position[Z];
m_end_position[X] = std::isnan(m_end_position[X]) || std::isinf(m_end_position[X]) ? 0.0f : m_end_position[X];
m_end_position[Y] = std::isnan(m_end_position[Y]) || std::isinf(m_end_position[Y]) ? 0.0f : m_end_position[Y];
m_end_position[Z] = std::isnan(m_end_position[Z]) || std::isinf(m_end_position[Z]) ? 0.0f : m_end_position[Z];
}
const std::string_view cmd = line.cmd(); const std::string_view cmd = line.cmd();
if (m_flavor == gcfKlipper) if (m_flavor == gcfKlipper)
{ {
@@ -2635,6 +2946,22 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line, const std::o
m_end_position[a] = absolute_position((Axis)a, line); m_end_position[a] = absolute_position((Axis)a, line);
} }
// SM Orca: 验证position更新捕获NaN/inf的源头
if (std::isnan(m_end_position[X]) || std::isinf(m_end_position[X]) ||
std::isnan(m_end_position[Y]) || std::isinf(m_end_position[Y]) ||
std::isnan(m_end_position[Z]) || std::isinf(m_end_position[Z])) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: Invalid m_end_position after G1 processing for extruder " << static_cast<int>(m_extruder_id)
<< " m_end_position=(" << m_end_position[X] << ", " << m_end_position[Y] << ", " << m_end_position[Z] << ")"
<< " m_start_position=(" << m_start_position[X] << ", " << m_start_position[Y] << ", " << m_start_position[Z] << ")"
<< " m_origin=(" << m_origin[X] << ", " << m_origin[Y] << ", " << m_origin[Z] << ")"
<< " has_X=" << line.has(X) << " has_Y=" << line.has(Y) << " has_Z=" << line.has(Z)
<< " X_value=" << (line.has(X) ? line.value(X) : 0.0f)
<< " Y_value=" << (line.has(Y) ? line.value(Y) : 0.0f)
<< " Z_value=" << (line.has(Z) ? line.value(Z) : 0.0f)
<< " positioning=" << (m_global_positioning_type == EPositioningType::Relative ? "relative" : "absolute")
<< " units=" << (m_units == EUnits::Inches ? "inches" : "mm");
}
// updates feedrate from line, if present // updates feedrate from line, if present
if (line.has_f()) if (line.has_f())
m_feedrate = line.f() * MMMIN_TO_MMSEC; m_feedrate = line.f() * MMMIN_TO_MMSEC;
@@ -2698,13 +3025,35 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line, const std::o
else if (m_extrusion_role == erExternalPerimeter) else if (m_extrusion_role == erExternalPerimeter)
// cross section: rectangle // cross section: rectangle
m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(1.05f * filament_radius)) / (delta_xyz * m_height); m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(1.05f * filament_radius)) / (delta_xyz * m_height);
else if (m_extrusion_role == erBridgeInfill || m_extrusion_role == erInternalBridgeInfill || m_extrusion_role == erNone) else if (m_extrusion_role == erBridgeInfill || m_extrusion_role == erInternalBridgeInfill || m_extrusion_role == erNone) {
// SM Orca: 添加边界检查
float diameter = (static_cast<size_t>(m_extruder_id) < m_result.filament_diameters.size())
? m_result.filament_diameters[m_extruder_id]
: m_result.filament_diameters.back();
// SM Orca: 防止sqrt(负数)产生NaN
float ratio = delta_pos[E] / delta_xyz;
if (ratio < 0.0f) {
BOOST_LOG_TRIVIAL(warning) << "SM Orca: Negative E/XYZ ratio (" << ratio
<< ") for extruder " << m_extruder_id << ", using absolute value";
ratio = std::abs(ratio);
}
// cross section: circle // cross section: circle
m_width = static_cast<float>(m_result.filament_diameters[m_extruder_id]) * std::sqrt(delta_pos[E] / delta_xyz); m_width = diameter * std::sqrt(ratio);
}
else else
// cross section: rectangle + 2 semicircles // cross section: rectangle + 2 semicircles
m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(filament_radius)) / (delta_xyz * m_height) + static_cast<float>(1.0 - 0.25 * M_PI) * m_height; m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(filament_radius)) / (delta_xyz * m_height) + static_cast<float>(1.0 - 0.25 * M_PI) * m_height;
// SM Orca: 验证宽度计算结果防止NaN/inf传播
if (std::isnan(m_width) || std::isinf(m_width)) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: Invalid width calculated: " << m_width
<< " for extruder " << m_extruder_id
<< " (E=" << delta_pos[E] << ", XYZ=" << delta_xyz << ")";
m_width = DEFAULT_TOOLPATH_WIDTH;
}
if (m_width == 0.0f) if (m_width == 0.0f)
m_width = DEFAULT_TOOLPATH_WIDTH; m_width = DEFAULT_TOOLPATH_WIDTH;
@@ -2960,6 +3309,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line, const std::o
// check for seam starting vertex // check for seam starting vertex
if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) { if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) {
//BBS: m_result.moves.back().position has plate offset, must minus plate offset before calculate the real seam position //BBS: m_result.moves.back().position has plate offset, must minus plate offset before calculate the real seam position
// SM Orca: m_extruder_offsets[i] 已经在 apply_config 中映射过了,这里直接使用 m_extruder_id 作为索引
const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset; const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset;
if (!m_seams_detector.has_first_vertex()) { if (!m_seams_detector.has_first_vertex()) {
m_seams_detector.set_first_vertex(new_pos); m_seams_detector.set_first_vertex(new_pos);
@@ -2979,6 +3329,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line, const std::o
const Vec3f curr_pos(m_end_position[X], m_end_position[Y], m_end_position[Z]); const Vec3f curr_pos(m_end_position[X], m_end_position[Y], m_end_position[Z]);
//BBS: m_result.moves.back().position has plate offset, must minus plate offset before calculate the real seam position //BBS: m_result.moves.back().position has plate offset, must minus plate offset before calculate the real seam position
// SM Orca: m_extruder_offsets[i] 已经在 apply_config 中映射过了,这里直接使用 m_extruder_id 作为索引
const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset; const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset;
const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex(); const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex();
// the threshold value = 0.0625f == 0.25 * 0.25 is arbitrary, we may find some smarter condition later // the threshold value = 0.0625f == 0.25 * 0.25 is arbitrary, we may find some smarter condition later
@@ -2994,6 +3345,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line, const std::o
} }
else if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) { else if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) {
m_seams_detector.activate(true); m_seams_detector.activate(true);
// SM Orca: m_extruder_offsets[i] 已经在 apply_config 中映射过了,这里直接使用 m_extruder_id 作为索引
m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset); m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset);
} }
@@ -3086,6 +3438,23 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
for (unsigned char a = X; a <= E; ++a) { for (unsigned char a = X; a <= E; ++a) {
m_end_position[a] = absolute_position((Axis)a, line); m_end_position[a] = absolute_position((Axis)a, line);
} }
// SM Orca: 验证position更新捕获NaN/inf的源头
if (std::isnan(m_end_position[X]) || std::isinf(m_end_position[X]) ||
std::isnan(m_end_position[Y]) || std::isinf(m_end_position[Y]) ||
std::isnan(m_end_position[Z]) || std::isinf(m_end_position[Z])) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: Invalid m_end_position after G2/G3 processing for extruder " << static_cast<int>(m_extruder_id)
<< " m_end_position=(" << m_end_position[X] << ", " << m_end_position[Y] << ", " << m_end_position[Z] << ")"
<< " m_start_position=(" << m_start_position[X] << ", " << m_start_position[Y] << ", " << m_start_position[Z] << ")"
<< " m_origin=(" << m_origin[X] << ", " << m_origin[Y] << ", " << m_origin[Z] << ")"
<< " has_X=" << line.has(X) << " has_Y=" << line.has(Y) << " has_Z=" << line.has(Z)
<< " X_value=" << (line.has(X) ? line.value(X) : 0.0f)
<< " Y_value=" << (line.has(Y) ? line.value(Y) : 0.0f)
<< " Z_value=" << (line.has(Z) ? line.value(Z) : 0.0f)
<< " positioning=" << (m_global_positioning_type == EPositioningType::Relative ? "relative" : "absolute")
<< " units=" << (m_units == EUnits::Inches ? "inches" : "mm");
}
//BBS: G2 G3 line but has no I and J axis, invalid G code format //BBS: G2 G3 line but has no I and J axis, invalid G code format
if (!line.has(I) && !line.has(J)) if (!line.has(I) && !line.has(J))
return; return;
@@ -3178,13 +3547,35 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
else if (m_extrusion_role == erExternalPerimeter) else if (m_extrusion_role == erExternalPerimeter)
//BBS: cross section: rectangle //BBS: cross section: rectangle
m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(1.05f * filament_radius)) / (delta_xyz * m_height); m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(1.05f * filament_radius)) / (delta_xyz * m_height);
else if (m_extrusion_role == erBridgeInfill || m_extrusion_role == erInternalBridgeInfill || m_extrusion_role == erNone) else if (m_extrusion_role == erBridgeInfill || m_extrusion_role == erInternalBridgeInfill || m_extrusion_role == erNone) {
// SM Orca: 添加边界检查
float diameter = (static_cast<size_t>(m_extruder_id) < m_result.filament_diameters.size())
? m_result.filament_diameters[m_extruder_id]
: m_result.filament_diameters.back();
// SM Orca: 防止sqrt(负数)产生NaN
float ratio = delta_pos[E] / delta_xyz;
if (ratio < 0.0f) {
BOOST_LOG_TRIVIAL(warning) << "SM Orca: Negative E/XYZ ratio (" << ratio
<< ") for extruder " << m_extruder_id << ", using absolute value";
ratio = std::abs(ratio);
}
//BBS: cross section: circle //BBS: cross section: circle
m_width = static_cast<float>(m_result.filament_diameters[m_extruder_id]) * std::sqrt(delta_pos[E] / delta_xyz); m_width = diameter * std::sqrt(ratio);
}
else else
//BBS: cross section: rectangle + 2 semicircles //BBS: cross section: rectangle + 2 semicircles
m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(filament_radius)) / (delta_xyz * m_height) + static_cast<float>(1.0 - 0.25 * M_PI) * m_height; m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(filament_radius)) / (delta_xyz * m_height) + static_cast<float>(1.0 - 0.25 * M_PI) * m_height;
// SM Orca: 验证宽度计算结果防止NaN/inf传播
if (std::isnan(m_width) || std::isinf(m_width)) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: Invalid width calculated: " << m_width
<< " for extruder " << m_extruder_id
<< " (E=" << delta_pos[E] << ", XYZ=" << delta_xyz << ")";
m_width = DEFAULT_TOOLPATH_WIDTH;
}
if (m_width == 0.0f) if (m_width == 0.0f)
m_width = DEFAULT_TOOLPATH_WIDTH; m_width = DEFAULT_TOOLPATH_WIDTH;
@@ -3395,6 +3786,7 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
if (m_seams_detector.is_active()) { if (m_seams_detector.is_active()) {
//BBS: check for seam starting vertex //BBS: check for seam starting vertex
if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) { if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) {
// SM Orca: m_extruder_offsets[i] 已经在 apply_config 中映射过了,这里直接使用 m_extruder_id 作为索引
const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset; const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset;
if (!m_seams_detector.has_first_vertex()) { if (!m_seams_detector.has_first_vertex()) {
m_seams_detector.set_first_vertex(new_pos); m_seams_detector.set_first_vertex(new_pos);
@@ -3412,6 +3804,7 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
m_end_position[X] = pos.x(); m_end_position[Y] = pos.y(); m_end_position[Z] = pos.z(); m_end_position[X] = pos.x(); m_end_position[Y] = pos.y(); m_end_position[Z] = pos.z();
}; };
const Vec3f curr_pos(m_end_position[X], m_end_position[Y], m_end_position[Z]); const Vec3f curr_pos(m_end_position[X], m_end_position[Y], m_end_position[Z]);
// SM Orca: m_extruder_offsets[i] 已经在 apply_config 中映射过了,这里直接使用 m_extruder_id 作为索引
const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset; const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset;
const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex(); const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex();
//BBS: the threshold value = 0.0625f == 0.25 * 0.25 is arbitrary, we may find some smarter condition later //BBS: the threshold value = 0.0625f == 0.25 * 0.25 is arbitrary, we may find some smarter condition later
@@ -3427,6 +3820,7 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
} }
else if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) { else if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) {
m_seams_detector.activate(true); m_seams_detector.activate(true);
// SM Orca: m_extruder_offsets[i] 已经在 apply_config 中映射过了,这里直接使用 m_extruder_id 作为索引
m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset); m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset);
} }
@@ -3472,17 +3866,21 @@ void GCodeProcessor::process_G29(const GCodeReader::GCodeLine& line)
void GCodeProcessor::process_G10(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_G10(const GCodeReader::GCodeLine& line)
{ {
// SM Orca: 回抽参数是挤出机属性,使用 physical_extruder
int physical_extruder = get_physical_extruder(m_extruder_id);
GCodeReader::GCodeLine g10; GCodeReader::GCodeLine g10;
g10.set(Axis::E, -this->m_parser.config().retraction_length.get_at(m_extruder_id)); g10.set(Axis::E, -this->m_parser.config().retraction_length.get_at(physical_extruder));
g10.set(Axis::F, this->m_parser.config().retraction_speed.get_at(m_extruder_id) * 60); g10.set(Axis::F, this->m_parser.config().retraction_speed.get_at(physical_extruder) * 60);
process_G1(g10); process_G1(g10);
} }
void GCodeProcessor::process_G11(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_G11(const GCodeReader::GCodeLine& line)
{ {
// SM Orca: 回抽参数是挤出机属性,使用 physical_extruder
int physical_extruder = get_physical_extruder(m_extruder_id);
GCodeReader::GCodeLine g11; GCodeReader::GCodeLine g11;
g11.set(Axis::E, this->m_parser.config().retraction_length.get_at(m_extruder_id) + this->m_parser.config().retract_restart_extra.get_at(m_extruder_id)); g11.set(Axis::E, this->m_parser.config().retraction_length.get_at(physical_extruder) + this->m_parser.config().retract_restart_extra.get_at(physical_extruder));
g11.set(Axis::F, this->m_parser.config().deretraction_speed.get_at(m_extruder_id) * 60); g11.set(Axis::F, this->m_parser.config().deretraction_speed.get_at(physical_extruder) * 60);
process_G1(g11); process_G1(g11);
} }
@@ -3580,6 +3978,20 @@ void GCodeProcessor::process_G92(const GCodeReader::GCodeLine& line)
m_origin[a] = m_end_position[a]; m_origin[a] = m_end_position[a];
} }
} }
// SM Orca: 验证m_origin防止NaN/inf传播
if (std::isnan(m_origin[X]) || std::isinf(m_origin[X]) ||
std::isnan(m_origin[Y]) || std::isinf(m_origin[Y]) ||
std::isnan(m_origin[Z]) || std::isinf(m_origin[Z])) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: Invalid m_origin after G92 processing"
<< " extruder=" << static_cast<int>(m_extruder_id)
<< " m_origin=(" << m_origin[X] << ", " << m_origin[Y] << ", " << m_origin[Z] << ")"
<< " m_end_position=(" << m_end_position[X] << ", " << m_end_position[Y] << ", " << m_end_position[Z] << ")";
// 重置为0防止污染继续传播
m_origin[X] = std::isnan(m_origin[X]) || std::isinf(m_origin[X]) ? 0.0f : m_origin[X];
m_origin[Y] = std::isnan(m_origin[Y]) || std::isinf(m_origin[Y]) ? 0.0f : m_origin[Y];
m_origin[Z] = std::isnan(m_origin[Z]) || std::isinf(m_origin[Z]) ? 0.0f : m_origin[Z];
}
} }
void GCodeProcessor::process_M1(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_M1(const GCodeReader::GCodeLine& line)
@@ -4045,12 +4457,54 @@ void GCodeProcessor::run_post_process()
double filament_total_cost = 0.0; double filament_total_cost = 0.0;
for (const auto& [id, volume] : m_result.print_statistics.total_volumes_per_extruder) { for (const auto& [id, volume] : m_result.print_statistics.total_volumes_per_extruder) {
filament_mm[id] = volume / (static_cast<double>(M_PI) * sqr(0.5 * m_result.filament_diameters[id])); // SM Orca: 边界检查 - 确保id在有效范围内
if (id >= m_result.filament_diameters.size() ||
id >= m_result.filament_densities.size() ||
id >= m_result.filament_costs.size()) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: Filament index " << id << " out of bounds (sizes: "
<< "diameter=" << m_result.filament_diameters.size()
<< ", density=" << m_result.filament_densities.size()
<< ", cost=" << m_result.filament_costs.size() << "), skipping cost calculation";
continue;
}
// SM Orca: 读取并验证配置值
double diameter = m_result.filament_diameters[id];
double density = m_result.filament_densities[id];
double cost = m_result.filament_costs[id];
// SM Orca: 防止除零和NaN传播
if (diameter <= 0.0 || std::isnan(diameter)) {
BOOST_LOG_TRIVIAL(warning) << "SM Orca: Invalid filament diameter " << diameter
<< " for filament " << id << ", using default 1.75mm";
diameter = 1.75;
}
if (density <= 0.0 || std::isnan(density)) {
BOOST_LOG_TRIVIAL(warning) << "SM Orca: Invalid filament density " << density
<< " for filament " << id << ", using default 1.25 g/cm³";
density = 1.25;
}
if (cost < 0.0 || std::isnan(cost)) {
BOOST_LOG_TRIVIAL(warning) << "SM Orca: Invalid filament cost " << cost
<< " for filament " << id << ", using 0.0";
cost = 0.0;
}
// SM Orca: 执行成本计算
double cross_section = M_PI * sqr(0.5 * diameter);
filament_mm[id] = volume / cross_section;
filament_cm3[id] = volume * 0.001; filament_cm3[id] = volume * 0.001;
filament_g[id] = filament_cm3[id] * double(m_result.filament_densities[id]); filament_g[id] = filament_cm3[id] * density;
filament_cost[id] = filament_g[id] * double(m_result.filament_costs[id]) * 0.001; filament_cost[id] = filament_g[id] * cost * 0.001;
filament_total_g += filament_g[id]; filament_total_g += filament_g[id];
filament_total_cost += filament_cost[id]; filament_total_cost += filament_cost[id];
BOOST_LOG_TRIVIAL(debug) << "SM Orca: Filament " << id
<< " - volume: " << volume << "mm³, length: " << filament_mm[id]
<< "mm, weight: " << filament_g[id] << "g, cost: " << filament_cost[id];
} }
double total_g_wipe_tower = m_print->print_statistics().total_wipe_tower_filament; double total_g_wipe_tower = m_print->print_statistics().total_wipe_tower_filament;
@@ -4771,6 +5225,11 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type)
m_line_id + 1 : m_line_id + 1 :
((type == EMoveType::Seam) ? m_last_line_id : m_line_id); ((type == EMoveType::Seam) ? m_last_line_id : m_line_id);
// SM Orca: 添加边界检查,防止访问越界
Vec3f extruder_offset = (static_cast<size_t>(m_extruder_id) < m_extruder_offsets.size())
? m_extruder_offsets[m_extruder_id]
: Vec3f(0.0f, 0.0f, 0.0f);
//BBS: apply plate's and extruder's offset to arc interpolation points //BBS: apply plate's and extruder's offset to arc interpolation points
if (path_type == EMovePathType::Arc_move_cw || if (path_type == EMovePathType::Arc_move_cw ||
path_type == EMovePathType::Arc_move_ccw) { path_type == EMovePathType::Arc_move_ccw) {
@@ -4779,7 +5238,40 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type)
Vec3f(m_interpolation_points[i].x() + m_x_offset, Vec3f(m_interpolation_points[i].x() + m_x_offset,
m_interpolation_points[i].y() + m_y_offset, m_interpolation_points[i].y() + m_y_offset,
m_processing_start_custom_gcode ? m_first_layer_height : m_interpolation_points[i].z()) + m_processing_start_custom_gcode ? m_first_layer_height : m_interpolation_points[i].z()) +
m_extruder_offsets[m_extruder_id]; extruder_offset;
}
// SM Orca: 最后一道防线防止无效值进入result
if (std::isnan(m_width) || std::isinf(m_width)) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: Blocking invalid width: " << m_width
<< " (extruder " << m_extruder_id << ")";
m_width = DEFAULT_TOOLPATH_WIDTH;
}
if (std::isnan(m_height) || std::isinf(m_height)) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: Blocking invalid height: " << m_height
<< " (extruder " << m_extruder_id << ")";
m_height = DEFAULT_TOOLPATH_HEIGHT;
}
// SM Orca: 验证position防止NaN/inf进入moves
Vec3f final_position = Vec3f(m_end_position[X] + m_x_offset,
m_end_position[Y] + m_y_offset,
m_processing_start_custom_gcode ? m_first_layer_height : m_end_position[Z] - m_z_offset)
+ extruder_offset;
if (std::isnan(final_position.x()) || std::isinf(final_position.x()) ||
std::isnan(final_position.y()) || std::isinf(final_position.y()) ||
std::isnan(final_position.z()) || std::isinf(final_position.z())) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: Invalid position calculated for extruder " << static_cast<int>(m_extruder_id)
<< " position=(" << final_position.x() << ", " << final_position.y() << ", " << final_position.z() << ")"
<< " m_end_position=(" << m_end_position[X] << ", " << m_end_position[Y] << ", " << m_end_position[Z] << ")"
<< " offset=(" << m_x_offset << ", " << m_y_offset << ", " << m_z_offset << ")"
<< " extruder_offset=(" << extruder_offset.x() << ", " << extruder_offset.y() << ", " << extruder_offset.z() << ")";
// 使用不带offset的position作为fallback
final_position = Vec3f(m_end_position[X] + m_x_offset,
m_end_position[Y] + m_y_offset,
m_processing_start_custom_gcode ? m_first_layer_height : m_end_position[Z] - m_z_offset);
} }
m_result.moves.push_back({ m_result.moves.push_back({
@@ -4789,7 +5281,8 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type)
m_extruder_id, m_extruder_id,
m_cp_color.current, m_cp_color.current,
//BBS: add plate's offset to the rendering vertices //BBS: add plate's offset to the rendering vertices
Vec3f(m_end_position[X] + m_x_offset, m_end_position[Y] + m_y_offset, m_processing_start_custom_gcode ? m_first_layer_height : m_end_position[Z]- m_z_offset) + m_extruder_offsets[m_extruder_id], // SM Orca: 使用验证过的final_position
final_position,
static_cast<float>(m_end_position[E] - m_start_position[E]), static_cast<float>(m_end_position[E] - m_start_position[E]),
m_feedrate, m_feedrate,
m_width, m_width,
@@ -4802,6 +5295,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type)
static_cast<float>(m_layer_id), //layer_duration: set later static_cast<float>(m_layer_id), //layer_duration: set later
//BBS: add arc move related data //BBS: add arc move related data
path_type, path_type,
// SM Orca: m_extruder_offsets[i] 已经在 apply_config 中映射过了,这里直接使用 m_extruder_id 作为索引
Vec3f(m_arc_center(0, 0) + m_x_offset, m_arc_center(1, 0) + m_y_offset, m_arc_center(2, 0)) + m_extruder_offsets[m_extruder_id], Vec3f(m_arc_center(0, 0) + m_x_offset, m_arc_center(1, 0) + m_y_offset, m_arc_center(2, 0)) + m_extruder_offsets[m_extruder_id],
m_interpolation_points, m_interpolation_points,
}); });

View File

@@ -14,6 +14,7 @@
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <optional> #include <optional>
#include <unordered_map>
namespace Slic3r { namespace Slic3r {
@@ -677,6 +678,8 @@ class Print;
EPositioningType m_global_positioning_type; EPositioningType m_global_positioning_type;
EPositioningType m_e_local_positioning_type; EPositioningType m_e_local_positioning_type;
std::vector<Vec3f> m_extruder_offsets; std::vector<Vec3f> m_extruder_offsets;
// SM Orca: 耗材到物理挤出机的映射
std::unordered_map<int, int> m_filament_extruder_map;
GCodeFlavor m_flavor; GCodeFlavor m_flavor;
float m_nozzle_volume; float m_nozzle_volume;
AxisCoords m_start_position; // mm AxisCoords m_start_position; // mm
@@ -776,6 +779,21 @@ class Print;
void apply_config(const PrintConfig& config); void apply_config(const PrintConfig& config);
void set_print(Print* print) { m_print = print; } void set_print(Print* print) { m_print = print; }
// SM Orca: 设置耗材到物理挤出机的映射
void set_filament_extruder_map(const std::unordered_map<int, int>& map) {
m_filament_extruder_map = map;
}
// SM Orca: 获取物理挤出机ID根据耗材索引
int get_physical_extruder(int filament_idx) const {
auto it = m_filament_extruder_map.find(filament_idx);
int physical_extruder_id = (it != m_filament_extruder_map.end()) ? it->second : filament_idx;
// SM Orca: 日志 - 映射查询
BOOST_LOG_TRIVIAL(info) << "GCodeProcessor::get_physical_extruder: filament_id=" << filament_idx
<< " -> physical_extruder_id=" << physical_extruder_id
<< " (map_size=" << m_filament_extruder_map.size() << ")"
<< (it != m_filament_extruder_map.end() ? " [from_map]" : " [default_identity]");
return physical_extruder_id;
}
void enable_stealth_time_estimator(bool enabled); void enable_stealth_time_estimator(bool enabled);
bool is_stealth_time_estimator_enabled() const { bool is_stealth_time_estimator_enabled() const {
return m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled; return m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled;

View File

@@ -668,18 +668,20 @@ WipeTower::WipeTower(const PrintConfig& config, int plate_idx, Vec3d plate_origi
void WipeTower::set_extruder(size_t idx, const PrintConfig& config) void WipeTower::set_extruder(size_t idx, int physical_extruder, const PrintConfig& config)
{ {
//while (m_filpar.size() < idx+1) // makes sure the required element is in the vector //while (m_filpar.size() < idx+1) // makes sure the required element is in the vector
m_filpar.push_back(FilamentParameters()); m_filpar.push_back(FilamentParameters());
// SM Orca: 耗材属性使用 idx (filament index)
m_filpar[idx].material = config.filament_type.get_at(idx); m_filpar[idx].material = config.filament_type.get_at(idx);
// m_filpar[idx].is_soluble = config.filament_soluble.get_at(idx); // m_filpar[idx].is_soluble = config.filament_soluble.get_at(idx);
m_filpar[idx].is_soluble = config.wipe_tower_filament == 0 ? config.filament_soluble.get_at(idx) : (idx != size_t(config.wipe_tower_filament - 1)); m_filpar[idx].is_soluble = config.wipe_tower_filament == 0 ? config.filament_soluble.get_at(idx) : (idx != size_t(config.wipe_tower_filament - 1));
// BBS // BBS
m_filpar[idx].is_support = config.filament_is_support.get_at(idx); m_filpar[idx].is_support = config.filament_is_support.get_at(idx);
m_filpar[idx].nozzle_temperature = config.nozzle_temperature.get_at(idx); // SM Orca: 温度是挤出机属性,使用 physical_extruder
m_filpar[idx].nozzle_temperature_initial_layer = config.nozzle_temperature_initial_layer.get_at(idx); m_filpar[idx].nozzle_temperature = config.nozzle_temperature.get_at(physical_extruder);
m_filpar[idx].nozzle_temperature_initial_layer = config.nozzle_temperature_initial_layer.get_at(physical_extruder);
// If this is a single extruder MM printer, we will use all the SE-specific config values. // If this is a single extruder MM printer, we will use all the SE-specific config values.
// Otherwise, the defaults will be used to turn off the SE stuff. // Otherwise, the defaults will be used to turn off the SE stuff.
@@ -698,14 +700,19 @@ void WipeTower::set_extruder(size_t idx, const PrintConfig& config)
#endif #endif
m_filpar[idx].filament_area = float((M_PI/4.f) * pow(config.filament_diameter.get_at(idx), 2)); // all extruders are assumed to have the same filament diameter at this point m_filpar[idx].filament_area = float((M_PI/4.f) * pow(config.filament_diameter.get_at(idx), 2)); // all extruders are assumed to have the same filament diameter at this point
float nozzle_diameter = float(config.nozzle_diameter.get_at(idx)); // SM Orca: 喷嘴直径是挤出机属性,使用 physical_extruder
float nozzle_diameter = float(config.nozzle_diameter.get_at(physical_extruder));
m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
float max_vol_speed = float(config.filament_max_volumetric_speed.get_at(idx)); float max_vol_speed = float(config.filament_max_volumetric_speed.get_at(idx));
if (max_vol_speed!= 0.f) if (max_vol_speed!= 0.f)
m_filpar[idx].max_e_speed = (max_vol_speed / filament_area()); m_filpar[idx].max_e_speed = (max_vol_speed / filament_area());
m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter // SM Orca: Store per-filament perimeter width and also set the global one
// Note: m_perimeter_width gets overwritten with each set_extruder() call
// The brim should use m_filpar[0].perimeter_width for consistency
m_filpar[idx].perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio;
m_perimeter_width = m_filpar[idx].perimeter_width; // all extruders are now assumed to have the same diameter
// BBS: remove useless config // BBS: remove useless config
#if 0 #if 0
if (m_semm) { if (m_semm) {
@@ -1305,7 +1312,10 @@ WipeTower::ToolChangeResult WipeTower::finish_layer(bool extrude_perimeter, bool
} }
// brim chamfer // brim chamfer
float spacing = m_perimeter_width - m_layer_height * float(1. - M_PI_4); // SM Orca: Use first filament's perimeter width for consistent brim spacing
// The brim is generated once for the entire wipe tower and should use a consistent spacing
float brim_perimeter_width = m_filpar.empty() ? m_perimeter_width : m_filpar[0].perimeter_width;
float spacing = brim_perimeter_width - m_layer_height * float(1. - M_PI_4);
// How many perimeters shall the brim have? // How many perimeters shall the brim have?
int loops_num = (m_wipe_tower_brim_width + spacing / 2.f) / spacing; int loops_num = (m_wipe_tower_brim_width + spacing / 2.f) / spacing;
const float max_chamfer_width = 3.f; const float max_chamfer_width = 3.f;

View File

@@ -144,7 +144,8 @@ public:
// Set the extruder properties. // Set the extruder properties.
void set_extruder(size_t idx, const PrintConfig& config); // SM Orca: 添加 physical_extruder 参数,用于支持耗材-挤出机映射
void set_extruder(size_t idx, int physical_extruder, const PrintConfig& config);
// Appends into internal structure m_plan containing info about the future wipe tower // Appends into internal structure m_plan containing info about the future wipe tower
// to be used before building begins. The entries must be added ordered in z. // to be used before building begins. The entries must be added ordered in z.
@@ -269,6 +270,8 @@ public:
std::vector<float> ramming_speed; std::vector<float> ramming_speed;
float nozzle_diameter; float nozzle_diameter;
float filament_area; float filament_area;
// SM Orca: Store per-filament perimeter width for correct brim generation
float perimeter_width = 0.f;
}; };
private: private:

File diff suppressed because it is too large Load Diff

View File

@@ -45,7 +45,9 @@ public:
// Set the extruder properties. // Set the extruder properties.
void set_extruder(size_t idx, const PrintConfig& config); // SM Orca: 添加 physical_extruder 参数,用于支持耗材-挤出机映射
// idx: 耗材索引, physical_extruder: 物理挤出机索引
void set_extruder(size_t idx, int physical_extruder, const PrintConfig& config);
// Appends into internal structure m_plan containing info about the future wipe tower // Appends into internal structure m_plan containing info about the future wipe tower
// to be used before building begins. The entries must be added ordered in z. // to be used before building begins. The entries must be added ordered in z.
@@ -160,6 +162,8 @@ public:
float filament_minimal_purge_on_wipe_tower = 0.f; float filament_minimal_purge_on_wipe_tower = 0.f;
float retract_length; float retract_length;
float retract_speed; float retract_speed;
// SM Orca: Store per-filament perimeter width for correct brim generation
float perimeter_width = 0.f;
}; };
private: private:

View File

@@ -45,17 +45,26 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config)
void GCodeWriter::set_extruders(std::vector<unsigned int> extruder_ids) void GCodeWriter::set_extruders(std::vector<unsigned int> extruder_ids)
{ {
BOOST_LOG_TRIVIAL(info) << "GCodeWriter::set_extruders: START - Creating Extruder objects for " << extruder_ids.size() << " extruders";
BOOST_LOG_TRIVIAL(info) << "GCodeWriter::set_extruders: filament_extruder_map size=" << m_filament_extruder_map.size();
std::sort(extruder_ids.begin(), extruder_ids.end()); std::sort(extruder_ids.begin(), extruder_ids.end());
m_extruder = nullptr; // this points to object inside `m_extruders`, so should be cleared too m_extruder = nullptr; // this points to object inside `m_extruders`, so should be cleared too
m_extruders.clear(); m_extruders.clear();
m_extruders.reserve(extruder_ids.size()); m_extruders.reserve(extruder_ids.size());
for (unsigned int extruder_id : extruder_ids) // SM Orca: 创建 Extruder 对象时传递物理挤出机ID
m_extruders.emplace_back(Extruder(extruder_id, &this->config, config.single_extruder_multi_material.value)); for (unsigned int extruder_id : extruder_ids) {
int physical_extruder_id = get_physical_extruder(extruder_id);
BOOST_LOG_TRIVIAL(info) << "GCodeWriter::set_extruders: Creating Extruder - filament_id=" << extruder_id
<< " -> physical_extruder_id=" << physical_extruder_id;
m_extruders.emplace_back(Extruder(extruder_id, physical_extruder_id, &this->config, config.single_extruder_multi_material.value));
}
/* we enable support for multiple extruder if any extruder greater than 0 is used /* we enable support for multiple extruder if any extruder greater than 0 is used
(even if prints only uses that one) since we need to output Tx commands (even if prints only uses that one) since we need to output Tx commands
first extruder has index 0 */ first extruder has index 0 */
this->multiple_extruders = (*std::max_element(extruder_ids.begin(), extruder_ids.end())) > 0; this->multiple_extruders = (*std::max_element(extruder_ids.begin(), extruder_ids.end())) > 0;
BOOST_LOG_TRIVIAL(info) << "GCodeWriter::set_extruders: END - multiple_extruders=" << this->multiple_extruders;
} }
std::string GCodeWriter::preamble() std::string GCodeWriter::preamble()
@@ -454,6 +463,12 @@ std::string GCodeWriter::toolchange_prefix() const
std::string GCodeWriter::toolchange(unsigned int extruder_id) std::string GCodeWriter::toolchange(unsigned int extruder_id)
{ {
BOOST_LOG_TRIVIAL(info) << "SM Orca: GCodeWriter::toolchange(" << extruder_id << ") called";
// SM Orca: 记录映射信息
int physical_extruder = get_physical_extruder(extruder_id);
BOOST_LOG_TRIVIAL(info) << "SM Orca: Toolchange - filament " << extruder_id << " maps to physical extruder " << physical_extruder << " (mapping table size: " << m_filament_extruder_map.size() << ")";
// set the new extruder // set the new extruder
auto it_extruder = Slic3r::lower_bound_by_predicate(m_extruders.begin(), m_extruders.end(), [extruder_id](const Extruder &e) { return e.id() < extruder_id; }); auto it_extruder = Slic3r::lower_bound_by_predicate(m_extruders.begin(), m_extruders.end(), [extruder_id](const Extruder &e) { return e.id() < extruder_id; });
assert(it_extruder != m_extruders.end() && it_extruder->id() == extruder_id); assert(it_extruder != m_extruders.end() && it_extruder->id() == extruder_id);
@@ -463,12 +478,16 @@ std::string GCodeWriter::toolchange(unsigned int extruder_id)
// if we are running a single-extruder setup, just set the extruder and return nothing // if we are running a single-extruder setup, just set the extruder and return nothing
std::ostringstream gcode; std::ostringstream gcode;
if (this->multiple_extruders || (this->config.filament_diameter.values.size() > 1 && !is_bbl_printers())) { if (this->multiple_extruders || (this->config.filament_diameter.values.size() > 1 && !is_bbl_printers())) {
// SM Orca: T命令使用耗材序号extruder_id物理换头由固件处理
BOOST_LOG_TRIVIAL(info) << "SM Orca: Generating T command: T" << extruder_id;
gcode << this->toolchange_prefix() << extruder_id; gcode << this->toolchange_prefix() << extruder_id;
//BBS //BBS
if (GCodeWriter::full_gcode_comment) if (GCodeWriter::full_gcode_comment)
gcode << " ; change extruder"; gcode << " ; change extruder";
gcode << "\n"; gcode << "\n";
gcode << this->reset_e(true); gcode << this->reset_e(true);
} else {
BOOST_LOG_TRIVIAL(info) << "SM Orca: Toolchange - Single extruder mode, no T command generated";
} }
return gcode.str(); return gcode.str();
} }
@@ -489,6 +508,14 @@ std::string GCodeWriter::set_speed(double F, const std::string &comment, const s
std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &comment) std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &comment)
{ {
// SM Orca: 诊断NaN输入
if (std::isnan(point(0)) || std::isinf(point(0)) || std::isnan(point(1)) || std::isinf(point(1))) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: travel_to_xy received NaN/inf point"
<< " extruder=" << (m_extruder ? m_extruder->id() : -1)
<< " point=(" << point(0) << ", " << point(1) << ")"
<< " comment=" << comment;
}
m_pos(0) = point(0); m_pos(0) = point(0);
m_pos(1) = point(1); m_pos(1) = point(1);
@@ -709,6 +736,15 @@ bool GCodeWriter::will_move_z(double z) const
std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std::string &comment, bool force_no_extrusion) std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std::string &comment, bool force_no_extrusion)
{ {
// SM Orca: 诊断NaN输入
if (std::isnan(point(0)) || std::isinf(point(0)) || std::isnan(point(1)) || std::isinf(point(1))) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: extrude_to_xy received NaN/inf point"
<< " extruder=" << (m_extruder ? m_extruder->id() : -1)
<< " point=(" << point(0) << ", " << point(1) << ")"
<< " dE=" << dE
<< " comment=" << comment;
}
m_pos(0) = point(0); m_pos(0) = point(0);
m_pos(1) = point(1); m_pos(1) = point(1);
if(std::abs(dE) <= std::numeric_limits<double>::epsilon()) if(std::abs(dE) <= std::numeric_limits<double>::epsilon())

View File

@@ -4,6 +4,7 @@
#include "libslic3r.h" #include "libslic3r.h"
#include <string> #include <string>
#include <charconv> #include <charconv>
#include <unordered_map>
#include "Extruder.hpp" #include "Extruder.hpp"
#include "Point.hpp" #include "Point.hpp"
#include "PrintConfig.hpp" #include "PrintConfig.hpp"
@@ -119,6 +120,21 @@ public:
void set_is_first_layer(bool bval) { m_is_first_layer = bval; } void set_is_first_layer(bool bval) { m_is_first_layer = bval; }
GCodeFlavor get_gcode_flavor() const { return config.gcode_flavor; } GCodeFlavor get_gcode_flavor() const { return config.gcode_flavor; }
// SM Orca: 设置耗材-挤出机映射
void set_filament_extruder_map(const std::unordered_map<int, int>& map) { m_filament_extruder_map = map; }
const std::unordered_map<int, int>& get_filament_extruder_map() const { return m_filament_extruder_map; }
// SM Orca: 获取物理挤出机ID如果没有映射返回耗材ID本身
int get_physical_extruder(int filament_idx) const {
auto it = m_filament_extruder_map.find(filament_idx);
int physical_extruder_id = (it != m_filament_extruder_map.end()) ? it->second : filament_idx;
// SM Orca: 日志 - 映射查询
BOOST_LOG_TRIVIAL(info) << "GCodeWriter::get_physical_extruder: filament_id=" << filament_idx
<< " -> physical_extruder_id=" << physical_extruder_id
<< " (map_size=" << m_filament_extruder_map.size() << ")"
<< (it != m_filament_extruder_map.end() ? " [from_map]" : " [default_identity]");
return physical_extruder_id;
}
// Returns whether this flavor supports separate print and travel acceleration. // Returns whether this flavor supports separate print and travel acceleration.
static bool supports_separate_travel_acceleration(GCodeFlavor flavor); static bool supports_separate_travel_acceleration(GCodeFlavor flavor);
private: private:
@@ -170,6 +186,9 @@ public:
double m_current_speed; double m_current_speed;
bool m_is_first_layer = true; bool m_is_first_layer = true;
// SM Orca: 耗材到物理挤出机的映射表filament_idx -> physical_extruder_id
std::unordered_map<int, int> m_filament_extruder_map;
enum class Acceleration { enum class Acceleration {
Travel, Travel,
Print Print

View File

@@ -293,8 +293,6 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|| opt_key == "wipe_tower_no_sparse_layers" || opt_key == "wipe_tower_no_sparse_layers"
|| opt_key == "flush_volumes_matrix" || opt_key == "flush_volumes_matrix"
|| opt_key == "prime_volume" || opt_key == "prime_volume"
|| opt_key == "prime_tower_brim_chamfer"
|| opt_key == "prime_tower_brim_chamfer_max_width"
|| opt_key == "flush_into_infill" || opt_key == "flush_into_infill"
|| opt_key == "flush_into_support" || opt_key == "flush_into_support"
|| opt_key == "initial_layer_infill_speed" || opt_key == "initial_layer_infill_speed"
@@ -495,6 +493,44 @@ std::vector<unsigned int> Print::extruders(bool conside_custom_gcode) const
return extruders; return extruders;
} }
// SM Orca: Initialize filament-to-physical-extruder mapping
// This must be called before the mapping is used (e.g., before export_gcode)
void Print::initialize_filament_extruder_map()
{
m_filament_extruder_map.clear();
// Get the number of physical extruders (number of nozzle_diameter entries)
size_t physical_extruder_count = m_config.nozzle_diameter.values.size();
if (physical_extruder_count == 0) {
BOOST_LOG_TRIVIAL(warning) << "Print::initialize_filament_extruder_map: No physical extruders configured!";
return;
}
// Get all filament indices that will be used
std::vector<unsigned int> filament_extruders = this->extruders();
BOOST_LOG_TRIVIAL(info) << "Print::initialize_filament_extruder_map: Initializing with "
<< physical_extruder_count << " physical extruders and "
<< filament_extruders.size() << " filaments to map";
// Create mapping: filament_id -> physical_extruder_id
// Mapping formula: physical_extruder = filament_id % physical_extruder_count
// This allows using 8 filaments with 4 physical extruders:
// filament 0,1,2,3 -> extruder 0,1,2,3
// filament 4,5,6,7 -> extruder 0,1,2,3
for (unsigned int filament_idx : filament_extruders) {
int physical_extruder = filament_idx % physical_extruder_count;
m_filament_extruder_map[filament_idx] = physical_extruder;
BOOST_LOG_TRIVIAL(info) << "Print::initialize_filament_extruder_map: filament "
<< filament_idx << " -> physical_extruder " << physical_extruder;
}
BOOST_LOG_TRIVIAL(info) << "Print::initialize_filament_extruder_map: Map initialized with "
<< m_filament_extruder_map.size() << " entries";
}
unsigned int Print::num_object_instances() const unsigned int Print::num_object_instances() const
{ {
unsigned int instances = 0; unsigned int instances = 0;
@@ -506,8 +542,11 @@ unsigned int Print::num_object_instances() const
double Print::max_allowed_layer_height() const double Print::max_allowed_layer_height() const
{ {
double nozzle_diameter_max = 0.; double nozzle_diameter_max = 0.;
for (unsigned int extruder_id : this->extruders()) for (unsigned int extruder_id : this->extruders()) {
nozzle_diameter_max = std::max(nozzle_diameter_max, m_config.nozzle_diameter.get_at(extruder_id)); // SM Orca: 使用物理挤出机的喷嘴直径
int physical_extruder = get_physical_extruder(extruder_id);
nozzle_diameter_max = std::max(nozzle_diameter_max, m_config.nozzle_diameter.get_at(physical_extruder));
}
return nozzle_diameter_max; return nozzle_diameter_max;
} }
@@ -1185,10 +1224,13 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
if (this->has_wipe_tower() && ! m_objects.empty()) { if (this->has_wipe_tower() && ! m_objects.empty()) {
// Make sure all extruders use same diameter filament and have the same nozzle diameter // Make sure all extruders use same diameter filament and have the same nozzle diameter
// EPSILON comparison is used for nozzles and 10 % tolerance is used for filaments // EPSILON comparison is used for nozzles and 10 % tolerance is used for filaments
double first_nozzle_diam = m_config.nozzle_diameter.get_at(extruders.front()); // SM Orca: 使用物理挤出机的喷嘴直径
int first_physical = get_physical_extruder(extruders.front());
double first_nozzle_diam = m_config.nozzle_diameter.get_at(first_physical);
double first_filament_diam = m_config.filament_diameter.get_at(extruders.front()); double first_filament_diam = m_config.filament_diameter.get_at(extruders.front());
for (const auto& extruder_idx : extruders) { for (const auto& extruder_idx : extruders) {
double nozzle_diam = m_config.nozzle_diameter.get_at(extruder_idx); int physical_extruder = get_physical_extruder(extruder_idx);
double nozzle_diam = m_config.nozzle_diameter.get_at(physical_extruder);
double filament_diam = m_config.filament_diameter.get_at(extruder_idx); double filament_diam = m_config.filament_diameter.get_at(extruder_idx);
if (nozzle_diam - EPSILON > first_nozzle_diam || nozzle_diam + EPSILON < first_nozzle_diam if (nozzle_diam - EPSILON > first_nozzle_diam || nozzle_diam + EPSILON < first_nozzle_diam
|| std::abs((filament_diam - first_filament_diam) / first_filament_diam) > 0.1) { || std::abs((filament_diam - first_filament_diam) / first_filament_diam) > 0.1) {
@@ -1294,7 +1336,9 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
double min_nozzle_diameter = std::numeric_limits<double>::max(); double min_nozzle_diameter = std::numeric_limits<double>::max();
double max_nozzle_diameter = 0; double max_nozzle_diameter = 0;
for (unsigned int extruder_id : extruders) { for (unsigned int extruder_id : extruders) {
double dmr = m_config.nozzle_diameter.get_at(extruder_id); // SM Orca: 使用物理挤出机的喷嘴直径
int physical_extruder = get_physical_extruder(extruder_id);
double dmr = m_config.nozzle_diameter.get_at(physical_extruder);
min_nozzle_diameter = std::min(min_nozzle_diameter, dmr); min_nozzle_diameter = std::min(min_nozzle_diameter, dmr);
max_nozzle_diameter = std::max(max_nozzle_diameter, dmr); max_nozzle_diameter = std::max(max_nozzle_diameter, dmr);
} }
@@ -1382,9 +1426,11 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
size_t first_layer_extruder = object->config().raft_layers == 1 size_t first_layer_extruder = object->config().raft_layers == 1
? object->config().support_interface_filament-1 ? object->config().support_interface_filament-1
: object->config().support_filament-1; : object->config().support_filament-1;
// SM Orca: 使用物理挤出机的喷嘴直径
int physical_extruder = get_physical_extruder(first_layer_extruder);
first_layer_min_nozzle_diameter = (first_layer_extruder == size_t(-1)) ? first_layer_min_nozzle_diameter = (first_layer_extruder == size_t(-1)) ?
min_nozzle_diameter : min_nozzle_diameter :
m_config.nozzle_diameter.get_at(first_layer_extruder); m_config.nozzle_diameter.get_at(physical_extruder);
} else { } else {
// if we don't have raft layers, any nozzle diameter is potentially used in first layer // if we don't have raft layers, any nozzle diameter is potentially used in first layer
first_layer_min_nozzle_diameter = min_nozzle_diameter; first_layer_min_nozzle_diameter = min_nozzle_diameter;
@@ -1702,11 +1748,14 @@ Flow Print::brim_flow() const
extruders and take the one with, say, the smallest index. extruders and take the one with, say, the smallest index.
The same logic should be applied to the code that selects the extruder during G-code The same logic should be applied to the code that selects the extruder during G-code
generation as well. */ generation as well. */
// SM Orca: 使用物理挤出机的喷嘴直径
int filament_idx = m_print_regions.front()->config().wall_filament - 1;
int physical_extruder = get_physical_extruder(filament_idx);
return Flow::new_from_config_width( return Flow::new_from_config_width(
frPerimeter, frPerimeter,
// Flow::new_from_config_width takes care of the percent to value substitution // Flow::new_from_config_width takes care of the percent to value substitution
width, width,
(float)m_config.nozzle_diameter.get_at(m_print_regions.front()->config().wall_filament-1), (float)m_config.nozzle_diameter.get_at(physical_extruder),
(float)this->skirt_first_layer_height()); (float)this->skirt_first_layer_height());
} }
@@ -1721,11 +1770,14 @@ Flow Print::skirt_flow() const
extruders and take the one with, say, the smallest index; extruders and take the one with, say, the smallest index;
The same logic should be applied to the code that selects the extruder during G-code The same logic should be applied to the code that selects the extruder during G-code
generation as well. */ generation as well. */
// SM Orca: 使用物理挤出机的喷嘴直径
int filament_idx = m_objects.front()->config().support_filament - 1;
int physical_extruder = get_physical_extruder(filament_idx);
return Flow::new_from_config_width( return Flow::new_from_config_width(
frPerimeter, frPerimeter,
// Flow::new_from_config_width takes care of the percent to value substitution // Flow::new_from_config_width takes care of the percent to value substitution
width, width,
(float)m_config.nozzle_diameter.get_at(m_objects.front()->config().support_filament-1), (float)m_config.nozzle_diameter.get_at(physical_extruder),
(float)this->skirt_first_layer_height()); (float)this->skirt_first_layer_height());
} }
@@ -2243,6 +2295,17 @@ std::string Print::export_gcode(const std::string& path_template, GCodeProcessor
//BBS: compute plate offset for gcode-generator //BBS: compute plate offset for gcode-generator
const Vec3d origin = this->get_plate_origin(); const Vec3d origin = this->get_plate_origin();
gcode.set_gcode_offset(origin(0), origin(1)); gcode.set_gcode_offset(origin(0), origin(1));
// SM Orca: Initialize filament-to-extruder mapping before it's used
this->initialize_filament_extruder_map();
// SM Orca: 设置耗材-挤出机映射
BOOST_LOG_TRIVIAL(info) << "SM Orca: Print::export_gcode - Setting filament_extruder_map to GCode, mapping size: " << m_filament_extruder_map.size();
for (const auto& pair : m_filament_extruder_map) {
BOOST_LOG_TRIVIAL(info) << " SM Orca: Print mapping: filament " << pair.first << " -> extruder " << pair.second;
}
gcode.set_filament_extruder_map(m_filament_extruder_map);
BOOST_LOG_TRIVIAL(info) << "SM Orca: Print::export_gcode - Mapping set to GCode object, calling do_export";
gcode.do_export(this, path.c_str(), result, thumbnail_cb); gcode.do_export(this, path.c_str(), result, thumbnail_cb);
//BBS //BBS
@@ -2333,7 +2396,9 @@ void Print::_make_skirt()
extruders_e_per_mm.reserve(set_extruders.size()); extruders_e_per_mm.reserve(set_extruders.size());
for (auto &extruder_id : set_extruders) { for (auto &extruder_id : set_extruders) {
extruders.push_back(extruder_id); extruders.push_back(extruder_id);
extruders_e_per_mm.push_back(Extruder((unsigned int)extruder_id, &m_config, m_config.single_extruder_multi_material).e_per_mm(mm3_per_mm)); // SM Orca: 创建 Extruder 对象时传递物理挤出机ID
int physical_extruder_id = get_physical_extruder(extruder_id);
extruders_e_per_mm.push_back(Extruder((unsigned int)extruder_id, physical_extruder_id, &m_config, m_config.single_extruder_multi_material).e_per_mm(mm3_per_mm));
} }
} }
@@ -2733,8 +2798,11 @@ void Print::_make_wipe_tower()
// wipe_tower.set_zhop(); // wipe_tower.set_zhop();
// Set the extruder & material properties at the wipe tower object. // Set the extruder & material properties at the wipe tower object.
for (size_t i = 0; i < number_of_extruders; ++i) // SM Orca: 传递物理挤出机ID以支持耗材-挤出机映射
wipe_tower.set_extruder(i, m_config); for (size_t i = 0; i < number_of_extruders; ++i) {
int physical_extruder = get_physical_extruder(i);
wipe_tower.set_extruder(i, physical_extruder, m_config);
}
// BBS: remove priming logic // BBS: remove priming logic
// m_wipe_tower_data.priming = Slic3r::make_unique<std::vector<WipeTower::ToolChangeResult>>( // m_wipe_tower_data.priming = Slic3r::make_unique<std::vector<WipeTower::ToolChangeResult>>(
@@ -2829,8 +2897,11 @@ void Print::_make_wipe_tower()
// wipe_tower.set_zhop(); // wipe_tower.set_zhop();
// Set the extruder & material properties at the wipe tower object. // Set the extruder & material properties at the wipe tower object.
for (size_t i = 0; i < number_of_extruders; ++i) // SM Orca: 传递物理挤出机ID以支持耗材-挤出机映射
wipe_tower.set_extruder(i, m_config); for (size_t i = 0; i < number_of_extruders; ++i) {
int physical_extruder = get_physical_extruder(i);
wipe_tower.set_extruder(i, physical_extruder, m_config);
}
m_wipe_tower_data.priming = Slic3r::make_unique<std::vector<WipeTower::ToolChangeResult>>( m_wipe_tower_data.priming = Slic3r::make_unique<std::vector<WipeTower::ToolChangeResult>>(
wipe_tower.prime((float)this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false)); wipe_tower.prime((float)this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));
@@ -2970,6 +3041,8 @@ void Print::export_gcode_from_previous_file(const std::string& file, GCodeProces
GCodeProcessor::s_IsBBLPrinter = is_BBL_printer(); GCodeProcessor::s_IsBBLPrinter = is_BBL_printer();
const Vec3d origin = this->get_plate_origin(); const Vec3d origin = this->get_plate_origin();
processor.set_xy_offset(origin(0), origin(1)); processor.set_xy_offset(origin(0), origin(1));
// SM Orca: 设置耗材到物理挤出机的映射
processor.set_filament_extruder_map(m_filament_extruder_map);
//processor.enable_producers(true); //processor.enable_producers(true);
processor.process_file(file); processor.process_file(file);

View File

@@ -23,6 +23,7 @@
#include <functional> #include <functional>
#include <set> #include <set>
#include <unordered_map>
#include "calib.hpp" #include "calib.hpp"
@@ -887,6 +888,24 @@ public:
std::vector<unsigned int> object_extruders() const; std::vector<unsigned int> object_extruders() const;
std::vector<unsigned int> support_material_extruders() const; std::vector<unsigned int> support_material_extruders() const;
// SM Orca: 设置耗材-挤出机映射
void set_filament_extruder_map(const std::unordered_map<int, int>& map) { m_filament_extruder_map = map; }
// SM Orca: 获取耗材-挤出机映射表
const std::unordered_map<int, int>& get_filament_extruder_map() const { return m_filament_extruder_map; }
// SM Orca: 获取物理挤出机ID根据耗材索引
int get_physical_extruder(int filament_idx) const {
auto it = m_filament_extruder_map.find(filament_idx);
int physical_extruder_id = (it != m_filament_extruder_map.end()) ? it->second : filament_idx;
// SM Orca: 日志 - 映射查询
BOOST_LOG_TRIVIAL(info) << "Print::get_physical_extruder: filament_id=" << filament_idx
<< " -> physical_extruder_id=" << physical_extruder_id
<< " (map_size=" << m_filament_extruder_map.size() << ")"
<< (it != m_filament_extruder_map.end() ? " [from_map]" : " [default_identity]");
return physical_extruder_id;
}
// SM Orca: Initialize filament-to-physical-extruder mapping table
void initialize_filament_extruder_map();
std::vector<unsigned int> extruders(bool conside_custom_gcode = false) const; std::vector<unsigned int> extruders(bool conside_custom_gcode = false) const;
double max_allowed_layer_height() const; double max_allowed_layer_height() const;
bool has_support_material() const; bool has_support_material() const;
@@ -1066,6 +1085,9 @@ private:
//SoftFever: calibration //SoftFever: calibration
Calib_Params m_calib_params; Calib_Params m_calib_params;
// SM Orca: 耗材到物理挤出机的映射表
std::unordered_map<int, int> m_filament_extruder_map;
// To allow GCode to set the Print's GCodeExport step status. // To allow GCode to set the Print's GCodeExport step status.
friend class GCode; friend class GCode;
// Allow PrintObject to access m_mutex and m_cancel_callback. // Allow PrintObject to access m_mutex and m_cancel_callback.

View File

@@ -216,13 +216,56 @@ static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector<Custo
return false; return false;
} }
// SM Orca: Apply physical extruder mapping to filament parameters without overrides
// For each filament slot, if no override is provided, inherit from the mapped physical extruder
static void apply_physical_extruder_defaults(
ConfigOption* target,
const ConfigOption* filament_overrides,
const ConfigOption* extruder_defaults,
const std::unordered_map<int, int>& filament_extruder_map)
{
if (!target->is_vector() || !extruder_defaults->is_vector())
return;
auto* target_vec = dynamic_cast<ConfigOptionVectorBase*>(target);
auto* extruder_vec = dynamic_cast<const ConfigOptionVectorBase*>(extruder_defaults);
const ConfigOptionVectorBase* override_vec = filament_overrides ?
dynamic_cast<const ConfigOptionVectorBase*>(filament_overrides) : nullptr;
if (!target_vec || !extruder_vec)
return;
size_t num_filaments = target_vec->size();
for (size_t filament_idx = 0; filament_idx < num_filaments; ++filament_idx) {
// Check if this filament has an override
bool has_override = false;
if (override_vec && filament_idx < override_vec->size()) {
has_override = override_vec->nullable() ? !override_vec->is_nil(filament_idx) : true;
}
// If no override, inherit from the mapped physical extruder
if (!has_override) {
auto map_it = filament_extruder_map.find(filament_idx);
int physical_extruder_idx = (map_it != filament_extruder_map.end()) ?
map_it->second : filament_idx;
if (physical_extruder_idx < extruder_vec->size()) {
target_vec->set_at(extruder_vec, filament_idx, physical_extruder_idx);
}
}
}
}
// Collect changes to print config, account for overrides of extruder retract values by filament presets. // Collect changes to print config, account for overrides of extruder retract values by filament presets.
//BBS: add plate index //BBS: add plate index
// SM Orca: add filament_extruder_map for physical extruder mapping
static t_config_option_keys print_config_diffs( static t_config_option_keys print_config_diffs(
const PrintConfig &current_config, const PrintConfig &current_config,
const DynamicPrintConfig &new_full_config, const DynamicPrintConfig &new_full_config,
DynamicPrintConfig &filament_overrides, DynamicPrintConfig &filament_overrides,
int plate_index) int plate_index,
const std::unordered_map<int, int> &filament_extruder_map)
{ {
const std::vector<std::string> &extruder_retract_keys = print_config_def.extruder_retract_keys(); const std::vector<std::string> &extruder_retract_keys = print_config_def.extruder_retract_keys();
const std::string filament_prefix = "filament_"; const std::string filament_prefix = "filament_";
@@ -249,6 +292,13 @@ static t_config_option_keys print_config_diffs(
if (!((opt_key == "long_retractions_when_cut" || opt_key == "retraction_distances_when_cut") if (!((opt_key == "long_retractions_when_cut" || opt_key == "retraction_distances_when_cut")
&& new_full_config.option<ConfigOptionInt>("enable_long_retraction_when_cut")->value != LongRectrationLevel::EnableFilament)) // ugly code, remove it later if firmware supports && new_full_config.option<ConfigOptionInt>("enable_long_retraction_when_cut")->value != LongRectrationLevel::EnableFilament)) // ugly code, remove it later if firmware supports
opt_copy->apply_override(opt_new_filament); opt_copy->apply_override(opt_new_filament);
// SM Orca: Apply physical extruder mapping for slots without overrides
bool is_extruder_retract_param = (iter != extruder_retract_keys.end());
if (is_extruder_retract_param && !filament_extruder_map.empty()) {
apply_physical_extruder_defaults(opt_copy, opt_new_filament, opt_new, filament_extruder_map);
}
bool changed = *opt_old != *opt_copy; bool changed = *opt_old != *opt_copy;
if (changed) if (changed)
print_diff.emplace_back(opt_key); print_diff.emplace_back(opt_key);
@@ -261,6 +311,19 @@ static t_config_option_keys print_config_diffs(
} else } else
delete opt_copy; delete opt_copy;
} }
} else if (iter != extruder_retract_keys.end() && !filament_extruder_map.empty()) {
// SM Orca: No filament override exists, but this is an extruder retract parameter
// Apply physical extruder mapping to inherit from correct extruders
auto opt_copy = opt_new->clone();
apply_physical_extruder_defaults(opt_copy, nullptr, opt_new, filament_extruder_map);
bool changed = *opt_old != *opt_copy;
if (changed) {
print_diff.emplace_back(opt_key);
filament_overrides.set_key_value(opt_key, opt_copy);
} else {
delete opt_copy;
}
} else if (*opt_new != *opt_old) { } else if (*opt_new != *opt_old) {
//BBS: add plate_index logic for wipe_tower_x/wipe_tower_y //BBS: add plate_index logic for wipe_tower_x/wipe_tower_y
if (!opt_key.compare("wipe_tower_x") || !opt_key.compare("wipe_tower_y")) { if (!opt_key.compare("wipe_tower_x") || !opt_key.compare("wipe_tower_y")) {
@@ -1131,7 +1194,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
// Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles. // Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles.
DynamicPrintConfig filament_overrides; DynamicPrintConfig filament_overrides;
//BBS: add plate index //BBS: add plate index
t_config_option_keys print_diff = print_config_diffs(m_config, new_full_config, filament_overrides, this->m_plate_index); // SM Orca: Pass filament_extruder_map to apply physical extruder mapping
t_config_option_keys print_diff = print_config_diffs(m_config, new_full_config, filament_overrides, this->m_plate_index, m_filament_extruder_map);
t_config_option_keys full_config_diff = full_print_config_diffs(m_full_print_config, new_full_config, this->m_plate_index); t_config_option_keys full_config_diff = full_print_config_diffs(m_full_print_config, new_full_config, this->m_plate_index);
// Collect changes to object and region configs. // Collect changes to object and region configs.
t_config_option_keys object_diff = m_default_object_config.diff(new_full_config); t_config_option_keys object_diff = m_default_object_config.diff(new_full_config);