Skip to content

Commit 0edfc1f

Browse files
committed
Created STUB for frontend development
1 parent aed7b84 commit 0edfc1f

2 files changed

Lines changed: 154 additions & 9 deletions

File tree

BlocksScreen/devices/AMU/manager.py

Lines changed: 115 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import logging
22
import re
3+
import typing
34
from pathlib import Path
45

56
from PyQt6 import QtCore
67

8+
from .models import MMUState
9+
710
logger: logging.Logger = logging.getLogger(__name__)
811

912
CONFIG_PATH: Path = Path("~/printer_data/config/printer.cfg").expanduser()
@@ -21,9 +24,22 @@
2124
class AMUManager(QtCore.QObject):
2225
"""Main manager of the AMU system"""
2326

27+
run_gcode_signal: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal(
28+
str, name="run-gcode"
29+
)
30+
31+
mmu_state_changed: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal(
32+
object, name="mmu-state-changed"
33+
)
34+
35+
amu_toggled: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal(
36+
bool, name="amu-toggled"
37+
)
38+
2439
def __init__(self, parent: QtCore.QObject | None = None) -> None:
2540
super().__init__(parent)
2641
self._amu_state = False
42+
self._mmu_state: MMUState | None = None
2743
self.__setup_configfile()
2844

2945
def __setup_configfile(self) -> None:
@@ -44,11 +60,10 @@ def _apply_patterns(self, state: bool) -> bool:
4460
state (bool): True: Uncomment, False: Comment
4561
4662
Returns:
47-
bool: True: Sucess, False: Failed
63+
bool: True: Success, False: Failed
4864
"""
49-
# signal to say that it was changed AMU para mainwindow
5065
if self._amu_state == state:
51-
return
66+
return False
5267

5368
replacement = r"\2"
5469
if not state:
@@ -62,23 +77,114 @@ def _apply_patterns(self, state: bool) -> bool:
6277
return True
6378
except OSError as e:
6479
logger.error(
65-
"Failed to apply(state=%s) AMU System: could not read/write %s\n%e",
80+
"Failed to apply(state=%s) AMU System: could not read/write %s\n%s",
6681
state,
6782
self._config_filename,
6883
e,
6984
)
7085
return False
7186

72-
def toggle_amu_system(self, activate: bool) -> bool:
73-
"""Method that comments/uncomments the AMU_FILES from the printer.cfg according with state value
87+
def toggle_amu_system(self, activate: bool) -> None:
88+
"""Enable or disable the AMU system by commenting/uncommenting config includes.
89+
90+
Emits:
91+
amu_toggled (bool): True if the operation succeeded, False otherwise.
7492
7593
Args:
76-
state (bool): True: Uncomment, False: Comment
94+
activate (bool): True to enable the AMU, False to disable it.
95+
96+
"""
97+
result: bool = self._apply_patterns(activate)
98+
self.amu_toggled.emit(result)
99+
100+
def get_state(self) -> MMUState | None:
101+
"""Returns current MMU state, None if not yet received.
77102
78103
Returns:
79-
bool: True: Sucess, False: Failed
104+
MMUState: Latest state received from Moonraker
105+
None: If no state has been received yet.
106+
107+
"""
108+
return self._mmu_state
109+
110+
def is_amu_active(self) -> bool:
111+
"""Returns whether AMU includes are currently uncommented in printer.cfg"""
112+
return self._amu_state
113+
114+
def set_gate_info(
115+
self, gate: int, material: str, color: str, spool_id: int
116+
) -> None:
117+
"""Sets all gate attributes for a single MMU_GATE
118+
119+
Args:
120+
gate (int): Gate index (0-based).
121+
material (str): Filament material name, e.g. ``"PLA"``.
122+
color (str): Filament color as hex string, e.g. ``"ff56e0"``.
123+
spool_id (int): Spoolman spool ID, or -1 if not tracked.
124+
"""
125+
...
126+
127+
def set_gate_material(self, gate: int, material: str) -> None:
128+
"""Set the `material` at the gate `gate`
129+
130+
Args:
131+
gate (int): Gate index (0-based).
132+
material (str): Filament material name, e.g. ``"PLA"``.
133+
"""
134+
135+
def set_gate_color(self, gate: int, color: str) -> None:
136+
"""Set the `color` at the gate `gate`
137+
138+
Args:
139+
gate (int): Gate index (0-based).
140+
color (str): Filament color, e.g. ``"ff56e0"``.
141+
"""
142+
143+
def set_gate_spool(self, gate: int, spool_id: int) -> None:
144+
"""Set the `spool_id` at the gate `gate`
145+
146+
Args:
147+
gate (int): Gate index (0-based).
148+
spool_id (int): Spoolman spool ID, or -1 to clear.
149+
"""
150+
151+
def home_mmu(self) -> None:
152+
"""Home the MMU selector by sending MMU_HOME."""
153+
154+
def reset_mmu(self) -> None:
155+
"""Reset the MMU and clear any pause or error state by sending MMU_RESET."""
156+
157+
def load_gate(self, gate: int) -> None:
158+
"""Load filament from the specified gate by sending MMU_LOAD
159+
160+
Args:
161+
gate (int): Gate index to select (0-based)
162+
"""
163+
164+
def unload(self) -> None:
165+
"""Unload the currently loaded filament by sending MMU_UNLOAD."""
166+
...
167+
168+
def select_tool(self, tool: int) -> None:
169+
"""Select a tool, triggering a filament change if needed.
170+
171+
Args:
172+
tool (int): Tool index to select (0-based).
173+
"""
174+
...
175+
176+
@QtCore.pyqtSlot(dict, name="update_mmu_state")
177+
def update_mmu_state(self, data: dict) -> None:
178+
"""Receive an MMU status dict from Moonraker and update internal state.
179+
180+
Called with either a full status response (on connect) or a diff
181+
(from notify_status_update). Builds or updates the MMUState and
182+
emits mmu_state_changed
183+
184+
Args:
185+
data (dict): Raw MMU status or diff dict from Moonraker
80186
"""
81-
return self._apply_patterns(activate)
187+
...
82188

