command: Fix except declaration for get_comedi_cmd_pointer
[pycomedi.git] / pycomedi / utility.py
index 166eafb4842ae8a6392377cfbf5119749284cfc0..a6c80111dde17f89b12bbeb8076b63fdee7e1c61 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2011-2012 W. Trevor King <wking@drexel.edu>
+# Copyright (C) 2011-2012 W. Trevor King <wking@tremily.us>
 #
 # This file is part of pycomedi.
 #
@@ -81,12 +81,14 @@ def _builtin_array(array):
 
 class _ReadWriteThread (_threading.Thread):
     "Base class for all reader/writer threads"
-    def __init__(self, subdevice, buffer, name=None):
+    def __init__(self, subdevice, buffer, name=None,
+                 block_while_running=False):
         if name == None:
             name = '<%s subdevice %d>' % (
                 self.__class__.__name__, subdevice.index)
         self.subdevice = subdevice
         self.buffer = buffer
+        self.block_while_running = block_while_running
         self._setup_buffer()
         super(_ReadWriteThread, self).__init__(name=name)
 
@@ -103,6 +105,11 @@ class _ReadWriteThread (_threading.Thread):
         """
         return self.subdevice.device.file
 
+    def block(self):
+        while self.subdevice.get_flags().running:
+            _time.sleep(0)
+        self.subdevice.cancel()  # become unbusy
+
 
 class Reader (_ReadWriteThread):
     """`read()`-based reader
@@ -180,12 +187,95 @@ class Reader (_ReadWriteThread):
                 shape=self.buffer.shape, dtype=self.buffer.dtype,
                 buffer=buf)
             self.buffer[:] = a
-        #_LOG.critical('ai running? %s' % self.subdevice.get_flags().running)
-        #while self.subdevice.get_flags().running:
-            ##_LOG.critical('ai running? %s' % self.subdevice.get_flags().running)
-        #    _time.sleep(0)
-        #_LOG.critical('ai running? %s' % self.subdevice.get_flags().running)
-        #_time.sleep(1)
+        if self.block_while_running:
+            self.block()
+
+
+class CallbackReader (Reader):
+    """`read()`-based reader with callbacks
+
+    Examples
+    --------
+
+    Setup a temporary data file for testing.
+
+    >>> from os import close, remove
+    >>> from sys import stdout
+    >>> from tempfile import mkstemp
+    >>> from time import sleep
+    >>> fd,t = mkstemp(suffix='.dat', prefix='pycomedi-')
+    >>> f = _os.fdopen(fd, 'rb+')
+    >>> buf = _numpy.array([[0,10],[1,11],[2,12]], dtype=_numpy.uint16)
+    >>> buf.tofile(t)
+
+    Override the default `Reader` methods for our dummy subdevice.
+
+    >>> class TestReader (CallbackReader):
+    ...     def _file(self):
+    ...         return f
+
+    Define a callback function.
+
+    >>> def callback(data):
+    ...     sleep(0.1)  # for consistent output spacing
+    ...     print('got: {0}'.format(repr(data)))
+    ...     stdout.flush()
+
+    Run the test reader.
+
+    >>> rbuf = _numpy.zeros((buf.shape[1],), dtype=_numpy.uint16)
+    >>> r = TestReader(subdevice=None, buffer=rbuf, name='Reader-doctest',
+    ...     callback=callback, count=buf.shape[0])
+    >>> r.start()
+    >>> sleep(0.25)
+    got: array([ 0, 10], dtype=uint16)
+    got: array([ 1, 11], dtype=uint16)
+    >>> r.join()
+    got: array([ 2, 12], dtype=uint16)
+
+    While `numpy` arrays make multi-channel indexing easy, they do
+    require an external library.  For single-channel input, the
+    `array` module is sufficient.
+
+    >>> f.seek(0)
+    >>> rbuf = _array.array('H', [0])
+    >>> r = TestReader(subdevice=None, buffer=rbuf, name='Reader-doctest',
+    ...     callback=callback, count=buf.size)
+    >>> r.start()
+    >>> sleep(0.35)
+    got: array('H', [0])
+    got: array('H', [10])
+    got: array('H', [1])
+    >>> r.join()
+    got: array('H', [11])
+    got: array('H', [2])
+    got: array('H', [12])
+
+    Cleanup the temporary data file.
+
+    >>> f.close()  # no need for `close(fd)`
+    >>> remove(t)
+    """
+    def __init__(self, callback=None, count=None, **kwargs):
+        self.callback = callback
+        self.count = count
+        super(CallbackReader, self).__init__(**kwargs)
+
+    def run(self):
+        count = self.count
+        block_while_running = self.block_while_running
+        while count is None or count > 0:
+            if count is not None:
+                count -= 1
+            try:
+                self.block_while_running = False
+                super(CallbackReader, self).run()
+            finally:
+                self.block_while_running = block_while_running
+            if self.callback:
+                self.callback(self.buffer)
+        if self.block_while_running:
+            self.block()
 
 
 class Writer (_ReadWriteThread):
@@ -266,16 +356,12 @@ class Writer (_ReadWriteThread):
         f = self._file()
         remaining_buffer.tofile(f)
         f.flush()
-        #_LOG.critical('ao running? %s' % self.subdevice.get_flags().running)
-        #while self.subdevice.get_flags().running:
-            ##_LOG.critical('ao running? %s' % self.subdevice.get_flags().running)
-            #_time.sleep(0)
-        #_LOG.critical('ao running? %s' % self.subdevice.get_flags().running)
-        #_time.sleep(1)
+        if self.block_while_running:
+            self.block()
 
 
 class _MMapReadWriteThread (_ReadWriteThread):
-    "`mmap()`-based reader/wrtier"
+    "`mmap()`-based reader/writer"
     def __init__(self, *args, **kwargs):
         preload = kwargs.pop('preload', 0)
         access = kwargs.pop('access')
@@ -325,6 +411,8 @@ class _MMapReadWriteThread (_ReadWriteThread):
                 remaining -= action
             else:
                 _time.sleep(sleep_time)
+        if self.block_while_running:
+            self.block()
 
     def _act(self, mmap, mmap_offset, buffer_offset, remaining, mmap_size,
              action_bytes=None, builtin_array=None):
@@ -350,7 +438,7 @@ class _MMapReadWriteThread (_ReadWriteThread):
         return self.subdevice.get_buffer_size()
 
     def _fileno(self):
-        return self.subdevice._device.fileno()
+        return self.subdevice.device.fileno()
 
     def _action_bytes(self):
         return self.subdevice.get_buffer_contents()