Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ PyVISA-py Changelog
Other transports (GPIB, USBTMC, HiSLIP, Serial) remain unsupported for now. PR #577
- Pass termchar information to USBTMC devices when reading PR #598
- Properly configure termchar on GPIB devices. This fix timeout issues for old GPIB devices which do not signal EOI PR #599
- Clean up GPIB calls to the ib*() routines, by using symbolic definitions
rather than bare integers. PR #601
Comment thread
DavidCPlatt marked this conversation as resolved.

0.8.1 (04-09-2025)
------------------
Expand Down
139 changes: 62 additions & 77 deletions pyvisa_py/gpib.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from pyvisa.constants import ResourceAttribute, StatusCode
from pyvisa.rname import GPIBInstr, GPIBIntfc, parse_resource_name

from . import prologix
from . import gpib_constants, prologix
from .common import LOGGER
from .sessions import Session, UnavailableSession, UnknownAttribute, VISARMSession

Expand Down Expand Up @@ -60,8 +60,9 @@ def __new__( # type: ignore[misc]
def list_resources() -> List[str]:
return [
"GPIB%d::%d::INSTR" % (board, pad)
if sad == 0
else "GPIB%d::%d::%d::INSTR" % (board, pad, sad - 0x60)
if sad == gpib_constants.sad.NO_SAD
else "GPIB%d::%d::%d::INSTR"
% (board, pad, sad - gpib_constants.sad.FIRST_SAD)
for board, pad, sad in _find_listeners()
]

Expand Down Expand Up @@ -172,7 +173,9 @@ def _find_listeners() -> Iterator[Tuple[int, int, int]]: # type: ignore[no-rede
if boardpad != i and gpib.listener(board, i):
yield board, i, j
elif boardpad != i:
for j in range(96, 126):
for j in range(
gpib_constants.sad.FIRST_SAD, gpib_constants.sad.LAST_SAD + 1
):
if gpib.listener(board, i, j):
yield board, i, j
except gpib.GpibError as e:
Expand Down Expand Up @@ -205,21 +208,17 @@ def _analyse_lines_value(value: int, line: int):

"""
if line == constants.VI_ATTR_GPIB_REN_STATE:
# REN bit valid = 0x10, REN bit value = 0x100
validity_mask = 0x10
value_mask = 0x100
Comment thread
MatthieuDartiailh marked this conversation as resolved.
validity_mask = gpib_constants.lines.ValidREN
value_mask = gpib_constants.lines.BusREN
elif line == constants.VI_ATTR_GPIB_ATN_STATE:
# ATN bit valid = 0x40, ATN bit value = 0x4000
validity_mask = 0x40
value_mask = 0x4000
validity_mask = gpib_constants.lines.ValidATN
value_mask = gpib_constants.lines.BusATN
elif line == constants.VI_ATTR_GPIB_NDAC_STATE:
# NDAC bit valid = 0x2, NDAC bit value = 0x200
validity_mask = 0x2
value_mask = 0x200
validity_mask = gpib_constants.lines.ValidNDAC
value_mask = gpib_constants.lines.BusNDAC
elif line == constants.VI_ATTR_GPIB_SRQ_STATE:
# SRQ bit valid = 0x20, SRQ bit value = 0x2000
validity_mask = 0x20
value_mask = 0x2000
validity_mask = gpib_constants.lines.ValidSRQ
value_mask = gpib_constants.lines.BusSRQ

if not value & validity_mask:
return constants.LineState.unknown, StatusCode.success
Expand Down Expand Up @@ -273,8 +272,8 @@ def convert_gpib_error(
Status code matching the GPIB error.

"""
# First check the imeout condition in the status byte
if status & 0x4000:
# First check the timeout condition in the status byte
if status & gpib_constants.status.TIMO:
return StatusCode.error_timeout
# All other cases are hard errors.
# In particular linux-gpib simply gives a string we could parse but that
Expand All @@ -284,26 +283,24 @@ def convert_gpib_error(
LOGGER.debug("Failed to %s.", operation, exc_info=error)
if not GPIB_CTYPES:
return StatusCode.error_system_error
if error.code == 1:
if error.code == gpib_constants.error.ECIC:
return StatusCode.error_not_cic
elif error.code == 2:
elif error.code == gpib_constants.error.ENOL:
return StatusCode.error_no_listeners
elif error.code == 4:
elif error.code == gpib_constants.error.EARG:
return StatusCode.error_invalid_mode
elif error.code == 11:
elif error.code == gpib_constants.error.ECAP:
return StatusCode.error_nonsupported_operation
elif error.code == 1:
return StatusCode.error_not_cic
elif error.code == 21:
elif error.code == gpib_constants.error.ELCK:
return StatusCode.error_resource_locked
else:
return StatusCode.error_system_error


def convert_gpib_status(status: int) -> StatusCode:
if status & 0x4000:
if status & gpib_constants.status.TIMO:
return StatusCode.error_timeout
elif status & 0x8000:
elif status & gpib_constants.status.ERR:
return StatusCode.error_system_error
else:
return StatusCode.success
Expand Down Expand Up @@ -350,15 +347,15 @@ def after_parsing(self) -> None:
minor = int(self.parsed.board)
# Secondary address (SAD) values should be in the range 96 to 126,
# 0 means the SAD is disabled.
sad = 0
timeout = 13
sad = gpib_constants.sad.NO_SAD.value
timeout = gpib_constants.timeout.T10s
send_eoi = 1
eos_mode = 0
self.interface = None
if isinstance(self.parsed, GPIBInstr):
pad = int(self.parsed.primary_address)
if self.parsed.secondary_address is not None:
sad = int(self.parsed.secondary_address) + 0x60
sad = int(self.parsed.secondary_address) + gpib_constants.sad.FIRST_SAD
# Used to talk to a specific resource
self.interface = Gpib(
name=minor,
Expand Down Expand Up @@ -389,9 +386,7 @@ def _get_timeout(
self, attribute: constants.ResourceAttribute
) -> Tuple[int, StatusCode]:
if self.interface:
# 0x3 is the hexadecimal reference to the IbaTMO (timeout) configuration
# option in linux-gpib.
gpib_timeout = self.interface.ask(3)
gpib_timeout = self.interface.ask(gpib_constants.ask.IbaTMO)
if gpib_timeout and gpib_timeout < len(TIMETABLE):
self.timeout = TIMETABLE[gpib_timeout]
else:
Expand Down Expand Up @@ -466,8 +461,7 @@ def read(self, count: int) -> Tuple[bytes, StatusCode]:
# INTFC don't have an interface so use the controller
ifc = self.interface or self.controller

# END 0x2000
checker = lambda current: ifc.ibsta() & 0x2000 # noqa: E731
checker = lambda current: ifc.ibsta() & gpib_constants.status.END # noqa: E731

reader = lambda: ifc.read(count) # noqa: E731

Expand Down Expand Up @@ -536,19 +530,17 @@ def gpib_control_ren(self, mode: constants.RENLineOperation) -> StatusCode:
try:
if mode == constants.VI_GPIB_REN_DEASSERT_GTL:
# Send GTL command byte (cf linux-gpib documentation)
ifc.command(chr(1))
ifc.command(gpib_constants.command.IcGTL)
if mode in (
constants.VI_GPIB_REN_DEASSERT,
constants.VI_GPIB_REN_DEASSERT_GTL,
):
self.controller.remote_enable(0)

if mode == constants.VI_GPIB_REN_ASSERT_LLO:
# LLO
ifc.command(b"0x11")
ifc.command(gpib_constants.command.IcLLO)
elif mode == constants.VI_GPIB_REN_ADDRESS_GTL:
# GTL
ifc.command(b"0x1")
ifc.command(gpib_constants.command.IcGTL)
elif mode == constants.VI_GPIB_REN_ASSERT_ADDRESS_LLO:
pass
elif mode in (
Expand Down Expand Up @@ -598,15 +590,15 @@ def _get_attribute(self, attribute: ResourceAttribute) -> Tuple[Any, StatusCode]
ifc = self.interface or self.controller

if attribute == ResourceAttribute.gpib_primary_address:
# IbaPAD 0x1
return ifc.ask(1), StatusCode.success
return ifc.ask(gpib_constants.ask.IbaPAD), StatusCode.success

elif attribute == ResourceAttribute.gpib_secondary_address:
# IbaSAD 0x2
# Remove 0x60 because National Instruments.
_ = ifc.ask(2)
if ifc.ask(2):
return ifc.ask(2) - 96, StatusCode.success
_ = ifc.ask(gpib_constants.ask.IbaSAD)
if ifc.ask(gpib_constants.ask.IbaSAD):
return ifc.ask(
gpib_constants.ask.IbaSAD
) - gpib_constants.sad.FIRST_SAD, StatusCode.success
else:
return constants.VI_NO_SEC_ADDR, StatusCode.success

Expand All @@ -621,24 +613,22 @@ def _get_attribute(self, attribute: ResourceAttribute) -> Tuple[Any, StatusCode]
elif attribute == ResourceAttribute.send_end_enabled:
# Do not use IbaEndBitIsNormal 0x1a which relates to EOI on read()
# not write(). see issue #196
# IbcEOT 0x4
if ifc.ask(4):
if ifc.ask(gpib_constants.ask.IbaEOT):
return constants.VI_TRUE, StatusCode.success
else:
return constants.VI_FALSE, StatusCode.success

elif attribute == ResourceAttribute.interface_number:
# IbaBNA 0x200
return ifc.ask(512), StatusCode.success
return ifc.ask(gpib_constants.ask.IbaBNA), StatusCode.success

elif attribute == ResourceAttribute.interface_type:
return constants.InterfaceType.gpib, StatusCode.success

elif attribute == ResourceAttribute.termchar:
return ifc.ask(0x0F), StatusCode.success
return ifc.ask(gpib_constants.ask.IbaEOSchar), StatusCode.success

elif attribute == ResourceAttribute.termchar_enabled:
return ifc.ask(0x0C), StatusCode.success
return ifc.ask(gpib_constants.ask.IbaEOSrd), StatusCode.success

raise UnknownAttribute(attribute)

Expand Down Expand Up @@ -672,63 +662,61 @@ def _set_attribute(
ifc = self.interface or self.controller

if attribute == ResourceAttribute.gpib_readdress_enabled:
# IbcREADDR 0x6
# Setting has no effect in linux-gpib.
if isinstance(attribute_state, int):
ifc.config(6, attribute_state)
ifc.config(gpib_constants.config.IbcREADDR, attribute_state)
return StatusCode.success
else:
return StatusCode.error_nonsupported_attribute_state

elif attribute == ResourceAttribute.gpib_primary_address:
# IbcPAD 0x1
if isinstance(attribute_state, int) and 0 <= attribute_state <= 30:
ifc.config(1, attribute_state)
ifc.config(gpib_constants.config.IbcPAD, attribute_state)
return StatusCode.success
else:
return StatusCode.error_nonsupported_attribute_state

elif attribute == ResourceAttribute.gpib_secondary_address:
# IbcSAD 0x2
# Add 0x60 because National Instruments.
if isinstance(attribute_state, int) and 0 <= attribute_state <= 30:
if ifc.ask(2):
ifc.config(2, attribute_state + 96)
if ifc.ask(gpib_constants.ask.IbaSAD):
ifc.config(
gpib_constants.config.IbcSAD,
attribute_state + gpib_constants.sad.FIRST_SAD,
)
return StatusCode.success
else:
return StatusCode.error_nonsupported_attribute
else:
return StatusCode.error_nonsupported_attribute_state

elif attribute == ResourceAttribute.gpib_unadress_enable:
# IbcUnAddr 0x1b
try:
ifc.config(27, attribute_state)
ifc.config(gpib_constants.config.IbcUnAddr, attribute_state)
return StatusCode.success
except gpib.GpibError:
return StatusCode.error_nonsupported_attribute_state

elif attribute == ResourceAttribute.send_end_enabled:
# Do not use IbaEndBitIsNormal 0x1a which relates to EOI on read()
# not write(). see issue #196
# IbcEOT 0x4
if isinstance(attribute_state, int):
ifc.config(4, attribute_state)
ifc.config(gpib_constants.config.IbcEOT, attribute_state)
return StatusCode.success
else:
return StatusCode.error_nonsupported_attribute_state

elif attribute == ResourceAttribute.termchar:
if isinstance(attribute_state, int):
ifc.config(0x0F, attribute_state) ## IbcEOSchar
ifc.config(0x0E, 1) ## IbcEOScmp
ifc.config(gpib_constants.config.IbcEOSchar, attribute_state)
ifc.config(gpib_constants.config.IbcEOScmp, 1)
return StatusCode.success
else:
return StatusCode.error_nonsupported_attribute_state

elif attribute == ResourceAttribute.termchar_enabled:
if isinstance(attribute_state, int):
ifc.config(0x0C, attribute_state) ## IbcEOSrd
ifc.config(gpib_constants.config.IbcEOSrd, attribute_state)
return StatusCode.success
else:
return StatusCode.error_nonsupported_attribute_state
Expand All @@ -753,8 +741,9 @@ class GPIBSession(_GPIBCommon): # type: ignore[no-redef]
def list_resources() -> List[str]:
return [
"GPIB%d::%d::INSTR" % (board, pad)
if sad == 0
else "GPIB%d::%d::%d::INSTR" % (board, pad, sad - 0x60)
if sad == gpib_constants.sad.NO_SAD
else "GPIB%d::%d::%d::INSTR"
% (board, pad, sad - gpib_constants.sad.FIRST_SAD)
for board, pad, sad in _find_listeners()
]

Expand Down Expand Up @@ -843,13 +832,11 @@ def _get_attribute(
ifc = self.interface

if attribute == constants.VI_ATTR_GPIB_READDR_EN:
# IbaREADDR 0x6
# Setting has no effect in linux-gpib.
return ifc.ask(6), StatusCode.success
return ifc.ask(gpib_constants.ask.IbaREADDR), StatusCode.success

elif attribute == constants.VI_ATTR_GPIB_UNADDR_EN:
# IbaUnAddr 0x1b
if ifc.ask(27):
if ifc.ask(gpib_constants.ask.IbaUnAddr):
return constants.VI_TRUE, StatusCode.success
else:
return constants.VI_FALSE, StatusCode.success
Expand Down Expand Up @@ -883,18 +870,16 @@ def _set_attribute(
ifc = self.interface

if attribute == constants.VI_ATTR_GPIB_READDR_EN:
# IbcREADDR 0x6
# Setting has no effect in linux-gpib.
if isinstance(attribute_state, int):
ifc.config(6, attribute_state)
ifc.config(gpib_constants.config.IbcREADDR, attribute_state)
return StatusCode.success
else:
return StatusCode.error_nonsupported_attribute_state

elif attribute == constants.VI_ATTR_GPIB_UNADDR_EN:
# IbcUnAddr 0x1b
try:
ifc.config(27, attribute_state)
ifc.config(gpib_constants.config.IbcUnAddr, attribute_state)
return StatusCode.success
except gpib.GpibError:
return StatusCode.error_nonsupported_attribute_state
Expand Down Expand Up @@ -1054,7 +1039,7 @@ def _get_attribute(self, attribute: ResourceAttribute) -> Tuple[Any, StatusCode]

if attribute == constants.VI_ATTR_GPIB_CIC_STATE:
# ibsta CIC = 0x0020
if ifc.ibsta() & 0x0020:
if ifc.ibsta() & gpib_constants.status.CIC:
return constants.VI_TRUE, StatusCode.success
else:
return constants.VI_FALSE, StatusCode.success
Expand Down
Loading
Loading