Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GPIBUSB connection enhancements #221

Merged
merged 59 commits into from
Jan 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
9359725
Add Fluke 3000 support
francois-drielsma May 31, 2019
9a4205f
Slight tweak
francois-drielsma Jun 1, 2019
6fdd7db
More robust measure function for fluke3000
francois-drielsma Jun 28, 2019
371ff09
Extend fluke3000 timeout
francois-drielsma Jun 28, 2019
06222e4
Much faster temp readouts
francois-drielsma Jul 3, 2019
1bcd0f8
Allow 0 measurements in fluke3000.py
francois-drielsma Jul 6, 2019
2c701c7
Restructured Fluke3000 read out around multiline reads
francois-drielsma Jul 6, 2019
a52d3aa
Added support for Fluke 3000FC wireless multimeter
francois-drielsma Jul 7, 2019
63283e6
Made the readout more robusts, implemented flush
francois-drielsma Jul 8, 2019
36fe586
Bug fix
francois-drielsma Jul 8, 2019
d387360
Added Prologix GPIB controller suppport
francois-drielsma Jul 17, 2019
3708c4e
Added Keithley 485 pico-ampmeter support
francois-drielsma Jul 17, 2019
4e66a2e
Bug fix
francois-drielsma Jul 17, 2019
9998772
Added status word parser for Keithley 485
francois-drielsma Jul 17, 2019
9c47584
Combined GPIB communicators into one
francois-drielsma Jul 17, 2019
9de7147
Corrected unit returned by Keithley 485
francois-drielsma Jul 17, 2019
db685e3
Attempt to fix build for python 3.7
francois-drielsma Jul 18, 2019
2442461
Reformated GPIB controller selection as a model selection
francois-drielsma Jul 18, 2019
c1e92ab
Multiple style fixes
francois-drielsma Jul 18, 2019
de8ba7c
More style corrections
francois-drielsma Jul 18, 2019
3006461
One more bug fix...
francois-drielsma Jul 18, 2019
b0dc07b
One more
francois-drielsma Jul 18, 2019
f81c946
GPIB model now passed as str
francois-drielsma Jul 18, 2019
23741e0
Bug fix
francois-drielsma Jul 18, 2019
8686ac4
Added support for HP E3631A power supply
francois-drielsma Jul 31, 2019
e93aaf2
Removed print functions inside HPe3631a class
francois-drielsma Jul 31, 2019
7b8bebd
Style fixes in class HPe3631a
francois-drielsma Jul 31, 2019
62a601f
Range use style fix
francois-drielsma Jul 31, 2019
2a242b0
Last style fix...
francois-drielsma Jul 31, 2019
ac056fe
Completed support for Keithley 485
francois-drielsma Aug 2, 2019
9529ec7
Style fixes for Keithley 485
francois-drielsma Aug 2, 2019
bf46193
Python 2.7 style corrections for Keithley 485
francois-drielsma Aug 2, 2019
eb0c679
Completed support for Prologix GPIB controllers
francois-drielsma Aug 3, 2019
508f9f1
Fixed and renamed GPIB communicator pytest
francois-drielsma Aug 3, 2019
15a2167
Keythley 485 no longer queries when setting properties (GPIB comm fixed)
francois-drielsma Aug 3, 2019
f527fff
Privatized obscure functions of Keithley 485, added get_status() func…
francois-drielsma Aug 3, 2019
b8c6459
Standardized range and trigger function names of Keithley 485
francois-drielsma Aug 3, 2019
73cdc72
Trigger name change fix
francois-drielsma Aug 3, 2019
b1ca842
Added support for Glassman FR (EJ, ET, EY, FJ) power supplies
francois-drielsma Aug 6, 2019
8bd3b85
Style fix
francois-drielsma Aug 6, 2019
a61a205
Completed documentation for Keithley 485
francois-drielsma Aug 6, 2019
23dc4ec
Completed documentation for HP E3631A
francois-drielsma Aug 6, 2019
1129ad4
Completed Fluke 3000 FC support
francois-drielsma Aug 9, 2019
a3fdf31
Fixed ignored variable name
francois-drielsma Aug 9, 2019
e0b05f5
Added tests for the Keithley 485
francois-drielsma Aug 11, 2019
eddb417
Style fixes, python 2.7 compatibility
francois-drielsma Aug 11, 2019
a1232a3
Stray print function removed
francois-drielsma Aug 11, 2019
a26cb40
Added tests for the Fluke 3000
francois-drielsma Aug 11, 2019
d270302
Indentation style fix in Fluke 3000 tests
francois-drielsma Aug 11, 2019
fea97d4
Convert Fluke 3000 unsettable attributed to read-only
francois-drielsma Aug 12, 2019
ec817e1
Removed superfluous tests for Fluke 3000
francois-drielsma Aug 12, 2019
c5b2180
Added tests for the Glassman FR-series power supplies
francois-drielsma Aug 12, 2019
2077b61
Explicit builtin round function import
francois-drielsma Aug 12, 2019
a74238e
Greatly simplified implementation of the HP E3631A power supply
francois-drielsma Aug 13, 2019
4b67b35
Added tests for the HP E3631A power supply
francois-drielsma Aug 13, 2019
cadfe76
Added generic SCPI commands to the HP E3631A power supply
francois-drielsma Aug 16, 2019
6df0c41
Fix voltage property of HP E3631A to handle negative channel
francois-drielsma Aug 23, 2019
1d39947
Remove non-GPIBUSB changes
scasagrande Sep 22, 2019
1458c7b
Update formatting and docstrings
scasagrande Sep 22, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions instruments/abstract_instruments/comm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@

