diff --git a/CHANGES b/CHANGES index 505bc0f2..ae8e2d86 100644 --- a/CHANGES +++ b/CHANGES @@ -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 #600 0.8.1 (04-09-2025) ------------------ diff --git a/pyvisa_py/gpib.py b/pyvisa_py/gpib.py index 3c9b696b..9e32dd7a 100644 --- a/pyvisa_py/gpib.py +++ b/pyvisa_py/gpib.py @@ -19,6 +19,8 @@ from .common import LOGGER from .sessions import Session, UnavailableSession, UnknownAttribute, VISARMSession +from . import gpib_constants + # NOTE dummy implementation that is overwritten when a GPIB library is found # Allow to provide session listing even when no GPIB library is available. @@ -205,21 +207,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 + 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 @@ -273,8 +271,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 @@ -284,26 +282,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 == GPIBerror.ECIC: return StatusCode.error_not_cic - elif error.code == 2: + elif error.code == GPIBerror.ENOL: return StatusCode.error_no_listeners - elif error.code == 4: + elif error.code == GPIBerror.EARG: return StatusCode.error_invalid_mode - elif error.code == 11: + elif error.code == GPIBerror.ECAP: return StatusCode.error_nonsupported_operation - elif error.code == 1: - return StatusCode.error_not_cic - elif error.code == 21: + elif error.code == GPIBerror.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 @@ -351,7 +347,7 @@ def after_parsing(self) -> None: # Secondary address (SAD) values should be in the range 96 to 126, # 0 means the SAD is disabled. sad = 0 - timeout = 13 + timeout = gpib_constants.timeout.T10s send_eoi = 1 eos_mode = 0 self.interface = None @@ -389,9 +385,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: @@ -466,8 +460,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 @@ -536,7 +529,7 @@ 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, @@ -544,11 +537,9 @@ def gpib_control_ren(self, mode: constants.RENLineOperation) -> StatusCode: 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 ( @@ -598,15 +589,13 @@ 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) - 96, StatusCode.success else: return constants.VI_NO_SEC_ADDR, StatusCode.success @@ -621,24 +610,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) @@ -672,28 +659,25 @@ 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 + 96) return StatusCode.success else: return StatusCode.error_nonsupported_attribute @@ -701,9 +685,8 @@ def _set_attribute( 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 @@ -711,24 +694,23 @@ def _set_attribute( 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 @@ -843,13 +825,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 @@ -883,18 +863,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(GPIBConfig.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(GPIBConfig.IbcUnAddr, attribute_state) return StatusCode.success except gpib.GpibError: return StatusCode.error_nonsupported_attribute_state @@ -1054,7 +1032,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 diff --git a/pyvisa_py/gpib_constants.py b/pyvisa_py/gpib_constants.py new file mode 100644 index 00000000..2b4fea6c --- /dev/null +++ b/pyvisa_py/gpib_constants.py @@ -0,0 +1,176 @@ +# -*- coding: utf-8 -*- + +# Derived from gpib_ctypes/constants.py + +from enum import IntEnum + + +class timeout(IntEnum): + TNONE = 0 # infinite + T10us = 1 # 10 usec + T30us = 2 # 30 usec + T100us = 3 # 100 usec + T300us = 4 # 300 usec + T1ms = 5 # 1 msec + T3ms = 6 # 3 msec + T10ms = 7 # 10 msec + T30ms = 8 # 30 msec + T100ms = 9 # 100 msec + T300ms = 10 # 300 msec + T1s = 11 # 1 sec + T3s = 12 # 3 sec + T10s = 13 # 10 sec + T30s = 14 # 30 sec + T100s = 15 # 100 sec + T300s = 16 # 300 sec + T1000s = 17 # 1000 sec + + +class config(IntEnum): + IbcPAD = 0x1 + IbcSAD = 0x2 + IbcTMO = 0x3 + IbcEOT = 0x4 + IbcPPC = 0x5 # board only + IbcREADDR = 0x6 # device only + IbcAUTOPOLL = 0x7 # board only + IbcCICPROT = 0x8 # board only + IbcIRQ = 0x9 # board only + IbcSC = 0xA # board only + IbcSRE = 0xB # board only + IbcEOSrd = 0xC + IbcEOSwrt = 0xD + IbcEOScmp = 0xE + IbcEOSchar = 0xF + IbcPP2 = 0x10 # board only + IbcTIMING = 0x11 # board only + IbcDMA = 0x12 # board only + IbcReadAdjust = 0x13 + IbcWriteAdjust = 0x14 + IbcEventQueue = 0x15 # board only + IbcSPollBit = 0x16 # board only + IbcSpollBit = 0x16 # board only + IbcSendLLO = 0x17 # board only + IbcSPollTime = 0x18 # device only + IbcPPollTime = 0x19 # board only + IbcEndBitIsNormal = 0x1A + IbcUnAddr = 0x1B # device only + IbcHSCableLength = 0x1F # board only + IbcIst = 0x20 # board only + IbcRsv = 0x21 # board only + IbcBNA = 0x200 # device only + + +class ask(IntEnum): + IbaPAD = 0x1 + IbaSAD = 0x2 + IbaTMO = 0x3 + IbaEOT = 0x4 + IbaPPC = 0x5 # board only + IbaREADDR = 0x6 # device only + IbaAUTOPOLL = 0x7 # board only + IbaCICPROT = 0x8 # board only + IbaIRQ = 0x9 # board only + IbaSC = 0xA # board only + IbaSRE = 0xB # board only + IbaEOSrd = 0xC + IbaEOSwrt = 0xD + IbaEOScmp = 0xE + IbaEOSchar = 0xF + IbaPP2 = 0x10 # board only + IbaTIMING = 0x11 # board only + IbaDMA = 0x12 # board only + IbaReadAdjust = 0x13 + IbaWriteAdjust = 0x14 + IbaEventQueue = 0x15 # board only + IbaSPollBit = 0x16 # board only + IbaSpollBit = 0x16 # board only + IbaSendLLO = 0x17 # board only + IbaSPollTime = 0x18 # device only + IbaPPollTime = 0x19 # board only + IbaEndBitIsNormal = 0x1A + IbaUnAddr = 0x1B # device only + IbaHSCableLength = 0x1F # board only + IbaIst = 0x20 # board only + IbaRsv = 0x21 # board only + IbaBNA = 0x200 # device only + Iba7BitEOS = 0x1000 # board only, linux-gpib only + + +class status(IntEnum): + DCAS = 0x0001 # device clear state + DTAS = 0x0002 # device trigger state + LACS = 0x0004 # interface is Listener + TACS = 0x0008 # interface is Talker + ATN = 0x0010 # attention + CIC = 0x0020 # Controller-in-Charge + REM = 0x0040 # remote state + LOK = 0x0080 # lockout state + CMPL = 0x0100 # IO completed + EVENT = 0x0200 # DCAS, DTAS, or IFC occurred + SPOLL = 0x0400 # board serial-polled by busmaster + RQS = 0x0800 # device requesting service + SRQI = 0x1000 # SRQ is asserted + END = 0x2000 # EOI or EOS + TIMO = 0x4000 # timeout + ERR = 0x8000 # error + + +class lines(IntEnum): + ValidDAV = 0x1 # the BusDAV bit is valid + ValidNDAC = 0x2 # the BusNDAC bit is valid + ValidNRFD = 0x4 # the BusNRFD bit is valid + ValidIFC = 0x8 # the BusIFC bit is valid + ValidREN = 0x10 # the BusREN bit is valid + ValidSRQ = 0x20 # the BusSRQ bit is valid + ValidATN = 0x40 # the BusATN bit is valid + ValidEOI = 0x80 # the BusEOI bit is valid + BusDAV = 0x100 # DAV line is asserted + BusNDAC = 0x200 # NDAC line is asserted + BusNRFD = 0x400 # NRFD line is asserted + BusIFC = 0x800 # IFC line is asserted + BusREN = 0x1000 # REN line is asserted + BusSRQ = 0x2000 # SRQ line is asserted + BusATN = 0x4000 # ATN line is asserted + BusEOI = 0x8000 # EOI line is asserted + + +class sad(IntEnum): + NO_SAD = 0 + ALL_SAD = -1 + + +class stb(IntEnum): + IbStbRQS = 0x40 + IbStbESB = 0x20 + IbStbMAV = 0x10 + + +class command(IntEnum): + IcGTL = 1 # go to local + IcLLO = 3 # local lock out + + +class error(IntEnum): + EDVR = 0 # system error + ECIC = 1 # not CIC + ENOL = 2 # no listener + EADR = 3 # CIC and not addressed before IO + EARG = 4 # bad argument to function call + ESAC = 5 # not SAC + EABO = 6 # IO aborted + ENEB = 7 # GPIB board offline + EDMA = 8 # DMA hardware error + EOIP = 10 # previous IO still in progress + ECAP = 11 # not capable + EFSO = 12 # file system operation error + EBUS = 14 # bus error + ESTB = 15 # lost serial poll bytes + ESRQ = 16 # SRQ stuck on + ETAB = 20 # table overflow + ELCK = 21 # interface locked + EARM = 22 # failed to rearm + EHDL = 23 # invalid handle + EWIP = 26 # previous wait still in progress + ERST = 27 # event notification cancelled due to reset + EPWR = 28 # interface lost power