many improvements:

Wed, 18 Mar 2009 00:26:27 +0100

author
David Douard <david.douard@logilab.fr>
date
Wed, 18 Mar 2009 00:26:27 +0100
changeset 79
b8eec4f9db52
parent 78
b73a9d9e45ec
child 80
89e31bd2524b

many improvements:
- in gpibcontroler system (it begins to work fine),
- add HP3456A description module,
- add an almost empty HP8904A description module,
- add forgotten files (tests, mockups)

bin/3456logger.py file | annotate | diff | comparison | revisions
bin/gpib_detect file | annotate | diff | comparison | revisions
pygpibtoolkit/HP3456.py file | annotate | diff | comparison | revisions
pygpibtoolkit/HP3562A/HP356X.py file | annotate | diff | comparison | revisions
pygpibtoolkit/HP3562A/q3562A.py file | annotate | diff | comparison | revisions
pygpibtoolkit/HP3562A/test/test_HP3562A.py file | annotate | diff | comparison | revisions
pygpibtoolkit/HP8904.py file | annotate | diff | comparison | revisions
pygpibtoolkit/gpibcontroller.py file | annotate | diff | comparison | revisions
pygpibtoolkit/gpibmockup.py file | annotate | diff | comparison | revisions
pygpibtoolkit/prologix.py file | annotate | diff | comparison | revisions
pygpibtoolkit/pygpib.py file | annotate | diff | comparison | revisions
pygpibtoolkit/test/mockupdevice.py file | annotate | diff | comparison | revisions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/3456logger.py	Wed Mar 18 00:26:27 2009 +0100
@@ -0,0 +1,45 @@
+#!/usr/bin/python
+import sys
+import os
+import signal
+import time
+import optparse
+
+try:
+    from pygpibtoolkit.gpibcontroller import GPIBController, deviceRegister
+except ImportError:
+    sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
+    from pygpibtoolkit.gpibcontroller import GPIBController, deviceRegister
+
+import pygpibtoolkit.HP3562A
+import pygpibtoolkit.HP3456
+
+from pygpibtoolkit.prologix import GPIB
+
+opt = optparse.OptionParser("A simple tool for detecting connected GPIB devices")
+opt.add_option('-d', '--device', default="/dev/ttyUSB0",
+               dest="device",
+               help="Device of connected Prologix GPIB bundle [/dev/ttyUSB0]",)
+options, argv = opt.parse_args(sys.argv)
+
+
+cnx = GPIB(device=options.device)
+c = GPIBController(cnx)
+
+m = c.register_device(24, "HP3456A")
+
+def cb(val):
+    print val
+    
+m.register_data_cb(cb)
+m.send_command('SM004')
+m.send_command('T1SO1')
+
+try:
+    while True:
+        time.sleep(0.1)
+except KeyboardInterrupt:
+    pass
+
+m.send_command('SO0')
+c.stop()
--- a/bin/gpib_detect	Fri Sep 05 00:25:19 2008 +0200
+++ b/bin/gpib_detect	Wed Mar 18 00:26:27 2009 +0100
@@ -10,7 +10,7 @@
     sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
     from pygpibtoolkit.gpibcontroller import GPIBController
 import pygpibtoolkit.HP3562A
-
+from pygpibtoolkit.prologix import GPIB
 def main():
     import optparse
     opt = optparse.OptionParser("A simple tool for detecting connected GPIB devices")
@@ -20,7 +20,9 @@
     options, argv = opt.parse_args(sys.argv)
 
     print "Detecting GPIB devices on the bus. Please wait until completion."
-    c = GPIBController(device=options.device)
+    cnx = GPIB(device=options.device)
+    c = GPIBController(cnx)
+    
     signal.signal(signal.SIGINT, c.stop)
     signal.signal(signal.SIGQUIT, c.stop)
     
@@ -31,7 +33,7 @@
     print "GPIB devices:"
     for k in sorted(devices.keys()):
         print "%-3d: %s"%(k, devices[k])
-    
+    return c, devices
 if __name__ == "__main__":
-    main()
+    c, dev = main()
     
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pygpibtoolkit/HP3456.py	Wed Mar 18 00:26:27 2009 +0100
@@ -0,0 +1,238 @@
+# -*- coding: utf-8 -*-
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+""" Copyright (c) 2007-2009 David Douard (Paris, FRANCE).
+http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr
+
+"""
+import mx.DateTime as dt
+
+from pygpibtoolkit.pygpib import Constants, Command
+from pygpibtoolkit.pygpib import Mode, ModeCommand
+from pygpibtoolkit.pygpib import WriteOnlyMode
+from pygpibtoolkit.pygpib import BoolValue, FloatValue
+from pygpibtoolkit.gpibcontroller import AbstractGPIBDevice, deviceRegister
+
+
+class Function(WriteOnlyMode):
+    "Function"
+    _init_value = "S0F1"
+    
+    #S0 = ModeCommand("Unshift", "S0")
+    #S1 = ModeCommand("Shift", "S1")
+    
+    S0F1 = ModeCommand("DC Voltage", "DCV")
+    S0F2 = ModeCommand("AC Voltage", "ACV")
+    S0F3 = ModeCommand("AC + DC Voltage", "ACV+DCV")
+    S0F4 = ModeCommand("2 wire Ohm", "OHM2")
+    S0F5 = ModeCommand("4 wire Ohm", "OHM4")
+
+    S1F1 = ModeCommand("DC/DC Voltage Ratio", "DCV/DCV")
+    S1F2 = ModeCommand("AC/DC Voltage Ratio", "ACV/DCV")
+    S1F3 = ModeCommand("(AC + DC)/DC Voltage Ratio", "ACV+DCV/DCV")
+    S1F4 = ModeCommand("O.C. 2 wire Ohm", "OCO2")
+    S1F5 = ModeCommand("O.C. 4 wire Ohm", "OCO4")
+    
+
+class Range(WriteOnlyMode):
+    "Range"
+    _init_value = "R1"
+    
+    R1 = ModeCommand("Auto", "AUTO")
+    R2 = ModeCommand("0.1", "0.1")
+    R3 = ModeCommand("1", "1")
+    R4 = ModeCommand("10", "10")
+    R5 = ModeCommand("100", "100")
+    R6 = ModeCommand("1E3", "1E3")
+    R7 = ModeCommand("10E3", "10E3")
+    R8 = ModeCommand("100E3", "100E3")
+    R9 = ModeCommand("1E6", "1E6")
+
+class Trigger(WriteOnlyMode):
+    "Trigger"
+    _init_value = "T1"
+    
+    T1 = ModeCommand("Internat", "INT")
+    T2 = ModeCommand("External", "EXT")
+    T3 = ModeCommand("Single", "SGL")
+    T4 = ModeCommand("Hold", "HLD")
+
+class Math(WriteOnlyMode):
+    "Math"
+    _init_value = "M0"
+
+    M0 = ModeCommand("Off", "OFF")
+    M1 = ModeCommand("Pass/Fail", "P/F")
+    M2 = ModeCommand("Statistics", "STAT")
+    M3 = ModeCommand("Null", "NULL")
+    M4 = ModeCommand("dBm", "DBM")
+    M5 = ModeCommand(u"Thermistor (°F)", "TH_F")
+    M6 = ModeCommand(u"Thermistor (°C)", "TH_C")
+    M7 = ModeCommand("Scale", "")
+    M8 = ModeCommand("% Error", "")
+    M9 = ModeCommand("dB", "")
+    
+class AutoZero(WriteOnlyMode):
+    "Auto Zero"
+    _init_value = "Z1"
+
+    Z0 = ModeCommand("Off", "OFF")
+    Z1 = ModeCommand("On", "ON")
+
+class Filter(WriteOnlyMode):
+    "Filter"
+    _init_value = "FL0"
+
+    FL0 = ModeCommand("Off", "OFF")
+    FL1 = ModeCommand("On", "ON")
+
+class Test(WriteOnlyMode):
+    "Test"
+    _init_value = "TE0"
+
+    TE0 = ModeCommand("Off", "OFF")
+    TE1 = ModeCommand("On", "ON")
+
+class ReadingStorage(WriteOnlyMode):
+    "Reading Storage"
+    _init_value = "RS0"
+
+    RS0 = ModeCommand("Off", "OFF")
+    RS1 = ModeCommand("On", "ON")
+    
+class SystemOutputMode(WriteOnlyMode):
+    "System Output Mode"
+    _init_value = "SO0"
+
+    SO0 = ModeCommand("Off", "OFF")
+    SO1 = ModeCommand("On", "ON")
+
+class Display(WriteOnlyMode):
+    "Display"
+    _init_value = "D1"
+
+    D0 = ModeCommand("Off", "OFF")
+    D1 = ModeCommand("On", "ON")
+
+class OutputFormat(WriteOnlyMode):
+    "Output Format"
+    _init_value = "P0"
+
+    P0 = ModeCommand("ASCII", "ASC")
+    P1 = ModeCommand("Packed Format", "PFOR")
+
+class EOI(WriteOnlyMode):
+    "EOI"
+    _init_value = "O1"
+    
+    O0 = ModeCommand("Disable", "OFF")
+    O1 = ModeCommand("Enable", "ON")
+
+class SW1(BoolValue):
+    _readonly = True
+    def convert_from(self, *value):
+        if value:
+            return  int(value[0]) and "Front" or "Rear"
+        return "?"
+
+class H(Command):
+    "HOME - Software Reset"
+
+class CL1(Command):
+    "CLEAR CONTINUE - Active"
+
+class _Register(FloatValue):
+    _readonly = False
+    _cached = False
+    def build_get_cmd(self):
+        return "RE"+self.__class__.__name__
+
+    def build_set_cmd(self, *value):
+        value = self.convert_to(*value)
+        cmd = "%sST%s"%(value, self.__class__.__name__)
+        return cmd, value
+
+class N(_Register):
+    "Number of readings"
+
+class G(_Register):
+    "Number of displayed digits"
+
+class I(_Register):
+    "Number of power line cycles int."
+
+class D(_Register):
+    "Delay"
+
+class M(_Register):
+    "Mean"
+    _readonly = True
+    
+class V(_Register):
+    "Variance"
+    _readonly = True
+
+class C(_Register):
+    "Count"
+    _readonly = True
+    
+class L(_Register):
+    "Lower"
+    
+class R(_Register):
+    "R"
+
+class U(_Register):
+    "Upper"
+    
+class Y(_Register):
+    "Y"
+    
+class Z(_Register):
+    "Z"
+
+    
+    
+class HP3456Device(AbstractGPIBDevice):
+    _accepts = [r"^(?P<model>HP3456A)$",]
+    _idn = None # cannot ask device its model number or version
+
+    def __init__(self, idn, address, controller):
+        super(HP3456Device, self).__init__(idn, address, controller)
+        self.reset_measures()
+        self._data_cb = None
+
+    def manage_srq(self, statusbyte):
+        #print "Managing SRQ", statusbyte
+        if statusbyte & 0x04:
+            # data ready, read the value on the GPIB bus
+            self._controller.send_command(self._address, '', cb=self.data_cb)
+
+    def data_cb(self, data):
+        if data:
+            try:
+                data = float(data)
+                self.measures.append((dt.now(), data))
+                if self._data_cb:
+                    self._data_cb(self.measures[-1])
+            except:
+                pass
+
+    def reset_measures(self):
+        self.measures = []
+
+    def register_data_cb(self, cb):
+        self._data_cb = cb
+
+        
+deviceRegister.register_manager(HP3456Device)
--- a/pygpibtoolkit/HP3562A/HP356X.py	Fri Sep 05 00:25:19 2008 +0200
+++ b/pygpibtoolkit/HP3562A/HP356X.py	Wed Mar 18 00:26:27 2009 +0100
@@ -344,7 +344,7 @@
     "SACR - Send Auto Carrier"
     
 class HP356XDevice(AbstractGPIBDevice):
