"""
Python Interchangeable Virtual Instrument Library
Copyright (c) 2012-2014 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
# import libraries
import inspect
import numpy as np
import re
from functools import partial
# try importing drivers
# python-vxi11 for LAN instruments
try:
import vxi11
except ImportError:
pass
# python-usbtmc for USBTMC instrument support
try:
import usbtmc
except ImportError:
pass
# linuxgpib wrapper for linux-gpib Gpib class
# for GPIB interfaces
try:
from .interface import linuxgpib
except ImportError:
pass
# pySerial wrapper for serial instrument support
try:
from .interface import pyserial
except ImportError:
pass
# pyvisa wrapper for PyVISA library support
try:
from .interface import pyvisa
except ImportError:
pass
# set to True to try loading PyVISA first before
# other interface libraries
_prefer_pyvisa = False
def get_prefer_pyvisa():
global _prefer_pyvisa
return _prefer_pyvisa
def set_prefer_pyvisa(value=True):
global _prefer_pyvisa
_prefer_pyvisa = bool(value)
# version information
from .version import __version__
version = __version__
# Exceptions
[docs]class IviException(Exception): pass
class IviDriverException(IviException): pass
class FileFormatException(IviDriverException): pass
class IdQueryFailedException(IviDriverException): pass
class InstrumentStatusExcpetion(IviDriverException): pass
class InvalidOptionValueException(IviDriverException): pass
class IOException(IviDriverException): pass
class IOTimeoutException(IviDriverException): pass
class MaxTimeoutExceededException(IviDriverException): pass
class NotInitializedException(IviDriverException): pass
class OperationNotSupportedException(IviDriverException): pass
class OperationPendingException(IviDriverException): pass
class OptionMissingException(IviDriverException): pass
class OptionStringFormatException(IviDriverException): pass
class OutOfRangeException(IviDriverException): pass
class ResetFailedException(IviDriverException): pass
class ResetNotSupportedException(IviDriverException): pass
class SelectorFormatException(IviDriverException): pass
class SelectorHierarchyException(IviDriverException): pass
class SelectorNameException(IviDriverException): pass
class SelectorNameRequiredException(IviDriverException): pass
class SelectorRangeException(IviDriverException): pass
class SimulationStateException(IviDriverException): pass
class TriggerNotSoftwareException(IviDriverException): pass
class UnexpectedResponseException(IviDriverException): pass
class UnknownOptionException(IviDriverException): pass
class UnknownPhysicalNameException(IviDriverException): pass
class ValueNotSupportedException(IviDriverException): pass
def get_index(l, i):
"""Validate index from list or dict of possible values"""
if type(l) is dict:
try:
return l[i]
except KeyError:
if type(i) is int:
raise SelectorRangeException()
raise SelectorNameException()
if i in l:
return l.index(i)
if type(i) == int:
if i < 0 or i >= len(l):
raise SelectorRangeException()
return i
raise SelectorNameException()
def get_index_dict(l):
"""Construct a dict object for faster index lookups"""
d = {}
for i in range(len(l)):
d[l[i]] = i
d[i] = i
return d
[docs]class PropertyCollection(object):
"A building block to create hierarchical trees of methods and properties"
def __init__(self):
d = object.__getattribute__(self, '__dict__')
d.setdefault('_props', dict())
d.setdefault('_docs', dict())
d.setdefault('_locked', False)
[docs] def _add_property(self, name, fget=None, fset=None, fdel=None, doc=None):
"Add a managed property"
d = object.__getattribute__(self, '__dict__')
d['_props'][name] = (fget, fset, fdel)
d['_docs'][name] = doc
d[name] = None
[docs] def _add_method(self, name, f=None, doc=None):
"Add a managed method"
d = object.__getattribute__(self, '__dict__')
d['_docs'][name] = doc
d[name] = f
[docs] def _del_property(self, name):
"Remove managed property or method"
d = object.__getattribute__(self, '__dict__')
del d['_props'][name]
del d['_docs'][name]
del d[name]
[docs] def _lock(self, lock=True):
"Set lock state to prevent creation or deletion of unmanaged members"
d = object.__getattribute__(self, '__dict__')
d['_locked'] = lock
[docs] def _unlock(self):
"Unlock object to allow creation or deletion of unmanaged members, equivalent to _lock(False)"
self._lock(False)
def __getattribute__(self, name):
if name == '__dict__':
return object.__getattribute__(self, name)
d = object.__getattribute__(self, '__dict__')
d.setdefault('_props', dict())
d.setdefault('_locked', False)
if name in d['_props']:
f = d['_props'][name][0]
if f is None:
raise AttributeError("unreadable attribute")
return f()
return object.__getattribute__(self, name)
def __setattr__(self, name, value):
d = object.__getattribute__(self, '__dict__')
d.setdefault('_props', dict())
d.setdefault('_locked', False)
if name in d['_props']:
f = d['_props'][name][1]
if f is None:
raise AttributeError("can't set attribute")
f(value)
return
if name not in d and self._locked:
raise AttributeError("locked")
object.__setattr__(self, name, value)
def __delattr__(self, name):
d = object.__getattribute__(self, '__dict__')
d.setdefault('_props', dict())
d.setdefault('_locked', False)
if name in d['_props']:
f = d['_props'][name][2]
if f is None:
raise AttributeError("can't delete attribute")
f()
return
if name not in d and self._locked:
raise AttributeError("locked")
object.__delattr__(self, name)
[docs]class IndexedPropertyCollection(object):
"A building block to create hierarchical trees of methods and properties with an index that is converted to a parameter"
def __init__(self):
self._props = dict()
self._docs = dict()
self._indicies = list()
self._indicies_dict = dict()
self._objs = list()
[docs] def _add_property(self, name, fget=None, fset=None, fdel=None, doc=None, props = None, docs = None):
"Add a managed property"
if props is None:
props = self._props
if docs is None:
docs = self._docs
l = name.split('.',1)
n = l[0]
r = ''
if len(l) > 1: r = l[1]
if n not in props:
props[n] = dict()
docs[n] = dict()
if type(props[n]) != dict:
raise AttributeError("property already defined")
if len(r) > 0:
self._add_property(r, fget, fset, fdel, doc, props[n], docs[n])
else:
props[n] = (fget, fset, fdel)
docs[n] = doc
[docs] def _add_method(self, name, f=None, doc=None, props = None, docs = None):
"Add a managed method"
if props is None:
props = self._props
if docs is None:
docs = self._docs
l = name.split('.',1)
n = l[0]
r = ''
if len(l) > 1: r = l[1]
if n not in props:
props[n] = dict()
docs[n] = dict()
if type(props[n]) != dict:
raise AttributeError("property already defined")
if len(r) > 0:
self._add_method(r, f, doc, props[n], docs[n])
else:
props[n] = f
docs[n] = doc
[docs] def _add_sub_property(self, sub, name, fget=None, fset=None, fdel=None, doc=None):
"Add a sub-property (equivalent to _add_property('sub.name', ...))"
self._add_property(sub+'.'+name, fget, fset, fdel, doc)
[docs] def _add_sub_method(self, sub, name, f=None, doc=None):
"Add a sub-method (equivalent to _add_method('sub.name', ...))"
self._add_method(sub+'.'+name, f, doc)
[docs] def _del_property(self, name):
"Delete property"
l = name.split('.',1)
n = l[0]
r = ''
if len(l) > 1: r = l[1]
if len(r) > 0:
self._del_property(r)
else:
del self._props[name]
del self._docs[name]
[docs] def _build_obj(self, props, docs, i):
"Build a tree of PropertyCollection objects with the proper index associations"
obj = PropertyCollection()
for n in props:
itm = props[n]
doc = docs[n]
if type(itm) == tuple:
fget, fset, fdel = itm
fgeti = fseti = fdeli = None
if fget is not None: fgeti = partial(fget, i)
if fset is not None: fseti = partial(fset, i)
if fdel is not None: fdeli = partial(fdel, i)
obj._add_property(n, fgeti, fseti, fdeli, doc)
elif type(itm) == dict:
o2 = self._build_obj(itm, doc, i)
obj.__dict__[n] = o2
elif hasattr(itm, "__call__"):
obj._add_method(n, partial(itm, i), doc)
obj._lock()
return obj
[docs] def _set_list(self, l):
"Set a list of allowable indicies as an associative array"
self._indicies = list(l)
self._indicies_dict = get_index_dict(self._indicies)
self._objs = list()
for i in range(len(self._indicies)):
self._objs.append(self._build_obj(self._props, self._docs, i))
def __getitem__(self, key):
i = get_index(self._indicies_dict, key)
return self._objs[i]
def __iter__(self):
return self._objs.__iter__()
def __len__(self):
return len(self._indicies)
def count(self):
return len(self._indicies)
class IviContainer(PropertyCollection):
def __init__(self, *args, **kwargs):
super(IviContainer, self).__init__(*args, **kwargs)
def _add_attribute(self, name, attr, doc = None):
cur_obj = self
# iterate over name
rest = name
while len(rest) > 0:
# split at first dot
l = rest.split('.',1)
base = l[0]
rest = ''
# save the rest
if len(l) > 1:
rest = l[1]
# is it an indexed object?
k = base.find('[')
if k > 0:
# if so, stop here and add an indexed property collection
base = base[:k]
cur_obj.__dict__.setdefault(base, IndexedPropertyCollection())
cur_obj = cur_obj.__dict__[base]
base = rest
rest = ''
else:
# if not, add a property collection and keep going
cur_obj.__dict__.setdefault(base, PropertyCollection())
cur_obj = cur_obj.__dict__[base]
if type(doc) == Doc:
doc.name = name
if cur_obj == self:
if type(attr) == tuple:
fget, fset, fdel = attr
PropertyCollection._add_property(self, base, fget, fset, fdel, doc)
else:
PropertyCollection._add_method(self, base, attr, doc)
else:
if type(attr) == tuple:
fget, fset, fdel = attr
cur_obj._add_property(base, fget, fset, fdel, doc)
else:
cur_obj._add_method(base, attr, doc)
def _add_method(self, name, f, doc = None):
self._add_attribute(name, f, doc)
def _add_property(self, name, fget, fset = None, fdel = None, doc = None):
self._add_attribute(name, (fget, fset, fdel), doc)
class Doc(object):
"IVI documentation object"
def __init__(self, doc = '', cls = '', grp = '', section = '', name = ''):
self.doc = trim_doc(doc)
self.name = name
self.cls = cls
self.grp = grp
self.section = section
def render(self):
txt = '.. attribute:: ' + self.name + '\n\n'
if self.cls != '':
txt += ' *IVI class ' + self.cls + \
', capability group ' + self.cls + self.grp + \
', section ' + self.section + '*\n\n'
txt += '\n'.join(' ' + x for x in self.doc.splitlines())
txt += '\n'
return txt
def __str__(self):
return self.doc
def add_attribute(obj, name, attr, doc = None):
IviContainer._add_attribute(obj, name, attr, doc)
def add_method(obj, name, f, doc = None):
add_attribute(obj, name, f, doc)
def add_property(obj, name, fget, fset = None, fdel = None, doc = None):
add_attribute(obj, name, (fget, fset, fdel), doc)
def add_group_capability(obj, cap):
obj.__dict__.setdefault('_identity_group_capabilities', list())
obj._identity_group_capabilities.insert(0, cap)
def build_ieee_block(data):
"Build IEEE block"
# IEEE block binary data is prefixed with #lnnnnnnnn
# where l is length of n and n is the
# length of the data
# ex: #800002000 prefixes 2000 data bytes
return str('#8%08d' % len(data)).encode('utf-8') + data
def decode_ieee_block(data):
"Decode IEEE block"
# IEEE block binary data is prefixed with #lnnnnnnnn
# where l is length of n and n is the
# length of the data
# ex: #800002000 prefixes 2000 data bytes
if len(data) == 0:
return b''
ind = 0
c = '#'.encode('utf-8')
while data[ind:ind+1] != c:
ind += 1
ind += 1
l = int(data[ind:ind+1])
ind += 1
if (l > 0):
num = int(data[ind:ind+l].decode('utf-8'))
ind += l
return data[ind:ind+num]
else:
return data[ind:]
def get_sig(sig):
"Parse various signal inputs into x and y components"
if type(sig) == tuple and len(sig) == 2:
# tuple of two lists or arrays
x, y = sig
x = np.array(x)
y = np.array(y)
elif type(sig) == list and type(sig[0]) == tuple and len(sig[0]) == 2:
# list of tuples
x, y = zip(*sig)
x = np.array(x)
y = np.array(y)
elif (type(sig) == np.ndarray or type(sig) == np.matrix) and len(sig.shape) == 2 and sig.shape[0] == 2:
# 2D array, hieght 2
x = np.array(sig[0])
y = np.array(sig[1])
elif (type(sig) == np.ndarray or type(sig) == np.matrix) and len(sig.shape) == 2 and sig.shape[1] == 2:
# 2D array, width 2
x = np.array(sig[:,0])
y = np.array(sig[:,1])
else:
raise Exception('Unknown argument')
if len(x) != len(y):
raise Exception('Signals must be the same length!')
return x, y
def rms(y):
"Calculate the RMS value of the signal"
return np.linalg.norm(y) / np.sqrt(y.size)
def trim_doc(docstring):
if not docstring:
return ''
docstring = str(docstring)
# Convert tabs to spaces (following the normal Python rules)
# and split into a list of lines:
lines = docstring.expandtabs().splitlines()
# Determine minimum indentation (first line doesn't count):
indent = 10000
for line in lines[1:]:
stripped = line.lstrip()
if stripped:
indent = min(indent, len(line) - len(stripped))
# Remove indentation (first line is special):
trimmed = [lines[0].strip()]
if indent < 10000:
for line in lines[1:]:
trimmed.append(line[indent:].rstrip())
# Strip off trailing and leading blank lines:
while trimmed and not trimmed[-1]:
trimmed.pop()
while trimmed and not trimmed[0]:
trimmed.pop(0)
# Return a single string:
return '\n'.join(trimmed)
def doc(obj=None, itm=None, docs=None, prefix=None):
"""Python IVI documentation generator"""
st = ""
# add a dot to prefix when needed
if prefix is None or len(prefix) == 0:
prefix = ''
elif not prefix[-1] == '.':
prefix += '.'
# if something passed in docs, iterate over it
if docs is not None:
for n in sorted(docs.keys()):
d = docs[n]
if type(d) == dict:
# recurse into node
st += doc(docs=d, prefix=prefix+n)
else:
# print leaf (method or property)
st += prefix + n + "\n"
return st
if itm is not None:
# split off first component before the dot
l = itm.split('.',1)
n = l[0]
r = ''
# remove brackets
k = n.find('[')
if k > 0:
n = n[:k]
# if there is more left, need to recurse
if len(l) > 1:
r = l[1]
# hand off to parent
if type(obj) == dict and n in obj:
return doc(obj[n], r, prefix=prefix+n)
elif n in obj.__dict__:
return doc(obj.__dict__[n], r, prefix=prefix+n)
elif hasattr(obj, '_docs') and n in obj._docs:
d = obj._docs[n]
if type(d) == dict:
return doc(d, r, prefix=prefix+n)
else:
d = None
# return documentation if present
if type(obj) == dict and n in obj:
d = obj[n]
elif hasattr(obj, '_docs') and n in obj._docs:
d = obj._docs[n]
if type(d) == Doc:
return d
elif type(d) == str:
return trim_doc(d)
return "error"
if hasattr(obj, '__dict__'):
# if obj has __dict__, iterate over it
for n in sorted(obj.__dict__.keys()):
o = obj.__dict__[n]
# add brackets for indexed property collections
extra = ''
if type(o) == IndexedPropertyCollection:
extra = '[]'
if n == '_docs':
# process documentation dict
st += doc(docs=o, prefix=prefix)
elif hasattr(o, '_docs'):
# process object that contains a documentation dict
st += doc(o, prefix=prefix+n)
# if we got something, return it
if len(st) > 0:
return st
return "error"
def help(obj=None, itm=None, complete=False, indent=0):
"""Python IVI help system"""
if complete:
l = doc(obj).split('\n')
l = sorted(filter(None, l))
for m in l:
d = doc(obj, m)
if type(d) == Doc:
print(d.render())
if type(d) == str:
print((indent * ' ') + '.. attribute:: ' + m + '\n')
#print('-'*len(m)+'\n')
d = '\n'.join(((indent + 3) * ' ') + x for x in d.splitlines())
print(d)
print('\n')
elif obj is not None:
print(doc(obj, itm))
else:
print(trim_doc("""
Using Python IVI help
---------------------
Use the help method to get documentation on IVI methods and properties. The
IVI help system is a little different from the built-in Python help system.
Here are some examples on how to use it correctly:
This help method can be called with no parameters:
import ivi
instr = ivi.Driver()
instr.help()
This will print a list of all of the available methods and properties,
like so:
close
initialized
initialize
driver_operation.cache
driver_operation.clear_interchange_warnings
driver_operation.driver_setup
...
The higher level groups can also be passed to the help method:
import ivi
instr = ivi.Driver()
instr.help(instr.identity)
This will output everything inside of the sub group:
get_supported_instrument_models
get_group_capabilities
specification_major_version
...
Finally, individual methods and properties can be passed as strings:
import ivi
instr = ivi.Driver()
instr.help("identity.supported_instrument_models")
This will result in the complete documentation:
Returns a comma-separated list of names of instrument models with which
the IVI specific driver is compatible. The string has no white space
...
"""))
[docs]class DriverOperation(IviContainer):
"Inherent IVI methods for driver operation"
def __init__(self, *args, **kwargs):
super(DriverOperation, self).__init__(*args, **kwargs)
self._driver_operation_cache = True
self._driver_operation_driver_setup = ""
self._driver_operation_interchange_check = False
self._driver_operation_logical_name = ""
self._driver_operation_query_instrument_status = False
self._driver_operation_range_check = True
self._driver_operation_record_coercions = False
self._driver_operation_io_resource_descriptor = ""
self._driver_operation_simulate = False
self._driver_operation_interchange_warnings = list()
self._driver_operation_coercion_records = list()
self._add_property('driver_operation.cache',
self._get_driver_operation_cache,
self._set_driver_operation_cache,
None,
"""
If True, the specific driver caches the value of attributes, and the IVI
specific driver keeps track of the current instrument settings so that it
can avoid sending redundant commands to the instrument. If False, the
specific driver does not cache the value of attributes.
The default value is True. When the user opens an instrument session
through an IVI class driver or uses a logical name to initialize a
specific driver, the user can override this value by specifying a value in
the IVI configuration store. The Initialize function allows the user to
override both the default value and the value that the user specifies in
the IVI configuration store.
""")
self._add_property('driver_operation.driver_setup',
self._get_driver_operation_driver_setup,
None,
None,
"""
Returns the driver setup string that the user specified in the IVI
configuration store when the instrument driver session was initialized or
passes in the OptionString parameter of the Initialize function. Refer to
Section 6.14, Initialize, for the restrictions on the format of the driver
setup string.
The string that this attribute returns does not have a predefined maximum
length.
""")
self._add_property('driver_operation.interchange_check',
self._get_driver_operation_interchange_check,
self._set_driver_operation_interchange_check,
None,
"""
If True, the specific driver performs interchangeability checking. If the
Interchange Check attribute is enabled, the specific driver maintains a
record of each interchangeability warning that it encounters. The user
calls the Get Next Interchange Warning function to extract and delete the
oldest interchangeability warning from the list. Refer to Section 6.11,
Get Next Interchange Warning, Section 6.2, Clear Interchange Warnings,
and Section 6.18, Reset Interchange Check, for more information. If False,
the specific driver does not perform interchangeability checking.
If the user opens an instrument session through an IVI class driver and
the Interchange Check attribute is enabled, the IVI class driver may
perform additional interchangeability checking. The IVI class driver
maintains a list of the interchangeability warnings that it encounters.
The user can retrieve both class driver interchangeability warnings and
specific driver interchangeability warnings by calling the Get Next
Interchange Warning function on the class driver session.
If the IVI specific driver does not implement interchangeability checking,
the specific driver returns the Value Not Supported error when the user
attempts to set the Interchange Check attribute to True. If the specific
driver does implement interchangeability checking and the user opens an
instrument session through an IVI class driver, the IVI class driver
accepts True as a valid value for the Interchange Check attribute even if
the class driver does not implement interchangeability checking
capabilities of its own.
The default value is False. If the user opens an instrument session
through an IVI class driver or initializes an IVI specific driver with a
logical name, the user can override this value in the IVI configuration
store. The Initialize function allows the user to override both the
default value and the value that the userspecifies in the IVI
configuration store.
""")
self._add_property('driver_operation.logical_name',
self._get_driver_operation_logical_name,
None,
None,
"""
Returns the IVI logical name that the user passed to the Initialize
function. If the user initialized the IVI specific driver directly and did
not pass a logical name, then this attribute returns an empty string.
Refer to IVI-3.5: Configuration Server Specification for restrictions on
the format of IVI logical names.
The string that this attribute returns contains a maximum of 256
characters including the NULL character.
""")
self._add_property('driver_operation.query_instrument_status',
self._get_driver_operation_query_instrument_status,
self._set_driver_operation_query_instrument_status,
None,
"""
If True, the IVI specific driver queries the instrument status at the end
of each user operation. If False, the IVI specific driver does not query
the instrument status at the end of each user operation. Querying the
instrument status is very useful for debugging. After validating the
program, the user can set this attribute to False to disable status
checking and maximize performance. The user specifies this value for the
entire IVI driver session.
The default value is False. When the user opens an instrument session
through an IVI class driver or uses a logical name to initialize an IVI
specific driver, the user can override this value by specifying a value in
the IVI configuration store. The Initialize function allows the user to
override both the default value and the value that the user specifies in
the IVI configuration store.
""")
self._add_property('driver_operation.range_check',
self._get_driver_operation_range_check,
self._set_driver_operation_range_check,
None,
"""
If True, the IVI specific driver validates attribute values and function
parameters. If False, the IVI specific driver does not validate attribute
values and function parameters.
If range check is enabled, the specific driver validates the parameter
values that users pass to driver functions. Validating attribute values
and function parameters is useful for debugging. After validating the
program, the user can set this attribute to False to disable range
checking and maximize performance. The default value is True. When the
user opens an instrument session through an IVI class driver or uses a
logical name to initialize an IVI specific driver, the user can override
this value by specifying a value in the IVI configuration store. The
Initialize function allows the user to override both the default value and
the value that the user specifies in the IVI configuration store.
""")
self._add_property('driver_operation.record_coercions',
self._get_driver_operation_record_coercions,
self._set_driver_operation_record_coercions,
None,
"""
If True, the IVI specific driver keeps a list of the value coercions it
makes for ViInt32 and ViReal64 attributes. If False, the IVI specific
driver does not keep a list of the value coercions it makes for ViInt32 and
ViReal64 attributes.
If the Record Value Coercions attribute is enabled, the specific driver
maintains a record of each coercion. The user calls the Get Next Coercion
Record function to extract and delete the oldest coercion record from the
list. Refer to Section 6.10, Get Next Coercion Record, for more
information.
If the IVI specific driver does not implement coercion recording, the
specific driver returns the Value Not Supported error when the user
attempts to set the Record Value Coercions attribute to True.
The default value is False. When the user opens an instrument session
through an IVI class driver or uses a logical name to initialize a IVI
specific driver, the user can override this value by specifying a value in
the IVI configuration store. The Initialize function allows the user to
override both the default value and the value that the user specifies in
the IVI configuration store.
""")
self._add_property('driver_operation.io_resource_descriptor',
self._get_driver_operation_io_resource_descriptor,
None,
None,
"""
Returns the resource descriptor that the user specified for the physical
device. The user specifies the resource descriptor by editing the IVI
configuration store or by passing a resource descriptor to the Initialize
function of the specific driver. Refer to Section 6.14, Initialize, for the
restrictions on the contents of the resource descriptor string.
The string that this attribute returns contains a maximum of 256 characters
including the NULL character.
""")
self._add_property('driver_operation.simulate',
self._get_driver_operation_simulate,
None,
None,
"""
If True, the IVI specific driver simulates instrument driver I/O
operations. If False, the IVI specific driver communicates directly with
the instrument.
If simulation is enabled, the specific driver functions do not perform
instrument I/O. For output parameters that represent instrument data, the
specific driver functions return simulated values.
The default value is False. When the user opens an instrument session
through an IVI class driver or uses a logical name to initialize an IVI
specific driver, the user can override this value by specifying a value in
the IVI configuration store. The Initialize function allows the user to
override both the default value and the value that the user specifies in
the IVI configuration store.
""")
self._add_method('driver_operation.clear_interchange_warnings',
self._driver_operation_clear_interchange_warnings,
"""
This function clears the list of interchangeability warnings that the IVI
specific driver maintains.
When this function is called on an IVI class driver session, the function
clears the list of interchangeability warnings that the class driver and
the specific driver maintain.
Refer to the Interchange Check attribute for more information on
interchangeability checking.
""")
self._add_method('driver_operation.get_next_coercion_record',
self._driver_operation_get_next_coercion_record,
"""
If the Record Value Coercions attribute is set to True, the IVI specific
driver keeps a list of all value coercions it makes on integer and
floating point attributes. This function obtains the coercion information
associated with the IVI session. It retrieves and clears the oldest
instance in which the specific driver coerced a value the user specified
to another value.
The function returns an empty string in the CoercionRecord parameter if no
coercion records remain for the session.
The coercion record string shall contain the following information:
* The name of the attribute that was coerced. This can be the generic name,
the COM property name, or the C defined constant.
* If the attribute applies to a repeated capability, the name of the
virtual or physical repeated capability identifier.
* The value that the user specified for the attribute.
* The value to which the attribute was coerced.
A recommended format for the coercion record string is as follows::
" Attribute " + <attribute name> + [" on <repeated capability> " +
<repeated capability identifier>] + " was coerced from " +
<desiredVal> + " to " + <coercedVal>
.
And example coercion record string is as follows::
Attribute TKTDS500_ATTR_VERTICAL_RANGE on channel ch1 was coerced from
9.0 to 10.0.
""")
self._add_method('driver_operation.get_next_interchange_warning',
self._driver_operation_get_next_interchange_warning,
"""
If the Interchange Check attribute is set to True, the IVI specific driver
keeps a list of all interchangeability warnings that it encounters. This
function returns the interchangeability warnings associated with the IVI
session. It retrieves and clears the oldest interchangeability warning
from the list. Interchangeability warnings indicate that using the
application with a different instrument might cause different behavior.
When this function is called on an IVI class driver session, it may return
interchangeability warnings generated by the IVI class driver as well as
interchangeability warnings generated by the IVI specific driver. The IVI
class driver determines the relative order in which the IVI class driver
warnings are returned in relation to the IVI specific driver warnings.
The function returns an empty string in the InterchangeWarning parameter
if no interchangeability warnings remain for the session.
Refer to the Interchange Check attribute for more information on
interchangeability checking.
""")
self._add_method('driver_operation.invalidate_all_attributes',
self._driver_operation_invalidate_all_attributes,
"""
This function invalidates the cached values of all attributes for the
session.
""")
self._add_method('driver_operation.reset_interchange_check',
self._driver_operation_reset_interchange_check,
"""
This function resets the interchangeability checking algorithms of the IVI
specific driver so that specific driver functions that execute prior to
calling this function have no effect on whether future calls to the
specific driver generate interchangeability warnings.
When developing a complex test system that consists of multiple test
modules, it is generally a good idea to design the test modules so that
they can run in any order. To do so requires ensuring that each test
module completely configures the state of each instrument it uses. If a
particular test module does not completely configure the state of an
instrument, the state of the instrument depends on the configuration from
a previously executed test module. If the test modules execute in a
different order, the behavior of the instrument and therefore the entire
test module is likely to change. This change in behavior is generally
instrument specific and represents an interchangeability problem.
Users can use this function to test for such cases. By calling this
function at the beginning of a test module, users can determine whether
the test module has dependencies on the operation of previously executed
test modules. Any interchangeability warnings that occur after the user
calls this function indicate that the section of the test program that
executes after this function and prior to the generation of the warning
does not completely configure the instrument and that the user is likely
to experience different behavior if the user changes the execution order
of the test modules or if the user changes instruments.
Note: This function does not clear interchangeability warnings from the
list of interchangeability warnings. To guarantee that the Get Next
Interchange Warning function returns interchangeability warnings that
occur only after the program calls function, the user must clear the list
of interchangeability warnings by calling the Clear Interchange Warnings
function.
Refer to the Interchange Check attribute for more information on
interchangeability checking.
""")
def _get_driver_operation_cache(self):
return self._driver_operation_cache
def _set_driver_operation_cache(self, value):
self._driver_operation_cache = bool(value)
def _get_driver_operation_driver_setup(self):
return self._driver_operation_driver_setup
def _get_driver_operation_interchange_check(self):
return self._driver_operation_interchange_check
def _set_driver_operation_interchange_check(self, value):
self._driver_operation_interchange_check = bool(value)
def _get_driver_operation_logical_name(self):
return self._driver_operation_logical_name
def _get_driver_operation_query_instrument_status(self):
return self._driver_operation_query_instrument_status
def _set_driver_operation_query_instrument_status(self, value):
self._driver_operation_query_instrument_status = bool(value)
def _get_driver_operation_range_check(self):
return self._driver_operation_range_check
def _set_driver_operation_range_check(self, value):
self._driver_operation_range_check = bool(value)
def _get_driver_operation_record_coercions(self):
return self._driver_operation_record_coercions
def _set_driver_operation_record_coercions(self, value):
self._driver_operation_record_coercions = bool(value)
def _get_driver_operation_io_resource_descriptor(self):
return self._driver_operation_io_resource_descriptor
def _get_driver_operation_simulate(self):
return self._driver_operation_simulate
def _set_driver_operation_simulate(self, value):
value = bool(value)
if self._driver_operation_simulate and not value:
raise SimulationStateException()
self._driver_operation_simulate = value
def _driver_operation_clear_interchange_warnings(self):
self._driver_operation_interchange_warnings = list()
def _driver_operation_get_next_coercion_record(self):
if len(self._driver_operation_coercion_records) > 0:
return self._driver_operation_coercion_records.pop()
return ""
def _driver_operation_get_next_interchange_warning(self):
if len(self._driver_operation_interchange_warnings) > 0:
return self._driver_operation_interchange_warnings.pop()
return ""
def _driver_operation_invalidate_all_attributes(self):
pass
def _driver_operation_reset_interchange_check(self):
pass
[docs]class DriverIdentity(IviContainer):
"Inherent IVI methods for identification"
def __init__(self, *args, **kwargs):
super(DriverIdentity, self).__init__(*args, **kwargs)
self._identity_description = "Base IVI Driver"
self._identity_identifier = ""
self._identity_revision = ""
self._identity_vendor = ""
self._identity_instrument_manufacturer = "Cannot query from instrument"
self._identity_instrument_model = "Cannot query from instrument"
self._identity_instrument_firmware_revision = "Cannot query from instrument"
self._identity_specification_major_version = 0
self._identity_specification_minor_version = 0
self._identity_supported_instrument_models = list()
self.__dict__.setdefault('_identity_group_capabilities', list())
self._add_property('identity.description',
self._get_identity_description,
None,
None,
"""
Returns a brief description of the IVI software component.
The string that this attribute returns has no maximum size.
""")
self._add_property('identity.identifier',
self._get_identity_identifier,
None,
None,
"""
Returns the case-sensitive unique identifier of the IVI software
component. The string that this attribute returns contains a maximum of 32
characters including the NULL character.
""")
self._add_property('identity.revision',
self._get_identity_revision,
None,
None,
"""
Returns version information about the IVI software component. Refer to
Section 3.1.2.2, Additional Compliance Rules for Revision String
Attributes, for additional rules regarding this attribute.
The string that this attribute returns has no maximum size.
""")
self._add_property('identity.vendor',
self._get_identity_vendor,
None,
None,
"""
Returns the name of the vendor that supplies the IVI software component.
The string that this attribute returns has no maximum size.
""")
self._add_property('identity.instrument_manufacturer',
self._get_identity_instrument_manufacturer,
None,
None,
"""
Returns the name of the manufacturer of the instrument. The IVI specific
driver returns the value it queries from the instrument as the value of
this attribute or a string indicating that it cannot query the instrument
identity.
In some cases, it is not possible for the specific driver to query the
manufacturer of the instrument. This can occur when the Simulate attribute
is set to True or if the instrument is not capable of returning the
manufacturer. For these cases, the specific driver returns defined strings
for this attribute. If the Simulate attribute is set to True, the specific
driver returns "Not available while simulating" as the value of this
attribute. If the instrument is not capable of returning the manufacturer
and the Simulate attribute is set to False, the specific driver returns
"Cannot query from instrument" as the value of this attribute.
The string that this attribute returns does not have a predefined maximum
length.
""")
self._add_property('identity.instrument_model',
self._get_identity_instrument_model,
None,
None,
"""
Returns the model number or name of the physical instrument. The IVI
specific driver returns the value it queries from the instrument or a
string indicating that it cannot query the instrument identity.
In some cases, it is not possible for the specific driver to query the
model of the instrument. This can occur when the Simulate attribute is
set to True or if the instrument is not capable of returning the model.
For these cases, the specific driver returns defined strings for this
attribute. If the Simulate attribute is set to True, the specific driver
returns "Not available while simulating" as the value of this attribute.
If the instrument is not capable of returning the model and the Simulate
attribute is set to False, the specific driver returns "Cannot query
from instrument" as the value of this attribute.
The string that this attribute returns does not have a predefined maximum
length.
""")
self._add_property('identity.instrument_firmware_revision',
self._get_identity_instrument_firmware_revision,
None,
None,
"""
Returns an instrument specific string that contains the firmware
revision information of the physical instrument. The IVI specific driver
returns the value it queries from the instrument as the value of this
attribute or a string indicating that it cannot query the instrument
identity.
In some cases, it is not possible for the specific driver to query the
firmware revision of the instrument. This can occur when the Simulate
attribute is set to True or if the instrument is not capable of returning
the firmware revision. For these cases, the specific driver returns
defined strings for this attribute. If the Simulate attribute is set to
True, the specific driver returns "Not available while simulating" as the
value of this attribute. If the instrument is not capable of returning the
firmware version and the Simulate attribute is set to False, the specific
driver returns "Cannot query from instrument" as the value of this
attribute.
The string that this attribute returns does not have a predefined maximum
length.
""")
self._add_property('identity.specification_major_version',
self._get_identity_specification_major_version,
None,
None,
"""
Returns the major version number of the class specification in accordance
with which the IVI software component was developed. The value is a
positive integer value.
If the software component is not compliant with a class specification, the
software component returns zero as the value of this attribute.
""")
self._add_property('identity.specification_minor_version',
self._get_identity_specification_minor_version,
None,
None,
"""
Returns the minor version number of the class specification in accordance
with which the IVI software component was developed. The value is a
positive integer value.
If the software component is not compliant with a class specification, the
software component returns zero as the value of this attribute.
""")
self._add_property('identity.supported_instrument_models',
self._get_identity_supported_instrument_models,
None,
None,
"""
Returns a comma-separated list of names of instrument models with which
the IVI specific driver is compatible. The string has no white space
except possibly embedded in the instrument model names. An example of a
string that this attribute might return is "TKTDS3012,TKTDS3014,TKTDS3016".
It is not necessary for the string to include the abbreviation for the
manufacturer if it is the same for all models. In the example above, it is
valid for the attribute to return the string "TDS3012,TDS3014,TDS3016".
The string that this attribute returns does not have a predefined maximum
length.
""")
self._add_property('identity.group_capabilities',
self._get_identity_group_capabilities,
None,
None,
"""
Returns a comma-separated list that identifies the class capability groups
that the IVI specific driver implements. The items in the list are
capability group names that the IVI class specifications define. The
string has no white space except for white space that might be embedded in
a capability group name.
If the IVI specific driver does not comply with an IVI class specification,
the specific driver returns an empty string as the value of this attribute.
The string that this attribute returns does not have a predefined maximum
length.
""")
self._add_method('identity.get_group_capabilities',
self._identity_get_group_capabilities,
"""
Returns a list of names of class capability groups that the IVI specific
driver implements. The items in the list are capability group names that
the IVI class specifications define. The list is returned as a list of
strings.
If the IVI specific driver does not comply with an IVI class specification,
the specific driver returns an array with zero elements.
""")
self._add_method('identity.get_supported_instrument_models',
self._identity_get_supported_instrument_models,
"""
Returns a list of names of instrument models with which the IVI specific
driver is compatible. The list is returned as a list of strings. For
example, this attribute might return the strings "TKTDS3012", "TKTDS3014",
and "TKTDS3016" .
It is not necessary for the string to include the abbreviation for the
manufacturer if it is the same for all models. In the example above, it is
valid for the attribute to return the strings "TDS3012", "TDS3014", and
"TDS3016".
""")
def _add_group_capability(self, name):
self.__dict__.setdefault('_identity_group_capabilities', list())
self._identity_group_capabilities.insert(0, name)
def _get_identity_description(self):
return self._identity_description
def _get_identity_identifier(self):
return self._identity_identifier
def _get_identity_revision(self):
return self._identity_revision
def _get_identity_vendor(self):
return self._identity_vendor
def _get_identity_instrument_manufacturer(self):
return self._identity_instrument_manufacturer
def _get_identity_instrument_model(self):
return self._identity_instrument_model
def _get_identity_instrument_firmware_revision(self):
return self._identity_instrument_firmware_revision
def _get_identity_specification_major_version(self):
return self._identity_specification_major_version
def _get_identity_specification_minor_version(self):
return self._identity_specification_minor_version
def _get_identity_supported_instrument_models(self):
return ",".join(self._identity_supported_instrument_models)
def _get_identity_group_capabilities(self):
return ",".join(self._identity_group_capabilities)
def _identity_get_group_capabilities(self):
return self._identity_group_capabilities
def _identity_get_supported_instrument_models(self):
return self._identity_supported_instrument_models
[docs]class DriverUtility(IviContainer):
"Inherent IVI utility methods"
def __init__(self, *args, **kwargs):
super(DriverUtility, self).__init__(*args, **kwargs)
self._add_method('utility.disable',
self._utility_disable,
"""
The Disable operation places the instrument in a quiescent state as
quickly as possible. In a quiescent state, an instrument has no or minimal
effect on the external system to which it is connected. The Disable
operation might be similar to the Reset operation in that it places the
instrument in a known state. However, the Disable operation does not
perform the other operations that the Reset operation performs such as
configuring the instrument options on which the IVI specific driver
depends. For some instruments, the disable function may do nothing.
The IVI class specifications define the exact behavior of this function
for each instrument class. Refer to the IVI class specifications for more
information on the behavior of this function.
""")
self._add_method('utility.error_query',
self._utility_error_query,
"""
Queries the instrument and returns instrument specific error information.
Generally, the user calls this function after another function in the IVI
driver returns the Instrument Status error. The IVI specific driver
returns the Instrument Status error when the instrument indicates that it
encountered an error and its error queue is not empty. Error Query
extracts an error out of the instrument's error queue.
For instruments that have status registers but no error queue, the IVI
specific driver emulates an error queue in software.
The method returns a tuple containing the error code and error message.
""")
self._add_method('utility.lock_object',
self._utility_lock_object,
"""
This function obtains a multithread lock for this instance of the driver.
Before it does so, Lock Session waits until all other execution threads
have released their locks or for the length of time specified by the
maximum time parameter, whichever come first. The type of lock obtained
depends upon the parameters passed to the specific driver constructor.
The user can use Lock Session with IVI specific drivers to protect a
section of code that requires exclusive access to the instrument. This
occurs when the user takes multiple actions that affect the instrument
and the user wants to ensure that other execution threads do not disturb
the instrument state until all the actions execute. For example, if the
user sets various instrument attributes and then triggers a measurement,
the user must ensure no other execution thread modifies the attribute
values until the user finishes taking the measurement.
It is important to note that this lock is not related to I/O locks such as
the VISA resource locking mechanism.
The user can safely make nested calls to Lock Session within the same
thread. To completely unlock the session, the user must balance each call
to Lock Session with a call to Unlock Session. Calls to Lock Session must
always obtain the same lock that is used internally by the IVI driver to
guard individual method calls.
""")
self._add_method('utility.reset',
self._utility_reset,
"""
This function performs the following actions:
* Places the instrument in a known state. In an IEEE 488.2 instrument, the
Reset function sends the command string ``*RST`` to the instrument.
* Configures instrument options on which the IVI specific driver depends.
A specific driver might enable or disable headers or enable binary mode
for waveform transfers.
The user can either call the Reset function separately or specify that it
be called from the Initialize function. The Initialize function performs
additional operations after performing the reset operation to place the
instrument in a state more suitable for interchangeable programming. To
reset the device and perform these additional operations, call the Reset
With Defaults function instead of the Reset function.
""")
self._add_method('utility.reset_with_defaults',
self._utility_reset_with_defaults,
"""
The Reset With Defaults function performs the same operations that the
Reset function performs and then performs the following additional
operations in the specified order:
* Disables the class extension capability groups that the IVI specific
driver implements.
* If the class specification with which the IVI specific driver is
compliant defines initial values for attributes, this function sets
those attributes to the initial values that the class specification
defines.
* Configures the initial settings for the specific driver and instrument
based on the information retrieved from the IVI configuration store when
the instrument driver session was initialized.
Notice that the Initialize function also performs these functions. To
place the instrument and the IVI specific driver in the exact same state
that they attain when the user calls the Initialize function, the user
must first call the Close function and then the Initialize function.
""")
self._add_method('utility.self_test',
self._utility_self_test,
"""
Causes the instrument to perform a self test. Self Test waits for the
instrument to complete the test. It then queries the instrument for the
results of the self test and returns the results to the user.
If the instrument passes the self test, this function returns the tuple::
(0, 'Self test passed')
Otherwise, the function returns a tuple of the result code and message.
""")
self._add_method('utility.unlock_object',
self._utility_unlock_object,
"""
This function releases a lock that the Lock Session function acquires.
Refer to Lock Session for additional information on IVI session locks.
""")
def _utility_disable(self):
pass
def _utility_error_query(self):
error_code = 0
error_message = "No error"
return (error_code, error_message)
def _utility_lock_object(self):
pass
def _utility_reset(self):
pass
def _utility_reset_with_defaults(self):
self.utility_reset()
def _utility_self_test(self):
code = 0
message = "Self test passed"
return (code, message)
def _utility_unlock_object(self):
pass
[docs]class Driver(DriverOperation, DriverIdentity, DriverUtility):
"Inherent IVI methods for all instruments"
def __init__(self, resource = None, id_query = False, reset = False, *args, **kwargs):
# process out args for initialize
kw = {}
for k in ('range_check', 'query_instr_status', 'cache', 'simulate', 'record_coercions',
'interchange_check', 'driver_setup', 'prefer_pyvisa'):
if k in kwargs:
kw[k] = kwargs.pop(k)
self._interface = None
self._initialized = False
self.__dict__.setdefault('_instrument_id', '')
self._cache_valid = dict()
super(Driver, self).__init__(*args, **kwargs)
self._add_method('initialize',
self._initialize,
"""
The user must call the Initialize function prior to calling other IVI
driver functions that access the instrument. The Initialize function is
called automatically by the constructor if a resource string is passed as
the first argument to the constructor.
If simulation is disabled when the user calls the Initialize function, the
function performs the following actions:
* Opens and configures an I/O session to the instrument.
* If the user passes True for the IdQuery parameter, the function queries
the instrument for its ID and verifies that the IVI specific driver
supports the particular instrument model. If the instrument cannot
return its ID, the specific driver returns the ID Query Not Supported
warning.
* If the user passes True for the Reset parameter, the function places the
instrument in a known state. In an IEEE 488.2 instrument, the function
sends the command string "*RST" to the instrument. If the instrument
cannot perform a reset, the IVI specific driver returns the Reset Not
Supported warning.
* Configures instrument options on which the IVI specific driver depends.
For example, a specific driver might enable or disable headers or enable
binary mode for waveform transfers.
* Performs the following operations in the given order:
1. Disables the class extension capability groups that the IVI
specific driver does not implement.
2. If the class specification with which the IVI specific driver is
compliant defines initial values for attributes, this function sets
the attributes to the values that the class specification defines.
3. If the ResourceName parameter is a logical name, the IVI specific
driver configures the initial settings for the specific driver and
instrument based on the configuration of the logical name in the IVI
configuration store.
If simulation is enabled when the user calls the Initialize function, the
function performs the following actions:
* If the user passes True for the IdQuery parameter and the instrument
cannot return its ID, the IVI specific driver returns the ID Query Not
Supported warning.
* If the user passes True for the Reset parameter and the instrument
cannot perform a reset, the IVI specific driver returns the Reset Not
Supported warning.
* If the ResourceName parameter is a logical name, the IVI specific driver
configures the initial settings for the specific driver based on the
configuration of the logical name in the IVI configuration store.
Some instrument driver operations require or take into account information
from the IVI configuration store. Examples of such information are virtual
repeated capability name mappings and the value of certain inherent
attributes. An IVI driver shall retrieve all the information for a session
from the IVI configuration store during the Initialization function. The
IVI driver shall not read any information from the IVI configuration store
for a session after the Initialization function completes. Refer to
Section 3.2.3, Instantiating the Right Configuration Store From Software
Modules, of IVI-3.5: Configuration Server Specification for details on how
to correctly instantiate the configuration store.
The ResourceName parameter must contain either a logical name that is
defined in the IVI configuration store or an instrument specific string
that identifies the I/O address of the instrument, such as a VISA resource
descriptor string. Refer to IVI-3.5: Configuration Server Specification
for restrictions on the format of IVI logical names. Refer to the
VXIplug&play specifications for the grammar of VISA resource descriptor
strings.
Example resource strings::
'TCPIP::10.0.0.1::INSTR'
'TCPIP0::10.0.0.1::INSTR'
'TCPIP::10.0.0.1::gpib,5::INSTR'
'TCPIP0::10.0.0.1::gpib,5::INSTR'
'TCPIP0::10.0.0.1::usb0::INSTR'
'TCPIP0::10.0.0.1::usb0[1234::5678::MYSERIAL::0]::INSTR'
'USB::1234::5678::INSTR'
'USB::1234::5678::SERIAL::INSTR'
'USB0::0x1234::0x5678::INSTR'
'USB0::0x1234::0x5678::SERIAL::INSTR'
'GPIB::10::INSTR'
'GPIB0::10::INSTR'
'ASRL1::INSTR'
'ASRL::COM1,9600,8n1::INSTR'
'ASRL::/dev/ttyUSB0,9600::INSTR'
'ASRL::/dev/ttyUSB0,9600,8n1::INSTR'
The user can use additional parameters to specify the initial values of
certain IVI inherent attributes for the session. The following table lists
the inherent attributes that the user can set through these named
parameters. The user does not have to specify all or any of the
attributes. If the user does not specify the initial value of an inherent
attribute, the initial value of the attribute depends on the value of the
ResourceName parameter:
* If the ResourceName parameter contains an IVI logical name, the IVI
specific driver configures the initial settings based on the
configuration of the logical name in the IVI configuration store.
* If the ResourceName parameter contains a resource descriptor string that
identifies the I/O address of the instrument, the IVI specific driver
sets inherent attributes to their default initial values. The following
table shows the default initial value for each attribute.
The following table lists the IVI inherent attributes that the user can
set, their default initial values, and the name that represents each
attribute. These options are passed to the initialize function or the
constructor as key-value pairs.
+-------------------------+----------------------+---------------------+
| Attribute | Default Inital Value | Options String Name |
+=========================+======================+=====================+
| Range Check | True | range_check |
+-------------------------+----------------------+---------------------+
| Query Instrument Status | False | query_instr_status |
+-------------------------+----------------------+---------------------+
| Cache | True | cache |
+-------------------------+----------------------+---------------------+
| Simulate | False | simulate |
+-------------------------+----------------------+---------------------+
| Record Value Coercions | False | record_coercions |
+-------------------------+----------------------+---------------------+
| Interchange Check | False | interchange_check |
+-------------------------+----------------------+---------------------+
| Driver Setup | '' | driver_setup |
+-------------------------+----------------------+---------------------+
| Prefer PyVISA | False | prefer_pyvisa |
+-------------------------+----------------------+---------------------+
Each IVI specific driver defines it own meaning and valid values for the
Driver Setup attribute. Many specific drivers ignore the value of the
Driver Setup attribute. Other specific drivers use the Driver Setup string
to configure instrument specific features at initialization. For example,
if a specific driver supports a family of instrument models, the driver
can use the Driver Setup attribute to allow the user to specify a
particular instrument model to simulate.
If the user attempts to initialize the instrument a second time without
first calling the Close function, the Initialize function returns the
Already Initialized error.
""")
self._add_property('initialized',
self._get_initialized,
None,
None,
"""
Returns a value that indicates whether the IVI specific driver is in the
initialized state. After the specific driver is instantiated and before
the Initialize function successfully executes, this attribute returns
False. After the Initialize function successfully executes and prior to
the execution of the Close function, this attribute returns True. After
the Close function executes, this attribute returns False.
The Initialized attribute is one of the few IVI specific driver attributes
that can be accessed while the specific driver is not in the initialized
state. All the attributes of an IVI specific driver that can be accessed
while the specific driver is not in the initialized state are listed below.
* Component Class Spec Major Version
* Component Class Spec Minor Version
* Component Description
* Component Prefix
* Component Identifier
* Component Revision
* Component Vendor
* Initialized
* Supported Instrument Models
""")
self._add_method('close',
self._close,
"""
When the user finishes using a Python IVI driver, the user should call
either the Close method or __del__. Note that __del__ will call close
automatically.
This function also does the following:
* Prevents the user from calling other functions in the driver that
access the instrument until the user calls the Initialize function
again.
* May deallocate internal resources used by the IVI session.
""")
# inherit prefer_pyvisa from global setting
self._prefer_pyvisa = _prefer_pyvisa
# call initialize if resource string or other args present
self._initialized_from_constructor = False
if resource is not None or len(kw) > 0:
self._initialized_from_constructor = True
self.initialize(resource, id_query, reset, **kw)
def _initialize(self, resource = None, id_query = False, reset = False, **keywargs):
"Opens an I/O session to the instrument."
# decode options
for op in keywargs:
val = keywargs[op]
if op == 'range_check':
self._driver_operation_range_check = bool(val)
elif op == 'query_instr_status':
self._driver_operation_query_instrument_status = bool(val)
elif op == 'cache':
self._driver_operation_cache = bool(val)
elif op == 'simulate':
self._driver_operation_simulate = bool(val)
elif op == 'record_coercions':
self._driver_operation_record_coercions = bool(val)
elif op == 'interchange_check':
self._driver_operation_interchange_check = bool(val)
elif op == 'driver_setup':
self._driver_operation_driver_setup = val
elif op == 'prefer_pyvisa':
self._prefer_pyvisa = bool(val)
else:
raise UnknownOptionException('Invalid option')
# process resource
if self._driver_operation_simulate:
print("Simulating; ignoring resource")
elif resource is None:
raise IOException('No resource specified!')
elif type(resource) == str:
# parse VISA resource string
# valid resource strings:
# TCPIP::10.0.0.1::INSTR
# TCPIP0::10.0.0.1::INSTR
# TCPIP::10.0.0.1::gpib,5::INSTR
# TCPIP0::10.0.0.1::gpib,5::INSTR
# TCPIP0::10.0.0.1::usb0::INSTR
# TCPIP0::10.0.0.1::usb0[1234::5678::MYSERIAL::0]::INSTR
# USB::1234::5678::INSTR
# USB::1234::5678::SERIAL::INSTR
# USB0::0x1234::0x5678::INSTR
# USB0::0x1234::0x5678::SERIAL::INSTR
# GPIB::10::INSTR
# GPIB0::10::INSTR
# ASRL1::INSTR
# ASRL::COM1,9600,8n1::INSTR
# ASRL::/dev/ttyUSB0,9600::INSTR
# ASRL::/dev/ttyUSB0,9600,8n1::INSTR
m = re.match('^(?P<prefix>(?P<type>TCPIP|USB|GPIB|ASRL)\d*)(::(?P<arg1>[^\s:]+))?(::(?P<arg2>[^\s:]+(\[.+\])?))?(::(?P<arg3>[^\s:]+))?(::(?P<suffix>INSTR))$', resource, re.I)
if m is None:
if 'pyvisa' in globals():
# connect with PyVISA
self._interface = pyvisa.PyVisaInstrument(resource)
else:
raise IOException('Invalid resource string')
res_type = m.group('type').upper()
res_prefix = m.group('prefix')
res_arg1 = m.group('arg1')
res_arg2 = m.group('arg2')
res_arg3 = m.group('arg3')
res_suffix = m.group('suffix')
if res_type == 'TCPIP':
# TCP connection
if self._prefer_pyvisa and 'pyvisa' in globals():
# connect with PyVISA
self._interface = pyvisa.PyVisaInstrument(resource)
elif 'vxi11' in globals():
# connect with VXI-11
self._interface = vxi11.Instrument(resource)
elif 'pyvisa' in globals():
# connect with PyVISA
self._interface = pyvisa.PyVisaInstrument(resource)
else:
raise IOException('Cannot use resource type %s' % res_type)
elif res_type == 'USB':
# USB connection
if self._prefer_pyvisa and 'pyvisa' in globals():
# connect with PyVISA
self._interface = pyvisa.PyVisaInstrument(resource)
elif 'usbtmc' in globals():
# connect with USBTMC
self._interface = usbtmc.Instrument(resource)
elif 'pyvisa' in globals():
# connect with PyVISA
self._interface = pyvisa.PyVisaInstrument(resource)
else:
raise IOException('Cannot use resource type %s' % res_type)
elif res_type == 'GPIB':
# GPIB connection
if self._prefer_pyvisa and 'pyvisa' in globals():
# connect with PyVISA
self._interface = pyvisa.PyVisaInstrument(resource)
elif 'linuxgpib' in globals():
# connect with linux-gpib
self._interface = linuxgpib.LinuxGpibInstrument(resource)
elif 'pyvisa' in globals():
# connect with PyVISA
self._interface = pyvisa.PyVisaInstrument(resource)
else:
raise IOException('Cannot use resource type %s' % res_type)
elif res_type == 'ASRL':
# Serial connection
if self._prefer_pyvisa and 'pyvisa' in globals():
# connect with PyVISA
self._interface = pyvisa.PyVisaInstrument(resource)
elif 'pyserial' in globals():
# connect with PySerial
self._interface = pyserial.SerialInstrument(resource)
elif 'pyvisa' in globals():
# connect with PyVISA
self._interface = pyvisa.PyVisaInstrument(resource)
else:
raise IOException('Cannot use resource type %s' % res_type)
elif 'pyvisa' in globals():
# connect with PyVISA
self._interface = pyvisa.PyVisaInstrument(resource)
else:
raise IOException('Unknown resource type %s' % res_type)
self._driver_operation_io_resource_descriptor = resource
elif 'vxi11' in globals() and resource.__class__ == vxi11.Instrument:
# Got a vxi11 instrument, can use it as is
self._interface = resource
elif 'usbtmc' in globals() and resource.__class__ == usbtmc.Instrument:
# Got a usbtmc instrument, can use it as is
self._interface = resource
elif set(['read_raw', 'write_raw']).issubset(set(resource.__class__.__dict__)):
# has read_raw and write_raw, so should be a usable interface
self._interface = resource
else:
# don't have a usable resource
raise IOException('Invalid resource')
self.driver_operation.invalidate_all_attributes()
self._initialized = True
def _close(self):
"Closes an IVI session"
if self._interface:
try:
self._interface.close()
except:
pass
self._interface = None
self._initialized = False
def _get_initialized(self):
"Returnes initialization state of driver"
return self._initialized
def _get_cache_tag(self, tag=None, skip=1):
if tag is None:
stack = inspect.stack()
start = 0 + skip
if len(stack) < start + 1:
return ''
tag = stack[start][3]
if tag[0:4] == "_get": tag = tag[4:]
if tag[0:4] == "_set": tag = tag[4:]
if tag[0] == "_": tag = tag[1:]
return tag
def _get_cache_valid(self, tag=None, index=-1, skip_disable=False):
if not skip_disable and not self._driver_operation_cache:
return False
tag = self._get_cache_tag(tag, 2)
if index >= 0:
tag = tag + '_%d' % index
try:
return self._cache_valid[tag]
except KeyError:
self._cache_valid[tag] = False
return False
def _set_cache_valid(self, valid=True, tag=None, index=-1):
tag = self._get_cache_tag(tag, 2)
if index >= 0:
tag = tag + '_%d' % index
self._cache_valid[tag] = valid
def _driver_operation_invalidate_all_attributes(self):
self._cache_valid = dict()
def _write_raw(self, data):
"Write binary data to instrument"
if self._driver_operation_simulate:
print("[simulating] Call to write_raw")
return
if not self._initialized or self._interface is None:
raise NotInitializedException()
self._interface.write_raw(data)
def _read_raw(self, num=-1):
"Read binary data from instrument"
if self._driver_operation_simulate:
print("[simulating] Call to read_raw")
return b''
if not self._initialized or self._interface is None:
raise NotInitializedException()
return self._interface.read_raw(num)
def _ask_raw(self, data, num=-1):
"Write then read binary data"
if self._driver_operation_simulate:
print("[simulating] Call to ask_raw")
return b''
if not self._initialized or self._interface is None:
raise NotInitializedException()
try:
return self._interface.ask_raw(data, num)
except AttributeError:
# if interface does not implement ask_raw, emulate it
self._write_raw(data)
return self._read_raw(num)
def _write(self, data, encoding = 'utf-8'):
"Write string to instrument"
if self._driver_operation_simulate:
print("[simulating] Write (%s) '%s'" % (encoding, data))
return
if not self._initialized or self._interface is None:
raise NotInitializedException()
try:
self._interface.write(data, encoding)
except AttributeError:
if type(data) is tuple or type(data) is list:
# recursive call for a list of commands
for data_i in data:
self._write(data_i, encoding)
return
self._write_raw(str(data).encode(encoding))
def _read(self, num=-1, encoding = 'utf-8'):
"Read string from instrument"
if self._driver_operation_simulate:
print("[simulating] Read (%s)" % encoding)
return ''
if not self._initialized or self._interface is None:
raise NotInitializedException()
try:
return self._interface.read(num, encoding)
except AttributeError:
return self._read_raw(num).decode(encoding).rstrip('\r\n')
def _ask(self, data, num=-1, encoding = 'utf-8'):
"Write then read string"
if self._driver_operation_simulate:
print("[simulating] Ask (%s) '%s'" % (encoding, data))
return ''
if not self._initialized or self._interface is None:
raise NotInitializedException()
try:
return self._interface.ask(data, num, encoding)
except AttributeError:
# if interface does not implement ask, emulate it
if type(data) is tuple or type(data) is list:
# # recursive call for a list of commands
val = list()
for data_i in data:
val.append(self._ask(data_i, num, encoding))
return val
self._write(data, encoding)
return self._read(num, encoding)
def _read_stb(self):
"Read status byte"
if self._driver_operation_simulate:
print("[simulating] Read status")
return 0
if not self._initialized or self._interface is None:
raise NotInitializedException()
try:
return self._interface.read_stb()
except (AttributeError, NotImplementedError):
return int(self._ask("*STB?"))
def _trigger(self):
"Device trigger"
if self._driver_operation_simulate:
print("[simulating] Trigger")
if not self._initialized or self._interface is None:
raise NotInitializedException()
try:
self._interface.trigger()
except (AttributeError, NotImplementedError):
self._write("*TRG")
def _clear(self):
"Device clear"
if self._driver_operation_simulate:
print("[simulating] Clear")
if not self._initialized or self._interface is None:
raise NotInitializedException()
try:
return self._interface.clear()
except (AttributeError, NotImplementedError):
self._write("*CLS")
def _remote(self):
"Device set remote"
if self._driver_operation_simulate:
print("[simulating] Remote")
if not self._initialized or self._interface is None:
raise NotInitializedException()
return self._interface.remote()
def _local(self):
"Device set local"
if self._driver_operation_simulate:
print("[simulating] Local")
if not self._initialized or self._interface is None:
raise NotInitializedException()
return self._interface.local()
def _read_ieee_block(self):
"Read IEEE block"
# IEEE block binary data is prefixed with #lnnnnnnnn
# where l is length of n and n is the
# length of the data
# ex: #800002000 prefixes 2000 data bytes
return decode_ieee_block(self._read_raw())
def _write_ieee_block(self, data, prefix = None, encoding = 'utf-8'):
"Write IEEE block"
# IEEE block binary data is prefixed with #lnnnnnnnn
# where l is length of n and n is the
# length of the data
# ex: #800002000 prefixes 2000 data bytes
block = b''
if type(prefix) == str:
block = prefix.encode(encoding)
elif type(prefix) == bytes:
block = prefix
block = block + build_ieee_block(data)
self._write_raw(block)
[docs] def doc(self, obj=None, itm=None, docs=None, prefix=None):
"""Python IVI documentation generator"""
# need an obj, if none specified, use self
if obj is None:
obj = self
# if first arg is a string, put in itm and use self for obj
if type(obj) == str:
itm = obj
obj = self
return doc(obj, itm, docs, prefix)
[docs] def help(self, itm=None, complete=False, indent=0):
"""Python IVI help system"""
return help(self, itm, complete, indent)