from .abstract_comm import AbstractCommunicator

from .file_communicator import FileCommunicator
from .gpib_communicator import GPIBCommunicator
from .loopback_communicator import LoopbackCommunicator
from .serial_communicator import SerialCommunicator
from .socket_communicator import SocketCommunicator
from .usb_communicator import USBCommunicator
from .serial_communicator import SerialCommunicator
from .visa_communicator import VisaCommunicator
from .loopback_communicator import LoopbackCommunicator
from .gi_gpib_communicator import GPIBCommunicator
from .file_communicator import FileCommunicator
from .usbtmc_communicator import USBTMCCommunicator
from .visa_communicator import VisaCommunicator
from .vxi11_communicator import VXI11Communicator
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
"""
Provides a communication layer for an instrument connected via a Galvant
Industries GPIB adapter.
Industries or Prologix GPIB adapter.
"""

# IMPORTS #####################################################################
Expand All @@ -11,6 +11,7 @@
from __future__ import division
from __future__ import unicode_literals

from enum import Enum
import io
import time

Expand All @@ -27,29 +28,43 @@ class GPIBCommunicator(io.IOBase, AbstractCommunicator):

"""
Communicates with a SocketCommunicator or SerialCommunicator object for
use with Galvant Industries GPIBUSB or GPIBETHERNET adapters.
use with Galvant Industries or Prologix GPIBUSB or GPIBETHERNET adapters.

It essentially wraps those physical communication layers with the extra
overhead required by the Galvant GPIB adapters.
overhead required by the GPIB adapters.
"""

# pylint: disable=too-many-instance-attributes
def __init__(self, filelike, gpib_address):
def __init__(self, filelike, gpib_address, model="gi"):
super(GPIBCommunicator, self).__init__(self)

self._model = self.Model(model)
self._file = filelike
self._gpib_address = gpib_address
self._file.terminator = "\r"
self._version = int(self._file.query("+ver"))
if self._model == GPIBCommunicator.Model.gi:
self._version = int(self._file.query("+ver"))
if self._model == GPIBCommunicator.Model.pl:
self._file.sendcmd("++auto 0")
self._terminator = None
self.terminator = "\n"
self._eoi = True
self._timeout = 1000 * pq.millisecond
if self._version <= 4:
if self._model == GPIBCommunicator.Model.gi and self._version <= 4:
self._eos = 10
else:
self._eos = "\n"

# ENUMS #

class Model(Enum):
"""
Enum containing the supported GPIB controller models
"""
#: Galvant Industries
gi = "gi"
#: Prologix, LLC
pl = "pl"

# PROPERTIES #

