diff --git a/python/lvmecp/actor/commands/dome.py b/python/lvmecp/actor/commands/dome.py index 3d92ccc..39b3a35 100644 --- a/python/lvmecp/actor/commands/dome.py +++ b/python/lvmecp/actor/commands/dome.py @@ -66,7 +66,7 @@ async def close(command: ECPCommand, force=False): async def status(command: ECPCommand): """Returns the status of the dome.""" - status = await command.actor.plc.dome.update() + status = await command.actor.plc.dome.update(use_cache=False) if status is None: return command.fail("Failed retrieving dome status.") diff --git a/python/lvmecp/actor/commands/status.py b/python/lvmecp/actor/commands/status.py index e8d0e8d..80a0555 100644 --- a/python/lvmecp/actor/commands/status.py +++ b/python/lvmecp/actor/commands/status.py @@ -26,11 +26,14 @@ async def status(command: ECPCommand): plc = command.actor.plc - command.info(registers=(await plc.read_all_registers())) + command.info(registers=(await plc.read_all_registers(use_cache=False))) modules: list[PLCModule] = [plc.dome, plc.safety, plc.lights] await asyncio.gather( - *[module.update(force_output=True, command=command) for module in modules] + *[ + module.update(force_output=True, command=command, use_cache=True) + for module in modules + ] ) command.info( diff --git a/python/lvmecp/dome.py b/python/lvmecp/dome.py index 40d418b..434d776 100644 --- a/python/lvmecp/dome.py +++ b/python/lvmecp/dome.py @@ -31,8 +31,8 @@ def __init__(self, *args, **kwargs): # us whether we are open or closed. self.dome_is_open: bool | None = None - async def _update_internal(self): - dome_registers = await self.plc.modbus.read_group("dome") + async def _update_internal(self, use_cache: bool = True): + dome_registers = await self.plc.modbus.read_group("dome", use_cache=use_cache) dome_status = SimpleNamespace(**dome_registers) @@ -76,7 +76,7 @@ async def set_direction(self, open: bool): """Sets the motor direction (`True` means open, `False` close).""" await self.modbus["drive_direction"].set(open) - await self.update() + await self.update(use_cache=False) async def _move(self, open: bool, force: bool = False): """Moves the dome to open/close position.""" @@ -84,7 +84,7 @@ async def _move(self, open: bool, force: bool = False): if not (await self.plc.safety.is_remote()): raise DomeError("Cannot move dome while in local mode.") - await self.update() + await self.update(use_cache=False) assert self.status is not None and self.flag is not None @@ -130,7 +130,7 @@ async def _move(self, open: bool, force: bool = False): self.dome_is_open = open - await self.update() + await self.update(use_cache=False) async def open(self, force: bool = False): """Open the dome.""" @@ -145,7 +145,7 @@ async def close(self, force: bool = False): async def stop(self): """Stops the dome.""" - status = await self.update() + status = await self.update(use_cache=False) if status is None or self.flag is None: raise RuntimeError("Failed retrieving dome status.") @@ -159,4 +159,4 @@ async def stop(self): if is_moving: self.dome_is_open = None - await self.update() + await self.update(use_cache=False) diff --git a/python/lvmecp/lights.py b/python/lvmecp/lights.py index 4969826..2b41f88 100644 --- a/python/lvmecp/lights.py +++ b/python/lvmecp/lights.py @@ -43,12 +43,15 @@ class LightsController(PLCModule): flag = LightStatus interval = 10.0 - async def _update_internal(self): + async def _update_internal(self, use_cache: bool = True): """Update status.""" assert self.flag is not None - light_registers = await self.plc.modbus.read_group("lights") + light_registers = await self.plc.modbus.read_group( + "lights", + use_cache=use_cache, + ) active_bits = self.flag(0) for key in light_registers: @@ -123,14 +126,14 @@ async def toggle(self, light: str): await self.modbus[f"{code}_new"].set(True) await asyncio.sleep(0.5) - await self.update() + await self.update(use_cache=False) async def on(self, light: str): """Turns on a light.""" assert self.status is not None - await self.update() + await self.update(use_cache=False) flag = self.get_flag(light) if self.status & flag: @@ -143,7 +146,7 @@ async def off(self, light: str): assert self.status is not None - await self.update() + await self.update(use_cache=False) flag = self.get_flag(light) if not (self.status & flag): diff --git a/python/lvmecp/modbus.py b/python/lvmecp/modbus.py index 4686973..3c7c94e 100644 --- a/python/lvmecp/modbus.py +++ b/python/lvmecp/modbus.py @@ -73,12 +73,12 @@ def __init__( self._last_value: int | float = 0 self._last_seen: float = 0 - async def _get_internal(self): + async def _get_internal(self, use_cache: bool = True): """Return the value of the modbus register.""" cache_timeout = self.modbus.cache_timeout last_seen_interval = time() - self._last_seen - if last_seen_interval < cache_timeout: + if use_cache and last_seen_interval < cache_timeout: return self._last_value if self.mode == "coil": @@ -138,7 +138,7 @@ async def _get_internal(self): return value - async def get(self, open_connection: bool = True): + async def get(self, open_connection: bool = True, use_cache: bool = True): """Return the value of the modbus register. Implements retry.""" for ntries in range(1, MAX_RETRIES + 1): @@ -152,7 +152,7 @@ async def get(self, open_connection: bool = True): raise ConnectionError("Not connected to modbus server.") try: - return await self._get_internal() + return await self._get_internal(use_cache=use_cache) except Exception: if ntries >= MAX_RETRIES: raise @@ -190,6 +190,9 @@ async def set(self, value: int | bool): f"{self.name!r}: 0x{resp.function_code:02X}." ) else: + self._last_value = int(value) + self._last_seen = time() + return except Exception as err: @@ -320,10 +323,10 @@ async def unlock_on_timeout(self): if self.lock.locked(): self.lock.release() - async def get_all(self): + async def get_all(self, use_cache: bool = True): """Returns a dictionary with all the registers.""" - if time() - self._register_last_seen < self.cache_timeout: + if use_cache and time() - self._register_last_seen < self.cache_timeout: if None not in self._register_cache.values(): return self._register_cache @@ -331,7 +334,10 @@ async def get_all(self): async with self: names = [name for name in self] - tasks = [elem.get(open_connection=False) for elem in self.values()] + tasks = [ + elem.get(open_connection=False, use_cache=False) + for elem in self.values() + ] results = await asyncio.gather(*tasks, return_exceptions=True) @@ -351,10 +357,10 @@ async def get_all(self): return registers - async def read_group(self, group: str): + async def read_group(self, group: str, use_cache: bool = True): """Returns a dictionary of all read registers that match a ``group``.""" - registers = await self.get_all() + registers = await self.get_all(use_cache=use_cache) group_registers = {} for name in self: diff --git a/python/lvmecp/module.py b/python/lvmecp/module.py index 500e3c2..e80e307 100644 --- a/python/lvmecp/module.py +++ b/python/lvmecp/module.py @@ -76,20 +76,25 @@ async def _status_loop(self): assert self._interval is not None while True: - await self.update() + await self.update(use_cache=False) await asyncio.sleep(self._interval) @abc.abstractmethod - async def _update_internal(self) -> Flag_co: + async def _update_internal(self, **kwargs) -> Flag_co: """Determines the new module flag status.""" pass - async def update(self, force_output: bool = False, **notifier_kwargs): + async def update( + self, + force_output: bool = False, + use_cache: bool = True, + **notifier_kwargs, + ): """Refreshes the module status.""" try: - new_status = await self._update_internal() + new_status = await self._update_internal(use_cache=use_cache) except Exception as err: log.warning(f"{self.name}: failed updating status: {err}") new_status = self.flag(self.flag.__unknown__) if self.flag else None diff --git a/python/lvmecp/plc.py b/python/lvmecp/plc.py index 8c3e90d..9087958 100644 --- a/python/lvmecp/plc.py +++ b/python/lvmecp/plc.py @@ -90,10 +90,10 @@ def __init__(self, config: dict, actor: ECPActor | None = None): notifier=None, ) - async def read_all_registers(self): + async def read_all_registers(self, use_cache: bool = True): """Reads all the connected registers and returns a dictionary.""" - registers = await self.modbus.get_all() - registers.update(await self.hvac_modbus.get_all()) + registers = await self.modbus.get_all(use_cache=use_cache) + registers.update(await self.hvac_modbus.get_all(use_cache=use_cache)) return registers diff --git a/python/lvmecp/safety.py b/python/lvmecp/safety.py index 8491209..c20de76 100644 --- a/python/lvmecp/safety.py +++ b/python/lvmecp/safety.py @@ -27,10 +27,13 @@ def __init__(self, *args, **kwargs): self.o2_level_utilities: float = math.nan self.o2_level_spectrograph: float = math.nan - async def _update_internal(self): + async def _update_internal(self, use_cache: bool = True): assert self.flag is not None - safety_registers = await self.plc.modbus.read_group("safety") + safety_registers = await self.plc.modbus.read_group( + "safety", + use_cache=use_cache, + ) safety_status = SimpleNamespace(**safety_registers)