Files
OrcaSlicer/docs/superpowers/specs/2026-07-07-ams-drying-control-design.md
2026-07-07 11:21:00 +08:00

15 KiB
Raw Blame History

AMS Drying Control — Design Spec

Date: 2026-07-07 Source: Port from BambuStudio AMSDryControl feature Reference: D:\projects\BambuStudio — match its behavior as much as possible; when in doubt, check the BambuStudio source Branch: dev/ams-heat

Overview

Port the AMS filament drying control feature from BambuStudio into OrcaSlicer. This allows users to start, monitor, and stop AMS-based filament drying directly from the slicer UI. Currently OrcaSlicer can monitor drying state and stop drying, but cannot initiate it. This feature adds the missing start-drying command, the full drying control dialog, and the drying preset lookup infrastructure.

Target AMS models: N3F (AMS 2 Pro, max 65°C) and N3S (AMS HT, max 85°C) — the only AMS units with heating hardware.

Architecture

The feature spans three layers, matching the existing OrcaSlicer DeviceCore/ pattern:

┌─────────────────────────────────────────────────────┐
│  UI Layer                                           │
│  AMSDryControl.hpp/.cpp    (new dialog)             │
│  AMSControl.cpp             (hook: click → dialog)  │
│  DeviceErrorDialog.cpp      (stop drying on error)  │
├─────────────────────────────────────────────────────┤
│  Command/Control Layer                              │
│  DevFilaSystemCtrl.cpp      (start/stop drying)     │
│  DevUtilBackend.h/.cpp      (preset lookup)         │
├─────────────────────────────────────────────────────┤
│  Data Model Layer                                   │
│  DevFilaSystem.h/.cpp       (enums, structs, parse) │
│  DevDefs.h                  (DevAmsType enum)       │
└─────────────────────────────────────────────────────┘
│  Communication Layer (already exists)               │
│  MachineObject::publish_json() → MQTT → printer     │
└─────────────────────────────────────────────────────┘

New files (4 source + headers)

File Purpose
src/slic3r/GUI/AMSDryControl.hpp AMS drying control dialog class declarations
src/slic3r/GUI/AMSDryControl.cpp Dialog implementation (~1800 lines)
src/slic3r/GUI/DeviceCore/DevFilaSystemCtrl.cpp CtrlAmsStartDryingHour() and CtrlAmsStopDrying()
src/slic3r/GUI/DeviceCore/DevUtilBackend.h Static utility class declaring GetFilamentDryingPreset()
src/slic3r/GUI/DeviceCore/DevUtilBackend.cpp Reads filament drying presets from printer config

Modified files

File Changes
DevFilaSystem.h Add drying enums/structs to DevAms; add DevFilamentDryingPreset struct; add ctrl method declarations to DevFilaSystem; add get_ams_drying_preset() to DevAmsTray
DevFilaSystem.cpp Parse dry status fields in DevFilaSystemParser; implement IsSupportRemoteDry(), AmsIsDrying(), get_ams_drying_preset()
DevDefs.h Add DevAmsType as a global enum
AMSControl.cpp Wire humidity indicator click to open AMSDryCtrWin for N3F/N3S AMS
DeviceErrorDialog.cpp Update stop-drying to use DevFilaSystem::CtrlAmsStopDrying()
DeviceCore/CMakeLists.txt Register new source files

New image assets (14)

Asset Purpose
hum_level1_no_num_light.svg hum_level5_no_num_light.svg 5 humidity level icons
dev_ams_dry_ctr_n3f_heating.png, _dehumidifying.png, _error.png, _cooling.png 4 N3F drying state images
dev_ams_dry_ctr_n3s_heating.png, _dehumidifying.png, _error.png, _cooling.png 4 N3S drying state images
dev_ams_dry_ctr_heating_icon.svg Animated heating indicator
dev_ams_dry_ctr_filament_in_chamber.png Guide page illustration
dev_ams_dry_ctr_enable.svg, dev_ams_dry_ctr_disable.svg Filament tray status icons

Additionally, OrcaSlicer already has ams_drying.svg and ams_is_drying.svg.

Data Model

DevDefs.h — Global DevAmsType enum

Promoted from DevAms::AmsType (currently local to the class) to a global enum in DevDefs.h so DevFilamentDryingPreset and DevUtilBackend can reference it without depending on DevAms. DevAms::AmsType is updated to be a typedef for DevAmsType, and all existing references to DevAms::AmsType remain valid (it's the same type):

// DevDefs.h — global enum matching BambuStudio's DevAmsType naming
enum DevAmsType : int {
    EXT_SPOOL = 0,
    AMS = 1,
    AMS_LITE = 2,
    N3F = 3,    // AMS 2 Pro
    N3S = 4,    // AMS HT
};

// DevFilaSystem.h — DevAms class updated:
// Replace "enum AmsType { DUMMY=0, AMS=1, AMS_LITE=2, N3F=3, N3S=4 }"
// with "using AmsType = DevAmsType;"
// Existing references to DevAms::DUMMY become DevAms::EXT_SPOOL (same value, different name).

DevAms class additions (in DevFilaSystem.h)

New enums:

Enum Values
DryCtrlMode Off=0, OnTime=1, OnHumidity=2
DryStatus Off=0, Checking=1, Drying=2, Cooling=3, Stopping=4, Error=5, CannotStopHeatOutofControl=6, PrdTesting=7
DrySubStatus Off=0, Heating=1, Dehumidify=2
DryFanStatus Off=0, On=1
CannotDryReason TaskOccupied=0, InsufficientPower=1, AmsBusy=2, ConsumableAtAmsOutlet=3, InitiatingAmsDrying=4, NotSupportedIn2dMode=5, DryingInProgress=6, Upgrading=7, InsufficientPowerNeedPluginPower=8, FilamentAtAmsOutletManualUnload=10

New struct:

struct DrySettings {
    std::string dry_filament;
    int dry_temp = -1;   // -1 means invalid
    int dry_hour = -1;   // hours
};

New getters on DevAms:

  • std::optional<DryStatus> GetDryStatus() const
  • std::optional<DrySubStatus> GetDrySubStatus() const
  • std::optional<DryFanStatus> GetFan1Status() const
  • std::optional<DryFanStatus> GetFan2Status() const
  • std::optional<std::vector<CannotDryReason>> GetCannotDryReason() const
  • std::optional<DrySettings> GetDrySettings() const
  • bool IsSupportRemoteDry(const MachineObject* obj) const
  • bool AmsIsDrying()

New private members: m_dry_status, m_dry_sub_status, m_dry_fan1_status, m_dry_fan2_status, m_dry_cannot_reasons, m_dry_settings — all std::optional<>.

DevFilamentDryingPreset struct (namespace scope in DevFilaSystem.h)

struct DevFilamentDryingPreset {
    std::string filament_id;
    std::unordered_set<DevAmsType> ams_limitations;
    std::unordered_map<DevAmsType, float> filament_dev_ams_drying_time_on_idle;       // hours
    std::unordered_map<DevAmsType, float> filament_dev_ams_drying_temperature_on_idle;
    std::unordered_map<DevAmsType, float> filament_dev_ams_drying_time_on_print;      // hours
    std::unordered_map<DevAmsType, float> filament_dev_ams_drying_temperature_on_print;
    float filament_dev_drying_cooling_temperature;
    float filament_dev_drying_softening_temperature;
    float filament_dev_ams_drying_heat_distortion_temperature;
};

Populated from filament preset config keys (filament_dev_ams_drying_temperature, filament_dev_ams_drying_time, filament_dev_drying_softening_temperature, etc.).

DevAmsTray addition

std::optional<DevFilamentDryingPreset> get_ams_drying_preset() const;

Delegates to DevUtilBackend::GetFilamentDryingPreset(setting_id).

JSON parsing

DevFilaSystemParser::ParseV1_0() extended to parse the following fields from the printer's AMS status JSON:

JSON field DevAms member
dry_status m_dry_status (DryStatus enum)
dry_sub_status m_dry_sub_status (DrySubStatus enum)
dry_fan1_status m_dry_fan1_status
dry_fan2_status m_dry_fan2_status
dry_cannot_reasons m_dry_cannot_reasons (vector of CannotDryReason)
dry_settings m_dry_settings (DrySettings struct)

Commands

DevFilaSystem::CtrlAmsStartDryingHour()

Publishes JSON command "ams_filament_drying" via MachineObject::publish_json():

{
    "print": {
        "command": "ams_filament_drying",
        "sequence_id": "<seq>",
        "ams_id": <int>,
        "mode": 1,
        "filament": "<type_string>",
        "temp": <int>,
        "duration": <int>,
        "humidity": 0,
        "rotate_tray": <bool>,
        "cooling_temp": <int>,
        "close_power_conflict": false
    }
}

DevFilaSystem::CtrlAmsStopDrying()

Same command with mode: 0 (Off).

Existing MachineObject::command_ams_drying_stop()

Kept for backward compatibility. The error dialog will be updated to call CtrlAmsStopDrying() instead, which is the preferred path.

Backend Utility

DevUtilBackend (new, DeviceCore/DevUtilBackend.h/.cpp)

Static utility class. Only method needed for this feature:

static std::optional<DevFilamentDryingPreset> GetFilamentDryingPreset(const std::string& fila_id);

Iterates wxGetApp().preset_bundle->filaments to find the matching filament_id, then reads config keys into a DevFilamentDryingPreset. Uses a static map {"0" → N3F, "1" → N3S} for the ams_limitations string-to-enum conversion.

UI Dialog

AMSDryCtrWin (extends DPIDialog)

Three pages in a wxSimplebook:

Page 0: Main Page

Split left/right layout:

Left panel — Status display:

  • Large humidity/drying state image: humidity level icon when idle; heating/dehumidifying/error animation when active
  • Status label with animated heating icon ("Idle", "Drying-Heating", "Drying-Dehumidifying")
  • Three stat labels: Humidity (%), Temperature (°C), Left Time (HH:MM). Left Time hidden when idle.

Right panel — Controls. Three sub-panels shown/hidden based on DryStatus and CannotDryReason:

Sub-panel When shown Contents
Normal state DryStatus is Off or Cooling Filament type combobox → Temperature input (numeric, validated to AMS limits) → Time input (hours, 124) → Validation warning text → Start button
Cannot dry CannotDryReason is non-empty (excluding only DryingInProgress) Formatted reason text + Unload button (only for ConsumableAtAmsOutlet reason)
Drying error DryStatus is Error Error message + Stop button
Drying active DryStatus is Checking/Drying/Stopping Read-only temp/time display + Stop button

Temperature validation rules:

  • Hardware limits: N3F 4565°C, N3S 4585°C
  • Heat distortion check: temp must not exceed filament_dev_ams_drying_heat_distortion_temperature when filament is present
  • Printing mode: temp must not exceed recommended drying temp when this AMS is actively feeding a print
  • Time: 124 hours

Page 1: Guide Page

Shown after clicking Start on Page 0, before sending the command:

  • Instructional text about removing heat-sensitive filament
  • AMSFilamentPanel showing each tray's filament type with /⚠ icons based on whether drying temp exceeds that filament's softening point
  • "Rotate spool when drying" checkbox
  • Back and Start buttons

Page 2: Progress Page

Shown while waiting for printer to confirm drying has started:

  • Animated progress bar (cycles 099% until printer state changes)
  • Cycling status messages
  • Auto-transitions back to Page 0 when printer reports drying active

Helper classes (in AMSDryControl.hpp)

  • FilamentItemPanel — Renders a single filament item with icon and text, with custom rounded-rectangle border painting
  • AMSFilamentPanel — Container for multiple FilamentItemPanel instances, labeled with AMS name
  • DryingPreset struct, DryCtrState enum, DryCtrDev enum — Local types for tracking preset state

Dialog lifecycle

  • Created on first click of the AMS humidity indicator
  • Updated on each AMSControl::parse_object() cycle via update(fila_system, obj)
  • Auto-closes if: AMS is removed from the system, AMS no longer supports remote drying, or AMS ID becomes invalid
  • Closed manually via close button or dialog destruction
  • On close: stops progress timer, resets to main page, restores button states

Integration

Entry point: AMS humidity click → dialog

In AMSControl.cpp, the EVT_AMS_SHOW_HUMIDITY_TIPS handler currently shows either AmsHumidityTipPopup or uiAmsPercentHumidityDryPopup. For N3F and N3S AMS types only, replace this with opening AMSDryCtrWin:

  1. Lazily create AMSDryCtrWin* member on AMSControl (like m_percent_humidity_dry_popup today)
  2. Call set_ams_id() and update(fila_system, obj) before showing
  3. Show the dialog modally via ShowModal() (matching BambuStudio behavior)

Periodic updates

AMSControl::parse_object() updates AMS state on a timer. Extend it to:

  1. If m_dry_ctr_win is shown, call update() to refresh readings
  2. Update the dialog's AMS ID if the user switches AMS view

Error dialog

DeviceErrorDialog.cpp line 452 calls m_obj->command_ams_drying_stop(). Update to use obj->GetFilaSystem()->CtrlAmsStopDrying(ams_id) for the correct AMS ID.

CMakeLists

  • src/slic3r/GUI/DeviceCore/CMakeLists.txt: add DevFilaSystemCtrl.cpp, DevUtilBackend.h, DevUtilBackend.cpp
  • GUI sources list: add AMSDryControl.hpp, AMSDryControl.cpp

Testing

Manual verification

  1. Prerequisite: Connect to a printer with N3F or N3S AMS hardware
  2. Click AMS humidity indicator → verify dialog opens with correct humidity/temperature readings
  3. Select a filament from the combobox → verify temp/time auto-fill from preset
  4. Enter valid temp/time → verify Start button enables
  5. Enter invalid temp (e.g., 100°C for N3F) → verify warning appears and button disables
  6. Click Start → verify guide page shows filament tray status
  7. Click Start on guide page → verify progress page appears, command is sent
  8. Verify printer begins drying → dialog shows active drying state with countdown
  9. Click Stop → verify drying stops
  10. Test "cannot dry" states (e.g., during print, filament at outlet)

Code-level checks

  • Verify CtrlAmsStartDryingHour publishes correct JSON structure
  • Verify CtrlAmsStopDrying publishes stop command
  • Verify GetFilamentDryingPreset returns correct values for known filament IDs
  • Verify dark mode rendering of all dialog pages

Constraints

  • N3F/N3S only: Standard AMS and AMS Lite do not have heating hardware; the dialog is not shown for them
  • Backward compatibility: Existing command_ams_drying_stop() is preserved; new commands are additive
  • Cross-platform: wxWidgets UI must work on Windows, macOS, Linux
  • Dark mode: Dialog must support dark mode via wxGetApp().dark_mode() and StateColor::darkModeColorFor()
  • Profile compatibility: Existing profiles with filament_dev_ams_drying_* keys (Qidi) continue to work; the feature reads the same keys
  • No printer-agent changes needed: All commands go through existing publish_json()NetworkAgent path, which already handles MQTT for connected printers