@property
Expand Down Expand Up @@ -95,12 +110,12 @@ def timeout(self):
@timeout.setter
def timeout(self, newval):
newval = assume_units(newval, pq.second)
if self._version <= 4:
if self._model == GPIBCommunicator.Model.gi and self._version <= 4:
newval = newval.rescale(pq.second)
self._file.sendcmd('+t:{}'.format(newval.magnitude))
elif self._version >= 5:
self._file.sendcmd('+t:{}'.format(int(newval.magnitude)))
else:
newval = newval.rescale(pq.millisecond)
self._file.sendcmd("++read_tmo_ms {}".format(newval.magnitude))
self._file.sendcmd("++read_tmo_ms {}".format(int(newval.magnitude)))
self._file.timeout = newval.rescale(pq.second)
self._timeout = newval.rescale(pq.second)

Expand All @@ -127,7 +142,7 @@ def terminator(self, newval):
if isinstance(newval, str):
newval = newval.lower()

if self._version <= 4:
if self._model == GPIBCommunicator.Model.gi and self._version <= 4:
if newval == 'eoi':
self.eoi = True
elif not isinstance(newval, int):
Expand All @@ -148,7 +163,7 @@ def terminator(self, newval):
self.eoi = False
self.eos = newval
self._terminator = chr(newval)
elif self._version >= 5:
else:
if newval != "eoi":
self.eos = newval
self.eoi = False
Expand Down Expand Up @@ -182,10 +197,10 @@ def eoi(self, newval):
if not isinstance(newval, bool):
raise TypeError("EOI status must be specified as a boolean")
self._eoi = newval
if self._version >= 5:
self._file.sendcmd("++eoi {}".format('1' if newval else '0'))
else:
if self._model == GPIBCommunicator.Model.gi and self._version <= 4:
self._file.sendcmd("+eoi:{}".format('1' if newval else '0'))
else:
self._file.sendcmd("++eoi {}".format('1' if newval else '0'))

@property
def eos(self):
Expand All @@ -203,12 +218,12 @@ def eos(self):

@eos.setter
def eos(self, newval):
if self._version <= 4:
if self._model == GPIBCommunicator.Model.gi and self._version <= 4:
if isinstance(newval, (str, bytes)):
newval = ord(newval)
self._file.sendcmd("+eos:{}".format(newval))
self._eos = newval
elif self._version >= 5:
else:
if isinstance(newval, int):
newval = str(chr(newval))
if newval == "\r\n":
Expand Down Expand Up @@ -299,8 +314,8 @@ def flush_input(self):
def _sendcmd(self, msg):
"""
This is the implementation of ``sendcmd`` for communicating with
the Galvant Industries GPIB adapter. This function is in turn wrapped by
the concrete method `AbstractCommunicator.sendcmd` to provide consistent
the GPIB adapters. This function is in turn wrapped by the concrete
method `AbstractCommunicator.sendcmd` to provide consistent
logging functionality across all communication layers.

:param str msg: The command message to send to the instrument
Expand All @@ -309,7 +324,10 @@ def _sendcmd(self, msg):

if msg == '':
return
self._file.sendcmd('+a:' + str(self._gpib_address))
if self._model == GPIBCommunicator.Model.gi:
self._file.sendcmd("+a:{0}".format(str(self._gpib_address)))
else:
self._file.sendcmd("++addr {0}".format(str(self._gpib_address)))
time.sleep(sleep_time)
self.eoi = self.eoi
time.sleep(sleep_time)
Expand All @@ -323,13 +341,18 @@ def _sendcmd(self, msg):
def _query(self, msg, size=-1):
"""
This is the implementation of ``query`` for communicating with
the Galvant Industries GPIB adapter. This function is in turn wrapped by
the concrete method `AbstractCommunicator.query` to provide consistent
the GPIB adapters. This function is in turn wrapped by the concrete
method `AbstractCommunicator.query` to provide consistent
logging functionality across all communication layers.

If a ``?`` is not present in ``msg`` then the adapter will be
instructed to get the response from the instrument via the ``+read``
command.
The Galvant Industries adaptor is set to automatically get a
response if a ``?`` is present in ``msg``. If it is not present,
then the adapter will be instructed to get the response from the
instrument via the ``+read`` command.

The Prologix adapter is set to not get a response unless told to do
so. It is instructed to get a response from the instrument via the
``++read`` command.

