pygpibtoolkit/pygpib.py

Fri, 05 Sep 2008 00:25:19 +0200

author
David Douard <david.douard@logilab.fr>
date
Fri, 05 Sep 2008 00:25:19 +0200
changeset 78
b73a9d9e45ec
parent 66
2a97995628a3
child 79
b8eec4f9db52
permissions
-rw-r--r--

Add an option to qgpib_plotter to make it auto display any newly received plot (+ fix in plot list management)

# 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

gpib: create serial connection to GPIB-USB device (Prologix is the
only supported device for now).
"""
import serial
from serial.serialutil import SerialException
import time
from pygpibtoolkit.tools import AbstractRegister 
import operator

class ConnectionError(Exception):
    pass

class Condition(object):
    def __call__(self, device):
        return True
    def __and__(self, cond):
        assert isinstance(cond, Condition)
        return _ComposedCondition(operator.__and__, self, cond)
    def __or__(self, cond):
        assert isinstance(cond, Condition)
        return _ComposedCondition(operator.__or__, self, cond)
    
class _ComposedCondition(Condition):
    def __init__(self, op, *args):
        self._conditions = list(args[:])
        self._reduc_op = op 
        for cond in args:
            assert isinstance(cond, Condition)
            if isinstance(cond, _ComposedCondition) and cond._reduc_op == op:
                i = self._conditions.index(cond)
                self._conditions[i:i+1] = cond._conditions
    def __call__(self, device):
        return reduce(self._reduc_op, [f(device) for f in self._conditions])
    
class Constants(object):
    def __init__(self):
        self.constants = {}
        self.descriptions = {}
        self.rev_constants = {}
        for v, k, m in self._constants:
            self.k = v
            self.constants[v] = k
            self.rev_constants[k] = v
            self.descriptions[v] = m
            
    def __getitem__(self, k):
        if isinstance(k, basestring):
            return self.rev_constants[k]
        else:
            return self.constants[k]

    def get_description(self, k):
        if isinstance(k, basestring):
            k = self.rev_constants[k]
        return self.descriptions[k]

class MODE(Constants):    
    _constants = [(1, "CONTROLLER", "Set device as Controller in Charge"),
                  (0, "DEVICE", "Set device as simple listener"),
                  ]

class ModeCommand(object):
    def __init__(self, description, name, condition=None):
        self.name = name
        self.description = description
        self.condition = condition
    
class AbstractCommand(object):
    """
    Base class for HPIB command descritption.

    This is actually a attribute descriptor, which should have
    AbstractGPIBDevice derived classes as owner.
    """
    _readonly = True
    _init_value = None
    
    def __get__(self, instance, owner):
        if instance is None:
            return self
        return instance._get(self.__class__.__name__)

    def __set__(self, instance, value):
        if instance is None:
            return self
        return instance._set(self.__class__.__name__, value)
    
    def get_value_from_device(self, device):
        cmd = self.build_get_cmd()
        value = device.send_command(cmd)
        value = self.convert_from(value)
        return value

    def send_value_to_device(self, device, value):
        cmd, value = self.build_set_cmd(value)
        res = device.send_command(cmd)
        return res, self.convert_to(value)
    
    def build_get_cmd(self):
        return self.__class__.__name__

    def build_set_cmd(self, *value):
        raise ValueError, "Can't set value for command '%s'"%self.__class__.__name__
    
    def convert_from(self, *value):
        return None

    def convert_to(self, *value):
        return None

    
#XXX TODO: remove this
class Command(AbstractCommand):
    pass

class AbstractValue(Command):
    _readonly = False

    def build_get_cmd(self):
        return self.__class__.__name__ + "?"

    def build_set_cmd(self, *value):
        value = self.convert_to(value)
        cmd = "%s %s"%(self.__class__.__name__, value)
        return cmd, value
    
    def convert_to(self, *value):
        if value:
            return str(value[0])
        return ""
    
    def convert_from(self, *value):
        if value:
            return self._type(value[0].strip())
        return None
    
class BoolValue(AbstractValue):
    _type = bool
    def convert_from(self, *value):
        if value:
            return value[0] and value[0].lower() in ['1','true','yes','on']
        return False
    
    def convert_to(self, *value):
        if value:
            return  value[0] and "1" or "0"
        return "" # XXX is it correct?
    
class IntValue(AbstractValue):
    _type = int
    def convert_from(self, *value):
        if value:
            # int is resturned as a string representing a float 
            return self._type(float(value[0].strip()))
        return None

class flag(int):
    def __new__(cls, val, constants):
        val = int.__new__(cls, val)
        val._constants = constants
        for v, name, desc in constants:
            if name not in ['', 'N/A']:
                setattr(val, name, v)
        return val
    
    def __str__(self):
        return '%s <' + '|'.join([x[1] for x in self._constants if x[0]&self]) + ">"
    def flags(self):
        return [x[1] for x in self._constants if x[0] & (self and x[1]) not in ['','N/A']]
        
    def descriptions(self):
        return [x[2] for x in self._constants if (x[0] & self) and x[1] not in ['','N/A']]

class Flag(IntValue):
    _readonly = True
    _constants = []
    _type = flag
    def convert_from(self, *value):
        if value:
            return self._type(float(value[0].strip()), _constants)
        return None
    
class FloatValue(AbstractValue):
    _type = float

class PercentageValue(FloatValue):
    pass #TODO

class FloatUnitValue(FloatValue):
    """
    A Float value with unit (frequency, etc.)
    """
    def convert_to(self, *value):
        if value:
            if len(value)==1:
                if isinstance(value[0], basestring):
                    value = value[0].strip().split()
                    if len(value)==1:
                        freq = float(value)
                        unit = self._units[0]
                    elif len(value)==2:
                        freq = float(value[0])
                        unit = value[1]
                    else:
                        raise ValueError, "Can't interpret %s as a %s"%(repr(value), self._name)
                else:
                    freq = float(value[0])
                    unit = self._units[0]
            elif len(value)==2:
                freq = float(value[0])
                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
            return "%s%s"%(freq, unit)
        return "" # XXX is it correct?

class FrequencyValue(FloatUnitValue):
    _units = ['Hz','kHz','mHz',]
    _name = "frequency"

class VoltageValue(FloatUnitValue):
    _units = ['V','mV',]
    _name = "voltage"
    
class DurationValue(FloatUnitValue):
    _units = ['sec','msec','usec','min']
    _name = "duration"

class StringValue(AbstractValue):
    _type = str
    def convert_to(self, *value):
        if value:
            return "'%s'"%str(value[0])
        return "''"

class EnumValue(StringValue):
    _values = []
    def convert_to(self, *value):
        if value:
            assert value[0] in self._values
            return "%s"%self._values.index(value[0])
        return "" # XXX

class Mode(AbstractValue):
    _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):
#     # IEEE 488.2 Status Byte constants
#     MAV = 0x10 # Message AVailable: bit 4 of the Status Byte
#     ESB = 0x20 # Event Status Bit: bit 5 of the Status Byte
#     MSS = 0x40 # Master Summary Status bit: bit 6 of the Status Byte (NOT
#                # sent in response to a serial poll)
#     RQS = 0x40 # Request Service: bit 6 of the Status Byte (when sent in
#                # response to a serial poll)
# class SESR(Constants):
#     # SESR constants (Standard Event Status Register)
#     PON = 0x80 # Power On: Power has been turned On since last register
#                # read access
#     URQ = 0x40 # User Request: the user has activated some device control
#                # (whatever the Remote Local state is)
#     CME = 0x20 # Command Error
#     EXE = 0x10 # Execution Error
#     DDE = 0x08 # Device Dependant Error
#     QYE = 0x04 # QuerY Error (attempt to read data while Output Queue is
#                # empty, or data in the OQ was lost)
#     RQC = 0x02 # Request Control: tell the CiC that the device wants to
#                # become the CiC
#     OPC = 0x01 # Operation Complete: device has completed any pending
#                # operation (ready to accept new commands). This bit is
#                # generated in response to a OPC command.
    

mercurial