Repository: microsoft/Qcodes

Issue:
certain Keithley_2000 commands not working
### Steps to reproduce

The following command does not work
`keithley.auto_range('off')`
### Expected behaviour

I expect the keithley to change the auto_range future
### Actual behaviour

the keithley beeps and gives error `-113`
Which means (keithley manual `Undefined header`)
Is this because `_set_mode_param` uses `self.mode()` which gives (e.g.) `'dc current'` instead of `'"CURR:DC"\n'`
### System

**operating system**
Windows 7
**qcodes branch**
ATS9870_test (not on github yet)
merged with feit/find_inst
**qcodes commit**
most recent github commit:
e1f65756253df4836c74fdfb2d56ad00af553de7

I don't know who to tag in this, because I don't know who wrote the keithley_2000 driver.
Could you forward this @giulioungaretti ?



Discussion:

Resolving patch:
diff --git a/qcodes/instrument_drivers/tektronix/Keithley_2000.py b/qcodes/instrument_drivers/tektronix/Keithley_2000.py
--- a/qcodes/instrument_drivers/tektronix/Keithley_2000.py
+++ b/qcodes/instrument_drivers/tektronix/Keithley_2000.py
@@ -1,8 +1,9 @@
 from qcodes import VisaInstrument
-from qcodes.utils.validators import Numbers, Ints, Enum, MultiType
+from qcodes.utils.validators import Numbers, Ints, Enum, MultiType, Bool
 
 from functools import partial
 
+
 def parse_output_string(s):
     """ Parses and cleans string outputs of the Keithley """
     # Remove surrounding whitespace and newline characters
@@ -25,49 +26,58 @@ def parse_output_string(s):
 
     return s
 
+
 def parse_output_bool(value):
-    return 'on' if int(value) == 1 else 'off'
+    return True if int(value) == 1 else False
+
 
 class Keithley_2000(VisaInstrument):
     """
     Driver for the Keithley 2000 multimeter.
     """
     def __init__(self, name, address, reset=False, **kwargs):
-        super().__init__(name, address, **kwargs)
+        super().__init__(name, address, terminator='\n', **kwargs)
 
         self._trigger_sent = False
 
+        # Unfortunately the strings have to contain quotation marks and a
+        # newline character, as this is how the instrument returns it.
+        self._mode_map = {
+            'ac current': '"CURR:AC"',
+            'dc current': '"CURR:DC"',
+            'ac voltage': '"VOLT:AC"',
+            'dc voltage': '"VOLT:DC"',
+            '2w resistance': '"RES"',
+            '4w resistance': '"FRES"',
+            'temperature': '"TEMP"',
+            'frequency': '"FREQ"',
+        }
+
         self.add_parameter('mode',
                            get_cmd='SENS:FUNC?',
                            set_cmd="SENS:FUNC {}",
-                           val_mapping={
-                               'ac current': '"CURR:AC"\n',
-                               'dc current': '"CURR:DC"\n',
-                               'ac voltage': '"VOLT:AC"\n',
-                               'dc voltage': '"VOLT:DC"\n',
-                               '2w resistance': '"RES"\n',
-                               '4w resistance': '"FRES"\n',
-                               'temperature': '"TEMP"\n',
-                               'frequency': '"FREQ"\n',
-                           })
+                           val_mapping=self._mode_map)
 
         # Mode specific parameters
         self.add_parameter('nplc',
-                           get_cmd=partial(self._get_mode_param, 'NPLC', float),
+                           get_cmd=partial(self._get_mode_param, 'NPLC',
+                                           float),
                            set_cmd=partial(self._set_mode_param, 'NPLC'),
                            vals=Numbers(min_value=0.01, max_value=10))
 
         # TODO: validator, this one is more difficult since different modes
         # require different validation ranges
         self.add_parameter('range',
-                           get_cmd=partial(self._get_mode_param, 'RANG', float),
+                           get_cmd=partial(self._get_mode_param, 'RANG',
+                                           float),
                            set_cmd=partial(self._set_mode_param, 'RANG'),
                            vals=Numbers())
 
-        self.add_parameter('auto_range',
-                           get_cmd=partial(self._get_mode_param, 'RANG:AUTO', parse_output_bool),
+        self.add_parameter('auto_range_enabled',
+                           get_cmd=partial(self._get_mode_param, 'RANG:AUTO',
+                                           parse_output_bool),
                            set_cmd=partial(self._set_mode_param, 'RANG:AUTO'),
-                           vals=Enum('on', 'off'))
+                           vals=Bool())
 
         self.add_parameter('digits',
                            get_cmd=partial(self._get_mode_param, 'DIG', int),
@@ -75,40 +85,51 @@ def __init__(self, name, address, reset=False, **kwargs):
                            vals=Ints(min_value=4, max_value=7))
 
         self.add_parameter('averaging_type',
-                           get_cmd=partial(self._get_mode_param, 'AVER:TCON', parse_output_string),
+                           get_cmd=partial(self._get_mode_param, 'AVER:TCON',
+                                           parse_output_string),
                            set_cmd=partial(self._set_mode_param, 'AVER:TCON'),
                            vals=Enum('moving', 'repeat'))
 
         self.add_parameter('averaging_count',
-                           get_cmd=partial(self._get_mode_param, 'AVER:COUN', int),
+                           get_cmd=partial(self._get_mode_param, 'AVER:COUN',
+                                           int),
                            set_cmd=partial(self._set_mode_param, 'AVER:COUN'),
                            vals=Ints(min_value=1, max_value=100))
 
