diff --git a/docs/Async Connections.md b/docs/Async Connections.md index b3de1eb3..b4074be1 100644 --- a/docs/Async Connections.md +++ b/docs/Async Connections.md @@ -2,10 +2,18 @@ Since the standard `query()` function is blocking, it can be a hazard for UI eve The update loop is controlled by calling `start()` and `stop()`. To subscribe a command for updating, call `watch()` with your requested OBDCommand. Because the update loop is threaded, commands can only be `watch`ed while the loop is `stop`ed. +General sequence to enable an asynchronous connection allowing non-blocking queries: +- *Async()* # set-up the connection (to be used in place of *OBD()*) +- *watch()* # add commands to the watch list +- *start()* # start a thread performing the update loop in background +- *query()* # perform the non-blocking query + +Example: + ```python import obd -connection = obd.Async() # same constructor as 'obd.OBD()' +connection = obd.Async() # same constructor as 'obd.OBD()'; see below. connection.watch(obd.commands.RPM) # keep track of the RPM @@ -39,6 +47,15 @@ connection.stop() --- +### Async(portstr=None, baudrate=None, protocol=None, fast=True, timeout=0.1, check_voltage=True, delay_cmds=0.25) + +Create asynchronous connection. +Arguments are the same as 'obd.OBD()' with the addition of *delay_cmds*, which defaults to 0.25 seconds and allows +controlling a delay after each loop executing all *watch*ed commands in background. If *delay_cmds* is set to 0, +the background thread continuously repeats the execution of all commands without any delay. + +--- + ### start() Starts the update loop. diff --git a/obd/asynchronous.py b/obd/asynchronous.py index be64c915..c50bd6a6 100644 --- a/obd/asynchronous.py +++ b/obd/asynchronous.py @@ -46,7 +46,7 @@ class Async(OBD): """ def __init__(self, portstr=None, baudrate=None, protocol=None, fast=True, - timeout=0.1, check_voltage=True): + timeout=0.1, check_voltage=True, delay_cmds=0.25): super(Async, self).__init__(portstr, baudrate, protocol, fast, timeout, check_voltage) self.__commands = {} # key = OBDCommand, value = Response @@ -54,6 +54,7 @@ def __init__(self, portstr=None, baudrate=None, protocol=None, fast=True, self.__thread = None self.__running = False self.__was_running = False # used with __enter__() and __exit__() + self.__delay_cmds = delay_cmds @property @@ -215,6 +216,11 @@ def run(self): if len(self.__commands) > 0: # loop over the requested commands, send, and collect the response for c in self.__commands: + if not self.is_connected(): + logger.info("Async thread terminated because device disconnected") + self.__running = False + self.__thread = None + return # force, since commands are checked for support in watch() r = super(Async, self).query(c, force=True) @@ -225,6 +231,7 @@ def run(self): # fire the callbacks, if there are any for callback in self.__callbacks[c]: callback(r) + time.sleep(self.__delay_cmds) else: time.sleep(0.25) # idle diff --git a/obd/elm327.py b/obd/elm327.py index 04ccaedc..d8b6b17e 100644 --- a/obd/elm327.py +++ b/obd/elm327.py @@ -444,9 +444,16 @@ def __write(self, cmd): if self.__port: cmd += b"\r" # terminate with carriage return in accordance with ELM327 and STN11XX specifications logger.debug("write: " + repr(cmd)) - self.__port.flushInput() # dump everything in the input buffer - self.__port.write(cmd) # turn the string into bytes and write - self.__port.flush() # wait for the output buffer to finish transmitting + try: + self.__port.flushInput() # dump everything in the input buffer + self.__port.write(cmd) # turn the string into bytes and write + self.__port.flush() # wait for the output buffer to finish transmitting + except Exception: + self.__status = OBDStatus.NOT_CONNECTED + self.__port.close() + self.__port = None + logger.critical("Device disconnected while writing") + return else: logger.info("cannot perform __write() when unconnected") @@ -466,7 +473,14 @@ def __read(self): while True: # retrieve as much data as possible - data = self.__port.read(self.__port.in_waiting or 1) + try: + data = self.__port.read(self.__port.in_waiting or 1) + except Exception: + self.__status = OBDStatus.NOT_CONNECTED + self.__port.close() + self.__port = None + logger.critical("Device disconnected while reading") + return [] # if nothing was recieved if not data: