1+ from __future__ import annotations
2+
13import logging
2- import re
34import typing
45from pathlib import Path
56
67from PyQt6 import QtCore
78
8- from .models import MMUState
9+ from BlocksScreen .devices .amu .config_toggler import ConfigToggler
10+
11+ from .models import MMUState , SpoolmanSupport
12+
13+ if typing .TYPE_CHECKING :
14+ from BlocksScreen .lib .moonrakerComm import MoonWebSocket
15+
916
1017logger : logging .Logger = logging .getLogger (__name__ )
1118
1219CONFIG_PATH : Path = Path ("~/printer_data/config/printer.cfg" ).expanduser ()
13- AMU_FILES : list [str ] = [
14- "mmu/base/*.cfg" ,
15- "mmu/optional/client_macros.cfg" ,
16- "filament_manager.cfg" ,
17- ]
18-
19- AMU_PATTERNS : list [re .Pattern [str ]] = [
20- re .compile (rf"^(#?)(\[include { re .escape (f )} \])" , re .MULTILINE ) for f in AMU_FILES
21- ]
2220
2321
2422class AMUManager (QtCore .QObject ):
@@ -39,55 +37,17 @@ class AMUManager(QtCore.QObject):
3937 pre_gate_changed : typing .ClassVar [QtCore .pyqtSignal ] = QtCore .pyqtSignal (
4038 int , bool , name = "pre-gate-changed"
4139 )
40+ spool_fetched : typing .ClassVar [QtCore .pyqtSignal ] = QtCore .pyqtSignal (
41+ int , dict , name = "spool-fetched"
42+ )
4243
43- def __init__ (self , parent : QtCore .QObject | None = None ) -> None :
44+ def __init__ (self , ws : MoonWebSocket , parent : QtCore .QObject | None = None ) -> None :
4445 super ().__init__ (parent )
46+ self ._config_toggler = ConfigToggler (CONFIG_PATH )
4547 self ._amu_state = False
48+ self ._ws = ws
4649 self ._mmu_state : MMUState | None = None
4750 self ._pre_gate_sensors : dict [int , bool ] = {}
48- self .__setup_configfile ()
49-
50- def __setup_configfile (self ) -> None :
51- """Sets up local configfile variable"""
52- self ._config_filename : Path | None = CONFIG_PATH
53- if not self ._config_filename .exists ():
54- logger .warning ("Config file not found %s" , self ._config_filename )
55- self ._config_filename = None
56-
57- def _apply_patterns (self , state : bool ) -> bool :
58- """Method that comments/uncomments the AMU_FILES from the printer.cfg according with state value
59-
60- Args:
61- state (bool): True: Uncomment, False: Comment
62-
63- Returns:
64- bool: True: Success, False: Failed
65- """
66- if self ._config_filename is None :
67- logger .warning ("_apply_patterns called but no config file available" )
68- return False
69-
70- if self ._amu_state == state :
71- return False
72-
73- replacement = r"\2"
74- if not state :
75- replacement = r"#\2"
76- try :
77- text : str = self ._config_filename .read_text ()
78- for file in AMU_PATTERNS :
79- text = file .sub (replacement , text )
80- self ._config_filename .write_text (text )
81- self ._amu_state = state
82- return True
83- except OSError as e :
84- logger .error (
85- "Failed to apply(state=%s) AMU System: could not read/write %s\n %s" ,
86- state ,
87- self ._config_filename ,
88- e ,
89- )
90- return False
9151
9252 def toggle_amu_system (self , activate : bool ) -> None :
9353 """Enable or disable the AMU system by commenting/uncommenting config includes.
@@ -99,8 +59,10 @@ def toggle_amu_system(self, activate: bool) -> None:
9959 activate (bool): True to enable the AMU, False to disable it.
10060
10161 """
102- result : bool = self ._apply_patterns (activate )
62+ result : bool = self ._config_toggler . toggle (activate )
10363 self .amu_toggled .emit (result )
64+ if result :
65+ self .run_gcode_signal .emit ("FIRMWARE_RESTART" )
10466
10567 def get_state (self ) -> MMUState | None :
10668 """Returns current MMU state, None if not yet received.
@@ -115,9 +77,30 @@ def get_state(self) -> MMUState | None:
11577 def get_pre_gate_sensors (self ) -> dict [int , bool ]:
11678 return dict (self ._pre_gate_sensors )
11779
80+ def is_amu_configured (self ) -> bool :
81+ """Return True if AMU includes are uncommented in printer.cfg."""
82+ return self ._config_toggler .is_configured ()
83+
11884 def is_amu_active (self ) -> bool :
11985 """Returns whether AMU includes are currently uncommented in printer.cfg"""
120- return self ._amu_state
86+ return self .is_amu_configured () and self ._mmu_state is not None
87+
88+ def fetch_spool (self , gate : int , spool_id : int ) -> None :
89+ """Request spool data from Moonraker via WebSocket.
90+
91+ No-op if MMU state not received or spoolman_support is OFF.
92+ Emits spool_fetched(gate, data) on sucess; logs and emits nothing on error.
93+ """
94+ if self ._mmu_state is None :
95+ return
96+ if self ._mmu_state .spoolman_support is SpoolmanSupport .OFF :
97+ return
98+
99+ def _on_result (result : dict | None ) -> None :
100+ if result is not None :
101+ self .spool_fetched .emit (gate , result )
102+
103+ self ._ws .api .get_spool (spool_id , _on_result )
121104
122105 def set_gate_info (
123106 self , gate : int , material : str , color : str , spool_id : int
@@ -239,3 +222,4 @@ def on_klippy_state(self, state: str) -> None:
239222 """React to changes in klippy states"""
240223 if state .lower () != "ready" :
241224 self ._mmu_state = None
225+ self ._pre_gate_sensors = {}
0 commit comments