-        self.add_parameter('averaging',
-                           get_cmd=partial(self._get_mode_param, 'AVER:STAT', parse_output_bool),
+        self.add_parameter('averaging_enabled',
+                           get_cmd=partial(self._get_mode_param, 'AVER:STAT',
+                                           parse_output_bool),
                            set_cmd=partial(self._set_mode_param, 'AVER:STAT'),
-                           vals=Enum('on', 'off'))
+                           vals=Bool())
 
         # Global parameters
-        self.add_parameter('display',
+        self.add_parameter('display_enabled',
                            get_cmd='DISP:ENAB?',
                            get_parser=parse_output_bool,
                            set_cmd='DISP:ENAB {}',
-                           vals=Enum('on', 'off'))
+                           set_parser=int,
+                           vals=Bool())
 
         self.add_parameter('trigger_continuous',
                            get_cmd='INIT:CONT?',
                            get_parser=parse_output_bool,
                            set_cmd='INIT:CONT {}',
-                           vals=Enum('on', 'off'))
+                           set_parser=int,
+                           vals=Bool())
 
         self.add_parameter('trigger_count',
                            get_cmd='TRIG:COUN?',
+                           get_parser=int,
                            set_cmd='TRIG:COUN {}',
-                           vals=MultiType(Ints(min_value=1, max_value=9999), Enum('inf')))
+                           vals=MultiType(Ints(min_value=1, max_value=9999),
+                                          Enum('inf',
+                                               'default',
+                                               'minimum',
+                                               'maximum')))
 
         self.add_parameter('trigger_delay',
                            get_cmd='TRIG:DEL?',
+                           get_parser=float,
                            set_cmd='TRIG:DEL {}',
                            units='s',
                            vals=Numbers(min_value=0, max_value=999999.999))
@@ -117,15 +138,16 @@ def __init__(self, name, address, reset=False, **kwargs):
                            get_cmd='TRIG:SOUR?',
                            set_cmd='TRIG:SOUR {}',
                            val_mapping={
-                               'immediate': 'IMM\n',
-                               'timer': 'TIM\n',
-                               'manual': 'MAN\n',
-                               'bus': 'BUS\n',
-                               'external': 'EXT\n',
+                               'immediate': 'IMM',
+                               'timer': 'TIM',
+                               'manual': 'MAN',
+                               'bus': 'BUS',
+                               'external': 'EXT',
                            })
 
         self.add_parameter('trigger_timer',
                            get_cmd='TRIG:TIM?',
+                           get_parser=float,
                            set_cmd='TRIG:TIM {}',
                            units='s',
                            vals=Numbers(min_value=0.001, max_value=999999.999))
@@ -142,13 +164,13 @@ def __init__(self, name, address, reset=False, **kwargs):
         self.connect_message()
 
     def trigger(self):
-        if self.trigger_continuous() == 'off':
+        if not self.trigger_continuous():
             self.write('INIT')
             self._trigger_sent = True
 
     def _read_next_value(self):
         # Prevent a timeout when no trigger has been sent
-        if self.trigger_continuous() == 'off' and not self._trigger_sent:
+        if not self.trigger_continuous() and not self._trigger_sent:
             return 0.0
 
         self._trigger_sent = False
@@ -156,11 +178,18 @@ def _read_next_value(self):
         return float(self.ask('DATA:FRESH?'))
 
     def _get_mode_param(self, parameter, parser):
-        cmd = '{}:{}?'.format(self.mode(), parameter)
+        """ Read the current Keithley mode and ask for a parameter """
+        mode = parse_output_string(self._mode_map[self.mode()])
+        cmd = '{}:{}?'.format(mode, parameter)
 
         return parser(self.ask(cmd))
 
     def _set_mode_param(self, parameter, value):
-        cmd = '{}:{} {}'.format(self.mode(), parameter, value)
+        """ Read the current Keithley mode and set a parameter """
+        if isinstance(value, bool):
+            value = int(value)
+
+        mode = parse_output_string(self._mode_map[self.mode()])
+        cmd = '{}:{} {}'.format(mode, parameter, value)
 
-        self.write(cmd)
\ No newline at end of file
+        self.write(cmd)


Test patch: