jmri_logixng.expressions.digital
Digital (boolean) expressions over layout state + boolean algebra.
1"""Digital (boolean) expressions over layout state + boolean algebra.""" 2from __future__ import annotations 3 4from typing import Any, ClassVar 5 6from jmri_core.state import LightState, SensorState, TurnoutState 7 8from jmri_logixng.context import ExecutionContext 9from jmri_logixng.registry import register 10from jmri_logixng.tree import Expression 11 12 13@register 14class ConstantBool(Expression[bool]): 15 """Always evaluates to ``value``. Used for testing + as the leaf of trees.""" 16 17 type_tag: ClassVar[str] = "expr:const-bool" 18 19 def __init__( 20 self, *, value: bool, user_name: str = "", node_id: str | None = None 21 ) -> None: 22 super().__init__(user_name=user_name, node_id=node_id) 23 self.value = value 24 25 async def evaluate(self, ctx: ExecutionContext) -> bool: 26 return self.value 27 28 def extra_fields(self) -> dict[str, Any]: 29 return {"value": self.value} 30 31 32@register 33class SensorIsActive(Expression[bool]): 34 """True when sensor *sensor* has state ACTIVE.""" 35 36 type_tag: ClassVar[str] = "expr:sensor-active" 37 38 def __init__( 39 self, *, sensor: str, user_name: str = "", node_id: str | None = None 40 ) -> None: 41 super().__init__(user_name=user_name, node_id=node_id) 42 self.sensor = sensor 43 44 async def evaluate(self, ctx: ExecutionContext) -> bool: 45 bean = ctx.connection.sensors.get(self.sensor) 46 if bean is None: 47 ctx.log.warning("SensorIsActive: no sensor %r", self.sensor) 48 return False 49 return bean.known_state == SensorState.ACTIVE 50 51 def extra_fields(self) -> dict[str, Any]: 52 return {"sensor": self.sensor} 53 54 55@register 56class TurnoutIsClosed(Expression[bool]): 57 """True when turnout *turnout* has known state CLOSED.""" 58 59 type_tag: ClassVar[str] = "expr:turnout-closed" 60 61 def __init__( 62 self, *, turnout: str, user_name: str = "", node_id: str | None = None 63 ) -> None: 64 super().__init__(user_name=user_name, node_id=node_id) 65 self.turnout = turnout 66 67 async def evaluate(self, ctx: ExecutionContext) -> bool: 68 bean = ctx.connection.turnouts.get(self.turnout) 69 if bean is None: 70 ctx.log.warning("TurnoutIsClosed: no turnout %r", self.turnout) 71 return False 72 return bean.known_state == TurnoutState.CLOSED 73 74 def extra_fields(self) -> dict[str, Any]: 75 return {"turnout": self.turnout} 76 77 78@register 79class LightIsOn(Expression[bool]): 80 """True when light *light* has state ON.""" 81 82 type_tag: ClassVar[str] = "expr:light-on" 83 84 def __init__( 85 self, *, light: str, user_name: str = "", node_id: str | None = None 86 ) -> None: 87 super().__init__(user_name=user_name, node_id=node_id) 88 self.light = light 89 90 async def evaluate(self, ctx: ExecutionContext) -> bool: 91 bean = ctx.connection.lights.get(self.light) 92 if bean is None: 93 ctx.log.warning("LightIsOn: no light %r", self.light) 94 return False 95 return bean.state == LightState.ON 96 97 def extra_fields(self) -> dict[str, Any]: 98 return {"light": self.light} 99 100 101@register 102class MemoryEquals(Expression[bool]): 103 """True when memory *memory*'s value compares equal to *value*.""" 104 105 type_tag: ClassVar[str] = "expr:memory-equals" 106 107 def __init__( 108 self, 109 *, 110 memory: str, 111 value: Any, 112 user_name: str = "", 113 node_id: str | None = None, 114 ) -> None: 115 super().__init__(user_name=user_name, node_id=node_id) 116 self.memory = memory 117 self.value = value 118 119 async def evaluate(self, ctx: ExecutionContext) -> bool: 120 bean = ctx.connection.memories.get(self.memory) 121 if bean is None: 122 ctx.log.warning("MemoryEquals: no memory %r", self.memory) 123 return False 124 return bool(bean.value == self.value) 125 126 def extra_fields(self) -> dict[str, Any]: 127 return {"memory": self.memory, "value": self.value} 128 129 130# --- boolean combinators --------------------------------------------------- 131 132 133@register 134class And(Expression[bool]): 135 """True iff every child expression evaluates true. Short-circuits.""" 136 137 type_tag: ClassVar[str] = "expr:and" 138 139 def __init__( 140 self, 141 children: list[Expression[bool]], 142 *, 143 user_name: str = "", 144 node_id: str | None = None, 145 ) -> None: 146 super().__init__(user_name=user_name, node_id=node_id) 147 self.children = list(children) 148 149 async def evaluate(self, ctx: ExecutionContext) -> bool: 150 for child in self.children: 151 if not await child.evaluate(ctx): 152 return False 153 return True 154 155 def extra_fields(self) -> dict[str, Any]: 156 return {"children": [c.to_dict() for c in self.children]} 157 158 159@register 160class Or(Expression[bool]): 161 """True if any child expression evaluates true. Short-circuits.""" 162 163 type_tag: ClassVar[str] = "expr:or" 164 165 def __init__( 166 self, 167 children: list[Expression[bool]], 168 *, 169 user_name: str = "", 170 node_id: str | None = None, 171 ) -> None: 172 super().__init__(user_name=user_name, node_id=node_id) 173 self.children = list(children) 174 175 async def evaluate(self, ctx: ExecutionContext) -> bool: 176 for child in self.children: 177 if await child.evaluate(ctx): 178 return True 179 return False 180 181 def extra_fields(self) -> dict[str, Any]: 182 return {"children": [c.to_dict() for c in self.children]} 183 184 185@register 186class Not(Expression[bool]): 187 """Negates its child expression.""" 188 189 type_tag: ClassVar[str] = "expr:not" 190 191 def __init__( 192 self, 193 child: Expression[bool], 194 *, 195 user_name: str = "", 196 node_id: str | None = None, 197 ) -> None: 198 super().__init__(user_name=user_name, node_id=node_id) 199 self.child = child 200 201 async def evaluate(self, ctx: ExecutionContext) -> bool: 202 return not await self.child.evaluate(ctx) 203 204 def extra_fields(self) -> dict[str, Any]: 205 return {"child": self.child.to_dict()}
14@register 15class ConstantBool(Expression[bool]): 16 """Always evaluates to ``value``. Used for testing + as the leaf of trees.""" 17 18 type_tag: ClassVar[str] = "expr:const-bool" 19 20 def __init__( 21 self, *, value: bool, user_name: str = "", node_id: str | None = None 22 ) -> None: 23 super().__init__(user_name=user_name, node_id=node_id) 24 self.value = value 25 26 async def evaluate(self, ctx: ExecutionContext) -> bool: 27 return self.value 28 29 def extra_fields(self) -> dict[str, Any]: 30 return {"value": self.value}
Always evaluates to value. Used for testing + as the leaf of trees.
Hook for subclasses to add their own fields to to_dict.
Inherited Members
33@register 34class SensorIsActive(Expression[bool]): 35 """True when sensor *sensor* has state ACTIVE.""" 36 37 type_tag: ClassVar[str] = "expr:sensor-active" 38 39 def __init__( 40 self, *, sensor: str, user_name: str = "", node_id: str | None = None 41 ) -> None: 42 super().__init__(user_name=user_name, node_id=node_id) 43 self.sensor = sensor 44 45 async def evaluate(self, ctx: ExecutionContext) -> bool: 46 bean = ctx.connection.sensors.get(self.sensor) 47 if bean is None: 48 ctx.log.warning("SensorIsActive: no sensor %r", self.sensor) 49 return False 50 return bean.known_state == SensorState.ACTIVE 51 52 def extra_fields(self) -> dict[str, Any]: 53 return {"sensor": self.sensor}
True when sensor sensor has state ACTIVE.
Hook for subclasses to add their own fields to to_dict.
Inherited Members
56@register 57class TurnoutIsClosed(Expression[bool]): 58 """True when turnout *turnout* has known state CLOSED.""" 59 60 type_tag: ClassVar[str] = "expr:turnout-closed" 61 62 def __init__( 63 self, *, turnout: str, user_name: str = "", node_id: str | None = None 64 ) -> None: 65 super().__init__(user_name=user_name, node_id=node_id) 66 self.turnout = turnout 67 68 async def evaluate(self, ctx: ExecutionContext) -> bool: 69 bean = ctx.connection.turnouts.get(self.turnout) 70 if bean is None: 71 ctx.log.warning("TurnoutIsClosed: no turnout %r", self.turnout) 72 return False 73 return bean.known_state == TurnoutState.CLOSED 74 75 def extra_fields(self) -> dict[str, Any]: 76 return {"turnout": self.turnout}
True when turnout turnout has known state CLOSED.
Hook for subclasses to add their own fields to to_dict.
Inherited Members
79@register 80class LightIsOn(Expression[bool]): 81 """True when light *light* has state ON.""" 82 83 type_tag: ClassVar[str] = "expr:light-on" 84 85 def __init__( 86 self, *, light: str, user_name: str = "", node_id: str | None = None 87 ) -> None: 88 super().__init__(user_name=user_name, node_id=node_id) 89 self.light = light 90 91 async def evaluate(self, ctx: ExecutionContext) -> bool: 92 bean = ctx.connection.lights.get(self.light) 93 if bean is None: 94 ctx.log.warning("LightIsOn: no light %r", self.light) 95 return False 96 return bean.state == LightState.ON 97 98 def extra_fields(self) -> dict[str, Any]: 99 return {"light": self.light}
True when light light has state ON.
Hook for subclasses to add their own fields to to_dict.
Inherited Members
102@register 103class MemoryEquals(Expression[bool]): 104 """True when memory *memory*'s value compares equal to *value*.""" 105 106 type_tag: ClassVar[str] = "expr:memory-equals" 107 108 def __init__( 109 self, 110 *, 111 memory: str, 112 value: Any, 113 user_name: str = "", 114 node_id: str | None = None, 115 ) -> None: 116 super().__init__(user_name=user_name, node_id=node_id) 117 self.memory = memory 118 self.value = value 119 120 async def evaluate(self, ctx: ExecutionContext) -> bool: 121 bean = ctx.connection.memories.get(self.memory) 122 if bean is None: 123 ctx.log.warning("MemoryEquals: no memory %r", self.memory) 124 return False 125 return bool(bean.value == self.value) 126 127 def extra_fields(self) -> dict[str, Any]: 128 return {"memory": self.memory, "value": self.value}
True when memory memory's value compares equal to value.
127 def extra_fields(self) -> dict[str, Any]: 128 return {"memory": self.memory, "value": self.value}
Hook for subclasses to add their own fields to to_dict.
Inherited Members
134@register 135class And(Expression[bool]): 136 """True iff every child expression evaluates true. Short-circuits.""" 137 138 type_tag: ClassVar[str] = "expr:and" 139 140 def __init__( 141 self, 142 children: list[Expression[bool]], 143 *, 144 user_name: str = "", 145 node_id: str | None = None, 146 ) -> None: 147 super().__init__(user_name=user_name, node_id=node_id) 148 self.children = list(children) 149 150 async def evaluate(self, ctx: ExecutionContext) -> bool: 151 for child in self.children: 152 if not await child.evaluate(ctx): 153 return False 154 return True 155 156 def extra_fields(self) -> dict[str, Any]: 157 return {"children": [c.to_dict() for c in self.children]}
True iff every child expression evaluates true. Short-circuits.
156 def extra_fields(self) -> dict[str, Any]: 157 return {"children": [c.to_dict() for c in self.children]}
Hook for subclasses to add their own fields to to_dict.
Inherited Members
160@register 161class Or(Expression[bool]): 162 """True if any child expression evaluates true. Short-circuits.""" 163 164 type_tag: ClassVar[str] = "expr:or" 165 166 def __init__( 167 self, 168 children: list[Expression[bool]], 169 *, 170 user_name: str = "", 171 node_id: str | None = None, 172 ) -> None: 173 super().__init__(user_name=user_name, node_id=node_id) 174 self.children = list(children) 175 176 async def evaluate(self, ctx: ExecutionContext) -> bool: 177 for child in self.children: 178 if await child.evaluate(ctx): 179 return True 180 return False 181 182 def extra_fields(self) -> dict[str, Any]: 183 return {"children": [c.to_dict() for c in self.children]}
True if any child expression evaluates true. Short-circuits.
182 def extra_fields(self) -> dict[str, Any]: 183 return {"children": [c.to_dict() for c in self.children]}
Hook for subclasses to add their own fields to to_dict.
Inherited Members
186@register 187class Not(Expression[bool]): 188 """Negates its child expression.""" 189 190 type_tag: ClassVar[str] = "expr:not" 191 192 def __init__( 193 self, 194 child: Expression[bool], 195 *, 196 user_name: str = "", 197 node_id: str | None = None, 198 ) -> None: 199 super().__init__(user_name=user_name, node_id=node_id) 200 self.child = child 201 202 async def evaluate(self, ctx: ExecutionContext) -> bool: 203 return not await self.child.evaluate(ctx) 204 205 def extra_fields(self) -> dict[str, Any]: 206 return {"child": self.child.to_dict()}
Negates its child expression.
Hook for subclasses to add their own fields to to_dict.