Skip to content

Commit ae673e4

Browse files
committed
Improved MessageBasedDriver
- Getting the default kwargs now accepts the resource class in addition to the interface type Clean up this code. - Added class methods to get TCPIP SOCKET and USB RAW. - Improved logging.
1 parent ed17c8a commit ae673e4

File tree

1 file changed

+107
-34
lines changed

1 file changed

+107
-34
lines changed

lantz/messagebased.py

+107-34
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import visa
1616

17+
from .errors import NotSupportedError
1718
from .driver import Driver
1819
from .log import LOGGER
1920
from .processors import ParseProcessor
@@ -39,10 +40,6 @@ def get_resource_manager():
3940
return _resource_manager
4041

4142

42-
class NotSupported:
43-
pass
44-
45-
4643
class MessageBasedDriver(Driver):
4744
"""Base class for message based drivers using PyVISA as underlying library.
4845
@@ -72,39 +69,45 @@ class MessageBasedDriver(Driver):
7269
__resource_manager = None
7370

7471
@classmethod
75-
def _get_defaults_kwargs(cls, instrument_type, **user_kwargs):
72+
def _get_defaults_kwargs(cls, instrument_type, resource_type, **user_kwargs):
7673
"""Compute the default keyword arguments combining:
77-
- common keyword arguments.
78-
- instrument_type keyword arguments.
7974
- user provided keyword arguments.
75+
- (instrument_type, resource_type) keyword arguments.
76+
- instrument_type keyword arguments.
77+
- resource_type keyword arguments.
78+
- common keyword arguments.
79+
80+
(the first ones have precedence)
8081
81-
(the lower ones have precedence)
82+
:param instrument_type: ASRL, USB, TCPIP, GPIB
83+
:type instrument_type: str
84+
:param resource_type: INSTR, SOCKET, RAW
85+
:type resource_type: str
8286
8387
:rtype: dict
8488
"""
8589

8690
if cls.DEFAULTS_KWARGS:
87-
maps = [user_kwargs]
8891

89-
specific = cls.DEFAULTS_KWARGS.get(instrument_type, None)
90-
if specific is NotSupported:
91-
raise ValueError
92-
elif specific:
93-
maps.append(specific)
92+
maps = [user_kwargs] if user_kwargs else []
9493

95-
if 'common' in cls.DEFAULTS_KWARGS:
96-
maps.append(cls['common'])
94+
for key in ((instrument_type, resource_type), instrument_type, resource_type, 'COMMON'):
95+
if key not in cls.DEFAULTS_KWARGS:
96+
continue
97+
value = cls.DEFAULTS_KWARGS[key]
98+
if value is None:
99+
raise NotSupportedError('An %s instrument is not supported by the driver %s',
100+
key, cls.__name__)
101+
if value:
102+
maps.append(value)
97103

98-
if len(maps) == 1:
99-
return user_kwargs
100-
else:
101-
return ChainMap(*maps)
104+
return dict(ChainMap(*maps))
102105
else:
103106
return user_kwargs
104107

105108
@classmethod
106-
def from_usbtmc(cls, serial_number=None, manufacturer_id=None, model_code=None, name=None, board=0, **kwargs):
107-
"""Return a Driver with an underlying USB Instrument resource.
109+
def _from_usb(cls, resource_type='INSTR', serial_number=None, manufacturer_id=None, model_code=None, name=None, board=0, **kwargs):
110+
"""Return a Driver with an underlying USB resource.
108111
109112
A connected USBTMC instrument with the specified serial_number, manufacturer_id,
110113
and model_code is returned. If any of these is missing, the first USBTMC driver
@@ -125,13 +128,14 @@ def from_usbtmc(self, serial_number=None, name=None, **kwargs):
125128
:param model_code: The unique identification number of the product.
126129
:param name: Unique name given within Lantz to the instrument for logging purposes.
127130
Defaults to one generated based on the class name if not provided.
131+
:param board: USB Board to use
128132
:param kwargs: keyword arguments passed to the Resource constructor on initialize.
129133
130134
:rtype: MessageBasedDriver
131135
"""
132136

133137
if serial_number is None or manufacturer_id is None or model_code is None:
134-
query = 'USB%d::%s::%s::%s::INSTR' % (board, manufacturer_id or '?*', model_code or '?*', serial_number or '?*')
138+
query = 'USB%d::%s::%s::%s::%s' % (board, manufacturer_id or '?*', model_code or '?*', serial_number or '?*', resource_type)
135139
try:
136140
resources = get_resource_manager().list_resources(query)
137141
except:
@@ -146,10 +150,59 @@ def from_usbtmc(self, serial_number=None, name=None, **kwargs):
146150

147151
resource_name = resources[0]
148152
else:
149-
resource_name = 'USB%d::%s::%s::%s::INSTR' % (board, manufacturer_id, model_code, serial_number)
153+
resource_name = 'USB%d::%s::%s::%s::%s' % (board, manufacturer_id, model_code, serial_number, resource_type)
150154

151155
return cls(resource_name, name)
152156

