jmri_core.beans.signal_mast
SignalMast — a logical signal that exposes aspect names defined by
a signal system (e.g. "Clear", "Approach Medium").
Where SignalHead uses a fixed enum of colours, a SignalMast's set of
valid aspects is open-ended and loaded from XML at runtime. The mast is
composed of one or more underlying signal heads; here we only carry the
logical aspect — the head-driving logic lives in a Stage 1.5 module.
1"""``SignalMast`` — a logical signal that exposes aspect *names* defined by 2a signal system (e.g. ``"Clear"``, ``"Approach Medium"``). 3 4Where :class:`SignalHead` uses a fixed enum of colours, a SignalMast's set of 5valid aspects is open-ended and loaded from XML at runtime. The mast is 6composed of one or more underlying signal heads; here we only carry the 7logical aspect — the head-driving logic lives in a Stage 1.5 module. 8""" 9from __future__ import annotations 10 11from dataclasses import dataclass 12 13from jmri_core.bean import AbstractNamedBean 14from jmri_core.manager import Manager 15 16 17@dataclass(frozen=True, slots=True) 18class SignalMastChange: 19 previous: str 20 current: str 21 22 23class SignalMast(AbstractNamedBean): 24 """Logical signal exposing named aspects from a signal system. 25 26 Attributes: 27 28 * ``aspect`` — current displayed aspect name; empty string means unknown. 29 * ``signal_system`` — the name of the signal-system XML this mast follows 30 (e.g. ``"basic"``, ``"AAR-1946"``). Resolved against 31 ``xml/signals/<system>/`` at load time by persistence code. 32 * ``held`` — forces ``"Stop"`` (or the system's most-restrictive aspect). 33 """ 34 35 __slots__ = ("_aspect", "_held", "signal_system") 36 37 type_letter = "F" # JMRI uses "F" for SignalMast system-type letter 38 39 def __init__( 40 self, 41 system_name: str, 42 signal_system: str = "", 43 user_name: str | None = None, 44 ) -> None: 45 super().__init__(system_name, user_name) 46 self.signal_system = signal_system 47 self._aspect: str = "" 48 self._held: bool = False 49 50 @property 51 def aspect(self) -> str: 52 return self._aspect 53 54 @property 55 def held(self) -> bool: 56 return self._held 57 58 async def set_aspect(self, aspect: str) -> None: 59 prev = self._aspect 60 if prev == aspect: 61 return 62 self._aspect = aspect 63 await self.state_changed.emit(SignalMastChange(prev, aspect)) 64 65 async def set_held(self, held: bool) -> None: 66 # Held flips a flag; aspect display is computed elsewhere by the 67 # signal-mast logic. We emit a state-changed so listeners know 68 # something changed even if `aspect` didn't. 69 if held == self._held: 70 return 71 self._held = held 72 await self.state_changed.emit(SignalMastChange(self._aspect, self._aspect)) 73 74 75class SignalMastManager(Manager[SignalMast]): 76 type_letter = "F" 77 78 def _create(self, system_name: str) -> SignalMast: 79 return SignalMast(system_name)
@dataclass(frozen=True, slots=True)
class
SignalMastChange:
24class SignalMast(AbstractNamedBean): 25 """Logical signal exposing named aspects from a signal system. 26 27 Attributes: 28 29 * ``aspect`` — current displayed aspect name; empty string means unknown. 30 * ``signal_system`` — the name of the signal-system XML this mast follows 31 (e.g. ``"basic"``, ``"AAR-1946"``). Resolved against 32 ``xml/signals/<system>/`` at load time by persistence code. 33 * ``held`` — forces ``"Stop"`` (or the system's most-restrictive aspect). 34 """ 35 36 __slots__ = ("_aspect", "_held", "signal_system") 37 38 type_letter = "F" # JMRI uses "F" for SignalMast system-type letter 39 40 def __init__( 41 self, 42 system_name: str, 43 signal_system: str = "", 44 user_name: str | None = None, 45 ) -> None: 46 super().__init__(system_name, user_name) 47 self.signal_system = signal_system 48 self._aspect: str = "" 49 self._held: bool = False 50 51 @property 52 def aspect(self) -> str: 53 return self._aspect 54 55 @property 56 def held(self) -> bool: 57 return self._held 58 59 async def set_aspect(self, aspect: str) -> None: 60 prev = self._aspect 61 if prev == aspect: 62 return 63 self._aspect = aspect 64 await self.state_changed.emit(SignalMastChange(prev, aspect)) 65 66 async def set_held(self, held: bool) -> None: 67 # Held flips a flag; aspect display is computed elsewhere by the 68 # signal-mast logic. We emit a state-changed so listeners know 69 # something changed even if `aspect` didn't. 70 if held == self._held: 71 return 72 self._held = held 73 await self.state_changed.emit(SignalMastChange(self._aspect, self._aspect))
Logical signal exposing named aspects from a signal system.
Attributes:
aspect— current displayed aspect name; empty string means unknown.signal_system— the name of the signal-system XML this mast follows (e.g."basic","AAR-1946"). Resolved againstxml/signals/<system>/at load time by persistence code.held— forces"Stop"(or the system's most-restrictive aspect).
async def
set_held(self, held: bool) -> None:
66 async def set_held(self, held: bool) -> None: 67 # Held flips a flag; aspect display is computed elsewhere by the 68 # signal-mast logic. We emit a state-changed so listeners know 69 # something changed even if `aspect` didn't. 70 if held == self._held: 71 return 72 self._held = held 73 await self.state_changed.emit(SignalMastChange(self._aspect, self._aspect))
Inherited Members
76class SignalMastManager(Manager[SignalMast]): 77 type_letter = "F" 78 79 def _create(self, system_name: str) -> SignalMast: 80 return SignalMast(system_name)
Per-type bean registry. Subclass and override _create().
Attributes set on the class describe the bean type:
type_letter— single uppercase letter the bean class owns (e.g."T"for Turnout). Must be set by subclass.system_prefix— connection prefix (e.g."L"). Set per-instance because one bean class can have many connection-specific managers.