diff --git a/localization/i18n/list.txt b/localization/i18n/list.txt
index 3e99dbbaae..1614bc453b 100644
--- a/localization/i18n/list.txt
+++ b/localization/i18n/list.txt
@@ -287,4 +287,6 @@ src/slic3r/GUI/DownloaderFileGet.cpp
src/slic3r/GUI/FileArchiveDialog.cpp
src/slic3r/GUI/PrinterCloudAuthDialog.cpp
src/slic3r/GUI/PrinterWebViewHandler.cpp
+src/slic3r/GUI/AMSDryControl.cpp
+src/slic3r/GUI/AMSDryControl.hpp
src/libslic3r/PresetBundle.cpp
diff --git a/resources/images/dev_ams_dry_ctr_disable.svg b/resources/images/dev_ams_dry_ctr_disable.svg
new file mode 100644
index 0000000000..f2212204e9
--- /dev/null
+++ b/resources/images/dev_ams_dry_ctr_disable.svg
@@ -0,0 +1,5 @@
+
diff --git a/resources/images/dev_ams_dry_ctr_enable.svg b/resources/images/dev_ams_dry_ctr_enable.svg
new file mode 100644
index 0000000000..31eb482d17
--- /dev/null
+++ b/resources/images/dev_ams_dry_ctr_enable.svg
@@ -0,0 +1,4 @@
+
diff --git a/resources/images/dev_ams_dry_ctr_filament_in_chamber.png b/resources/images/dev_ams_dry_ctr_filament_in_chamber.png
new file mode 100644
index 0000000000..1b36643c42
Binary files /dev/null and b/resources/images/dev_ams_dry_ctr_filament_in_chamber.png differ
diff --git a/resources/images/dev_ams_dry_ctr_heating_icon.svg b/resources/images/dev_ams_dry_ctr_heating_icon.svg
new file mode 100644
index 0000000000..614b0c9a92
--- /dev/null
+++ b/resources/images/dev_ams_dry_ctr_heating_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/resources/images/dev_ams_dry_ctr_n3f_cooling.png b/resources/images/dev_ams_dry_ctr_n3f_cooling.png
new file mode 100644
index 0000000000..6a43425da5
Binary files /dev/null and b/resources/images/dev_ams_dry_ctr_n3f_cooling.png differ
diff --git a/resources/images/dev_ams_dry_ctr_n3f_dehumidifying.png b/resources/images/dev_ams_dry_ctr_n3f_dehumidifying.png
new file mode 100644
index 0000000000..92064da370
Binary files /dev/null and b/resources/images/dev_ams_dry_ctr_n3f_dehumidifying.png differ
diff --git a/resources/images/dev_ams_dry_ctr_n3f_error.png b/resources/images/dev_ams_dry_ctr_n3f_error.png
new file mode 100644
index 0000000000..e0e49df9d1
Binary files /dev/null and b/resources/images/dev_ams_dry_ctr_n3f_error.png differ
diff --git a/resources/images/dev_ams_dry_ctr_n3f_heating.png b/resources/images/dev_ams_dry_ctr_n3f_heating.png
new file mode 100644
index 0000000000..7c48397b63
Binary files /dev/null and b/resources/images/dev_ams_dry_ctr_n3f_heating.png differ
diff --git a/resources/images/dev_ams_dry_ctr_n3s_cooling.png b/resources/images/dev_ams_dry_ctr_n3s_cooling.png
new file mode 100644
index 0000000000..f663c57c5f
Binary files /dev/null and b/resources/images/dev_ams_dry_ctr_n3s_cooling.png differ
diff --git a/resources/images/dev_ams_dry_ctr_n3s_dehumidifying.png b/resources/images/dev_ams_dry_ctr_n3s_dehumidifying.png
new file mode 100644
index 0000000000..5926a6cb54
Binary files /dev/null and b/resources/images/dev_ams_dry_ctr_n3s_dehumidifying.png differ
diff --git a/resources/images/dev_ams_dry_ctr_n3s_error.png b/resources/images/dev_ams_dry_ctr_n3s_error.png
new file mode 100644
index 0000000000..0b3df1c3fe
Binary files /dev/null and b/resources/images/dev_ams_dry_ctr_n3s_error.png differ
diff --git a/resources/images/dev_ams_dry_ctr_n3s_heating.png b/resources/images/dev_ams_dry_ctr_n3s_heating.png
new file mode 100644
index 0000000000..3613a3aefc
Binary files /dev/null and b/resources/images/dev_ams_dry_ctr_n3s_heating.png differ
diff --git a/resources/profiles/BBL/filament/Bambu ASA-Aero @base.json b/resources/profiles/BBL/filament/Bambu ASA-Aero @base.json
index 2dcf633a26..1a36118d7f 100644
--- a/resources/profiles/BBL/filament/Bambu ASA-Aero @base.json
+++ b/resources/profiles/BBL/filament/Bambu ASA-Aero @base.json
@@ -42,8 +42,8 @@
"filament_z_hop_types": [
"Normal Lift"
],
- "filament_scarf_seam_type": [
- "none"
+ "filament_dev_drying_cooling_temperature": [
+ "70"
],
"impact_strength_z": [
"3.4"
diff --git a/resources/profiles/BBL/filament/Bambu ASA-CF @base.json b/resources/profiles/BBL/filament/Bambu ASA-CF @base.json
index 9c425dba90..69624032bb 100644
--- a/resources/profiles/BBL/filament/Bambu ASA-CF @base.json
+++ b/resources/profiles/BBL/filament/Bambu ASA-CF @base.json
@@ -32,6 +32,9 @@
"filament_vendor": [
"Bambu Lab"
],
+ "filament_dev_drying_cooling_temperature": [
+ "95"
+ ],
"hot_plate_temp": [
"100"
],
diff --git a/resources/profiles/BBL/filament/Bambu PAHT-CF @base.json b/resources/profiles/BBL/filament/Bambu PAHT-CF @base.json
index 54122f3bbf..28b1391f84 100644
--- a/resources/profiles/BBL/filament/Bambu PAHT-CF @base.json
+++ b/resources/profiles/BBL/filament/Bambu PAHT-CF @base.json
@@ -33,6 +33,9 @@
"full_fan_speed_layer": [
"2"
],
+ "filament_dev_chamber_drying_time": [
+ "16"
+ ],
"impact_strength_z": [
"13.3"
],
diff --git a/resources/profiles/BBL/filament/Bambu PET-CF @base.json b/resources/profiles/BBL/filament/Bambu PET-CF @base.json
index b982cd1361..ff8b40070f 100644
--- a/resources/profiles/BBL/filament/Bambu PET-CF @base.json
+++ b/resources/profiles/BBL/filament/Bambu PET-CF @base.json
@@ -45,6 +45,27 @@
"filament_adhesiveness_category": [
"800"
],
+ "filament_dev_chamber_drying_bed_temperature": [
+ "90"
+ ],
+ "filament_dev_ams_drying_ams_limitations": [
+ "1"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "165"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "150"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "65",
+ "80",
+ "65",
+ "80"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "150"
+ ],
"hot_plate_temp": [
"100"
],
diff --git a/resources/profiles/BBL/filament/Bambu Support For PA PET @base.json b/resources/profiles/BBL/filament/Bambu Support For PA PET @base.json
index a622bff105..d199cdb0a0 100644
--- a/resources/profiles/BBL/filament/Bambu Support For PA PET @base.json
+++ b/resources/profiles/BBL/filament/Bambu Support For PA PET @base.json
@@ -24,6 +24,24 @@
"filament_adhesiveness_category": [
"703"
],
+ "filament_dev_chamber_drying_time": [
+ "16"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "90"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "75"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "65",
+ "80",
+ "65",
+ "80"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "85"
+ ],
"nozzle_temperature": [
"280"
],
diff --git a/resources/profiles/BBL/filament/Bambu Support For PLA @base.json b/resources/profiles/BBL/filament/Bambu Support For PLA @base.json
index 10e4983ebf..9bfdef5bf9 100644
--- a/resources/profiles/BBL/filament/Bambu Support For PLA @base.json
+++ b/resources/profiles/BBL/filament/Bambu Support For PLA @base.json
@@ -24,11 +24,20 @@
"filament_vendor": [
"Bambu Lab"
],
- "filament_scarf_seam_type": [
- "none"
- ],
"filament_adhesiveness_category": [
"702"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "60",
+ "60",
+ "50",
+ "50"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "50"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "55"
],
"slow_down_layer_time": [
"8"
diff --git a/resources/profiles/BBL/filament/Bambu Support For PLA-PETG @base.json b/resources/profiles/BBL/filament/Bambu Support For PLA-PETG @base.json
index 0f1b87e83f..38443f5be3 100644
--- a/resources/profiles/BBL/filament/Bambu Support For PLA-PETG @base.json
+++ b/resources/profiles/BBL/filament/Bambu Support For PLA-PETG @base.json
@@ -27,14 +27,26 @@
"filament_max_volumetric_speed": [
"6"
],
- "filament_scarf_seam_type": [
- "none"
- ],
"filament_vendor": [
"Bambu Lab"
],
"filament_adhesiveness_category": [
"705"
+ ],
+ "filament_dev_chamber_drying_bed_temperature": [
+ "80"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "60",
+ "60",
+ "50",
+ "50"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "50"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "55"
],
"hot_plate_temp": [
"60"
diff --git a/resources/profiles/BBL/filament/Bambu Support G @base.json b/resources/profiles/BBL/filament/Bambu Support G @base.json
index c903f92d07..9cb01972b8 100644
--- a/resources/profiles/BBL/filament/Bambu Support G @base.json
+++ b/resources/profiles/BBL/filament/Bambu Support G @base.json
@@ -21,11 +21,26 @@
"filament_vendor": [
"Bambu Lab"
],
- "filament_scarf_seam_type": [
- "none"
- ],
"filament_adhesiveness_category": [
"701"
+ ],
+ "filament_dev_chamber_drying_time": [
+ "16"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "90"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "75"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "65",
+ "80",
+ "65",
+ "80"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "85"
],
"nozzle_temperature": [
"280"
diff --git a/resources/profiles/BBL/filament/Bambu Support for ABS @base.json b/resources/profiles/BBL/filament/Bambu Support for ABS @base.json
index 35e13e67a0..bc8867db2a 100644
--- a/resources/profiles/BBL/filament/Bambu Support for ABS @base.json
+++ b/resources/profiles/BBL/filament/Bambu Support for ABS @base.json
@@ -27,11 +27,14 @@
"filament_vendor": [
"Bambu Lab"
],
- "filament_scarf_seam_type": [
- "none"
- ],
"filament_adhesiveness_category": [
"706"
+ ],
+ "filament_dev_ams_drying_time": [
+ "12",
+ "4",
+ "12",
+ "4"
],
"nozzle_temperature_range_high": [
"270"
diff --git a/resources/profiles/BBL/filament/Generic PA @base.json b/resources/profiles/BBL/filament/Generic PA @base.json
index 5947f5a859..fe819487b0 100644
--- a/resources/profiles/BBL/filament/Generic PA @base.json
+++ b/resources/profiles/BBL/filament/Generic PA @base.json
@@ -21,6 +21,21 @@
"filament_max_volumetric_speed": [
"12"
],
+ "filament_dev_ams_drying_temperature": [
+ "65",
+ "80",
+ "65",
+ "80"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "90"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "65"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "85"
+ ],
"nozzle_temperature": [
"260"
],
diff --git a/resources/profiles/BBL/filament/Generic PCTG @base.json b/resources/profiles/BBL/filament/Generic PCTG @base.json
index d0ec60848f..e8b7c1ce0a 100644
--- a/resources/profiles/BBL/filament/Generic PCTG @base.json
+++ b/resources/profiles/BBL/filament/Generic PCTG @base.json
@@ -38,6 +38,34 @@
"filament_max_volumetric_speed": [
"6"
],
+ "filament_dev_chamber_drying_bed_temperature": [
+ "80"
+ ],
+ "filament_dev_ams_drying_time": [
+ "12",
+ "12",
+ "12",
+ "12"
+ ],
+ "filament_dev_ams_drying_ams_limitations": [
+ "1",
+ "0"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "75"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "55"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "65",
+ "65",
+ "55",
+ "55"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "60"
+ ],
"hot_plate_temp": [
"70"
],
diff --git a/resources/profiles/BBL/filament/Generic PPS @base.json b/resources/profiles/BBL/filament/Generic PPS @base.json
index e8da776dfc..882fea94e0 100644
--- a/resources/profiles/BBL/filament/Generic PPS @base.json
+++ b/resources/profiles/BBL/filament/Generic PPS @base.json
@@ -4,5 +4,26 @@
"inherits": "fdm_filament_pps",
"from": "system",
"filament_id": "GFT97",
- "instantiation": "false"
+ "instantiation": "false",
+ "filament_dev_chamber_drying_bed_temperature": [
+ "90"
+ ],
+ "filament_dev_ams_drying_ams_limitations": [
+ "1"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "90"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "80"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "65",
+ "80",
+ "65",
+ "80"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "85"
+ ]
}
diff --git a/resources/profiles/BBL/filament/Polymaker/Fiberon PET-CF @base.json b/resources/profiles/BBL/filament/Polymaker/Fiberon PET-CF @base.json
index 921ce55dd6..365ebb2fd7 100644
--- a/resources/profiles/BBL/filament/Polymaker/Fiberon PET-CF @base.json
+++ b/resources/profiles/BBL/filament/Polymaker/Fiberon PET-CF @base.json
@@ -41,6 +41,30 @@
"filament_vendor": [
"Polymaker"
],
+ "filament_adhesiveness_category": [
+ "800"
+ ],
+ "filament_dev_chamber_drying_bed_temperature": [
+ "90"
+ ],
+ "filament_dev_ams_drying_ams_limitations": [
+ "1"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "165"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "150"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "65",
+ "80",
+ "65",
+ "80"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "150"
+ ],
"hot_plate_temp": [
"70"
],
@@ -83,9 +107,6 @@
"textured_plate_temp_initial_layer": [
"70"
],
- "filament_adhesiveness_category": [
- "800"
- ],
"filament_start_gcode": [
"; filament start gcode\n{if (bed_temperature[current_extruder] >80)||(bed_temperature_initial_layer[current_extruder] >80)}M106 P3 S255\n{elsif (bed_temperature[current_extruder] >60)||(bed_temperature_initial_layer[current_extruder] >60)}M106 P3 S180\n{endif}\n\n{if activate_air_filtration[current_extruder] && support_air_filtration}\nM106 P3 S{during_print_exhaust_fan_speed_num[current_extruder]} \n{endif}"
]
diff --git a/resources/profiles/BBL/filament/fdm_filament_abs.json b/resources/profiles/BBL/filament/fdm_filament_abs.json
index d05b005db9..4eb47ce0bd 100644
--- a/resources/profiles/BBL/filament/fdm_filament_abs.json
+++ b/resources/profiles/BBL/filament/fdm_filament_abs.json
@@ -43,6 +43,27 @@
"filament_adhesiveness_category": [
"200"
],
+ "filament_dev_ams_drying_time": [
+ "12",
+ "8.0",
+ "12",
+ "8.0"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "90"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "75"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "65",
+ "80",
+ "65",
+ "75"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "80"
+ ],
"hot_plate_temp": [
"90"
],
diff --git a/resources/profiles/BBL/filament/fdm_filament_asa.json b/resources/profiles/BBL/filament/fdm_filament_asa.json
index de053b5e14..b5ae3ad8b5 100644
--- a/resources/profiles/BBL/filament/fdm_filament_asa.json
+++ b/resources/profiles/BBL/filament/fdm_filament_asa.json
@@ -43,6 +43,27 @@
"filament_adhesiveness_category": [
"200"
],
+ "filament_dev_ams_drying_time": [
+ "12",
+ "8.0",
+ "12",
+ "8.0"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "100"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "85"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "65",
+ "80",
+ "65",
+ "80"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "85"
+ ],
"hot_plate_temp": [
"90"
],
diff --git a/resources/profiles/BBL/filament/fdm_filament_bvoh.json b/resources/profiles/BBL/filament/fdm_filament_bvoh.json
index 1a766a5a71..ee15cc5e30 100644
--- a/resources/profiles/BBL/filament/fdm_filament_bvoh.json
+++ b/resources/profiles/BBL/filament/fdm_filament_bvoh.json
@@ -43,6 +43,34 @@
"filament_type": [
"BVOH"
],
+ "filament_dev_chamber_drying_bed_temperature": [
+ "80"
+ ],
+ "filament_dev_ams_drying_time": [
+ "12",
+ "12",
+ "12",
+ "12"
+ ],
+ "filament_dev_ams_drying_ams_limitations": [
+ "1",
+ "0"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "65"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "40"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "60",
+ "60",
+ "45",
+ "45"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "50"
+ ],
"hot_plate_temp": [
"55"
],
diff --git a/resources/profiles/BBL/filament/fdm_filament_eva.json b/resources/profiles/BBL/filament/fdm_filament_eva.json
index 283606c21e..a3f9ab0011 100644
--- a/resources/profiles/BBL/filament/fdm_filament_eva.json
+++ b/resources/profiles/BBL/filament/fdm_filament_eva.json
@@ -7,6 +7,34 @@
"filament_type": [
"EVA"
],
+ "filament_dev_chamber_drying_bed_temperature": [
+ "70"
+ ],
+ "filament_dev_ams_drying_time": [
+ "12",
+ "12",
+ "12",
+ "12"
+ ],
+ "filament_dev_ams_drying_ams_limitations": [
+ "1",
+ "0"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "45"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "40"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "45",
+ "45",
+ "45",
+ "45"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "50"
+ ],
"supertack_plate_temp": [
"0"
],
diff --git a/resources/profiles/BBL/filament/fdm_filament_hips.json b/resources/profiles/BBL/filament/fdm_filament_hips.json
index 25c0acbb6f..7016a866f6 100644
--- a/resources/profiles/BBL/filament/fdm_filament_hips.json
+++ b/resources/profiles/BBL/filament/fdm_filament_hips.json
@@ -4,9 +4,6 @@
"inherits": "fdm_filament_common",
"from": "system",
"instantiation": "false",
- "additional_cooling_fan_speed": [
- "0"
- ],
"cool_plate_temp": [
"0"
],
@@ -42,6 +39,27 @@
],
"filament_adhesiveness_category": [
"798"
+ ],
+ "filament_dev_ams_drying_time": [
+ "12",
+ "12",
+ "12",
+ "12"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "90"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "75"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "65",
+ "80",
+ "65",
+ "75"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "80"
],
"hot_plate_temp": [
"90"
diff --git a/resources/profiles/BBL/filament/fdm_filament_pa.json b/resources/profiles/BBL/filament/fdm_filament_pa.json
index 629ef70885..61aeed66ce 100644
--- a/resources/profiles/BBL/filament/fdm_filament_pa.json
+++ b/resources/profiles/BBL/filament/fdm_filament_pa.json
@@ -43,6 +43,30 @@
"filament_adhesiveness_category": [
"400"
],
+ "filament_dev_ams_drying_time": [
+ "12",
+ "12",
+ "12",
+ "12"
+ ],
+ "filament_dev_chamber_drying_time": [
+ "18"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "165"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "150"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "65",
+ "85",
+ "65",
+ "85"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "150"
+ ],
"hot_plate_temp": [
"100"
],
diff --git a/resources/profiles/BBL/filament/fdm_filament_pc.json b/resources/profiles/BBL/filament/fdm_filament_pc.json
index 11dcf5ea46..a73a56b440 100644
--- a/resources/profiles/BBL/filament/fdm_filament_pc.json
+++ b/resources/profiles/BBL/filament/fdm_filament_pc.json
@@ -40,6 +40,27 @@
"filament_adhesiveness_category": [
"500"
],
+ "filament_dev_ams_drying_time": [
+ "12",
+ "8.0",
+ "12",
+ "8.0"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "105"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "90"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "65",
+ "80",
+ "65",
+ "80"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "90"
+ ],
"hot_plate_temp": [
"110"
],
diff --git a/resources/profiles/BBL/filament/fdm_filament_pctg.json b/resources/profiles/BBL/filament/fdm_filament_pctg.json
index f735c26f48..01810a2ddc 100644
--- a/resources/profiles/BBL/filament/fdm_filament_pctg.json
+++ b/resources/profiles/BBL/filament/fdm_filament_pctg.json
@@ -28,6 +28,37 @@
"filament_type": [
"PCTG"
],
+ "filament_dev_chamber_drying_bed_temperature": [
+ "80"
+ ],
+ "filament_dev_ams_drying_time": [
+ "12",
+ "12",
+ "12",
+ "12"
+ ],
+ "filament_dev_ams_drying_ams_limitations": [
+ "1",
+ "0"
+ ],
+ "filament_dev_chamber_drying_time": [
+ "12"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "75"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "55"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "65",
+ "65",
+ "55",
+ "55"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "60"
+ ],
"hot_plate_temp": [
"80"
],
diff --git a/resources/profiles/BBL/filament/fdm_filament_pe.json b/resources/profiles/BBL/filament/fdm_filament_pe.json
index 51549b2d8d..9235c3301e 100644
--- a/resources/profiles/BBL/filament/fdm_filament_pe.json
+++ b/resources/profiles/BBL/filament/fdm_filament_pe.json
@@ -40,6 +40,31 @@
"filament_type": [
"PE"
],
+ "filament_dev_chamber_drying_bed_temperature": [
+ "70"
+ ],
+ "filament_dev_ams_drying_time": [
+ "12",
+ "12",
+ "12",
+ "12"
+ ],
+ "filament_dev_ams_drying_ams_limitations": [
+ "1",
+ "0"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "50"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "45",
+ "45",
+ "45",
+ "45"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "50"
+ ],
"hot_plate_temp": [
"55"
],
diff --git a/resources/profiles/BBL/filament/fdm_filament_pet.json b/resources/profiles/BBL/filament/fdm_filament_pet.json
index 8fde5f2faa..23a8f73fe4 100644
--- a/resources/profiles/BBL/filament/fdm_filament_pet.json
+++ b/resources/profiles/BBL/filament/fdm_filament_pet.json
@@ -31,6 +31,34 @@
"filament_adhesiveness_category": [
"300"
],
+ "filament_dev_chamber_drying_bed_temperature": [
+ "80"
+ ],
+ "filament_dev_ams_drying_time": [
+ "12",
+ "12",
+ "12",
+ "12"
+ ],
+ "filament_dev_ams_drying_ams_limitations": [
+ "1",
+ "0"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "75"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "55"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "65",
+ "65",
+ "55",
+ "55"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "60"
+ ],
"hot_plate_temp": [
"80"
],
diff --git a/resources/profiles/BBL/filament/fdm_filament_pha.json b/resources/profiles/BBL/filament/fdm_filament_pha.json
index b9ac18587d..2bd348e2de 100644
--- a/resources/profiles/BBL/filament/fdm_filament_pha.json
+++ b/resources/profiles/BBL/filament/fdm_filament_pha.json
@@ -40,6 +40,31 @@
"filament_type": [
"PHA"
],
+ "filament_dev_chamber_drying_bed_temperature": [
+ "70"
+ ],
+ "filament_dev_ams_drying_time": [
+ "12",
+ "12",
+ "12",
+ "12"
+ ],
+ "filament_dev_ams_drying_ams_limitations": [
+ "1",
+ "0"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "45"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "45",
+ "45",
+ "45",
+ "45"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "50"
+ ],
"hot_plate_temp": [
"55"
],
diff --git a/resources/profiles/BBL/filament/fdm_filament_pla.json b/resources/profiles/BBL/filament/fdm_filament_pla.json
index 3c6713b86d..f1e1c9a244 100644
--- a/resources/profiles/BBL/filament/fdm_filament_pla.json
+++ b/resources/profiles/BBL/filament/fdm_filament_pla.json
@@ -37,14 +37,39 @@
"filament_max_volumetric_speed": [
"12"
],
- "filament_scarf_seam_type": [
- "none"
- ],
"filament_scarf_gap": [
"15%"
],
"filament_adhesiveness_category": [
"100"
+ ],
+ "filament_dev_chamber_drying_bed_temperature": [
+ "70"
+ ],
+ "filament_dev_ams_drying_time": [
+ "12",
+ "12",
+ "12",
+ "12"
+ ],
+ "filament_dev_ams_drying_ams_limitations": [
+ "1",
+ "0"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "45"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "45"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "45",
+ "45",
+ "45",
+ "45"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "50"
],
"hot_plate_temp": [
"55"
diff --git a/resources/profiles/BBL/filament/fdm_filament_pp.json b/resources/profiles/BBL/filament/fdm_filament_pp.json
index dd390e0a82..2d8c3772ba 100644
--- a/resources/profiles/BBL/filament/fdm_filament_pp.json
+++ b/resources/profiles/BBL/filament/fdm_filament_pp.json
@@ -43,6 +43,34 @@
"filament_adhesiveness_category": [
"902"
],
+ "filament_dev_chamber_drying_bed_temperature": [
+ "70"
+ ],
+ "filament_dev_ams_drying_time": [
+ "12",
+ "12",
+ "12",
+ "12"
+ ],
+ "filament_dev_ams_drying_ams_limitations": [
+ "1",
+ "0"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "60"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "50"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "60",
+ "60",
+ "50",
+ "50"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "55"
+ ],
"hot_plate_temp": [
"55"
],
diff --git a/resources/profiles/BBL/filament/fdm_filament_ppa.json b/resources/profiles/BBL/filament/fdm_filament_ppa.json
index bb06a73c76..3bba318bc5 100644
--- a/resources/profiles/BBL/filament/fdm_filament_ppa.json
+++ b/resources/profiles/BBL/filament/fdm_filament_ppa.json
@@ -46,6 +46,34 @@
"filament_vendor": [
"Bambu Lab"
],
+ "filament_dev_chamber_drying_bed_temperature": [
+ "100"
+ ],
+ "filament_dev_ams_drying_time": [
+ "12",
+ "12",
+ "12",
+ "12"
+ ],
+ "filament_dev_ams_drying_ams_limitations": [],
+ "filament_dev_chamber_drying_time": [
+ "18"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "165"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "150"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "65",
+ "85",
+ "65",
+ "85"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "150"
+ ],
"hot_plate_temp": [
"100"
],
diff --git a/resources/profiles/BBL/filament/fdm_filament_pps.json b/resources/profiles/BBL/filament/fdm_filament_pps.json
index a19a5aca2b..d1c6a6aed2 100644
--- a/resources/profiles/BBL/filament/fdm_filament_pps.json
+++ b/resources/profiles/BBL/filament/fdm_filament_pps.json
@@ -43,6 +43,31 @@
"filament_adhesiveness_category": [
"801"
],
+ "filament_dev_chamber_drying_bed_temperature": [
+ "100"
+ ],
+ "filament_dev_ams_drying_time": [
+ "12",
+ "12",
+ "12",
+ "12"
+ ],
+ "filament_dev_ams_drying_ams_limitations": [],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "165"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "150"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "65",
+ "85",
+ "65",
+ "85"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "150"
+ ],
"hot_plate_temp": [
"110"
],
diff --git a/resources/profiles/BBL/filament/fdm_filament_pva.json b/resources/profiles/BBL/filament/fdm_filament_pva.json
index 98ca7e63b9..55d83517ce 100644
--- a/resources/profiles/BBL/filament/fdm_filament_pva.json
+++ b/resources/profiles/BBL/filament/fdm_filament_pva.json
@@ -49,6 +49,27 @@
"filament_adhesiveness_category": [
"704"
],
+ "filament_dev_ams_drying_time": [
+ "12",
+ "18",
+ "12",
+ "18"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "75"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "40"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "65",
+ "85",
+ "65",
+ "70"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "75"
+ ],
"hot_plate_temp": [
"55"
],
diff --git a/resources/profiles/BBL/filament/fdm_filament_tpu.json b/resources/profiles/BBL/filament/fdm_filament_tpu.json
index f6ab1d3edf..d2ef5db84e 100644
--- a/resources/profiles/BBL/filament/fdm_filament_tpu.json
+++ b/resources/profiles/BBL/filament/fdm_filament_tpu.json
@@ -46,6 +46,30 @@
"filament_adhesiveness_category": [
"600"
],
+ "filament_dev_ams_drying_time": [
+ "12",
+ "18",
+ "12",
+ "18"
+ ],
+ "filament_dev_chamber_drying_time": [
+ "16"
+ ],
+ "filament_dev_ams_drying_heat_distortion_temperature": [
+ "45"
+ ],
+ "filament_dev_drying_cooling_temperature": [
+ "40"
+ ],
+ "filament_dev_ams_drying_temperature": [
+ "65",
+ "75",
+ "45",
+ "45"
+ ],
+ "filament_dev_drying_softening_temperature": [
+ "50"
+ ],
"hot_plate_temp": [
"35"
],
diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp
index 500ef42738..e3f8f53b31 100644
--- a/src/libslic3r/Preset.cpp
+++ b/src/libslic3r/Preset.cpp
@@ -470,6 +470,8 @@ void Preset::normalize(DynamicPrintConfig &config)
continue;
if (filament_options_with_variant.find(key) != filament_options_with_variant.end())
continue;
+ if (filament_dev_options.find(key) != filament_dev_options.end())
+ continue;
auto *opt = config.option(key, false);
/*assert(opt != nullptr);
assert(opt->is_vector());*/
@@ -1371,7 +1373,11 @@ static std::vector s_Preset_filament_options {/*"filament_colour",
"filament_pre_cooling_temperature", "filament_pre_cooling_temperature_nc",
"filament_preheat_temperature_delta", "filament_retract_length_nc",
"filament_change_length_nc", "filament_prime_volume_nc",
- "long_retractions_when_ec", "retraction_distances_when_ec"
+ "long_retractions_when_ec", "retraction_distances_when_ec",
+ //ams chamber
+ "filament_dev_ams_drying_ams_limitations", "filament_dev_ams_drying_temperature", "filament_dev_ams_drying_time", "filament_dev_ams_drying_heat_distortion_temperature",
+ "filament_dev_chamber_drying_bed_temperature", "filament_dev_chamber_drying_time",
+ "filament_dev_drying_softening_temperature", "filament_dev_drying_cooling_temperature"
};
static std::vector s_Preset_machine_limits_options {
@@ -3812,6 +3818,26 @@ void PresetCollection::set_custom_preset_alias(Preset &preset)
set_printer_hold_alias(preset.alias, preset);
}
+std::string PresetCollection::get_preset_alias(Preset &preset, bool force)
+{
+ if (!preset.alias.empty())
+ return preset.alias;
+ else
+ set_custom_preset_alias(preset);
+
+ if (!preset.alias.empty() || !force)
+ return preset.alias;
+
+ std::string alias_name;
+ std::string preset_name = preset.name;
+ size_t end_pos = preset_name.find_first_of("@");
+ if (end_pos != std::string::npos) {
+ alias_name = preset_name.substr(0, end_pos);
+ boost::trim_right(alias_name);
+ }
+ return alias_name;
+}
+
void PresetCollection::set_printer_hold_alias(const std::string &alias, Preset &preset, bool remove)
{
auto compatible_printers = dynamic_cast(preset.config.option("compatible_printers"));
diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp
index 75150f85c9..8863f04672 100644
--- a/src/libslic3r/Preset.hpp
+++ b/src/libslic3r/Preset.hpp
@@ -805,6 +805,9 @@ public:
std::string path_from_name(const std::string &new_name, bool detach = false) const;
std::string path_for_preset(const Preset & preset) const;
+ // Get the alias of a preset, setting it if it's empty
+ std::string get_preset_alias(Preset &preset, bool force = false);
+
size_t num_default_presets() { return m_num_default_presets; }
protected:
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index d8ed7ace58..e92562e132 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -7797,6 +7797,30 @@ void PrintConfigDef::init_fff_params()
def->min = 0;
def->set_default_value(new ConfigOptionPercent(85));
+ def = this->add("filament_dev_ams_drying_ams_limitations", coStrings);
+ def->set_default_value(new ConfigOptionStrings{""});
+
+ def = this->add("filament_dev_ams_drying_temperature", coFloats);
+ def->set_default_value(new ConfigOptionFloats{0});
+
+ def = this->add("filament_dev_ams_drying_time", coFloats);
+ def->set_default_value(new ConfigOptionFloats{0});
+
+ def = this->add("filament_dev_ams_drying_heat_distortion_temperature", coFloats);
+ def->set_default_value(new ConfigOptionFloats{0});
+
+ def = this->add("filament_dev_chamber_drying_bed_temperature", coFloats);
+ def->set_default_value(new ConfigOptionFloats{0});
+
+ def = this->add("filament_dev_chamber_drying_time", coFloats);
+ def->set_default_value(new ConfigOptionFloats{0});
+
+ def = this->add("filament_dev_drying_softening_temperature", coFloats);
+ def->set_default_value(new ConfigOptionFloats{0});
+
+ def = this->add("filament_dev_drying_cooling_temperature", coFloats);
+ def->set_default_value(new ConfigOptionFloats{0});
+
// Declare retract values for filament profile, overriding the printer's extruder profile.
for (auto& opt_key : filament_extruder_override_keys) {
const std::string filament_prefix = "filament_";
@@ -9232,6 +9256,17 @@ std::set printer_options_with_variant_2 = {
std::set empty_options;
+std::set filament_dev_options = {
+ "filament_dev_ams_drying_ams_limitations",
+ "filament_dev_ams_drying_temperature",
+ "filament_dev_ams_drying_time",
+ "filament_dev_ams_drying_heat_distortion_temperature",
+ "filament_dev_chamber_drying_bed_temperature",
+ "filament_dev_chamber_drying_time",
+ "filament_dev_drying_softening_temperature",
+ "filament_dev_drying_cooling_temperature"
+};
+
DynamicPrintConfig DynamicPrintConfig::full_print_config()
{
return DynamicPrintConfig((const PrintRegionConfig&)FullPrintConfig::defaults());
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index 2cad1da3d2..f9aab25f2f 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -840,6 +840,8 @@ extern std::set printer_options_with_variant_1;
extern std::set printer_options_with_variant_2;
extern std::set empty_options;
+extern std::set filament_dev_options;
+
extern void update_static_print_config_from_dynamic(ConfigBase& config, const DynamicPrintConfig& dest_config, std::vector variant_index, std::set& key_set1, int stride = 1);
extern void compute_filament_override_value(const std::string& opt_key, const ConfigOption *opt_old_machine, const ConfigOption *opt_new_machine, const ConfigOption *opt_new_filament, const DynamicPrintConfig& new_full_config,
t_config_option_keys& diff_keys, DynamicPrintConfig& filament_overrides, std::vector& f_map_indices);
@@ -1678,6 +1680,16 @@ PRINT_CONFIG_CLASS_DEFINE(
// Printer flag: whether the printer offers the fast-purge mode selector.
// Default false; no shipping profile sets it, so the fast-purge UI stays hidden.
((ConfigOptionBool, support_fast_purge_mode))
+
+ //ams chamber
+ ((ConfigOptionStrings, filament_dev_ams_drying_ams_limitations))
+ ((ConfigOptionFloats, filament_dev_ams_drying_temperature))
+ ((ConfigOptionFloats, filament_dev_ams_drying_time))
+ ((ConfigOptionFloats, filament_dev_ams_drying_heat_distortion_temperature))
+ ((ConfigOptionFloats, filament_dev_chamber_drying_bed_temperature))
+ ((ConfigOptionFloats, filament_dev_chamber_drying_time))
+ ((ConfigOptionFloats, filament_dev_drying_softening_temperature))
+ ((ConfigOptionFloats, filament_dev_drying_cooling_temperature))
)
// This object is mapped to Perl as Slic3r::Config::Print.
diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index 78ceb762a1..087825fffa 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -28,6 +28,8 @@ set(SLIC3R_GUI_SOURCES
GUI/AMSMaterialsSetting.hpp
GUI/AMSSetting.cpp
GUI/AMSSetting.hpp
+ GUI/AMSDryControl.cpp
+ GUI/AMSDryControl.hpp
GUI/AmsWidgets.cpp
GUI/AmsWidgets.hpp
GUI/Auxiliary.cpp
diff --git a/src/slic3r/GUI/AMSDryControl.cpp b/src/slic3r/GUI/AMSDryControl.cpp
new file mode 100644
index 0000000000..9efe7faa87
--- /dev/null
+++ b/src/slic3r/GUI/AMSDryControl.cpp
@@ -0,0 +1,1864 @@
+#include "AMSDryControl.hpp"
+#include "slic3r/GUI/DeviceCore/DevFilaSystem.h"
+#include "GUI_App.hpp"
+#include "I18N.hpp"
+
+#include "slic3r/GUI/DeviceCore/DevExtruderSystem.h"
+
+#include "slic3r/GUI/DeviceCore/DevManager.h"
+
+#include "slic3r/GUI/MsgDialog.hpp"
+
+#include "slic3r/GUI/Widgets/AnimaController.hpp"
+#include "slic3r/GUI/Widgets/Label.hpp"
+#include "slic3r/GUI/Widgets/ComboBox.hpp"
+#include "slic3r/GUI/Widgets/ProgressBar.hpp"
+#include "slic3r/GUI/DeviceCore/DevUtilBackend.h"
+
+#include "libslic3r/PresetBundle.hpp"
+#include "libslic3r/Preset.hpp"
+
+#include
+
+namespace Slic3r { namespace GUI {
+
+const int AMS_DRY_STATUS_IMAGE_SIZE = 96;
+
+static std::string get_humidity_level_img_path(int humidity_percent)
+{
+ const int num_levels = 5;
+ int hum_level;
+
+ if (humidity_percent <= 100 / num_levels) {
+ hum_level = 5;
+ } else if (humidity_percent <= 100 / num_levels * 2) {
+ hum_level = 4;
+ } else if (humidity_percent <= 100 / num_levels * 3) {
+ hum_level = 3;
+ } else if (humidity_percent <= 100 / num_levels * 4) {
+ hum_level = 2;
+ } else {
+ hum_level = 1;
+ }
+
+ if (wxGetApp().dark_mode()) {
+ return "hum_level" + std::to_string(hum_level) + "_no_num_dark";
+ } else {
+ return "hum_level" + std::to_string(hum_level) + "_no_num_light";
+ }
+
+}
+
+static std::string get_dry_status_img_path(DevAmsType type, DevAms::DryStatus status, DevAms::DrySubStatus sub_status)
+{
+ std::string img_name = "dev_ams_dry_ctr_";
+ switch (type) {
+ case DevAmsType::N3S:
+ img_name += "n3s";
+ break;
+ case DevAmsType::N3F:
+ img_name += "n3f";
+ break;
+ default:
+ return "";
+ }
+
+ img_name += "_";
+ switch (status) {
+ case DevAms::DryStatus::Error:
+ img_name += "error";
+ return img_name;
+ }
+
+ switch (sub_status) {
+ case DevAms::DrySubStatus::Heating:
+ img_name += "heating";
+ return img_name;
+ case DevAms::DrySubStatus::Dehumidify:
+ img_name += "dehumidifying";
+ return img_name;
+ }
+
+ return "";
+}
+
+FilamentItemPanel::FilamentItemPanel(wxWindow* parent, const wxString& text, const std::string& icon_name, wxWindowID id)
+ : wxPanel(parent, id)
+ , m_icon_name(icon_name)
+{
+ SetBackgroundColour(wxColour("#F0F0F1")); // Light gray background
+ SetMinSize(wxSize(FromDIP(64), FromDIP(106))); // Width: 64, Height: 106
+ SetSize(wxSize(FromDIP(64), FromDIP(106))); // Fixed size
+
+ // Create sizer for vertical layout
+ wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
+
+ // Top section with text - moved to be closer to center
+ wxBoxSizer* top_sizer = new wxBoxSizer(wxVERTICAL);
+ top_sizer->AddStretchSpacer(5);
+
+ m_text_label = new Label(this, text);
+ m_text_label->SetForegroundColour(StateColor::darkModeColorFor(wxColour(*wxBLACK)));
+ m_text_label->SetBackgroundColour(StateColor::darkModeColorFor(wxColour("#F0F0F1")));
+ m_text_label->SetFont(Label::Body_12);
+ m_text_label->Wrap(FromDIP(40));
+ top_sizer->Add(m_text_label, 0, wxALIGN_CENTER_HORIZONTAL);
+
+ top_sizer->AddStretchSpacer(1); // Increased bottom spacer to push text down
+ sizer->Add(top_sizer, 1, wxEXPAND);
+
+ // Bottom section with icon - vertically centered in bottom half
+ wxBoxSizer* bottom_sizer = new wxBoxSizer(wxVERTICAL);
+ bottom_sizer->AddStretchSpacer(1);
+
+ m_icon_bitmap = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap);
+ m_icon_bitmap->SetBackgroundColour(StateColor::darkModeColorFor(wxColour("#F0F0F1")));
+ m_icon_bitmap->SetMinSize(wxSize(FromDIP(24), FromDIP(24)));
+ m_icon_bitmap->SetMaxSize(wxSize(FromDIP(24), FromDIP(24)));
+ bottom_sizer->Add(m_icon_bitmap, 0, wxALIGN_CENTER_HORIZONTAL);
+
+ bottom_sizer->AddStretchSpacer(1);
+ sizer->Add(bottom_sizer, 1, wxEXPAND);
+
+ SetSizer(sizer);
+
+ // Bind events
+ Bind(wxEVT_PAINT, &FilamentItemPanel::OnPaint, this);
+ Bind(wxEVT_SIZE, &FilamentItemPanel::OnSize, this);
+
+ SetIcon(icon_name);
+ wxGetApp().UpdateDarkUI(this);
+}
+
+void FilamentItemPanel::SetText(const wxString& text)
+{
+ m_text_label->SetLabel(text);
+ Layout();
+}
+
+void FilamentItemPanel::SetIcon(const std::string& icon_name)
+{
+ m_icon_name = icon_name;
+ if (!icon_name.empty()) {
+ try {
+ m_icon = ScalableBitmap(this, icon_name, 20);
+ m_icon.msw_rescale();
+ m_icon_bitmap->SetBitmap(m_icon.bmp());
+ } catch (Exception&) {
+ BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": failed to load filament icon";
+ m_icon_bitmap->SetBitmap(wxNullBitmap);
+ }
+ } else {
+ m_icon_bitmap->SetBitmap(wxNullBitmap);
+ }
+ m_icon_bitmap->Refresh();
+}
+
+void FilamentItemPanel::msw_rescale()
+{
+ if (m_icon_bitmap && !m_icon_name.empty()) {
+ m_icon.msw_rescale();
+ m_icon_bitmap->SetBitmap(m_icon.bmp());
+ m_icon_bitmap->Refresh();
+ }
+ Layout();
+}
+
+void FilamentItemPanel::OnPaint(wxPaintEvent& event)
+{
+ wxPaintDC dc(this);
+ wxSize size = GetSize();
+
+ // bool is_dark_mode = wxGetApp().dark_mode();
+ wxColour backgroundColor = StateColor::darkModeColorFor(wxColour("#F0F0F1"));
+ wxColour borderColor = StateColor::darkModeColorFor(wxColour("#DBDBDB"));
+
+ // Draw white background rectangle with rounded corners inside the thick vertical lines
+ dc.SetBrush(wxBrush(backgroundColor));
+ dc.SetPen(wxPen(backgroundColor));
+ dc.DrawRectangle(FromDIP(6), FromDIP(12), size.GetWidth() - FromDIP(12), size.GetHeight() - 2 * FromDIP(12));
+
+ // Draw much thicker vertical rounded rectangles on left and right (3 times thicker)
+ dc.SetBrush(wxBrush(wxColour(borderColor)));
+ dc.SetPen(wxPen(wxColour(borderColor)));
+ dc.DrawRoundedRectangle(FromDIP(0), FromDIP(0), FromDIP(6), size.GetHeight(), FromDIP(6)); // Left rounded rectangle
+ dc.DrawRoundedRectangle(size.GetWidth() - FromDIP(6), FromDIP(0), FromDIP(6), size.GetHeight(), FromDIP(6)); // Right rounded rectangle
+
+ // Draw thin horizontal rounded rectangles on top and bottom (moved closer to center by half the distance)
+ dc.SetBrush(wxBrush(wxColour(borderColor)));
+ dc.SetPen(wxPen(wxColour(borderColor)));
+ dc.DrawRoundedRectangle(FromDIP(6), FromDIP(12), size.GetWidth() - FromDIP(12), FromDIP(3), FromDIP(1)); // Top rounded rectangle
+ dc.DrawRoundedRectangle(FromDIP(6), size.GetHeight() - FromDIP(13), size.GetWidth() - FromDIP(12), FromDIP(3), FromDIP(1)); // Bottom rounded rectangle
+}
+
+void FilamentItemPanel::OnSize(wxSizeEvent& event)
+{
+ Refresh();
+ event.Skip();
+}
+
+// class AMSFilamentPanel
+
+AMSFilamentPanel::AMSFilamentPanel(wxWindow* parent, const wxString& ams_name, wxWindowID id)
+ : wxPanel(parent, id)
+{
+ SetBackgroundColour(wxColour("#DBDBDB"));
+
+ wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
+
+ // Filament items section
+ m_filament_container = new wxPanel(this);
+ m_filament_container->SetBackgroundColour(wxColour("#F0F0F1"));
+ m_filament_sizer = new wxBoxSizer(wxHORIZONTAL);
+ m_filament_container->SetSizer(m_filament_sizer);
+
+ // AMS name section
+ m_ams_name_label = new Label(this, ams_name);
+ m_ams_name_label->SetForegroundColour(wxColour("#858585"));
+ m_ams_name_label->SetFont(Label::Body_14);
+ m_ams_name_label->SetBackgroundColour(wxColour("#DBDBDB"));
+
+ main_sizer->Add(m_filament_container, 1, wxEXPAND | wxALL, 0);
+ main_sizer->Add(m_ams_name_label, 0, wxALIGN_LEFT | wxALL, FromDIP(5));
+
+ SetSizer(main_sizer);
+ wxGetApp().UpdateDarkUI(this);
+}
+
+void AMSFilamentPanel::AddFilamentItem(FilamentItemPanel* panel)
+{
+ if (panel) {
+ panel->Reparent(m_filament_container);
+ m_filament_sizer->Add(panel, 0, wxALL, FromDIP(5));
+ m_filament_items.push_back(panel);
+ Layout();
+ }
+}
+
+void AMSFilamentPanel::AddFilamentItem(const wxString& text, const std::string& icon_name)
+{
+ FilamentItemPanel* item = new FilamentItemPanel(m_filament_container, text, icon_name);
+ m_filament_items.push_back(item);
+ m_filament_sizer->Add(item, 0, wxALL, FromDIP(5));
+ Layout();
+}
+
+void AMSFilamentPanel::SetAmsName(const wxString& ams_name)
+{
+ m_ams_name_label->SetLabel(ams_name);
+ Layout();
+}
+
+void AMSFilamentPanel::Clear()
+{
+ m_filament_sizer->Clear(true);
+ m_filament_items.clear();
+}
+
+void AMSFilamentPanel::msw_rescale()
+{
+ for (auto item : m_filament_items) {
+ item->msw_rescale();
+ }
+ Layout();
+}
+
+
+AMSDryCtrWin::AMSDryCtrWin(wxWindow *parent)
+ :DPIDialog(parent, wxID_ANY, _L("AMS Dryness Control"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
+{
+ create();
+}
+
+AMSDryCtrWin::~AMSDryCtrWin()
+{
+ if (m_progress_timer) {
+ delete m_progress_timer;
+ }
+}
+
+
+wxScrolledWindow* AMSDryCtrWin::create_preview_scrolled_window(wxWindow* parent)
+{
+ wxScrolledWindow* panel = new wxScrolledWindow(parent, wxID_ANY);
+ panel->SetScrollRate(10, 0);
+ panel->SetSize(AMS_ITEMS_PANEL_SIZE);
+ panel->SetMinSize(AMS_ITEMS_PANEL_SIZE);
+ panel->SetBackgroundColour(AMS_CONTROL_DEF_BLOCK_BK_COLOUR);
+
+ return panel;
+}
+
+wxBoxSizer* AMSDryCtrWin::create_humidity_status_section(wxPanel* parent)
+{
+ wxBoxSizer* image_sizer = new wxBoxSizer(wxVERTICAL);
+
+ m_humidity_img = new wxStaticBitmap(parent, wxID_ANY, wxNullBitmap);
+ image_sizer->Add(m_humidity_img, 1, wxALIGN_CENTER_HORIZONTAL, 0);
+
+ m_humidity_img->SetBitmap(wxNullBitmap);
+
+ // Create a horizontal sizer for description and icon
+ wxBoxSizer* desc_sizer = new wxBoxSizer(wxHORIZONTAL);
+
+ m_image_description_icon = new wxStaticBitmap(parent, wxID_ANY, wxNullBitmap);
+ m_image_description_icon->SetBackgroundColour(*wxWHITE);
+ m_image_description_icon->SetMinSize(wxSize(FromDIP(20), FromDIP(20)));
+ m_image_description_icon->SetMaxSize(wxSize(FromDIP(20), FromDIP(20)));
+ m_image_description_icon->Show(false);
+ desc_sizer->Add(m_image_description_icon, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(5));
+
+ m_image_description = new Label(parent, _L("Idle"));
+ m_image_description->SetFont(Label::Head_14);
+ m_image_description->SetForegroundColour(*wxBLACK);
+ m_image_description->SetBackgroundColour(*wxWHITE);
+ desc_sizer->Add(m_image_description, 0, wxALIGN_CENTER_VERTICAL);
+
+ image_sizer->Add(desc_sizer, 0, wxALIGN_CENTER | wxALL, FromDIP(5));
+
+ return image_sizer;
+}
+
+wxBoxSizer* AMSDryCtrWin::create_description_item(wxPanel* parent, const wxString& title, Label*& dataLabel)
+{
+ wxBoxSizer* item_sizer = new wxBoxSizer(wxVERTICAL);
+
+ Label* titleLabel = new Label(parent, title);
+ titleLabel->SetForegroundColour(*wxBLACK);
+ titleLabel->SetFont(Label::Body_16);
+
+ dataLabel = new Label(parent, wxT("--"));
+ dataLabel->SetForegroundColour(*wxBLACK);
+ dataLabel->SetFont(Label::Body_14);
+
+ item_sizer->AddStretchSpacer();
+ item_sizer->Add(titleLabel, 0, wxALIGN_CENTER | wxALL, FromDIP(2));
+ item_sizer->Add(dataLabel, 0, wxALIGN_CENTER | wxALL, FromDIP(2));
+ item_sizer->AddStretchSpacer();
+
+ return item_sizer;
+}
+
+wxBoxSizer* AMSDryCtrWin::create_status_descriptions_section(wxPanel* parent)
+{
+ wxBoxSizer* desc_sizer = new wxBoxSizer(wxHORIZONTAL);
+
+ wxBoxSizer* hum_desc_sizer = create_description_item(parent, _L("Humidity"), m_humidity_data_label);
+ desc_sizer->Add(hum_desc_sizer, 1, wxEXPAND, FromDIP(1));
+
+ wxStaticLine* vert_separator = new wxStaticLine(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL);
+ desc_sizer->Add(vert_separator, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(5));
+
+ wxBoxSizer* temp_desc_sizer = create_description_item(parent, _L("Temperature"), m_temperature_data_label);
+ desc_sizer->Add(temp_desc_sizer, 1, wxEXPAND, FromDIP(1));
+
+ m_time_descrition_container = new wxBoxSizer(wxHORIZONTAL);
+ wxStaticLine* vert_separator2 = new wxStaticLine(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL);
+ m_time_descrition_container->Add(vert_separator2, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(5));
+
+ wxBoxSizer* time_desc_sizer = create_description_item(parent, _L("Left Time"), m_time_data_label);
+ m_time_descrition_container->Add(time_desc_sizer, 1, wxEXPAND, FromDIP(1));
+
+ desc_sizer->Add(m_time_descrition_container, 1, wxEXPAND, 0);
+ // m_time_descrition_container->Show(false);
+
+ return desc_sizer;
+}
+
+wxBoxSizer* AMSDryCtrWin::create_left_panel(wxPanel* parent)
+{
+ wxBoxSizer* left_sizer = new wxBoxSizer(wxVERTICAL);
+
+ wxBoxSizer* image_section = create_humidity_status_section(parent);
+ left_sizer->Add(image_section, 1, wxEXPAND | wxALL, FromDIP(5));
+
+ wxBoxSizer* descriptions_section = create_status_descriptions_section(parent);
+ left_sizer->Add(descriptions_section, 0, wxEXPAND | wxALL, FromDIP(20));
+
+ return left_sizer;
+}
+
+Button* AMSDryCtrWin::create_button(wxPanel* parent, const wxString& title,
+ const wxColour& background_color, const wxColour& border_color, const wxColour& text_color)
+{
+ Button* button = new Button(parent, title);
+
+ // Create state colors for background
+ StateColor bg_color(
+ std::pair(AMS_CONTROL_DISABLE_COLOUR, StateColor::Disabled),
+ std::pair(background_color.ChangeLightness(80), StateColor::Pressed),
+ std::pair(background_color.ChangeLightness(120), StateColor::Hovered),
+ std::pair(background_color, StateColor::Normal)
+ );
+
+ // Create state colors for border
+ StateColor bd_color(
+ std::pair(AMS_CONTROL_WHITE_COLOUR, StateColor::Disabled),
+ std::pair(border_color, StateColor::Enabled)
+ );
+
+ button->SetBackgroundColor(bg_color);
+ button->SetBorderColor(bd_color);
+ button->SetTextColor(text_color);
+ button->SetFont(Label::Body_14);
+
+ // Auto-size button based on text content with padding
+ wxSize best_size = button->GetBestSize();
+ int padding_width = FromDIP(4);
+ int padding_height = FromDIP(2);
+ wxSize final_size(best_size.GetWidth() + padding_width, best_size.GetHeight() + padding_height);
+ button->SetMinSize(final_size);
+
+ return button;
+}
+
+wxBoxSizer* AMSDryCtrWin::create_normal_state_panel(wxPanel* parent)
+{
+ wxBoxSizer* normal_state_sizer = new wxBoxSizer(wxVERTICAL);
+
+ Label* description_label = new Label(parent, _L("Filament Drying Settings"));
+ description_label->SetForegroundColour(*wxBLACK);
+ description_label->SetFont(Label::Head_14);
+ normal_state_sizer->Add(description_label, 0, wxALL, FromDIP(5));
+
+ // Part 2: ComboBox for material selection
+ wxBoxSizer* combo_sizer = new wxBoxSizer(wxHORIZONTAL);
+ m_trays_combo = new ComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1, FromDIP(26)), 0, nullptr, wxCB_READONLY);
+ m_trays_combo->SetFont(Label::Body_14);
+
+ combo_sizer->Add(m_trays_combo, 1, wxEXPAND);
+ normal_state_sizer->Add(combo_sizer, 0, wxEXPAND | wxALL, FromDIP(5));
+
+ m_trays_combo->Bind(wxEVT_COMBOBOX, &AMSDryCtrWin::OnFilamentSelectionChanged, this);
+
+ // part 3 time and temperature
+ // Temperature part
+ wxBoxSizer* temp_sizer = new wxBoxSizer(wxHORIZONTAL);
+ m_temperature_input = new wxTextCtrl(parent, wxID_ANY, "", wxDefaultPosition, wxSize(FromDIP(80), -1));
+ m_temperature_input->SetMaxLength(3); // Limit to 3 digits
+
+ m_temperature_input->Bind(wxEVT_CHAR, [this](wxKeyEvent& event) {
+ int keycode = event.GetKeyCode();
+ if (keycode >= '0' && keycode <= '9') {
+ event.Skip();
+ } else if (keycode == WXK_BACK || keycode == WXK_DELETE || keycode == WXK_LEFT || keycode == WXK_RIGHT) {
+ event.Skip();
+ }
+ });
+
+ m_temperature_input->Bind(wxEVT_TEXT, [this](wxCommandEvent&) {
+ m_next_button->Disable();
+ m_start_button->Disable();
+ });
+
+ m_temperature_input->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE));
+ m_temperature_input->SetForegroundColour(StateColor::darkModeColorFor(*wxBLACK));
+
+ Label* temp_unit_label = new Label(parent, wxString::FromUTF8("℃"));
+ temp_unit_label->SetForegroundColour(*wxBLACK);
+ temp_sizer->Add(m_temperature_input, 1, wxRIGHT, FromDIP(1));
+ temp_sizer->Add(temp_unit_label, 0, wxALIGN_CENTER_VERTICAL);
+
+ // Time part
+ wxBoxSizer* time_sizer = new wxBoxSizer(wxHORIZONTAL);
+ m_time_input = new wxTextCtrl(parent, wxID_ANY, "", wxDefaultPosition, wxSize(FromDIP(100), -1));
+ m_time_input->SetMaxLength(3); // Limit to 3 digits
+
+ m_time_input->Bind(wxEVT_CHAR, [this](wxKeyEvent& event) {
+ int keycode = event.GetKeyCode();
+ if (keycode >= '0' && keycode <= '9') {
+ event.Skip();
+ } else if (keycode == WXK_BACK || keycode == WXK_DELETE || keycode == WXK_LEFT || keycode == WXK_RIGHT) {
+ event.Skip();
+ }
+ });
+
+ m_time_input->Bind(wxEVT_TEXT, [this](wxCommandEvent&) {
+ m_next_button->Disable();
+ m_start_button->Disable();
+ });
+
+ m_time_input->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE));
+ m_time_input->SetForegroundColour(StateColor::darkModeColorFor(*wxBLACK));
+
+ Label* time_unit_label = new Label(parent, "H");
+ time_unit_label->SetForegroundColour(*wxBLACK);
+ time_sizer->Add(m_time_input, 1, wxRIGHT, FromDIP(1));
+ time_sizer->Add(time_unit_label, 0, wxALIGN_CENTER_VERTICAL);
+
+ wxBoxSizer* input_sizer = new wxBoxSizer(wxHORIZONTAL);
+ input_sizer->Add(temp_sizer, 1, wxRIGHT, FromDIP(10));
+ input_sizer->Add(time_sizer, 1, 0);
+ normal_state_sizer->Add(input_sizer, 0, wxEXPAND | wxALL, FromDIP(5));
+
+ // Part 4: Abnormal description/message area
+ m_normal_description = new Label(parent, "");
+ m_normal_description->SetForegroundColour(wxColour("#F09A17"));
+ m_normal_description->SetFont(Label::Body_12);
+ normal_state_sizer->Add(m_normal_description, 0, wxALL, FromDIP(5));
+
+ // Part 5: Start button
+ m_next_button = create_button(
+ parent,
+ _L("Start"),
+ AMS_CONTROL_BRAND_COLOUR, // Background color - green
+ AMS_CONTROL_BRAND_COLOUR, // Border color - green
+ wxColour("#FFFFFE") // Text color - white
+ );
+
+ m_next_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](wxCommandEvent& event) {
+ m_main_simplebook->SetSelection(1);
+ });
+
+ normal_state_sizer->Add(m_next_button, 0, wxALL, FromDIP(5));
+ m_next_button->Disable();
+
+ m_stop_button = create_button(
+ parent,
+ _L("Stop"),
+ wxColour(255, 0, 0), // Background color - red
+ wxColour(255, 0, 0), // Border color - red
+ wxColour("#FFFFFE") // Text color - white
+ );
+
+ m_stop_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](wxCommandEvent& event) {
+ auto fila_system = get_fila_system();
+ if (!fila_system) {
+ BOOST_LOG_TRIVIAL(info) << "AMSDryCtrWin::start_sending_drying_command: Invalid FilaSystem Pointer";
+ return;
+ }
+
+ fila_system->CtrlAmsStopDrying(std::stoi(m_ams_info.m_ams_id));
+ // Temporarily show stopping state, then restore after 2 seconds
+ if (m_stop_button) {
+ m_stop_button->SetLabel(_L("Stopping"));
+ update_button_size(m_stop_button); // Adjust button size for longer text
+ m_stop_button->Disable();
+ m_stop_button->Layout();
+ m_stop_button->GetParent()->Layout(); // Relayout parent container
+ m_stop_button->Refresh();
+
+ m_stop_button_restore_deadline.reset();
+ m_stop_button_restore_deadline = std::chrono::steady_clock::now() + std::chrono::seconds(2);
+ }
+ });
+
+ m_stop_button->Show(false);
+ normal_state_sizer->Add(m_stop_button, 0, wxALL, FromDIP(5));
+
+ return normal_state_sizer;
+}
+
+wxBoxSizer* AMSDryCtrWin::create_cannot_dry_panel(wxPanel* parent)
+{
+ wxBoxSizer* abnormal_sizer = new wxBoxSizer(wxVERTICAL);
+
+ Label* description_label = new Label(parent, _L("Unable to dry temporarily due to ..."));
+ description_label->SetForegroundColour(*wxBLACK);
+ description_label->SetFont(Label::Head_16);
+ description_label->Wrap(FromDIP(300));
+ abnormal_sizer->Add(description_label, 0, wxALIGN_CENTER_VERTICAL, FromDIP(10));
+
+ // Add a description label for the abnormal state
+ m_cannot_dry_description_label = new Label(parent, (""));
+ m_cannot_dry_description_label->SetForegroundColour(*wxBLACK);
+ m_cannot_dry_description_label->SetFont(Label::Body_14);
+ m_cannot_dry_description_label->Wrap(FromDIP(250)); // Wrap text to fit within panel
+
+ abnormal_sizer->Add(m_cannot_dry_description_label, 0, wxALIGN_CENTER_VERTICAL, FromDIP(10));
+
+ // Unload button shown only when ConsumableAtAmsOutlet reason is active
+ m_unload_button = create_button(
+ parent,
+ _L("Unload"),
+ AMS_CONTROL_BRAND_COLOUR, // Background color - light gray
+ AMS_CONTROL_BRAND_COLOUR, // Border color - gray
+ *wxWHITE // Text color - white
+ );
+
+ m_unload_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](wxCommandEvent& event) {
+ auto fila_system = get_fila_system();
+ if (!fila_system) {
+ BOOST_LOG_TRIVIAL(info) << "AMSDryCtrWin::unload_button: Invalid FilaSystem Pointer";
+ return;
+ }
+
+ MachineObject* obj = fila_system->GetOwner();
+ if (!obj) {
+ BOOST_LOG_TRIVIAL(info) << "AMSDryCtrWin::unload_button: Invalid MachineObject Pointer";
+ return;
+ }
+
+ obj->command_ams_change_filament(false, m_ams_info.m_ams_id, "255");
+
+ m_unload_button->Disable();
+ m_unload_button_restore_deadline.reset();
+ m_unload_button_restore_deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5);
+ });
+
+ m_unload_button->Show(false);
+ abnormal_sizer->Add(m_unload_button, 0, wxALL, FromDIP(5));
+
+ return abnormal_sizer;
+}
+
+wxBoxSizer* AMSDryCtrWin::create_drying_error_panel(wxPanel* parent)
+{
+ wxBoxSizer* err_sizer = new wxBoxSizer(wxVERTICAL);
+ Label* description_label = new Label(parent, _L("Drying Error"));
+ description_label->SetForegroundColour(*wxRED);
+ description_label->SetFont(Label::Body_14); // Wrap text to fit within panel
+ err_sizer->Add(description_label, 0, wxALIGN_CENTER_VERTICAL, FromDIP(10));
+
+ Label* additional_description_label = new Label(parent, _L("Please check the Assistant for troubleshooting"));
+ additional_description_label->SetForegroundColour(*wxBLACK);
+ additional_description_label->SetFont(Label::Body_14); // Wrap text to fit within panel
+ err_sizer->Add(additional_description_label, 0, wxALIGN_CENTER_VERTICAL, FromDIP(10));
+
+ return err_sizer;
+}
+
+wxBoxSizer* AMSDryCtrWin::create_right_panel(wxPanel* parent)
+{
+ wxBoxSizer* right_sizer = new wxBoxSizer(wxHORIZONTAL);
+
+ m_normal_state_sizer = create_normal_state_panel(parent);
+ m_cannot_dry_sizer = create_cannot_dry_panel(parent);
+ m_dry_error_sizer = create_drying_error_panel(parent);
+
+ right_sizer->Add(m_normal_state_sizer, 1, wxEXPAND);
+ right_sizer->Add(m_cannot_dry_sizer, 1, wxEXPAND);
+
+ m_cannot_dry_sizer->Show(false);
+ m_dry_error_sizer->Show(false);
+
+ return right_sizer;
+}
+
+wxBoxSizer* AMSDryCtrWin::create_main_content_section(wxPanel* parent)
+{
+ wxBoxSizer* content_sizer = new wxBoxSizer(wxHORIZONTAL);
+
+ wxBoxSizer* left_panel = create_left_panel(parent);
+ content_sizer->Add(left_panel, 2, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5));
+ left_panel->SetMinSize(wxSize(FromDIP(250), -1));
+
+ wxBoxSizer* right_panel = create_right_panel(parent);
+ content_sizer->Add(right_panel, 1, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5));
+ right_panel->SetMinSize(wxSize(FromDIP(250), -1));
+
+ return content_sizer;
+}
+
+wxBoxSizer* AMSDryCtrWin::create_main_page_sizer(wxPanel* parent)
+{
+ wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
+
+ wxBoxSizer* content_section = create_main_content_section(parent);
+ main_sizer->Add(content_section, 1, wxEXPAND | wxALL, FromDIP(5));
+
+ return main_sizer;
+}
+
+wxBoxSizer* AMSDryCtrWin::create_guide_info_filament(wxPanel* parent)
+{
+ wxBoxSizer* filament_section = new wxBoxSizer(wxHORIZONTAL);
+
+ m_ams_filament_panel = new AMSFilamentPanel(parent, "");
+
+ filament_section->Add(m_ams_filament_panel, 0, wxEXPAND | wxALL | wxALIGN_CENTER_HORIZONTAL, FromDIP(5));
+
+ return filament_section;
+}
+
+wxBoxSizer* AMSDryCtrWin::create_guide_info_section(wxPanel* parent)
+{
+ wxBoxSizer* info_section = new wxBoxSizer(wxVERTICAL);
+
+ // Part 1: Title
+ m_guide_title_label = new Label(parent, _L("Please remove and store the filament (as shown)."));
+ m_guide_title_label->SetForegroundColour(*wxBLACK);
+ m_guide_title_label->SetFont(Label::Head_18);
+ info_section->Add(m_guide_title_label, 0, wxEXPAND | wxALL, FromDIP(5));
+
+ // Part 2: Description
+ m_guide_description_label = new Label(parent, _L("The AMS can rotate the filament which is properly stored, providing better drying results."));
+ m_guide_description_label->SetForegroundColour(*wxBLACK);
+ m_guide_description_label->SetFont(Label::Body_14);
+ m_guide_description_label->Wrap(FromDIP(300)); // Wrap text to fit within panel
+ info_section->Add(m_guide_description_label, 0, wxEXPAND | wxALL, FromDIP(5));
+
+ // Part 3: filament panel
+ wxBoxSizer* fila_section = create_guide_info_filament(parent);
+ info_section->Add(fila_section, 0, wxEXPAND | wxALL, FromDIP(5));
+
+ // Part 4: Circular toggle with description
+ wxBoxSizer* toggle_section = new wxBoxSizer(wxHORIZONTAL);
+
+ m_rotate_spool_toggle = new wxCheckBox(parent, wxID_ANY, "");
+ m_rotate_spool_toggle->SetValue(false);
+
+ m_rotate_spool_toggle->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& event) {
+ bool is_checked = event.IsChecked();
+ // Add toggle behavior logic here
+ });
+
+ toggle_section->Add(m_rotate_spool_toggle, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(10));
+
+ // Toggle description
+ Label* toggle_description = new Label(parent, _L("Rotate spool when drying"), LB_AUTO_WRAP);
+ toggle_description->SetForegroundColour(*wxBLACK);
+ toggle_description->SetFont(Label::Body_12);
+ toggle_section->Add(toggle_description, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND, 0);
+
+ info_section->Add(toggle_section, 0, wxEXPAND | wxALL, FromDIP(5));
+
+ return info_section;
+}
+
+wxBoxSizer* AMSDryCtrWin::create_guide_right_section(wxPanel* parent)
+{
+ wxBoxSizer* right_section = new wxBoxSizer(wxVERTICAL);
+
+ // Upper part: Image section
+ m_image_placeholder = new wxStaticBitmap(parent, wxID_ANY, wxNullBitmap);
+ m_guide_image = ScalableBitmap(parent, "dev_ams_dry_ctr_filament_in_chamber", 256);
+ m_image_placeholder->SetBitmap(m_guide_image.bmp());
+
+ wxBoxSizer* image_container = new wxBoxSizer(wxHORIZONTAL);
+ image_container->AddStretchSpacer(1);
+ image_container->Add(m_image_placeholder, 0, wxALL, FromDIP(5));
+
+ right_section->Add(image_container, 0, wxEXPAND | wxALL, FromDIP(5));
+
+ wxBoxSizer* buttons_container = new wxBoxSizer(wxHORIZONTAL);
+ buttons_container->AddStretchSpacer(1);
+
+ m_back_button = create_button(
+ parent,
+ wxString::FromUTF8(_CTX_utf8(L_CONTEXT("Back", "amsdrying"), "amsdrying")),
+ wxColour("#F8F8F8"), // Background color - light gray
+ wxColour("#D0D0D0"), // Border color - gray
+ *wxBLACK // Text color - black
+ );
+
+ m_back_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](wxCommandEvent& event) {
+ m_main_simplebook->SetSelection(0);
+ });
+
+ buttons_container->Add(m_back_button, 0, wxALL, FromDIP(5));
+
+ m_start_button = create_button(
+ parent,
+ _L("Start"),
+ AMS_CONTROL_BRAND_COLOUR, // Background color - green
+ AMS_CONTROL_BRAND_COLOUR, // Border color - green
+ wxColour("#FFFFFE") // Text color - white
+ );
+
+ m_start_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](wxCommandEvent& event) {
+ m_main_simplebook->SetSelection(2);
+ m_progress_gauge->SetValue(0);
+
+ m_progress_timer->Start(70);
+ m_progress_value = 0;
+ start_sending_drying_command();
+ });
+
+ buttons_container->Add(m_start_button, 0, wxALL, FromDIP(5));
+
+ right_section->Add(buttons_container, 0, wxEXPAND | wxALL, FromDIP(5));
+
+ return right_section;
+}
+
+wxBoxSizer* AMSDryCtrWin::create_guide_page_sizer(wxPanel* parent)
+{
+ wxBoxSizer* guide_sizer = new wxBoxSizer(wxHORIZONTAL);
+
+ wxBoxSizer* info_section = create_guide_info_section(parent);
+ guide_sizer->Add(info_section, 1, wxEXPAND | wxALL, FromDIP(10));
+
+ wxBoxSizer* right_section = create_guide_right_section(parent);
+ guide_sizer->Add(right_section, 0, wxEXPAND | wxALL, FromDIP(10));
+
+ return guide_sizer;
+}
+
+void AMSDryCtrWin::start_sending_drying_command()
+{
+ auto fila_system = get_fila_system();
+ if (!fila_system) {
+ BOOST_LOG_TRIVIAL(info) << "AMSDryCtrWin::start_sending_drying_command: Invalid FilaSystem Pointer";
+ return;
+ }
+
+ if (m_temperature_input->GetValue().IsEmpty() || m_time_input->GetValue().IsEmpty()) {
+ // Show error message to user
+ BOOST_LOG_TRIVIAL(info) << "AMSDryCtrWin::start_sending_drying_command: Time or temperature is empty";
+ return;
+ }
+
+ long temperature, time;
+ if (!m_temperature_input->GetValue().ToLong(&temperature) ||
+ !m_time_input->GetValue().ToLong(&time)) {
+ BOOST_LOG_TRIVIAL(info) << "AMSDryCtrWin::start_sending_drying_command: Failed to convert temperature or time";
+ return;
+ }
+
+ int tray_index;
+ tray_index = m_trays_combo->GetSelection();
+ if (m_tray_ids.empty() || tray_index < 0 || tray_index >= m_tray_ids.size()) {
+ BOOST_LOG_TRIVIAL(warning) << "AMSDryCtrWin::start_sending_drying_command: Invalid tray_index " << tray_index
+ << ", m_tray_ids.size=" << m_tray_ids.size();
+ return;
+ }
+
+ int cooling_temp = 50;
+ std::optional preset = DevUtilBackend::GetFilamentDryingPreset(m_tray_ids[tray_index].filament_id);
+ if (preset.has_value()) {
+ cooling_temp = static_cast(preset.value().filament_dev_drying_softening_temperature);
+ }
+
+ fila_system->CtrlAmsStartDryingHour(std::stoi(m_ams_info.m_ams_id), m_tray_ids[tray_index].filament_type,
+ temperature, time, m_rotate_spool_toggle->GetValue(), cooling_temp, false);
+
+ m_dry_setting.m_filament_names[m_ams_info.m_ams_id] = m_tray_ids[tray_index].filament_name;
+ m_dry_setting.m_filament_type[m_ams_info.m_ams_id] = m_tray_ids[tray_index].filament_type;
+ m_dry_setting.m_dry_temp[m_ams_info.m_ams_id] = temperature;
+ m_dry_setting.m_dry_time[m_ams_info.m_ams_id] = time;
+}
+
+void AMSDryCtrWin::OnProgressTimer(wxTimerEvent& event)
+{
+ m_progress_value += 1;
+
+ if (m_progress_value <= 100)
+ m_progress_gauge->SetValue(m_progress_value);
+
+ if (m_progress_value == 1) { // First tick, reset message index
+ m_progress_message_index = 0;
+ if (!m_progress_text.empty()) {
+ m_progress_title->SetLabel(m_progress_text[0]);
+ }
+ }
+ else if (m_progress_value % 15 == 0) { // Approximately every second
+ m_progress_message_index++;
+ if (m_progress_message_index < m_progress_text.size()) {
+ m_progress_title->SetLabel(m_progress_text[m_progress_message_index]);
+ m_progress_title->Refresh();
+ }
+ }
+
+ if (m_progress_value >= 100 && !is_dry_ctr_idle()) {
+ m_progress_value = 0;
+ m_progress_timer->Stop();
+ m_main_simplebook->SetSelection(0);
+ }
+}
+
+void AMSDryCtrWin::restore_stop_button_if_deadline_passed()
+{
+ if (m_stop_button_restore_deadline.has_value()) {
+ if (std::chrono::steady_clock::now() >= m_stop_button_restore_deadline.value()) {
+ if (m_stop_button) {
+ m_stop_button->SetLabel(_L("Stop"));
+ update_button_size(m_stop_button); // Adjust button size back to original
+ m_stop_button->Enable();
+ m_stop_button->Layout();
+ m_stop_button->GetParent()->Layout();
+ m_stop_button->Refresh();
+ }
+ m_stop_button_restore_deadline.reset();
+ }
+ }
+}
+
+void AMSDryCtrWin::restore_unload_button_if_deadline_passed()
+{
+ if (m_unload_button_restore_deadline.has_value()) {
+ if (std::chrono::steady_clock::now() >= m_unload_button_restore_deadline.value()) {
+ if (m_unload_button) {
+ m_unload_button->Enable();
+ }
+ m_unload_button_restore_deadline.reset();
+ }
+ }
+}
+
+void AMSDryCtrWin::update_button_size(Button* button)
+{
+ if (!button) return;
+
+ // Reset MinSize first to avoid accumulation of size
+ button->SetMinSize(wxSize(-1, -1));
+
+ // Recalculate button size based on current text and DPI settings
+ wxSize best_size = button->GetBestSize();
+ int padding_width = FromDIP(4);
+ int padding_height = FromDIP(2);
+ wxSize final_size(best_size.GetWidth() + padding_width, best_size.GetHeight() + padding_height);
+ button->SetMinSize(final_size);
+}
+
+void AMSDryCtrWin::OnShow(wxShowEvent& event)
+{
+ if (event.IsShown()) {
+ wxGetApp().UpdateDlgDarkUI(this);
+ msw_rescale();
+ }
+ event.Skip();
+}
+
+void AMSDryCtrWin::OnClose(wxCloseEvent& event)
+{
+ if (m_progress_timer && m_progress_timer->IsRunning()) {
+ m_progress_timer->Stop();
+ }
+
+ if (m_progress_gauge) {
+ m_progress_gauge->SetValue(0);
+ }
+
+ m_progress_value = 0;
+ m_progress_message_index = 0;
+
+ if (m_main_simplebook) {
+ m_main_simplebook->SetSelection(0);
+ }
+
+ if (m_progress_title && !m_progress_text.empty()) {
+ m_progress_title->SetLabel(m_progress_text[0]);
+ }
+
+ // Clean up and restore stop button state
+ if (m_stop_button) {
+ m_stop_button->SetLabel(_L("Stop"));
+ update_button_size(m_stop_button); // Adjust button size back to original
+ m_stop_button->Enable();
+ m_stop_button->Layout();
+ m_stop_button->GetParent()->Layout();
+ m_stop_button->Refresh();
+ }
+ m_stop_button_restore_deadline.reset();
+
+ // Clean up unload button state
+ if (m_unload_button) {
+ m_unload_button->Enable();
+ }
+ m_unload_button_restore_deadline.reset();
+
+ event.Skip();
+}
+
+wxBoxSizer* AMSDryCtrWin::create_progress_page_sizer(wxPanel* parent)
+{
+ wxBoxSizer* progress_sizer = new wxBoxSizer(wxVERTICAL);
+
+ m_progress_title = new Label(parent, m_progress_text[0]);
+ m_progress_title->SetForegroundColour(*wxBLACK);
+ m_progress_title->SetFont(Label::Body_16);
+
+ progress_sizer->Add(0, 0, 1, wxEXPAND, 0); // Spacer
+ progress_sizer->Add(m_progress_title, 0, wxALIGN_CENTER | wxALL, FromDIP(30));
+
+ m_progress_gauge = new ProgressBar(parent, wxID_ANY, 100, wxDefaultPosition, wxSize(FromDIP(300), FromDIP(8)));
+ m_progress_gauge->SetValue(0);
+ m_progress_gauge->SetHeight(FromDIP(8));
+
+ progress_sizer->Add(m_progress_gauge, 0, wxALIGN_CENTER | wxALL, FromDIP(10));
+ progress_sizer->Add(0, 0, 1, wxEXPAND, 0);
+
+ return progress_sizer;
+}
+
+void AMSDryCtrWin::create()
+{
+ // set title icon
+ SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE));
+ this->SetDoubleBuffered(true);
+ std::string icon_path = (boost::format("%1%/images/OrcaSlicerTitle.ico") % resources_dir()).str();
+ SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO));
+
+ SetSize(wxSize(FromDIP(700), FromDIP(500)));
+ SetMinSize(wxSize(FromDIP(700), FromDIP(500)));
+ SetMaxSize(wxSize(FromDIP(700), FromDIP(500)));
+
+ m_main_simplebook = new wxSimplebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize);
+
+ // Create main page
+ m_original_page = new wxPanel(m_main_simplebook, wxID_ANY);
+ m_original_page->SetBackgroundColour(*wxWHITE);
+ wxBoxSizer* main_sizer = create_main_page_sizer(m_original_page);
+ m_original_page->SetSizer(main_sizer);
+
+ m_main_simplebook->AddPage(m_original_page, "Main Page");
+
+ m_guide_page = new wxPanel(m_main_simplebook, wxID_ANY);
+ m_guide_page->SetBackgroundColour(*wxWHITE);
+ wxBoxSizer* guide_sizer = create_guide_page_sizer(m_guide_page);
+ m_guide_page->SetSizer(guide_sizer);
+ m_main_simplebook->AddPage(m_guide_page, "Guide Page");
+
+ // Create progress page
+ m_progress_page = new wxPanel(m_main_simplebook, wxID_ANY);
+ m_progress_page->SetBackgroundColour(*wxWHITE);
+ wxBoxSizer* progress_sizer = create_progress_page_sizer(m_progress_page);
+ m_progress_page->SetSizer(progress_sizer);
+ m_main_simplebook->AddPage(m_progress_page, "Progress Page");
+
+ m_progress_timer = new wxTimer(this, wxID_ANY);
+ Bind(wxEVT_TIMER, &AMSDryCtrWin::OnProgressTimer, this, m_progress_timer->GetId());
+
+ Bind(wxEVT_CLOSE_WINDOW, &AMSDryCtrWin::OnClose, this);
+
+ wxBoxSizer* top_level_sizer = new wxBoxSizer(wxVERTICAL);
+ top_level_sizer->Add(m_main_simplebook, 1, wxEXPAND | wxALL, FromDIP(10));
+
+ Bind(wxEVT_SHOW, &AMSDryCtrWin::OnShow, this);
+
+ SetSizer(top_level_sizer);
+ Layout();
+ Refresh();
+ wxGetApp().UpdateDlgDarkUI(this);
+}
+
+void AMSDryCtrWin::on_dpi_changed(const wxRect &suggested_rect)
+{
+ msw_rescale();
+}
+
+void AMSDryCtrWin::msw_rescale()
+{
+ if (!IsShown()) {
+ return;
+ }
+
+ if (m_ams_filament_panel) {m_ams_filament_panel->msw_rescale();}
+ if (m_trays_combo) {m_trays_combo->Rescale(); m_trays_combo->Layout(); m_trays_combo->SetFont(Label::Body_14), m_trays_combo->Refresh();}
+ if (m_humidity_img && m_humidity_image.bmp().IsOk()) {m_humidity_image.msw_rescale(); m_humidity_img->SetBitmap(m_humidity_image.bmp());}
+ if (m_image_placeholder && m_guide_image.bmp().IsOk()) {m_guide_image.msw_rescale();m_image_placeholder->SetBitmap(m_guide_image.bmp());}
+ if (m_image_description_icon && m_description_icon_bitmap.bmp().IsOk()) {m_description_icon_bitmap.msw_rescale(); m_image_description_icon->SetBitmap(m_description_icon_bitmap.bmp());}
+
+ // Rescale and update button sizes based on text content
+ if (m_next_button) {
+ m_next_button->Rescale();
+ update_button_size(m_next_button);
+ m_next_button->Layout();
+ m_next_button->Refresh();
+ }
+ if (m_stop_button) {
+ m_stop_button->Rescale();
+ update_button_size(m_stop_button);
+ m_stop_button->Layout();
+ m_stop_button->Refresh();
+ }
+ if (m_start_button) {
+ m_start_button->Rescale();
+ update_button_size(m_start_button);
+ m_start_button->Layout();
+ m_start_button->Refresh();
+ }
+ if (m_back_button) {
+ m_back_button->Rescale();
+ update_button_size(m_back_button);
+ m_back_button->Layout();
+ m_back_button->Refresh();
+ }
+ if (m_unload_button) {
+ m_unload_button->Rescale();
+ update_button_size(m_unload_button);
+ m_unload_button->Layout();
+ m_unload_button->Refresh();
+ }
+
+ if (m_guide_page) {m_guide_page->Layout();}
+ if (m_original_page) {m_original_page->Layout();}
+
+ Fit();
+ Layout();
+ Refresh();
+}
+
+void AMSDryCtrWin::set_ams_id(const std::string& ams_id)
+{
+ m_ams_info.m_ams_id = ams_id;
+ m_is_ams_changed = true;
+}
+
+void AMSDryCtrWin::update_img_description(DevAms::DryStatus status, DevAms::DrySubStatus sub_status)
+{
+ if (status == DevAms::DryStatus::Off || status == DevAms::DryStatus::Cooling) {
+ m_image_description->SetLabel(_L("Idle"));
+ m_image_description_icon->Show(false);
+ return;
+ }
+
+ // Determine label text for non-idle states
+ wxString label_text;
+ if (status == DevAms::DryStatus::Error) {
+ label_text = _L("Drying");
+ } else if (sub_status == DevAms::DrySubStatus::Heating) {
+ label_text = _L("Drying-Heating");
+ } else if (sub_status == DevAms::DrySubStatus::Dehumidify) {
+ label_text = _L("Drying-Dehumidifying");
+ } else {
+ m_image_description_icon->Show(false);
+ return;
+ }
+
+ m_image_description->SetLabel(label_text);
+
+ try {
+ m_description_icon_bitmap = ScalableBitmap(this, "dev_ams_dry_ctr_heating_icon", 20);
+ m_description_icon_bitmap.msw_rescale();
+ m_image_description_icon->SetBitmap(m_description_icon_bitmap.bmp());
+ m_image_description_icon->Show(true);
+ } catch (Exception&) {
+ BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Error loading drying icon";
+ m_image_description_icon->Show(false);
+ }
+}
+
+int AMSDryCtrWin::update_image(DevAmsType model, DevAms::DryStatus status, DevAms::DrySubStatus sub_status, int humidity_percent)
+{
+ if (model == m_ams_info.m_model && status == m_ams_info.m_dry_status
+ && sub_status == m_ams_info.m_dry_sub_status && humidity_percent == m_ams_info.m_humidity_percent) {
+ return 0;
+ }
+
+ std::string img_path;
+
+ if (status == DevAms::DryStatus::Off && sub_status == DevAms::DrySubStatus::Off) {
+ img_path = get_humidity_level_img_path(humidity_percent);
+ } else {
+ img_path = get_dry_status_img_path(model, status, sub_status);
+ }
+
+ if (img_path.empty()) {
+ return 0;
+ }
+
+ m_humidity_image = ScalableBitmap(this, img_path, AMS_DRY_STATUS_IMAGE_SIZE);
+ m_humidity_img->SetBitmap(m_humidity_image.bmp());
+
+ update_img_description(status, sub_status);
+
+ return 1;
+}
+
+bool AMSDryCtrWin::check_values_changed(DevAms* dev_ams)
+{
+ bool changed = false;
+
+ if (m_ams_info.m_model != dev_ams->GetAmsType()) {
+ m_ams_info.m_model = dev_ams->GetAmsType();
+ changed = true;
+ }
+
+ if (dev_ams->GetDryStatus().has_value() && m_ams_info.m_dry_status != dev_ams->GetDryStatus()) {
+ m_ams_info.m_dry_status = dev_ams->GetDryStatus().value();
+ changed = true;
+ }
+
+ if (dev_ams->GetDrySubStatus().has_value() && m_ams_info.m_dry_sub_status != dev_ams->GetDrySubStatus()) {
+ m_ams_info.m_dry_sub_status = dev_ams->GetDrySubStatus().value();
+ changed = true;
+ }
+
+ if (m_ams_info.m_humidity_percent != dev_ams->GetHumidityPercent()) {
+ m_ams_info.m_humidity_percent = dev_ams->GetHumidityPercent();
+ changed = true;
+ }
+
+ return changed;
+}
+
+void AMSDryCtrWin::update_normal_description(DevAms* dev_ams)
+{
+ if (m_tray_ids.empty() || m_trays_combo->GetSelection() >= m_tray_ids.size()) { return; }
+
+ FilamentBaseInfo info = m_tray_ids[m_trays_combo->GetSelection()];
+ wxString warning_text;
+ bool can_enable_button = true;
+ long temp_val = 0, time_val = 0;
+ m_temperature_input->GetValue().ToLong(&temp_val);
+ m_time_input->GetValue().ToLong(&time_val);
+ std::optional preset = DevUtilBackend::GetFilamentDryingPreset(info.filament_id);
+ auto total_dry = preset.has_value() ? preset.value().ams_limitations : std::unordered_set();
+
+ struct AmsTempLimit { DevAmsType type; int min_temp; int max_temp; const char* name; };
+ static const AmsTempLimit ams_limits[] = {
+ { DevAmsType::N3F, 45, 65, "AMS2" },
+ { DevAmsType::N3S, 45, 85, "AMS-S" }
+ };
+
+ for (const auto& lim : ams_limits) {
+ if (dev_ams->GetAmsType() == lim.type) {
+ if (temp_val > lim.max_temp) {
+ wxString msg = wxString(lim.name) + _L(" maximum drying temperature is ") + wxString::Format(wxT("%d"), lim.max_temp) + wxString::FromUTF8("°C.");
+ warning_text += msg + "\n";
+ can_enable_button = false;
+ } else if (temp_val < lim.min_temp) {
+ wxString msg = wxString(lim.name) + _L(" minimum drying temperature is ") + wxString::Format(wxT("%d"), lim.min_temp) + wxString::FromUTF8("°C.");
+ warning_text += msg + "\n";
+ can_enable_button = false;
+ }
+ if (total_dry.find(lim.type) == total_dry.end()) {
+ warning_text += _L("This filament may not be completely dried.") + "\n\n";
+ }
+ break;
+ }
+ }
+
+ if (m_printer_status.m_is_printing && temp_val > m_ams_info.m_recommand_dry_temp) {
+ warning_text += _L("This AMS is currently printing. To ensure print quality, the drying temperature cannot exceed the recommended drying temperature.") + "\n";
+ can_enable_button = false;
+ } else if (preset.has_value()) {
+ // Only check heat distortion temperature when AMS has filament inserted;
+ // empty AMS only needs to respect the device hardware temperature limit.
+ bool has_filament_inserted = false;
+ for (const auto& tray_pair : dev_ams->GetTrays()) {
+ if (tray_pair.second && tray_pair.second->is_exists) {
+ has_filament_inserted = true;
+ break;
+ }
+ }
+ if (has_filament_inserted) {
+ auto limit_temperature = preset.value().filament_dev_ams_drying_heat_distortion_temperature;
+ if (temp_val > limit_temperature) {
+ warning_text += _L("The temperature shall not exceed the filament's heat distortion temperature") + "(" +
+ wxString::Format(wxT("%d"), static_cast(limit_temperature)) + wxString::FromUTF8("°C)\n");
+ can_enable_button = false;
+ }
+ }
+ }
+
+ if (time_val < 1) {
+ warning_text += _L("Minimum time value cannot be less than 1.") + "\n";
+ can_enable_button = false;
+ } else if (time_val > 24) {
+ warning_text += _L("Maximum time value cannot be greater than 24.") + "\n";
+ can_enable_button = false;
+ }
+
+ m_next_button->Enable(can_enable_button);
+ m_normal_description->SetLabel(warning_text);
+ m_normal_description->Wrap(FromDIP(250));
+ m_normal_description->GetParent()->Layout();
+}
+
+void AMSDryCtrWin::update_normal_state(DevAms* dev_ams)
+{
+ if (is_dry_ctr_idle(dev_ams)) {
+ m_next_button->Show(true);
+ m_normal_description->Show(true);
+ m_trays_combo->Enable();
+ m_temperature_input->SetEditable(true);
+ m_time_input->SetEditable(true);
+
+ m_stop_button->Hide();
+ m_dry_error_sizer->Show(false);
+ } else if (is_dry_ctr_err(dev_ams)) {
+ m_dry_error_sizer->Show(true);
+ m_next_button->Hide();
+ m_normal_description->Hide();
+
+ m_trays_combo->Hide();
+ m_temperature_input->Hide();
+ m_time_input->Hide();
+ m_stop_button->Show(true);
+ } else {
+ m_next_button->Hide();
+ m_normal_description->Hide();
+
+ m_trays_combo->Disable();
+ m_temperature_input->SetEditable(false);
+ m_time_input->SetEditable(false);
+ m_stop_button->Show(true);
+ m_dry_error_sizer->Show(false);
+ }
+
+ if (m_temperature_input->GetValue().IsEmpty() || m_time_input->GetValue().IsEmpty()) {
+ m_next_button->Disable();
+ m_start_button->Disable();
+ } else {
+ m_next_button->Enable();
+ }
+
+ update_normal_description(dev_ams);
+}
+
+void AMSDryCtrWin::OnFilamentSelectionChanged(wxCommandEvent& event)
+{
+ int selectionIndex = event.GetSelection();
+ wxString selectedFilament = event.GetString();
+
+ auto fila_system = get_fila_system();
+ if (!fila_system) {
+ BOOST_LOG_TRIVIAL(info) << "AMSDryCtrWin::OnFilamentSelectionChanged: Invalid FilaSystem Pointer";
+ return;
+ }
+
+ DevAms* dev_ams = fila_system->GetAmsById(m_ams_info.m_ams_id);
+ if (!dev_ams) {
+ BOOST_LOG_TRIVIAL(info) << "AMSDryCtrWin::OnFilamentSelectionChanged: Invalid AMS id";
+ return;
+ }
+
+ std::optional preset = DevUtilBackend::GetFilamentDryingPreset(m_tray_ids[selectionIndex].filament_id);
+ if (preset.has_value()) {
+ DevFilamentDryingPreset info = preset.value();
+ if (m_printer_status.m_is_printing) {
+ m_temperature_input->SetValue(std::to_string(static_cast(info.filament_dev_ams_drying_temperature_on_print[dev_ams->GetAmsType()])));
+ m_time_input->SetValue(std::to_string(static_cast(info.filament_dev_ams_drying_time_on_print[dev_ams->GetAmsType()])));
+ } else {
+ m_temperature_input->SetValue(std::to_string(static_cast(info.filament_dev_ams_drying_temperature_on_idle[dev_ams->GetAmsType()])));
+ m_time_input->SetValue(std::to_string(static_cast(info.filament_dev_ams_drying_time_on_idle[dev_ams->GetAmsType()])));
+ }
+ }
+
+ update_filament_guide_info(dev_ams);
+}
+
+wxString get_cannot_reason_text(DevAms::CannotDryReason reason)
+{
+ wxString cannot_reason_text;
+ switch (reason)
+ {
+ case DevAms::CannotDryReason::InsufficientPower:
+ cannot_reason_text = "*" + _L("Insufficient power") + "\n";
+ cannot_reason_text += _L(" Too many AMS drying simultaneously. Please plug in the power or stop other drying processes before starting.") + "\n";
+ break;
+ case DevAms::CannotDryReason::AmsBusy:
+ cannot_reason_text = "*" + _L("AMS is busy") + "\n";
+ cannot_reason_text += _L(" AMS is calibrating | reading RFID | loading/unloading material, please wait.") + "\n";
+ break;
+ case DevAms::CannotDryReason::ConsumableAtAmsOutlet:
+ cannot_reason_text = "*" + _L("Filament in AMS outlet") + "\n";
+ cannot_reason_text += _L(" The high drying temperature may cause AMS blockage, please unload first.");
+ break;
+ case DevAms::CannotDryReason::InitiatingAmsDrying:
+ cannot_reason_text = "*" + _L("Initiating AMS drying") + "\n";
+ break;
+ case DevAms::CannotDryReason::NotSupportedIn2dMode:
+ cannot_reason_text = "*" + _L("Not supported in 2D mode") + "\n";
+ break;
+ case DevAms::CannotDryReason::DryingInProgress:
+ cannot_reason_text = "*" + _L("Task in progress") + "\n";
+ cannot_reason_text += _L(" The AMS might be in use during Task.") + "\n";
+ break;
+ case DevAms::CannotDryReason::Upgrading:
+ cannot_reason_text = "*" + _L("Upgrading") + "\n";
+ cannot_reason_text += _L(" Firmware update in progress, please wait...") + "\n";
+ break;
+ case DevAms::CannotDryReason::InsufficientPowerNeedPluginPower:
+ cannot_reason_text = "*" + _L("Insufficient power") + "\n";
+ cannot_reason_text += _L(" Please plug in the power and then use the drying function.") + "\n";
+ break;
+ case DevAms::CannotDryReason::FilamentAtAmsOutletManualUnload:
+ cannot_reason_text = "*" + _L("Filament in AMS outlet") + "\n";
+ cannot_reason_text += _L(" The high drying temperature may cause AMS blockage. Please unload the filament manually before proceeding.") + "\n";
+ break;
+ default:
+ cannot_reason_text = "*" + _L("System is busy") + "\n";
+ cannot_reason_text += _L(" Initiating other drying processes, please wait a few seconds...") + "\n";
+ BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": unknown cannot dry reason";
+ break;
+ }
+ return cannot_reason_text;
+}
+
+wxString organize_cannot_reasons_text(std::vector& reasons)
+{
+ wxString cannot_reasons_text;
+ if (std::find(reasons.begin(), reasons.end(), DevAms::CannotDryReason::DryingInProgress) != reasons.end()) {
+ cannot_reasons_text += get_cannot_reason_text(DevAms::CannotDryReason::DryingInProgress);
+ } else if (std::find(reasons.begin(), reasons.end(), DevAms::CannotDryReason::AmsBusy) != reasons.end()) {
+ cannot_reasons_text += get_cannot_reason_text(DevAms::CannotDryReason::AmsBusy);
+ } else if (std::find(reasons.begin(), reasons.end(), DevAms::CannotDryReason::ConsumableAtAmsOutlet) != reasons.end()) {
+ cannot_reasons_text += get_cannot_reason_text(DevAms::CannotDryReason::ConsumableAtAmsOutlet);
+ } else if (std::find(reasons.begin(), reasons.end(), DevAms::CannotDryReason::FilamentAtAmsOutletManualUnload) != reasons.end()) {
+ cannot_reasons_text += get_cannot_reason_text(DevAms::CannotDryReason::FilamentAtAmsOutletManualUnload);
+ }
+
+ if (std::find(reasons.begin(), reasons.end(), DevAms::CannotDryReason::InsufficientPower) != reasons.end()) {
+ cannot_reasons_text += get_cannot_reason_text(DevAms::CannotDryReason::InsufficientPower);
+ }
+
+ for (auto reason : reasons) {
+ if (reason == DevAms::CannotDryReason::InsufficientPowerNeedPluginPower) {
+ cannot_reasons_text += get_cannot_reason_text(DevAms::CannotDryReason::InsufficientPowerNeedPluginPower);
+ } else if (reason == DevAms::CannotDryReason::NotSupportedIn2dMode) {
+ cannot_reasons_text += get_cannot_reason_text(DevAms::CannotDryReason::NotSupportedIn2dMode);
+ } else if (reason == DevAms::CannotDryReason::InitiatingAmsDrying) {
+ cannot_reasons_text += get_cannot_reason_text(DevAms::CannotDryReason::InitiatingAmsDrying);
+ } else if (reason == DevAms::CannotDryReason::Upgrading) {
+ cannot_reasons_text += get_cannot_reason_text(DevAms::CannotDryReason::Upgrading);
+ }
+ }
+
+ if (cannot_reasons_text.empty()) {
+ cannot_reasons_text += "*" + _L("System is busy") + "\n";
+ cannot_reasons_text += _L(" Initiating other drying processes, please wait a few seconds...") + "\n";
+ }
+
+ return cannot_reasons_text;
+}
+
+int AMSDryCtrWin::update_state(DevAms* dev_ams)
+{
+ std::vector cannot_reasons = dev_ams->GetCannotDryReason().has_value()?
+ dev_ams->GetCannotDryReason().value(): std::vector();
+
+ if (cannot_reasons.size() == 0 ||
+ (cannot_reasons.size() == 1 && cannot_reasons[0] == DevAms::CannotDryReason::DryingInProgress)) {
+ m_normal_state_sizer->Show(true);
+ m_cannot_dry_sizer->Show(false);
+ m_unload_button->Show(false);
+ update_normal_state(dev_ams);
+ } else if (cannot_reasons.size() > 0) {
+ m_normal_state_sizer->Show(false);
+ m_cannot_dry_sizer->Show(true);
+
+ m_cannot_dry_description_label->SetLabel(organize_cannot_reasons_text(cannot_reasons));
+ m_cannot_dry_description_label->Wrap(FromDIP(300));
+
+ // Show unload button when ConsumableAtAmsOutlet reason is present
+ bool has_consumable_at_outlet = std::find(cannot_reasons.begin(), cannot_reasons.end(),
+ DevAms::CannotDryReason::ConsumableAtAmsOutlet) != cannot_reasons.end();
+ m_unload_button->Show(has_consumable_at_outlet);
+ }
+
+ restore_stop_button_if_deadline_passed();
+ restore_unload_button_if_deadline_passed();
+
+ return 1;
+}
+
+int AMSDryCtrWin::update_ams_change(DevAms* dev_ams)
+{
+ if (!is_ams_changed(dev_ams)) {
+ return 0;
+ }
+
+ m_ams_info.m_ams_id = dev_ams->GetAmsId();
+ if (dev_ams->GetAmsType() == DevAmsType::N3F) {
+ m_temperature_input->SetHint("45-65" + wxString::FromUTF8("°C"));
+ } else if (dev_ams->GetAmsType() == DevAmsType::N3S) {
+ m_temperature_input->SetHint("45-85" + wxString::FromUTF8("°C"));
+ }
+
+ m_time_input->SetHint("1-24 h");
+
+ return 0;
+}
+
+int AMSDryCtrWin::update_dryness_status(DevAms* dev_ams)
+{
+ int updated = 0;
+
+ if (m_ams_info.m_humidity_percent != dev_ams->GetHumidityPercent()) {
+ updated += 1;
+ m_ams_info.m_humidity_percent = dev_ams->GetHumidityPercent();
+ m_humidity_data_label->SetLabel(std::to_string(m_ams_info.m_humidity_percent) + "%");
+ }
+
+ if (m_ams_info.m_temperature != dev_ams->GetCurrentTemperature()) {
+ updated += 1;
+ m_ams_info.m_temperature = dev_ams->GetCurrentTemperature();
+ m_temperature_data_label->SetLabel(std::to_string(m_ams_info.m_temperature) + wxString::FromUTF8("°C"));
+ }
+
+ if (is_dry_ctr_idle(dev_ams)) {
+ m_time_descrition_container->Show(false);
+ m_original_page->Layout();
+ } else {
+ if (is_dry_status_changed(dev_ams)) {
+ m_time_descrition_container->Show(true);
+ m_original_page->Layout();
+ }
+
+ if (m_ams_info.m_left_dry_time != dev_ams->GetLeftDryTime()) {
+ updated += 1;
+ m_ams_info.m_left_dry_time = dev_ams->GetLeftDryTime();
+ m_time_data_label->SetLabel(wxString::Format("%02d : %02d",
+ m_ams_info.m_left_dry_time / 60, m_ams_info.m_left_dry_time % 60));
+ }
+ }
+
+ return updated;
+}
+
+bool AMSDryCtrWin::is_tray_changed(DevAms* dev_ams)
+{
+ return false;
+}
+
+void AMSDryCtrWin::update_filament_guide_info(DevAms* dev_ams)
+{
+ if (!IsShown()) {
+ return;
+ }
+
+ m_guide_page->Freeze();
+ m_ams_filament_panel->Clear();
+ m_ams_filament_panel->SetAmsName(dev_ams->GetDisplayName());
+
+ // Get the temperature input value
+ long input_temp = 0;
+ bool valid_temp = !m_temperature_input->GetValue().IsEmpty() &&
+ m_temperature_input->GetValue().ToLong(&input_temp);
+ bool can_start = true;
+
+ int slot_count = 0, empty_count = 0;
+ for (auto& tray_pair : dev_ams->GetTrays()) {
+ if (!tray_pair.second) {
+ continue;
+ }
+ ++slot_count;
+ if (!tray_pair.second->is_exists) {
+ m_ams_filament_panel->AddFilamentItem("/", "dev_ams_dry_ctr_enable");
+ ++empty_count;
+ continue;
+ }
+ auto preset_opt = tray_pair.second->get_ams_drying_preset();
+ wxString filament_type = tray_pair.second->get_display_filament_type();
+ DevFilamentDryingPreset preset;
+ if (filament_type.IsEmpty()) {
+ auto fallback_preset = DevUtilBackend::GetFilamentDryingPreset("GFA00");
+ preset = fallback_preset.value();
+ filament_type = "?";
+ } else if (preset_opt.has_value()) {
+ preset = preset_opt.value();
+ } else {
+ auto fallback_preset = DevUtilBackend::GetFilamentDryingPreset("GFA00");
+ preset = fallback_preset.value();
+ }
+ std::string icon_path = "dev_ams_dry_ctr_enable";
+ int distortion_temp = static_cast(preset.filament_dev_ams_drying_heat_distortion_temperature);
+ if (valid_temp && distortion_temp < input_temp) {
+ icon_path = "dev_ams_dry_ctr_disable";
+ can_start = false;
+ }
+ m_ams_filament_panel->AddFilamentItem(filament_type, icon_path);
+ }
+ if (slot_count == 0) {
+ can_start = false;
+ }
+
+ if (can_start) {
+ m_guide_title_label->SetLabel(_L("For better drying results, remove the filament and allow it to rotate."));
+ m_guide_description_label->SetLabel(_L("The AMS will automatically rotate the stored filament slots to enhance the drying performance.") +
+ "\n" + _L("Alternatively, you can dry the filament without removing it.") +
+ "\n" + "*" + _L("Unknown filaments will be treated as PLA."));
+ m_guide_description_label->Wrap(FromDIP(300));
+ } else {
+ m_guide_title_label->SetLabel(_L("Please store the filament marked with an exclamation mark."));
+ m_guide_description_label->SetLabel(_L("Filament left in the feeder during drying may soften because the drying temperature exceeds the softening point of materials like PLA and TPU.") +
+ "\n" + "*" + _L("Unknown filaments will be treated as PLA."));
+ m_guide_description_label->Wrap(FromDIP(300));
+ }
+
+ m_start_button->Enable(can_start);
+ m_guide_page->Layout();
+ m_guide_page->Thaw();
+}
+
+int AMSDryCtrWin::update_filament_list(DevAms* dev_ams, MachineObject* obj)
+{
+ const bool ams_changed = is_ams_changed(dev_ams);
+ bool rebuilt_filament_list = false;
+
+ // Build `m_tray_ids` and the combobox items from the preset bundle.
+ // This list is also used as a lookup table in non-idle state (type -> name),
+ // so it must be available even when the combobox is disabled.
+ auto rebuild_filament_list = [&]() -> bool {
+ m_trays_combo->Clear();
+ m_tray_ids.clear();
+
+ PresetBundle *preset_bundle = wxGetApp().preset_bundle;
+ if (!preset_bundle || !obj) {
+ return false;
+ }
+
+ std::set filament_id_set;
+ auto & filaments = preset_bundle->filaments;
+
+ // Get nozzle diameter using the same method as AMSMaterialsSetting::Popup
+ std::ostringstream stream;
+ int extruder_id = obj->GetFilaSystem()->GetExtruderIdByAmsId(m_ams_info.m_ams_id);
+ if (!obj->GetExtderSystem()->GetExtderById(extruder_id)) {
+ BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " get extruder id failed";
+ extruder_id = 0;
+ }
+ stream << std::fixed << std::setprecision(1) << obj->GetExtderSystem()->GetNozzleDiameter(extruder_id);
+ std::string nozzle_diameter_str = stream.str();
+ std::set printer_names = preset_bundle->get_printer_names_by_printer_type_and_nozzle(
+ DevPrinterConfigUtil::get_printer_display_name(obj->printer_type), nozzle_diameter_str);
+
+ for (auto filament_it = filaments.begin(); filament_it != filaments.end(); ++filament_it) {
+ Preset& preset = *filament_it;
+ // Filter by system preset: root preset and (system preset or user preset is supported)
+ if (filaments.get_preset_base(*filament_it) != &preset || (!filament_it->is_system && !obj->is_support_user_preset)) {
+ continue;
+ }
+
+ ConfigOption * printer_opt = filament_it->config.option("compatible_printers");
+ ConfigOptionStrings *printer_strs = dynamic_cast(printer_opt);
+ if (!printer_strs) continue;
+
+ for (auto printer_str : printer_strs->values) {
+ if (printer_names.find(printer_str) != printer_names.end()) {
+ if (filament_id_set.find(filament_it->filament_id) != filament_id_set.end()) {
+ continue;
+ }
+
+ filament_id_set.insert(filament_it->filament_id);
+ auto filament_alias = filaments.get_preset_alias(*filament_it, true);
+ if (!filament_alias.empty()) {
+ auto opt_info = preset_bundle->get_filament_by_filament_id(filament_it->filament_id);
+ if (opt_info.has_value()) {
+ auto real_info = opt_info.value();
+ real_info.filament_name = filament_alias;
+ m_tray_ids.push_back(std::move(real_info));
+ m_trays_combo->Append(wxString::FromUTF8(filament_alias));
+ }
+ }
+ }
+ }
+ }
+
+ if (m_tray_ids.empty()) {
+ BOOST_LOG_TRIVIAL(warning) << "AMSDryCtrWin::update_filament_list: No valid filaments found";
+ return false;
+ }
+
+ return true;
+ };
+
+ if (ams_changed || m_tray_ids.empty() || m_trays_combo->GetCount() == 0) {
+ // Disable start button during rebuild
+ if (m_start_button) {
+ m_start_button->Disable();
+ }
+
+ rebuilt_filament_list = rebuild_filament_list();
+ }
+
+ if (!is_dry_ctr_idle(dev_ams)) {
+ const std::string& ams_id = dev_ams->GetAmsId();
+ const auto settings_opt = dev_ams->GetDrySettings();
+
+ // Set the combobox display label by the filament "type" string.
+ // Priority: last saved selection for this AMS (if the type matches) -> fallback to current `m_tray_ids`.
+ auto set_filament_label_by_type = [&](const std::string& filament_type) {
+ if (filament_type.empty()) {
+ return;
+ }
+
+ auto it_name = m_dry_setting.m_filament_names.find(ams_id);
+ auto it_type = m_dry_setting.m_filament_type.find(ams_id);
+ if (it_name != m_dry_setting.m_filament_names.end() && it_type != m_dry_setting.m_filament_type.end()
+ && it_type->second == filament_type) {
+ m_trays_combo->SetLabel(wxString::FromUTF8(it_name->second));
+ return;
+ }
+
+ const auto it = std::find_if(m_tray_ids.begin(), m_tray_ids.end(), [&](const FilamentBaseInfo& tray) {
+ return tray.filament_type == filament_type;
+ });
+ if (it != m_tray_ids.end()) {
+ m_trays_combo->SetLabel(wxString::FromUTF8(it->filament_name));
+ }
+ };
+
+ if (settings_opt.has_value()) {
+ const auto& settings = settings_opt.value();
+ m_temperature_input->SetValue(std::to_string(settings.dry_temp));
+ m_time_input->SetValue(std::to_string(settings.dry_hour));
+ set_filament_label_by_type(settings.dry_filament);
+ } else {
+ auto it_name = m_dry_setting.m_filament_names.find(ams_id);
+ if (it_name != m_dry_setting.m_filament_names.end()) {
+ m_trays_combo->SetLabel(wxString::FromUTF8(it_name->second));
+ m_temperature_input->SetValue(std::to_string(m_dry_setting.m_dry_temp[ams_id]));
+ m_time_input->SetValue(std::to_string(m_dry_setting.m_dry_time[ams_id]));
+ }
+ }
+
+ return 0;
+ }
+
+ // Idle state: avoid re-selecting / firing selection change unless the list was rebuilt.
+ if (!rebuilt_filament_list && !ams_changed && is_dry_ctr_idle()) {
+ return 0;
+ }
+
+ if (m_tray_ids.empty()) {
+ return 0;
+ }
+
+ // Select recommended drying temperature and default filament
+ float min_dry_temp = std::numeric_limits::max();
+ std::string default_filament_id = "GFA00";
+ bool has_ready = false;
+ const auto fallback_preset = DevUtilBackend::GetFilamentDryingPreset("GFA00");
+ for (const auto& tray_pair : dev_ams->GetTrays()) {
+ if (!tray_pair.second || !tray_pair.second->is_tray_info_ready()) continue;
+ has_ready = true;
+ wxString filament_type = tray_pair.second->get_display_filament_type();
+ Slic3r::DevFilamentDryingPreset preset;
+ if (filament_type.IsEmpty()) {
+ // no filament type, is PLA
+ if (!fallback_preset) continue;
+ preset = fallback_preset.value();
+ } else {
+ auto preset_opt = tray_pair.second->get_ams_drying_preset();
+ if (preset_opt.has_value()) {
+ preset = preset_opt.value();
+ } else {
+ // no preset,is PLA
+ if (!fallback_preset) continue;
+ preset = fallback_preset.value();
+ }
+ }
+ float cur_temp = m_printer_status.m_is_printing
+ ? std::min({preset.filament_dev_ams_drying_temperature_on_print[dev_ams->GetAmsType()],
+ preset.filament_dev_drying_softening_temperature, preset.filament_dev_ams_drying_heat_distortion_temperature})
+ : preset.filament_dev_ams_drying_temperature_on_idle[dev_ams->GetAmsType()];
+ if (cur_temp < min_dry_temp) {
+ min_dry_temp = cur_temp;
+ default_filament_id = preset.filament_id;
+ }
+ }
+ // if no tray is ready, use PLA as default
+ if (!has_ready) {
+ if (fallback_preset.has_value()) {
+ Slic3r::DevFilamentDryingPreset preset = fallback_preset.value();
+ float cur_temp = m_printer_status.m_is_printing
+ ? std::min({preset.filament_dev_ams_drying_temperature_on_print[dev_ams->GetAmsType()],
+ preset.filament_dev_drying_softening_temperature, preset.filament_dev_ams_drying_heat_distortion_temperature})
+ : preset.filament_dev_ams_drying_temperature_on_idle[dev_ams->GetAmsType()];
+ min_dry_temp = cur_temp;
+ default_filament_id = preset.filament_id;
+ }
+ }
+ m_ams_info.m_recommand_dry_temp = min_dry_temp;
+
+ // Set default selection
+ unsigned int default_index = 0;
+ for (unsigned int i = 0; i < m_tray_ids.size(); i++) {
+ if (m_tray_ids[i].filament_id == default_filament_id) {
+ default_index = i;
+ break;
+ }
+ }
+ m_trays_combo->SetSelection(default_index);
+ wxCommandEvent evt(wxEVT_COMBOBOX, m_trays_combo->GetId());
+ evt.SetInt(default_index);
+ OnFilamentSelectionChanged(evt);
+ return 1;
+}
+
+std::shared_ptr AMSDryCtrWin::get_fila_system() const
+{
+ std::shared_ptr fila_system = m_fila_system.lock();
+ if (!fila_system) {
+ const_cast(this)->Close();
+ }
+ return fila_system;
+}
+
+void AMSDryCtrWin::update_printer_state(MachineObject* obj)
+{
+ m_printer_status.m_is_printing = obj->is_in_printing()
+ && obj->GetExtderSystem()->GetCurrentAmsId() == m_ams_info.m_ams_id;
+}
+
+void AMSDryCtrWin::update(std::shared_ptr fila_system, MachineObject* obj)
+{
+ if (!fila_system || !obj) {
+ return;
+ }
+
+ update_printer_state(obj);
+ m_fila_system = fila_system;
+
+ DevAms* dev_ams = fila_system->GetAmsById(m_ams_info.m_ams_id);
+ if (!dev_ams) {
+ BOOST_LOG_TRIVIAL(info) << "AMSDryCtrWin::update: Invalid AMS id";
+ m_ams_info.m_ams_id = "";
+ Close();
+ return;
+ }
+
+ if (!dev_ams->IsSupportRemoteDry(fila_system->GetOwner())) {
+ BOOST_LOG_TRIVIAL(info) << "AMSDryCtrWin::update: Selected AMS does not support remote drying";
+ Close();
+ return;
+ }
+
+
+ update_ams_change(dev_ams);
+
+ update_image(dev_ams->GetAmsType(),
+ dev_ams->GetDryStatus().has_value()? dev_ams->GetDryStatus().value(): DevAms::DryStatus::Off,
+ dev_ams->GetDrySubStatus().has_value()? dev_ams->GetDrySubStatus().value(): DevAms::DrySubStatus::Off,
+ dev_ams->GetHumidityPercent());
+
+ update_state(dev_ams);
+ update_dryness_status(dev_ams);
+
+ update_filament_list(dev_ams, obj);
+ update_filament_guide_info(dev_ams);
+
+ m_is_ams_changed = false;
+
+ check_values_changed(dev_ams);
+
+ Layout();
+ Refresh();
+}
+
+bool AMSDryCtrWin::is_ams_changed(DevAms* dev_ams)
+{
+ return m_ams_info.m_ams_id != dev_ams->GetAmsId() || m_is_ams_changed;
+}
+
+bool AMSDryCtrWin::is_dry_status_changed(DevAms* dev_ams)
+{
+ if (!dev_ams->GetDryStatus().has_value() || !dev_ams->GetDrySubStatus().has_value()) {
+ return true;
+ }
+
+ return dev_ams->GetDryStatus().value() != m_ams_info.m_dry_status
+ || dev_ams->GetDrySubStatus().value() != m_ams_info.m_dry_sub_status;
+}
+
+bool AMSDryCtrWin::is_dry_ctr_idle(DevAms* dev_ams)
+{
+ if (!dev_ams->GetDryStatus().has_value() || !dev_ams->GetDrySubStatus().has_value()) {
+ return true;
+ }
+
+ return dev_ams->GetDryStatus().value() == DevAms::DryStatus::Off
+ || dev_ams->GetDryStatus().value() == DevAms::DryStatus::Cooling;
+}
+
+bool AMSDryCtrWin::is_dry_ctr_idle()
+{
+ return m_ams_info.m_dry_status == DevAms::DryStatus::Off || m_ams_info.m_dry_status == DevAms::DryStatus::Cooling;
+}
+
+bool AMSDryCtrWin::is_dry_ctr_err(DevAms* dev_ams)
+{
+ if (!dev_ams->GetDryStatus().has_value() || !dev_ams->GetDrySubStatus().has_value()) {
+ return false;
+ }
+
+ return dev_ams->GetDryStatus().value() == DevAms::DryStatus::Error;
+}
+
+} // GUI
+} // Slic3r
diff --git a/src/slic3r/GUI/AMSDryControl.hpp b/src/slic3r/GUI/AMSDryControl.hpp
new file mode 100644
index 0000000000..43e177eeb1
--- /dev/null
+++ b/src/slic3r/GUI/AMSDryControl.hpp
@@ -0,0 +1,253 @@
+#pragma once
+#include "GUI_ObjectLayers.hpp"
+#include "slic3r/GUI/Widgets/AMSItem.hpp"
+#include "slic3r/GUI/Widgets/Label.hpp"
+#include "slic3r/GUI/Widgets/PopupWindow.hpp"
+
+#include "slic3r/GUI/wxExtensions.hpp"
+#include "slic3r/GUI/DeviceCore/DevFilaSystem.h"
+#include "slic3r/GUI/I18N.hpp"
+
+#include
+#include
+
+
+//Previous defintions
+class wxGrid;
+
+namespace Slic3r {
+
+namespace GUI {
+
+
+enum class DryCtrState {
+ IDLE,
+ DRY_WHEN_PRINT,
+ UNKNOWN
+};
+
+enum class DryCtrDev {
+ N3S,
+ N3F,
+ UNKNOWN
+};
+
+struct DryingPreset {
+ DryCtrState state;
+ DryCtrDev dev;
+ int dry_temp;
+ int dry_time;
+};
+
+class FilamentItemPanel : public wxPanel
+{
+public:
+ FilamentItemPanel(wxWindow* parent, const wxString& text, const std::string& icon_name = "",
+ wxWindowID id = wxID_ANY);
+
+ void SetText(const wxString& text);
+ void SetIcon(const std::string& icon_name);
+ void msw_rescale();
+
+private:
+ void OnPaint(wxPaintEvent& event);
+ void OnSize(wxSizeEvent& event);
+
+ Label* m_text_label;
+ wxStaticBitmap* m_icon_bitmap;
+ int m_target_size;
+ std::string m_icon_name;
+ ScalableBitmap m_icon;
+};
+
+class AMSFilamentPanel : public wxPanel
+{
+ wxBoxSizer* m_filament_sizer;
+ Label* m_ams_name_label;
+ int m_border_radius;
+ wxPanel* m_filament_container{nullptr};
+ std::vector m_filament_items;
+
+public:
+ AMSFilamentPanel(wxWindow* parent, const wxString& ams_name, wxWindowID id = wxID_ANY);
+
+ void AddFilamentItem(const wxString& text, const std::string& icon_name);
+ void AddFilamentItem(FilamentItemPanel* panel);
+ void SetAmsName(const wxString& ams_name);
+ void Clear();
+ void msw_rescale();
+private:
+ void OnPaint(wxPaintEvent& event);
+};
+
+
+class AMSDryCtrWin : public DPIDialog
+{
+public:
+ AMSDryCtrWin(wxWindow *parent);
+ ~AMSDryCtrWin();
+
+ void msw_rescale();
+ void update(std::shared_ptr fila_system, MachineObject* obj);
+ void set_ams_id(const std::string& ams_id);
+
+protected:
+ void on_dpi_changed(const wxRect &suggested_rect) override;
+
+private:
+ wxSimplebook* m_main_simplebook{nullptr};
+ wxPanel* m_original_page{nullptr};
+
+ wxWindow* m_amswin{nullptr};
+ wxBoxSizer* m_sizer_ams_items{nullptr};
+ wxScrolledWindow* m_panel_prv_left {nullptr};
+ wxScrolledWindow* m_panel_prv_right{nullptr};
+ wxBoxSizer* m_sizer_prv_left{nullptr};
+ wxBoxSizer* m_sizer_prv_right{nullptr};
+
+ // left panel related members
+ ScalableBitmap m_humidity_image;
+ wxStaticBitmap* m_humidity_img{nullptr};
+ Label* m_image_description{nullptr};
+ wxStaticBitmap* m_image_description_icon{nullptr};
+ ScalableBitmap m_description_icon_bitmap;
+
+ Label* m_humidity_data_label = nullptr;
+ Label* m_temperature_data_label = nullptr;
+ Label* m_time_data_label = nullptr;
+ wxBoxSizer* m_time_descrition_container = nullptr;
+
+ // right panel related members
+ wxBoxSizer* m_normal_state_sizer{nullptr};
+ wxBoxSizer* m_cannot_dry_sizer{nullptr};
+ wxBoxSizer* m_dry_error_sizer{nullptr};
+ Label* m_cannot_dry_description_label = nullptr;
+
+ // right panel normal state
+ ComboBox* m_trays_combo;
+ std::vector m_tray_ids;
+ wxTextCtrl* m_temperature_input;
+ wxTextCtrl* m_time_input;
+ Label* m_normal_description;
+ Button* m_start_button{nullptr};
+ Button* m_next_button{nullptr};
+ Button* m_stop_button{nullptr};
+ Button* m_back_button{nullptr};
+ Button* m_unload_button{nullptr};
+
+ // guide page description
+ Label* m_guide_title_label{nullptr};
+ Label* m_guide_description_label{nullptr};
+
+ wxCheckBox* m_rotate_spool_toggle{nullptr};
+
+ wxPanel* m_progress_page;
+ ProgressBar* m_progress_gauge;
+ wxTimer* m_progress_timer;
+
+ std::optional m_stop_button_restore_deadline;
+ std::optional m_unload_button_restore_deadline;
+
+ Label* m_progress_title;
+ int m_progress_value;
+ int m_progress_message_index;
+ std::vector m_progress_text = {
+ _L("Starting: Checking adapter connection"),
+ _L("Starting: Checking filament status"),
+ _L("Starting: Checking drying presets"),
+ _L("Starting: Checking filament location"),
+ _L("Starting: Checking air intake"),
+ _L("Starting: Checking air vent")
+ };
+
+ // Guide page
+ wxPanel* m_guide_page{nullptr};
+ AMSFilamentPanel* m_ams_filament_panel{nullptr};
+ std::map m_filament_items;
+ wxStaticBitmap* m_image_placeholder{nullptr};
+ ScalableBitmap m_guide_image;
+
+
+ bool m_is_ams_changed = false;
+ std::weak_ptr m_fila_system;
+ struct {
+ std::string m_ams_id;
+ DevAmsType m_model = DevAmsType::EXT_SPOOL;
+ DevAms::DryStatus m_dry_status = DevAms::DryStatus::Off;
+ DevAms::DrySubStatus m_dry_sub_status = DevAms::DrySubStatus::Off;
+ int m_humidity_percent;
+ int m_temperature;
+ int m_left_dry_time;
+ float m_recommand_dry_temp;
+ } m_ams_info;
+
+ struct {
+ std::unordered_map m_filament_names;
+ std::unordered_map m_filament_type;
+ std::unordered_map m_dry_temp;
+ std::unordered_map m_dry_time;
+ } m_dry_setting;
+
+ struct {
+ bool m_is_printing = false;
+ } m_printer_status;
+
+private:
+ void create();
+ wxBoxSizer* create_guide_page_sizer(wxPanel* parent);
+ wxBoxSizer* create_main_content_section(wxPanel* parent);
+ wxBoxSizer* create_guide_info_filament(wxPanel* parent);
+ wxBoxSizer* create_guide_info_section(wxPanel* parent);
+ wxBoxSizer* create_guide_right_section(wxPanel* parent);
+ wxBoxSizer* create_main_page_sizer(wxPanel* parent);
+ wxBoxSizer* create_left_panel(wxPanel* parent);
+ wxBoxSizer* create_humidity_status_section(wxPanel* parent);
+ wxBoxSizer* create_description_item(wxPanel* parent, const wxString& title, Label*& dataLabel);
+ wxBoxSizer* create_status_descriptions_section(wxPanel* parent);
+
+ wxBoxSizer* create_right_panel(wxPanel* parent);
+ wxBoxSizer* create_normal_state_panel(wxPanel* parent);
+ wxBoxSizer* create_cannot_dry_panel(wxPanel* parent);
+ wxBoxSizer* create_drying_error_panel(wxPanel* parent);
+ Button* create_button(wxPanel* parent, const wxString& title,
+ const wxColour& background_color, const wxColour& border_color, const wxColour& text_color);
+
+ wxBoxSizer* create_progress_page_sizer(wxPanel* parent);
+ void OnProgressTimer(wxTimerEvent& event);
+ void OnClose(wxCloseEvent& event);
+ void OnShow(wxShowEvent& event);
+
+ void OnFilamentSelectionChanged(wxCommandEvent& event);
+
+
+ wxScrolledWindow* create_preview_scrolled_window(wxWindow* parent);
+
+ bool check_values_changed(DevAms* dev_ams);
+ int update_image(DevAmsType type, DevAms::DryStatus status, DevAms::DrySubStatus sub_status, int humidity_percent);
+ void update_img_description(DevAms::DryStatus status, DevAms::DrySubStatus sub_status);
+ void update_normal_description(DevAms* dev_ams);
+ int update_state(DevAms* dev_ams);
+ int update_dryness_status(DevAms* dev_ams);
+ int update_ams_change(DevAms* dev_ams);
+ int update_filament_list(DevAms* dev_ams, MachineObject* obj);
+ void update_filament_guide_info(DevAms* dev_ams);
+ void update_normal_state(DevAms* dev_ams);
+ void update_printer_state(MachineObject* obj);
+
+ std::shared_ptr get_fila_system() const;
+ void start_sending_drying_command();
+ void restore_stop_button_if_deadline_passed();
+ void restore_unload_button_if_deadline_passed();
+ void update_button_size(Button* button);
+
+ bool is_dry_status_changed(DevAms* dev_ams);
+ bool is_dry_ctr_idle(DevAms* dev_ams);
+ bool is_ams_changed(DevAms* dev_ams);
+ bool is_dry_ctr_idle();
+ bool is_tray_changed(DevAms* dev_ams);
+ bool is_dry_ctr_err(DevAms* dev_ams);
+
+};
+
+} // GUI
+} // Slic3r
diff --git a/src/slic3r/GUI/DeviceCore/DevDefs.h b/src/slic3r/GUI/DeviceCore/DevDefs.h
index 4e55f7cbae..640f984024 100644
--- a/src/slic3r/GUI/DeviceCore/DevDefs.h
+++ b/src/slic3r/GUI/DeviceCore/DevDefs.h
@@ -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.
diff --git a/src/slic3r/GUI/DeviceCore/DevFilaSystem.cpp b/src/slic3r/GUI/DeviceCore/DevFilaSystem.cpp
index fccc31c47b..038032d0ca 100644
--- a/src/slic3r/GUI/DeviceCore/DevFilaSystem.cpp
+++ b/src/slic3r/GUI/DeviceCore/DevFilaSystem.cpp
@@ -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 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();
+ std::string temp = (*it)["temp"].get();
+ 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();
- try
- {
- curr_ams->m_current_temperature = DevUtil::string_to_float(temp);
+ curr_ams->m_left_dry_time = (*it)["dry_time"].get();
+ }
+
+ // 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();
+ 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>((*it), "dry_sf_reason");
}
}
diff --git a/src/slic3r/GUI/DeviceCore/DevFilaSystem.h b/src/slic3r/GUI/DeviceCore/DevFilaSystem.h
index 604f228c13..27c59183c1 100644
--- a/src/slic3r/GUI/DeviceCore/DevFilaSystem.h
+++ b/src/slic3r/GUI/DeviceCore/DevFilaSystem.h
@@ -12,6 +12,9 @@
#include
#include
#include
+#include
+#include
+#include
#include
#include
@@ -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 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 GetDryStatus() const { return m_dry_status; };
+ std::optional GetDrySubStatus() const { return m_dry_sub_status; }
+ std::optional GetFan1Status() const { return m_dry_fan1_status; }
+ std::optional GetFan2Status() const { return m_dry_fan2_status; }
+ std::optional> GetCannotDryReason() const { return m_dry_cannot_reasons; }
+ std::optional 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 m_dry_status;
+ std::optional m_dry_sub_status;
+ std::optional m_dry_fan1_status;
+ std::optional m_dry_fan2_status;
+ std::optional> m_dry_cannot_reasons;
+ std::optional 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);
@@ -337,4 +417,18 @@ public:
static void ParseV1_0(const json& print_json, MachineObject* obj, DevFilaSystem* system, bool key_field_only);
};
+struct DevFilamentDryingPreset
+{
+ std::string filament_id;
+
+ std::unordered_set ams_limitations; // only use ams types in the set
+ std::unordered_map filament_dev_ams_drying_time_on_idle; // hour
+ std::unordered_map filament_dev_ams_drying_temperature_on_idle;
+ std::unordered_map filament_dev_ams_drying_time_on_print; // hour
+ std::unordered_map 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
\ No newline at end of file
diff --git a/src/slic3r/GUI/DeviceCore/DevFilaSystemCtrl.cpp b/src/slic3r/GUI/DeviceCore/DevFilaSystemCtrl.cpp
index f81d6a2835..acdc81dd8c 100644
--- a/src/slic3r/GUI/DeviceCore/DevFilaSystemCtrl.cpp
+++ b/src/slic3r/GUI/DeviceCore/DevFilaSystemCtrl.cpp
@@ -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(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(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);
+}
+
}
\ No newline at end of file
diff --git a/src/slic3r/GUI/DeviceCore/DevMapping.cpp b/src/slic3r/GUI/DeviceCore/DevMapping.cpp
index cccc62d606..5dc16f4250 100644
--- a/src/slic3r/GUI/DeviceCore/DevMapping.cpp
+++ b/src/slic3r/GUI/DeviceCore/DevMapping.cpp
@@ -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));
}
}
diff --git a/src/slic3r/GUI/DeviceCore/DevUtilBackend.cpp b/src/slic3r/GUI/DeviceCore/DevUtilBackend.cpp
index 4d909bf68f..32d30379f6 100644
--- a/src/slic3r/GUI/DeviceCore/DevUtilBackend.cpp
+++ b/src/slic3r/GUI/DeviceCore/DevUtilBackend.cpp
@@ -1,7 +1,11 @@
#include "DevUtilBackend.h"
#include "slic3r/GUI/BackgroundSlicingProcess.hpp"
+
#include "slic3r/GUI/Plater.hpp"
+#include "slic3r/GUI/GUI_App.hpp"
+
+#include
namespace Slic3r
{
@@ -15,4 +19,68 @@ std::shared_ptr DevUtilBackend::GetNozz
return nullptr;
}
+static std::unordered_map s_ams_type_map = {
+ {"0", DevAmsType::N3F},
+ {"1", DevAmsType::N3S},
+};
+
+std::optional 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 types = config.option("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("filament_dev_ams_drying_temperature")->get_at(0);
+ info.filament_dev_ams_drying_temperature_on_idle[DevAmsType::N3S] = config.option("filament_dev_ams_drying_temperature")->get_at(1);
+ info.filament_dev_ams_drying_temperature_on_print[DevAmsType::N3F] = config.option("filament_dev_ams_drying_temperature")->get_at(2);
+ info.filament_dev_ams_drying_temperature_on_print[DevAmsType::N3S] = config.option("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("filament_dev_ams_drying_time")->get_at(0);
+ info.filament_dev_ams_drying_time_on_idle[DevAmsType::N3S] = config.option("filament_dev_ams_drying_time")->get_at(1);
+ info.filament_dev_ams_drying_time_on_print[DevAmsType::N3F] = config.option("filament_dev_ams_drying_time")->get_at(2);
+ info.filament_dev_ams_drying_time_on_print[DevAmsType::N3S] = config.option("filament_dev_ams_drying_time")->get_at(3);
+ }
+
+ if (config.has("filament_dev_drying_softening_temperature")) {
+ info.filament_dev_drying_softening_temperature = config.option("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("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("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
diff --git a/src/slic3r/GUI/DeviceCore/DevUtilBackend.h b/src/slic3r/GUI/DeviceCore/DevUtilBackend.h
index 54839d9315..5c7869f1e4 100644
--- a/src/slic3r/GUI/DeviceCore/DevUtilBackend.h
+++ b/src/slic3r/GUI/DeviceCore/DevUtilBackend.h
@@ -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
+#include
+#include
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 GetNozzleGroupResult(Slic3r::GUI::Plater* plater);
+
+ // for filament preset
+ static std::optional GetFilamentDryingPreset(const std::string& fila_id);
};
}; // namespace Slic3r
diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp
index 527fe4f372..b23528a372 100644
--- a/src/slic3r/GUI/DeviceManager.cpp
+++ b/src/slic3r/GUI/DeviceManager.cpp
@@ -568,7 +568,7 @@ MachineObject::MachineObject(DeviceManager* manager, NetworkAgent* agent, std::s
m_extder_system = new DevExtderSystem(this);
m_extension_tool = DevExtensionTool::Create(this);
m_nozzle_system = new DevNozzleSystem(this);
- m_fila_system = new DevFilaSystem(this);
+ m_fila_system = std::make_shared(this);
m_fila_switch = new DevFilaSwitch(this);
m_hms_system = new DevHMS(this);
m_config = new DevConfig(this);
@@ -617,8 +617,6 @@ MachineObject::~MachineObject()
delete m_ctrl;
m_ctrl = nullptr;
- delete m_fila_system;
- m_fila_system = nullptr;
delete m_fila_switch;
m_fila_switch = nullptr;
@@ -3770,7 +3768,7 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
update_printer_preset_name();
update_filament_list();
if (jj.contains("ams")) {
- DevFilaSystemParser::ParseV1_0(jj, this, m_fila_system, key_field_only);
+ DevFilaSystemParser::ParseV1_0(jj, this, m_fila_system.get(), key_field_only);
}
/* vitrual tray*/
@@ -5095,6 +5093,7 @@ void MachineObject::parse_new_info(json print)
// fun2 may have infinite length, use get_flag_bits_no_border
if (!fun2.empty()) {
is_support_print_with_emmc = get_flag_bits_no_border(fun2, 0) == 1;
+ is_support_remote_dry = (get_flag_bits_no_border(fun2, 5) == 1);
is_support_check_track_switch_match_slice_printer = get_flag_bits_no_border(fun2, 19) == 1;
}
diff --git a/src/slic3r/GUI/DeviceManager.hpp b/src/slic3r/GUI/DeviceManager.hpp
index 773bdc200b..4ce4cad8de 100644
--- a/src/slic3r/GUI/DeviceManager.hpp
+++ b/src/slic3r/GUI/DeviceManager.hpp
@@ -117,7 +117,7 @@ private:
std::shared_ptr m_extension_tool;
DevExtderSystem* m_extder_system;
DevNozzleSystem* m_nozzle_system;
- DevFilaSystem* m_fila_system;
+ std::shared_ptr m_fila_system;
DevFilaSwitch* m_fila_switch;
DevFan* m_fan;
DevBed * m_bed;
@@ -342,7 +342,7 @@ public:
std::shared_ptr get_nozzle_mapping_result() const { return m_nozzle_mapping_ptr; }
void clear_auto_nozzle_mapping();// defined in DevMappingNozzle.cpp
- DevFilaSystem* GetFilaSystem() const { return m_fila_system;}
+ std::shared_ptr GetFilaSystem() const { return m_fila_system;}
DevFilaSwitch* GetFilaSwitch() const { return m_fila_switch;}
bool HasAms() const;
@@ -635,6 +635,7 @@ public:
// fun2
bool is_support_print_with_emmc{false};
+ bool is_support_remote_dry = false;
bool is_support_check_track_switch_match_slice_printer{false};
bool installed_upgrade_kit{false};
diff --git a/src/slic3r/GUI/SelectMachine.cpp b/src/slic3r/GUI/SelectMachine.cpp
index d8bab12a03..f7d7b6b6fe 100644
--- a/src/slic3r/GUI/SelectMachine.cpp
+++ b/src/slic3r/GUI/SelectMachine.cpp
@@ -521,6 +521,14 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater)
//m_change_filament_times_sizer->Add(m_img_change_filament_times, 0, wxTOP, FromDIP(2));
m_change_filament_times_sizer->Add(m_txt_change_filament_times, 0, wxTOP, 0);
+ m_warn_when_drying_sizer = new wxBoxSizer(wxHORIZONTAL);
+ m_txt_warn_when_drying = new Label(m_scroll_area, wxEmptyString);
+ m_txt_warn_when_drying->SetFont(::Label::Body_13);
+ m_txt_warn_when_drying->SetForegroundColour(wxColour("#F09A17"));
+ m_txt_warn_when_drying->SetBackgroundColour(*wxWHITE);
+ m_txt_warn_when_drying->SetLabel(_L("To ensure print quality, the drying temperature will be lowered during printing."));
+ m_warn_when_drying_sizer->Add(m_txt_warn_when_drying, 0, wxTOP, FromDIP(2));
+
/*Advanced Options*/
wxBoxSizer* sizer_split_options = new wxBoxSizer(wxHORIZONTAL);
auto m_split_options_line = new wxPanel(m_scroll_area, wxID_ANY);
@@ -735,6 +743,8 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater)
m_scroll_sizer->Add(m_change_filament_times_sizer, 0,wxLEFT|wxRIGHT, FromDIP(15));
// m_scroll_sizer->Add(m_link_edit_nozzle, 0, wxLEFT|wxRIGHT, FromDIP(15));
m_scroll_sizer->Add(suggestion_sizer, 0, wxLEFT|wxRIGHT|wxEXPAND, FromDIP(15));
+ m_scroll_sizer->Add(0, 0, 0, wxTOP, FromDIP(10));
+ m_scroll_sizer->Add(m_warn_when_drying_sizer, 0, wxLEFT|wxRIGHT, FromDIP(15));
m_scroll_sizer->Add(sizer_split_options, 1, wxEXPAND|wxLEFT|wxRIGHT, FromDIP(15));
m_scroll_sizer->Add(0, 0, 0, wxTOP, FromDIP(10));
m_scroll_sizer->Add(m_options_other, 0, wxEXPAND|wxLEFT|wxRIGHT, FromDIP(15));
@@ -1734,6 +1744,47 @@ bool SelectMachineDialog::CheckErrorSyncNozzleMappingResultV0(MachineObject* obj
return true;
}
+bool SelectMachineDialog::is_ams_drying(MachineObject* obj)
+{
+ const auto& ams_list = obj->GetFilaSystem()->GetAmsList();
+ for (auto ams = ams_list.begin(); ams != ams_list.end(); ams++) {
+ if (ams->second->AmsIsDrying()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool SelectMachineDialog::is_selected_ams_drying(MachineObject* obj)
+{
+ if (!obj) return false;
+
+ // If a UI material is selected, only when that material is mapped to an AMS
+ // and that AMS is currently drying.
+ for (const auto &kv : m_materialList) {
+ Material *mat = kv.second;
+ if (!mat || !mat->item) continue;
+ if (!mat->item->m_selected) continue;
+
+ // find mapping entry for this material id
+ for (const FilamentInfo &f : m_ams_mapping_result) {
+ if (f.id != mat->id) continue;
+
+ if (f.ams_id.empty()) return false;
+
+ auto fila_system = obj->GetFilaSystem();
+ if (!fila_system) return false;
+ DevAms* dev_ams = fila_system->GetAmsById(f.ams_id);
+ return (dev_ams && dev_ams->AmsIsDrying());
+ }
+
+ return false;
+ }
+
+ return false;
+}
+
void SelectMachineDialog::prepare(int print_plate_idx)
{
m_print_plate_idx = print_plate_idx;
@@ -3888,6 +3939,16 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_)
m_check_ext_change_assist->Enable(false);
}
+ {
+ const bool show_warn_when_drying = is_selected_ams_drying(obj_);
+ const bool is_currently_shown = (m_txt_warn_when_drying != nullptr) ? m_txt_warn_when_drying->IsShown() : false;
+ if (is_currently_shown != show_warn_when_drying) {
+ m_warn_when_drying_sizer->Show(show_warn_when_drying);
+ Layout();
+ Fit();
+ }
+ }
+
/*reading done*/
if (wxGetApp().app_config) {
if (obj_->upgrade_force_upgrade) {
@@ -4404,6 +4465,7 @@ void SelectMachineDialog::set_default()
m_mapping_sugs_sizer->Show(false);
m_change_filament_times_sizer->Show(false);
m_txt_change_filament_times->Show(false);
+ m_warn_when_drying_sizer->Show(false);
// rset status bar
m_status_bar->reset();
@@ -4513,6 +4575,10 @@ void SelectMachineDialog::reset_and_sync_ams_list()
m_filaments_map = wxGetApp().plater()->get_partplate_list().get_curr_plate()->get_real_filament_maps(project_config);
}
+ bool selected_any = false;
+ MaterialItem *first_enabled = nullptr;
+ int first_enabled_id = -1;
+
for (auto i = 0; i < extruders.size(); i++) {
auto extruder = extruders[i] - 1;
auto colour = wxGetApp().preset_bundle->project_config.opt_string("filament_colour", (unsigned int) extruder);
@@ -4542,7 +4608,20 @@ void SelectMachineDialog::reset_and_sync_ams_list()
item = new MaterialItem(m_filament_panel, colour_rgb, _L(display_materials[extruder]));
m_sizer_ams_mapping->Add(item, 0, wxALL, FromDIP(5));
}
+
+ if (!item) continue;
+
item->SetToolTip(m_ams_tooltip);
+
+ if (!selected_any && extruder == m_current_filament_id && item->m_enable) {
+ item->on_selected();
+ selected_any = true;
+ }
+ if (!first_enabled && item->m_enable) {
+ first_enabled = item;
+ first_enabled_id = extruder;
+ }
+
item->Bind(wxEVT_LEFT_UP, [this, item, materials, extruder](wxMouseEvent &e) {});
item->Bind(wxEVT_LEFT_DOWN, [this, item, materials, extruder](wxMouseEvent &e) {
if (!item->m_enable) {return;}
@@ -4621,6 +4700,11 @@ void SelectMachineDialog::reset_and_sync_ams_list()
}
}
+ if (!selected_any && first_enabled) {
+ m_current_filament_id = first_enabled_id;
+ first_enabled->on_selected();
+ }
+
if (use_double_extruder)
{
m_filament_left_panel->Show();
@@ -5031,6 +5115,10 @@ void SelectMachineDialog::set_default_from_sdcard()
m_materialList.clear();
m_filaments.clear();
+ bool selected_any = false;
+ MaterialItem *first_enabled = nullptr;
+ int first_enabled_id = -1;
+
for (auto i = 0; i < m_required_data_plate_data_list[m_print_plate_idx]->slice_filaments_info.size(); i++) {
FilamentInfo fo = m_required_data_plate_data_list[m_print_plate_idx]->slice_filaments_info[i];
@@ -5053,6 +5141,17 @@ void SelectMachineDialog::set_default_from_sdcard()
m_sizer_ams_mapping->Add(item, 0, wxALL, FromDIP(5));
}
+ if (!item) continue;
+
+ if (!selected_any && fo.id == m_current_filament_id && item->m_enable) {
+ item->on_selected();
+ selected_any = true;
+ }
+ if (!first_enabled && item->m_enable) {
+ first_enabled = item;
+ first_enabled_id = fo.id;
+ }
+
item->Bind(wxEVT_LEFT_UP, [this, item, materials](wxMouseEvent& e) {});
item->Bind(wxEVT_LEFT_DOWN, [this, obj_, item, materials, diameters_count, fo](wxMouseEvent& e) {
if (!item->m_enable) {return;}
@@ -5129,6 +5228,11 @@ void SelectMachineDialog::set_default_from_sdcard()
wxSize screenSize = wxGetDisplaySize();
auto dialogSize = this->GetSize();
+ if (!selected_any && first_enabled) {
+ m_current_filament_id = first_enabled_id;
+ first_enabled->on_selected();
+ }
+
reset_ams_material();
// basic info
diff --git a/src/slic3r/GUI/SelectMachine.hpp b/src/slic3r/GUI/SelectMachine.hpp
index 5d51f17f4b..a62e8de3d2 100644
--- a/src/slic3r/GUI/SelectMachine.hpp
+++ b/src/slic3r/GUI/SelectMachine.hpp
@@ -28,6 +28,7 @@
#include "boost/bimap/bimap.hpp"
#include "AmsMappingPopup.hpp"
+#include "GUI_ObjectLayers.hpp"
#include "ReleaseNote.hpp"
#include "GUI_Utils.hpp"
#include "wxExtensions.hpp"
@@ -354,6 +355,7 @@ protected:
wxBoxSizer* m_sizer_autorefill{ nullptr };
wxBoxSizer* m_mapping_sugs_sizer{ nullptr };
wxBoxSizer* m_change_filament_times_sizer{ nullptr };
+ wxBoxSizer* m_warn_when_drying_sizer{ nullptr };
Button* m_button_ensure{ nullptr };
wxStaticBitmap * m_rename_button{nullptr};
wxStaticBitmap* m_staticbitmap{ nullptr };
@@ -387,6 +389,8 @@ protected:
CheckBox* m_check_ext_change_assist{ nullptr };
Label* m_label_ext_change_assist{ nullptr };
+ Label* m_txt_warn_when_drying{ nullptr };
+
PrinterInfoBox* m_printer_box { nullptr};
PrinterMsgPanel * m_text_printer_msg{nullptr};
Label* m_text_printer_msg_tips{ nullptr };
@@ -542,6 +546,9 @@ public:
bool CheckErrorDynamicSwitchNozzle(MachineObject* obj_);
void on_flow_cali_option_changed();
+ bool is_ams_drying(MachineObject* obj);
+ bool is_selected_ams_drying(MachineObject* obj);
+
PrintFromType get_print_type() {return m_print_type;};
wxString format_steel_name(NozzleType type);
PrintDialogStatus get_status() { return m_print_status; }
diff --git a/src/slic3r/GUI/StatusPanel.cpp b/src/slic3r/GUI/StatusPanel.cpp
index 2be47207d6..1bd564b5e1 100644
--- a/src/slic3r/GUI/StatusPanel.cpp
+++ b/src/slic3r/GUI/StatusPanel.cpp
@@ -3392,6 +3392,7 @@ void StatusPanel::update_ams(MachineObject *obj)
// must select a current can
m_ams_control->UpdateAms(obj->get_printer_series_str(), obj->printer_type, ams_info, ext_info, *obj->GetExtderSystem(), obj->get_dev_id(), false);
+ m_ams_control->UpdateAmsDryControl(obj);
last_tray_exist_bits = obj->tray_exist_bits;
last_ams_exist_bits = obj->ams_exist_bits;
diff --git a/src/slic3r/GUI/Widgets/AMSControl.cpp b/src/slic3r/GUI/Widgets/AMSControl.cpp
index ea693fbd4c..7334cb960a 100644
--- a/src/slic3r/GUI/Widgets/AMSControl.cpp
+++ b/src/slic3r/GUI/Widgets/AMSControl.cpp
@@ -31,6 +31,7 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons
, m_Humidity_tip_popup(AmsHumidityTipPopup(this))
, m_percent_humidity_dry_popup(new uiAmsPercentHumidityDryPopup(this))
, m_ams_introduce_popup(AmsIntroducePopup(this))
+ , m_ams_dry_ctr_win(new AMSDryCtrWin(this))
{
Slic3r::DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager();
if (dev) {
@@ -252,6 +253,12 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons
uiAmsHumidityInfo *info = (uiAmsHumidityInfo *) evt.GetClientData();
if (info)
{
+ Slic3r::DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager();
+ MachineObject *obj = nullptr;
+ if (dev) {
+ obj = dev->get_selected_machine();
+ }
+
if (info->ams_type == AMSModel::GENERIC_AMS)
{
wxPoint img_pos = ClientToScreen(wxPoint(0, 0));
@@ -261,9 +268,14 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons
int humidity_value = info->humidity_display_idx;
if (humidity_value > 0 && humidity_value <= 5) { m_Humidity_tip_popup.set_humidity_level(humidity_value); }
m_Humidity_tip_popup.Popup();
- }
- else
- {
+ } else if (obj && obj->is_support_remote_dry && (info->ams_type == AMSModel::N3F_AMS || info->ams_type == AMSModel::N3S_AMS)){
+ m_ams_dry_ctr_win->set_ams_id(info->ams_id);
+
+ wxPoint img_pos = ClientToScreen(wxPoint(0, 0));
+ wxPoint popup_pos(img_pos.x - m_ams_dry_ctr_win->GetSize().GetWidth() + FromDIP(150), img_pos.y - FromDIP(80));
+ m_ams_dry_ctr_win->Move(popup_pos);
+ m_ams_dry_ctr_win->ShowModal();
+ } else {
m_percent_humidity_dry_popup->Update(info);
wxPoint img_pos = ClientToScreen(wxPoint(0, 0));
@@ -283,7 +295,12 @@ void AMSControl::on_retry()
post_event(wxCommandEvent(EVT_AMS_RETRY));
}
-AMSControl::~AMSControl() {}
+AMSControl::~AMSControl()
+{
+ if (m_ams_dry_ctr_win) {
+ delete m_ams_dry_ctr_win;
+ }
+}
std::string AMSControl::GetCurentAms() {
return m_current_ams;
@@ -501,6 +518,10 @@ void AMSControl::msw_rescale()
m_percent_humidity_dry_popup->msw_rescale();
}
+ if (m_ams_dry_ctr_win) {
+ m_ams_dry_ctr_win->msw_rescale();
+ }
+
m_Humidity_tip_popup.msw_rescale();
Layout();
@@ -824,6 +845,27 @@ void AMSControl::show_vams_kn_value(bool show)
//m_vams_lib->show_kn_value(show);
}
+void AMSControl::UpdateAmsDryControl(MachineObject* obj)
+{
+ if (!m_ams_dry_ctr_win->IsShown()) {
+ return;
+ }
+
+ if (!obj || !obj->GetFilaSystem()) {
+ m_ams_dry_ctr_win->Close();
+ return;
+ }
+
+ std::weak_ptr weak_fila_system = obj->GetFilaSystem();
+
+ if (auto locaked_fila_system = weak_fila_system.lock()) {
+ m_ams_dry_ctr_win->update(locaked_fila_system, obj);
+ } else {
+ m_ams_dry_ctr_win->Close();
+ return;
+ }
+}
+
std::vector AMSControl::GenerateSimulateData() {
auto caninfo0_0 = Caninfo{ "0", (""), *wxRED, AMSCanType::AMS_CAN_TYPE_VIRTUAL };
auto caninfo0_1 = Caninfo{ "1", (""), *wxGREEN, AMSCanType::AMS_CAN_TYPE_VIRTUAL };
diff --git a/src/slic3r/GUI/Widgets/AMSControl.hpp b/src/slic3r/GUI/Widgets/AMSControl.hpp
index 7616d269f2..1fe8876eba 100644
--- a/src/slic3r/GUI/Widgets/AMSControl.hpp
+++ b/src/slic3r/GUI/Widgets/AMSControl.hpp
@@ -16,6 +16,7 @@
#include
#include "slic3r/GUI/DeviceCore/DevExtruderSystem.h"
+#include "slic3r/GUI/AMSDryControl.hpp"
namespace Slic3r { namespace GUI {
@@ -127,6 +128,7 @@ protected:
AmsHumidityTipPopup m_Humidity_tip_popup;
uiAmsPercentHumidityDryPopup* m_percent_humidity_dry_popup;
+ AMSDryCtrWin* m_ams_dry_ctr_win;
std::string m_last_ams_id = "";
std::string m_last_tray_id = "";
@@ -170,6 +172,7 @@ public:
void CreateAmsDoubleNozzle(const std::string &series_name, const std::string& printer_type);
void CreateAmsSingleNozzle(const std::string &series_name, const std::string &printer_type);
void ClearAms();
+ void UpdateAmsDryControl(MachineObject* obj);
void UpdateAms(const std::string &series_name,
const std::string &printer_type,
std::vector ams_info,
diff --git a/src/slic3r/Utils/MoonrakerPrinterAgent.cpp b/src/slic3r/Utils/MoonrakerPrinterAgent.cpp
index f1d892134b..b0ea653dd3 100644
--- a/src/slic3r/Utils/MoonrakerPrinterAgent.cpp
+++ b/src/slic3r/Utils/MoonrakerPrinterAgent.cpp
@@ -536,7 +536,7 @@ void MoonrakerPrinterAgent::build_ams_payload(int ams_count, int max_lane_index,
print_json["ams"] = ams_json;
// Call the parser to populate DevFilaSystem
- DevFilaSystemParser::ParseV1_0(print_json, obj, obj->GetFilaSystem(), false);
+ DevFilaSystemParser::ParseV1_0(print_json, obj, obj->GetFilaSystem().get(), false);
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::build_ams_payload: Parsed " << trays.size() << " trays";
// Set printer_type so update_sync_status() can match it against the preset's printer type.