Repository: microsoft/Qcodes

Issue:
Keithley 2400 does not get added to the station cleanly
The ":read:" command and possibly others does not work when output is off but fails with an error. This is called when getting volt and current are snapshotted 

We should wrap these calls in checking that output is off


Discussion:
I ran into this. For now the easy workaround is to add `keithley.output.set(1)` before adding it to a station or reading any values. Otherwise you'll see error 803 and it'll be borked until you restart it.

Resolving patch:
diff --git a/qcodes/instrument_drivers/tektronix/Keithley_2400.py b/qcodes/instrument_drivers/tektronix/Keithley_2400.py
--- a/qcodes/instrument_drivers/tektronix/Keithley_2400.py
+++ b/qcodes/instrument_drivers/tektronix/Keithley_2400.py
@@ -34,18 +34,31 @@ def __init__(self, name, address, **kwargs):
                            label='Current Compliance')
 
         self.add_parameter('volt',
-                           get_cmd=':READ?',
+                           get_cmd=self._get_read_output_protected,
                            get_parser=self._volt_parser,
                            set_cmd=':SOUR:VOLT:LEV {:.8f}',
                            label='Voltage',
-                           unit='V')
+                           unit='V',
+                           docstring="Sets voltage in 'VOLT' mode. "
+                                     "Get returns measured voltage if "
+                                     "sensing 'VOLT' otherwise it returns "
+                                     "setpoint value. "
+                                     "Note that it is an error to read voltage with "
+                                     "output off")
 
         self.add_parameter('curr',
-                           get_cmd=':READ?',
+                           get_cmd=self._get_read_output_protected,
                            get_parser=self._curr_parser,
                            set_cmd=':SOUR:CURR:LEV {:.8f}',
                            label='Current',
-                           unit='A')
+                           unit='A',
+                           docstring = "Sets current in 'CURR' mode. "
+                                        "Get returns measured current if "
+                                        "sensing 'CURR' otherwise it returns "
+                                        "setpoint value. "
+                                        "Note that it is an error to read current with "
+                                        "output off")
+
 
         self.add_parameter('mode',
                            vals=Enum('VOLT', 'CURR'),
@@ -77,10 +90,32 @@ def __init__(self, name, address, **kwargs):
                            label='Current integration time')
 
         self.add_parameter('resistance',
-                           get_cmd=':READ?',
+                           get_cmd=self._get_read_output_protected,
                            get_parser=self._resistance_parser,
                            label='Resistance',
-                           unit='Ohm')
+                           unit='Ohm',
+                           docstring="Measure resistance from current and voltage "
+                                     "Note that it is an error to read current "
+                                     "and voltage with output off")
+
+    def _get_read_output_protected(self) -> str:
+        """
+        This wrapper function around ":READ?" exists because calling
+        ":READ?" on an instrument with output disabled is an error.
+        So first we check that output is on and if not we return
+        nan for volt, curr etc.
+        """
+        output = self.output.get_latest()
+        if output is None:
+            # if get_latest returns None we have
+            # to ask the instrument for the status of output
+            output = self.output.get()
+
+        if output == 1:
+            msg = self.ask(':READ?')
+        else:
+            raise RuntimeError("Cannot perform read with output off")
+        return msg
 
     def _set_mode_and_sense(self, msg):
         # This helps set the correct read out curr/volt
@@ -115,4 +150,5 @@ def _curr_parser(self, msg):
 
     def _resistance_parser(self, msg):
         fields = [float(x) for x in msg.split(',')]
-        return fields[0]/fields[1]
+        res = fields[0] / fields[1]
+        return res


Test patch: