From 672371c93204226d3722d441de3a6e0c0e340281 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Mon, 22 Feb 2016 10:41:02 +0100 Subject: [PATCH 1/6] ManualParameter and tests --- qcodes/instrument/parameter.py | 24 ++++++++++++++++++++++ qcodes/tests/test_instrument.py | 36 +++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index e6059f7ee80..dccff47b4b2 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -418,3 +418,27 @@ def set_sweep(self, sweep_step, sweep_delay, max_val_age=None): if max_val_age < 0: raise ValueError('max_val_age must be non-negative') self._max_val_age = max_val_age + + +class ManualParameter(Parameter): + def __init__(self, instrument, name, initial_value=None, **kwargs): + super().__init__(name=name, **kwargs) + if initial_value is not None: + self.validate(initial_value) + + self._value = initial_value + + def set(self, value): + self.validate(value) + self._value = value + + @asyncio.coroutine + def set_async(self, value): + return self.set(value) + + def get(self): + return self._value + + @asyncio.coroutine + def get_async(self): + return self.get() diff --git a/qcodes/tests/test_instrument.py b/qcodes/tests/test_instrument.py index 0056b36e780..1151d23b5ab 100644 --- a/qcodes/tests/test_instrument.py +++ b/qcodes/tests/test_instrument.py @@ -4,6 +4,7 @@ from qcodes.instrument.base import Instrument from qcodes.instrument.mock import MockInstrument +from qcodes.instrument.parameter import ManualParameter from qcodes.utils.validators import Numbers, Ints, Strings, MultiType, Enum from qcodes.utils.sync_async import wait_for_async, NoCommandError @@ -484,3 +485,38 @@ def test_sweep_values_valid(self): self.assertEqual(list(c0_sv6), [1, 3, 4]) self.assertEqual(list(c0_sv7), [1, 3, 4]) self.assertFalse(c0_sv6 is c0_sv7) + + def test_manual_parameter(self): + self.source.add_parameter('bias_resistor', + parameter_class=ManualParameter, + initial_value=1000) + res = self.source.bias_resistor + self.assertEqual(res.get(), 1000) + + res.set(1e9) + self.assertEqual(wait_for_async(res.get_async), 1e9) + # default vals is all numbers + # TODO - maybe non-negative numbers would be a better + # default? + wait_for_async(res.set_async, -1) + self.assertEqual(res.get(), -1) + + self.source.add_parameter('alignment', + parameter_class=ManualParameter, + vals=Enum('lawful', 'neutral', 'chaotic')) + alignment = self.source.alignment + + # a ManualParameter can have initial_value=None (default) even if + # that's not a valid value to set later + self.assertIsNone(alignment.get()) + with self.assertRaises(ValueError): + alignment.set(None) + + alignment.set('lawful') + self.assertEqual(alignment.get(), 'lawful') + + # None is the only invalid initial_value you can use + with self.assertRaises(ValueError): + self.source.add_parameter('alignment2', + parameter_class=ManualParameter, + initial_value='nearsighted') From 81b20df34348befc4417af9a17c3571f3f6c61e6 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Mon, 22 Feb 2016 11:06:17 +0100 Subject: [PATCH 2/6] docstring for ManualParameter --- qcodes/instrument/parameter.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index dccff47b4b2..01a2788be62 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -421,6 +421,19 @@ def set_sweep(self, sweep_step, sweep_delay, max_val_age=None): class ManualParameter(Parameter): + ''' + defines one parameter that reflects a manual setting / configuration + + instrument: the instrument this applies to. Not actually used for + anything, just required so this class can be used with + Instrument.add_parameter(name, parameter_class=ManualParameter) + + name: the local name of this parameter + + initial_value: optional starting value. Default is None, which is the + only invalid value allowed (and None is only allowed as an initial + value, it cannot be set later) + ''' def __init__(self, instrument, name, initial_value=None, **kwargs): super().__init__(name=name, **kwargs) if initial_value is not None: From e39605978c0bc2b8c3cf394e48dad2e0760b1177 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Mon, 22 Feb 2016 11:08:26 +0100 Subject: [PATCH 3/6] move Parameter and InstrumentParameter docstrings out of __init__ --- qcodes/instrument/parameter.py | 222 ++++++++++++++++----------------- 1 file changed, 111 insertions(+), 111 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 01a2788be62..ba29fc30600 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -50,6 +50,71 @@ def no_func(*args, **kwargs): class Parameter(Metadatable): + ''' + defines one generic parameter, not necessarily part of + an instrument. can be settable and/or gettable. + + A settable Parameter has a .set and/or a .set_async method, + and supports only a single value at a time (see below) + + A gettable Parameter has a .get and/or a .get_async method, + which may return: + 1. a single value + 2. a sequence of values with different names (for example, + raw and interpreted, I and Q, several fit parameters...) + 3. an array of values all with the same name, but at different + setpoints (for example, a time trace or fourier transform that + was acquired in the hardware and all sent to the computer at once) + 4. 2 & 3 together: a sequence of arrays. All arrays should be the same + size. + 5. a sequence of differently sized items + + Because .set only supports a single value, if a Parameter is both + gettable AND settable, .get should return a single value too (case 1) + + The constructor arguments change somewhat between these cases: + + name: (1&3) the local name of this parameter, should be a valid + identifier, ie no spaces or special characters + names: (2,4,5) a tuple of names + + label: (1&3) string to use as an axis label for this parameter + defaults to name + labels: (2,4,5) a tuple of labels + + units: (1&3) string that indicates units of parameter for use in axis + label and snapshot + (2,4,5) a tuple of units + + size: (3&4) an integer or tuple of integers for the size of array + returned by .get(). Can be an integer only if the array is 1D, but + as a tuple it can describe any dimensionality (including 1D) + If size is an integer then setpoints, setpoint_names, + and setpoint_labels should also not be wrapped in tuples. + sizes: (5) a tuple of integers or tuples, each one as in `size`. + + setpoints: (3,4,5) the setpoints for the returned array of values. + 3&4 - This should be an array if `size` is an integer, or a + tuple of arrays if `size` is a tuple + The first array should be 1D, the second 2D, etc. + 5 - This should be a tuple of arrays or tuples, each item as above + Single values should be denoted by None or (), not 1 (because 1 + would be a length-1 array) + Defaults to integers from zero in each respective direction + Each may be either a DataArray, a numpy array, or a sequence + (sequences will be converted to numpy arrays) + NOTE: if the setpoints will be different each measurement, leave + this out and return the setpoints (with extra names) in the get. + setpoint_names: (3,4,5) one identifier (like `name`) per setpoint + array. + Ignored if `setpoints` are DataArrays, which already have names. + setpoint_labels: (3&4) one label (like `label`) per setpoint array. + Overridden if `setpoints` are DataArrays and already have labels. + + vals: allowed values for setting this parameter (only relevant + if it has a setter) + defaults to Numbers() + ''' def __init__(self, name=None, names=None, label=None, labels=None, @@ -57,71 +122,6 @@ def __init__(self, size=None, sizes=None, setpoints=None, setpoint_names=None, setpoint_labels=None, vals=None, **kwargs): - ''' - defines one generic parameter, not necessarily part of - an instrument. can be settable and/or gettable. - - A settable Parameter has a .set and/or a .set_async method, - and supports only a single value at a time (see below) - - A gettable Parameter has a .get and/or a .get_async method, - which may return: - 1. a single value - 2. a sequence of values with different names (for example, - raw and interpreted, I and Q, several fit parameters...) - 3. an array of values all with the same name, but at different - setpoints (for example, a time trace or fourier transform that - was acquired in the hardware and all sent to the computer at once) - 4. 2 & 3 together: a sequence of arrays. All arrays should be the same - size. - 5. a sequence of differently sized items - - Because .set only supports a single value, if a Parameter is both - gettable AND settable, .get should return a single value too (case 1) - - The constructor arguments change somewhat between these cases: - - name: (1&3) the local name of this parameter, should be a valid - identifier, ie no spaces or special characters - names: (2,4,5) a tuple of names - - label: (1&3) string to use as an axis label for this parameter - defaults to name - labels: (2,4,5) a tuple of labels - - units: (1&3) string that indicates units of parameter for use in axis - label and snapshot - (2,4,5) a tuple of units - - size: (3&4) an integer or tuple of integers for the size of array - returned by .get(). Can be an integer only if the array is 1D, but - as a tuple it can describe any dimensionality (including 1D) - If size is an integer then setpoints, setpoint_names, - and setpoint_labels should also not be wrapped in tuples. - sizes: (5) a tuple of integers or tuples, each one as in `size`. - - setpoints: (3,4,5) the setpoints for the returned array of values. - 3&4 - This should be an array if `size` is an integer, or a - tuple of arrays if `size` is a tuple - The first array should be 1D, the second 2D, etc. - 5 - This should be a tuple of arrays or tuples, each item as above - Single values should be denoted by None or (), not 1 (because 1 - would be a length-1 array) - Defaults to integers from zero in each respective direction - Each may be either a DataArray, a numpy array, or a sequence - (sequences will be converted to numpy arrays) - NOTE: if the setpoints will be different each measurement, leave - this out and return the setpoints (with extra names) in the get. - setpoint_names: (3,4,5) one identifier (like `name`) per setpoint - array. - Ignored if `setpoints` are DataArrays, which already have names. - setpoint_labels: (3&4) one label (like `label`) per setpoint array. - Overridden if `setpoints` are DataArrays and already have labels. - - vals: allowed values for setting this parameter (only relevant - if it has a setter) - defaults to Numbers() - ''' super().__init__(**kwargs) if name is not None: @@ -175,58 +175,58 @@ def __getitem__(self, keys): class InstrumentParameter(Parameter): + ''' + defines one measurement parameter + + instrument: an instrument that handles this parameter + name: the local name of this parameter + + get_cmd: a string or function to get this parameter + async_get_cmd: a function to use for async get, or for both sync + and async if get_cmd is missing or None + get_parser: function to transform the response from get + to the final output value. + NOTE: only applies if get_cmd is a string. The function forms + of get_cmd and async_get_cmd should do their own parsing + See also val_mapping + + set_cmd: command to set this parameter, either: + - a string (containing one field to .format, like "{}" etc) + - a function (of one parameter) + async_set_cmd: a function to use for async set, or for both sync + and async if set_cmd is missing or None + set_parser: function to transform the input set value to an encoded + value sent to the instrument. + NOTE: only applies if set_cmd is a string. The function forms + of set_cmd and async_set_cmd should do their own parsing + See also val_mapping + + parse_function: DEPRECATED - use get_parser instead + + val_mapping: a bidirectional map from data/readable values to + instrument codes, expressed as a dict {data_val: instrument_code} + For example, if the instrument uses '0' to mean 1V and '1' to mean + 10V, set val_mapping={1: '0', 10: '1'} and on the user side you + only see 1 and 10, never the coded '0' and '1' + + If vals is omitted, will also construct a matching Enum validator. + NOTE: only applies to get if get_cmd is a string, and to set if + set_cmd is a string. + + vals: a Validator object for this parameter + + sweep_step: max increment of parameter value - larger changes + are broken into steps this size + sweep_delay: time (in seconds) to wait after each sweep step + max_val_age: max time (in seconds) to trust a saved value from + this parameter as the starting point of a sweep + ''' def __init__(self, instrument, name, get_cmd=None, async_get_cmd=None, get_parser=None, parse_function=None, val_mapping=None, set_cmd=None, async_set_cmd=None, set_parser=None, sweep_step=None, sweep_delay=None, max_val_age=3600, vals=None, **kwargs): - ''' - defines one measurement parameter - - instrument: an instrument that handles this parameter - name: the local name of this parameter - - get_cmd: a string or function to get this parameter - async_get_cmd: a function to use for async get, or for both sync - and async if get_cmd is missing or None - get_parser: function to transform the response from get - to the final output value. - NOTE: only applies if get_cmd is a string. The function forms - of get_cmd and async_get_cmd should do their own parsing - See also val_mapping - - set_cmd: command to set this parameter, either: - - a string (containing one field to .format, like "{}" etc) - - a function (of one parameter) - async_set_cmd: a function to use for async set, or for both sync - and async if set_cmd is missing or None - set_parser: function to transform the input set value to an encoded - value sent to the instrument. - NOTE: only applies if set_cmd is a string. The function forms - of set_cmd and async_set_cmd should do their own parsing - See also val_mapping - - parse_function: DEPRECATED - use get_parser instead - - val_mapping: a bidirectional map from data/readable values to - instrument codes, expressed as a dict {data_val: instrument_code} - For example, if the instrument uses '0' to mean 1V and '1' to mean - 10V, set val_mapping={1: '0', 10: '1'} and on the user side you - only see 1 and 10, never the coded '0' and '1' - - If vals is omitted, will also construct a matching Enum validator. - NOTE: only applies to get if get_cmd is a string, and to set if - set_cmd is a string. - - vals: a Validator object for this parameter - - sweep_step: max increment of parameter value - larger changes - are broken into steps this size - sweep_delay: time (in seconds) to wait after each sweep step - max_val_age: max time (in seconds) to trust a saved value from - this parameter as the starting point of a sweep - ''' # handle val_mapping before super init because it impacts # vals / validation in the base class if val_mapping: From c73f8c5175507f03fa778489a95daf2b48fd8212 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Mon, 22 Feb 2016 14:36:15 +0100 Subject: [PATCH 4/6] InstrumentParameter -> StandardParameter, instrument is optional --- docs/objects.md | 4 ++-- qcodes/__init__.py | 2 +- qcodes/instrument/base.py | 15 ++++++++------- qcodes/instrument/parameter.py | 22 ++++++++++++---------- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/docs/objects.md b/docs/objects.md index 6f114568fdb..d12baa458d3 100644 --- a/docs/objects.md +++ b/docs/objects.md @@ -8,7 +8,7 @@ Normal text the container includes and uses this object - Station - Instrument: IPInstrument, VisaInstrument, MockInstrument - - **Parameter**: InstrumentParameter + - **Parameter**: StandardParameter - Validator: Anything, Strings, Numbers, Ints, Enum, MultiType - **SweepValues**: SweepFixedValues, AdaptiveSweep - Function @@ -50,7 +50,7 @@ a serialized communication channel with an apparatus. A representation of one particular state variable. Most `Parameter`s are part of an `Instrument`, using the subclass -`InstrumentParameter` which links it to specific commands sent to a specific +`StandardParameter` which links it to specific commands sent to a specific instrument. But you can also create `Parameter`s that execute arbitrary functions, for example to combine several gate voltages in a diagonal sweep. Parameters can have setters and/or getters (they must define at least a setter OR a getter but diff --git a/qcodes/__init__.py b/qcodes/__init__.py index 67957b4004d..20c82984ff4 100644 --- a/qcodes/__init__.py +++ b/qcodes/__init__.py @@ -39,5 +39,5 @@ from qcodes.instrument.mock import MockInstrument from qcodes.instrument.function import Function -from qcodes.instrument.parameter import Parameter, InstrumentParameter +from qcodes.instrument.parameter import Parameter, StandardParameter from qcodes.instrument.sweep_values import SweepFixedValues, AdaptiveSweep diff --git a/qcodes/instrument/base.py b/qcodes/instrument/base.py index fc14f7a62ca..28c4276e652 100644 --- a/qcodes/instrument/base.py +++ b/qcodes/instrument/base.py @@ -3,7 +3,7 @@ from qcodes.utils.metadata import Metadatable from qcodes.utils.sync_async import wait_for_async from qcodes.utils.helpers import DelegateAttributes -from .parameter import InstrumentParameter +from .parameter import StandardParameter from .function import Function @@ -20,10 +20,10 @@ def __init__(self, name, **kwargs): # anyway threading.Lock is unpicklable on Windows # self.lock = threading.Lock() - def add_parameter(self, name, parameter_class=InstrumentParameter, + def add_parameter(self, name, parameter_class=StandardParameter, **kwargs): ''' - binds one InstrumentParameter to this instrument. + binds one Parameter to this instrument. instrument subclasses can call this repeatedly in their __init__ for every real parameter of the instrument. @@ -31,19 +31,20 @@ def add_parameter(self, name, parameter_class=InstrumentParameter, In this sense, parameters are the state variables of the instrument, anything the user can set and/or get - `name` is how the InstrumentParameter will be stored within + `name` is how the Parameter will be stored within instrument.parameters and also how you address it using the shortcut methods: instrument.set(param_name, value) etc. `parameter_class` can be used to construct the parameter out of - something other than InstrumentParameter + something other than StandardParameter - kwargs: see InstrumentParameter (or `parameter_class`) + kwargs: see StandardParameter (or `parameter_class`) ''' if name in self.parameters: raise KeyError('Duplicate parameter name {}'.format(name)) - self.parameters[name] = parameter_class(self, name, **kwargs) + self.parameters[name] = parameter_class(name=name, instrument=self, + **kwargs) def add_function(self, name, **kwargs): ''' diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index ba29fc30600..cc673652350 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -174,12 +174,13 @@ def __getitem__(self, keys): return SweepFixedValues(self, keys) -class InstrumentParameter(Parameter): +class StandardParameter(Parameter): ''' defines one measurement parameter - instrument: an instrument that handles this parameter name: the local name of this parameter + instrument: an instrument that handles this parameter + default None get_cmd: a string or function to get this parameter async_get_cmd: a function to use for async get, or for both sync @@ -221,7 +222,7 @@ class InstrumentParameter(Parameter): max_val_age: max time (in seconds) to trust a saved value from this parameter as the starting point of a sweep ''' - def __init__(self, instrument, name, + def __init__(self, name, instrument=None, get_cmd=None, async_get_cmd=None, get_parser=None, parse_function=None, val_mapping=None, set_cmd=None, async_set_cmd=None, set_parser=None, @@ -295,8 +296,8 @@ def get_async(self): def _set_get(self, get_cmd, async_get_cmd, get_parser): self._get, self._get_async = syncable_command( param_count=0, cmd=get_cmd, acmd=async_get_cmd, - exec_str=self._instrument.ask, - aexec_str=self._instrument.ask_async, + exec_str=self._instrument.ask if self._instrument else None, + aexec_str=self._instrument.ask_async if self._instrument else None, output_parser=get_parser, no_cmd_function=no_func) if self._get is not no_func: @@ -307,8 +308,9 @@ def _set_set(self, set_cmd, async_set_cmd, set_parser): # in self.set_sweep, when we choose a swept or non-swept setter. self._set, self._set_async = syncable_command( param_count=1, cmd=set_cmd, acmd=async_set_cmd, - exec_str=self._instrument.write, - aexec_str=self._instrument.write_async, + exec_str=self._instrument.write if self._instrument else None, + aexec_str=(self._instrument.write_async if self._instrument + else None), input_parser=set_parser, no_cmd_function=no_func) if self._set is not no_func: @@ -424,17 +426,17 @@ class ManualParameter(Parameter): ''' defines one parameter that reflects a manual setting / configuration + name: the local name of this parameter + instrument: the instrument this applies to. Not actually used for anything, just required so this class can be used with Instrument.add_parameter(name, parameter_class=ManualParameter) - name: the local name of this parameter - initial_value: optional starting value. Default is None, which is the only invalid value allowed (and None is only allowed as an initial value, it cannot be set later) ''' - def __init__(self, instrument, name, initial_value=None, **kwargs): + def __init__(self, name, instrument=None, initial_value=None, **kwargs): super().__init__(name=name, **kwargs) if initial_value is not None: self.validate(initial_value) From d035740364641489aae1b511ef7027017606f554 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Mon, 22 Feb 2016 14:43:53 +0100 Subject: [PATCH 5/6] comment about only function form of get/set_cmd with no instrument --- qcodes/instrument/parameter.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index cc673652350..4fb2ad4a66f 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -183,6 +183,8 @@ class StandardParameter(Parameter): default None get_cmd: a string or function to get this parameter + you can only use a string if an instrument is provided, + this string will be passed to instrument.ask async_get_cmd: a function to use for async get, or for both sync and async if get_cmd is missing or None get_parser: function to transform the response from get @@ -193,6 +195,8 @@ class StandardParameter(Parameter): set_cmd: command to set this parameter, either: - a string (containing one field to .format, like "{}" etc) + you can only use a string if an instrument is provided, + this string will be passed to instrument.write - a function (of one parameter) async_set_cmd: a function to use for async set, or for both sync and async if set_cmd is missing or None From ce89956b5122043af64da5c6084d14508f329fa5 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Mon, 22 Feb 2016 14:50:22 +0100 Subject: [PATCH 6/6] make instrument optional in Function too --- qcodes/instrument/base.py | 2 +- qcodes/instrument/function.py | 83 +++++++++++++++++++---------------- 2 files changed, 46 insertions(+), 39 deletions(-) diff --git a/qcodes/instrument/base.py b/qcodes/instrument/base.py index 28c4276e652..3e1519be19c 100644 --- a/qcodes/instrument/base.py +++ b/qcodes/instrument/base.py @@ -64,7 +64,7 @@ def add_function(self, name, **kwargs): ''' if name in self.functions: raise KeyError('Duplicate function name {}'.format(name)) - self.functions[name] = Function(self, name, **kwargs) + self.functions[name] = Function(name=name, instrument=self, **kwargs) def snapshot_base(self, update=False): if update: diff --git a/qcodes/instrument/function.py b/qcodes/instrument/function.py index bde4b17e236..cd829fa45f6 100644 --- a/qcodes/instrument/function.py +++ b/qcodes/instrument/function.py @@ -6,41 +6,45 @@ class Function(Metadatable): - def __init__(self, instrument, name, call_cmd=None, async_call_cmd=None, + ''' + defines a function (with arbitrary parameters) that this instrument + can execute. + + You execute this function object like a normal function, or use its + .call method; or call it async with the .call_async method. + + name: the local name of this parameter + instrument: an instrument that handles this function + default None + + call_cmd: command to execute on instrument + - a string (with positional fields to .format, "{}" or "{0}" etc) + you can only use a string if an instrument is provided, + this string will be passed to instrument.write + - a function (with parameter count matching parameters list) + async_call_cmd: an async function to use for call_async, or for both + sync and async if call_cmd is missing or None. + + parameters: list of Validator objects, + one for each parameter to the Function + + parameter_parser: function to transform the input parameter(s) + to encoded value(s) sent to the instrument. + If there are multiple arguments, this function should accept all + the arguments in order, and return a tuple of values. + return_parser: function to transform the response from the instrument + to the final output value. + may be a type casting function like `int` or `float`. + If None (default), will not wait for or read any response + NOTE: parsers only apply if call_cmd is a string. The function forms + of call_cmd and async_call_cmd should do their own parsing. + + parse_function: DEPRECATED - use return_parser instead + ''' + def __init__(self, name, instrument=None, + call_cmd=None, async_call_cmd=None, parameters=[], parameter_parser=None, return_parser=None, parse_function=None, **kwargs): - ''' - defines a function (with arbitrary parameters) that this instrument - can execute. - - You execute this function object like a normal function, or use its - .call method; or call it async with the .call_async method. - - instrument: an instrument that handles this function - name: the local name of this parameter - - call_cmd: command to execute on instrument - - a string (with positional fields to .format, "{}" or "{0}" etc) - - a function (with parameter count matching parameters list) - async_call_cmd: an async function to use for call_async, or for both - sync and async if call_cmd is missing or None. - - parameters: list of Validator objects, - one for each parameter to the Function - - parameter_parser: function to transform the input parameter(s) - to encoded value(s) sent to the instrument. - If there are multiple arguments, this function should accept all - the arguments in order, and return a tuple of values. - return_parser: function to transform the response from the instrument - to the final output value. - may be a type casting function like `int` or `float`. - If None (default), will not wait for or read any response - NOTE: parsers only apply if call_cmd is a string. The function forms - of call_cmd and async_call_cmd should do their own parsing. - - parse_function: DEPRECATED - use return_parser instead - ''' super().__init__(**kwargs) self._instrument = instrument @@ -63,11 +67,14 @@ def _set_params(self, parameters): def _set_call(self, call_cmd, async_call_cmd, parameter_parser, return_parser): - ask_or_write = self._instrument.write - ask_or_write_async = self._instrument.write_async - if isinstance(call_cmd, str) and return_parser: - ask_or_write = self._instrument.ask - ask_or_write_async = self._instrument.ask_async + if self._instrument: + ask_or_write = self._instrument.write + ask_or_write_async = self._instrument.write_async + if isinstance(call_cmd, str) and return_parser: + ask_or_write = self._instrument.ask + ask_or_write_async = self._instrument.ask_async + else: + ask_or_write, ask_or_write_async = None, None self._call, self._call_async = syncable_command( param_count=self._param_count,