niolabs / python-xbee

Python tools for working with XBee radios
MIT License
101 stars 45 forks source link

Serial Reinitialise #27

Closed jamesleesaunders closed 7 years ago

jamesleesaunders commented 7 years ago

This PR modifies base.py, it adds code which will attempt to re-initialise a serial port following a failure, physical unplugging of the USB port or premature closing of the serial port. This means that an XBee thread will continue to run even if the USB port is pulled out and will continually retry (every 10 seconds) to re-initiate the serial port if/when it becomes available again.

A second feature is being added which allows for the 'ser' parameter to be passed either a serial object or a dict of serial settings. For example, it means as well as initiating an XBee/ZigBee object as per current method:

import serial
serialObject = serial.Serial('/dev/ttyUSB0', 9600)
zb = ZigBee(ser=serialObject, callback=receiveMessage, error_callback=xbeeError)

or you can also initiate an XBee/ZigBee object by passing serial settings dict as follows:

serialDict = {'port': XBEE_PORT, 'parity': 'N', 'baudrate': 9600, 'bytesize': 8, 
    'xonxoff': False, 'rtscts': False, 'timeout': None, 'inter_byte_timeout': None, 
    'stopbits': 1, 'dsrdtr': False, 'write_timeout': None}
zb = ZigBee(ser=serialDict, callback=receiveMessage, error_callback=xbeeError)

Both of the above result in the same end, the only difference is that with the second (dict) method base.py initialises the serial object (you will also see serial has been added to the use imports on base.py) where as the first method the serial object must be initialised by the calling code before hand.

Credit must be given to @thom-nic who had the original idea for this and wrote the first part of these changes a few years ago. I have simply progressed his code and brought it up-to-date with the current base.

In order to ensure the modified base.py still passes unit tests I have also modified some of the tests, most notably the 'Fake.py' device. These have been proposed on PR #25 (but you will also see them merged into this PR so as to allow Travis tests to run cleanly). Each should be reviewed under their respective PR's and if PR #25 is approved I will re-base this PR such that only base.py is in the diff.

As well as the unit tests I have also tested this on MacBook and Raspberry Pi testing various combinations of serial port failure (I could not think of a way to write any unit test code which would simulate a serial port failure). There were occasional situations where following unplugging and plugging back in the serial port would not recover (the serial os.path e.g '/dev/ttyUSB0' would not come back until system restart), but I don't know if this is an OS issue more than Python code? I do not know enough about unix/linux UART drivers to diagnose any further. I would love to hear from anyone who may be able to enlighten/improve on this.

I would also be interested to hear if @etherfi has any comments on this PR as I know they was also recently looking at modifying base.py (PR #23) to add timeouts to wait_read_frame() and I think had other enhancements in mind.

jamesleesaunders commented 7 years ago

Hi @hansmosh please will you consider this PR fir inclusion upstream? This is also dependent on PR #25.

PR #25 could be merged on its own as it improves the 'fake serial' test code.

jamesleesaunders commented 7 years ago

Does not seem to have sparked any interest. Will revisit again in the future.