#!/usr/bin/env python

import os
import os.path
import sys
import numpy as np

import keithley.keithley6514 as multi
import time
import types
import string

# from multiprocessing import Process
from threading import Thread

def auto_method(self, command):
    f = string.Formatter()
    g = f.parse(command)
    fields = [entry[1] for entry in g if entry[1] not in ['', 'mode']]
    def meth(self, keys={}):
        if isinstance(keys, dict):
            self.settings.update(keys)
        elif len(fields) == 1:
            self.settings[fields[0]] =keys
        else:
            raise ValueError('Command needs the following values: %s' % str(fields))
        self.handle.write(command.format(**self.settings))
    meth.__doc__= command
    return types.MethodType(meth, self)

def auto_getter(self, name):
    def meth(self):
        return self.settings[name]
    return types.MethodType(meth, self)

class Keithley6514(object):
    commands = {'reset':"*RST",
                'zch_on': "SYST:ZCH ON",
                'zch_off': "SYST:ZCH OFF",
                'set_nsamples': 'TRIG:COUN {nsamples:d}',
                'set_rate': ':SENS:{mode}:NPLC {nplc:d}',
                'set_range': '{mode}:RANG {range}',
                'set_func': "FUNC '{mode}:DC'", 
                'disp_off': ':DISP:ENAB OFF',
    }
    
    def __init__(self, dev):
        self.dev = dev
        k = self.handle = multi.Multimeter(dev)
        k.open()
        k.write("*IDN?")
        self.id = k.read()
        k.reset()
        self.settings = {'mode':'CURR',
                         'nplc':1,
                         'range':'2e-6',
                         'median_filter':'OFF',
                         'digital_filter':'OFF',
                         'nsamples':1,
        }
        for name in self.commands:
            setattr(self, name, auto_method(self, self.commands[name]))

        for name in self.settings:
            setattr(self, 'get_'+name, auto_getter(self, name))
            
    def get_settings(self):
        return self.settings
    
    def setup(self, nsamples=1, krange=2.E-7):
        self.reset()
        time.sleep(1)
        self.zch_on()
        self.set_func({'mode':'CURR'})
        self.set_range({'range':krange})
        self.set_nsamples({'nsamples':nsamples})
        self.set_rate({'nplc':1})
        self.disp_off()
        self.zch_off()

    def read(self, repeats=1):
        allvalues = []
        allKtimes = []
        allKflags = []
        K = self.handle
        for i in range(repeats):
            time.sleep(0.1)
            K.write("READ?")
            data_str = K.read()
            parts = data_str.split(b',')
            print(parts)
            if (len(parts) != 3*self.settings['nsamples']):
                print("Incorrect data length. stop.", file=sys.stderr)
                return None

            values_str = parts[0::3]
            # print "values =", values_str
            allvalues.extend([float(v) for v in values_str])
            # print allvalues

            Ktimes_str = parts[1::3]
            # print "Ktimes = ", Ktimes_str
            allKtimes.extend([float(v) for v in Ktimes_str])
            # print allKtimes

            Kflags_str = parts[2::3]
            # print "Kflags = ", Kflags_str
            allKflags.extend([int(float(v)) for v in Kflags_str])
            # print allKflags

        self.result = allvalues, allKtimes, allKflags

        return self.result

    def non_blocking_read(self, repeats=1):        
        # self.p = Process(target=self.read, args=(repeats,))
        # self.p.start()
        self.p = Thread(target=self.read, args=(repeats,))
        self.p.start()
        
        
    def join(self):
        self.p.join()
        self.p = None
        return self.result

    
class Keithley6485(Keithley6514):

    def read(self, repeats=1):
        allvalues = []
        allKtimes = []
        allKflags = []
        K = self.handle
        for i in range(repeats):
            time.sleep(0.1)
            K.write("READ?")
            data_str = K.read()
            parts = data_str.split(',')
            print(parts)
            if (len(parts) != 3*self.samples):
                print("Incorrect data length. stop.", file=sys.stderr)
                return None

            values_str = parts[0::3]
            # print "values =", values_str
            allvalues.extend([float(v[:-1]) for v in values_str])
            # print allvalues

            Ktimes_str = parts[1::3]
            # print "Ktimes = ", Ktimes_str
            allKtimes.extend([float(v) for v in Ktimes_str])
            # print allKtimes

            Kflags_str = parts[2::3]
            # print "Kflags = ", Kflags_str
            allKflags.extend([int(float(v)) for v in Kflags_str])
            # print allKflags

        self.result = allvalues, allKtimes, allKflags
        
        return self.result

        
    
    
if __name__ == '__main__':
    pass
    #    k = Keithley('/dev/ttyUSB0')
#    k = Keithley('/dev/ttyUSB2')    
#    k.setup(samples=20)
#    v, t, f = k.read()
    