:param str msg: The query message to send to the instrument
:param int size: The number of bytes to read back from the instrument
Expand All @@ -338,6 +361,8 @@ def _query(self, msg, size=-1):
:rtype: `str`
"""
self.sendcmd(msg)
if '?' not in msg:
if self._model == GPIBCommunicator.Model.gi and '?' not in msg:
self._file.sendcmd('+read')
if self._model == GPIBCommunicator.Model.pl:
self._file.sendcmd('++read')
return self._file.read(size).strip()
24 changes: 17 additions & 7 deletions instruments/abstract_instruments/instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ def open_serial(cls, port=None, baud=9600, vid=None, pid=None,
return cls(ser)

@classmethod
def open_gpibusb(cls, port, gpib_address, timeout=3, write_timeout=3):
def open_gpibusb(cls, port, gpib_address, timeout=3, write_timeout=3, model="gi"):
"""
Opens an instrument, connecting via a
`Galvant Industries GPIB-USB adapter`_.
Expand All @@ -544,6 +544,8 @@ def open_gpibusb(cls, port, gpib_address, timeout=3, write_timeout=3):
instrument before timing out.
:param float write_timeout: Number of seconds to wait when writing to the
instrument before timing out.
:param str model: The brand of adapter to be connected to. Currently supported
is "gi" for Galvant Industries, and "pl" for Prologix LLC.

:rtype: `Instrument`
:return: Object representing the connected instrument.
Expand All @@ -559,18 +561,26 @@ def open_gpibusb(cls, port, gpib_address, timeout=3, write_timeout=3):
timeout=timeout,
write_timeout=write_timeout
)
return cls(GPIBCommunicator(ser, gpib_address))
return cls(GPIBCommunicator(ser, gpib_address, model))

@classmethod
def open_gpibethernet(cls, host, port, gpib_address):
def open_gpibethernet(cls, host, port, gpib_address, model="pl"):
"""
.. warning:: The GPIB-Ethernet adapter that this connection would
use does not actually exist, and thus this class method should
not be used.
Opens an instrument, connecting via a Prologix GPIBETHERNET adapter.

:param str host: Name or IP address of the instrument.
:param int port: TCP port on which the insturment is listening.
:param int gpib_address: Address on the connected GPIB bus assigned to
the instrument.
:param str model: The brand of adapter to be connected to. Currently supported
is "gi" for Galvant Industries, and "pl" for Prologix LLC.

.. warning:: This function has been setup for use with the Prologix
GPIBETHERNET adapter but has not been tested as confirmed working.
"""
conn = socket.socket()
conn.connect((host, port))
return cls(GPIBCommunicator(conn, gpib_address))
return cls(GPIBCommunicator(conn, gpib_address, model))

@classmethod
def open_visa(cls, resource_name):
Expand Down
5 changes: 3 additions & 2 deletions instruments/tests/test_base_instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ def test_instrument_open_gpibusb(mock_serial_manager, mock_gpib_comm):
mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator
mock_gpib_comm.return_value.__class__ = GPIBCommunicator

inst = ik.Instrument.open_gpibusb("/dev/port", gpib_address=1)
inst = ik.Instrument.open_gpibusb("/dev/port", gpib_address=1, model="gi")

assert isinstance(inst._file, GPIBCommunicator) is True

Expand All @@ -270,7 +270,8 @@ def test_instrument_open_gpibusb(mock_serial_manager, mock_gpib_comm):

mock_gpib_comm.assert_called_with(
mock_serial_manager.new_serial_connection.return_value,
1
1,
"gi"
)


Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Unit tests for the GI GPIBUSB communication layer
Unit tests for the GPIBUSB communication layer
"""

# IMPORTS ####################################################################
Expand Down Expand Up @@ -199,7 +199,7 @@ def test_gpibusbcomm_timeout():
unit_eq(comm.timeout, 1000 * pq.millisecond)

comm.timeout = 5000 * pq.millisecond
comm._file.sendcmd.assert_called_with("++read_tmo_ms 5000.0")
comm._file.sendcmd.assert_called_with("++read_tmo_ms 5000")


def test_gpibusbcomm_close():
Expand Down Expand Up @@ -235,7 +235,7 @@ def test_gpibusbcomm_sendcmd():
comm._file.sendcmd.assert_has_calls([
mock.call("+a:1"),
mock.call("++eoi 1"),
mock.call("++read_tmo_ms 1000.0"),
mock.call("++read_tmo_ms 1000"),
mock.call("++eos 2"),
mock.call("mock")
])
Expand Down