157+
@classmethod
158+
def from_usbtmc(cls, serial_number=None, manufacturer_id=None, model_code=None, name=None, board=0, **kwargs):
159+
"""Return a Driver with an underlying USB Instrument resource.
160+
161+
A connected USBTMC instrument with the specified serial_number, manufacturer_id,
162+
and model_code is returned. If any of these is missing, the first USBTMC driver
163+
matching any of the provided values is returned.
164+
165+
Override this method to specify the manufacturer id and/or the model code::
166+
167+
class RigolDS1052E(MessageBasedDriver):
168+
169+
@classmethod
170+
def from_usbtmc(self, serial_number=None, name=None, **kwargs):
171+
172+
return super().from_usbtmc(serial_number, '0x1AB1', '0x0588', name, **kwargs)
173+
174+
175+
:param serial_number: The serial number of the instrument.
176+
:param manufacturer_id: The unique identification number of the manufacturer.
177+
:param model_code: The unique identification number of the product.
178+
:param name: Unique name given within Lantz to the instrument for logging purposes.
179+
Defaults to one generated based on the class name if not provided.
180+
:param board: USB Board to use
181+
:param kwargs: keyword arguments passed to the Resource constructor on initialize.
182+
183+
:rtype: MessageBasedDriver
184+
"""
185+
186+
return cls._from_usb('INSTR', serial_number, manufacturer_id, model_code, name, board, **kwargs)
187+
188+
189+
@classmethod
190+
def from_usbtmc_raw(cls, serial_number=None, manufacturer_id=None, model_code=None, name=None, board=0, **kwargs):
191+
"""Return a Driver with an underlying USB RAW resource.
192+
193+
:param serial_number: The serial number of the instrument.
194+
:param manufacturer_id: The unique identification number of the manufacturer.
195+
:param model_code: The unique identification number of the product.
196+
:param name: Unique name given within Lantz to the instrument for logging purposes.
197+
Defaults to one generated based on the class name if not provided.
198+
:param board: USB Board to use
199+
:param kwargs: keyword arguments passed to the Resource constructor on initialize.
200+
201+
:rtype: MessageBasedDriver
202+
"""
203+
204+
return cls._from_usb('RAW', serial_number, manufacturer_id, model_code, name, board, **kwargs)
205+
153206
@classmethod
154207
def from_serial_port(cls, port, name=None, **kwargs):
155208
"""Return a Driver with an underlying ASRL (Serial) Instrument resource.
@@ -178,6 +231,20 @@ def from_hostname(cls, hostname, name=None, **kwargs):
178231
resource_name = 'TCPIP::%s::INSTR' % hostname
179232
return cls(resource_name, name, **kwargs)
180233

234+
@classmethod
235+
def from_hostname_socket(cls, hostname, name=None, **kwargs):
236+
"""Return a Driver with an underlying TCP Socket resource.
237+
238+
:param port: The ip address or hostname of the instrument.
239+
:param name: Unique name given within Lantz to the instrument for logging purposes.
240+
Defaults to one generated based on the class name if not provided.
241+
:param kwargs: keyword arguments passed to the Resource constructor on initialize.
242+
243+
:rtype: MessageBasedDriver
244+
"""
245+
resource_name = 'TCPIP::%s::INSTR' % hostname
246+
return cls(resource_name, name, **kwargs)
247+
181248
@classmethod
182249
def from_gpib_address(cls, address, name=None, **kwargs):
183250
"""Return a Driver with an underlying GPIB Instrument resource.
@@ -201,13 +268,9 @@ def __init__(self, resource_name, name=None, **kwargs):
201268
:param kwargs: keyword arguments passed to the resource during initialization.
202269
"""
203270

204-
# Add the DEFAULT INTERFACE TYPE prefix if the resource name
205-
# does not (naively) look like a valid one.
206-
if resource_name.startswith('ASRL'):
207-
interface_type = 'ASRL'
208-
elif '::' not in resource_name:
209-
interface_type = resource_name.split('::')[0]
210-
else:
271+
try:
272+
resource_info = get_resource_manager().resource_info(resource_name)
273+
except visa.VisaIOError:
211274
raise ValueError('The resource name is invalid')
212275

213276
super().__init__(name=name)
@@ -221,17 +284,24 @@ def __init__(self, resource_name, name=None, **kwargs):
221284

222285
#: keyword arguments passed to the resource during initialization.
223286
#: :type: dict
224-
self.resource_kwargs = self._get_defaults_kwargs(interface_type, **kwargs)
287+
self.resource_kwargs = self._get_defaults_kwargs(resource_info.interface_type.name.upper(),
288+
resource_info.resource_class,
289+
**kwargs)
225290

226291
# The resource will be created when the driver is initialized.
227292
#: :type: pyvisa.resources.MessageBasedResource
228293
self.resource = None
229294

295+
self.log_debug('Using MessageBasedDriver for {}', self.resource_name)
296+
230297
def initialize(self):
231298
super().initialize()
299+
self.log_debug('Opening resource {}', self.resource_name)
300+
self.log_debug('Setting {}', list(self.resource_kwargs.items()))
232301
self.resource = get_resource_manager().open_resource(self.resource_name, **self.resource_kwargs)
233302

234303
def finalize(self):
304+
self.log_debug('Closing resource {}', self.resource_name)
235305
self.resource.close()
236306
super().finalize()
237307

@@ -276,7 +346,8 @@ def write(self, command, termination=None, encoding=None):
276346
:return: number of bytes sent.
277347
278348
"""
279-
return self.resource.write(command)
349+
self.log_debug('Writing {!r}', command)
350+
return self.resource.write(command, termination, encoding)
280351

281352
def read(self, termination=None, encoding=None):
282353
"""Receive string from instrument.
@@ -286,4 +357,6 @@ def read(self, termination=None, encoding=None):
286357
:param encoding: encoding to transform bytes to string (overrides class default)
287358
:return: string encoded from received bytes
288359
"""
289-
return self.resource.read(termination, encoding)
360+
ret = self.resource.read(termination, encoding)
361+
self.log_debug('Read {!r}', ret)
362+
return ret

0 commit comments

Comments
 (0)