diff --git a/src/gallia/services/uds/core/client.py b/src/gallia/services/uds/core/client.py index b176c642f..7c734d571 100644 --- a/src/gallia/services/uds/core/client.py +++ b/src/gallia/services/uds/core/client.py @@ -97,6 +97,9 @@ async def request_unsafe( except ConnectionError as e: logger.warning(f"{request} failed with: {e!r}") last_exception = MissingResponse(request, str(e)) + # Explicitly set __cause__ such that higher levels might react, e.g. wait_for_ecu() + # Basically equal to "raise last_exception from e" + last_exception.__cause__ = e if i < max_retry: logger.info(f"Sleeping for {wait_time}s before attempting to reconnect") await asyncio.sleep(wait_time) diff --git a/src/gallia/services/uds/ecu.py b/src/gallia/services/uds/ecu.py index aef007b1f..8f17cb308 100644 --- a/src/gallia/services/uds/ecu.py +++ b/src/gallia/services/uds/ecu.py @@ -323,11 +323,16 @@ async def _wait_for_ecu_endless_loop(self, sleep_time: float) -> None: await asyncio.sleep(sleep_time) await self.ping(config=config) break - except ConnectionError as e: - logger.debug(f"ECU not ready: {e!r}, reconnecting…") - await self.reconnect() - except UDSException as e: + # When the ECU is not ready, we expect an UDSException, e.g. MissingResponse. + # On ConnectionError, we additionally reconnect the transport to ensure connectivity. + # Since Gallia converts a ConnectionError to a MissingResponse in `request_unsafe`, however, + # there is a need to reconnect the transport also in case of high-level UDSExceptions + # such as MissingResponses that are raised (__cause__) from ConnectionErrors. + except (ConnectionError, UDSException) as e: logger.debug(f"ECU not ready: {e!r}") + if isinstance(e, ConnectionError) or isinstance(e.__cause__, ConnectionError): + logger.debug("Reconnecting…") + await self.reconnect() logger.info("ECU ready") async def wait_for_ecu(