Merge branch 'main' into feat/plugin-feature

This commit is contained in:
SoftFever
2026-07-17 11:30:34 +08:00
committed by GitHub
127 changed files with 3663 additions and 283 deletions

View File

@@ -41,6 +41,16 @@ enum AmsStatusMain
AMS_STATUS_MAIN_UNKNOWN = 0xFF,
};
enum DevAmsType : int
{
EXT_SPOOL = 0, // EXT
AMS = 1, // AMS1
AMS_LITE = 2, // AMS-Lite
N3F = 3, // N3F, AMS 2PRO
N3S = 4, // N3S, AMS HT
AMS_LITE_MIXED = 5, // AMS-Lite for N9
};
// Device-numbered filament-change step codes. Newer firmware reports the exact change-step
// sequence through the AMS (ams.cfs), keyed by these codes, instead of the client hardcoding
// steps per model. Distinct from the legacy GUI-side Slic3r::GUI::FilamentStep enum.

View File

@@ -7,6 +7,7 @@
#include "slic3r/GUI/I18N.hpp"
#include "DevUtil.h"
#include "DevUtilBackend.h"
using namespace nlohmann;
@@ -99,6 +100,11 @@ std::string DevAmsTray::get_filament_type()
return m_fila_type;
}
std::optional<Slic3r::DevFilamentDryingPreset> DevAmsTray::get_ams_drying_preset() const
{
return DevUtilBackend::GetFilamentDryingPreset(setting_id);
}
DevAms::DevAms(const std::string& ams_id, int extruder_id, AmsType type)
{
@@ -112,7 +118,7 @@ DevAms::DevAms(const std::string& ams_id, int nozzle_id, int type)
m_ams_id = ams_id;
m_ext_id = nozzle_id;
m_ams_type = (AmsType)type;
assert(DUMMY < type && m_ams_type <= AMS_LITE_MIXED);
assert(EXT_SPOOL < type && m_ams_type <= AMS_LITE_MIXED);
}
DevAms::~DevAms()
@@ -193,6 +199,27 @@ DevAmsTray* DevAms::GetTray(const std::string& tray_id) const
return nullptr;
}
bool DevAms::IsSupportRemoteDry(const MachineObject* obj) const
{
if (obj && obj->is_support_remote_dry) {
return SupportDrying();
}
return false;
}
bool DevAms::AmsIsDrying()
{
if (!GetDryStatus().has_value()) {
return false;
}
return GetDryStatus().value() == DevAms::DryStatus::Checking
|| GetDryStatus().value() == DevAms::DryStatus::Drying
|| GetDryStatus().value() == DevAms::DryStatus::Error
|| GetDryStatus().value() == DevAms::DryStatus::CannotStopHeatOutofControl;
}
DevFilaSystem::~DevFilaSystem()
{
for (auto it = amsList.begin(); it != amsList.end(); it++)
@@ -515,9 +542,18 @@ void DevFilaSystemParser::ParseV1_0(const json& jj, MachineObject* obj, DevFilaS
;
}
if (it->contains("dry_time") && (*it)["dry_time"].is_number())
if (it->contains("temp"))
{
curr_ams->m_left_dry_time = (*it)["dry_time"].get<int>();
std::string temp = (*it)["temp"].get<std::string>();
try
{
curr_ams->m_current_temperature = DevUtil::string_to_float(temp);
}
catch (...)
{
curr_ams->m_current_temperature = INVALID_AMS_TEMPERATURE;
}
}
if (it->contains("humidity"))
@@ -546,17 +582,32 @@ void DevFilaSystemParser::ParseV1_0(const json& jj, MachineObject* obj, DevFilaS
}
}
if (it->contains("temp"))
if (it->contains("dry_time") && (*it)["dry_time"].is_number())
{
std::string temp = (*it)["temp"].get<std::string>();
try
{
curr_ams->m_current_temperature = DevUtil::string_to_float(temp);
curr_ams->m_left_dry_time = (*it)["dry_time"].get<int>();
}
// Drying status — only parse if printer supports remote drying
if (obj->is_support_remote_dry) {
if (it->contains("info")) {
const std::string& info = (*it)["info"].get<std::string>();
curr_ams->m_dry_status = (DevAms::DryStatus)DevUtil::get_flag_bits(info, 4, 4);
curr_ams->m_dry_fan1_status = (DevAms::DryFanStatus)DevUtil::get_flag_bits(info, 18, 2);
curr_ams->m_dry_fan2_status = (DevAms::DryFanStatus)DevUtil::get_flag_bits(info, 20, 2);
curr_ams->m_dry_sub_status = (DevAms::DrySubStatus)DevUtil::get_flag_bits(info, 22, 2);
}
catch (...)
{
curr_ams->m_current_temperature = INVALID_AMS_TEMPERATURE;
if (it->contains("dry_setting")) {
const auto& j_dry_settings = (*it)["dry_setting"];
DevAms::DrySettings dry_settings;
DevJsonValParser::ParseVal(j_dry_settings, "dry_filament", dry_settings.dry_filament);
DevJsonValParser::ParseVal(j_dry_settings, "dry_temperature", dry_settings.dry_temp);
DevJsonValParser::ParseVal(j_dry_settings, "dry_duration", dry_settings.dry_hour);
curr_ams->m_dry_settings = dry_settings;
}
if (it->contains("dry_sf_reason")) {
curr_ams->m_dry_cannot_reasons = DevJsonValParser::GetVal<std::vector<DevAms::CannotDryReason>>((*it), "dry_sf_reason");
}
}

View File

@@ -12,6 +12,9 @@
#include <vector>
#include <optional>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <wx/string.h>
#include <wx/colour.h>
@@ -20,6 +23,7 @@
namespace Slic3r
{
class MachineObject;
struct DevFilamentDryingPreset;
/**
* DevAmsTray - Represents a single filament tray/slot in an AMS unit or virtual tray.
@@ -106,6 +110,8 @@ public:
// static
static wxColour decode_color(const std::string& color);
std::optional<DevFilamentDryingPreset> get_ams_drying_preset() const;
};
/**
@@ -133,14 +139,67 @@ class DevAms
{
friend class DevFilaSystemParser;
public:
enum AmsType : int
using AmsType = DevAmsType;
static constexpr AmsType EXT_SPOOL = DevAmsType::EXT_SPOOL;
static constexpr AmsType AMS = DevAmsType::AMS;
static constexpr AmsType AMS_LITE = DevAmsType::AMS_LITE;
static constexpr AmsType N3F = DevAmsType::N3F;
static constexpr AmsType N3S = DevAmsType::N3S;
static constexpr AmsType AMS_LITE_MIXED = DevAmsType::AMS_LITE_MIXED;
public:
enum class DryCtrlMode : int
{
DUMMY = 0,
AMS = 1, // AMS
AMS_LITE = 2, // AMS-Lite
N3F = 3, // N3F
N3S = 4, // N3S
AMS_LITE_MIXED = 5, // AMS-Lite mixed (A2L / N9). Presents as AMS_LITE to generic callers; see GetAmsType()/IsAmsLiteMixed().
Off = 0,
OnTime = 1,
OnHumidity = 2,
};
enum class DryStatus : char
{
Off = 0,
Checking = 1,
Drying = 2,
Cooling = 3,
Stopping = 4,
Error = 5,
CannotStopHeatOutofControl = 6,
PrdTesting = 7,
};
enum class DrySubStatus
{
Off = 0,
Heating = 1,
Dehumidify = 2,
};
enum class DryFanStatus : char
{
Off = 0,
On = 1,
};
enum class CannotDryReason : int
{
TaskOccupied = 0,
InsufficientPower = 1,
AmsBusy = 2,
ConsumableAtAmsOutlet = 3,
InitiatingAmsDrying = 4,
NotSupportedIn2dMode = 5,
DryingInProgress = 6,
Upgrading = 7,
InsufficientPowerNeedPluginPower = 8,
FilamentAtAmsOutletManualUnload = 10,
};
struct DrySettings
{
std::string dry_filament;
int dry_temp = -1; // -1 means invalid
int dry_hour = -1; // -1 means invalid, hours
};
public:
@@ -193,11 +252,20 @@ public:
int GetHumidityLevel() const { return m_humidity_level; }
int GetHumidityPercent() const { return m_humidity_percent; }
// Only N3F/N3S dry. Written explicitly (not `> AMS_LITE`) so the new AMS_LITE_MIXED (N9) is
// excluded; identical to the old expression for every pre-existing type.
bool SupportDrying() const { return (m_ams_type == N3F) || (m_ams_type == N3S); }
bool SupportDrying() const { return m_ams_type == DevAmsType::N3F || m_ams_type == DevAmsType::N3S; }
int GetLeftDryTime() const { return m_left_dry_time; }
// remote drying control
bool IsSupportRemoteDry(const MachineObject* obj) const;
std::optional<DryStatus> GetDryStatus() const { return m_dry_status; };
std::optional<DrySubStatus> GetDrySubStatus() const { return m_dry_sub_status; }
std::optional<DryFanStatus> GetFan1Status() const { return m_dry_fan1_status; }
std::optional<DryFanStatus> GetFan2Status() const { return m_dry_fan2_status; }
std::optional<std::vector<CannotDryReason>> GetCannotDryReason() const { return m_dry_cannot_reasons; }
std::optional<DrySettings> GetDrySettings() const { return m_dry_settings; };
bool AmsIsDrying();
private:
AmsType m_ams_type = AmsType::AMS;
std::string m_ams_id;
@@ -218,6 +286,14 @@ private:
int m_humidity_level = 5; // AmsType::AMS
int m_humidity_percent = -1; // N3F N3S, the percentage, -1 means invalid. eg. 100 means 100%
int m_left_dry_time = 0;
// see is_support_remote_dry
std::optional<DryStatus> m_dry_status;
std::optional<DrySubStatus> m_dry_sub_status;
std::optional<DryFanStatus> m_dry_fan1_status;
std::optional<DryFanStatus> m_dry_fan2_status;
std::optional<std::vector<CannotDryReason>> m_dry_cannot_reasons;
std::optional<DrySettings> m_dry_settings;
};
/**
@@ -299,7 +375,11 @@ public:
public:
// ctrls
int CtrlAmsReset() const;
// crtl
int CtrlAmsStartDryingHour(int ams_id, std::string filament_type, int tag_temp, int tag_duration_hour, bool rotate_tray, int cooling_temp, bool close_power_conflict = false) const;
int CtrlAmsStopDrying(int ams_id) const;
public:
static bool IsBBL_Filament(std::string tag_uid);
@@ -339,4 +419,18 @@ public:
static void ParseAgentFilament(const json& data, MachineObject* obj, DevFilaSystem* system);
};
struct DevFilamentDryingPreset
{
std::string filament_id;
std::unordered_set<DevAmsType> ams_limitations; // only use ams types in the set
std::unordered_map<DevAmsType, float> filament_dev_ams_drying_time_on_idle; // hour
std::unordered_map<DevAmsType, float> filament_dev_ams_drying_temperature_on_idle;
std::unordered_map<DevAmsType, float> filament_dev_ams_drying_time_on_print; // hour
std::unordered_map<DevAmsType, float> filament_dev_ams_drying_temperature_on_print;
float filament_dev_drying_cooling_temperature = 0.0f;
float filament_dev_drying_softening_temperature = 0.0f;
float filament_dev_ams_drying_heat_distortion_temperature = 0.0f;
};
}// namespace Slic3r

View File

@@ -16,4 +16,44 @@ int DevFilaSystem::CtrlAmsReset() const
return m_owner->publish_json(jj_command);
}
int DevFilaSystem::CtrlAmsStartDryingHour(int ams_id,
std::string filament_type,
int tag_temp,
int tag_duration_hour,
bool rotate_tray,
int cooling_temp,
bool close_power_conflict) const
{
json jj_command;
jj_command["print"]["command"] = "ams_filament_drying";
jj_command["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
jj_command["print"]["ams_id"] = ams_id;
jj_command["print"]["mode"] = static_cast<int>(DevAms::DryCtrlMode::OnTime);
jj_command["print"]["filament"] = filament_type;
jj_command["print"]["temp"] = tag_temp;
jj_command["print"]["duration"] = tag_duration_hour;
jj_command["print"]["humidity"] = 0;
jj_command["print"]["rotate_tray"] = rotate_tray;
jj_command["print"]["cooling_temp"] = cooling_temp;
jj_command["print"]["close_power_conflict"] = close_power_conflict;
return m_owner->publish_json(jj_command);
}
int DevFilaSystem::CtrlAmsStopDrying(int ams_id) const
{
json jj_command;
jj_command["print"]["command"] = "ams_filament_drying";
jj_command["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
jj_command["print"]["ams_id"] = ams_id;
jj_command["print"]["mode"] = static_cast<int>(DevAms::DryCtrlMode::Off);
jj_command["print"]["filament"] = "";
jj_command["print"]["temp"] = 0;
jj_command["print"]["duration"] = 0;
jj_command["print"]["humidity"] = 0;
jj_command["print"]["rotate_tray"] = false;
jj_command["print"]["cooling_temp"] = 0;
jj_command["print"]["close_power_conflict"] = false;
return m_owner->publish_json(jj_command);
}
}

View File

@@ -199,7 +199,7 @@ namespace Slic3r
}
}
FilamentInfo info;
_parse_tray_info(atoi(tray.id.c_str()), 0, DevAms::DUMMY, tray, info);
_parse_tray_info(atoi(tray.id.c_str()), 0, DevAms::EXT_SPOOL, tray, info);
tray_filaments.emplace(std::make_pair(info.tray_id, info));
}
}

View File

@@ -1,7 +1,11 @@
#include "DevUtilBackend.h"
#include "slic3r/GUI/BackgroundSlicingProcess.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include <boost/log/trivial.hpp>
namespace Slic3r
{
@@ -15,4 +19,68 @@ std::shared_ptr<MultiNozzleUtils::NozzleGroupResultBase> DevUtilBackend::GetNozz
return nullptr;
}
static std::unordered_map<std::string, DevAmsType> s_ams_type_map = {
{"0", DevAmsType::N3F},
{"1", DevAmsType::N3S},
};
std::optional<Slic3r::DevFilamentDryingPreset> DevUtilBackend::GetFilamentDryingPreset(const std::string& fila_id)
{
if (fila_id.empty() || !GUI::wxGetApp().preset_bundle) {
return std::nullopt;
}
for (auto iter = GUI::wxGetApp().preset_bundle->filaments.begin(); iter != GUI::wxGetApp().preset_bundle->filaments.end(); ++iter) {
const Preset& filament_preset = *iter;
const auto& config = filament_preset.config;
if (filament_preset.filament_id == fila_id) {
DevFilamentDryingPreset info;
info.filament_id = fila_id;
try {
if (config.has("filament_dev_ams_drying_ams_limitations")) {
std::vector<std::string> types = config.option<ConfigOptionStrings>("filament_dev_ams_drying_ams_limitations")->values;
for (auto type : types) {
if (s_ams_type_map.count(type) == 0) {
continue;
}
info.ams_limitations.insert(s_ams_type_map[type]);
}
}
if (config.has("filament_dev_ams_drying_temperature")) {
info.filament_dev_ams_drying_temperature_on_idle[DevAmsType::N3F] = config.option<ConfigOptionFloats>("filament_dev_ams_drying_temperature")->get_at(0);
info.filament_dev_ams_drying_temperature_on_idle[DevAmsType::N3S] = config.option<ConfigOptionFloats>("filament_dev_ams_drying_temperature")->get_at(1);
info.filament_dev_ams_drying_temperature_on_print[DevAmsType::N3F] = config.option<ConfigOptionFloats>("filament_dev_ams_drying_temperature")->get_at(2);
info.filament_dev_ams_drying_temperature_on_print[DevAmsType::N3S] = config.option<ConfigOptionFloats>("filament_dev_ams_drying_temperature")->get_at(3);
}
if (config.has("filament_dev_ams_drying_time")) {
info.filament_dev_ams_drying_time_on_idle[DevAmsType::N3F] = config.option<ConfigOptionFloats>("filament_dev_ams_drying_time")->get_at(0);
info.filament_dev_ams_drying_time_on_idle[DevAmsType::N3S] = config.option<ConfigOptionFloats>("filament_dev_ams_drying_time")->get_at(1);
info.filament_dev_ams_drying_time_on_print[DevAmsType::N3F] = config.option<ConfigOptionFloats>("filament_dev_ams_drying_time")->get_at(2);
info.filament_dev_ams_drying_time_on_print[DevAmsType::N3S] = config.option<ConfigOptionFloats>("filament_dev_ams_drying_time")->get_at(3);
}
if (config.has("filament_dev_drying_softening_temperature")) {
info.filament_dev_drying_softening_temperature = config.option<ConfigOptionFloats>("filament_dev_drying_softening_temperature")->get_at(0);
}
if (config.has("filament_dev_ams_drying_heat_distortion_temperature")) {
info.filament_dev_ams_drying_heat_distortion_temperature = config.option<ConfigOptionFloats>("filament_dev_ams_drying_heat_distortion_temperature")->get_at(0);
}
if (config.has("filament_dev_drying_cooling_temperature")) {
info.filament_dev_drying_cooling_temperature = config.option<ConfigOptionFloats>("filament_dev_drying_cooling_temperature")->get_at(0);
}
return info;
} catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " exception: " << e.what();
}
}
}
return std::nullopt;
}
}; // namespace Slic3r

View File

@@ -1,18 +1,15 @@
/**
* @file DevUtilBackend.h
* @brief Static helpers bridging the backend (slicer/preset) to the device GUI.
*
* Scope (H2C nozzle rack): exposes only GetNozzleGroupResult — the accessor the print-dispatch
* nozzle-mapping V1 request and result-resolve helpers read. Related helpers (per-nozzle info
* collection via the ExtruderNozzleInfos/NozzleDef type pair, and the drying-preset lookup) are
* not implemented here and left to the consuming code.
* @brief Provides common static utility methods for backend (preset/slicing).
*/
#pragma once
#include "DevDefs.h"
#include "DevFilaSystem.h"
#include "libslic3r/MultiNozzleUtils.hpp"
#include <memory>
#include <optional>
#include <string>
namespace Slic3r
{
@@ -23,10 +20,15 @@ class DevUtilBackend
public:
DevUtilBackend() = delete;
public:
// for rack: the slicer's per-filament -> logical-nozzle grouping for the current plate, read off
// the post-slice GCodeProcessorResult (plater->background_process().get_current_gcode_result()).
// Returns nullptr when there is no plater / no current result.
static std::shared_ptr<MultiNozzleUtils::NozzleGroupResultBase> GetNozzleGroupResult(Slic3r::GUI::Plater* plater);
// for filament preset
static std::optional<DevFilamentDryingPreset> GetFilamentDryingPreset(const std::string& fila_id);
};
}; // namespace Slic3r