#-------------------------------------------------------------------------------
# Name:          Controller for the syringe pumps
# Purpose:       Controls the syringe pumps
# Authors:       Eric N
#
# Last Version:  22.09.2022
# Copyright:     (c) Institute of Technical Biocatalysis TUHH
# Licence:       GNU GPL-v3 
#-------------------------------------------------------------------------------
import serial
import time
import math

serial_port = 'COM6' #USB-Anschluss Labtop rechts
factor = 1.05

class pumps_new_era(serial.Serial):
    
    def __init__(self,*args, **kwargs):
        super().__init__(*args, **kwargs)
        self.ser = serial.Serial(serial_port,19200,timeout=.1)

    def readOutput(self,output):
        tempstr = output.split(bytes('\x02','UTF-8'))[1]
        tempstr = tempstr.split(bytes('\x03','UTF-8'))[0]
        str = tempstr.decode('UTF-8')
        return str

    def getState(self,pump):
        self.ser.write(bytes('%iDIR\x0D'%int(pump),'UTF-8'))
        state = self.readOutput(self.ser.readline())[2]
        return state

    def getRate(self,pump):
        rate = ''
        self.ser.write(bytes('%iDIR\x0D'%int(pump),'UTF-8'))
        if self.readOutput(self.ser.readline())[-3:] == 'WDR':
            rate+='-'
        self.ser.write(bytes('%iRAT\x0D'%int(pump),'UTF-8'))
        output = self.readOutput(self.ser.readline())
        rate+=(output[3:-2])
        unit = output[-2:]
        return rate,unit

    def printRate(self,pump):
        temp = self.getRate(pump)
        print(temp[0]+temp[1])

    def setRate(self,pump,rate):
        rate = factor*float(rate)
        print('Pump: '+str(pump))
        print('Rate: '+str(rate))
        if abs(rate)<0.0004:
            return
        #print(rate)
        if rate>9.999:
            raise Exception('Pump Rate set to high (%.3f ml/min) for pump %s'% (rate,pump))
        # TODO: propper check
        direction = 'INF'
        if rate<0:
            direction = 'WDR'
        self.ser.write(bytes('%iDIR%s\x0D'% (int(pump),direction),'UTF-8'))
        self.ser.readline()
        unit = 'MM'
        rate = abs(rate)
        if rate<1:
            unit = 'UM'
            rate = rate*1000
        rate = self.cropNumber(rate,4)
        #print(rate)
        rate = str(rate)+unit
        #print(rate)
        #print(bytes('%iRAT%s\x0D'% (int(pump),rate),'UTF-8'))
        self.ser.write(bytes('%iRAT%s\x0D'% (int(pump),rate),'UTF-8'))
        status = self.readOutput(self.ser.readline())
        #print('Rate set for: ' + status)

    def changeRate(self,pump,change):
        if change>9.999:
            raise Exception('Change set to high (%.3f ml/min) for pump %s'% (change,pump))
        rate, unit = self.getRate(pump)
        print('change %s: %f'% (pump,change))
        if unit == 'UM':
            change = change*1000
        rate = self.cropNumber(float(rate)+change,4)
        self.ser.write(bytes('%iRAT%s\x0D'% (int(pump),rate),'UTF-8'))
        self.ser.readline()

    def setDiameter(self,pump,diameter):
        self.ser.write(bytes('%iDIA%.2f\x0D'% (int(pump),diameter),'UTF-8'))
        self.ser.readline()

    def run(self,pump):
        self.ser.write(bytes('%iRUN\x0D'% int(pump),'UTF-8'))
        self.ser.readline()

    def pause(self,pump):
        state = self.getState(pump)
        if state == 'I' or state == 'W':
            self.ser.write(bytes('%iSTP\x0D'% int(pump),'UTF-8'))
            self.ser.readline()

    def stop(self,pump):
        state = self.getState(pump)
        if state == 'I' or state == 'W':
            self.ser.write(bytes('%iSTP\x0D'% int(pump),'UTF-8'))
            self.ser.readline()
            self.ser.write(bytes('%iSTP\x0D'% int(pump),'UTF-8'))
            self.ser.readline()
            print('Stopped Pump %i'% int(pump))
        elif state == 'P':
            self.ser.write(bytes('%iSTP\x0D'% int(pump),'UTF-8'))
            self.ser.readline()

    def cropNumber(self,number,nPositions):
        # Crops the size of a number (the sum of the positions before and after the decimal point) to nPositions.
        # Works only for numbers with less positions before the decimal point than nPositions''' 

        # checks whether the input is negativ and sets it positiv if so
        isPositiv = True
        if number < 0:
            isPositiv = False
            number = abs(number)
        # gets the decimal power of the input
        decPow = math.floor(math.log10(number))
        if number > 1:
            # checks the input
            if decPow > nPositions-1:
                print('Input number is to large!')
            # changes the decimal power of the input to 1 
            number = number/math.pow(10,decPow)
            # limits the positions after the decimal point to nPositions-1
            number = round(float(number),nPositions-1)
            # changes the number back to its original decimal power and original sign
            number = number*math.pow(10,decPow)
        else:
            # rounds numbers smaller than 1 to the corresponding amount of decimal places
            number = round(float(number),nPositions-1)
        if not isPositiv:
            number = number*(-1)
        #print(number)

        # floats will sometimes add further zeros and another digit to the end of a number.
        # The following will avoid this by formating the float
        if decPow > 0:
            format = "{:."+str(nPositions-1-decPow)+"f}"
        else:
            format = "{:."+str(nPositions-1)+"f}"
        number = float(format.format(number))
        return number

if __name__ == '__main__':
    pump = pumps_new_era()

    
    pump.setRate('01',0.250)
    pump.setRate('02',0.250)
    pump.setRate('03',0.250)
    pump.setRate('04',0.250)
    time.sleep(0.5)
    pump.run('01')
    pump.run('02')
    pump.run('03')
    pump.run('04')
    time.sleep(300)
    pump.stop('01')
    pump.stop('02')
    pump.stop('03')
    pump.stop('04')
    '''
    #pump.ser.readline()
    pump.setRate('02',1.5)
    pump.printRate('02')
    pump.setDiameter('02',15.96)
    pump.run('02')
    pump.changeRate('02',0.01)
    pump.printRate('02')

    time.sleep(5)

    pump.stop('02')
    '''

    '''
    pump.run('01')
    time.sleep(5)
    pump.pause('01')
    time.sleep(5)
    pump.run('01')
    time.sleep(5)
    pump.pause('01')
    time.sleep(5)
    pump.stop('01')
'''
