'''
KEITHLEY 224 instrument driver
TODO: SRQ decoding
https://www.eevblog.com/forum/testgear/keithley-224-python-code/
'''
import pyvisa
import enum

class Readout_Values:
    def __init__(self):
        self.raw = ""
        self.current = 0.0
        self.overcompliance = False
        self.voltage = 0.0
        self.time = 0.0

# Range Commands
RANGE_LIST = (
    'R0',
    'R5',
    'R6',
    'R7',
    'R8',
    'R9',
    )

def get_available_devices():
    rm = pyvisa.ResourceManager()
    devices = rm.list_resources()
    rm.close()
    return devices

def _decode_values(rawdata):
    splitted = rawdata.split(',')
    readout = Readout_Values()
    readout.raw = rawdata
    for element in splitted:
        if 'DCI' in element:
            if element[0] is 'O':
                readout.overcompliance = True
            readout.current = float(element[4:])
        if 'V' in element:
            readout.voltage = float(element[1:])
        if 'W' in element:
            readout.time = float(element[1:])
    return readout

def _format_e(n):
    a = '%E' % n
    return a.split('E')[0].rstrip('0').rstrip('.') + 'E' + a.split('E')[1]

class KEITHLEY_224(object):

    class Ranges(enum.Enum):
        AUTO = 0
        MAN_20uA = 1
        MAN_200uA = 2
        MAN_2mA = 3
        MAN_20mA = 4
        MAN_1m01A = 5

    def __init__(self, address):
        self._address = address
        self._rm = pyvisa.ResourceManager()
        self._inst = self._rm.open_resource(address)
        self._range = self.Ranges.AUTO
        self.voltage = 3.0
        self.current = float(1e-06)
        self.time = 0.05
        self.operate = False
        self._inst.control_ren(1)  ## +++++++++++++++++++++++

    def __del__(self):
        self.operate = False
        self._inst.control_ren(0)  ## +++++++++++++++++++++++
        self._rm.close()

    def get_measurement(self):
        self._inst.timeout = 1000
        result = _decode_values(self._inst.read())
        return result

    @property
    def range(self):
        return self._range

    @range.setter
    def range(self, range):
        if not isinstance(range, self.Ranges):
            raise TypeError('mode must be an instance of Ranges Enum')
        self._range = range
        self._inst.write(RANGE_LIST[self._range.value]+'X')

    @property
    def voltage(self):
        return self._voltage

    @voltage.setter
    def voltage(self, voltage):
        if (voltage < 1) or (voltage > 105):
            raise ValueError('voltage limits: 1 to 105')
        self._voltage = voltage
        self._inst.write('V'+ _format_e(voltage)+'X')

    @property
    def current(self):
        return self._current

    @current.setter
    def current(self, current):
        if (current < -0.101) or (current > 0.101):
            raise ValueError('current limits: +/- 0.101')
        self._current = current
        self._inst.write('I' + _format_e(current) + 'X')
#        print('I' + _format_e(current) + 'X')  ## +++++++++++++++++++++++

    @property
    def time(self):
        return self._time

    @time.setter
    def time(self, time):
        if (time < 0.05) or (time > 0.9999):
            raise ValueError('time limits: 0.05 to 0.9999 sec')
        self._time = time
        self._inst.write('W' + _format_e(time) + 'X')

    @property
    def operate(self):
        return self._operate

    @operate.setter
    def operate(self, operate):
        if type(operate) is not type(True):
            raise ValueError('operate takes a bool value')
        self._operate = operate
        if operate is True:
            self._inst.write('F1X')
        else:
            self._inst.write('F0X')


# testing the code
if __name__ == '__main__':
    import numpy
    import time

##    instrument = KEITHLEY_224("GPIB0::15::INSTR")
##
##    meas = instrument.get_measurement()
##    print('Raw data: ' + str(meas.raw))
##    print('Current: ' + str(meas.current))
##    print('Overcompliance: ' + str(meas.overcompliance))
##    print('Voltage: ' + str(meas.voltage))
##    print('Time: ' + str(meas.time))
##
##    instrument.operate = True
##    instrument.voltage = 15
##    instrument.time = 0.5
##
##    time.sleep(5)
##    
##    for i in numpy.arange(0.001,0.015,0.001):
##        instrument.current = i
##        time.sleep(5.1)
##
##        meas = instrument.get_measurement()
##        print('Raw data: ' + str(meas.raw))
##        print('Current: ' + str(meas.current))
##        print('Overcompliance: ' + str(meas.overcompliance))
##        print('Voltage: ' + str(meas.voltage))
##        print('Time: ' + str(meas.time) + '\n\n\n')
##
##    del instrument

    instrument = KEITHLEY_224("GPIB0::15::INSTR")

    while True:
        try:
            pass
        except:
            del instrument