-    _accepts = ["HP3562A", "HP3563A"]
+    _accepts = [ r"^(?P<model>HP *356[23][ABCD]).*$",]
     _idn = "ID?"
     def manage_srq(self, statusbyte):
         print "Managing SRQ", statusbyte
--- a/pygpibtoolkit/HP3562A/q3562A.py	Fri Sep 05 00:25:19 2008 +0200
+++ b/pygpibtoolkit/HP3562A/q3562A.py	Wed Mar 18 00:26:27 2009 +0100
@@ -22,10 +22,8 @@
 from PyQt4 import QtGui, QtCore, uic
 from PyQt4.QtCore import SIGNAL, Qt
 
-if "-m" in sys.argv:
-    from dump_datablock_mockup import HP3562dumper
-else: 
-    from dump_datablock import HP3562dumper
+# WARNING, may be "replaced" by mockup
+from dump_datablock import HP3562dumper
 
 from pygpibtoolkit.qt4.qpreferences import BaseItem, IntItem, UnicodeItem, ColorItem
 from pygpibtoolkit.qt4.qpreferences import PointItem, SizeItem, ByteArrayItem
@@ -481,6 +479,11 @@
     
     options, argv = opt.parse_args(sys.argv)
 
+    if options.mockup:
+        # overwrite "normal" module
+        global HP3562dumper
+        from dump_datablock_mockup import HP3562dumper
+
     a = QtGui.QApplication(argv)
     w = Qt3562()
     files = [f for f in argv[1:] if os.path.isfile(f)]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pygpibtoolkit/HP3562A/test/test_HP3562A.py	Wed Mar 18 00:26:27 2009 +0100
