From 46195f5885210670dbd9fe0477381502f4b80970 Mon Sep 17 00:00:00 2001 From: Seth Foster Date: Wed, 24 Jan 2024 14:11:13 -0500 Subject: [PATCH] fix(api): use move flags in aionotify for modules (#14345) After updating openembedded and buildroot, their udevs will actually create temporary files for module serial port symlinks and then rename the tempfiles to the real thing. This wouldn't get noticed because they were moves, rather than creates or deletes. Getting aionotify to tell us about moves too and plumbing those events through the add/delete paths fixes the issue. Closes RQA-2240 --- .../hardware_control/backends/controller.py | 7 ++++++- .../hardware_control/backends/ot3controller.py | 10 ++++++++-- .../opentrons/hardware_control/module_control.py | 14 ++++++++++---- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/api/src/opentrons/hardware_control/backends/controller.py b/api/src/opentrons/hardware_control/backends/controller.py index 5525dce3105..eb75389869d 100644 --- a/api/src/opentrons/hardware_control/backends/controller.py +++ b/api/src/opentrons/hardware_control/backends/controller.py @@ -111,7 +111,12 @@ def _build_event_watcher() -> aionotify.Watcher: watcher.watch( alias="modules", path="/dev", - flags=(aionotify.Flags.CREATE | aionotify.Flags.DELETE), + flags=( + aionotify.Flags.CREATE + | aionotify.Flags.DELETE + | aionotify.Flags.MOVED_FROM + | aionotify.Flags.MOVED_TO + ), ) return watcher diff --git a/api/src/opentrons/hardware_control/backends/ot3controller.py b/api/src/opentrons/hardware_control/backends/ot3controller.py index 2e49989b7e9..1cc0105dd4f 100644 --- a/api/src/opentrons/hardware_control/backends/ot3controller.py +++ b/api/src/opentrons/hardware_control/backends/ot3controller.py @@ -1094,7 +1094,12 @@ def _build_event_watcher() -> aionotify.Watcher: watcher.watch( alias="modules", path="/dev", - flags=(aionotify.Flags.CREATE | aionotify.Flags.DELETE), + flags=( + aionotify.Flags.CREATE + | aionotify.Flags.DELETE + | aionotify.Flags.MOVED_FROM + | aionotify.Flags.MOVED_TO + ), ) return watcher @@ -1105,9 +1110,10 @@ async def _handle_watch_event(self) -> None: log.debug("incomplete read error when quitting watcher") return if event is not None: + flags = aionotify.Flags.parse(event.flags) + log.debug(f"aionotify: {flags} {event.name}") if "ot_module" in event.name: event_name = event.name - flags = aionotify.Flags.parse(event.flags) event_description = AionotifyEvent.build(event_name, flags) await self.module_controls.handle_module_appearance(event_description) diff --git a/api/src/opentrons/hardware_control/module_control.py b/api/src/opentrons/hardware_control/module_control.py index d7c5f391ea1..ee64b82dd9e 100644 --- a/api/src/opentrons/hardware_control/module_control.py +++ b/api/src/opentrons/hardware_control/module_control.py @@ -26,7 +26,13 @@ log = logging.getLogger(__name__) -MODULE_PORT_REGEX = re.compile("|".join(modules.MODULE_TYPE_BY_NAME.keys()), re.I) +MODULE_PORT_REGEX = re.compile( + # capture all modules by name using alternation + "(" + "|".join(modules.MODULE_TYPE_BY_NAME.keys()) + ")" + # add a negative lookahead to suppress matches on tempfiles udev creates + + r"\d+(?!\.tmp-c\d+:\d+)", + re.I, +) class AttachedModulesControl: @@ -183,7 +189,7 @@ def get_module_at_port(port: str) -> Optional[modules.ModuleAtPort]: """ match = MODULE_PORT_REGEX.search(port) if match: - name = match.group().lower() + name = match.group(1).lower() if name not in modules.MODULE_TYPE_BY_NAME: log.warning(f"Unexpected module connected: {name} on {port}") return None @@ -205,10 +211,10 @@ async def handle_module_appearance(self, event: AionotifyEvent) -> None: new_modules = None removed_modules = None if maybe_module_at_port is not None: - if hasattr(event.flags, "DELETE"): + if hasattr(event.flags, "DELETE") or hasattr(event.flags, "MOVED_FROM"): removed_modules = [maybe_module_at_port] log.info(f"Module Removed: {maybe_module_at_port}") - elif hasattr(event.flags, "CREATE"): + elif hasattr(event.flags, "CREATE") or hasattr(event.flags, "MOVED_TO"): new_modules = [maybe_module_at_port] log.info(f"Module Added: {maybe_module_at_port}") try: