mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-09 10:16:50 +00:00
Adaptive Pressure Advance Validation (#14198)
Co-authored-by: Wegerich <23041237+Wegerich@users.noreply.github.com>
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@@ -282,4 +283,81 @@ std::string AdaptivePAProcessor::process_layer(std::string &&gcode) {
|
|||||||
return output.str();
|
return output.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string AdaptivePAProcessor::validate_adaptive_pa_model(const std::string& model_str)
|
||||||
|
{
|
||||||
|
if (model_str.empty())
|
||||||
|
return {}; // Empty model is valid
|
||||||
|
|
||||||
|
std::istringstream model_stream(model_str);
|
||||||
|
std::string line;
|
||||||
|
int line_number = 0;
|
||||||
|
|
||||||
|
while (std::getline(model_stream, line)) {
|
||||||
|
++line_number;
|
||||||
|
|
||||||
|
// Trim whitespace
|
||||||
|
const auto first = line.find_first_not_of(" \t\r\n");
|
||||||
|
if (first == std::string::npos)
|
||||||
|
continue; // Skip empty lines
|
||||||
|
|
||||||
|
const auto last = line.find_last_not_of(" \t\r\n");
|
||||||
|
line = line.substr(first, last - first + 1);
|
||||||
|
|
||||||
|
// Only numbers, commas and dots are allowed (no letters or other characters)
|
||||||
|
for (char c : line) {
|
||||||
|
if (!std::isdigit(static_cast<unsigned char>(c)) && c != ',' && c != '.') {
|
||||||
|
return "Line " + std::to_string(line_number) +
|
||||||
|
": only numbers, commas and dots are allowed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count commas to validate format (should be exactly 2 for 3 values)
|
||||||
|
int comma_count = 0;
|
||||||
|
for (char c : line) {
|
||||||
|
if (c == ',') comma_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comma_count != 2) {
|
||||||
|
return "Line " + std::to_string(line_number) +
|
||||||
|
": must contain exactly 3 comma-separated values (PA, flow, acceleration)";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse and validate the values
|
||||||
|
try {
|
||||||
|
std::istringstream line_stream(line);
|
||||||
|
std::string value;
|
||||||
|
|
||||||
|
// Parse PA
|
||||||
|
if (!std::getline(line_stream, value, ','))
|
||||||
|
return "Line " + std::to_string(line_number) + ": missing PA value";
|
||||||
|
double pa = std::stod(value);
|
||||||
|
|
||||||
|
// Parse flow
|
||||||
|
if (!std::getline(line_stream, value, ','))
|
||||||
|
return "Line " + std::to_string(line_number) + ": missing flow value";
|
||||||
|
double flow = std::stod(value);
|
||||||
|
|
||||||
|
// Parse acceleration
|
||||||
|
if (!std::getline(line_stream, value, ','))
|
||||||
|
return "Line " + std::to_string(line_number) + ": missing acceleration value";
|
||||||
|
double accel = std::stod(value);
|
||||||
|
|
||||||
|
// Validate constraints
|
||||||
|
if (pa >= 2.0) {
|
||||||
|
return "Line " + std::to_string(line_number) + ": PA value must be less than 2";
|
||||||
|
}
|
||||||
|
if (flow <= pa) {
|
||||||
|
return "Line " + std::to_string(line_number) + ": flow value must be greater than PA value";
|
||||||
|
}
|
||||||
|
if (accel <= flow) {
|
||||||
|
return "Line " + std::to_string(line_number) + ": acceleration value must be greater than flow value";
|
||||||
|
}
|
||||||
|
} catch (const std::exception&) {
|
||||||
|
return "Line " + std::to_string(line_number) + ": invalid numeric value";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {}; // All validations passed
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|||||||
@@ -54,6 +54,20 @@ public:
|
|||||||
*/
|
*/
|
||||||
void resetPreviousPA(double PA){ m_last_predicted_pa = PA; };
|
void resetPreviousPA(double PA){ m_last_predicted_pa = PA; };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Validates an adaptive pressure advance model string.
|
||||||
|
*
|
||||||
|
* Checks that:
|
||||||
|
* - Each non-empty line has exactly 3 comma-separated values (PA, flow, accel)
|
||||||
|
* - PA value is less than 2
|
||||||
|
* - Flow value is greater than PA value
|
||||||
|
* - Accel value is greater than flow value
|
||||||
|
*
|
||||||
|
* @param model_str The model string to validate (typically from config)
|
||||||
|
* @return Empty string if valid, or an error message describing the first issue found
|
||||||
|
*/
|
||||||
|
static std::string validate_adaptive_pa_model(const std::string& model_str);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GCode &m_gcodegen; ///< Reference to the GCode object.
|
GCode &m_gcodegen; ///< Reference to the GCode object.
|
||||||
std::unordered_map<unsigned int, std::unique_ptr<AdaptivePAInterpolator>> m_AdaptivePAInterpolators; ///< Map between Interpolator objects and tool ID's
|
std::unordered_map<unsigned int, std::unique_ptr<AdaptivePAInterpolator>> m_AdaptivePAInterpolators; ///< Map between Interpolator objects and tool ID's
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
#include <sstream>
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
@@ -1925,6 +1926,23 @@ StringObjectException Print::validate(std::vector<StringObjectException> *warnin
|
|||||||
if (m_default_region_config.precise_outer_wall && m_default_region_config.wall_sequence != WallSequence::InnerOuter)
|
if (m_default_region_config.precise_outer_wall && m_default_region_config.wall_sequence != WallSequence::InnerOuter)
|
||||||
warn(L("The precise wall option will be ignored for outer-inner or inner-outer-inner wall sequences."), "precise_outer_wall");
|
warn(L("The precise wall option will be ignored for outer-inner or inner-outer-inner wall sequences."), "precise_outer_wall");
|
||||||
|
|
||||||
|
// check adaptive pressure advance model
|
||||||
|
for (unsigned int extruder_id : extruders) {
|
||||||
|
if (m_config.adaptive_pressure_advance.get_at(extruder_id) &&
|
||||||
|
m_config.enable_pressure_advance.get_at(extruder_id)) {
|
||||||
|
|
||||||
|
const std::string pa_model = m_config.adaptive_pressure_advance_model.get_at(extruder_id);
|
||||||
|
if (!pa_model.empty()) {
|
||||||
|
std::string validation_error = AdaptivePAProcessor::validate_adaptive_pa_model(pa_model);
|
||||||
|
if (!validation_error.empty()) {
|
||||||
|
warn(L("The Adaptive Pressure Advance model for one or more extruders may contain invalid values."),
|
||||||
|
"adaptive_pressure_advance_model");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
BOOST_LOG_TRIVIAL(warning) << "Orca: validate motion ability failed: " << e.what() << std::endl;
|
BOOST_LOG_TRIVIAL(warning) << "Orca: validate motion ability failed: " << e.what() << std::endl;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,29 @@
|
|||||||
#include "libslic3r/MaterialType.hpp"
|
#include "libslic3r/MaterialType.hpp"
|
||||||
#include "MsgDialog.hpp"
|
#include "MsgDialog.hpp"
|
||||||
#include "libslic3r/PrintConfig.hpp"
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
|
#include "libslic3r/GCode/AdaptivePAProcessor.hpp"
|
||||||
#include "Plater.hpp"
|
#include "Plater.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
#include <wx/msgdlg.h>
|
#include <wx/msgdlg.h>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::string trim_copy(const std::string& text)
|
||||||
|
{
|
||||||
|
const auto first = text.find_first_not_of(" \t\r\n");
|
||||||
|
if (first == std::string::npos)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const auto last = text.find_last_not_of(" \t\r\n");
|
||||||
|
return text.substr(first, last - first + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void ConfigManipulation::apply(DynamicPrintConfig* config, DynamicPrintConfig* new_config)
|
void ConfigManipulation::apply(DynamicPrintConfig* config, DynamicPrintConfig* new_config)
|
||||||
{
|
{
|
||||||
bool modified = false;
|
bool modified = false;
|
||||||
@@ -141,6 +157,30 @@ void ConfigManipulation::check_nozzle_temperature_initial_layer_range(DynamicPri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConfigManipulation::check_adaptive_pressure_advance_model(DynamicPrintConfig* config)
|
||||||
|
{
|
||||||
|
if (is_msg_dlg_already_exist || !config->has("adaptive_pressure_advance_model"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto* model = config->option<ConfigOptionStrings>("adaptive_pressure_advance_model");
|
||||||
|
if (model == nullptr || model->values.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string raw_model;
|
||||||
|
for (const std::string& chunk : model->values)
|
||||||
|
raw_model += chunk;
|
||||||
|
|
||||||
|
std::string error = AdaptivePAProcessor::validate_adaptive_pa_model(raw_model);
|
||||||
|
if (!error.empty()) {
|
||||||
|
wxString msg_text = _L("Adaptive Pressure Advance model validation failed:\n");
|
||||||
|
msg_text += from_u8(error);
|
||||||
|
MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | wxOK);
|
||||||
|
is_msg_dlg_already_exist = true;
|
||||||
|
dialog.ShowModal();
|
||||||
|
is_msg_dlg_already_exist = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void ConfigManipulation::check_filament_max_volumetric_speed(DynamicPrintConfig *config)
|
void ConfigManipulation::check_filament_max_volumetric_speed(DynamicPrintConfig *config)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ public:
|
|||||||
void check_nozzle_recommended_temperature_range(DynamicPrintConfig *config);
|
void check_nozzle_recommended_temperature_range(DynamicPrintConfig *config);
|
||||||
void check_nozzle_temperature_range(DynamicPrintConfig* config);
|
void check_nozzle_temperature_range(DynamicPrintConfig* config);
|
||||||
void check_nozzle_temperature_initial_layer_range(DynamicPrintConfig* config);
|
void check_nozzle_temperature_initial_layer_range(DynamicPrintConfig* config);
|
||||||
|
void check_adaptive_pressure_advance_model(DynamicPrintConfig* config);
|
||||||
void check_filament_max_volumetric_speed(DynamicPrintConfig *config);
|
void check_filament_max_volumetric_speed(DynamicPrintConfig *config);
|
||||||
void check_chamber_temperature(DynamicPrintConfig* config);
|
void check_chamber_temperature(DynamicPrintConfig* config);
|
||||||
void set_is_BBL_Printer(bool is_bbl_printer) { is_BBL_Printer = is_bbl_printer; };
|
void set_is_BBL_Printer(bool is_bbl_printer) { is_BBL_Printer = is_bbl_printer; };
|
||||||
|
|||||||
@@ -3946,6 +3946,15 @@ void TabFilament::build()
|
|||||||
option.opt.is_code = true;
|
option.opt.is_code = true;
|
||||||
option.opt.height = 15;
|
option.opt.height = 15;
|
||||||
optgroup->append_single_option_line(option);
|
optgroup->append_single_option_line(option);
|
||||||
|
optgroup->m_on_change = [this](const t_config_option_key& opt_key, const boost::any& value) {
|
||||||
|
DynamicPrintConfig& filament_config = m_preset_bundle->filaments.get_edited_preset().config;
|
||||||
|
|
||||||
|
update_dirty();
|
||||||
|
if (opt_key == "adaptive_pressure_advance_model")
|
||||||
|
m_config_manipulation.check_adaptive_pressure_advance_model(&filament_config);
|
||||||
|
|
||||||
|
on_value_change(opt_key, value);
|
||||||
|
};
|
||||||
//
|
//
|
||||||
|
|
||||||
optgroup = page->new_optgroup(L("Print chamber temperature"), L"param_chamber_temp");
|
optgroup = page->new_optgroup(L("Print chamber temperature"), L"param_chamber_temp");
|
||||||
|
|||||||
Reference in New Issue
Block a user