summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--.travis.yml37
-rw-r--r--AUTHORS1
-rw-r--r--CHANGES31
-rw-r--r--README2
-rw-r--r--docs/conf.py2
-rw-r--r--docs/index.rst24
-rw-r--r--pyvisa-py/common.py3
-rw-r--r--pyvisa-py/gpib.py191
-rw-r--r--pyvisa-py/highlevel.py185
-rw-r--r--pyvisa-py/protocols/rpc.py287
-rw-r--r--pyvisa-py/protocols/usbraw.py18
-rw-r--r--pyvisa-py/protocols/usbtmc.py117
-rw-r--r--pyvisa-py/protocols/usbutil.py9
-rw-r--r--pyvisa-py/protocols/vxi11.py86
-rw-r--r--pyvisa-py/serial.py76
-rw-r--r--pyvisa-py/sessions.py348
-rw-r--r--pyvisa-py/tcpip.py441
-rw-r--r--pyvisa-py/testsuite/__init__.py6
-rw-r--r--pyvisa-py/usb.py93
-rw-r--r--setup.py26
21 files changed, 1389 insertions, 598 deletions
diff --git a/.gitignore b/.gitignore
index ac02c9e..1ecccd2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,7 @@ MANIFEST
# WebDAV file system cache files
.DAV/
_test/
+.spyproject/
+.mypy_cache/
+.pytest_cache/
+.cache/ \ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 2b115fb..37021e7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,19 +1,36 @@
language: python
-python:
- - "2.6"
- - "2.7"
- - "3.2"
- - "3.3"
- - "3.4"
+# newer python versions are available only on xenial
+# (while some older only on trusty) Ubuntu distribution
+dist: xenial
+sudo: required
+
+branches:
+ only:
+ # This is where pull requests from "bors r+" are built.
+ - staging
+ # This is where pull requests from "bors try" are built.
+ - trying
+ # Build master too
+ - master
+
+matrix:
+ include:
+ - python: 2.7
+ dist: trusty
+ - python: 3.4
+ dist: trusty
+ - python: 3.5
+ dist: trusty
+ - python: 3.6
+ - python: 3.7
install:
- - if [ $TRAVIS_PYTHON_VERSION == '2.6' ]; then pip install unittest2; fi
- - pip install coverage coveralls
+ - pip install coverage
+ - pip install coveralls
script:
- - if [ $TRAVIS_PYTHON_VERSION == '2.6' ]; then coverage run -p --source=pyvisa-py --omit="*test*" setup.py test; fi
- - if [ $TRAVIS_PYTHON_VERSION != '2.6' ]; then python -bb -m coverage run -p --source=pyvisa-py --omit="*test*" setup.py test; fi
+ - python -bb -m coverage run -p --source=pyvisa-py --omit="*test*" setup.py test
- coverage combine
- coverage report -m
diff --git a/AUTHORS b/AUTHORS
index ea3490d..5f17e57 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -8,6 +8,7 @@ Other contributors, listed alphabetically, are:
* Colin Marquardt <github@marquardt-home.de>
* Lance McCulley <lancemcculley@gmail.com>
* Martin Ritter <ritter@mpp.mpg.de>
+* Matthieu Dartiailh <marul@laposte.net>
* Sebastian Held <sebastian.held@imst.de>
* Thomas Kopp <20.kopp@gmail.com>
* Thorsten Liebig <liebig@imst.de>
diff --git a/CHANGES b/CHANGES
index 70cc2d5..58c4944 100644
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,37 @@ PyVISA-py Changelog
===================
+0.3 (2018-09-05)
+----------------
+
+- Fix handling of seesion registration under Python 3.7 PR #155
+- Add read_stb, assert_trigger, lock, unlock to highlevel PR #139
+- Fix timeout handling in usb PR #144
+- Add gpib_command and assert_trigger to GPIB PR # 136
+- Handle ValueError in usb list fix #131 PR #132
+- Fix reading on GPIB and implement clear and gpib_send_ifc PR #132
+- Do not error when listing USB devices PR #126
+- Fix an error in the handling of the termchar for TCPIP INSTR PR #126
+- Make list_resources return an empty tuple instead of erroring PR #121
+- Proper support for timeout in TCPIP INSTR sessions PR #120 #127 #130 #144
+- Proper encoding of data before transfer for all backends PR #119
+- Unify use of StatusCode PR #118
+- Improve handling of sessions attrs PR #116
+- TCPIP SOCKET timeout handling improvement PR #115
+- Fix compatibility with pyserial 3.0 PR #112
+- TCPIP SOCKET handler read should not block PR #107
+- TCPIP error handling fixes PR #100
+- Use repr() instead of str() to log RPC record PR #97
+- Speed up large transfer over GPIB 2beb52a5bcea2dae32d4a9908dc19f7874bfc0b7
+- Catch GPIB errors while enumerating devices 9fea9d5c40cc6c33ce1244c209e5e576a33abfc2
+- Add a serial poll function to GPIB backend PR #67
+- Handle timeout in USB TMC backend PR #64
+- Make USB TMC backend faster by transferring multiple bytes PR #63
+- Fix issue with encoding before data transfer PR #59 #
+- Get Linux GPIB version PR #55
+- Fix broken import in TCPIP sessions PR #51
+
+
0.2 (2015-08-25)
----------------
diff --git a/README b/README
index 88404e7..1e35b58 100644
--- a/README
+++ b/README
@@ -38,7 +38,7 @@ Python has a couple of features that make it very interesting for measurement co
Requirements
------------
-- Python (tested with 2.6 and 2.7, 3.2+)
+- Python (tested with 2.7, 3.4+)
- PyVISA 1.6+
Optionally
diff --git a/docs/conf.py b/docs/conf.py
index 86a548c..896fd6c 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -45,10 +45,10 @@ master_doc = 'index'
# General information about the project.
project = 'PyVISA'
author = 'PyVISA Authors'
+
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
-
version = pkg_resources.get_distribution(project).version
release = version
this_year = datetime.date.today().year
diff --git a/docs/index.rst b/docs/index.rst
index 862532f..ca73826 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -50,6 +50,8 @@ It depends on the interface type. For **ASRL** and **USB** we use PySerial_ and
respectively. For **TCPIP** we use the :py:mod:`socket` module in the Python Standard Library.
**GPIB** resources are not currently supported but they are in the plan using `linux-gpib`_.
+PySerial_ version 3.0 or newer is required.
+
If I only need **TCPIP**, do I need to install PySerial and PyUSB?
------------------------------------------------------------------
@@ -75,24 +77,18 @@ Now:
- ASRL INSTR
- USB INSTR
- TCPIP INSTR
-
-Soon we will be supporting:
-
- USB RAW
- TCPIP SOCKET
-
-And later:
-
- GPIB INSTR
-If you want that `soon` or `later` becomes now, give us a hand!
Are all VISA attributes and methods implemented?
------------------------------------------------
-No. We have implemented those attributes and methods that are most commonly needed.
-We would like to reach feature parity. If there is something that you need, let us know.
+No. We have implemented those attributes and methods that are most commonly
+needed. We would like to reach feature parity. If there is something that you
+need, let us know.
Why are you developing this?
@@ -105,9 +101,9 @@ We wanted to provide a compatible alternative.
Why not using LibreVISA?
------------------------
-LibreVISA_ is still young. However, you can already use it with the NI backend as it
-has the same API. We think that PyVISA-py is easier to hack and we can quickly
-reach feature parity with NI-VISA for message-based instruments.
+LibreVISA_ is still young. However, you can already use it with the NI backend
+as it has the same API. We think that PyVISA-py is easier to hack and we can
+quickly reach feature parity with NI-VISA for message-based instruments.
Why putting PyVISA in the middle?
@@ -125,9 +121,9 @@ from higher level applications.
.. _PyVISA: http://pyvisa.readthedocs.org/
.. _PyUSB: http://walac.github.io/pyusb/
.. _PyPI: https://pypi.python.org/pypi/PyVISA-py
-.. _GitHub: https://github.com/hgrecco/pyvisa-py
+.. _GitHub: https://github.com/pyvisa/pyvisa-py
.. _`National Instruments's VISA`: http://ni.com/visa/
.. _`LibreVISA`: http://www.librevisa.org/
-.. _`issue tracker`: https://github.com/hgrecco/pyvisa-py/issues
+.. _`issue tracker`: https://github.com/pyvisa/pyvisa-py/issues
.. _`linux-gpib`: http://linux-gpib.sourceforge.net/
diff --git a/pyvisa-py/common.py b/pyvisa-py/common.py
index 1f91543..acf9edc 100644
--- a/pyvisa-py/common.py
+++ b/pyvisa-py/common.py
@@ -9,7 +9,8 @@
:license: MIT, see LICENSE for more details.
"""
-from __future__ import division, unicode_literals, print_function, absolute_import
+from __future__ import (division, unicode_literals, print_function,
+ absolute_import)
import logging
import sys
diff --git a/pyvisa-py/gpib.py b/pyvisa-py/gpib.py
index ef495d0..a80201b 100644
--- a/pyvisa-py/gpib.py
+++ b/pyvisa-py/gpib.py
@@ -13,18 +13,16 @@
from __future__ import division, unicode_literals, print_function, absolute_import
from bisect import bisect
-from pyvisa import constants, logger
+from pyvisa import constants, logger, attributes
from .sessions import Session, UnknownAttribute
try:
import gpib
from Gpib import Gpib
-
except ImportError as e:
Session.register_unavailable(constants.InterfaceType.gpib, 'INSTR',
'Please install linux-gpib to use this resource type.\n%s' % e)
-
raise
@@ -32,17 +30,18 @@ def _find_listeners():
"""Find GPIB listeners.
"""
for i in range(31):
- if gpib.listener(BOARD, i) and gpib.ask(BOARD, 1) != i:
- yield i
+ try:
+ if gpib.listener(BOARD, i) and gpib.ask(BOARD, 1) != i:
+ yield i
+ except gpib.GpibError as e:
+ logger.debug("GPIB error in _find_listeners(): %s", repr(e))
StatusCode = constants.StatusCode
-SUCCESS = StatusCode.success
-
-# linux-gpib timeout constants, in milliseconds. See self.timeout.
-TIMETABLE = (0, 1e-2, 3e-2, 1e-1, 3e-1, 1e0, 3e0, 1e1, 3e1, 1e2, 3e2, 1e3, 3e3,
- 1e4, 3e4, 1e5, 3e5, 1e6)
+# linux-gpib timeout constants, in seconds. See GPIBSession._set_timeout.
+TIMETABLE = (0, 10e-6, 30e-6, 100e-6, 300e-6, 1e-3, 3e-3, 10e-3, 30e-3, 100e-3, 300e-3, 1.0, 3.0,
+ 10.0, 30.0, 100.0, 300.0, 1000.0)
# TODO: Check board indices other than 0.
BOARD = 0
@@ -56,22 +55,41 @@ class GPIBSession(Session):
def list_resources():
return ['GPIB0::%d::INSTR' % pad for pad in _find_listeners()]
- def after_parsing(self):
- minor = self.parsed.board
- pad = self.parsed.primary_address
- self.handle = gpib.dev(int(minor), int(pad))
- self.interface = Gpib(self.handle)
-
- @property
- def timeout(self):
+ @classmethod
+ def get_low_level_info(cls):
+ try:
+ ver = gpib.version()
+ except AttributeError:
+ ver = '< 4.0'
- # 0x3 is the hexadecimal reference to the IbaTMO (timeout) configuration
- # option in linux-gpib.
- return TIMETABLE[self.interface.ask(3)]
+ return 'via Linux GPIB (%s)' % ver
- @timeout.setter
- def timeout(self, value):
+ def after_parsing(self):
+ minor = int(self.parsed.board)
+ pad = int(self.parsed.primary_address)
+ sad = 0
+ timeout = 13
+ send_eoi = 1
+ eos_mode = 0
+ self.interface = Gpib(name=minor, pad=pad, sad=sad, timeout=timeout, send_eoi=send_eoi, eos_mode=eos_mode)
+ self.controller = Gpib(name=minor) # this is the bus controller device
+ self.handle = self.interface.id
+ # force timeout setting to interface
+ self.set_attribute(constants.VI_ATTR_TMO_VALUE, attributes.AttributesByID[constants.VI_ATTR_TMO_VALUE].default)
+
+ def _get_timeout(self, attribute):
+ if self.interface:
+ # 0x3 is the hexadecimal reference to the IbaTMO (timeout) configuration
+ # option in linux-gpib.
+ gpib_timeout = self.interface.ask(3)
+ if gpib_timeout and gpib_timeout < len(TIMETABLE):
+ self.timeout = TIMETABLE[gpib_timeout]
+ else:
+ # value is 0 or out of range -> infinite
+ self.timeout = None
+ return super(GPIBSession, self)._get_timeout(attribute)
+ def _set_timeout(self, attribute, value):
"""
linux-gpib only supports 18 discrete timeout values. If a timeout
value other than these is requested, it will be rounded up to the closest
@@ -96,7 +114,16 @@ class GPIBSession(Session):
16 300 seconds
17 1000 seconds
"""
- self.interface.timeout(bisect(TIMETABLE, value))
+ status = super(GPIBSession, self)._set_timeout(attribute, value)
+ if self.interface:
+ if self.timeout is None:
+ gpib_timeout = 0
+ else:
+ # round up only values that are higher by 0.1% than discrete values
+ gpib_timeout = min(bisect(TIMETABLE, 0.999 * self.timeout), 17)
+ self.timeout = TIMETABLE[gpib_timeout]
+ self.interface.timeout(gpib_timeout)
+ return status
def close(self):
gpib.close(self.handle)
@@ -114,7 +141,7 @@ class GPIBSession(Session):
# 0x2000 = 8192 = END
checker = lambda current: self.interface.ibsta() & 8192
- reader = lambda: self.interface.read(1)
+ reader = lambda: self.interface.read(count)
return self._read(reader, count, checker, False, None, False, gpib.GpibError)
@@ -133,8 +160,9 @@ class GPIBSession(Session):
try:
self.interface.write(data)
+ count = self.interface.ibcnt() # number of bytes transmitted
- return SUCCESS
+ return count, StatusCode.success
except gpib.GpibError:
# 0x4000 = 16384 = TIMO
@@ -143,6 +171,85 @@ class GPIBSession(Session):
else:
return 0, StatusCode.error_system_error
+ def clear(self):
+ """Clears a device.
+
+ Corresponds to viClear function of the VISA library.
+
+ :param session: Unique logical identifier to a session.
+ :return: return value of the library call.
+ :rtype: :class:`pyvisa.constants.StatusCode`
+ """
+
+ logger.debug('GPIB.device clear')
+
+ try:
+ self.interface.clear()
+ return 0, StatusCode.success
+ except Exception:
+ return 0, StatusCode.error_system_error
+
+ def gpib_command(self, command_byte):
+ """Write GPIB command byte on the bus.
+
+ Corresponds to viGpibCommand function of the VISA library.
+ See: https://linux-gpib.sourceforge.io/doc_html/gpib-protocol.html#REFERENCE-COMMAND-BYTES
+
+ :param command_byte: command byte to send
+ :type command_byte: int, must be [0 255]
+ :return: return value of the library call
+ :rtype: :class:`pyvisa.constants.StatusCode`
+ """
+
+ if 0 <= command_byte <= 255:
+ data = chr(command_byte)
+ else:
+ return StatusCode.error_nonsupported_operation
+
+ try:
+ self.controller.command(data)
+ return StatusCode.success
+
+ except gpib.GpibError:
+ return StatusCode.error_system_error
+
+ def trigger(self, protocol):
+ """Asserts hardware trigger.
+ Only supports protocol = constants.VI_TRIG_PROT_DEFAULT
+
+ :return: return value of the library call.
+ :rtype: :class:`pyvisa.constants.StatusCode`
+ """
+
+ logger.debug('GPIB.device assert hardware trigger')
+
+ try:
+ if protocol == constants.VI_TRIG_PROT_DEFAULT:
+ self.interface.trigger()
+ return StatusCode.success
+ else:
+ return StatusCode.error_nonsupported_operation
+ except gpib.GpibError:
+ return StatusCode.error_system_error
+
+ def gpib_send_ifc(self):
+ """Pulse the interface clear line (IFC) for at least 100 microseconds.
+
+ Corresponds to viGpibSendIFC function of the VISA library.
+
+ :param session: Unique logical identifier to a session.
+ :return: return value of the library call.
+ :rtype: :class:`pyvisa.constants.StatusCode`
+ """
+
+ logger.debug('GPIB.interface clear')
+
+ try:
+ self.controller.interface_clear()
+ return 0, StatusCode.success
+ except:
+ return 0, StatusCode.error_system_error
+
def _get_attribute(self, attribute):
"""Get the value for a given VISA attribute for this session.
@@ -156,20 +263,20 @@ class GPIBSession(Session):
if attribute == constants.VI_ATTR_GPIB_READDR_EN:
# IbaREADDR 0x6
# Setting has no effect in linux-gpib.
- return self.interface.ask(6), SUCCESS
+ return self.interface.ask(6), StatusCode.success
elif attribute == constants.VI_ATTR_GPIB_PRIMARY_ADDR:
# IbaPAD 0x1
- return self.interface.ask(1), SUCCESS
+ return self.interface.ask(1), StatusCode.success
elif attribute == constants.VI_ATTR_GPIB_SECONDARY_ADDR:
# IbaSAD 0x2
# Remove 0x60 because National Instruments.
sad = self.interface.ask(2)
if self.interface.ask(2):
- return self.interface.ask(2) - 96, SUCCESS
+ return self.interface.ask(2) - 96, StatusCode.success
else:
- return constants.VI_NO_SEC_ADDR, SUCCESS
+ return constants.VI_NO_SEC_ADDR, StatusCode.success
elif attribute == constants.VI_ATTR_GPIB_REN_STATE:
# I have no idea how to implement this.
@@ -178,23 +285,23 @@ class GPIBSession(Session):
elif attribute == constants.VI_ATTR_GPIB_UNADDR_EN:
# IbaUnAddr 0x1b
if self.interface.ask(27):
- return constants.VI_TRUE, SUCCESS
+ return constants.VI_TRUE, StatusCode.success
else:
- return constants.VI_FALSE, SUCCESS
+ return constants.VI_FALSE, StatusCode.success
elif attribute == constants.VI_ATTR_SEND_END_EN:
# IbaEndBitIsNormal 0x1a
if self.interface.ask(26):
- return constants.VI_TRUE, SUCCESS
+ return constants.VI_TRUE, StatusCode.success
else:
- return constants.VI_FALSE, SUCCESS
+ return constants.VI_FALSE, StatusCode.success
elif attribute == constants.VI_ATTR_INTF_NUM:
# IbaBNA 0x200
- return self.interface.ask(512), SUCCESS
+ return self.interface.ask(512), StatusCode.success
elif attribute == constants.VI_ATTR_INTF_TYPE:
- return constants.InterfaceType.gpib, SUCCESS
+ return constants.InterfaceType.gpib, StatusCode.success
raise UnknownAttribute(attribute)
@@ -214,7 +321,7 @@ class GPIBSession(Session):
# Setting has no effect in linux-gpib.
if isinstance(attribute_state, int):
self.interface.config(6, attribute_state)
- return SUCCESS
+ return StatusCode.success
else:
return StatusCode.error_nonsupported_attribute_state
@@ -222,7 +329,7 @@ class GPIBSession(Session):
# IbcPAD 0x1
if isinstance(attribute_state, int) and 0 <= attribute_state <= 30:
self.interface.config(1, attribute_state)
- return SUCCESS
+ return StatusCode.success
else:
return StatusCode.error_nonsupported_attribute_state
@@ -232,7 +339,7 @@ class GPIBSession(Session):
if isinstance(attribute_state, int) and 0 <= attribute_state <= 30:
if self.interface.ask(2):
self.interface.config(2, attribute_state + 96)
- return SUCCESS
+ return StatusCode.success
else:
return StatusCode.error_nonsupported_attribute
else:
@@ -242,7 +349,7 @@ class GPIBSession(Session):
# IbcUnAddr 0x1b
try:
self.interface.config(27, attribute_state)
- return SUCCESS
+ return StatusCode.success
except gpib.GpibError:
return StatusCode.error_nonsupported_attribute_state
@@ -250,9 +357,11 @@ class GPIBSession(Session):
# IbcEndBitIsNormal 0x1a
if isinstance(attribute_state, int):
self.interface.config(26, attribute_state)
- return SUCCESS
+ return StatusCode.success
else:
return StatusCode.error_nonsupported_attribute_state
raise UnknownAttribute(attribute)
+ def read_stb(self):
+ return self.interface.serial_poll()
diff --git a/pyvisa-py/highlevel.py b/pyvisa-py/highlevel.py
index 1753e01..33d1201 100644
--- a/pyvisa-py/highlevel.py
+++ b/pyvisa-py/highlevel.py
@@ -22,6 +22,8 @@ from pyvisa.compat import integer_types, OrderedDict
from . import sessions
from .common import logger
+StatusCode = constants.StatusCode
+
class PyVisaLibrary(highlevel.VisaLibraryBase):
"""A pure Python backend for PyVISA.
@@ -42,25 +44,25 @@ class PyVisaLibrary(highlevel.VisaLibraryBase):
try:
from .serial import SerialSession
logger.debug('SerialSession was correctly imported.')
- except ImportError as e:
+ except Exception as e:
logger.debug('SerialSession was not imported %s.' % e)
try:
from .usb import USBSession, USBRawSession
logger.debug('USBSession and USBRawSession were correctly imported.')
- except ImportError as e:
+ except Exception as e:
logger.debug('USBSession and USBRawSession were not imported %s.' % e)
try:
- from .tcpip import TCPIPSession
+ from .tcpip import TCPIPInstrSession, TCPIPSocketSession
logger.debug('TCPIPSession was correctly imported.')
- except ImportError as e:
+ except Exception as e:
logger.debug('TCPIPSession was not imported %s.' % e)
try:
from .gpib import GPIBSession
logger.debug('GPIBSession was correctly imported.')
- except ImportError as e:
+ except Exception as e:
logger.debug('GPIBSession was not imported %s.' % e)
@classmethod
@@ -122,7 +124,7 @@ class PyVisaLibrary(highlevel.VisaLibraryBase):
extra=self._logging_extra)
try:
- ret_value = constants.StatusCode(ret_value)
+ ret_value = StatusCode(ret_value)
except ValueError:
pass
@@ -162,7 +164,8 @@ class PyVisaLibrary(highlevel.VisaLibraryBase):
# noinspection PyShadowingBuiltins
def open(self, session, resource_name,
- access_mode=constants.AccessModes.no_lock, open_timeout=constants.VI_TMO_IMMEDIATE):
+ access_mode=constants.AccessModes.no_lock,
+ open_timeout=constants.VI_TMO_IMMEDIATE):
"""Opens a session to the specified resource.
Corresponds to viOpen function of the VISA library.
@@ -184,13 +187,105 @@ class PyVisaLibrary(highlevel.VisaLibraryBase):
try:
parsed = rname.parse_resource_name(resource_name)
except rname.InvalidResourceName:
- return 0, constants.StatusCode.error_invalid_resource_name
+ return 0, StatusCode.error_invalid_resource_name
cls = sessions.Session.get_session_class(parsed.interface_type_const, parsed.resource_class)
- sess = cls(session, resource_name, parsed)
+ sess = cls(session, resource_name, parsed, open_timeout)
+
+ return self._register(sess), StatusCode.success
+
+ def assert_trigger(self, session, protocol):
+ """Asserts software or hardware trigger.
+
+ Corresponds to viAssertTrigger function of the VISA library.
+
+ :param session: Unique logical identifier to a session.
+ :param protocol: Trigger protocol to use during assertion. (Constants.PROT*)
+ :return: return value of the library call.
+ :rtype: :class:`pyvisa.constants.StatusCode`
+ """
+ try:
+ sess = self.sessions[session]
+ except KeyError:
+ return 0, constants.StatusCode.error_invalid_object
+ return sess.assert_trigger(protocol)
+
+ def clear(self, session):
+ """Clears a device.
+
+ Corresponds to viClear function of the VISA library.
+
+ :param session: Unique logical identifier to a session.
+ :return: return value of the library call.
+ :rtype: :class:`pyvisa.constants.StatusCode`
+ """
+ try:
+ sess = self.sessions[session]
+ except KeyError:
+ return 0, constants.StatusCode.error_invalid_object
+ return sess.clear()
+
+ def gpib_command(self, session, command_byte):
+ """Write GPIB command byte on the bus.
+
+ Corresponds to viGpibCommand function of the VISA library.
+ See: https://linux-gpib.sourceforge.io/doc_html/gpib-protocol.html#REFERENCE-COMMAND-BYTES
+
+ :param command_byte: command byte to send
+ :type command_byte: int, must be [0 255]
+ :return: return value of the library call
+ :rtype: :class:`pyvisa.constants.StatusCode`
+ """
+ try:
+ return self.sessions[session].gpib_command(command_byte)
+
+ except KeyError:
+ return constants.StatusCode.error_invalid_object
+
+ def assert_trigger(self, session, protocol):
+ """Asserts software or hardware trigger.
+
+ Corresponds to viAssertTrigger function of the VISA library.
+
+ :param session: Unique logical identifier to a session.
+ :param protocol: Trigger protocol to use during assertion. (Constants.PROT*)
+ :return: return value of the library call.
+ :rtype: :class:`pyvisa.constants.StatusCode`
+ """
+ try:
+ return self.sessions[session].trigger(protocol)
+
+ except KeyError:
+ return constants.StatusCode.error_invalid_object
+
+ def gpib_send_ifc(self, session):
+ """Pulse the interface clear line (IFC) for at least 100 microseconds.
+
+ Corresponds to viGpibSendIFC function of the VISA library.
+
+ :param session: Unique logical identifier to a session.
+ :return: return value of the library call.
+ :rtype: :class:`pyvisa.constants.StatusCode`
+ """
+ try:
+ sess = self.sessions[session]
+ except KeyError:
+ return 0, constants.StatusCode.error_invalid_object
+ return sess.gpib_send_ifc()
- return self._register(sess), constants.StatusCode.success
+ def read_stb(self, session):
+ """Reads a status byte of the service request.
+ Corresponds to viReadSTB function of the VISA library.
+ :param session: Unique logical identifier to a session.
+ :return: Service request status byte, return value of the library call.
+ :rtype: int, :class:`pyvisa.constants.StatusCode`
+ """
+ try:
+ sess = self.sessions[session]
+ except KeyError:
+ return 0, constants.StatusCode.error_invalid_object
+ return sess.read_stb()
def close(self, session):
"""Closes the specified session, event, or find list.
@@ -206,7 +301,7 @@ class PyVisaLibrary(highlevel.VisaLibraryBase):
if sess is not self:
sess.close()
except KeyError:
- return constants.StatusCode.error_invalid_object
+ return StatusCode.error_invalid_object
def open_default_resource_manager(self):
"""This function returns a session to the Default Resource Manager resource.
@@ -216,7 +311,7 @@ class PyVisaLibrary(highlevel.VisaLibraryBase):
:return: Unique logical identifier to a Default Resource Manager session, return value of the library call.
:rtype: session, VISAStatus
"""
- return self._register(self), constants.StatusCode.success
+ return self._register(self), StatusCode.success
def list_resources(self, session, query='?*::INSTR'):
"""Returns a tuple of all connected devices matching query.
@@ -232,10 +327,7 @@ class PyVisaLibrary(highlevel.VisaLibraryBase):
resources = rname.filter(resources, query)
- if resources:
- return resources
-
- raise errors.VisaIOError(errors.StatusCode.error_resource_not_found.value)
+ return resources
def read(self, session, count):
"""Reads data from device or interface synchronously.
@@ -250,9 +342,14 @@ class PyVisaLibrary(highlevel.VisaLibraryBase):
# from the session handle, dispatch to the read method of the session object.
try:
- return self.sessions[session].read(count)
+ ret = self.sessions[session].read(count)
except KeyError:
- return constants.StatusCode.error_invalid_object
+ return 0, StatusCode.error_invalid_object
+
+ if ret[1] < 0:
+ raise errors.VisaIOError(ret[1])
+
+ return ret
def write(self, session, data):
"""Writes data to device or interface synchronously.
@@ -268,9 +365,14 @@ class PyVisaLibrary(highlevel.VisaLibraryBase):
# from the session handle, dispatch to the write method of the session object.
try:
- return self.sessions[session].write(data)
+ ret = self.sessions[session].write(data)
except KeyError:
- return constants.StatusCode.error_invalid_object
+ return 0, StatusCode.error_invalid_object
+
+ if ret[1] < 0:
+ raise errors.VisaIOError(ret[1])
+
+ return ret
def get_attribute(self, session, attribute):
"""Retrieves the state of an attribute.
@@ -285,7 +387,7 @@ class PyVisaLibrary(highlevel.VisaLibraryBase):
try:
sess = self.sessions[session]
except KeyError:
- return None, constants.StatusCode.error_invalid_object
+ return None, StatusCode.error_invalid_object
return sess.get_attribute(attribute)
@@ -304,10 +406,46 @@ class PyVisaLibrary(highlevel.VisaLibraryBase):
try:
sess = self.sessions[session]
except KeyError:
- return constants.StatusCode.error_invalid_object
+ return StatusCode.error_invalid_object
return sess.set_attribute(attribute, attribute_state)
-
+
+ def lock(self, session, lock_type, timeout, requested_key=None):
+ """Establishes an access mode to the specified resources.
+
+ Corresponds to viLock function of the VISA library.
+
+ :param session: Unique logical identifier to a session.
+ :param lock_type: Specifies the type of lock requested, either Constants.EXCLUSIVE_LOCK or Constants.SHARED_LOCK.
+ :param timeout: Absolute time period (in milliseconds) that a resource waits to get unlocked by the
+ locking session before returning an error.
+ :param requested_key: This parameter is not used and should be set to VI_NULL when lockType is VI_EXCLUSIVE_LOCK.
+ :return: access_key that can then be passed to other sessions to share the lock, return value of the library call.
+ :rtype: str, :class:`pyvisa.constants.StatusCode`
+ """
+ try:
+ sess = self.sessions[session]
+ except KeyError:
+ return StatusCode.error_invalid_object
+
+ return sess.lock(lock_type, timeout, requested_key)
+
+ def unlock(self, session):
+ """Relinquishes a lock for the specified resource.
+
+ Corresponds to viUnlock function of the VISA library.
+
+ :param session: Unique logical identifier to a session.
+ :return: return value of the library call.
+ :rtype: :class:`pyvisa.constants.StatusCode`
+ """
+ try:
+ sess = self.sessions[session]
+ except KeyError:
+ return StatusCode.error_invalid_object
+
+ return sess.unlock()
+
def disable_event(self, session, event_type, mechanism):
# TODO: implement this for GPIB finalization
pass
@@ -315,4 +453,3 @@ class PyVisaLibrary(highlevel.VisaLibraryBase):
def discard_events(self, session, event_type, mechanism):
# TODO: implement this for GPIB finalization
pass
-
diff --git a/pyvisa-py/protocols/rpc.py b/pyvisa-py/protocols/rpc.py
index 66fe51c..c5c1245 100644
--- a/pyvisa-py/protocols/rpc.py
+++ b/pyvisa-py/protocols/rpc.py
@@ -13,21 +13,23 @@
XXX The UDP version of the protocol resends requests when it does
XXX not receive a timely reply -- use only for idempotent calls!
- XXX There is no provision for call timeout on TCP connections
-
- Original source: http://svn.python.org/projects/python/trunk/Demo/rpc/rpc.py
+ Original source:
+ http://svn.python.org/projects/python/trunk/Demo/rpc/rpc.py
:copyright: 2014 by PyVISA-py Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
-from __future__ import division, unicode_literals, print_function, absolute_import
+from __future__ import (division, unicode_literals, print_function,
+ absolute_import)
import sys
import enum
import xdrlib
import socket
+import select
+import time
from pyvisa.compat import struct
@@ -197,7 +199,8 @@ class Unpacker(xdrlib.Unpacker):
if stat == RejectStatus.rpc_mismatch:
low = self.unpack_uint()
high = self.unpack_uint()
- raise RPCUnpackError('denied: rpc_mismatch: %r' % ((low, high),))
+ raise RPCUnpackError('denied: rpc_mismatch: %r' %
+ ((low, high),))
if stat == RejectStatus.auth_error:
stat = self.unpack_uint()
raise RPCUnpackError('denied: auth_error: %r' % (stat,))
@@ -211,7 +214,8 @@ class Unpacker(xdrlib.Unpacker):
if stat == AcceptStatus.program_mismatch:
low = self.unpack_uint()
high = self.unpack_uint()
- raise RPCUnpackError('call failed: program_mismatch: %r' % ((low, high),))
+ raise RPCUnpackError('call failed: program_mismatch: %r' %
+ ((low, high),))
if stat == AcceptStatus.procedure_unavailable:
raise RPCUnpackError('call failed: procedure_unavailable')
if stat == AcceptStatus.garbage_args:
@@ -237,7 +241,8 @@ class Client(object):
def make_call(self, proc, args, pack_func, unpack_func):
# Don't normally override this (but see Broadcast)
- logger.debug('Make call %r, %r, %r, %r', proc, args, pack_func, unpack_func)
+ logger.debug('Make call %r, %r, %r, %r',
+ proc, args, pack_func, unpack_func)
if pack_func is None and args is not None:
raise TypeError('non-null args with null pack_func')
@@ -254,12 +259,12 @@ class Client(object):
def start_call(self, proc):
# Don't override this
- self.lastxid = xid = self.lastxid + 1
+ self.lastxid += 1
cred = self.mkcred()
verf = self.mkverf()
p = self.packer
p.reset()
- p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf)
+ p.pack_callheader(self.lastxid, self.prog, self.vers, proc, cred, verf)
def do_call(self):
# This MUST be overridden
@@ -292,80 +297,200 @@ def sendfrag(sock, last, frag):
sock.send(header + frag)
-def sendrecord(sock, record):
- logger.debug('Sending record through %s: %s', sock, record)
- sendfrag(sock, 1, record)
-
-
-def recvfrag(sock):
- header = sock.recv(4)
- if len(header) < 4:
- raise EOFError
- x = struct.unpack(">I", header[0:4])[0]
- last = ((x & 0x80000000) != 0)
- n = int(x & 0x7fffffff)
- frag = b''
- while n > 0:
- buf = sock.recv(n)
- if not buf:
- raise EOFError
- n = n - len(buf)
- frag = frag + buf
- return last, frag
+def _sendrecord(sock, record, fragsize=None, timeout=None):
+ logger.debug('Sending record through %s: %r', sock, record)
+ if timeout is not None:
+ r, w, x = select.select([], [sock], [], timeout)
+ if sock not in w:
+ msg = ("socket.timeout: The instrument seems to have stopped "
+ "responding.")
+ raise socket.timeout(msg)
-
-def recvrecord(sock):
- record = b''
- last = 0
+ last = False
+ if not fragsize:
+ fragsize = 0x7fffffff
while not last:
- last, frag = recvfrag(sock)
- record = record + frag
-
- logger.debug('Received record through %s: %r', sock, record)
-
- return record
+ record_len = len(record)
+ if record_len <= fragsize:
+ fragsize = record_len
+ last = True
+ if last:
+ fragsize = fragsize | 0x80000000
+ header = struct.pack(">I", fragsize)
+ sock.send(header + record[:fragsize])
+ record = record[fragsize:]
+
+
+def _recvrecord(sock, timeout, read_fun=None):
+
+ record = bytearray()
+ buffer = bytearray()
+ if not read_fun:
+ read_fun = sock.recv
+
+ wait_header = True
+ last = False
+ exp_length = 4
+
+ # minimum is in interval 1 - 100ms based on timeout or for infinite it is
+ # 1 sec
+ min_select_timeout = (max(min(timeout/100.0, 0.1), 0.001)
+ if timeout is not None else 1.0)
+ # initial 'select_timout' is half of timeout or max 2 secs
+ # (max blocking time).
+ # min is from 'min_select_timeout'
+ select_timout = (max(min(timeout/2.0, 2.0), min_select_timeout)
+ if timeout is not None else 1.0)
+ # time, when loop shall finish
+ finish_time = time.time() + timeout if timeout is not None else 0
+ while True:
+
+ # use select to wait for read ready, max `select_timout` seconds
+ r, w, x = select.select([sock], [], [], select_timout)
+ read_data = b''
+ if sock in r:
+ read_data = read_fun(exp_length)
+ buffer.extend(read_data)
+ elif timeout is not None and time.time() >= finish_time:
+ # reached timeout
+ logger.debug(('Time out encountered in %s.'
+ 'Already receieved %d bytes. Last fragment is %d '
+ 'bytes long and we were expecting %d'),
+ sock, len(record), len(buffer), exp_length)
+ msg = ("socket.timeout: The instrument seems to have stopped "
+ "responding.")
+ raise socket.timeout(msg)
+ else:
+ # `select_timout` decreased to 50% of previous or
+ # min_select_timeout
+ select_timout = max(select_timout/2.0, min_select_timeout)
+ continue
+
+ if wait_header:
+ # need to find header
+ if len(buffer) >= exp_length:
+ header = buffer[:exp_length]
+ buffer = buffer[exp_length:]
+ x = struct.unpack(">I", header)[0]
+ last = ((x & 0x80000000) != 0)
+ exp_length = int(x & 0x7fffffff)
+ wait_header = False
+ else:
+ if len(buffer) >= exp_length:
+ record.extend(buffer[:exp_length])
+ buffer = buffer[exp_length:]
+ if last:
+ logger.debug('Received record through %s: %r',
+ sock, record)
+ return bytes(record)
+ else:
+ wait_header = True
+ exp_length = 4
+
+
+def _connect(sock, host, port, timeout=0):
+ try:
+ sock.setblocking(0)
+ sock.connect_ex((host, port))
+ except Exception as e:
+ sock.close()
+ return False
+ finally:
+ sock.setblocking(1)
+
+ # minimum is in interval 100 - 500ms based on timeout
+ min_select_timeout = max(min(timeout/10.0, 0.5), 0.1)
+ # initial 'select_timout' is half of timeout or max 2 secs
+ # (max blocking time).
+ # min is from 'min_select_timeout'
+ select_timout = max(min(timeout/2.0, 2.0), min_select_timeout)
+ # time, when loop shall finish
+ finish_time = time.time() + timeout
+ while True:
+ # use select to wait for socket ready, max `select_timout` seconds
+ r, w, x = select.select([sock], [sock], [], select_timout)
+ if sock in r or sock in w:
+ return True
+
+ if time.time() >= finish_time:
+ # reached timeout
+ return False
+
+ # `select_timout` decreased to 50% of previous or min_select_timeout
+ select_timout = max(select_timout/2.0, min_select_timeout)
class RawTCPClient(Client):
"""Client using TCP to a specific port.
+
"""
- def __init__(self, host, prog, vers, port):
+ def __init__(self, host, prog, vers, port, open_timeout=5000):
Client.__init__(self, host, prog, vers, port)
- self.connect()
-
- def connect(self):
- logger.debug('RawTCPClient: connecting to socket at (%s, %s)', self.host, self.port)
+ self.connect((open_timeout / 1000.0) + 1.0)
+ # self.timeout defaults higher than the default 2 second VISA timeout,
+ # ensuring that VISA timeouts take precedence.
+ self.timeout = 4.0
+
+ def make_call(self, proc, args, pack_func, unpack_func):
+ """Overridden to allow for utilizing io_timeout (passed in args).
+
+ """
+ if proc == 11:
+ # vxi11.DEVICE_WRITE
+ self.timeout = (args[1] / 1000.0) + 2.0
+ elif proc in (12, 22):
+ # vxi11.DEVICE_READ or vxi11.DEVICE_DOCMD
+ self.timeout = (args[2] / 1000.0) + 2.0
+ elif proc in (13, 14, 15, 16, 17):
+ # vxi11.DEVICE_READSTB, vxi11.DEVICE_TRIGGER, vxi11.DEVICE_CLEAR,
+ # vxi11.DEVICE_REMOTE, or vxi11.DEVICE_LOCAL
+ self.timeout = (args[3] / 1000.0) + 2.0
+ else:
+ self.timeout = 4.0
+
+ return super(RawTCPClient, self).make_call(proc, args, pack_func,
+ unpack_func)
+
+ def connect(self, timeout=5.0):
+ logger.debug('RawTCPClient: connecting to socket at (%s, %s)',
+ self.host, self.port)
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- self.sock.connect((self.host, self.port))
-
+ if not _connect(self.sock, self.host, self.port, timeout):
+ raise RPCError('can\'t connect to server')
+
def close(self):
logger.debug('RawTCPClient: closing socket')
self.sock.close()
-
+
def do_call(self):
call = self.packer.get_buf()
- sendrecord(self.sock, call)
- reply = recvrecord(self.sock)
+
+ _sendrecord(self.sock, call, timeout=self.timeout)
+
+ reply = _recvrecord(self.sock, self.timeout)
u = self.unpacker
u.reset(reply)
xid, verf = u.unpack_replyheader()
if xid != self.lastxid:
# Can't really happen since this is TCP...
- raise RPCError('wrong xid in reply %r instead of %r' % (xid, self.lastxid))
+ msg = 'wrong xid in reply {0} instead of {1}'
+ raise RPCError(msg.format(xid, self.lastxid))
-# Client using UDP to a specific port
-
class RawUDPClient(Client):
+ """Client using UDP to a specific port.
+
+ """
def __init__(self, host, prog, vers, port):
Client.__init__(self, host, prog, vers, port)
self.connect()
-
+
def connect(self):
- logger.debug('RawTCPClient: connecting to socket at (%s, %s)', self.host, self.port)
+ logger.debug('RawTCPClient: connecting to socket at (%s, %s)',
+ self.host, self.port)
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.connect((self.host, self.port))
-
+
def close(self):
logger.debug('RawTCPClient: closing socket')
self.sock.close()
@@ -373,18 +498,14 @@ class RawUDPClient(Client):
def do_call(self):
call = self.packer.get_buf()
self.sock.send(call)
- try:
- from select import select
- except ImportError:
- logger.warn('select not found, RPC may hang')
- select = None
+
BUFSIZE = 8192 # Max UDP buffer size
timeout = 1
count = 5
while 1:
r, w, x = [self.sock], [], []
if select:
- r, w, x = select(r, w, x, timeout)
+ r, w, x = select.select(r, w, x, timeout)
if self.sock not in r:
count = count - 1
if count < 0:
@@ -410,7 +531,7 @@ class RawBroadcastUDPClient(RawUDPClient):
RawUDPClient.__init__(self, bcastaddr, prog, vers, port)
self.reply_handler = None
self.timeout = 30
-
+
def connect(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
@@ -429,11 +550,7 @@ class RawBroadcastUDPClient(RawUDPClient):
pack_func(args)
call = self.packer.get_buf()
self.sock.sendto(call, (self.host, self.port))
- try:
- from select import select
- except ImportError:
- logger.warn('select not found, broadcast will hang')
- select = None
+
BUFSIZE = 8192 # Max UDP buffer size (for reply)
replies = []
if unpack_func is None:
@@ -444,9 +561,9 @@ class RawBroadcastUDPClient(RawUDPClient):
r, w, x = [self.sock], [], []
if select:
if self.timeout is None:
- r, w, x = select(r, w, x)
+ r, w, x = select.select(r, w, x)
else:
- r, w, x = select(r, w, x, self.timeout)
+ r, w, x = select.select(r, w, x, self.timeout)
if self.sock not in r:
break
reply, fromaddr = self.sock.recvfrom(BUFSIZE)
@@ -485,8 +602,8 @@ class PortMapperVersion(enum.IntEnum):
#: (call_args) -> call_result
call_it = 5
-# A mapping is (prog, vers, prot, port) and prot is one of:
+# A mapping is (prog, vers, prot, port) and prot is one of:
IPPROTO_TCP = 6
IPPROTO_UDP = 17
@@ -569,8 +686,9 @@ class PartialPortMapperClient(object):
class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient):
- def __init__(self, host):
- RawTCPClient.__init__(self, host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
+ def __init__(self, host, open_timeout=5000):
+ RawTCPClient.__init__(self, host, PMAP_PROG, PMAP_VERS, PMAP_PORT,
+ open_timeout)
PartialPortMapperClient.__init__(self)
@@ -581,23 +699,25 @@ class UDPPortMapperClient(PartialPortMapperClient, RawUDPClient):
PartialPortMapperClient.__init__(self)
-class BroadcastUDPPortMapperClient(PartialPortMapperClient, RawBroadcastUDPClient):
+class BroadcastUDPPortMapperClient(PartialPortMapperClient,
+ RawBroadcastUDPClient):
def __init__(self, bcastaddr):
- RawBroadcastUDPClient.__init__(self, bcastaddr, PMAP_PROG, PMAP_VERS, PMAP_PORT)
+ RawBroadcastUDPClient.__init__(self, bcastaddr, PMAP_PROG, PMAP_VERS,
+ PMAP_PORT)
PartialPortMapperClient.__init__(self)
class TCPClient(RawTCPClient):
"""A TCP Client that find their server through the Port mapper
"""
- def __init__(self, host, prog, vers):
- pmap = TCPPortMapperClient(host)
+ def __init__(self, host, prog, vers, open_timeout=5000):
+ pmap = TCPPortMapperClient(host, open_timeout)
port = pmap.get_port((prog, vers, IPPROTO_TCP, 0))
pmap.close()
if port == 0:
raise RPCError('program not registered')
- RawTCPClient.__init__(self, host, prog, vers, port)
+ RawTCPClient.__init__(self, host, prog, vers, port, open_timeout)
class UDPClient(RawUDPClient):
@@ -653,7 +773,8 @@ class BroadcastUDPClient(Client):
self.unpack_func = unpack_func
self.replies = []
packed_args = self.packer.get_buf()
- dummy_replies = self.pmap.Callit((self.prog, self.vers, proc, packed_args))
+ dummy_replies = self.pmap.Callit((self.prog, self.vers, proc,
+ packed_args))
return self.replies
@@ -757,7 +878,7 @@ class TCPServer(Server):
def __init__(self, host, prog, vers, port):
Server.__init__(self, host, prog, vers, port)
self.connect()
-
+
def connect(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.prot = IPPROTO_TCP
@@ -772,7 +893,7 @@ class TCPServer(Server):
sock, (host, port) = connection
while 1:
try:
- call = recvrecord(sock)
+ call = _recvrecord(sock, None)
except EOFError:
break
except socket.error:
@@ -780,7 +901,7 @@ class TCPServer(Server):
break
reply = self.handle(call)
if reply is not None:
- sendrecord(sock, reply)
+ _sendrecord(sock, reply)
def forkingloop(self):
# Like loop but uses forksession()
@@ -816,12 +937,12 @@ class UDPServer(Server):
def __init__(self, host, prog, vers, port):
Server.__init__(self, host, prog, vers, port)
self.connect()
-
+
def connect(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.prot = IPPROTO_UDP
self.sock.bind((self.host, self.port))
-
+
def loop(self):
while 1:
self.session()
diff --git a/pyvisa-py/protocols/usbraw.py b/pyvisa-py/protocols/usbraw.py
index 0e06c4a..9ed8e25 100644
--- a/pyvisa-py/protocols/usbraw.py
+++ b/pyvisa-py/protocols/usbraw.py
@@ -18,18 +18,18 @@ from __future__ import division, unicode_literals, print_function, absolute_impo
from .usbtmc import USBRaw as USBRaw
-import usb
+from .usbutil import find_devices, find_interfaces
-from .usbutil import find_devices, find_interfaces, find_endpoint, usb_find_desc
-
-def find_raw_devices(vendor=None, product=None, serial_number=None, custom_match=None, **kwargs):
+def find_raw_devices(vendor=None, product=None, serial_number=None,
+ custom_match=None, **kwargs):
"""Find connected USB RAW devices. See usbutil.find_devices for more info.
"""
def is_usbraw(dev):
if custom_match and not custom_match(dev):
return False
- return bool(find_interfaces(dev, bInterfaceClass=0xFF, bInterfaceSubClass=0xFF))
+ return bool(find_interfaces(dev, bInterfaceClass=0xFF,
+ bInterfaceSubClass=0xFF))
return find_devices(vendor, product, serial_number, is_usbraw, **kwargs)
@@ -66,7 +66,6 @@ class USBRawDevice(USBRaw):
return bytes_sent
-
def read(self, size):
"""Read raw bytes from the instrument.
@@ -77,13 +76,12 @@ class USBRawDevice(USBRaw):
"""
raw_read = super(USBRawDevice, self).read
- raw_write = super(USBRawDevice, self).write
- received = b''
+ received = bytearray()
while not len(received) >= size:
resp = raw_read(self.RECV_CHUNK)
- received += resp
+ received.extend(resp)
- return received
+ return bytes(received)
diff --git a/pyvisa-py/protocols/usbtmc.py b/pyvisa-py/protocols/usbtmc.py
index 20c53d2..472d274 100644
--- a/pyvisa-py/protocols/usbtmc.py
+++ b/pyvisa-py/protocols/usbtmc.py
@@ -5,7 +5,9 @@
Implements Session to control USBTMC instruments
- Loosely based on PyUSBTMC:python module to handle USB-TMC(Test and Measurement class) devices.
+ Loosely based on PyUSBTMC:python module to handle USB-TMC(Test and
+ Measurement class) devices.
+
by Noboru Yamamot, Accl. Lab, KEK, JAPAN
This file is an offspring of the Lantz Project.
@@ -14,7 +16,8 @@
:license: MIT, see LICENSE for more details.
"""
-from __future__ import division, unicode_literals, print_function, absolute_import
+from __future__ import (division, unicode_literals, print_function,
+ absolute_import)
import enum
from pyvisa.compat import struct
@@ -23,7 +26,8 @@ from collections import namedtuple
import usb
-from .usbutil import find_devices, find_interfaces, find_endpoint, usb_find_desc
+from .usbutil import (find_devices, find_interfaces, find_endpoint,
+ usb_find_desc)
import sys
@@ -58,35 +62,44 @@ class Request(enum.IntEnum):
indicator_pulse = 64
-def find_tmc_devices(vendor=None, product=None, serial_number=None, custom_match=None, **kwargs):
+def find_tmc_devices(vendor=None, product=None, serial_number=None,
+ custom_match=None, **kwargs):
"""Find connected USBTMC devices. See usbutil.find_devices for more info.
+
"""
def is_usbtmc(dev):
if custom_match and not custom_match(dev):
return False
- return bool(find_interfaces(dev, bInterfaceClass=0xfe, bInterfaceSubClass=3))
+ return bool(find_interfaces(dev, bInterfaceClass=0xfe,
+ bInterfaceSubClass=3))
return find_devices(vendor, product, serial_number, is_usbtmc, **kwargs)
class BulkOutMessage(object):
- """The Host uses the Bulk-OUT endpoint to send USBTMC command messages to the device.
+ """The Host uses the Bulk-OUT endpoint to send USBTMC command messages to
+ the device.
+
"""
@staticmethod
def build_array(btag, eom, chunk):
size = len(chunk)
- return struct.pack('BBBx', MsgID.dev_dep_msg_out, btag, ~btag & 0xFF) + \
- struct.pack("<LBxxx", size, eom) + \
- chunk + \
- b'\0' * ((4 - size) % 4)
+ return (struct.pack('BBBx', MsgID.dev_dep_msg_out, btag,
+ ~btag & 0xFF) +
+ struct.pack("<LBxxx", size, eom) +
+ chunk +
+ b'\0' * ((4 - size) % 4))
class BulkInMessage(namedtuple('BulkInMessage', 'msgid btag btaginverse '
- 'transfer_size transfer_attributes data')):
- """The Host uses the Bulk-IN endpoint to read USBTMC response messages from the device.
- The Host must first send a USBTMC command message that expects a response before
- attempting to read a USBTMC response message.
+ 'transfer_size transfer_attributes data')):
+ """The Host uses the Bulk-IN endpoint to read USBTMC response messages from
+ the device.
+
+ The Host must first send a USBTMC command message that expects a response
+ before attempting to read a USBTMC response message.
+
"""
@classmethod
@@ -94,10 +107,12 @@ class BulkInMessage(namedtuple('BulkInMessage', 'msgid btag btaginverse '
msgid, btag, btaginverse = struct.unpack_from('BBBx', data)
assert msgid == MsgID.dev_dep_msg_in
- transfer_size, transfer_attributes = struct.unpack_from('<LBxxx', data, 4)
+ transfer_size, transfer_attributes = struct.unpack_from('<LBxxx', data,
+ 4)
data = data[12:]
- return cls(msgid, btag, btaginverse, transfer_size, transfer_attributes, data)
+ return cls(msgid, btag, btaginverse, transfer_size,
+ transfer_attributes, data)
@staticmethod
def build_array(btag, transfer_size, term_char=None):
@@ -115,8 +130,10 @@ class BulkInMessage(namedtuple('BulkInMessage', 'msgid btag btaginverse '
else:
transfer_attributes = 2
- return struct.pack('BBBx', MsgID.request_dev_dep_msg_in, btag, ~btag & 0xFF) + \
- struct.pack("<LBBxx", transfer_size, transfer_attributes, term_char)
+ return (struct.pack('BBBx', MsgID.request_dev_dep_msg_in, btag,
+ ~btag & 0xFF) +
+ struct.pack("<LBBxx", transfer_size, transfer_attributes,
+ term_char))
class USBRaw(object):
@@ -126,10 +143,12 @@ class USBRaw(object):
#: Configuration number to be used. If None, the default will be used.
CONFIGURATION = None
+
#: Interface index it be used
INTERFACE = (0, 0)
- #: Receive and Send endpoints to be used. If None the first IN (or OUT) BULK
- #: endpoint will be used.
+
+ #: Receive and Send endpoints to be used. If None the first IN (or OUT)
+ #: BULK endpoint will be used.
ENDPOINTS = (None, None)
timeout = 2000
@@ -143,16 +162,17 @@ class USBRaw(object):
self.timeout = timeout
device_filters = device_filters or {}
- devices = list(self.find_devices(vendor, product, serial_number, None, **device_filters))
+ devices = list(self.find_devices(vendor, product, serial_number, None,
+ **device_filters))
if not devices:
raise ValueError('No device found.')
elif len(devices) > 1:
desc = '\n'.join(str(dev) for dev in devices)
- raise ValueError('{} devices found:\n{}\n'
- 'Please narrow the search criteria'.format(len(devices), desc))
+ raise ValueError('{} devices found:\n{}\nPlease narrow the search'
+ ' criteria'.format(len(devices), desc))
- self.usb_dev, other = devices[0], devices[1:]
+ self.usb_dev = devices[0]
try:
if self.usb_dev.is_kernel_driver_active(0):
@@ -172,7 +192,8 @@ class USBRaw(object):
self.usb_intf = self._find_interface(self.usb_dev, self.INTERFACE)
- self.usb_recv_ep, self.usb_send_ep = self._find_endpoints(self.usb_intf, self.ENDPOINTS)
+ self.usb_recv_ep, self.usb_send_ep =\
+ self._find_endpoints(self.usb_intf, self.ENDPOINTS)
def _find_interface(self, dev, setting):
return self.usb_dev.get_active_configuration()[self.INTERFACE]
@@ -180,12 +201,14 @@ class USBRaw(object):
def _find_endpoints(self, interface, setting):
recv, send = setting
if recv is None:
- recv = find_endpoint(interface, usb.ENDPOINT_IN, usb.ENDPOINT_TYPE_BULK)
+ recv = find_endpoint(interface, usb.ENDPOINT_IN,
+ usb.ENDPOINT_TYPE_BULK)
else:
recv = usb_find_desc(interface, bEndpointAddress=recv)
if send is None:
- send = find_endpoint(interface, usb.ENDPOINT_OUT, usb.ENDPOINT_TYPE_BULK)
+ send = find_endpoint(interface, usb.ENDPOINT_OUT,
+ usb.ENDPOINT_TYPE_BULK)
else:
send = usb_find_desc(interface, bEndpointAddress=send)
@@ -228,9 +251,11 @@ class USBTMC(USBRaw):
find_devices = staticmethod(find_tmc_devices)
- def __init__(self, vendor=None, product=None, serial_number=None, **kwargs):
+ def __init__(self, vendor=None, product=None, serial_number=None,
+ **kwargs):
super(USBTMC, self).__init__(vendor, product, serial_number, **kwargs)
- self.usb_intr_in = find_endpoint(self.usb_intf, usb.ENDPOINT_IN, usb.ENDPOINT_TYPE_INTERRUPT)
+ self.usb_intr_in = find_endpoint(self.usb_intf, usb.ENDPOINT_IN,
+ usb.ENDPOINT_TYPE_INTERRUPT)
self.usb_dev.reset()
self.usb_dev.set_configuration()
@@ -242,21 +267,23 @@ class USBTMC(USBRaw):
self._btag = 0
if not (self.usb_recv_ep and self.usb_send_ep):
- raise ValueError("TMC device must have both Bulk-In and Bulk-out endpoints.")
+ msg = "TMC device must have both Bulk-In and Bulk-out endpoints."
+ raise ValueError(msg)
def _get_capabilities(self):
- cap = self.usb_dev.ctrl_transfer(
- usb.util.build_request_type(usb.util.CTRL_IN,
- usb.util.CTRL_TYPE_CLASS,
- usb.util.CTRL_RECIPIENT_INTERFACE),
- Request.get_capabilities,
- 0x0000,
- self.usb_intf.index,
- 0x0018,
- timeout=self.timeout)
+ self.usb_dev.ctrl_transfer(
+ usb.util.build_request_type(usb.util.CTRL_IN,
+ usb.util.CTRL_TYPE_CLASS,
+ usb.util.CTRL_RECIPIENT_INTERFACE),
+ Request.get_capabilities,
+ 0x0000,
+ self.usb_intf.index,
+ 0x0018,
+ timeout=self.timeout)
def _find_interface(self, dev, setting):
- interfaces = find_interfaces(dev, bInterfaceClass=0xFE, bInterfaceSubClass=3)
+ interfaces = find_interfaces(dev, bInterfaceClass=0xFE,
+ bInterfaceSubClass=3)
if not interfaces:
raise ValueError('USB TMC interface not found.')
elif len(interfaces) > 1:
@@ -281,13 +308,13 @@ class USBTMC(USBRaw):
self._btag = (self._btag % 255) + 1
- data = BulkOutMessage.build_array(self._btag, end > size, data[begin:end])
+ data = BulkOutMessage.build_array(self._btag, end > size,
+ data[begin:end])
bytes_sent += raw_write(data)
return bytes_sent
-
def read(self, size):
recv_chunk = self.RECV_CHUNK
@@ -297,7 +324,7 @@ class USBTMC(USBRaw):
raw_read = super(USBTMC, self).read
raw_write = super(USBTMC, self).write
- received = b''
+ received = bytearray()
while not eom:
self._btag = (self._btag % 255) + 1
@@ -310,8 +337,8 @@ class USBTMC(USBRaw):
response = BulkInMessage.from_bytes(resp)
- received += response.data
+ received.extend(response.data)
eom = response.transfer_attributes & 1
- return received
+ return bytes(received)
diff --git a/pyvisa-py/protocols/usbutil.py b/pyvisa-py/protocols/usbutil.py
index 44791df..e02ce07 100644
--- a/pyvisa-py/protocols/usbutil.py
+++ b/pyvisa-py/protocols/usbutil.py
@@ -120,10 +120,6 @@ AllCodes = {
}
-class LantzUSBTimeoutError(usb.core.USBError):
- pass
-
-
def ep_attributes(ep):
c = ep.bmAttributes
attrs = []
@@ -136,7 +132,7 @@ def ep_attributes(ep):
attrs.append('Bulk')
elif tp == usb.ENDPOINT_TYPE_INTERRUPT:
attrs.append('Interrupt')
-
+
sync = (c & 12) >> 2
if sync == 0:
attrs.append('No sync')
@@ -159,7 +155,8 @@ def ep_attributes(ep):
return ', '.join(attrs)
-def find_devices(vendor=None, product=None, serial_number=None, custom_match=None, **kwargs):
+def find_devices(vendor=None, product=None, serial_number=None,
+ custom_match=None, **kwargs):
"""Find connected USB devices matching certain keywords.
Wildcards can be used for vendor, product and serial_number.
diff --git a/pyvisa-py/protocols/vxi11.py b/pyvisa-py/protocols/vxi11.py
index 0026b06..69d2a7f 100644
--- a/pyvisa-py/protocols/vxi11.py
+++ b/pyvisa-py/protocols/vxi11.py
@@ -13,9 +13,11 @@
:license: MIT, see LICENSE for more details.
"""
-from __future__ import division, unicode_literals, print_function, absolute_import
+from __future__ import (division, unicode_literals, print_function,
+ absolute_import)
import enum
+import socket
from . import rpc
@@ -69,6 +71,7 @@ class ErrorCodes(enum.IntEnum):
abort = 23
channel_already_established = 29
+
# Flags
OP_FLAG_WAIT_BLOCK = 1
OP_FLAG_END = 8
@@ -87,14 +90,14 @@ class Vxi11Packer(rpc.Packer):
def pack_device_link(self, link):
self.pack_int(link)
-
+
def pack_create_link_parms(self, params):
id, lock_device, lock_timeout, device = params
self.pack_int(id)
self.pack_bool(lock_device)
self.pack_uint(lock_timeout)
self.pack_string(device.encode('ascii'))
-
+
def pack_device_write_parms(self, params):
link, io_timeout, lock_timeout, flags, data = params
self.pack_int(link)
@@ -102,7 +105,7 @@ class Vxi11Packer(rpc.Packer):
self.pack_uint(lock_timeout)
self.pack_int(flags)
self.pack_opaque(data)
-
+
def pack_device_read_parms(self, params):
link, request_size, io_timeout, lock_timeout, flags, term_char = params
self.pack_int(link)
@@ -111,14 +114,14 @@ class Vxi11Packer(rpc.Packer):
self.pack_uint(lock_timeout)
self.pack_int(flags)
self.pack_int(term_char)
-
+
def pack_device_generic_parms(self, params):
link, flags, lock_timeout, io_timeout = params
self.pack_int(link)
self.pack_int(flags)
self.pack_uint(lock_timeout)
self.pack_uint(io_timeout)
-
+
def pack_device_remote_func_parms(self, params):
host_addr, host_port, prog_num, prog_vers, prog_family = params
self.pack_uint(host_addr)
@@ -126,7 +129,7 @@ class Vxi11Packer(rpc.Packer):
self.pack_uint(prog_num)
self.pack_uint(prog_vers)
self.pack_int(prog_family)
-
+
def pack_device_enable_srq_parms(self, params):
link, enable, handle = params
self.pack_int(link)
@@ -134,15 +137,16 @@ class Vxi11Packer(rpc.Packer):
if len(handle) > 40:
raise Vxi11Error("array length too long")
self.pack_opaque(handle)
-
+
def pack_device_lock_parms(self, params):
link, flags, lock_timeout = params
self.pack_int(link)
self.pack_int(flags)
self.pack_uint(lock_timeout)
-
+
def pack_device_docmd_parms(self, params):
- link, flags, io_timeout, lock_timeout, cmd, network_order, datasize, data_in = params
+ (link, flags, io_timeout, lock_timeout,
+ cmd, network_order, datasize, data_in) = params
self.pack_int(link)
self.pack_int(flags)
self.pack_uint(io_timeout)
@@ -156,33 +160,33 @@ class Vxi11Packer(rpc.Packer):
class Vxi11Unpacker(rpc.Unpacker):
def unpack_device_link(self):
return self.unpack_int()
-
+
def unpack_device_error(self):
return self.unpack_int()
-
+
def unpack_create_link_resp(self):
error = self.unpack_int()
link = self.unpack_int()
abort_port = self.unpack_uint()
max_recv_size = self.unpack_uint()
return error, link, abort_port, max_recv_size
-
+
def unpack_device_write_resp(self):
error = self.unpack_int()
size = self.unpack_uint()
return error, size
-
+
def unpack_device_read_resp(self):
error = self.unpack_int()
reason = self.unpack_int()
data = self.unpack_opaque()
return error, reason, data
-
+
def unpack_device_read_stb_resp(self):
error = self.unpack_int()
stb = self.unpack_uint()
return error, stb
-
+
def unpack_device_docmd_resp(self):
error = self.unpack_int()
data_out = self.unpack_opaque()
@@ -191,10 +195,11 @@ class Vxi11Unpacker(rpc.Unpacker):
class CoreClient(rpc.TCPClient):
- def __init__(self, host):
+ def __init__(self, host, open_timeout=5000):
self.packer = Vxi11Packer()
self.unpacker = Vxi11Unpacker('')
- super(CoreClient, self).__init__(host, DEVICE_CORE_PROG, DEVICE_CORE_VERS)
+ super(CoreClient, self).__init__(host, DEVICE_CORE_PROG,
+ DEVICE_CORE_VERS, open_timeout)
def create_link(self, id, lock_device, lock_timeout, name):
params = (id, lock_device, lock_timeout, name)
@@ -204,15 +209,23 @@ class CoreClient(rpc.TCPClient):
def device_write(self, link, io_timeout, lock_timeout, flags, data):
params = (link, io_timeout, lock_timeout, flags, data)
- return self.make_call(DEVICE_WRITE, params,
- self.packer.pack_device_write_parms,
- self.unpacker.unpack_device_write_resp)
-
- def device_read(self, link, request_size, io_timeout, lock_timeout, flags, term_char):
- params = (link, request_size, io_timeout, lock_timeout, flags, term_char)
- return self.make_call(DEVICE_READ, params,
- self.packer.pack_device_read_parms,
- self.unpacker.unpack_device_read_resp)
+ try:
+ return self.make_call(DEVICE_WRITE, params,
+ self.packer.pack_device_write_parms,
+ self.unpacker.unpack_device_write_resp)
+ except socket.timeout as e:
+ return ErrorCodes.io_timeout, e.args[0], ''
+
+ def device_read(self, link, request_size, io_timeout, lock_timeout, flags,
+ term_char):
+ params = (link, request_size, io_timeout, lock_timeout, flags,
+ term_char)
+ try:
+ return self.make_call(DEVICE_READ, params,
+ self.packer.pack_device_read_parms,
+ self.unpacker.unpack_device_read_resp)
+ except socket.timeout as e:
+ return ErrorCodes.io_timeout, e.args[0], ''
def device_read_stb(self, link, flags, lock_timeout, io_timeout):
params = (link, flags, lock_timeout, io_timeout)
@@ -261,8 +274,10 @@ class CoreClient(rpc.TCPClient):
self.packer.pack_device_enable_srq_parms,
self.unpacker.unpack_device_error)
- def device_docmd(self, link, flags, io_timeout, lock_timeout, cmd, network_order, datasize, data_in):
- params = (link, flags, io_timeout, lock_timeout, cmd, network_order, datasize, data_in)
+ def device_docmd(self, link, flags, io_timeout, lock_timeout, cmd,
+ network_order, datasize, data_in):
+ params = (link, flags, io_timeout, lock_timeout, cmd, network_order,
+ datasize, data_in)
return self.make_call(DEVICE_DOCMD, params,
self.packer.pack_device_docmd_parms,
self.unpacker.unpack_device_docmd_resp)
@@ -271,20 +286,15 @@ class CoreClient(rpc.TCPClient):
return self.make_call(DESTROY_LINK, link,
self.packer.pack_device_link,
self.unpacker.unpack_device_error)
-
- def create_intr_chan(self, host_addr, host_port, prog_num, prog_vers, prog_family):
+
+ def create_intr_chan(self, host_addr, host_port, prog_num, prog_vers,
+ prog_family):
params = (host_addr, host_port, prog_num, prog_vers, prog_family)
return self.make_call(CREATE_INTR_CHAN, params,
self.packer.pack_device_docmd_parms,
self.unpacker.unpack_device_error)
-
+
def destroy_intr_chan(self):
return self.make_call(DESTROY_INTR_CHAN, None,
None,
self.unpacker.unpack_device_error)
-
-
-
-
-
-
diff --git a/pyvisa-py/serial.py b/pyvisa-py/serial.py
index 36ff2ab..12128b2 100644
--- a/pyvisa-py/serial.py
+++ b/pyvisa-py/serial.py
@@ -19,12 +19,10 @@ from . import common
try:
import serial
- from serial import Serial
from serial.tools.list_ports import comports
except ImportError as e:
Session.register_unavailable(constants.InterfaceType.asrl, 'INSTR',
- 'Please install PySerial to use this resource type.\n%s' % e)
-
+ 'Please install PySerial (>=3.0) to use this resource type.\n%s' % e)
raise
@@ -37,7 +35,6 @@ def to_state(boolean_input):
StatusCode = constants.StatusCode
-SUCCESS = StatusCode.success
SerialTermination = constants.SerialTermination
@@ -63,37 +60,26 @@ class SerialSession(Session):
if 'mock' in self.parsed:
cls = self.parsed.mock
else:
- cls = Serial
+ cls = serial.Serial
- self.interface = cls(port=self.parsed.board, timeout=2000, writeTimeout=2000)
+ self.interface = cls(port=self.parsed.board, timeout=self.timeout, write_timeout=self.timeout)
- for name in 'ASRL_END_IN,ASRL_END_OUT,SEND_END_EN,TERMCHAR,' \
- 'TERMCHAR_EN,SUPPRESS_END_EN'.split(','):
+ for name in ('ASRL_END_IN', 'ASRL_END_OUT', 'SEND_END_EN', 'TERMCHAR',
+ 'TERMCHAR_EN', 'SUPPRESS_END_EN'):
attribute = getattr(constants, 'VI_ATTR_' + name)
self.attrs[attribute] = attributes.AttributesByID[attribute].default
- @property
- def timeout(self):
- value = self.interface.timeout
-
- if value is None:
- return constants.VI_TMO_INFINITE
- elif value == 0:
- return constants.VI_TMO_IMMEDIATE
- else:
- return int(value * 1000)
-
- @timeout.setter
- def timeout(self, value):
- if value == constants.VI_TMO_INFINITE:
- value = None
- elif value == constants.VI_TMO_IMMEDIATE:
- value = 0
- else:
- value = value / 1000.
+ def _get_timeout(self, attribute):
+ if self.interface:
+ self.timeout = self.interface.timeout
+ return super(SerialSession, self)._get_timeout(attribute)
- self.interface.timeout = value
- self.interface.writeTimeout = value
+ def _set_timeout(self, attribute, value):
+ status = super(SerialSession, self)._set_timeout(attribute, value)
+ if self.interface:
+ self.interface.timeout = self.timeout
+ self.interface.write_timeout = self.timeout
+ return status
def close(self):
self.interface.close()
@@ -172,7 +158,7 @@ class SerialSession(Session):
logger.debug('Serial.sendBreak')
self.interface.sendBreak()
- return count, constants.StatusCode.success
+ return count, StatusCode.success
except serial.SerialTimeoutException:
return 0, StatusCode.error_timeout
@@ -191,10 +177,10 @@ class SerialSession(Session):
raise NotImplementedError
elif attribute == constants.VI_ATTR_ASRL_AVAIL_NUM:
- return self.interface.inWaiting(), SUCCESS
+ return self.interface.inWaiting(), StatusCode.success
elif attribute == constants.VI_ATTR_ASRL_BAUD:
- return self.interface.baudrate, SUCCESS
+ return self.interface.baudrate, StatusCode.success
elif attribute == constants.VI_ATTR_ASRL_BREAK_LEN:
raise NotImplementedError
@@ -206,10 +192,10 @@ class SerialSession(Session):
raise NotImplementedError
elif attribute == constants.VI_ATTR_ASRL_CTS_STATE:
- return to_state(self.interface.getCTS()), SUCCESS
+ return to_state(self.interface.getCTS()), StatusCode.success
elif attribute == constants.VI_ATTR_ASRL_DATA_BITS:
- return self.interface.bytesize, SUCCESS
+ return self.interface.bytesize, StatusCode.success
elif attribute == constants.VI_ATTR_ASRL_DCD_STATE:
raise NotImplementedError
@@ -218,7 +204,7 @@ class SerialSession(Session):
raise NotImplementedError
elif attribute == constants.VI_ATTR_ASRL_DSR_STATE:
- return to_state(self.interface.getDSR()), SUCCESS
+ return to_state(self.interface.getDSR()), StatusCode.success
elif attribute == constants.VI_ATTR_ASRL_DTR_STATE:
raise NotImplementedError
@@ -226,20 +212,20 @@ class SerialSession(Session):
elif attribute == constants.VI_ATTR_ASRL_FLOW_CNTRL:
return (self.interface.xonxoff * constants.VI_ASRL_FLOW_XON_XOFF |
self.interface.rtscts * constants.VI_ASRL_FLOW_RTS_CTS |
- self.interface.dsrdtr * constants.VI_ASRL_FLOW_DTR_DSR), SUCCESS
+ self.interface.dsrdtr * constants.VI_ASRL_FLOW_DTR_DSR), StatusCode.success
elif attribute == constants.VI_ATTR_ASRL_PARITY:
parity = self.interface.parity
if parity == serial.PARITY_NONE:
- return constants.Parity.none, SUCCESS
+ return constants.Parity.none, StatusCode.success
elif parity == serial.PARITY_EVEN:
- return constants.Parity.even, SUCCESS
+ return constants.Parity.even, StatusCode.success
elif parity == serial.PARITY_ODD:
- return constants.Parity.odd, SUCCESS
+ return constants.Parity.odd, StatusCode.success
elif parity == serial.PARITY_MARK:
- return constants.Parity.mark, SUCCESS
+ return constants.Parity.mark, StatusCode.success
elif parity == serial.PARITY_SPACE:
- return constants.Parity.space, SUCCESS
+ return constants.Parity.space, StatusCode.success
raise Exception('Unknown parity value: %r' % parity)
@@ -252,11 +238,11 @@ class SerialSession(Session):
elif attribute == constants.VI_ATTR_ASRL_STOP_BITS:
bits = self.interface.stopbits
if bits == serial.STOPBITS_ONE:
- return constants.StopBits.one, SUCCESS
+ return constants.StopBits.one, StatusCode.success
elif bits == serial.STOPBITS_ONE_POINT_FIVE:
- return constants.StopBits.one_and_a_half, SUCCESS
+ return constants.StopBits.one_and_a_half, StatusCode.success
elif bits == serial.STOPBITS_TWO:
- return constants.StopBits.two, SUCCESS
+ return constants.StopBits.two, StatusCode.success
raise Exception('Unknown bits value: %r' % bits)
@@ -264,7 +250,7 @@ class SerialSession(Session):
raise NotImplementedError
elif attribute == constants.VI_ATTR_INTF_TYPE:
- return constants.InterfaceType.asrl, SUCCESS
+ return constants.InterfaceType.asrl, StatusCode.success
raise UnknownAttribute(attribute)
diff --git a/pyvisa-py/sessions.py b/pyvisa-py/sessions.py
index c389fa4..5674177 100644
--- a/pyvisa-py/sessions.py
+++ b/pyvisa-py/sessions.py
@@ -10,7 +10,8 @@
:license: MIT, see LICENSE for more details.
"""
-from __future__ import division, unicode_literals, print_function, absolute_import
+from __future__ import (division, unicode_literals, print_function,
+ absolute_import)
import abc
import time
@@ -20,6 +21,9 @@ from pyvisa import logger, constants, attributes, compat, rname
from . import common
+StatusCode = constants.StatusCode
+
+
class UnknownAttribute(Exception):
def __init__(self, attribute):
@@ -45,7 +49,8 @@ class Session(compat.with_metaclass(abc.ABCMeta)):
Just makes sure that common methods are defined and information is stored.
- :param resource_manager_session: The session handle of the parent Resource Manager
+ :param resource_manager_session: The session handle of the parent Resource
+ Manager
:param resource_name: The resource name.
:param parsed: the parsed resource name (optional).
If not provided, the resource_name will be parsed.
@@ -58,7 +63,8 @@ class Session(compat.with_metaclass(abc.ABCMeta)):
Use to implement custom logic for attributes.
:param attribute: Resource attribute for which the state query is made
- :return: The state of the queried attribute for a specified resource, return value of the library call.
+ :return: The state of the queried attribute for a specified resource,
+ return value of the library call.
:rtype: (unicode | str | list | int, VISAStatus)
"""
@@ -79,7 +85,8 @@ class Session(compat.with_metaclass(abc.ABCMeta)):
"""Close the session. Use it to do final clean ups.
"""
- #: Maps (Interface Type, Resource Class) to Python class encapsulating that resource.
+ #: Maps (Interface Type, Resource Class) to Python class encapsulating that
+ #: resource.
#: dict[(Interface Type, Resource Class) , Session]
_session_classes = dict()
@@ -122,7 +129,8 @@ class Session(compat.with_metaclass(abc.ABCMeta)):
try:
return cls._session_classes[(interface_type, resource_class)]
except KeyError:
- raise ValueError('No class registered for %s, %s' % (interface_type, resource_class))
+ raise ValueError('No class registered for %s, %s' %
+ (interface_type, resource_class))
@classmethod
def register(cls, interface_type, resource_class):
@@ -133,35 +141,45 @@ class Session(compat.with_metaclass(abc.ABCMeta)):
"""
def _internal(python_class):
if (interface_type, resource_class) in cls._session_classes:
- logger.warning('%s is already registered in the ResourceManager. '
- 'Overwriting with %s' % ((interface_type, resource_class), python_class))
+ logger.warning('%s is already registered in the '
+ 'ResourceManager. Overwriting with %s',
+ ((interface_type, resource_class), python_class)
+ )
python_class.session_type = (interface_type, resource_class)
- cls._session_classes[(interface_type, resource_class)] = python_class
+ cls._session_classes[(interface_type,
+ resource_class)] = python_class
return python_class
return _internal
@classmethod
def register_unavailable(cls, interface_type, resource_class, msg):
- """Register an unavailable session class for a given interface type and resource class.
+ """Register an unavailable session class for a given interface type and
+ resource class.
+
raising a ValueError if called.
:type interface_type: constants.InterfaceType
:type resource_class: str
"""
# noinspection PyUnusedLocal
- def _internal(*args, **kwargs):
- raise ValueError(msg)
+ class _internal(object):
+ session_issue = msg
+
+ def __init__(self, *args, **kwargs):
+ raise ValueError(msg)
_internal.session_issue = msg
if (interface_type, resource_class) in cls._session_classes:
logger.warning('%s is already registered in the ResourceManager. '
- 'Overwriting with unavailable %s' % ((interface_type, resource_class), msg))
+ 'Overwriting with unavailable %s',
+ ((interface_type, resource_class), msg))
cls._session_classes[(interface_type, resource_class)] = _internal
- def __init__(self, resource_manager_session, resource_name, parsed=None):
+ def __init__(self, resource_manager_session, resource_name, parsed=None,
+ open_timeout=None):
if isinstance(resource_name, common.MockInterface):
parsed = rname.parse_resource_name(resource_name.resource_name)
parsed['mock'] = resource_name
@@ -170,31 +188,159 @@ class Session(compat.with_metaclass(abc.ABCMeta)):
parsed = rname.parse_resource_name(resource_name)
self.parsed = parsed
+ self.open_timeout = open_timeout
- #: Used as a place holder for the object doing the lowlevel communication.
+ #: Get default timeout from constants
+ self.timeout =\
+ (attributes.AttributesByID[constants.VI_ATTR_TMO_VALUE].default /
+ 1000.0)
+
+ #: Used as a place holder for the object doing the lowlevel
+ #: communication.
self.interface = None
#: Used for attributes not handled by the underlying interface.
- #: Values are get or set automatically by get_attribute and set_attribute
+ #: Values are get or set automatically by get_attribute and
+ #: set_attribute
#: Add your own by overriding after_parsing.
self.attrs = {constants.VI_ATTR_RM_SESSION: resource_manager_session,
constants.VI_ATTR_RSRC_NAME: str(parsed),
constants.VI_ATTR_RSRC_CLASS: parsed.resource_class,
- constants.VI_ATTR_INTF_TYPE: parsed.interface_type}
+ constants.VI_ATTR_INTF_TYPE: parsed.interface_type,
+ constants.VI_ATTR_TMO_VALUE: (self._get_timeout,
+ self._set_timeout)}
self.after_parsing()
def after_parsing(self):
"""Override this method to provide custom initialization code, to be
called after the resourcename is properly parsed
+
+ ResourceSession can register resource specific attributes handling of
+ them into self.attrs.
+ It is also possible to change handling of already registerd common
+ attributes. List of attributes is available in pyvisa package:
+ * name is in constants module as: VI_ATTR_<NAME>
+ * validity of attribute for resource is defined module attributes,
+ AttrVI_ATTR_<NAME>.resources
+
+ For static (read only) values, simple readonly and also readwrite
+ attributes simplified construction can be used:
+ ` self.attrs[constants.VI_ATTR_<NAME>] = 100`
+ or
+ ` self.attrs[constants.VI_ATTR_<NAME>] = <self.variable_name>`
+
+ For more complex handling of attributes, it is possible to register
+ getter and/or setter. When Null is used, NotSupported error is
+ returned.
+ Getter has same signature as see Session._get_attribute and setter has
+ same signature as see Session._set_attribute. (It is possible to
+ register also see Session._get_attribute and see Session._set_attribute
+ as getter/setter). Getter and Setter are registered as tupple.
+ For readwrite attribute:
+ ` self.attrs[constants.VI_ATTR_<NAME>] = (<getter_name>,
+ <setter_name>)`
+ For readonly attribute:
+ ` self.attrs[constants.VI_ATTR_<NAME>] = (<getter_name>, None)`
+ For reusing of see Session._get_attribute and see
+ Session._set_attribute
+ ` self.attrs[constants.VI_ATTR_<NAME>] = (self._get_attribute,
+ self._set_attribute)`
+ """
+ pass
+
+ def gpib_command(self, command_byte):
+ """Write GPIB command byte on the bus.
+
+ Corresponds to viGpibCommand function of the VISA library.
+ See: https://linux-gpib.sourceforge.io/doc_html/gpib-protocol.html#REFERENCE-COMMAND-BYTES
+
+ :param command_byte: command byte to send
+ :type command_byte: int, must be [0 255]
+ :return: return value of the library call
+ :rtype: :class:`pyvisa.constants.StatusCode`
+ """
+ try:
+ return self.sessions[session].gpib_command(command_byte)
+
+ except KeyError:
+ return constants.StatusCode.error_invalid_object
+
+ def assert_trigger(self, protocol):
+ """Asserts software or hardware trigger.
+
+ Corresponds to viAssertTrigger function of the VISA library.
+
+ :param protocol: Trigger protocol to use during assertion. (Constants.PROT*)
+ :return: return value of the library call.
+ :rtype: :class:`pyvisa.constants.StatusCode`
+ """
+ raise NotImplementedError
+
+ def gpib_send_ifc(self):
+ """Pulse the interface clear line (IFC) for at least 100 microseconds.
+
+ Corresponds to viGpibSendIFC function of the VISA library.
+
+ :return: return value of the library call.
+ :rtype: :class:`pyvisa.constants.StatusCode`
+ """
+ return StatusCode.error_nonsupported_operation
+
+ def clear(self):
+ """Clears a device.
+
+ Corresponds to viClear function of the VISA library.
+
+ :return: return value of the library call.
+ :rtype: :class:`pyvisa.constants.StatusCode`
"""
+ return StatusCode.error_nonsupported_operation
+
+ def read_stb(self):
+ """Reads a status byte of the service request.
+
+ Corresponds to viReadSTB function of the VISA library.
+
+ :return: Service request status byte, return value of the library call.
+ :rtype: int, :class:`pyvisa.constants.StatusCode`
+ """
+ return 0, StatusCode.error_nonsupported_operation
+
+ def lock(self, session, lock_type, timeout, requested_key=None):
+ """Establishes an access mode to the specified resources.
+
+ Corresponds to viLock function of the VISA library.
+
+ :param session: Unique logical identifier to a session.
+ :param lock_type: Specifies the type of lock requested, either Constants.EXCLUSIVE_LOCK or Constants.SHARED_LOCK.
+ :param timeout: Absolute time period (in milliseconds) that a resource waits to get unlocked by the
+ locking session before returning an error.
+ :param requested_key: This parameter is not used and should be set to VI_NULL when lockType is VI_EXCLUSIVE_LOCK.
+ :return: access_key that can then be passed to other sessions to share the lock, return value of the library call.
+ :rtype: str, :class:`pyvisa.constants.StatusCode`
+ """
+ return '', StatusCode.error_nonsupported_operation
+
+ def unlock(self, session):
+ """Relinquishes a lock for the specified resource.
+
+ Corresponds to viUnlock function of the VISA library.
+
+ :param session: Unique logical identifier to a session.
+ :return: return value of the library call.
+ :rtype: :class:`pyvisa.constants.StatusCode`
+ """
+ return StatusCode.error_nonsupported_operation
def get_attribute(self, attribute):
"""Get the value for a given VISA attribute for this session.
- Does a few checks before and calls before dispatching to `_get_attribute`.
+ Does a few checks before and calls before dispatching to
+ `_get_attribute`.
:param attribute: Resource attribute for which the state query is made
- :return: The state of the queried attribute for a specified resource, return value of the library call.
+ :return: The state of the queried attribute for a specified resource,
+ return value of the library call.
:rtype: (unicode | str | list | int, VISAStatus)
"""
@@ -202,36 +348,41 @@ class Session(compat.with_metaclass(abc.ABCMeta)):
try:
attr = attributes.AttributesByID[attribute]
except KeyError:
- return 0, constants.StatusCode.error_nonsupported_attribute
+ return 0, StatusCode.error_nonsupported_attribute
# Check if the attribute is defined for this session type.
if not attr.in_resource(self.session_type):
- return 0, constants.StatusCode.error_nonsupported_attribute
+ return 0, StatusCode.error_nonsupported_attribute
# Check if reading the attribute is allowed.
if not attr.read:
raise Exception('Do not now how to handle write only attributes.')
- # First try to answer those attributes that are common to all session types
- # or user defined because they are not defined by the interface.
+ # First try to answer those attributes that are registered in
+ # self.attrs, see Session.after_parsing
if attribute in self.attrs:
- return self.attrs[attribute], constants.StatusCode.success
+ value = self.attrs[attribute]
+ status = StatusCode.success
+ if isinstance(value, tuple):
+ getter = value[0]
+ value, status = (getter(attribute) if getter else
+ (0, StatusCode.error_nonsupported_attribute))
+ return value, status
- elif attribute == constants.VI_ATTR_TMO_VALUE:
- return self.timeout, constants.StatusCode.success
-
- # Dispatch to `_get_attribute`, which must be implemented by subclasses.
+ # Dispatch to `_get_attribute`, which must be implemented by subclasses
try:
return self._get_attribute(attribute)
except UnknownAttribute as e:
logger.exception(str(e))
- return 0, constants.StatusCode.error_nonsupported_attribute
+ return 0, StatusCode.error_nonsupported_attribute
def set_attribute(self, attribute, attribute_state):
- """Set the attribute_state value for a given VISA attribute for this session.
+ """Set the attribute_state value for a given VISA attribute for this
+ session.
- Does a few checks before and calls before dispatching to `_gst_attribute`.
+ Does a few checks before and calls before dispatching to
+ `_gst_attribute`.
:param attribute: Resource attribute for which the state query is made.
:param attribute_state: value.
@@ -243,43 +394,42 @@ class Session(compat.with_metaclass(abc.ABCMeta)):
try:
attr = attributes.AttributesByID[attribute]
except KeyError:
- return constants.StatusCode.error_nonsupported_attribute
+ return StatusCode.error_nonsupported_attribute
# Check if the attribute is defined for this session type.
if not attr.in_resource(self.session_type):
- return constants.StatusCode.error_nonsupported_attribute
+ return StatusCode.error_nonsupported_attribute
# Check if writing the attribute is allowed.
if not attr.write:
- return constants.StatusCode.error_attribute_read_only
+ return StatusCode.error_attribute_read_only
- # First try to answer those attributes that are common to all session types
- # or user defined because they are not defined by the interface.
+ # First try to answer those attributes that are registered in
+ # self.attrs, see Session.after_parsing
if attribute in self.attrs:
- self.attrs[attribute] = attribute_state
- return constants.StatusCode.success
-
- elif attribute == constants.VI_ATTR_TMO_VALUE:
- try:
- self.timeout = attribute_state
- except:
- return constants.StatusCode.error_nonsupported_attribute_state
-
- return constants.StatusCode.success
-
- # Dispatch to `_set_attribute`, which must be implemented by subclasses.
+ value = self.attrs[attribute]
+ status = StatusCode.success
+ if isinstance(value, tuple):
+ setter = value[1]
+ status = (setter(attribute, attribute_state) if setter else
+ StatusCode.error_nonsupported_attribute)
+ else:
+ self.attrs[attribute] = attribute_state
+ return status
+
+ # Dispatch to `_set_attribute`, which must be implemented by subclasses
try:
return self._set_attribute(attribute, attribute_state)
except ValueError:
- return constants.StatusCode.error_nonsupported_attribute_state
+ return StatusCode.error_nonsupported_attribute_state
except NotImplementedError:
e = UnknownAttribute(attribute)
logger.exception(str(e))
- return constants.StatusCode.error_nonsupported_attribute
+ return StatusCode.error_nonsupported_attribute
except UnknownAttribute as e:
logger.exception(str(e))
- return constants.StatusCode.error_nonsupported_attribute
+ return StatusCode.error_nonsupported_attribute
def _read(self, reader, count, end_indicator_checker, suppress_end_en,
termination_char, termination_char_en, timeout_exception):
@@ -287,47 +437,101 @@ class Session(compat.with_metaclass(abc.ABCMeta)):
Corresponds to viRead function of the VISA library.
- :param reader: Function to read a single byte.
- :type reader: () -> byte
+ :param reader: Function to read one or more bytes.
+ :type reader: () -> bytes
:param count: Number of bytes to be read.
:type count: int
- :param end_indicator_checker: Function to check if the byte.
- :type end_indicator_checker: (byte) -> boolean
+ :param end_indicator_checker: Function to check if the message is
+ complete.
+ :type end_indicator_checker: (bytes) -> boolean
:param suppress_end_en: suppress end.
:type suppress_end_en: bool
- :param termination_char: Number of bytes to be read.
+ :param termination_char: Stop reading if this character is received.
+ :type suppress_end_en: int or str
:param termination_char_en: termination char enabled.
:type termination_char_en: boolean
- :param: timeout_exception: Exception to capture time out for the given interface.
+ :param: timeout_exception: Exception to capture time out for the given
+ interface.
:type: Exception
:return: data read, return value of the library call.
:rtype: bytes, constants.StatusCode
"""
- timeout = self.get_attribute(constants.VI_ATTR_TMO_VALUE)[0] / 1000.
+ # NOTE: Some interfaces return not only a single byte but a complete
+ # block for each read therefore we must handle the case that the
+ # termination character is in the middle of the block or that the
+ # maximum number of bytes is exceeded
- start = time.time()
- out = b''
+ # Make sure termination_char is a string
+ try:
+ termination_char = chr(termination_char)
+ except TypeError:
+ pass
+
+ finish_time = (None if self.timeout is None else
+ (time.time() + self.timeout))
+ out = bytearray()
while True:
try:
current = reader()
except timeout_exception:
- return out, constants.StatusCode.error_timeout
+ return out, StatusCode.error_timeout
if current:
- out += current
+ out.extend(current)
end_indicator_received = end_indicator_checker(current)
- if end_indicator_received and not suppress_end_en:
- # RULE 6.1.1
- return out, constants.StatusCode.success
+ if end_indicator_received:
+ if not suppress_end_en:
+ # RULE 6.1.1
+ return bytes(out), StatusCode.success
+ else:
+ if termination_char_en and termination_char in current:
+ # RULE 6.1.2
+ # Return everything upto and including the termination
+ # character
+ return (bytes(out[:out.index(termination_char)+1]),
+ StatusCode.success_termination_character_read)
+ elif len(out) >= count:
+ # RULE 6.1.3
+ # Return at most the number of bytes requested
+ return (bytes(out[:count]),
+ StatusCode.success_max_count_read)
+
+ if finish_time and time.time() > finish_time:
+ return bytes(out), StatusCode.error_timeout
+
+ def _get_timeout(self, attribute):
+ """ Returns timeout calculated value from python way to VI_ way
+
+ In VISA, the timeout is expressed in milliseconds or using the
+ constants VI_TMO_INFINITE or VI_TMO_IMMEDIATE.
+
+ In Python we store it as either None (VI_TMO_INFINITE), 0
+ (VI_TMO_IMMEDIATE) or as a floating point number in seconds.
+
+ """
+ if self.timeout is None:
+ ret_value = constants.VI_TMO_INFINITE
+ elif self.timeout == 0:
+ ret_value = constants.VI_TMO_IMMEDIATE
+ else:
+ ret_value = int(self.timeout * 1000.0)
+ return ret_value, StatusCode.success
+
+ def _set_timeout(self, attribute, value):
+ """ Sets timeout calculated value from python way to VI_ way
- elif not end_indicator_received and current == termination_char and termination_char_en:
- # RULE 6.1.2
- return out, constants.StatusCode.success_termination_character_read
+ In VISA, the timeout is expressed in milliseconds or using the
+ constants VI_TMO_INFINITE or VI_TMO_IMMEDIATE.
- elif not end_indicator_received and current != termination_char and len(out) == count:
- # RULE 6.1.3
- return out, constants.StatusCode.success_max_count_read
+ In Python we store it as either None (VI_TMO_INFINITE), 0
+ (VI_TMO_IMMEDIATE) or as a floating point number in seconds.
- if time.time() - start > timeout:
- return out, constants.StatusCode.error_timeout
+ """
+ if value == constants.VI_TMO_INFINITE:
+ self.timeout = None
+ elif value == constants.VI_TMO_IMMEDIATE:
+ self.timeout = 0
+ else:
+ self.timeout = value / 1000.0
+ return StatusCode.success
diff --git a/pyvisa-py/tcpip.py b/pyvisa-py/tcpip.py
index a862ef8..4b84140 100644
--- a/pyvisa-py/tcpip.py
+++ b/pyvisa-py/tcpip.py
@@ -15,29 +15,41 @@ import socket
import select
import time
-from pyvisa import constants, attributes
+from pyvisa import constants, attributes, errors
from .sessions import Session, UnknownAttribute
-from .protocols import vxi11
+from .protocols import vxi11, rpc
from . import common
StatusCode = constants.StatusCode
-SUCCESS = StatusCode.success
+
+# Conversion between VXI11 error codes and VISA status
+# TODO this is so far a best guess, in particular 6 and 29 are likely wrong
+VXI11_ERRORS_TO_VISA =\
+ {0: StatusCode.success, # no_error
+ 1: StatusCode.error_invalid_format, # syntax_error
+ 3: StatusCode.error_connection_lost, # device_no_accessible
+ 4: StatusCode.error_invalid_access_key, # invalid_link_identifier
+ 5: StatusCode.error_invalid_parameter, # parameter_error
+ 6: StatusCode.error_handler_not_installed, # channel_not_established
+ 8: StatusCode.error_nonsupported_operation, # operation_not_supported
+ 9: StatusCode.error_allocation, # out_of_resources
+ 11: StatusCode.error_resource_locked, # device_locked_by_another_link
+ 12: StatusCode.error_session_not_locked, # no_lock_held_by_this_link
+ 15: StatusCode.error_timeout, # io_timeout
+ 17: StatusCode.error_io, # io_error
+ 23: StatusCode.error_abort, # abort
+ 29: StatusCode.error_window_already_mapped, # channel_already_established
+ }
@Session.register(constants.InterfaceType.tcpip, 'INSTR')
class TCPIPInstrSession(Session):
- """A TCPIP Session that uses the network standard library to do the low level communication
- using VXI-11
- """
-
- lock_timeout = 1000
- timeout = 1000
- client_id = None
+ """A TCPIP Session that uses the network standard library to do the low
+ level communication using VXI-11
- link = None
- max_recv_size = 1024
+ """
@staticmethod
def list_resources():
@@ -47,16 +59,17 @@ class TCPIPInstrSession(Session):
def after_parsing(self):
# TODO: board_number not handled
# TODO: lan_device_name not handled
- self.interface = vxi11.CoreClient(self.parsed.host_address)
+ # vx11 expect all timeouts to be expressed in ms and should be integers
+ self.interface = vxi11.CoreClient(self.parsed.host_address,
+ self.open_timeout)
+ self.max_recv_size = 1024
self.lock_timeout = 10000
- self.timeout = 10000
self.client_id = random.getrandbits(31)
- (error, link,
- abort_port,
- max_recv_size) = self.interface.create_link(self.client_id, 0, self.lock_timeout,
- self.parsed.lan_device_name)
+ error, link, abort_port, max_recv_size =\
+ self.interface.create_link(self.client_id, 0, self.lock_timeout,
+ self.parsed.lan_device_name)
if error:
raise Exception("error creating link: %d" % error)
@@ -64,12 +77,17 @@ class TCPIPInstrSession(Session):
self.link = link
self.max_recv_size = min(max_recv_size, 2 ** 30) # 1GB
- for name in 'SEND_END_EN,TERMCHAR,TERMCHAR_EN'.split(','):
+ for name in ('SEND_END_EN', 'TERMCHAR', 'TERMCHAR_EN'):
attribute = getattr(constants, 'VI_ATTR_' + name)
- self.attrs[attribute] = attributes.AttributesByID[attribute].default
+ self.attrs[attribute] =\
+ attributes.AttributesByID[attribute].default
def close(self):
- self.interface.destroy_link(self.link)
+ try:
+ self.interface.destroy_link(self.link)
+ except (errors.VisaIOError, socket.error, rpc.RPCError) as e:
+ print("Error closing VISA link: {}".format(e))
+
self.interface.close()
self.link = None
self.interface = None
@@ -88,32 +106,35 @@ class TCPIPInstrSession(Session):
else:
chunk_length = self.max_recv_size
- flags = 0
- reason = 0
-
if self.get_attribute(constants.VI_ATTR_TERMCHAR_EN)[0]:
term_char, _ = self.get_attribute(constants.VI_ATTR_TERMCHAR)
- else:
- term_char = 0
-
- if term_char:
flags = vxi11.OP_FLAG_TERMCHAR_SET
- term_char = str(term_char).encode('utf-8')[0]
+ else:
+ term_char = flags = 0
read_data = bytearray()
-
+ reason = 0
+ # Stop on end of message or when a termination character has been
+ # encountered.
end_reason = vxi11.RX_END | vxi11.RX_CHR
-
read_fun = self.interface.device_read
+ status = StatusCode.success
- status = SUCCESS
-
+ timeout = self._io_timeout
+ start_time = time.time()
while reason & end_reason == 0:
- error, reason, data = read_fun(self.link, chunk_length, self.timeout,
+ # Decrease timeout so that the total timeout does not get larger
+ # than the specified timeout.
+ timeout = max(0,
+ timeout - int((time.time() - start_time)*1000))
+ error, reason, data = read_fun(self.link, chunk_length,
+ timeout,
self.lock_timeout, flags, term_char)
- if error:
- return read_data, StatusCode.error_io
+ if error == vxi11.ErrorCodes.io_timeout:
+ return bytes(read_data), StatusCode.error_timeout
+ elif error:
+ return bytes(read_data), StatusCode.error_io
read_data.extend(data)
count -= len(data)
@@ -133,42 +154,44 @@ class TCPIPInstrSession(Session):
:param data: data to be written.
:type data: str
- :return: Number of bytes actually transferred, return value of the library call.
+ :return: Number of bytes actually transferred, return value of the
+ library call.
:rtype: int, VISAStatus
"""
send_end, _ = self.get_attribute(constants.VI_ATTR_SEND_END_EN)
-
chunk_size = 1024
- try:
+ try:
if send_end:
flags = vxi11.OP_FLAG_TERMCHAR_SET
else:
flags = 0
num = len(data)
-
offset = 0
while num > 0:
if num <= chunk_size:
flags |= vxi11.OP_FLAG_END
- block = data[offset:offset+self.max_recv_size]
+ block = data[offset:offset + self.max_recv_size]
- error, size = self.interface.device_write(self.link, self.timeout,
- self.lock_timeout, flags, block)
+ error, size = self.interface.device_write(
+ self.link, self._io_timeout, self.lock_timeout,
+ flags, block)
- if error:
- return offset, StatusCode.error_io
- elif size < len(block):
+ if error == vxi11.ErrorCodes.io_timeout:
+ return offset, StatusCode.error_timeout
+
+ elif error or size < len(block):
return offset, StatusCode.error_io
offset += size
num -= size
- return offset, SUCCESS
+ return offset, StatusCode.success
+
except vxi11.Vxi11Error:
return 0, StatusCode.error_timeout
@@ -178,12 +201,13 @@ class TCPIPInstrSession(Session):
Use to implement custom logic for attributes.
:param attribute: Resource attribute for which the state query is made
- :return: The state of the queried attribute for a specified resource, return value of the library call.
+ :return: The state of the queried attribute for a specified resource,
+ return value of the library call.
:rtype: (unicode | str | list | int, VISAStatus)
"""
if attribute == constants.VI_ATTR_TCPIP_ADDR:
- return self.host_address, SUCCESS
+ return self.host_address, StatusCode.success
elif attribute == constants.VI_ATTR_TCPIP_DEVICE_NAME:
raise NotImplementedError
@@ -210,8 +234,10 @@ class TCPIPInstrSession(Session):
Corresponds to viSetAttribute function of the VISA library.
- :param attribute: Attribute for which the state is to be modified. (Attributes.*)
- :param attribute_state: The state of the attribute to be set for the specified object.
+ :param attribute: Attribute for which the state is to be modified.
+ (Attributes.*)
+ :param attribute_state: The state of the attribute to be set for the
+ specified object.
:return: return value of the library call.
:rtype: VISAStatus
"""
@@ -223,20 +249,16 @@ class TCPIPInstrSession(Session):
Corresponds to viAssertTrigger function of the VISA library.
- :param protocol: Trigger protocol to use during assertion. (Constants.PROT*)
+ :param protocol: Trigger protocol to use during assertion.
+ (Constants.PROT*)
:return: return value of the library call.
:rtype: VISAStatus
"""
- flags = 0
-
- error = self.interface.device_trigger(self.link, flags, self.lock_timeout, self.io_timeout)
-
- if error:
- # TODO: Which status to return
- raise Exception("error triggering: %d" % error)
+ error = self.interface.device_trigger(self.link, 0, self.lock_timeout,
+ self._io_timeout)
- return SUCCESS
+ return VXI11_ERRORS_TO_VISA[error]
def clear(self):
"""Clears a device.
@@ -247,13 +269,10 @@ class TCPIPInstrSession(Session):
:rtype: VISAStatus
"""
- flags = 0
-
- error = self.interface.device_clear(self.link, flags, self.lock_timeout, self.io_timeout)
+ error = self.interface.device_clear(self.link, 0, self.lock_timeout,
+ self._io_timeout)
- if error:
- # TODO: Which status to return
- raise Exception("error clearing: %d" % error)
+ return VXI11_ERRORS_TO_VISA[error]
def read_stb(self):
"""Reads a status byte of the service request.
@@ -261,28 +280,29 @@ class TCPIPInstrSession(Session):
Corresponds to viReadSTB function of the VISA library.
:return: Service request status byte, return value of the library call.
- :rtype: int, VISAStatus
+ :rtype: int, :class:`pyvisa.constants.StatusCode`
"""
- flags = 0
-
- error, stb = self.interface.device_read_stb(self.link, flags, self.lock_timeout, self.io_timeout)
- if error:
- # TODO: Which status to return
- raise Exception("error reading status: %d" % error)
+ error, stb = self.interface.device_read_stb(self.link, 0,
+ self.lock_timeout,
+ self._io_timeout)
- return stb, SUCCESS
+ return stb, VXI11_ERRORS_TO_VISA[error]
def lock(self, lock_type, timeout, requested_key=None):
"""Establishes an access mode to the specified resources.
Corresponds to viLock function of the VISA library.
- :param lock_type: Specifies the type of lock requested, either Constants.EXCLUSIVE_LOCK or Constants.SHARED_LOCK.
- :param timeout: Absolute time period (in milliseconds) that a resource waits to get unlocked by the
- locking session before returning an error.
- :param requested_key: This parameter is not used and should be set to VI_NULL when lockType is VI_EXCLUSIVE_LOCK.
- :return: access_key that can then be passed to other sessions to share the lock, return value of the library call.
+ :param lock_type: Specifies the type of lock requested, either
+ Constants.EXCLUSIVE_LOCK or Constants.SHARED_LOCK.
+ :param timeout: Absolute time period (in milliseconds) that a resource
+ waits to get unlocked by the locking session before returning an
+ error.
+ :param requested_key: This parameter is not used and should be set to
+ VI_NULL when lockType is VI_EXCLUSIVE_LOCK.
+ :return: access_key that can then be passed to other sessions to share
+ the lock, return value of the library call.
:rtype: str, VISAStatus
"""
@@ -291,9 +311,7 @@ class TCPIPInstrSession(Session):
error = self.interface.device_lock(self.link, flags, self.lock_timeout)
- if error:
- # TODO: Which status to return
- raise Exception("error locking: %d" % error)
+ return VXI11_ERRORS_TO_VISA[error]
def unlock(self):
"""Relinquishes a lock for the specified resource.
@@ -303,27 +321,39 @@ class TCPIPInstrSession(Session):
:return: return value of the library call.
:rtype: VISAStatus
"""
- flags = 0
-
error = self.interface.device_unlock(self.link)
- if error:
- # TODO: Which message to return
- raise Exception("error unlocking: %d" % error)
+ return VXI11_ERRORS_TO_VISA[error]
+ def _set_timeout(self, attribute, value):
+ """ Sets timeout calculated value from python way to VI_ way
-@Session.register(constants.InterfaceType.tcpip, 'SOCKET')
-class TCPIPSocketSession(Session):
- """A TCPIP Session that uses the network standard library to do the low level communication.
- """
+ """
+ if value == constants.VI_TMO_INFINITE:
+ self.timeout = None
+ self._io_timeout = 2**32-1
+ elif value == constants.VI_TMO_IMMEDIATE:
+ self.timeout = 0
+ self._io_timeout = 0
+ else:
+ self.timeout = value / 1000.0
+ self._io_timeout = int(self.timeout*1000)
+ return StatusCode.success
- lock_timeout = 1000
- timeout = 1000
- max_recv_size = 4096
+@Session.register(constants.InterfaceType.tcpip, 'SOCKET')
+class TCPIPSocketSession(Session):
+ """A TCPIP Session that uses the network standard library to do the low
+ level communication.
- # This buffer is used to store the bytes that appeared after termination char
- _pending_buffer = b''
+ """
+ # Details about implementation:
+ # On Windows, select is not interrupted by KeyboardInterrupt, to avoid
+ # blocking for very long time, we use a decreasing timeout in select.
+ # A minimum select timeout which prevents using too short select interval
+ # is also calculated and select timeout is not lower that that minimum
+ # timeout. The absolute minimum is 1 ms as a consequence.
+ # This is valid for connect and read operations
@staticmethod
def list_resources():
@@ -333,21 +363,65 @@ class TCPIPSocketSession(Session):
def after_parsing(self):
# TODO: board_number not handled
- self.interface = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- self.interface.setblocking(0)
+ ret_status = self._connect()
+ if ret_status != StatusCode.success:
+ self.close()
+ raise Exception("could not connect: {0}".format(str(ret_status)))
- try:
- self.interface.connect_ex((self.parsed.host_address, int(self.parsed.port)))
- except Exception as e:
- raise Exception("could not create socket: %s" % e)
+ self.max_recv_size = 4096
+ # This buffer is used to store the bytes that appeared after
+ # termination char
+ self._pending_buffer = bytearray()
self.attrs[constants.VI_ATTR_TCPIP_ADDR] = self.parsed.host_address
self.attrs[constants.VI_ATTR_TCPIP_PORT] = self.parsed.port
self.attrs[constants.VI_ATTR_INTF_NUM] = self.parsed.board
-
- for name in 'TERMCHAR,TERMCHAR_EN'.split(','):
+ self.attrs[constants.VI_ATTR_TCPIP_NODELAY] = (self._get_tcpip_nodelay,
+ self._set_attribute)
+ self.attrs[constants.VI_ATTR_TCPIP_HOSTNAME] = ''
+ self.attrs[constants.VI_ATTR_TCPIP_KEEPALIVE] = \
+ (self._get_tcpip_keepalive, self._set_tcpip_keepalive)
+ # to use default as ni visa driver (NI-VISA 15.0)
+ self.attrs[constants.VI_ATTR_SUPPRESS_END_EN] = True
+
+ for name in ('TERMCHAR', 'TERMCHAR_EN'):
attribute = getattr(constants, 'VI_ATTR_' + name)
- self.attrs[attribute] = attributes.AttributesByID[attribute].default
+ self.attrs[attribute] =\
+ attributes.AttributesByID[attribute].default
+
+ def _connect(self):
+ timeout = self.open_timeout / 1000.0 if self.open_timeout else 10.0
+ try:
+ self.interface = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.interface.setblocking(0)
+ self.interface.connect_ex((self.parsed.host_address,
+ int(self.parsed.port)))
+ except Exception as e:
+ raise Exception("could not connect: {0}".format(str(e)))
+ finally:
+ self.interface.setblocking(1)
+
+ # minimum is in interval 100 - 500ms based on timeout
+ min_select_timeout = max(min(timeout/10.0, 0.5), 0.1)
+ # initial 'select_timout' is half of timeout or max 2 secs
+ # (max blocking time). min is from 'min_select_timeout'
+ select_timout = max(min(timeout/2.0, 2.0), min_select_timeout)
+ # time, when loop shall finish
+ finish_time = time.time() + timeout
+ while True:
+ # use select to wait for socket ready, max `select_timout` seconds
+ r, w, x = select.select([self.interface], [self.interface], [],
+ select_timout)
+ if self.interface in r or self.interface in w:
+ return StatusCode.success
+
+ if time.time() >= finish_time:
+ # reached timeout
+ return StatusCode.error_timeout
+
+ # `select_timout` decreased to 50% of previous or
+ # min_select_timeout
+ select_timout = max(select_timout / 2.0, min_select_timeout)
def close(self):
self.interface.close()
@@ -367,47 +441,72 @@ class TCPIPSocketSession(Session):
else:
chunk_length = self.max_recv_size
- end_char, _ = self.get_attribute(constants.VI_ATTR_TERMCHAR)
- enabled, _ = self.get_attribute(constants.VI_ATTR_TERMCHAR_EN)
- timeout, _ = self.get_attribute(constants.VI_ATTR_TMO_VALUE)
- timeout /= 1000
-
- end_byte = common.int_to_byte(end_char) if end_char else b''
+ term_char, _ = self.get_attribute(constants.VI_ATTR_TERMCHAR)
+ term_byte = common.int_to_byte(term_char) if term_char else b''
+ term_char_en, _ = self.get_attribute(constants.VI_ATTR_TERMCHAR_EN)
+ suppress_end_en, _ =\
+ self.get_attribute(constants.VI_ATTR_SUPPRESS_END_EN)
read_fun = self.interface.recv
- now = start = time.time()
-
- out = self._pending_buffer
-
- if enabled and end_byte in out:
- parts = out.split(end_byte)
- self._pending_buffer = b''.join(parts[1:])
- return (out + parts[0] + end_byte,
- constants.StatusCode.success_termination_character_read)
-
- while now - start <= timeout:
- # use select to wait for read ready
- select.select([self.interface], [], [])
- last = read_fun(chunk_length)
-
- if not last:
- time.sleep(.01)
- now = time.time()
- continue
-
- if enabled and end_byte in last:
- parts = last.split(end_byte)
- self._pending_buffer = b''.join(parts[1:])
- return (out + parts[0] + end_byte,
- constants.StatusCode.success_termination_character_read)
-
- out += last
-
- if len(out) == count:
- return out, constants.StatusCode.success_max_count_read
- else:
- return out, constants.StatusCode.error_timeout
+ # minimum is in interval 1 - 100ms based on timeout, 1sec if no timeout
+ # defined
+ min_select_timeout = (1 if self.timeout is None else
+ max(min(self.timeout / 100.0, 0.1), 0.001))
+ # initial 'select_timout' is half of timeout or max 2 secs
+ # (max blocking time). min is from 'min_select_timeout'
+ select_timout = (2.0 if self.timeout is None else
+ max(min(self.timeout / 2.0, 2.0), min_select_timeout))
+ # time, when loop shall finish, None means never ending story if no
+ # data arrives
+ finish_time = (None if self.timeout is None else
+ (time.time() + self.timeout))
+ while True:
+
+ # check, if we have any data received (from pending buffer or
+ # further reading)
+ if term_char_en and term_byte in self._pending_buffer:
+ term_byte_index = self._pending_buffer.index(term_byte) + 1
+ if term_byte_index > count:
+ term_byte_index = count
+ status = StatusCode.success_max_count_read
+ else:
+ status = StatusCode.success_termination_character_read
+ out = bytes(self._pending_buffer[:term_byte_index])
+ self._pending_buffer = self._pending_buffer[term_byte_index:]
+ return out, status
+
+ if len(self._pending_buffer) >= count:
+ out = bytes(self._pending_buffer[:count])
+ self._pending_buffer = self._pending_buffer[count:]
+ return out, StatusCode.success_max_count_read
+
+ # use select to wait for read ready, max `select_timout` seconds
+ r, w, x = select.select([self.interface], [], [], select_timout)
+
+ read_data = b''
+ if self.interface in r:
+ read_data = read_fun(chunk_length)
+ self._pending_buffer.extend(read_data)
+
+ if not read_data:
+ # can't read chunk or timeout
+ if self._pending_buffer and not suppress_end_en:
+ # we have some data without termchar but no further data
+ # expected
+ out = bytes(self._pending_buffer[:count])
+ self._pending_buffer = self._pending_buffer[count:]
+ return out, StatusCode.succes
+
+ if finish_time and time.time() >= finish_time:
+ # reached timeout
+ out = bytes(self._pending_buffer[:count])
+ self._pending_buffer = self._pending_buffer[count:]
+ return out, StatusCode.error_timeout
+
+ # `select_timout` decreased to 50% of previous or
+ # min_select_timeout
+ select_timout = max(select_timout / 2.0, min_select_timeout)
def write(self, data):
"""Writes data to device or interface synchronously.
@@ -416,7 +515,8 @@ class TCPIPSocketSession(Session):
:param data: data to be written.
:type data: str
- :return: Number of bytes actually transferred, return value of the library call.
+ :return: Number of bytes actually transferred, return value of the
+ library call.
:rtype: int, VISAStatus
"""
@@ -428,7 +528,7 @@ class TCPIPSocketSession(Session):
while num > 0:
- block = data[offset:min(offset+chunk_size, sz)]
+ block = data[offset:min(offset + chunk_size, sz)]
try:
# use select to wait for write ready
@@ -443,7 +543,37 @@ class TCPIPSocketSession(Session):
offset += size
num -= size
- return offset, SUCCESS
+ return offset, StatusCode.success
+
+ def _get_tcpip_nodelay(self, attribute):
+ if self.interface:
+ value = self.interface.getsockopt(socket.IPPROTO_TCP,
+ socket.TCP_NODELAY)
+ return (constants.VI_TRUE if value == 1 else
+ constants.VI_FALSE, StatusCode.succes)
+ return 0, StatusCode.error_nonsupported_attribute
+
+ def _set_tcpip_nodelay(self, attribute, attribute_state):
+ if self.interface:
+ self.interface.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY,
+ 1 if attribute_state else 0)
+ return StatusCode.succes
+ return 0, StatusCode.error_nonsupported_attribute
+
+ def _get_tcpip_keepalive(self, attribute):
+ if self.interface:
+ value = self.interface.getsockopt(socket.SOL_SOCKET,
+ socket.SO_KEEPALIVE)
+ return (constants.VI_TRUE if value == 1 else
+ constants.VI_FALSE, StatusCode.succes)
+ return 0, StatusCode.error_nonsupported_attribute
+
+ def _set_tcpip_keepalive(self, attribute, attribute_state):
+ if self.interface:
+ self.interface.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE,
+ 1 if attribute_state else 0)
+ return StatusCode.succes
+ return 0, StatusCode.error_nonsupported_attribute
def _get_attribute(self, attribute):
"""Get the value for a given VISA attribute for this session.
@@ -451,19 +581,10 @@ class TCPIPSocketSession(Session):
Use to implement custom logic for attributes.
:param attribute: Resource attribute for which the state query is made
- :return: The state of the queried attribute for a specified resource, return value of the library call.
+ :return: The state of the queried attribute for a specified resource,
+ return value of the library call.
:rtype: (unicode | str | list | int, VISAStatus)
"""
-
- if attribute == constants.VI_ATTR_TCPIP_HOSTNAME:
- raise NotImplementedError
-
- elif attribute == constants.VI_ATTR_TCPIP_KEEPALIVE:
- raise NotImplementedError
-
- elif attribute == constants.VI_ATTR_TCPIP_NODELAY:
- raise NotImplementedError
-
raise UnknownAttribute(attribute)
def _set_attribute(self, attribute, attribute_state):
@@ -471,8 +592,10 @@ class TCPIPSocketSession(Session):
Corresponds to viSetAttribute function of the VISA library.
- :param attribute: Attribute for which the state is to be modified. (Attributes.*)
- :param attribute_state: The state of the attribute to be set for the specified object.
+ :param attribute: Attribute for which the state is to be modified.
+ (Attributes.*)
+ :param attribute_state: The state of the attribute to be set for the
+ specified object.
:return: return value of the library call.
:rtype: VISAStatus
"""
diff --git a/pyvisa-py/testsuite/__init__.py b/pyvisa-py/testsuite/__init__.py
index 6157c88..27784b2 100644
--- a/pyvisa-py/testsuite/__init__.py
+++ b/pyvisa-py/testsuite/__init__.py
@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
-from __future__ import division, unicode_literals, print_function, absolute_import
+from __future__ import (division, unicode_literals, print_function,
+ absolute_import)
import os
-from pyvisa.compat import unittest
+import unittest
def testsuite():
@@ -29,4 +30,3 @@ def run():
"""
test_runner = unittest.TextTestRunner()
return test_runner.run(testsuite())
-
diff --git a/pyvisa-py/usb.py b/pyvisa-py/usb.py
index 154879e..bf7cfc3 100644
--- a/pyvisa-py/usb.py
+++ b/pyvisa-py/usb.py
@@ -12,6 +12,7 @@
from __future__ import division, unicode_literals, print_function, absolute_import
+import logging
from pyvisa import constants, attributes
from .sessions import Session, UnknownAttribute
@@ -20,41 +21,34 @@ try:
import usb
from .protocols import usbtmc, usbutil, usbraw
except ImportError as e:
- Session.register_unavailable(constants.InterfaceType.usb, 'INSTR',
- 'Please install PyUSB to use this resource type.\n%s' % e)
-
- Session.register_unavailable(constants.InterfaceType.usb, 'RAW',
- 'Please install PyUSB to use this resource type.\n%s' % e)
+ msg = 'Please install PyUSB to use this resource type.\n%s'
+ Session.register_unavailable(constants.InterfaceType.usb,
+ 'INSTR', msg % e)
+ Session.register_unavailable(constants.InterfaceType.usb,
+ 'RAW', msg % e)
raise
try:
_ = usb.core.find()
-except ValueError as e:
+except Exception as e:
msg = 'PyUSB does not seem to be properly installed.\n' \
- 'Please refer to PyUSB documentation and ' \
- 'install a suitable backend like ' \
- 'libusb 0.1, libusb 1.0, libusbx, ' \
- 'libusb-win32 or OpenUSB\%s' % e
-
+ 'Please refer to PyUSB documentation and \n' \
+ 'install a suitable backend like \n' \
+ 'libusb 0.1, libusb 1.0, libusbx, \n' \
+ 'libusb-win32 or OpenUSB.\n%s' % e
Session.register_unavailable(constants.InterfaceType.usb, 'INSTR', msg)
-
Session.register_unavailable(constants.InterfaceType.usb, 'RAW', msg)
-
raise
-from . import common
-
StatusCode = constants.StatusCode
-SUCCESS = StatusCode.success
+
class USBSession(Session):
"""Base class for drivers that communicate with usb devices
via usb port using pyUSB
"""
- timeout = 2000
-
@staticmethod
def list_resources():
"""Return list of resources for this type of USB device"""
@@ -70,11 +64,26 @@ class USBSession(Session):
try:
# noinspection PyProtectedMember
backend = usb.core.find()._ctx.backend.__class__.__module__.split('.')[-1]
- except:
+ except Exception:
backend = 'N/A'
return 'via PyUSB (%s). Backend: %s' % (ver, backend)
+ def _get_timeout(self, attribute):
+ if self.interface:
+ if self.interface.timeout == 2**32-1:
+ self.timeout = None
+ else:
+ self.timeout = self.interface.timeout / 1000
+ return super(USBSession, self)._get_timeout(attribute)
+
+ def _set_timeout(self, attribute, value):
+ status = super(USBSession, self)._set_timeout(attribute, value)
+ timeout = int(self.timeout*1000) if self.timeout else 2**32-1
+ if self.interface:
+ self.interface.timeout = timeout
+ return status
+
def read(self, count):
"""Reads data from device or interface synchronously.
@@ -93,9 +102,9 @@ class USBSession(Session):
term_char, _ = self.get_attribute(constants.VI_ATTR_TERMCHAR)
term_char_en, _ = self.get_attribute(constants.VI_ATTR_TERMCHAR_EN)
- return self._read(lambda: self.interface.read(1),
+ return self._read(lambda: self.interface.read(count),
count,
- lambda current: False,
+ lambda current: True, # USB always returns a complete message
supress_end_en,
term_char,
term_char_en,
@@ -116,7 +125,7 @@ class USBSession(Session):
count = self.interface.write(data)
- return count, SUCCESS
+ return count, StatusCode.success
def close(self):
self.interface.close()
@@ -146,6 +155,7 @@ class USBSession(Session):
raise UnknownAttribute(attribute)
+
@Session.register(constants.InterfaceType.usb, 'INSTR')
class USBInstrSession(USBSession):
"""Base class for drivers that communicate with instruments
@@ -158,15 +168,30 @@ class USBInstrSession(USBSession):
fmt = 'USB%(board)s::%(manufacturer_id)s::%(model_code)s::' \
'%(serial_number)s::%(usb_interface_number)s::INSTR'
for dev in usbtmc.find_tmc_devices():
- intfc = usbutil.find_interfaces(dev, bInterfaceClass=0xfe, bInterfaceSubClass=3)
+ intfc = usbutil.find_interfaces(dev, bInterfaceClass=0xfe,
+ bInterfaceSubClass=3)
try:
intfc = intfc[0].index
except (IndexError, AttributeError):
intfc = 0
+
+ try:
+ serial = dev.serial_number
+ except (NotImplementedError, ValueError):
+ logger = logging.getLogger(__name__)
+ msg = ('Found a device whose serial number cannot be read.'
+ ' The partial VISA resource name is: ' + fmt)
+ logger.warning(msg, dict(board=0,
+ manufacturer_id=dev.idVendor,
+ model_code=dev.idProduct,
+ serial_number='???',
+ usb_interface_number=intfc))
+ continue
+
out.append(fmt % dict(board=0,
manufacturer_id=dev.idVendor,
model_code=dev.idProduct,
- serial_number=dev.serial_number,
+ serial_number=serial,
usb_interface_number=intfc))
return out
@@ -175,7 +200,7 @@ class USBInstrSession(USBSession):
int(self.parsed.model_code, 0),
self.parsed.serial_number)
- for name in 'SEND_END_EN,TERMCHAR,TERMCHAR_EN'.split(','):
+ for name in ('SEND_END_EN', 'TERMCHAR', 'TERMCHAR_EN'):
attribute = getattr(constants, 'VI_ATTR_' + name)
self.attrs[attribute] = attributes.AttributesByID[attribute].default
@@ -197,10 +222,24 @@ class USBRawSession(USBSession):
intfc = intfc[0].index
except (IndexError, AttributeError):
intfc = 0
+
+ try:
+ serial = dev.serial_number
+ except (NotImplementedError, ValueError):
+ logger = logging.getLogger(__name__)
+ msg = ('Found a device whose serial number cannot be read.'
+ ' The partial VISA resource name is: ' + fmt)
+ logger.warning(msg, dict(board=0,
+ manufacturer_id=dev.idVendor,
+ model_code=dev.idProduct,
+ serial_number='???',
+ usb_interface_number=intfc))
+ continue
+
out.append(fmt % dict(board=0,
manufacturer_id=dev.idVendor,
model_code=dev.idProduct,
- serial_number=dev.serial_number,
+ serial_number=serial,
usb_interface_number=intfc))
return out
@@ -209,6 +248,6 @@ class USBRawSession(USBSession):
int(self.parsed.model_code, 0),
self.parsed.serial_number)
- for name in 'SEND_END_EN,TERMCHAR,TERMCHAR_EN'.split(','):
+ for name in ('SEND_END_EN', 'TERMCHAR', 'TERMCHAR_EN'):
attribute = getattr(constants, 'VI_ATTR_' + name)
self.attrs[attribute] = attributes.AttributesByID[attribute].default
diff --git a/setup.py b/setup.py
index 411b3d2..a5a9e52 100644
--- a/setup.py
+++ b/setup.py
@@ -1,12 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-
-try:
- import sys
- reload(sys).setdefaultencoding("UTF-8")
-except:
- pass
-
+import sys
try:
from setuptools import setup
@@ -15,11 +9,9 @@ except ImportError:
sys.exit(1)
-import codecs
-
-
def read(filename):
- return codecs.open(filename, encoding='utf-8').read()
+ with open(filename, 'rb') as f:
+ return f.read().decode('utf8')
long_description = '\n\n'.join([read('README'),
@@ -30,18 +22,16 @@ __doc__ = long_description
requirements = ['pyvisa>=1.8']
-if sys.version_info < (2, 7):
- requirements.append('importlib')
setup(name='PyVISA-py',
description='Python VISA bindings for GPIB, RS232, and USB instruments',
- version='0.2',
+ version='0.3.0',
long_description=long_description,
author='Hernan E. Grecco',
author_email='hernan.grecco@gmail.com',
maintainer='Hernan E. Grecco',
maintainer_email='hernan.grecco@gmail.com',
- url='https://github.com/hgrecco/pyvisa-py',
+ url='https://github.com/pyvisa/pyvisa-py',
test_suite='pyvisa-py.testsuite.testsuite',
keywords='Remote VISA GPIB USB serial RS232 measurement acquisition',
license='MIT License',
@@ -57,11 +47,11 @@ setup(name='PyVISA-py',
'Programming Language :: Python',
'Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator',
'Topic :: Software Development :: Libraries :: Python Modules',
- 'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3.2',
- 'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
+ 'Programming Language :: Python :: 3.5',
+ 'Programming Language :: Python :: 3.6',
+ 'Programming Language :: Python :: 3.7',
],
packages=['pyvisa-py',
'pyvisa-py.protocols',