qcodes 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. qcodes/__init__.py +65 -0
  2. qcodes/actions.py +207 -0
  3. qcodes/config/__init__.py +2 -0
  4. qcodes/config/config.py +396 -0
  5. qcodes/config/qcodesrc.json +11 -0
  6. qcodes/config/qcodesrc_schema.json +55 -0
  7. qcodes/data/__init__.py +0 -0
  8. qcodes/data/data_array.py +520 -0
  9. qcodes/data/data_set.py +803 -0
  10. qcodes/data/format.py +309 -0
  11. qcodes/data/gnuplot_format.py +366 -0
  12. qcodes/data/hdf5_format.py +385 -0
  13. qcodes/data/io.py +266 -0
  14. qcodes/data/location.py +166 -0
  15. qcodes/data/manager.py +163 -0
  16. qcodes/instrument/__init__.py +0 -0
  17. qcodes/instrument/base.py +630 -0
  18. qcodes/instrument/function.py +134 -0
  19. qcodes/instrument/ip.py +236 -0
  20. qcodes/instrument/metaclass.py +45 -0
  21. qcodes/instrument/mock.py +278 -0
  22. qcodes/instrument/parameter.py +1001 -0
  23. qcodes/instrument/remote.py +529 -0
  24. qcodes/instrument/server.py +189 -0
  25. qcodes/instrument/sweep_values.py +286 -0
  26. qcodes/instrument/visa.py +198 -0
  27. qcodes/instrument_drivers/AlazarTech/ATS.py +1001 -0
  28. qcodes/instrument_drivers/AlazarTech/ATS9870.py +266 -0
  29. qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py +143 -0
  30. qcodes/instrument_drivers/AlazarTech/__init__.py +0 -0
  31. qcodes/instrument_drivers/QuTech/IVVI.py +390 -0
  32. qcodes/instrument_drivers/QuTech/__init__.py +0 -0
  33. qcodes/instrument_drivers/__init__.py +0 -0
  34. qcodes/instrument_drivers/agilent/Agilent_34400A.py +155 -0
  35. qcodes/instrument_drivers/agilent/E8527D.py +79 -0
  36. qcodes/instrument_drivers/agilent/HP33210A.py +104 -0
  37. qcodes/instrument_drivers/agilent/__init__.py +0 -0
  38. qcodes/instrument_drivers/agilent/test_suite.py +96 -0
  39. qcodes/instrument_drivers/ithaco/Ithaco_1211.py +110 -0
  40. qcodes/instrument_drivers/ithaco/__init__.py +0 -0
  41. qcodes/instrument_drivers/oxford/__init__.py +0 -0
  42. qcodes/instrument_drivers/oxford/mercuryiPS.py +358 -0
  43. qcodes/instrument_drivers/oxford/triton.py +227 -0
  44. qcodes/instrument_drivers/rigol/DG4000.py +591 -0
  45. qcodes/instrument_drivers/rigol/__init__.py +0 -0
  46. qcodes/instrument_drivers/rohde_schwarz/SGS100A.py +99 -0
  47. qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +154 -0
  48. qcodes/instrument_drivers/rohde_schwarz/__init__.py +0 -0
  49. qcodes/instrument_drivers/signal_hound/USB_SA124B.py +567 -0
  50. qcodes/instrument_drivers/signal_hound/__init__.py +0 -0
  51. qcodes/instrument_drivers/stanford_research/SR560.py +116 -0
  52. qcodes/instrument_drivers/stanford_research/SR830.py +314 -0
  53. qcodes/instrument_drivers/stanford_research/SR865.py +305 -0
  54. qcodes/instrument_drivers/stanford_research/__init__.py +0 -0
  55. qcodes/instrument_drivers/tektronix/AWG5014.py +1228 -0
  56. qcodes/instrument_drivers/tektronix/AWG520.py +659 -0
  57. qcodes/instrument_drivers/tektronix/Keithley_2000.py +195 -0
  58. qcodes/instrument_drivers/tektronix/Keithley_2600.py +83 -0
  59. qcodes/instrument_drivers/tektronix/Keithley_2700.py +395 -0
  60. qcodes/instrument_drivers/tektronix/__init__.py +0 -0
  61. qcodes/instrument_drivers/test.py +94 -0
  62. qcodes/instrument_drivers/weinschel/Weinschel_8320.py +21 -0
  63. qcodes/instrument_drivers/weinschel/__init__.py +0 -0
  64. qcodes/instrument_drivers/weinschel/test_suite.py +27 -0
  65. qcodes/loops.py +1009 -0
  66. qcodes/measure.py +155 -0
  67. qcodes/plots/__init__.py +0 -0
  68. qcodes/plots/base.py +280 -0
  69. qcodes/plots/colors.py +135 -0
  70. qcodes/plots/pyqtgraph.py +384 -0
  71. qcodes/plots/qcmatplotlib.py +206 -0
  72. qcodes/process/__init__.py +0 -0
  73. qcodes/process/helpers.py +60 -0
  74. qcodes/process/qcodes_process.py +72 -0
  75. qcodes/process/server.py +395 -0
  76. qcodes/process/stream_queue.py +152 -0
  77. qcodes/station.py +178 -0
  78. qcodes/test.py +119 -0
  79. qcodes/tests/__init__.py +0 -0
  80. qcodes/tests/common.py +7 -0
  81. qcodes/tests/data_mocks.py +173 -0
  82. qcodes/tests/instrument_mocks.py +327 -0
  83. qcodes/tests/py35_syntax.py +13 -0
  84. qcodes/tests/test_combined_par.py +156 -0
  85. qcodes/tests/test_command.py +120 -0
  86. qcodes/tests/test_config.py +222 -0
  87. qcodes/tests/test_data.py +668 -0
  88. qcodes/tests/test_deferred_operations.py +105 -0
  89. qcodes/tests/test_driver_testcase.py +58 -0
  90. qcodes/tests/test_format.py +414 -0
  91. qcodes/tests/test_hdf5formatter.py +333 -0
  92. qcodes/tests/test_helpers.py +630 -0
  93. qcodes/tests/test_instrument.py +981 -0
  94. qcodes/tests/test_instrument_server.py +156 -0
  95. qcodes/tests/test_json.py +48 -0
  96. qcodes/tests/test_location_provider.py +109 -0
  97. qcodes/tests/test_loop.py +825 -0
  98. qcodes/tests/test_measure.py +49 -0
  99. qcodes/tests/test_metadata.py +49 -0
  100. qcodes/tests/test_multiprocessing.py +471 -0
  101. qcodes/tests/test_nested_attrs.py +100 -0
  102. qcodes/tests/test_parameter.py +151 -0
  103. qcodes/tests/test_plots.py +44 -0
  104. qcodes/tests/test_sweep_values.py +159 -0
  105. qcodes/tests/test_validators.py +482 -0
  106. qcodes/tests/test_visa.py +184 -0
  107. qcodes/utils/__init__.py +0 -0
  108. qcodes/utils/command.py +176 -0
  109. qcodes/utils/deferred_operations.py +276 -0
  110. qcodes/utils/helpers.py +417 -0
  111. qcodes/utils/metadata.py +42 -0
  112. qcodes/utils/nested_attrs.py +177 -0
  113. qcodes/utils/reload_code.py +98 -0
  114. qcodes/utils/threading.py +72 -0
  115. qcodes/utils/timing.py +89 -0
  116. qcodes/utils/validators.py +393 -0
  117. qcodes/version.py +1 -0
  118. qcodes/widgets/__init__.py +0 -0
  119. qcodes/widgets/display.py +47 -0
  120. qcodes/widgets/widgets.css +77 -0
  121. qcodes/widgets/widgets.js +237 -0
  122. qcodes/widgets/widgets.py +215 -0
  123. qcodes-0.1.0.dist-info/DESCRIPTION.rst +165 -0
  124. qcodes-0.1.0.dist-info/METADATA +194 -0
  125. qcodes-0.1.0.dist-info/RECORD +128 -0
  126. qcodes-0.1.0.dist-info/WHEEL +5 -0
  127. qcodes-0.1.0.dist-info/metadata.json +1 -0
  128. qcodes-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,630 @@
1
+ """Instrument base class."""
2
+ import logging
3
+ import time
4
+ import warnings
5
+ import weakref
6
+
7
+ from qcodes.utils.metadata import Metadatable
8
+ from qcodes.utils.helpers import DelegateAttributes, strip_attrs, full_class
9
+ from qcodes.utils.nested_attrs import NestedAttrAccess
10
+ from qcodes.utils.validators import Anything
11
+ from .parameter import StandardParameter
12
+ from .function import Function
13
+ from .metaclass import InstrumentMetaclass
14
+
15
+
16
+ class Instrument(Metadatable, DelegateAttributes, NestedAttrAccess,
17
+ metaclass=InstrumentMetaclass):
18
+
19
+ """
20
+ Base class for all QCodes instruments.
21
+
22
+ Args:
23
+ name (str): an identifier for this instrument, particularly for
24
+ attaching it to a Station.
25
+
26
+ server_name (Optional[str]): If not ``None``, this instrument starts a
27
+ separate server process (or connects to one, if one already exists
28
+ with the same name) and all hardware calls are made there.
29
+
30
+ Default '', then we call classmethod ``default_server_name``,
31
+ passing in all the constructor kwargs, to determine the name.
32
+ If not overridden, this just gives 'Instruments'.
33
+
34
+ **see subclass constructors below for more on ``server_name``**
35
+
36
+ Use None to operate without a server - but then this Instrument
37
+ will not work with qcodes Loops or other multiprocess procedures.
38
+
39
+ If a server is used, the ``Instrument`` you asked for is
40
+ instantiated on the server, and the object you get in the main
41
+ process is actually a ``RemoteInstrument`` that proxies all method
42
+ calls, ``Parameters``, and ``Functions`` to the server.
43
+
44
+ The metaclass ``InstrumentMetaclass`` handles making either the
45
+ requested class or its RemoteInstrument proxy.
46
+
47
+ metadata (Optional[Dict]): additional static metadata to add to this
48
+ instrument's JSON snapshot.
49
+
50
+
51
+ Any unpicklable objects that are inputs to the constructor must be set
52
+ on server initialization, and must be shared between all instruments
53
+ that reside on the same server. To make this happen, set the
54
+ ``shared_kwargs`` class attribute to a tuple of kwarg names that should
55
+ be treated this way.
56
+
57
+ It is an error to initialize two instruments on the same server with
58
+ different keys or values for ``shared_kwargs``, unless the later
59
+ instruments have NO ``shared_kwargs`` at all.
60
+
61
+ subclass constructors: ``server_name`` and any ``shared_kwargs`` must be
62
+ available as kwargs and kwargs ONLY (not positional) in all subclasses,
63
+ and not modified in the inheritance chain. This is because we need to
64
+ create the server before instantiating the actual instrument. The easiest
65
+ way to manage this is to accept ``**kwargs`` in your subclass and pass them
66
+ on to ``super().__init()``.
67
+
68
+ Attributes:
69
+ name (str): an identifier for this instrument, particularly for
70
+ attaching it to a Station.
71
+
72
+ parameters (Dict[Parameter]): All the parameters supported by this
73
+ instrument. Usually populated via ``add_parameter``
74
+
75
+ functions (Dict[Function]): All the functions supported by this
76
+ instrument. Usually populated via ``add_function``
77
+ """
78
+
79
+ shared_kwargs = ()
80
+
81
+ _all_instruments = {}
82
+
83
+ def __init__(self, name, server_name=None, **kwargs):
84
+ self._t0 = time.time()
85
+ super().__init__(**kwargs)
86
+ self.parameters = {}
87
+ self.functions = {}
88
+
89
+ self.name = str(name)
90
+
91
+ self.add_parameter('IDN', get_cmd=self.get_idn,
92
+ vals=Anything())
93
+
94
+ self._meta_attrs = ['name']
95
+
96
+ self._no_proxy_methods = {'__getstate__'}
97
+
98
+ def get_idn(self):
99
+ """
100
+ Parse a standard VISA '*IDN?' response into an ID dict.
101
+
102
+ Even though this is the VISA standard, it applies to various other
103
+ types as well, such as IPInstruments, so it is included here in the
104
+ Instrument base class.
105
+
106
+ Override this if your instrument does not support '*IDN?' or
107
+ returns a nonstandard IDN string. This string is supposed to be a
108
+ comma-separated list of vendor, model, serial, and firmware, but
109
+ semicolon and colon are also common separators so we accept them here
110
+ as well.
111
+
112
+ Returns:
113
+ A dict containing vendor, model, serial, and firmware.
114
+ """
115
+ try:
116
+ idstr = '' # in case self.ask fails
117
+ idstr = self.ask('*IDN?')
118
+ # form is supposed to be comma-separated, but we've seen
119
+ # other separators occasionally
120
+ for separator in ',;:':
121
+ # split into no more than 4 parts, so we don't lose info
122
+ idparts = [p.strip() for p in idstr.split(separator, 3)]
123
+ if len(idparts) > 1:
124
+ break
125
+ # in case parts at the end are missing, fill in None
126
+ if len(idparts) < 4:
127
+ idparts += [None] * (4 - len(idparts))
128
+ except:
129
+ logging.warn('Error getting or interpreting *IDN?: ' + repr(idstr))
130
+ idparts = [None, None, None, None]
131
+
132
+ # some strings include the word 'model' at the front of model
133
+ if str(idparts[1]).lower().startswith('model'):
134
+ idparts[1] = str(idparts[1])[5:].strip()
135
+
136
+ return dict(zip(('vendor', 'model', 'serial', 'firmware'), idparts))
137
+
138
+ @classmethod
139
+ def default_server_name(cls, **kwargs):
140
+ """
141
+ Generate a default name for the server to host this instrument.
142
+
143
+ Args:
144
+ **kwargs: the constructor kwargs, used if necessary to choose a
145
+ name.
146
+
147
+ Returns:
148
+ str: The default server name for the specific instrument instance
149
+ we are constructing.
150
+ """
151
+ return 'Instruments'
152
+
153
+ def connect_message(self, idn_param='IDN', begin_time=None):
154
+ """
155
+ Print a standard message on initial connection to an instrument.
156
+
157
+ Args:
158
+ idn_param (str): name of parameter that returns ID dict.
159
+ Default 'IDN'.
160
+ begin_time (number): time.time() when init started.
161
+ Default is self._t0, set at start of Instrument.__init__.
162
+ """
163
+ # start with an empty dict, just in case an instrument doesn't
164
+ # heed our request to return all 4 fields.
165
+ idn = {'vendor': None, 'model': None,
166
+ 'serial': None, 'firmware': None}
167
+ idn.update(self.get(idn_param))
168
+ t = time.time() - (begin_time or self._t0)
169
+
170
+ con_msg = ('Connected to: {vendor} {model} '
171
+ '(serial:{serial}, firmware:{firmware}) '
172
+ 'in {t:.2f}s'.format(t=t, **idn))
173
+ print(con_msg)
174
+
175
+ def __repr__(self):
176
+ """Simplified repr giving just the class and name."""
177
+ return '<{}: {}>'.format(type(self).__name__, self.name)
178
+
179
+ def __del__(self):
180
+ """Close the instrument and remove its instance record."""
181
+ try:
182
+ wr = weakref.ref(self)
183
+ if wr in getattr(self, '_instances', {}):
184
+ self._instances.remove(wr)
185
+ self.close()
186
+ except:
187
+ pass
188
+
189
+ def close(self):
190
+ """
191
+ Irreversibly stop this instrument and free its resources.
192
+
193
+ Subclasses should override this if they have other specific
194
+ resources to close.
195
+ """
196
+ if hasattr(self, 'connection') and hasattr(self.connection, 'close'):
197
+ self.connection.close()
198
+
199
+ strip_attrs(self, whitelist=['name'])
200
+ self.remove_instance(self)
201
+
202
+ @classmethod
203
+ def record_instance(cls, instance):
204
+ """
205
+ Record (a weak ref to) an instance in a class's instance list.
206
+
207
+ Also records the instance in list of *all* instruments, and verifies
208
+ that there are no other instruments with the same name.
209
+
210
+ Args:
211
+ instance (Union[Instrument, RemoteInstrument]): Note: we *do not*
212
+ check that instance is actually an instance of ``cls``. This is
213
+ important, because a ``RemoteInstrument`` should function as an
214
+ instance of the instrument it proxies.
215
+
216
+ Raises:
217
+ KeyError: if another instance with the same name is already present
218
+ """
219
+ wr = weakref.ref(instance)
220
+ name = instance.name
221
+
222
+ # First insert this instrument in the record of *all* instruments
223
+ # making sure its name is unique
224
+ existing_wr = cls._all_instruments.get(name)
225
+ if existing_wr and existing_wr():
226
+ raise KeyError('Another instrument has the name: {}'.format(name))
227
+
228
+ cls._all_instruments[name] = wr
229
+
230
+ # Then add it to the record for this specific subclass, using ``_type``
231
+ # to make sure we're not recording it in a base class instance list
232
+ if getattr(cls, '_type', None) is not cls:
233
+ cls._type = cls
234
+ cls._instances = []
235
+ cls._instances.append(wr)
236
+
237
+ @classmethod
238
+ def instances(cls):
239
+ """
240
+ Get all currently defined instances of this instrument class.
241
+
242
+ You can use this to get the objects back if you lose track of them,
243
+ and it's also used by the test system to find objects to test against.
244
+
245
+ Note:
246
+ Will also include ``RemoteInstrument`` instances that proxy
247
+ instruments of this class.
248
+
249
+ Returns:
250
+ List[Union[Instrument, RemoteInstrument]]
251
+ """
252
+ if getattr(cls, '_type', None) is not cls:
253
+ # only instances of a superclass - we want instances of this
254
+ # exact class only
255
+ return []
256
+ return [wr() for wr in getattr(cls, '_instances', []) if wr()]
257
+
258
+ @classmethod
259
+ def remove_instance(cls, instance):
260
+ """
261
+ Remove a particular instance from the record.
262
+
263
+ Args:
264
+ instance (Union[Instrument, RemoteInstrument])
265
+ """
266
+ wr = weakref.ref(instance)
267
+ if wr in cls._instances:
268
+ cls._instances.remove(wr)
269
+
270
+ # remove from all_instruments too, but don't depend on the
271
+ # name to do it, in case name has changed or been deleted
272
+ all_ins = cls._all_instruments
273
+ for name, ref in list(all_ins.items()):
274
+ if ref is wr:
275
+ del all_ins[name]
276
+
277
+ @classmethod
278
+ def find_instrument(cls, name, instrument_class=None):
279
+ """
280
+ Find an existing instrument by name.
281
+
282
+ Args:
283
+ name (str)
284
+ instrument_class (Optional[class]): The type of instrument
285
+ you are looking for.
286
+
287
+ Returns:
288
+ Union[Instrument, RemoteInstrument]
289
+
290
+ Raises:
291
+ KeyError: if no instrument of that name was found, or if its
292
+ reference is invalid (dead).
293
+ TypeError: if a specific class was requested but a different
294
+ type was found
295
+ """
296
+ ins = cls._all_instruments[name]()
297
+
298
+ if ins is None:
299
+ del cls._all_instruments[name]
300
+ raise KeyError('Instrument {} has been removed'.format(name))
301
+
302
+ if instrument_class is not None:
303
+ if not isinstance(ins, instrument_class):
304
+ raise TypeError(
305
+ 'Instrument {} is {} but {} was requested'.format(
306
+ name, type(ins), instrument_class))
307
+
308
+ return ins
309
+
310
+ @classmethod
311
+ def find_component(cls, name_attr, instrument_class=None):
312
+ """
313
+ Find a component of an existing instrument by name and attribute.
314
+
315
+ Args:
316
+ name_attr (str): A string in nested attribute format:
317
+ <name>.<attribute>[.<subattribute>] and so on.
318
+ For example, <attribute> can be a parameter name,
319
+ or a method name.
320
+ instrument_class (Optional[class]): The type of instrument
321
+ you are looking for this component within.
322
+
323
+ Returns:
324
+ Any: The component requested.
325
+ """
326
+
327
+ if '.' in name_attr:
328
+ name, attr = name_attr.split('.', 1)
329
+ ins = cls.find_instrument(name, instrument_class=instrument_class)
330
+ return ins.getattr(attr)
331
+
332
+ else:
333
+ # allow find_component to return the whole instrument,
334
+ # if no attribute was specified, for maximum generality.
335
+ return cls.find_instrument(name_attr,
336
+ instrument_class=instrument_class)
337
+
338
+ def add_parameter(self, name, parameter_class=StandardParameter,
339
+ **kwargs):
340
+ """
341
+ Bind one Parameter to this instrument.
342
+
343
+ Instrument subclasses can call this repeatedly in their ``__init__``
344
+ for every real parameter of the instrument.
345
+
346
+ In this sense, parameters are the state variables of the instrument,
347
+ anything the user can set and/or get
348
+
349
+ Args:
350
+ name (str): How the parameter will be stored within
351
+ ``instrument.parameters`` and also how you address it using the
352
+ shortcut methods: ``instrument.set(param_name, value)`` etc.
353
+
354
+ parameter_class (Optional[type]): You can construct the parameter
355
+ out of any class. Default ``StandardParameter``.
356
+
357
+ **kwargs: constructor arguments for ``parameter_class``.
358
+
359
+ Returns:
360
+ dict: attribute information. Only used if you add parameters
361
+ from the ``RemoteInstrument`` rather than at construction, to
362
+ properly construct the proxy for this parameter.
363
+
364
+ Raises:
365
+ KeyError: if this instrument already has a parameter with this
366
+ name.
367
+ """
368
+ if name in self.parameters:
369
+ raise KeyError('Duplicate parameter name {}'.format(name))
370
+ param = parameter_class(name=name, instrument=self, **kwargs)
371
+ self.parameters[name] = param
372
+
373
+ # for use in RemoteInstruments to add parameters to the server
374
+ # we return the info they need to construct their proxy
375
+ return param.get_attrs()
376
+
377
+ def add_function(self, name, **kwargs):
378
+ """
379
+ Bind one Function to this instrument.
380
+
381
+ Instrument subclasses can call this repeatedly in their ``__init__``
382
+ for every real function of the instrument.
383
+
384
+ This functionality is meant for simple cases, principally things that
385
+ map to simple commands like '*RST' (reset) or those with just a few
386
+ arguments. It requires a fixed argument count, and positional args
387
+ only. If your case is more complicated, you're probably better off
388
+ simply making a new method in your ``Instrument`` subclass definition.
389
+
390
+ Args:
391
+ name (str): how the Function will be stored within
392
+ ``instrument.Functions`` and also how you address it using the
393
+ shortcut methods: ``instrument.call(func_name, *args)`` etc.
394
+
395
+ **kwargs: constructor kwargs for ``Function``
396
+
397
+ Returns:
398
+ A dict of attribute information. Only used if you add functions
399
+ from the ``RemoteInstrument`` rather than at construction, to
400
+ properly construct the proxy for this function.
401
+
402
+ Raises:
403
+ KeyError: if this instrument already has a function with this
404
+ name.
405
+ """
406
+ if name in self.functions:
407
+ raise KeyError('Duplicate function name {}'.format(name))
408
+ func = Function(name=name, instrument=self, **kwargs)
409
+ self.functions[name] = func
410
+
411
+ # for use in RemoteInstruments to add functions to the server
412
+ # we return the info they need to construct their proxy
413
+ return func.get_attrs()
414
+
415
+ def snapshot_base(self, update=False):
416
+ """
417
+ State of the instrument as a JSON-compatible dict.
418
+
419
+ Args:
420
+ update (bool): If True, update the state by querying the
421
+ instrument. If False, just use the latest values in memory.
422
+
423
+ Returns:
424
+ dict: base snapshot
425
+ """
426
+ snap = {'parameters': dict((name, param.snapshot(update=update))
427
+ for name, param in self.parameters.items()),
428
+ 'functions': dict((name, func.snapshot(update=update))
429
+ for name, func in self.functions.items()),
430
+ '__class__': full_class(self),
431
+ }
432
+ for attr in set(self._meta_attrs):
433
+ if hasattr(self, attr):
434
+ snap[attr] = getattr(self, attr)
435
+ return snap
436
+
437
+ #
438
+ # `write_raw` and `ask_raw` are the interface to hardware #
439
+ # `write` and `ask` are standard wrappers to help with error reporting #
440
+ #
441
+
442
+ def write(self, cmd):
443
+ """
444
+ Write a command string with NO response to the hardware.
445
+
446
+ Subclasses that transform ``cmd`` should override this method, and in
447
+ it call ``super().write(new_cmd)``. Subclasses that define a new
448
+ hardware communication should instead override ``write_raw``.
449
+
450
+ Args:
451
+ cmd (str): the string to send to the instrument
452
+
453
+ Raises:
454
+ Exception: wraps any underlying exception with extra context,
455
+ including the command and the instrument.
456
+ """
457
+ try:
458
+ self.write_raw(cmd)
459
+ except Exception as e:
460
+ e.args = e.args + ('writing ' + repr(cmd) + ' to ' + repr(self),)
461
+ raise e
462
+
463
+ def write_raw(self, cmd):
464
+ """
465
+ Low level method to write a command string to the hardware.
466
+
467
+ Subclasses that define a new hardware communication should override
468
+ this method. Subclasses that transform ``cmd`` should instead
469
+ override ``write``.
470
+
471
+ Args:
472
+ cmd (str): the string to send to the instrument
473
+ """
474
+ raise NotImplementedError(
475
+ 'Instrument {} has not defined a write method'.format(
476
+ type(self).__name__))
477
+
478
+ def ask(self, cmd):
479
+ """
480
+ Write a command string to the hardware and return a response.
481
+
482
+ Subclasses that transform ``cmd`` should override this method, and in
483
+ it call ``super().ask(new_cmd)``. Subclasses that define a new
484
+ hardware communication should instead override ``ask_raw``.
485
+
486
+ Args:
487
+ cmd (str): the string to send to the instrument
488
+
489
+ Returns:
490
+ response (str, normally)
491
+
492
+ Raises:
493
+ Exception: wraps any underlying exception with extra context,
494
+ including the command and the instrument.
495
+ """
496
+ try:
497
+ return self.ask_raw(cmd)
498
+ except Exception as e:
499
+ e.args = e.args + ('asking ' + repr(cmd) + ' to ' + repr(self),)
500
+ raise e
501
+
502
+ def ask_raw(self, cmd):
503
+ """
504
+ Low level method to write to the hardware and return a response.
505
+
506
+ Subclasses that define a new hardware communication should override
507
+ this method. Subclasses that transform ``cmd`` should instead
508
+ override ``ask``.
509
+
510
+ Args:
511
+ cmd (str): the string to send to the instrument
512
+ """
513
+ raise NotImplementedError(
514
+ 'Instrument {} has not defined an ask method'.format(
515
+ type(self).__name__))
516
+
517
+ #
518
+ # shortcuts to parameters & setters & getters #
519
+ #
520
+ # instrument['someparam'] === instrument.parameters['someparam'] #
521
+ # instrument.someparam === instrument.parameters['someparam'] #
522
+ # instrument.get('someparam') === instrument['someparam'].get() #
523
+ # etc... #
524
+ #
525
+
526
+ delegate_attr_dicts = ['parameters', 'functions']
527
+
528
+ def __getitem__(self, key):
529
+ """Delegate instrument['name'] to parameter or function 'name'."""
530
+ try:
531
+ return self.parameters[key]
532
+ except KeyError:
533
+ return self.functions[key]
534
+
535
+ def set(self, param_name, value):
536
+ """
537
+ Shortcut for setting a parameter from its name and new value.
538
+
539
+ Args:
540
+ param_name (str): The name of a parameter of this instrument.
541
+ value (any): The new value to set.
542
+ """
543
+ self.parameters[param_name].set(value)
544
+
545
+ def get(self, param_name):
546
+ """
547
+ Shortcut for getting a parameter from its name.
548
+
549
+ Args:
550
+ param_name (str): The name of a parameter of this instrument.
551
+
552
+ Returns:
553
+ any: The current value of the parameter.
554
+ """
555
+ return self.parameters[param_name].get()
556
+
557
+ def call(self, func_name, *args):
558
+ """
559
+ Shortcut for calling a function from its name.
560
+
561
+ Args:
562
+ func_name (str): The name of a function of this instrument.
563
+ *args: any arguments to the function.
564
+
565
+ Returns:
566
+ any: The return value of the function.
567
+ """
568
+ return self.functions[func_name].call(*args)
569
+
570
+ #
571
+ # info about what's in this instrument, to help construct the remote #
572
+ #
573
+
574
+ def connection_attrs(self, new_id):
575
+ """
576
+ Collect info to reconstruct the instrument API in the RemoteInstrument.
577
+
578
+ Args:
579
+ new_id (int): The ID of this instrument on its server.
580
+ This is how the RemoteInstrument points its calls to the
581
+ correct server instrument when it calls the server.
582
+
583
+ Returns:
584
+ dict: Dictionary of name: str, id: int, parameters: dict,
585
+ functions: dict, _methods: dict
586
+ parameters, functions, and _methods are dictionaries of
587
+ name: List(str) of attributes to be proxied in the remote.
588
+ """
589
+ return {
590
+ 'name': self.name,
591
+ 'id': new_id,
592
+ 'parameters': {name: p.get_attrs()
593
+ for name, p in self.parameters.items()},
594
+ 'functions': {name: f.get_attrs()
595
+ for name, f in self.functions.items()},
596
+ '_methods': self._get_method_attrs()
597
+ }
598
+
599
+ def _get_method_attrs(self):
600
+ """
601
+ Construct a dict of methods this instrument has.
602
+
603
+ Returns:
604
+ dict: Dictionary of method names : list of attributes each method
605
+ has that should be proxied. As of now, this is just its
606
+ docstring, if it has one.
607
+ """
608
+ out = {}
609
+
610
+ for attr in dir(self):
611
+ value = getattr(self, attr)
612
+ if ((not callable(value)) or
613
+ value is self.parameters.get(attr) or
614
+ value is self.functions.get(attr) or
615
+ attr in self._no_proxy_methods):
616
+ # Functions and Parameters are callable and they show up in
617
+ # dir(), but they have their own listing.
618
+ continue
619
+
620
+ out[attr] = ['__doc__'] if hasattr(value, '__doc__') else []
621
+
622
+ return out
623
+
624
+ def __getstate__(self):
625
+ """Prevent pickling instruments, and give a nice error message."""
626
+ raise RuntimeError(
627
+ 'qcodes Instruments should not be pickled. Likely this means you '
628
+ 'were trying to use a local instrument (defined with '
629
+ 'server_name=None) in a background Loop. Local instruments can '
630
+ 'only be used in Loops with background=False.')