@@ -0,0 +1,11 @@
+#
+from  logilab.common.testlib import TestCase, InnerTest, unittest_main
+
+from pygpibtoolkit.HP3562A.dump_datablock_mockup import HP3562dumper
+from pygpibtoolkit.gpibcontroller import GPIBController
+from pygpibtoolkit.gpibmockup import GPIB
+
+class TestHP3562ADeviceManager(TestCase):
+    def test_device(self):
+        c = GPIBController(GPIB())
+        self.failUnless(c.detect_devices() == {})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pygpibtoolkit/HP8904.py	Wed Mar 18 00:26:27 2009 +0100
@@ -0,0 +1,34 @@
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+""" Copyright (c) 2007-2009 David Douard (Paris, FRANCE).
+http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr
+
+"""
+import mx.DateTime as dt
+
+from pygpibtoolkit.pygpib import Constants, Command
+from pygpibtoolkit.pygpib import Mode, ModeCommand
+from pygpibtoolkit.pygpib import WriteOnlyMode
+from pygpibtoolkit.gpibcontroller import AbstractGPIBDevice, deviceRegister
+
+
+    
+class HP8904Device(AbstractGPIBDevice):
+    _accepts = ["^(?P<model>HP *8904A).*$",]
+    _idn = "ID"
+
+    def __init__(self, idn, address, controller):
+        super(HP8904Device, self).__init__(idn, address, controller)
+
+        
+deviceRegister.register_manager(HP8904Device)
--- a/pygpibtoolkit/gpibcontroller.py	Fri Sep 05 00:25:19 2008 +0200
+++ b/pygpibtoolkit/gpibcontroller.py	Wed Mar 18 00:26:27 2009 +0100
@@ -20,6 +20,7 @@
 import sys
 import threading
 import time
+import re
 from inspect import isclass
 
 from serial.serialutil import SerialException
@@ -40,7 +41,17 @@
             if not pname.startswith('_') and isclass(param) and \
                     issubclass(param, AbstractCommand) and \
                     param.__module__ == modname:
-                setattr(new_cls, pname, param())
+                p = param()
+                setattr(new_cls, pname, p)
+                for subattr in p._cmds:
+                    def getter(self, cmd=subattr):
+                        print "self=",self
+                        ret = self.send_command(cmd)
+                        if not ret:
+                            ret = ""
+                        return ret
+                    setattr(new_cls, subattr, property(getter))
+                    
         return new_cls
 
 class AbstractGPIBDevice(object):
@@ -49,7 +60,12 @@
 
     @classmethod
     def accepts(cls, idn):
-        return idn in cls._accepts
+        for regex in cls._accepts:
+            m = re.match(regex, idn)
+            if m:
+                return m.group('model')
+        return False
+        #return idn in cls._accepts
     
     def __init__(self, idn, address, controller):
         self._idn = idn
@@ -59,8 +75,9 @@
         
         self._cache = {}
         for pname, param in self.__class__.__dict__.items():
-            if isinstance(param, AbstractCommand) and not param._readonly:
+            if isinstance(param, AbstractCommand) and not param._readonly and param._cached:
                 self._cache[pname] = param._init_value
+
     def use_cache(self, use=None):
         if use is None:
             return self._use_cache
@@ -88,20 +105,34 @@
         pass
 
     def send_command(self, cmd):
