# HG changeset patch # User David Douard # Date 1237332387 -3600 # Node ID b8eec4f9db5258117edd6143e58af1a24609c725 # Parent b73a9d9e45ecd66e060dc9b60e7ab7d0c785c57b 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) diff -r b73a9d9e45ec -r b8eec4f9db52 bin/3456logger.py --- /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() diff -r b73a9d9e45ec -r b8eec4f9db52 bin/gpib_detect --- 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() diff -r b73a9d9e45ec -r b8eec4f9db52 pygpibtoolkit/HP3456.py --- /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"^(?PHP3456A)$",] + _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) diff -r b73a9d9e45ec -r b8eec4f9db52 pygpibtoolkit/HP3562A/HP356X.py --- 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"^(?PHP *356[23][ABCD]).*$",] _idn = "ID?" def manage_srq(self, statusbyte): print "Managing SRQ", statusbyte diff -r b73a9d9e45ec -r b8eec4f9db52 pygpibtoolkit/HP3562A/q3562A.py --- 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)] diff -r b73a9d9e45ec -r b8eec4f9db52 pygpibtoolkit/HP3562A/test/test_HP3562A.py --- /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() == {}) diff -r b73a9d9e45ec -r b8eec4f9db52 pygpibtoolkit/HP8904.py --- /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 = ["^(?PHP *8904A).*$",] + _idn = "ID" + + def __init__(self, idn, address, controller): + super(HP8904Device, self).__init__(idn, address, controller) + + +deviceRegister.register_manager(HP8904Device) diff -r b73a9d9e45ec -r b8eec4f9db52 pygpibtoolkit/gpibcontroller.py --- 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 = "[-]?[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): diff -r b73a9d9e45ec -r b8eec4f9db52 pygpibtoolkit/test/mockupdevice.py --- /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)