From 93f0700764b9a224c8a968499875a7861e94adce Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Fri, 9 Jun 2017 18:14:28 -0400 Subject: [PATCH] Add set_cmd to property factories --- instruments/agilent/agilent33220a.py | 12 +- .../generic_scpi/scpi_function_generator.py | 6 +- instruments/generic_scpi/scpi_multimeter.py | 10 +- instruments/lakeshore/lakeshore475.py | 2 +- instruments/picowatt/picowattavs47.py | 10 +- instruments/qubitekk/mc1.py | 10 +- instruments/srs/srs345.py | 8 +- .../test_bool_property.py | 26 ++- .../test_bounded_unitful_property.py | 4 +- .../test_enum_property.py | 15 ++ .../test_int_property.py | 12 ++ .../test_string_property.py | 12 ++ .../test_unitful_property.py | 15 ++ .../test_unitless_property.py | 12 ++ instruments/thorlabs/lcc25.py | 12 +- instruments/thorlabs/sc10.py | 12 +- instruments/util_fns.py | 150 +++++++++++++----- 17 files changed, 241 insertions(+), 87 deletions(-) diff --git a/instruments/agilent/agilent33220a.py b/instruments/agilent/agilent33220a.py index 5a24ea9ad..8afb868cf 100644 --- a/instruments/agilent/agilent33220a.py +++ b/instruments/agilent/agilent33220a.py @@ -87,7 +87,7 @@ def frequency(self, newval): super(Agilent33220a, self).frequency = newval function = enum_property( - name="FUNC", + command="FUNC", enum=Function, doc=""" Gets/sets the output function of the function generator @@ -98,7 +98,7 @@ def frequency(self, newval): ) duty_cycle = int_property( - name="FUNC:SQU:DCYC", + command="FUNC:SQU:DCYC", doc=""" Gets/sets the duty cycle of a square wave. @@ -111,7 +111,7 @@ def frequency(self, newval): ) ramp_symmetry = int_property( - name="FUNC:RAMP:SYMM", + command="FUNC:RAMP:SYMM", doc=""" Gets/sets the ramp symmetry for ramp waves. @@ -124,7 +124,7 @@ def frequency(self, newval): ) output = bool_property( - name="OUTP", + command="OUTP", inst_true="ON", inst_false="OFF", doc=""" @@ -138,7 +138,7 @@ def frequency(self, newval): ) output_sync = bool_property( - name="OUTP:SYNC", + command="OUTP:SYNC", inst_true="ON", inst_false="OFF", doc=""" @@ -149,7 +149,7 @@ def frequency(self, newval): ) output_polarity = enum_property( - name="OUTP:POL", + command="OUTP:POL", enum=OutputPolarity, doc=""" Gets/sets the polarity of the waveform relative to the offset voltage. diff --git a/instruments/generic_scpi/scpi_function_generator.py b/instruments/generic_scpi/scpi_function_generator.py index 66630006a..4877168ae 100644 --- a/instruments/generic_scpi/scpi_function_generator.py +++ b/instruments/generic_scpi/scpi_function_generator.py @@ -75,7 +75,7 @@ def _set_amplitude_(self, magnitude, units): # PROPERTIES # frequency = unitful_property( - name="FREQ", + command="FREQ", units=pq.Hz, doc=""" Gets/sets the output frequency. @@ -86,7 +86,7 @@ def _set_amplitude_(self, magnitude, units): ) function = enum_property( - name="FUNC", + command="FUNC", enum=lambda: Function, # pylint: disable=undefined-variable doc=""" Gets/sets the output function of the function generator @@ -96,7 +96,7 @@ def _set_amplitude_(self, magnitude, units): ) offset = unitful_property( - name="VOLT:OFFS", + command="VOLT:OFFS", units=pq.volt, doc=""" Gets/sets the offset voltage of the function generator. diff --git a/instruments/generic_scpi/scpi_multimeter.py b/instruments/generic_scpi/scpi_multimeter.py index b6cc062fd..1a273f7de 100644 --- a/instruments/generic_scpi/scpi_multimeter.py +++ b/instruments/generic_scpi/scpi_multimeter.py @@ -141,7 +141,7 @@ class SampleSource(Enum): # pylint: disable=unnecessary-lambda,undefined-variable mode = enum_property( - name="CONF", + command="CONF", enum=Mode, doc=""" Gets/sets the current measurement mode for the multimeter. @@ -157,7 +157,7 @@ class SampleSource(Enum): ) trigger_mode = enum_property( - name="TRIG:SOUR", + command="TRIG:SOUR", enum=TriggerMode, doc=""" Gets/sets the SCPI Multimeter trigger mode. @@ -317,7 +317,7 @@ def sample_count(self, newval): self.sendcmd("SAMP:COUN {}".format(newval)) trigger_delay = unitful_property( - name="TRIG:DEL", + command="TRIG:DEL", units=pq.second, doc=""" Gets/sets the time delay which the multimeter will use following @@ -329,7 +329,7 @@ def sample_count(self, newval): ) sample_source = enum_property( - name="SAMP:SOUR", + command="SAMP:SOUR", enum=SampleSource, doc=""" Gets/sets the multimeter sample source. This determines whether the @@ -344,7 +344,7 @@ def sample_count(self, newval): ) sample_timer = unitful_property( - name="SAMP:TIM", + command="SAMP:TIM", units=pq.second, doc=""" Gets/sets the sample interval when the sample counter is greater than diff --git a/instruments/lakeshore/lakeshore475.py b/instruments/lakeshore/lakeshore475.py index 86d02fb59..0db502339 100644 --- a/instruments/lakeshore/lakeshore475.py +++ b/instruments/lakeshore/lakeshore475.py @@ -269,7 +269,7 @@ def control_slope_limit(self, newval): self.field_control_params = tuple(values) control_mode = bool_property( - name="CMODE", + command="CMODE", inst_true="1", inst_false="0", doc=""" diff --git a/instruments/picowatt/picowattavs47.py b/instruments/picowatt/picowattavs47.py index c255b8d28..acf1d8d0f 100644 --- a/instruments/picowatt/picowattavs47.py +++ b/instruments/picowatt/picowattavs47.py @@ -97,7 +97,7 @@ def sensor(self): return ProxyList(self, PicowattAVS47.Sensor, range(8)) remote = bool_property( - name="REM", + command="REM", inst_true="1", inst_false="0", doc=""" @@ -111,7 +111,7 @@ def sensor(self): ) input_source = enum_property( - name="INP", + command="INP", enum=InputSource, input_decoration=int, doc=""" @@ -122,7 +122,7 @@ def sensor(self): ) mux_channel = int_property( - name="MUX", + command="MUX", doc=""" Gets/sets the multiplexer sensor number. It is recommended that you ground the input before switching the @@ -136,7 +136,7 @@ def sensor(self): ) excitation = int_property( - name="EXC", + command="EXC", doc=""" Gets/sets the excitation sensor number. @@ -148,7 +148,7 @@ def sensor(self): ) display = int_property( - name="DIS", + command="DIS", doc=""" Gets/sets the sensor that is displayed on the front panel. diff --git a/instruments/qubitekk/mc1.py b/instruments/qubitekk/mc1.py index 98b353962..facc54eab 100644 --- a/instruments/qubitekk/mc1.py +++ b/instruments/qubitekk/mc1.py @@ -91,7 +91,7 @@ def upper_limit(self, newval): self._upper_limit = assume_units(newval, pq.ms).rescale(pq.ms) direction = unitful_property( - name="DIRE", + command="DIRE", doc=""" Get the internal direction variable, which is a function of how far the motor needs to go. @@ -104,7 +104,7 @@ def upper_limit(self, newval): ) inertia = unitful_property( - name="INER", + command="INER", doc=""" Gets/Sets the amount of force required to overcome static inertia. Must be between 0 and 100 milliseconds. @@ -133,7 +133,7 @@ def internal_position(self): return response metric_position = unitful_property( - name="METR", + command="METR", doc=""" Get the estimated motor position, in millimeters. @@ -145,7 +145,7 @@ def internal_position(self): ) setting = int_property( - name="OUTP", + command="OUTP", doc=""" Gets/sets the output port of the optical switch. 0 means input 1 is directed to output 1, and input 2 is directed to output 2. 1 means that @@ -158,7 +158,7 @@ def internal_position(self): ) step_size = unitful_property( - name="STEP", + command="STEP", doc=""" Gets/Sets the number of milliseconds per step. Must be between 1 and 100 milliseconds. diff --git a/instruments/srs/srs345.py b/instruments/srs/srs345.py index a344a4b5d..c86de3c63 100644 --- a/instruments/srs/srs345.py +++ b/instruments/srs/srs345.py @@ -78,7 +78,7 @@ class Function(IntEnum): # PROPERTIES ## frequency = unitful_property( - name="FREQ", + command="FREQ", units=pq.Hz, doc=""" Gets/sets the output frequency. @@ -89,7 +89,7 @@ class Function(IntEnum): ) function = enum_property( - name="FUNC", + command="FUNC", enum=Function, input_decoration=int, doc=""" @@ -100,7 +100,7 @@ class Function(IntEnum): ) offset = unitful_property( - name="OFFS", + command="OFFS", units=pq.volt, doc=""" Gets/sets the offset voltage for the output waveform. @@ -111,7 +111,7 @@ class Function(IntEnum): ) phase = unitful_property( - name="PHSE", + command="PHSE", units=pq.degree, doc=""" Gets/sets the phase for the output waveform. diff --git a/instruments/tests/test_property_factories/test_bool_property.py b/instruments/tests/test_property_factories/test_bool_property.py index 9fc36fc19..01995485e 100644 --- a/instruments/tests/test_property_factories/test_bool_property.py +++ b/instruments/tests/test_property_factories/test_bool_property.py @@ -20,8 +20,8 @@ def test_bool_property_basics(): class BoolMock(MockInstrument): - mock1 = bool_property('MOCK1', 'ON', 'OFF') - mock2 = bool_property('MOCK2', 'YES', 'NO') + mock1 = bool_property('MOCK1') + mock2 = bool_property('MOCK2', inst_true='YES', inst_false='NO') mock_inst = BoolMock({'MOCK1?': 'OFF', 'MOCK2?': 'YES'}) @@ -36,7 +36,7 @@ class BoolMock(MockInstrument): def test_bool_property_set_fmt(): class BoolMock(MockInstrument): - mock1 = bool_property('MOCK1', 'ON', 'OFF', set_fmt="{}={}") + mock1 = bool_property('MOCK1', set_fmt="{}={}") mock_instrument = BoolMock({'MOCK1?': 'OFF'}) @@ -48,7 +48,7 @@ class BoolMock(MockInstrument): @raises(AttributeError) def test_bool_property_readonly_writing_fails(): class BoolMock(MockInstrument): - mock1 = bool_property('MOCK1', 'ON', 'OFF', readonly=True) + mock1 = bool_property('MOCK1', readonly=True) mock_instrument = BoolMock({'MOCK1?': 'OFF'}) @@ -57,7 +57,7 @@ class BoolMock(MockInstrument): def test_bool_property_readonly_reading_passes(): class BoolMock(MockInstrument): - mock1 = bool_property('MOCK1', 'ON', 'OFF', readonly=True) + mock1 = bool_property('MOCK1', readonly=True) mock_instrument = BoolMock({'MOCK1?': 'OFF'}) @@ -67,7 +67,7 @@ class BoolMock(MockInstrument): @raises(AttributeError) def test_bool_property_writeonly_reading_fails(): class BoolMock(MockInstrument): - mock1 = bool_property('MOCK1', 'ON', 'OFF', writeonly=True) + mock1 = bool_property('MOCK1', writeonly=True) mock_instrument = BoolMock({'MOCK1?': 'OFF'}) @@ -76,8 +76,20 @@ class BoolMock(MockInstrument): def test_bool_property_writeonly_writing_passes(): class BoolMock(MockInstrument): - mock1 = bool_property('MOCK1', 'ON', 'OFF', writeonly=True) + mock1 = bool_property('MOCK1', writeonly=True) mock_instrument = BoolMock({'MOCK1?': 'OFF'}) mock_instrument.mock1 = False + + +def test_bool_property_set_cmd(): + class BoolMock(MockInstrument): + mock1 = bool_property('MOCK1', set_cmd='FOOBAR') + + mock_inst = BoolMock({'MOCK1?': 'OFF'}) + + eq_(mock_inst.mock1, False) + mock_inst.mock1 = True + + eq_(mock_inst.value, 'MOCK1?\nFOOBAR ON\n') diff --git a/instruments/tests/test_property_factories/test_bounded_unitful_property.py b/instruments/tests/test_property_factories/test_bounded_unitful_property.py index 543320c5c..d982dc989 100644 --- a/instruments/tests/test_property_factories/test_bounded_unitful_property.py +++ b/instruments/tests/test_property_factories/test_bounded_unitful_property.py @@ -124,7 +124,7 @@ class BoundedUnitfulMock(MockInstrument): @mock.patch("instruments.util_fns.unitful_property") def test_bounded_unitful_property_passes_kwargs(mock_unitful_property): bounded_unitful_property( - name='MOCK', + command='MOCK', units=pq.Hz, derp="foobar" ) @@ -139,7 +139,7 @@ def test_bounded_unitful_property_passes_kwargs(mock_unitful_property): @mock.patch("instruments.util_fns.unitful_property") def test_bounded_unitful_property_valid_range_none(mock_unitful_property): bounded_unitful_property( - name='MOCK', + command='MOCK', units=pq.Hz, valid_range=(None, None) ) diff --git a/instruments/tests/test_property_factories/test_enum_property.py b/instruments/tests/test_property_factories/test_enum_property.py index 6384e7305..862a55df2 100644 --- a/instruments/tests/test_property_factories/test_enum_property.py +++ b/instruments/tests/test_property_factories/test_enum_property.py @@ -196,3 +196,18 @@ class EnumMock(MockInstrument): eq_(mock_instrument.a, SillyEnum.a) eq_(mock_instrument.value, 'MOCK:A?\n') + + +def test_enum_property_set_cmd(): + class SillyEnum(Enum): + a = 'aa' + + class EnumMock(MockInstrument): + a = enum_property('MOCK:A', SillyEnum, set_cmd='FOOBAR:A') + + mock_inst = EnumMock({'MOCK:A?': 'aa'}) + + eq_(mock_inst.a, SillyEnum.a) + mock_inst.a = SillyEnum.a + + eq_(mock_inst.value, 'MOCK:A?\nFOOBAR:A aa\n') diff --git a/instruments/tests/test_property_factories/test_int_property.py b/instruments/tests/test_property_factories/test_int_property.py index b0bec2064..1a9ab2340 100644 --- a/instruments/tests/test_property_factories/test_int_property.py +++ b/instruments/tests/test_property_factories/test_int_property.py @@ -97,3 +97,15 @@ class IntMock(MockInstrument): mock_inst.int_property = 1 eq_(mock_inst.value, 'MOCK {:e}\n'.format(1)) + + +def test_int_property_set_cmd(): + class IntMock(MockInstrument): + int_property = int_property('MOCK', set_cmd='FOOBAR') + + mock_inst = IntMock({'MOCK?': '1'}) + + eq_(mock_inst.int_property, 1) + mock_inst.int_property = 1 + + eq_(mock_inst.value, 'MOCK?\nFOOBAR 1\n') diff --git a/instruments/tests/test_property_factories/test_string_property.py b/instruments/tests/test_property_factories/test_string_property.py index d1c94554d..e30e5998f 100644 --- a/instruments/tests/test_property_factories/test_string_property.py +++ b/instruments/tests/test_property_factories/test_string_property.py @@ -52,3 +52,15 @@ class StringMock(MockInstrument): mock_inst.mock_property = 'foo' eq_(mock_inst.value, 'MOCK?\nMOCK foo\n') + + +def test_string_property_set_cmd(): + class StringMock(MockInstrument): + mock_property = string_property('MOCK', set_cmd='FOOBAR') + + mock_inst = StringMock({'MOCK?': '"derp"'}) + + eq_(mock_inst.mock_property, 'derp') + + mock_inst.mock_property = 'qwerty' + eq_(mock_inst.value, 'MOCK?\nFOOBAR "qwerty"\n') diff --git a/instruments/tests/test_property_factories/test_unitful_property.py b/instruments/tests/test_property_factories/test_unitful_property.py index 551d28d42..9d146236b 100644 --- a/instruments/tests/test_property_factories/test_unitful_property.py +++ b/instruments/tests/test_property_factories/test_unitful_property.py @@ -242,3 +242,18 @@ class UnitfulMock(MockInstrument): value = mock_inst.unitful_property assert value.magnitude == 1000 assert value.units == pq.hertz + + +def test_unitful_property_name_read_not_none(): + class UnitfulMock(MockInstrument): + a = unitful_property( + 'MOCK', + units=pq.hertz, + set_cmd='FOOBAR' + ) + + mock_inst = UnitfulMock({'MOCK?': '1000'}) + eq_(mock_inst.a, 1000 * pq.hertz) + mock_inst.a = 1000 * pq.hertz + + eq_(mock_inst.value, 'MOCK?\nFOOBAR {:e}\n'.format(1000)) diff --git a/instruments/tests/test_property_factories/test_unitless_property.py b/instruments/tests/test_property_factories/test_unitless_property.py index 16654d3b5..7698f2aaf 100644 --- a/instruments/tests/test_property_factories/test_unitless_property.py +++ b/instruments/tests/test_property_factories/test_unitless_property.py @@ -88,3 +88,15 @@ class UnitlessMock(MockInstrument): mock_inst = UnitlessMock({'MOCK?': '1'}) eq_(mock_inst.mock_property, 1) + + +def test_unitless_property_set_cmd(): + class UnitlessMock(MockInstrument): + mock_property = unitless_property('MOCK', set_cmd='FOOBAR') + + mock_inst = UnitlessMock({'MOCK?': '1'}) + + eq_(mock_inst.mock_property, 1) + mock_inst.mock_property = 1 + + eq_(mock_inst.value, 'MOCK?\nFOOBAR {:e}\n'.format(1)) diff --git a/instruments/thorlabs/lcc25.py b/instruments/thorlabs/lcc25.py index a44211507..2989283d9 100644 --- a/instruments/thorlabs/lcc25.py +++ b/instruments/thorlabs/lcc25.py @@ -95,8 +95,8 @@ def name(self): enable = bool_property( "enable", - "1", - "0", + inst_true="1", + inst_false="0", set_fmt="{}={}", doc=""" Gets/sets the output enable status. @@ -109,8 +109,8 @@ def name(self): extern = bool_property( "extern", - "1", - "0", + inst_true="1", + inst_false="0", set_fmt="{}={}", doc=""" Gets/sets the use of the external TTL modulation. @@ -124,8 +124,8 @@ def name(self): remote = bool_property( "remote", - "1", - "0", + inst_true="1", + inst_false="0", set_fmt="{}={}", doc=""" Gets/sets front panel lockout status for remote instrument operation. diff --git a/instruments/thorlabs/sc10.py b/instruments/thorlabs/sc10.py index 135d65b3c..cfce252b4 100644 --- a/instruments/thorlabs/sc10.py +++ b/instruments/thorlabs/sc10.py @@ -67,8 +67,8 @@ def name(self): enable = bool_property( "ens", - "1", - "0", + inst_true="1", + inst_false="0", set_fmt="{}={}", doc=""" Gets/sets the shutter enable status, False for disabled, True if @@ -182,8 +182,8 @@ def baud_rate(self, newval): closed = bool_property( "closed", - "1", - "0", + inst_true="1", + inst_false="0", readonly=True, doc=""" Gets the shutter closed status. @@ -197,8 +197,8 @@ def baud_rate(self, newval): interlock = bool_property( "interlock", - "1", - "0", + inst_true="1", + inst_false="0", readonly=True, doc=""" Gets the interlock tripped status. diff --git a/instruments/util_fns.py b/instruments/util_fns.py index fad59455e..d0430170a 100644 --- a/instruments/util_fns.py +++ b/instruments/util_fns.py @@ -20,6 +20,7 @@ # FUNCTIONS ################################################################### + # pylint: disable=too-many-arguments def assume_units(value, units): """ @@ -39,6 +40,7 @@ def assume_units(value, units): value = pq.Quantity(value, units) return value + def setattr_expression(target, name_expr, value): """ Recursively calls getattr/setattr for attribute @@ -184,16 +186,29 @@ def rproperty(fget=None, fset=None, doc=None, readonly=False, writeonly=False): return property(fget=fget, fset=fset, doc=doc) -def bool_property(name, inst_true, inst_false, doc=None, readonly=False, - writeonly=False, set_fmt="{} {}"): +def bool_property(command, set_cmd=None, inst_true="ON", inst_false="OFF", + doc=None, readonly=False, writeonly=False, set_fmt="{} {}"): """ Called inside of SCPI classes to instantiate boolean properties of the device cleanly. For example: - >>> my_property = bool_property("BEST:PROPERTY", "ON", "OFF") # doctest: +SKIP - - :param str name: Name of the SCPI command corresponding to this property. + >>> my_property = bool_property( + ... "BEST:PROPERTY", + ... inst_true="ON", + ... inst_false="OFF" + ... ) # doctest: +SKIP + + This will result in "BEST:PROPERTY ON" or "BEST:PROPERTY OFF" being sent + when setting, and "BEST:PROPERTY?" being sent when getting. + + :param str command: Name of the SCPI command corresponding to this property. + If parameter set_cmd is not specified, then this parameter is also used + for both getting and setting. + :param str set_cmd: If not `None`, this parameter sets the command string + to be used when sending commands with no return values to the + instrument. This allows for non-symmetric properties that have different + strings for getting vs setting a property. :param str inst_true: String returned and accepted by the instrument for `True` values. :param str inst_false: String returned and accepted by the instrument for @@ -210,19 +225,22 @@ def bool_property(name, inst_true, inst_false, doc=None, readonly=False, """ def _getter(self): - return self.query(name + "?").strip() == inst_true + return self.query(command + "?").strip() == inst_true def _setter(self, newval): if not isinstance(newval, bool): raise TypeError("Bool properties must be specified with a " "boolean value") - self.sendcmd(set_fmt.format(name, inst_true if newval else inst_false)) + self.sendcmd(set_fmt.format( + command if set_cmd is None else set_cmd, + inst_true if newval else inst_false + )) return rproperty(fget=_getter, fset=_setter, doc=doc, readonly=readonly, writeonly=writeonly) -def enum_property(name, enum, doc=None, input_decoration=None, +def enum_property(command, enum, set_cmd=None, doc=None, input_decoration=None, output_decoration=None, readonly=False, writeonly=False, set_fmt="{} {}"): """ @@ -234,7 +252,13 @@ def enum_property(name, enum, doc=None, input_decoration=None, Example: my_property = bool_property("BEST:PROPERTY", enum_class) - :param str name: Name of the SCPI command corresponding to this property. + :param str command: Name of the SCPI command corresponding to this property. + If parameter set_cmd is not specified, then this parameter is also used + for both getting and setting. + :param str set_cmd: If not `None`, this parameter sets the command string + to be used when sending commands with no return values to the + instrument. This allows for non-symmetric properties that have different + strings for getting vs setting a property. :param type enum: Class derived from `Enum` representing valid values. :param callable input_decoration: Function called on responses from the instrument before passing to user code. @@ -249,6 +273,10 @@ def enum_property(name, enum, doc=None, input_decoration=None, non-query to the instrument. The default is "{} {}" which places a space between the SCPI command the associated parameter. By switching to "{}={}" an equals sign would instead be used as the separator. + :param str get_cmd: If not `None`, this parameter sets the command string + to be used when reading/querying from the instrument. If used, the name + parameter is still used to set the command for pure-write commands to + the instrument. """ def _in_decor_fcn(val): if input_decoration is None: @@ -265,7 +293,7 @@ def _out_decor_fcn(val): return output_decoration(val) def _getter(self): - return enum(_in_decor_fcn(self.query("{}?".format(name)).strip())) + return enum(_in_decor_fcn(self.query("{}?".format(command)).strip())) def _setter(self, newval): try: # First assume newval is Enum.value @@ -275,19 +303,28 @@ def _setter(self, newval): newval = enum(newval) except ValueError: raise ValueError("Enum property new value not in enum.") - self.sendcmd(set_fmt.format(name, _out_decor_fcn(enum(newval).value))) + self.sendcmd(set_fmt.format( + command if set_cmd is None else set_cmd, + _out_decor_fcn(enum(newval).value) + )) return rproperty(fget=_getter, fset=_setter, doc=doc, readonly=readonly, writeonly=writeonly) -def unitless_property(name, format_code='{:e}', doc=None, readonly=False, - writeonly=False, set_fmt="{} {}"): +def unitless_property(command, set_cmd=None, format_code='{:e}', doc=None, + readonly=False, writeonly=False, set_fmt="{} {}"): """ Called inside of SCPI classes to instantiate properties with unitless numeric values. - :param str name: Name of the SCPI command corresponding to this property. + :param str command: Name of the SCPI command corresponding to this property. + If parameter set_cmd is not specified, then this parameter is also used + for both getting and setting. + :param str set_cmd: If not `None`, this parameter sets the command string + to be used when sending commands with no return values to the + instrument. This allows for non-symmetric properties that have different + strings for getting vs setting a property. :param str format_code: Argument to `str.format` used in sending values to the instrument. :param str doc: Docstring to be associated with the new property. @@ -302,7 +339,7 @@ def unitless_property(name, format_code='{:e}', doc=None, readonly=False, """ def _getter(self): - raw = self.query("{}?".format(name)) + raw = self.query("{}?".format(command)) return float(raw) def _setter(self, newval): @@ -312,19 +349,29 @@ def _setter(self, newval): else: raise ValueError strval = format_code.format(newval) - self.sendcmd(set_fmt.format(name, strval)) + self.sendcmd(set_fmt.format( + command if set_cmd is None else set_cmd, + strval + )) return rproperty(fget=_getter, fset=_setter, doc=doc, readonly=readonly, writeonly=writeonly) -def int_property(name, format_code='{:d}', doc=None, readonly=False, - writeonly=False, valid_set=None, set_fmt="{} {}"): +def int_property(command, set_cmd=None, format_code='{:d}', doc=None, + readonly=False, writeonly=False, valid_set=None, + set_fmt="{} {}"): """ Called inside of SCPI classes to instantiate properties with unitless numeric values. - :param str name: Name of the SCPI command corresponding to this property. + :param str command: Name of the SCPI command corresponding to this property. + If parameter set_cmd is not specified, then this parameter is also used + for both getting and setting. + :param str set_cmd: If not `None`, this parameter sets the command string + to be used when sending commands with no return values to the + instrument. This allows for non-symmetric properties that have different + strings for getting vs setting a property. :param str format_code: Argument to `str.format` used in sending values to the instrument. :param str doc: Docstring to be associated with the new property. @@ -341,12 +388,15 @@ def int_property(name, format_code='{:d}', doc=None, readonly=False, """ def _getter(self): - raw = self.query("{}?".format(name)) + raw = self.query("{}?".format(command)) return int(raw) if valid_set is None: def _setter(self, newval): strval = format_code.format(newval) - self.sendcmd(set_fmt.format(name, strval)) + self.sendcmd(set_fmt.format( + command if set_cmd is None else set_cmd, + strval + )) else: def _setter(self, newval): if newval not in valid_set: @@ -355,13 +405,16 @@ def _setter(self, newval): "must be one of {}.".format(newval, valid_set) ) strval = format_code.format(newval) - self.sendcmd(set_fmt.format(name, strval)) + self.sendcmd(set_fmt.format( + command if set_cmd is None else set_cmd, + strval + )) return rproperty(fget=_getter, fset=_setter, doc=doc, readonly=readonly, writeonly=writeonly) -def unitful_property(name, units, format_code='{:e}', doc=None, +def unitful_property(command, units, set_cmd=None, format_code='{:e}', doc=None, input_decoration=None, output_decoration=None, readonly=False, writeonly=False, set_fmt="{} {}", valid_range=(None, None)): @@ -373,7 +426,13 @@ def unitful_property(name, units, format_code='{:e}', doc=None, for instruments where the units can change dynamically due to front-panel interaction or due to remote commands. - :param str name: Name of the SCPI command corresponding to this property. + :param str command: Name of the SCPI command corresponding to this property. + If parameter set_cmd is not specified, then this parameter is also used + for both getting and setting. + :param str set_cmd: If not `None`, this parameter sets the command string + to be used when sending commands with no return values to the + instrument. This allows for non-symmetric properties that have different + strings for getting vs setting a property. :param units: Units to assume in sending and receiving magnitudes to and from the instrument. :param str format_code: Argument to `str.format` used in sending the @@ -413,7 +472,7 @@ def _out_decor_fcn(val): return output_decoration(val) def _getter(self): - raw = _in_decor_fcn(self.query("{}?".format(name))) + raw = _in_decor_fcn(self.query("{}?".format(command))) return pq.Quantity(*split_unit_str(raw, units)).rescale(units) def _setter(self, newval): @@ -434,13 +493,16 @@ def _setter(self, newval): # catch bad units. strval = format_code.format( assume_units(newval, units).rescale(units).item()) - self.sendcmd(set_fmt.format(name, _out_decor_fcn(strval))) + self.sendcmd(set_fmt.format( + command if set_cmd is None else set_cmd, + _out_decor_fcn(strval) + )) return rproperty(fget=_getter, fset=_setter, doc=doc, readonly=readonly, writeonly=writeonly) -def bounded_unitful_property(name, units, min_fmt_str="{}:MIN?", +def bounded_unitful_property(command, units, min_fmt_str="{}:MIN?", max_fmt_str="{}:MAX?", valid_range=("query", "query"), **kwargs): """ @@ -454,7 +516,13 @@ def bounded_unitful_property(name, units, min_fmt_str="{}:MIN?", the one created by `unitful_property`, one for the minimum value, and one for the maximum value. - :param str name: Name of the SCPI command corresponding to this property. + :param str command: Name of the SCPI command corresponding to this property. + If parameter set_cmd is not specified, then this parameter is also used + for both getting and setting. + :param str set_cmd: If not `None`, this parameter sets the command string + to be used when sending commands with no return values to the + instrument. This allows for non-symmetric properties that have different + strings for getting vs setting a property. :param units: Units to assume in sending and receiving magnitudes to and from the instrument. :param str min_fmt_str: Specify the string format to use when sending a @@ -480,13 +548,13 @@ def bounded_unitful_property(name, units, min_fmt_str="{}:MIN?", def _min_getter(self): if valid_range[0] == "query": - return pq.Quantity(*split_unit_str(self.query(min_fmt_str.format(name)), units)) + return pq.Quantity(*split_unit_str(self.query(min_fmt_str.format(command)), units)) return assume_units(valid_range[0], units).rescale(units) def _max_getter(self): if valid_range[1] == "query": - return pq.Quantity(*split_unit_str(self.query(max_fmt_str.format(name)), units)) + return pq.Quantity(*split_unit_str(self.query(max_fmt_str.format(command)), units)) return assume_units(valid_range[1], units).rescale(units) @@ -496,18 +564,24 @@ def _max_getter(self): ) return ( - unitful_property(name, units, valid_range=new_range, **kwargs), + unitful_property(command, units, valid_range=new_range, **kwargs), property(_min_getter) if valid_range[0] is not None else None, property(_max_getter) if valid_range[1] is not None else None ) -def string_property(name, bookmark_symbol='"', doc=None, readonly=False, - writeonly=False, set_fmt="{} {}{}{}"): +def string_property(command, set_cmd=None, bookmark_symbol='"', doc=None, + readonly=False, writeonly=False, set_fmt="{} {}{}{}"): """ Called inside of SCPI classes to instantiate properties with a string value. - :param str name: Name of the SCPI command corresponding to this property. + :param str command: Name of the SCPI command corresponding to this property. + If parameter set_cmd is not specified, then this parameter is also used + for both getting and setting. + :param str set_cmd: If not `None`, this parameter sets the command string + to be used when sending commands with no return values to the + instrument. This allows for non-symmetric properties that have different + strings for getting vs setting a property. :param str doc: Docstring to be associated with the new property. :param bool readonly: If `True`, the returned property does not have a setter. @@ -523,14 +597,16 @@ def string_property(name, bookmark_symbol='"', doc=None, readonly=False, bookmark_length = len(bookmark_symbol) def _getter(self): - string = self.query("{}?".format(name)) + string = self.query("{}?".format(command)) string = string[ bookmark_length:-bookmark_length] if bookmark_length > 0 else string return string def _setter(self, newval): - self.sendcmd( - set_fmt.format(name, bookmark_symbol, newval, bookmark_symbol)) + self.sendcmd(set_fmt.format( + command if set_cmd is None else set_cmd, + bookmark_symbol, newval, bookmark_symbol + )) return rproperty(fget=_getter, fset=_setter, doc=doc, readonly=readonly, writeonly=writeonly)