Skip to content

Commit

Permalink
Better Async management
Browse files Browse the repository at this point in the history
Manage connection drop while Async is active without throwing an
exception.

Add delay_cmds argument to Async.

Revised documentation.
  • Loading branch information
Ircama committed Dec 29, 2018
1 parent 64109ec commit 684a5e4
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 6 deletions.
19 changes: 18 additions & 1 deletion docs/Async Connections.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand Down
9 changes: 8 additions & 1 deletion obd/asynchronous.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,15 @@ 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
self.__callbacks = {} # key = OBDCommand, value = list of Functions
self.__thread = None
self.__running = False
self.__was_running = False # used with __enter__() and __exit__()
self.__delay_cmds = delay_cmds


@property
Expand Down Expand Up @@ -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)
Expand All @@ -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
22 changes: 18 additions & 4 deletions obd/elm327.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand All @@ -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:
Expand Down

0 comments on commit 684a5e4

Please sign in to comment.