-        return self._controller.send_command(self._address, cmd).strip()
+        ret = self._controller.send_command(self._address, cmd)
+        if ret:
+            ret = ret.strip()
+        return ret
+
+    def read_data(self):
+        """
+        Read currently available DATA in buffer
+        """
+        return self.send_command('')
+    
     
 class GPIBDeviceRegister(object):
     def __init__(self):
         self._registry = []
+
     def register_manager(self, mgr):
         self._registry.append(mgr)
+
     def get_manager(self, idn):
         for mgr in self._registry:
             if mgr.accepts(idn):
                 return mgr
         return None
+
     def get_idn_cmds(self):
-        return [mgr._idn for mgr in self._registry]
+        return [mgr._idn for mgr in self._registry if mgr._idn]
+
     def __str__(self):
         msg = "<GPIBDeviceRegister: %s managers\n"%len(self._registry)
         for mgr in self._registry:
@@ -133,15 +164,13 @@
     ready.)
     
     """
-    def __init__(self, device="/dev/ttyUSB0", controller_address=21):
+    def __init__(self, gpibcnx=None, controller_address=21):
         self._address = controller_address
-        try:
-            self._cnx = GPIB(device)
-        except SerialException:
-            self._cnx = None
+        self._cnx = gpibcnx
         self._devices = {}
+        self._debug = False
         self._setup_threading_system()
-
+        
     def __del__(self):
         print "deleting controller"
         self._stop.set()
@@ -158,6 +187,7 @@
         self._cnx_thread = threading.Thread(name="GPIBConnectionThread",
                                             target=self._cnx_loop)
         self._cnx_thread.start()
+        self._suspended = False
         self._loop_interrupter.set()
         
     def _check_srq(self):
@@ -166,18 +196,20 @@
             polled = self._cnx.poll(addrs)
             for add in addrs:
                 if polled[add] & 0x40:
-                    print "device %s (#%s) requested attention"%(self._devices[add]._idn, add)
+                    #print "device %s (#%s) requested attention"%(self._devices[add]._idn, add)
                     # TODO: check what to do...
                     self._devices[add].manage_srq(polled[add])
         
     def _cnx_loop(self):
         while 1:
-            #sys.stderr.write('.')
+            if self._debug:
+                sys.stderr.write('.')
             if self._stop.isSet():
                 print "_stop set, exiting"
                 return
             while not self._loop_interrupter.isSet():
-                #sys.stderr.write('|')
+                if self._debug:
+                    sys.stderr.write('|')
                 self._loop_interrupter.wait()
             self._loop_interrupter.clear()
             # first we check for SRQ and dispatch its management
@@ -213,10 +245,29 @@
 
     def stop(self, *args):
         self._stop.set()
+
+    def suspend(self, *args):
+        while not self._loop_interrupter.isSet():
+            self._loop_interrupter.wait()
+        print "Suspending main connection loop"
+        self._suspended = True
+        self._loop_interrupter.clear()
+
+    def resume(self, *args):
+        if not self._suspended:
+            return
+        print "Resuming main connection loop"
+        self._suspended = False
+        self._loop_interrupter.set()
     
     def send_command(self, addr, cmd, sync=True, cb=None):
+        #print "SEND CMD %s"%repr(cmd)
         if cb is not None and callable(cb):
             sync = False
+        if sync and self._loop_interrupter.isSet():
+            print "WARNING: send synchronized command when cnx is locked"
+            #sync = False
+        
         self._n_cmds_lock.acquire()
         self._n_cmds += 1
         n_cmd = self._n_cmds
@@ -238,7 +289,7 @@
             resu = self._results_queue.pop(n_cmd)
             cond.release()
             return resu
-        
+    
     def detect_devices(self):
         """
         Perform a Serial Poll on all addresses to detect alive devices
@@ -258,6 +309,7 @@
                         if idn:
                             self.register_device(address, idn)
                             devices[address] = str(idn)
+                            print "registered device", idn
                             break
                         else:
                             # redo a spoll, it should have set the err bit
@@ -304,4 +356,11 @@
         finally:
             self._loop_interrupter.set()
             
-        
+    def get_devicemanager(self, address):
+        return self._devices.get(address)
+
+    def __getitem__(self, address):
+        return self._devices.get(address)
+
+    def keys(self):
+        return self._devices.keys()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pygpibtoolkit/gpibmockup.py	Wed Mar 18 00:26:27 2009 +0100
@@ -0,0 +1,170 @@
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+""" Copyright (c) 2007-2008 David Douard (Paris, FRANCE).
+http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr
+
+Module defining a mockup GPIB communication object.
+
+"""
+
+class BaseMockup(object):
+    _idnstr = "BaseMockup"
+    _idncmd = "*IDN?"
+
+    _cmds = {}
+    
+    def __init__(self):
+        #self._current_cmd = None
+        self._current_data = None
+        
+    def cmd(self, cmd, *args):
+        if cmd == self._idncmd:
+            return self._idnstr
+        try:
+            if ' ' in cmd:
+                cmd, cmdargs = cmd.split(None, 1)
+                args = args + (cmdargs,)
+            _cmd = getattr(self, cmd)
+            self._current_data = _cmd(*args)
+        except AttributeError:
+            if cmd in self._cmds:
+                self._current_data = iter(self._cmds[cmd])
+            else:
+                raise ValueError('Unknown command %s'%repr(cmd))
+        else:
+            if isinstance(self._current_data, basestring):
+                ret = self._current_data
+                self._current_data = None
+                return ret
+            elif self._current_data is None:
+                return None
+            else:
+                try:
+                    return self._current_data.next()
+                except StopIteration:
+                    self._current_data = None
+                    return None        
+        return None
+
+    def read(self):
+        try:
+            return self._current_data.next()
+        except StopIteration:
+            self._current_data = None
+            return None        
+        except:
+            return None
+    
+GPIB_CONTROLLER = 1
+GPIB_DEVICE = 0
+
+class GPIB(object):
+    _retries = 15
+    def __init__(self, address=0, mode=1, **kw):
+        """
+        Create a new mockup GPIB controller.        
+        """
+        self.set_mode(mode)
+        self.set_address(address)
+        self._devices = {}
+
+    def adddevice(self, add, mockup):
+        self._devices[add] = mockup
+            
+    def set_address(self, address, check=True):
+        """
+        Set the address of the GPIB device:
+        
+        - if the device is the Controller In Charge, this is the
+          address of the device commands are sent to,
+
+        - if the device is in GPIB_DEVICE mode, this is its address.
+        """
+        self._address = address
+        
+    def set_mode(self, mode):
+        """
+        Set GPIB device mode to 'mode':
+        
+        - GPIB_CONTROLLER: set the device as the Controller In Charge
+          on the GPIB bus
+
+        - GPIB_DEVICE: set the device as a standard GPIB device on the
+          bus.
+        """
+        self._mode = mode
+
+    def set_controller(self):
+        """
+        Set GPIB device the Controller In Charge on the GPIB bus.
+        """
+        self.set_mode(1)
+
+    def set_device(self):
+        """
+        Set the GPIB device as a simple device on the GPIB bus.        
+        """
+        self.set_mode(0)
+
+    def send_command(self, cmd, address=None):
+        """
+        Send the specified GPIB command on the bus (must be the CIC),
+        and read the answer.
+        Eventually, set the addressed device first.
+        """
+        assert self._mode == 1
+        if address is not None:
+            self.set_address(address)
+
+        if self._address in self._devices:
+            return self._devices[self._address].cmd(cmd)
+        return None
+        
+    def read_eoi(self, address=None):
+        """
+        Read the HPIB buffer from device, till EOI is performed, or timeout.
+        """
+        if address is not None:
+            self.set_address(address)
+        if self._address in self._devices:
+            return "".join(self._devices[self._address].read())
+        ret = ""
+        return ret
+            
+    def check_srq(self):
+        """
+        Check the SRQ line
+        """
+        assert self._mode == 1, "must be the Controller In Charge"
+        return None
+
+    def poll(self, addresses=None):
+        """
+        Poll every address, and return a dictionnary
+         {add: status, ...}        
+        """        
+        assert self._mode == 1, "must be the Controller In Charge"
+        ret = {}
+        if addresses is None:
+            for add, mock in self._devices.items():
+                ret[add] = mock._idnstr
+            return ret
+        return {}
+    
+    def reset(self):
+        """
+        Perform a reset of the USB device
+        
+        """
+        pass
+        
--- a/pygpibtoolkit/prologix.py	Fri Sep 05 00:25:19 2008 +0200
+++ b/pygpibtoolkit/prologix.py	Wed Mar 18 00:26:27 2009 +0100
@@ -19,6 +19,7 @@
 Module defining a communication object to talk to Prologix USB-GPIB controler.
 
 """
+import sys
 import serial
 import time
 
@@ -31,7 +32,7 @@
 class GPIB(object):
     _retries = 15
     def __init__(self, device="/dev/ttyUSB0", baudrate=115200, timeout=0.1,
-                    address=0, mode=1):        
+                 address=0, mode=1):        
         """
         Create a new GPIB controller for the Prologix USB-GPIB device
         located on serial device 'device'.        
@@ -108,7 +109,7 @@
         Read the HPIB buffer from device, till EOI is performed, or timeout.
         """
         if address is not None:
-            self.set_address(address)
+            self.set_address(address, check=False)
         self._cnx.write('++read eoi\r') # idem
         ret = ""
         i = 0
@@ -123,13 +124,24 @@
         Check the SRQ line
         """
         assert self._mode == 1, "must be the Controller In Charge"
+        ret = self._cnx.readline().strip()
+        if ret:
+            print "garbage:", ret
         self._cnx.write('++srq\r')
         ret = self._cnx.readline().strip()
-        if ret:
+        if ret in ["0","1"]:
             return bool(int(ret))
         return None
 
-    def poll(self, addresses=None):
+    def trigger(self, address=None):
+        """
+        Trigger device at 'address'
+        """
+        if address is not None:
+            self.set_address(address, check=False)
+        self._cnx.write('++trg\r') # idem
+        
+    def poll(self, addresses=None, verbose=False):
         """
         Poll every address, and return a dictionnary
          {add: status, ...}        
@@ -143,14 +155,25 @@
             only_one = True
         if len(addresses)==0:
             return None
-        
+
+        if verbose:
+            sys.stderr.write('polling ')
         dico = {}
         for add in addresses:
             self._cnx.write('++spoll %d\r'%add)
+            time.sleep(0.1)
             ret = self._cnx.readline().strip()
             if ret:
+                if verbose:
+                    sys.stderr.write('X')
                 dico[add] = int(ret)
-            time.sleep(0.3) # need to wait at least 150ms (not enough on prologix)
+            else:
+                if verbose:
+                    sys.stderr.write('.')
+                
+            time.sleep(0.30) # need to wait at least 150ms (not enough on prologix)
+        if verbose:
+            sys.stderr.write('\n')
         self.set_address(self._address)
         if only_one and dico:
             return dico.values()[0]
--- a/pygpibtoolkit/pygpib.py	Fri Sep 05 00:25:19 2008 +0200
+++ b/pygpibtoolkit/pygpib.py	Wed Mar 18 00:26:27 2009 +0100
@@ -16,6 +16,7 @@
 gpib: create serial connection to GPIB-USB device (Prologix is the
 only supported device for now).
 """
+import re
 import serial
 from serial.serialutil import SerialException
 import time
@@ -82,13 +83,15 @@
     
 class AbstractCommand(object):
     """
-    Base class for HPIB command descritption.
+    Base class for HPIB command description.
 
-    This is actually a attribute descriptor, which should have
+    This is actually an attribute descriptor, which should have
     AbstractGPIBDevice derived classes as owner.
     """
     _readonly = True
     _init_value = None
+    _cached = True
+    _cmds = []
     
     def __get__(self, instance, owner):
         if instance is None:
@@ -124,9 +127,10 @@
         return None
 
     
-#XXX TODO: remove this
 class Command(AbstractCommand):
-    pass
+    """pure command
+    """
+    
 
 class AbstractValue(Command):
     _readonly = False
@@ -135,7 +139,7 @@
         return self.__class__.__name__ + "?"
 
     def build_set_cmd(self, *value):
-        value = self.convert_to(value)
+        value = self.convert_to(*value)
         cmd = "%s %s"%(self.__class__.__name__, value)
         return cmd, value
     
@@ -209,14 +213,18 @@
         if value:
             if len(value)==1:
                 if isinstance(value[0], basestring):
