Repository: microsoft/Qcodes

Issue:
bug: decadac getting and setting voltages fails unpredictably
### Steps to reproduce
1. start Harvard Decadac and  import a qcodes instrument instrument:

```
from qcodes.instrument_drivers.Harvard.Decadac import Decadac
sl0 = Decadac('dec_slot_0', port=5, slot=0)
```

2. run 

`sl0.ch1_voltage(0)`

3. fail as below, seems like its something to do with the mode (which is set in the init but somehow that isn't reliable), It is also not super reliable to fix by setting the mode to 1 but seems to work if I set it to 0 and back to 1 a few times...

### Expected behaviour
should set the voltage

### Actual behaviour
```
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-18-e543db036233> in <module>()
----> 1 sl0.ch1_voltage(0)

a:\qcodes\qcodes\instrument\parameter.py in __call__(self, *args)
    133         else:
    134             if self.has_set:
--> 135                 self.set(*args)
    136             else:
    137                 raise NotImplementedError('no set cmd found in' +

a:\qcodes\qcodes\instrument\parameter.py in _validate_and_sweep(self, value)
    945             e.args = e.args + (
    946                 'setting {} to {}'.format(self.full_name, repr(value)),)
--> 947             raise e
    948 
    949     def set_step(self, step, max_val_age=None):

a:\qcodes\qcodes\instrument\parameter.py in _validate_and_sweep(self, value)
    929             step_clock = time.perf_counter()
    930 
--> 931             for step_val in self._sweep_steps(value):
    932                 self._set(step_val)
    933                 self._save_val(step_val)

a:\qcodes\qcodes\instrument\parameter.py in _sweep_steps(self, value)
    891         state = self._latest()
    892         if state['ts'] is None or state['ts'] < oldest_ok_val:
--> 893             start_value = self.get()
    894         else:
    895             start_value = state['value']

a:\qcodes\qcodes\instrument\parameter.py in get(self)
    826         except Exception as e:
    827             e.args = e.args + ('getting {}'.format(self.full_name),)
--> 828             raise e
    829 
    830     def _valmapping_get_parser(self, val):

a:\qcodes\qcodes\instrument\parameter.py in get(self)
    821     def get(self):
    822         try:
--> 823             value = self._get()
    824             self._save_val(value)
    825             return value

a:\qcodes\qcodes\utils\command.py in __call__(self, *args)
    175             raise TypeError(
    176                 'command takes exactly {} args'.format(self.arg_count))
--> 177         return self.exec_function(*args)

a:\qcodes\qcodes\instrument_drivers\Harvard\Decadac.py in _getvoltage(self, channel)
    129         response = temp[::-1]
    130 
--> 131         rawvoltage = self._code2voltage(response, channel)
    132         actualvoltage = rawvoltage - self._offsets[channel]
    133 

a:\qcodes\qcodes\instrument_drivers\Harvard\Decadac.py in _code2voltage(self, code, channel)
    232         """
    233 
--> 234         code = float(code)
    235         translationdict = {1: lambda x: (x+1)*20/2**16-10,
    236                            2: lambda x: (x+1)*10/2**16,

ValueError: ("could not convert string to float: '0! M 0'", 'getting dec_slot_0_ch1_voltage', 'setting dec_slot_0_ch1_voltage to 0')
```

@WilliamHPNielsen @giulioungaretti 


Discussion:
@nataliejpg that string you get looks like a response from the decadac upon setting the mode.
Looking into the driver, no response is read when setting the mode.

You could try setting the mode, and then calling
`dac.visa_handle.read()`
maybe a few times...

thanks @MerlinSmiles , I'll put that in the my initialisation wrapper and see if it helps, perhaps a fix in the driver where it does that after  setting the mode (as part of set_cmd) would help. Also I found another bug which is that if you initialise the instrument and then ask what the voltages are they do not correspond to what you last set them to which is maybe because the get isn't actually querying the instrument (I don't know) but at least a warning on this would be good, do you think this is the case @WilliamHPNielsen? I'm right now in the middle of measuring so am disinclined to disconnect and check what they are actually outputting!
ok tried that and answer is to run `visa_handle.read()` twice (but no more because it times out if you try more times than it has messages)
actually the above is only true sometimes. Also I now cannot get the instrument to output anything even with turning mode to 1 a lot of times. @MerlinSmiles are you (or anyone else?) actually using this instrument and driver reliably?
@nataliejpg yes it will time out, thats because visa will read until it finds a line termination character.
If that reading is helping you, it should be included in that set_mode command, but it would be strange if you'd have to call it more than once, but at every time that mode is set...

For your other bug, the get is asking the instrument, just in a funny way of handling the response (with things like inverting strings, and searching from the back 😮) , which seems to be a double response.
when I compare that to our matlab driver, we select the channel, but that also reads the instrument response. Basically every command also generates a response, which one needs to read, otherwise it will mess up your next reading.

For your last question, No, I dont have that instrument installed, but spent quite some time on our 21bit resolution matlab driver a few years ago. Which worked just fine.
https://github.com/qdev-dk/matlab-qd/tree/master/%2Bqd/%2Bins
look especially in the `DecaDACChannel.m` and `HRDecaDACChannel.m`
@WilliamHPNielsen this driver is putting the decadac in mode 1, which is a high resolution mode, and effectively disables output on channels 3,4,7,8,... aaaand it is not handling that extra resolution?
There is a manual on the qdev wiki, and those matlab drivers for copying functionality...

Resolving patch:
diff --git a/qcodes/instrument_drivers/Harvard/Decadac.py b/qcodes/instrument_drivers/Harvard/Decadac.py
--- a/qcodes/instrument_drivers/Harvard/Decadac.py
+++ b/qcodes/instrument_drivers/Harvard/Decadac.py
@@ -90,15 +90,23 @@ def __init__(self, name, port, slot, timeout=2, baudrate=9600,
 
         self.add_parameter('mode',
                            label='Output mode',
-                           set_cmd='B {}; M {};'.format(self.slot, '{}'),
-                           vals=vals.Enum(0, 1),
+                           set_cmd=partial(self._setmode, self.slot),
+                           vals=vals.Enum(0, 2),
                            docstring="""
                                      The operational mode of the slot.
-                                     0: output off, 1: output on.
+                                     0: output off,
+                                     2: 4-channel (low res) mode.
                                      """)
 
         # initialise hardware settings
-        self.mode.set(1)
+        self.mode.set(2)
+
+    def _setmode(slot, mode):
+        """
+        set_cmd for the mode parameter
+        """
+        self.visa_handle.write('B {}; M {};'.format(slot, mode)
+        self.visa_handle.read()
 
     def _getoffset(self, n):
         return self._offsets[n]
@@ -256,4 +264,3 @@ def _voltage2code(self, voltage, channel):
                            3: lambda x: 2**16/10*(x-2**-16+10)}
         voltage_float = translationdict[self._voltranges[channel]](voltage)
         return str(int(voltage_float))
-


Test patch: