'''
Created on Jun 14, 2012

@author: manuel
'''
from PhysioMonitor import SyringePump
import serial
import time
from PhysioMonitor.SyringePump import valueOORException, unknownCommandException,\
    pumpNotRunningException, pumpNotStoppedException

class Model11plusPump(SyringePump):    
    __PROMPT_STP  = '\r\n:'
    __PROMPT_FWD  = '\r\n>'
    __PROMPT_REV  = '\r\n<'
    __ANS_OOR     = '\r\nOOR'
    __ANS_UNKNOWN = '\r\n?'
    __CMD_RUN     = 'RUN\r'
    __CMD_STOP    = 'STP\r'
    __CMD_CLEAR_VOLUME = 'CLV\r'
    __CMD_CLEAR_TARGET = 'CLT\r'
    __CMD_REV     = 'REV\r'
    __CMD_SET_DIAMETER = 'MMD %5.4f\r'
    __CMD_SET_FLOW_UL_H = 'ULH %5.4f\r'
    __CMD_SET_FLOW_UL_MIN = 'ULM %5.4f\r'
    __CMD_SET_FLOW_ML_H = 'MLH %5.4f\r'
    __CMD_SET_FLOW_ML_MIN = 'MLM %5.4f\r'
    __CMD_SET_RATE = [__CMD_SET_FLOW_ML_H,__CMD_SET_FLOW_ML_MIN,__CMD_SET_FLOW_UL_H,__CMD_SET_FLOW_UL_MIN]
    __CMD_SET_TARGET = 'MLT %5.4f\r'
    __CMD_GET_DIAMETER = 'DIA\r'
    __CMD_GET_RATE = 'RAT\r'
    __CMD_GET_UNITS = 'RNG\r'
    __CMD_GET_VOLUME = 'VOL\r'
    __CMD_GET_VERSION = 'VER\r'
    __CMD_GET_TARGET = 'TAR\r'
    __CMD_QUIT_REMOTE = 'KEY\r'
    
    def __init__(self, inPort=0, inBaudRate=9600):
        self.port = inPort
        self.baudRate = inBaudRate
        
        self.serial = None
        self.serial = serial.Serial(self.port, self.baudRate,
            bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE,
            stopbits=serial.STOPBITS_TWO,
            timeout=0)
        self.serial.flush()
        self.serial.flushInput()
        self.serial.flushOutput()
        self.currUnits = 0

    def __del__(self):
        if self.serial is not None:
            self.sendCommand(self.__CMD_QUIT_REMOTE)
            self.serial.flush()
            self.serial.flushInput()
            self.serial.flushOutput()
            self.serial.close()


    def getInfo(self):
        return self.getStatus()

    def sendCommand(self, inCommand):
        self.serial.flush()
        self.serial.flushInput()
        self.serial.flushOutput()
#        print "sending command \"%s\"..."%inCommand #DEBUG
        self.serial.write(inCommand)
        time.sleep(0.3)
        nbChar = self.serial.inWaiting()
        ans = str(self.serial.read(nbChar))
        self.serial.flush()
        self.serial.flushInput()
        self.serial.flushOutput()
#        print "reading %d bytes in respone: \"%s\""%(nbChar,repr(ans)) #DEBUG
        if ans.startswith(self.__ANS_OOR):
#            print "OOR Error encountered!" #DEBUG
            raise valueOORException()
        elif ans.startswith(self.__ANS_UNKNOWN):
#            print "Unknown command Error encountered!" #DEBUG
            raise unknownCommandException()
        else:
            return ans

    def stripPrompt(self, inString):
        inString = inString.replace(self.__PROMPT_STP,'')
        inString = inString.replace(self.__PROMPT_FWD,'')
        inString = inString.replace(self.__PROMPT_REV,'')
        inString = inString.strip()
        return inString

    def run(self):
        self.sendCommand(self.__CMD_RUN)
        ans = self.getDirection()
        if ans == "FWD" or ans == "REV":
            pass
        else:
            raise pumpNotRunningException()

    def start(self):
        self.run()
        
    def stop(self):
        self.sendCommand(self.__CMD_STOP)
        ans = self.getDirection()
        if ans == "STOPPED":
            pass
        else:
            raise pumpNotStoppedException()

    def clearAccumulatedVolume(self):
        self.sendCommand(self.__CMD_CLEAR_VOLUME)

    def clearTargetVolume(self):
        self.sendCommand(self.__CMD_CLEAR_TARGET)

    def reverse(self):
        self.sendCommand(self.__CMD_REV)

    def setSyringeDiameter(self, inDiameter):
        self.sendCommand(self.__CMD_SET_DIAMETER%inDiameter)

    def setRate(self, inValue, inUnits):
        if inValue<=0:
            raise valueOORException("Rate must be a positive value")
        if inUnits<0 or inUnits>len(self._units)-1:
            raise valueOORException("Units must be an integer between %d and %d"%(0,len(self._units)-1))
        self.sendCommand((self.__CMD_SET_RATE[self.currUnits])%inValue) #FIXME: this needs fixin'

    def setTargetVolume(self, inValue):
        self.sendCommand(self.__CMD_SET_TARGET%inValue)

    def getDiameter(self):
        diameter = self.sendCommand(self.__CMD_GET_DIAMETER)
        diameter = self.stripPrompt(diameter)
        return float(diameter)

    def getRate(self):
        rate = self.sendCommand(self.__CMD_GET_RATE)
        rate = self.stripPrompt(rate)
        return float(rate)

    def getAccumulatedVolume(self):
        volume = self.sendCommand(self.__CMD_GET_VOLUME)
        volume = self.stripPrompt(volume)
        return float(volume)

    def getVersion(self):
        version = self.sendCommand(self.__CMD_GET_VERSION)
        version = self.stripPrompt(version)
        return version

    def getTargetVolume(self):
        target = self.sendCommand(self.__CMD_GET_TARGET)
        target = self.stripPrompt(target)
        return float(target)

    def getUnits(self):
        units = self.sendCommand(self.__CMD_GET_UNITS)
        units = self.stripPrompt(units)
        if units=='ML/HR':
            return 'ML/HR'
        elif units=='ML/MIN':
            return 'ML/MIN'
        elif units=='UL/HR':
            return 'UL/HR'
        elif units=='UL/MIN':
            return 'UL/MIN'
        else:
            raise LookupError
    
    def getDirection(self):
        _ = self.sendCommand("\r")
        _ = self.sendCommand("\r")
        ans = self.sendCommand("\r")
        if ans == self.__PROMPT_FWD:
            return "FWD"
        elif ans == self.__PROMPT_REV:
            return "REV"
        elif ans == self.__PROMPT_STP:
            return "STOPPED"

    def getStatus(self):
        port = self.serial.portstr
        version = self.getVersion()
        diameter = self.getDiameter()
        rate = self.getRate()
        units = self.getUnits()
        target = self.getTargetVolume()
        volume = self.getAccumulatedVolume()
        direction = self.getDirection()
        return "Syringe Pump v.%s (%s) {direction: %s, diameter: %.4f mm, rate: %.4f %s, accumulated volume: %.4f, target volume: %.4f}"%(version,port,direction,diameter,rate,units,volume,target)
        
    def printStatus(self):
        print self.getStatus()