-                    value = value[0].strip().split()
-                    if len(value)==1:
+                    value = value[0].strip()
+                    reunits = '(?P<unit>'+'|'.join(self._units)+')'
+                    reexpr = r'(?P<value>[-]?[0-9]+([.][0-9]+)?) *' + reunits
+                    m = re.match(reexpr, value)
+                    try:
+                        if m and m.group('unit'):
+                            value = m.group('value')
+                            unit = m.group('unit')
+                        else:
+                            unit = self._units[0]
                         freq = float(value)
-                        unit = self._units[0]
-                    elif len(value)==2:
-                        freq = float(value[0])
-                        unit = value[1]
-                    else:
+                    except Exception, e:
                         raise ValueError, "Can't interpret %s as a %s"%(repr(value), self._name)
                 else:
                     freq = float(value[0])
@@ -226,7 +234,7 @@
                 unit = value[1]
             else:
                 raise ValueError, "Can't interpret %s as a %s"%(repr(value), self._name)
-            assert unit.lower() in self._units, "Unit is not correct (%s)"%unit
+            assert unit in self._units, "Unit is not correct (%s)"%repr(unit)
             return "%s%s"%(freq, unit)
         return "" # XXX is it correct?
 
@@ -257,34 +265,50 @@
             return "%s"%self._values.index(value[0])
         return "" # XXX
 
-class Mode(AbstractValue):
+class WriteOnlyMode(AbstractValue):
+    """
+    A device mode which can only be wrote (there is no way for reading
+    the device mode), thus it must be kept in cache (and we must know
+    the init value mode (the subclass must have defined the
+    '_init_value' class attribute)).
+    """
+    def __init__(self):
+        self._cmds = []
+        for cmdname, cmd in self.__class__.__dict__.items():
+            if isinstance(cmd, ModeCommand):
+                self._cmds.append(cmdname)
+
+    def get_value_from_device(self, device):
+        raise ValueError("can't retrieve mode value from device")
+        
+    def __set__(self, instance, value):
+        if instance is None:
+            return self
+        instance._cache[self.__class__.__name__] = value
+        return getattr(instance, value)
+
+    def __get__(self, instance, owner):
+        if instance is None:
+            return self
+        return instance._get(self.__class__.__name__)
+
+    def build_set_cmd(self, value):
+        assert value in self._cmds
+        return value, value
+
+class Mode(WriteOnlyMode):
     _DATA_BLOCK = None
     _key = None
-    
-    def __init__(self):
-        self._cmds = []
-        for cmdname, cmd in self.__dict__.items():
-            if isinstance(cmd, ModeCommand):
-                self._cmds.append(cmdname)
 
     def get_mode(self, device):
         if self._key and self._DATA_BLOCK:
             mode = getattr(getattr(device, self._DATA_BLOCK), self._key)
             return mode
         return None
-    
+
     def get_value_from_device(self, device):
         value = self.get_mode(device)
         return value
-
-    def __get__(self, instance, owner):
-        if instance is None:
-            return self
-        return instance._get(self.__class__.__name__)
-
-    def build_set_command(self, value):
-        assert value in self._cmds
-        return value
         
 # TODO
 # class STATUS_BYTE(Constants):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pygpibtoolkit/test/mockupdevice.py	Wed Mar 18 00:26:27 2009 +0100
@@ -0,0 +1,28 @@
+from pygpibtoolkit.pygpib import Constants, Command
+from pygpibtoolkit.pygpib import Condition
+from pygpibtoolkit.pygpib import BoolValue, IntValue, FloatValue
+from pygpibtoolkit.pygpib import PercentageValue, FrequencyValue, DurationValue
+from pygpibtoolkit.pygpib import VoltageValue
+from pygpibtoolkit.pygpib import EnumValue, StringValue
+from pygpibtoolkit.pygpib import Mode, ModeCommand, Flag
+
+from pygpibtoolkit.gpibcontroller import AbstractGPIBDevice, deviceRegister
+
+class CH(Mode):
+    A = ModeCommand("A trace","A")
+    B = ModeCommand("A trace","B")
+
+    def get_mode(self, device):
+        return device.send_command('CH')
+
+class FR(FrequencyValue):
+    "FReq"
+
+
+class SimpleMockupDevice(AbstractGPIBDevice):
+    _idn = "*IDN?"
+    _accepts = "Simple Mockup Device"
+    def manage_srq(self, statusbyte):
+        pass
+
+deviceRegister.register_manager(SimpleMockupDevice)

mercurial