From 7c794fc4986dbff53d51d233dd2d0554c87a51d8 Mon Sep 17 00:00:00 2001 From: Scott Petersen Date: Tue, 7 May 2013 13:31:37 -0700 Subject: [PATCH] Working event implementation and threaded device reads. --- .gitignore | 1 + pyad2usb/__init__.py | 1 + pyad2usb/ad2usb.py | 109 +++++++++++++++++++++++++++++-------- pyad2usb/event/__init__.py | 1 + pyad2usb/event/event.py | 71 ++++++++++++++++++++++++ pyftdi | 1 + test.py | 39 +++++++++++++ 7 files changed, 199 insertions(+), 24 deletions(-) create mode 100644 .gitignore create mode 100644 pyad2usb/event/__init__.py create mode 100644 pyad2usb/event/event.py create mode 120000 pyftdi create mode 100644 test.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/pyad2usb/__init__.py b/pyad2usb/__init__.py index e69de29..55fff94 100644 --- a/pyad2usb/__init__.py +++ b/pyad2usb/__init__.py @@ -0,0 +1 @@ +__all__ = ['AD2USB', 'Device'] diff --git a/pyad2usb/ad2usb.py b/pyad2usb/ad2usb.py index 4c8e563..b647c5c 100644 --- a/pyad2usb/ad2usb.py +++ b/pyad2usb/ad2usb.py @@ -3,37 +3,59 @@ from pyftdi.pyftdi.usbtools import * import time import usb.core import usb.util +from .event import event +import threading + +class NoDeviceError(Exception): + pass class AD2USB(object): - @classmethod - def find_all(cls): - cls.__devices = Device.find_all() + on_test = event.Event('testing') + + on_open = event.Event('Called when the device has been opened') + on_close = event.Event('Called when the device has been closed') + on_read = event.Event('Called when a line has been read from the device') + on_write = event.Event('Called when data has been written to the device') + + __devices = [] - return cls.__devices + @classmethod + def find_all(cls): + cls.__devices = Device.find_all() - def __init__(self): - self._device = None + return cls.__devices - AD2USB.find_all() + def __init__(self): + self._device = None - def __del__(self): - pass + AD2USB.find_all() + pass - def open(self, device=None): - if len(cls.__devices) == 0: - raise NoDeviceError + def __del__(self): + pass - if device is None: - self._device = cls.__devices[0] - else - self._device = device + def open(self, device=None): + if len(self.__devices) == 0: + raise NoDeviceError - self._device.open() + if device is None: + device = self.__devices[0] - def close(self): - self._device.close() - self._device = None + self._device = Device(serial=device[2], description=device[4]) + self._wire_events() + + self._device.open() + + def close(self): + self._device.close() + self._device = None + + def _wire_events(self): + self._device.on_open += self.on_open + self._device.on_close += self.on_close + self._device.on_read += self.on_read + self._device.on_write += self.on_write class Device(object): @@ -41,14 +63,19 @@ class Device(object): FTDI_PRODUCT_ID = 0x6001 BAUDRATE = 115200 + on_open = event.Event('Called when the device has been opened') + on_close = event.Event('Called when the device has been closed') + on_read = event.Event('Called when a line has been read from the device') + on_write = event.Event('Called when data has been written to the device') + @staticmethod def find_all(): devices = [] try: - devices = Ftdi.find_all([(FTDI_VENDOR_ID, FTDI_PRODUCT_ID)], nocache=True) + devices = Ftdi.find_all([(Device.FTDI_VENDOR_ID, Device.FTDI_PRODUCT_ID)], nocache=True) except usb.core.USBError, e: - pass + print e return devices @@ -59,8 +86,13 @@ class Device(object): self._description = description self._buffer = '' self._device = Ftdi() + self._running = False + + self._read_thread = Device.ReadThread(self) def open(self, baudrate=BAUDRATE, interface=0, index=0): + self._running = True + self._device.open(self._vendor_id, self._product_id, interface, @@ -68,24 +100,34 @@ class Device(object): self._serial_number, self._description) - self.device.set_baudrate(baudrate) + self._device.set_baudrate(baudrate) + self._read_thread.start() + + self.on_open((self._serial_number, self._description)) def close(self): try: + self._running = False + self._read_thread.stop() + self._device.close() except FtdiError, e: pass + self.on_close() + def write(self, data): self._device.write_data(data) + self.on_write(data) + def read_line(self, timeout=0.0): start_time = time.time() got_line = False ret = None try: - while 1: + while self._running: buf = self._device.read_data(1) self._buffer += buf @@ -112,4 +154,23 @@ class Device(object): ret = self._buffer self._buffer = '' + self.on_read(ret) + return ret + + class ReadThread(threading.Thread): + def __init__(self, device): + threading.Thread.__init__(self) + self._device = device + self._running = False + + def stop(self): + self._running = False + + def run(self): + self._running = True + + while self._running: + self._device.read_line() + + time.sleep(0.25) diff --git a/pyad2usb/event/__init__.py b/pyad2usb/event/__init__.py new file mode 100644 index 0000000..15302a2 --- /dev/null +++ b/pyad2usb/event/__init__.py @@ -0,0 +1 @@ +__all__ = ['Event'] diff --git a/pyad2usb/event/event.py b/pyad2usb/event/event.py new file mode 100644 index 0000000..8f3d10c --- /dev/null +++ b/pyad2usb/event/event.py @@ -0,0 +1,71 @@ +# event.py (improved) + +class Event(object): + + def __init__(self, doc=None): + self.__doc__ = doc + + def __get__(self, obj, objtype=None): + if obj is None: + return self + return EventHandler(self, obj) + + def __set__(self, obj, value): + pass + + +class EventHandler(object): + + def __init__(self, event, obj): + + self.event = event + self.obj = obj + + def _getfunctionlist(self): + + """(internal use) """ + + try: + eventhandler = self.obj.__eventhandler__ + except AttributeError: + eventhandler = self.obj.__eventhandler__ = {} + return eventhandler.setdefault(self.event, []) + + def add(self, func): + + """Add new event handler function. + + Event handler function must be defined like func(sender, earg). + You can add handler also by using '+=' operator. + """ + + self._getfunctionlist().append(func) + return self + + def remove(self, func): + + """Remove existing event handler function. + + You can remove handler also by using '-=' operator. + """ + + self._getfunctionlist().remove(func) + return self + + def fire(self, earg=None): + + """Fire event and call all handler functions + + You can call EventHandler object itself like e(earg) instead of + e.fire(earg). + """ + + for func in self._getfunctionlist(): + if type(func) == EventHandler: + func.fire(earg) + else: + func(self.obj, earg) + + __iadd__ = add + __isub__ = remove + __call__ = fire diff --git a/pyftdi b/pyftdi new file mode 120000 index 0000000..e58079b --- /dev/null +++ b/pyftdi @@ -0,0 +1 @@ +../../pyftdi/pyftdi/ \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..945a3c7 --- /dev/null +++ b/test.py @@ -0,0 +1,39 @@ +import pyad2usb.ad2usb +import time +import signal + +running = True + +def signal_handler(signal, frame): + global running + + running = False + +def handle_open(sender, args): + print 'opened', args + +def handle_close(sender, args): + print 'closed', args + +def handle_read(sender, args): + print 'read', args + +def handle_write(sender, args): + print 'write', args + +signal.signal(signal.SIGINT, signal_handler) + +#pyad2usb.ad2usb.AD2USB.find_all() + +wut = pyad2usb.ad2usb.AD2USB() +wut.on_open += handle_open +wut.on_close += handle_close +wut.on_read += handle_read +wut.on_write += handle_write + +wut.open() + +while running: + time.sleep(0.1) + +wut.close()