83189

84190
if __name__ == "__main__":

BlocksScreen/devices/AMU/models.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ class GateInfo:
1919
color_rgb: tuple[float, float, float]
2020
spool_id: int
2121

22+
@property
23+
def is_available(self) -> bool:
24+
"""Return True if the gate has filament available to load."""
25+
return self.status in (GateStatus.AVAILABLE, GateStatus.AVAILABLE_FROM_BUFFER)
26+
2227

2328
@dataclass(frozen=True, slots=True)
2429
class MMUState:
@@ -34,8 +39,28 @@ class MMUState:
3439
gates: tuple[GateInfo, ...]
3540
ttg_map: tuple[int, ...]
3641

42+
@property
43+
def is_paused(self) -> bool:
44+
"""Return True if the MMU is in a paused/error state."""
45+
return self.print_state == "pause"
46+
47+
@property
48+
def current_gate_info(self) -> GateInfo | None:
49+
"""Return the GateInfo for the currently selected gate, or None if no gate is selected."""
50+
if 0 <= self.gate < len(self.gates):
51+
return self.gates[self.gate]
52+
return None
53+
3754
@classmethod
3855
def from_status(cls, data: dict) -> "MMUState":
56+
"""Build an MMUState from a full Moonraker mmu status dict.
57+
58+
Args:
59+
data (dict): Full status dict from printer.objects.query or the initial notify_status_update payload.
60+
61+
Returns:
62+
MMUState: New instance populated from *data*
63+
"""
3964
num_gates = data.get("num_gates", 0)
4065

4166
statuses = data.get("gate_status", [GateStatus.UNKNOWN] * num_gates)
@@ -69,7 +94,21 @@ def from_status(cls, data: dict) -> "MMUState":
6994
ttg_map=tuple(data.get("ttg_map", [])),
7095
)
7196

97+
def gate_for_tool(self, tool: int) -> int:
98+
"""Returns the gate mapped to *tool*, or -1 if unmapped."""
99+
if 0 <= tool < len(self.ttg_map):
100+
return self.ttg_map[tool]
101+
return -1
102+
72103
def apply_diff(self, diff: dict) -> "MMUState":
104+
"""Apply a Moonraker status diff and return an updated MMUState.
105+
106+
Args:
107+
diff (dict): Partial status dict from notify_status_update.
108+
109+
Returns:
110+
MMUState: New instance with updated fields
111+
"""
73112
gate_keys: set[str] = {
74113
"gate_status",
75114
"gate_material",

0 commit comments

Comments
 (0)