14
14
15
15
import visa
16
16
17
+ from .errors import NotSupportedError
17
18
from .driver import Driver
18
19
from .log import LOGGER
19
20
from .processors import ParseProcessor
@@ -39,10 +40,6 @@ def get_resource_manager():
39
40
return _resource_manager
40
41
41
42
42
- class NotSupported :
43
- pass
44
-
45
-
46
43
class MessageBasedDriver (Driver ):
47
44
"""Base class for message based drivers using PyVISA as underlying library.
48
45
@@ -72,39 +69,45 @@ class MessageBasedDriver(Driver):
72
69
__resource_manager = None
73
70
74
71
@classmethod
75
- def _get_defaults_kwargs (cls , instrument_type , ** user_kwargs ):
72
+ def _get_defaults_kwargs (cls , instrument_type , resource_type , ** user_kwargs ):
76
73
"""Compute the default keyword arguments combining:
77
- - common keyword arguments.
78
- - instrument_type keyword arguments.
79
74
- 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)
80
81
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
82
86
83
87
:rtype: dict
84
88
"""
85
89
86
90
if cls .DEFAULTS_KWARGS :
87
- maps = [user_kwargs ]
88
91
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 []
94
93
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 )
97
103
98
- if len (maps ) == 1 :
99
- return user_kwargs
100
- else :
101
- return ChainMap (* maps )
104
+ return dict (ChainMap (* maps ))
102
105
else :
103
106
return user_kwargs
104
107
105
108
@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.
108
111
109
112
A connected USBTMC instrument with the specified serial_number, manufacturer_id,
110
113
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):
125
128
:param model_code: The unique identification number of the product.
126
129
:param name: Unique name given within Lantz to the instrument for logging purposes.
127
130
Defaults to one generated based on the class name if not provided.
131
+ :param board: USB Board to use
128
132
:param kwargs: keyword arguments passed to the Resource constructor on initialize.
129
133
130
134
:rtype: MessageBasedDriver
131
135
"""
132
136
133
137
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 )
135
139
try :
136
140
resources = get_resource_manager ().list_resources (query )
137
141
except :
@@ -146,10 +150,59 @@ def from_usbtmc(self, serial_number=None, name=None, **kwargs):
146
150
147
151
resource_name = resources [0 ]
148
152
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 )
150
154
151
155
return cls (resource_name , name )
152
156
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
+
153
206
@classmethod
154
207
def from_serial_port (cls , port , name = None , ** kwargs ):
155
208
"""Return a Driver with an underlying ASRL (Serial) Instrument resource.
@@ -178,6 +231,20 @@ def from_hostname(cls, hostname, name=None, **kwargs):
178
231
resource_name = 'TCPIP::%s::INSTR' % hostname
179
232
return cls (resource_name , name , ** kwargs )
180
233
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
+
181
248
@classmethod
182
249
def from_gpib_address (cls , address , name = None , ** kwargs ):
183
250
"""Return a Driver with an underlying GPIB Instrument resource.
@@ -201,13 +268,9 @@ def __init__(self, resource_name, name=None, **kwargs):
201
268
:param kwargs: keyword arguments passed to the resource during initialization.
202
269
"""
203
270
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 :
211
274
raise ValueError ('The resource name is invalid' )
212
275
213
276
super ().__init__ (name = name )
@@ -221,17 +284,24 @@ def __init__(self, resource_name, name=None, **kwargs):
221
284
222
285
#: keyword arguments passed to the resource during initialization.
223
286
#: :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 )
225
290
226
291
# The resource will be created when the driver is initialized.
227
292
#: :type: pyvisa.resources.MessageBasedResource
228
293
self .resource = None
229
294
295
+ self .log_debug ('Using MessageBasedDriver for {}' , self .resource_name )
296
+
230
297
def initialize (self ):
231
298
super ().initialize ()
299
+ self .log_debug ('Opening resource {}' , self .resource_name )
300
+ self .log_debug ('Setting {}' , list (self .resource_kwargs .items ()))
232
301
self .resource = get_resource_manager ().open_resource (self .resource_name , ** self .resource_kwargs )
233
302
234
303
def finalize (self ):
304
+ self .log_debug ('Closing resource {}' , self .resource_name )
235
305
self .resource .close ()
236
306
super ().finalize ()
237
307
@@ -276,7 +346,8 @@ def write(self, command, termination=None, encoding=None):
276
346
:return: number of bytes sent.
277
347
278
348
"""
279
- return self .resource .write (command )
349
+ self .log_debug ('Writing {!r}' , command )
350
+ return self .resource .write (command , termination , encoding )
280
351
281
352
def read (self , termination = None , encoding = None ):
282
353
"""Receive string from instrument.
@@ -286,4 +357,6 @@ def read(self, termination=None, encoding=None):
286
357
:param encoding: encoding to transform bytes to string (overrides class default)
287
358
:return: string encoded from received bytes
288
359
"""
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