Skip to content

Commit

Permalink
Optionally, allow to not use the modbus cache
Browse files Browse the repository at this point in the history
  • Loading branch information
albireox committed Jan 15, 2024
1 parent ed2aac7 commit 839e822
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 33 deletions.
2 changes: 1 addition & 1 deletion python/lvmecp/actor/commands/dome.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.")

Expand Down
7 changes: 5 additions & 2 deletions python/lvmecp/actor/commands/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
14 changes: 7 additions & 7 deletions python/lvmecp/dome.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -76,15 +76,15 @@ 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."""

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

Expand Down Expand Up @@ -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."""
Expand All @@ -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.")

Expand All @@ -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)
13 changes: 8 additions & 5 deletions python/lvmecp/lights.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -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):
Expand Down
24 changes: 15 additions & 9 deletions python/lvmecp/modbus.py
Original file line number Diff line number Diff line change
Expand Up @@ -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":
Expand Down Expand Up @@ -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):
Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -320,18 +323,21 @@ 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

names = results = []

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)

Expand All @@ -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:
Expand Down
13 changes: 9 additions & 4 deletions python/lvmecp/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions python/lvmecp/plc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
7 changes: 5 additions & 2 deletions python/lvmecp/safety.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down

0 comments on commit 839e822

Please sign in to comment.