#! /usr/bin/env python
##########################################
##    MICRO 2T PC Exchange Version 2.B  ##
##    World Precision Instruments, LLC  ##
##                                      ##
##          www.wpiinc.com              ##
##                                      ##
## Requires pySerial load module        ##
## Install with pip:                    ##
## python -m pip install pyserial       ##  
##                                      ##
## Requires keyboard load module        ##
## Install with pip:                    ##
## python -m pip install keyboard       ##
##                                      ##
## World Precision Instruments, LLC     ##
## provides this software "as is",      ## 
## without any warranty or guarentee    ##
## of any kind or statement of fitness  ##
## of use.                              ##
## (c) WORLD PRECISION INSTRUMENTS, LLC ##
##       AUG 2020                       ##
##                                      ##
##########################################

import serial, sys, time, glob
import keyboard
from os import name,system

global zcom,Config,ConfigOut1,ConfigOut2,chgConfig,RunPSet,LogDump
#
#zcom: FOUND SERIAL PORT FOR USB COMMUNICATIONS

#Config: Command parameters
#   Config[0] #BEEP (Acknowledge):"F5\r\n"#
#   Config[1] #DISPLAY TARGET VOLUME:"?V\r\n"
#   Config[2] #DISPLAY VOLUME COUNTER:"?C\r\n" #
#   Config[3] #DISPLAY MOTOR DRIVE OPTION:"?B\r\n"#
#   Config[4] #DISPLAY MOTOR COUNTER MODE:"?E\r\n"#
#   Config[5] #DISPLAY PUMP MODE:"?M\r\n"#
#   Config[6] #DISPLAY SYRINGE TYPE:"?S\r\n" #
#   Config[7] #DISPLAY DIRECTION TYPE:"?D\r\n"#
#   Config[8] #DISPLAY RATE UNITS:"?U\r\n"#
#   Config[9] #DISPLAY RUN MODE:"?G\r\n#
Config=["F5","?V","?C","?B","?E","?M","?S","?D","?U","?G"]

#ConfigOut1: CURRENT CONFIGURATION FOR PUMP #1
#ConfigOut2: CURRENT CONFIGURATION FOR PUMP #2
#   ConfigOutx[0] #RESERVED: NOT USED#
#   ConfigOutx[1] #TARGET VOLUME     #
#   ConfigOutx[2] #VOLUME COUNTER    #
#   ConfigOutx[3] #MOTOR DRIVE OPTION#
#   ConfigOutx[4] #MOTOR COUNTER MODE#
#   ConfigOutx[5] #PUMP MODE         #
#   ConfigOutx[6] #SYRINGE TYPE      #
#   ConfigOutx[7] #PUMP DIRECTION    #
#   ConfigOutx[8] #RATE UNITS        #
#   ConfigOutx[9] #CURRENT RUN MODE  #
ConfigOut1=["","","","Motor Drive Option:","Motor Counter Mode:","Pump ","Syringe ","","","",""]
ConfigOut2=["","","","Motor Drive Option:","Motor Counter Mode:","Pump ","Syringe ","","","",""]

#cngConfig: CONFIG TO CHANGE
#   chgConfig[0] # RESERVED: NOT USED #
#   chgConfig[1] # SYRINGE TYPE       #
#   chgConfig[2] # GROUP MODE         #
#   chgConfig[3] # ACTIVE PUMP        #
#   chgConfig[4] # RATE UNITS         #
#   chgConfig[5] # RATE               #
#   chgConfig[6] # VOLUME TARGET      #
#   chgConfig[7] # PUMP DIRECTION     #
#   chgConfig[8] # VOLUME COUNTER     #
#   chgConfig[9] # MOTOR DRIVE OPTION #
#   chgConfig[10]# RESERVED: NOT USED #
chgConfig=[""]*10

#RunPSet: SETTINGS FOR AUTOMATED PROGRAM # 
#RunPSet[0]: ACTIVE PUMP                 #
#RunPSet[1]:(I)NFUSE (W)ITHDRAW          #
#RunPSet[2]: AMOUNT(nL)                  #
#RunPSet[3]: SPEED                       # 
#RunPSet[4]: UNITS nL/SEC or nL/MIN      #
#RunPSet[5]: PAUSE (x/100) SECS          #
#RunPSet[6]: # TIMES TO REPEAT, 0=NONE)  # 
RunPSet=["1","INFUSE","100","20","SEC","200","0"]

#LogDump: DUMP FILE FOR ERROR EXCEPTION
LogDump=[" "]*20
LogDump[0]="**********************************************"


#########################################
#    START FUNCTION DESCRIPTIONS        #
#########################################
def WriteLog(flag):
    # THIS FILE WRITES A LOG FILE IF AN ERROR CONDITION IS TRAPPED
    global LogDump
    if(flag==0):
        LogDump=[" "]*20
        LogDump[0]="**********************************************"
    if(flag==1):
        td=time.strftime("%Y-%b-%d %H:%M:%S",time.localtime())
        try:
            log=open("error.log","a")
            log.write(LogDump[0]+"\n")
            log.write(td)
            for i in range(10):log.write(LogDump[i]+"\n")
            log.write(LogDump[0]+"\n")
            log.close()
        except:
            print(" #! ERROR LOG EXCEPTION !#")
    return
#END DEF

 
def ClrScrn():
    # THIS FUNCTION CLEARS THE CONSOLE
    try: 
        if name == 'posix':
            _=system('clear')# for mac and linux (os.name is'posix')
        else:
            _=system('cls') # for windows (os.name is typically 'nt')
    except:
        print("\n")
    return
#END DEF

def serial_ports():
    # THIS FUNCTION FINDS SERIAL PORTS
    # WORKS ON WIN_PC, LINUX, MAC_OS
    # RAISES UNSUPPORTED ERROR IF NOT FOUND
    
    if sys.platform.startswith('win'):
        ports = ['COM%s' % (i + 1) for i in range(256)]
    elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
        # this excludes your current terminal "/dev/tty"
        ports = glob.glob('/dev/tty[A-Za-z]*')
    elif sys.platform.startswith('darwin'):
        ports = glob.glob('/dev/tty.*')
    else:
        raise EnvironmentError('Unsupported platform')

    result = []
    for port in ports:
        try:
            s = serial.Serial(port)
            s.close()
            result.append(port)
        except (OSError, serial.SerialException):
            pass
    return result
#END DEF

def GetCnx(results):
    # THIS FUNCTION GETS AND TESTS THE SERIAL (USB TO MC2T)CONNECTION
    
    global zcom
    rx=False
    print("SERIAL PORTS FOUND:",results)
    print("TESTING PORTS FOR MICROTOUCH-2T ...",end='')
    for i in range(256):
        try:
            if(results[i]=="COM1" or results[i]=="COM2"):
                pass
            else:
                portstr=str(results[i])
                ser=serial.Serial(portstr,baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=None)
                wxx="F5 \r\n"  # this causes MC2T to Beep if connection is established. 
                wx3=wxx.encode()
                q=ser.write(wx3)
                time.sleep(0.5)
                
                qq=ser.read(ser.in_waiting).decode()
                if len(qq)>5:
                    print( " FOUND MC2T PORT AS: ",portstr)
                    rx=True
                    zcom=portstr
                    ser.close()
                    return(rx)
                    
                #q=ser.write(wx3)
                ser.close()
        except:
            pass
    return(rx)
#END DEF

def SendCmd(Tag2):
    # THIS FUNCTION SENDS ONE COMMAND TO PUMP. MAKE SURE YOU ARE TALKING TO THE ACTIVE PUMP!#
    global zcom, LogDump
    
    with serial.Serial(zcom, baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=None) as ser: # open serial port and get configuration
        try:
            ser.flush()
            Tag=Tag2+"\r\n"
            tagx=Tag.encode()
            q=ser.write(tagx)
            time.sleep(0.2)
            ChkOK=ser.read(ser.in_waiting).decode().split('\n')
            ser.close()
        except:
            print( "!# ERRROR: PUMP COMMUNICATION ERROR #!")
            ser.close()
            ChkOK=["!#ERROR#!"]
            WriteLog(0)
            LogDump[1]="SendCmd"
            LogDump[2]="ZCOM:"+str(zcom)
            LogDump[3]="tagx:"+tagx
            WriteLog()
    return ChkOK
#END DEF

def GetConfig(flg):
    global LogDump
    # THIS FUNCTION GETS THE BLOCK OF CURRENT CONFIGURATION DATA FROM PUMP#1 AND PUMP#2
    # NOTE: PUMPS DO NOT HAVE TO BE ACTUALLY INSTALLED. THIS IS JUST MC2T SETTINGS
    # flg 0 = no output, flg 1 = console and file  print output 
    global zcom,Config,ConfigOut1,ConfigOut2
    # ConfigOut1 AND ConfigOut2 NEED TO BE RESET IN EVERY CALL OF THE ROUTINE
    ConfigOut1=["","","","Motor Drive Option:","Motor Counter Mode:","Pump ","Syringe ","","","",""]
    ConfigOut2=["","","","Motor Drive Option:","Motor Counter Mode:","Pump ","Syringe ","","","",""]
    #BP=Config[0] #BEEP (Acknowledge):"F5\r\n"#
    #TV=Config[1] #DISPLAY TARGET VOLUME:"?V\r\n" #
    #VC=Config[2] #DISPLAY VOLUME COUNTER:"?C\r\n" #
    #MD=Config[3] #DISPLAY MOTOR DRIVE OPTION:"?B\r\n"#
    #MC=Config[4] #DISPLAY MOTOR COUNTER MODE:"?E\r\n"#
    #PM=Config[5] #DISPLAY PUMP MODE:"?M\r\n"#
    #ST=Config[6] #DISPLAY SYRINGE TYPE:"?S\r\n" #
    #DT=Config[7] #DISPLAY DIRECTION TYPE:"?D\r\n"#
    #UR=Config[8] #DISPLAY RATE UNITS:"?U\r\n"#
    #RM=Config[9] #DISPLAY RUN MODE:"?G\r\n#
    td=time.strftime("%Y-%b-%d %H:%M:%S",time.localtime())
    LinePrint="                                                              *"
    try:
        with serial.Serial(zcom, baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=None) as ser: # open serial port and get configuration
            ser.flush()
            # Get Syringe #1 Configuration #
            wx="L1\r\n"
            wxx=wx.encode()
            q=ser.write(wxx)
            time.sleep(0.2)
            ChkOK=ser.read(ser.in_waiting).decode().split('/n')
            for j in range(0,10):
                wx=Config[j]+"\r\n"
                wxx=wx.encode()
                q=ser.write(wxx)
                time.sleep(0.2)
                xx=ser.read(ser.in_waiting).decode().split('\n')
                ConfigOut1[j]=ConfigOut1[j]+xx[0]
                
            # Get Syringe #2 Configuration #
            wx="L2\r\n"
            wxx=wx.encode()
            q=ser.write(wxx)
            time.sleep(0.2)
            ChkOK=ser.read(ser.in_waiting).decode().split('/n')
            for j in range(0,10):
                wx=Config[j]+"\r\n"
                wxx=wx.encode()
                q=ser.write(wxx)
                time.sleep(0.2)
                xx=ser.read(ser.in_waiting).decode().split('\n')
                ConfigOut2[j]=ConfigOut2[j]+xx[0] 

            # RESET THE DEFALT PUMP TO PUMP #1
            wx="L1\r\n"
            wxx=wx.encode()
            q=ser.write(wxx)
            time.sleep(0.2)
            ChkOK=ser.read(ser.in_waiting).decode().split('/n')
            ser.close()    
            if(flg==1):
                # PRINT OUTPUT FILE #
                ClrScrn()
                print("\n")
                print(" **********************************************************************")
                print(" *                  CURRENT CONFIGURATION                             *")
                print(" **********************************************************************")
                print(" *    "+td+LinePrint[:-(len(td)-1)]+"*")
                print(" **********************************************************************")
                print(" *                  ##   SYRINGE #1:  ##                              *")
                print(" **********************************************************************")
                for j in range (1,10):
                    lx=len(ConfigOut1[j])
                    print(" *     "+ConfigOut1[j]+LinePrint[:-lx]+"*")
                print(" **********************************************************************")    
                print(" **********************************************************************")    
                print(" *                  ##   SYRINGE #2:  ##                              *")
                print(" **********************************************************************")
                for j in range (1,10):
                    lx=len(ConfigOut2[j])
                    print(" *     "+ConfigOut2[j]+LinePrint[:-lx]+"*")
                print(" **********************************************************************")
                print("\n\n")
                    
                # WRITE OUTPUT FILE #
                # THIS WRITE AN OUTPUT FILE ON THE COMPUTER #
                try:
                    f=open("WPI_MC2T_config.txt","w",-1)
                    f.write("***************************************\n")
                    f.write("*       MPI_MC2T CONFIGURATION        *\n")
                    f.write("***************************************\n")
                    f.write("*  ["+td+"]\n")
                    f.write("***************************************\n")
                    f.write("*        CONFIG SYRINGE #1:           *\n")
                    f.write("***************************************\n")
                    for i in range(1,10):f.write(ConfigOut1[i]+"\n")
                    f.write("***************************************\n")
                    f.write("*        CONFIG SYRINGE #2:           *\n")
                    f.write("***************************************\n")
                    for i in range(1,10):f.write(ConfigOut2[i]+"\n")
                    f.write("***************************************")
                    f.close()
                except:
                    print(" #! ERROR !#: COULD NOT WRITE CONFIG FILE \n")
        return
    except:
        print(" COMMUNICATIONS ERROR WITH CONTROLLER ")
        WriteLog(0)
        LogDump[1]="FUNCTION:GetConfig()"
        LogDump[2]=" ERROR COMMUNICATING TO CONTROLLER"
        WriteLog(1)
        return
#END DEF

def SetProgram():
    global RunPSet, LogDump
    # THIS FUNCTION SETS UP A PROGRAM
    # SETS THE METHOD,AMOUNT TO DELIVER, SPEED, PAUSE AND CYCLE COUNT
    # SET SOME DEFAULTS
    #RunPSet: SETTINGS FOR AUTOMATED PROGRAM
    RunPSet[0]="1"  # ACTIVE PUMP                            #
    RunPSet[1]="INFUSE" #RunPSet[1]:INFUSE or WITHDRAW       #
    RunPSet[2]="50" #RunPSet[2]: AMOUNT(nL)                  #
    RunPSet[3]="10" #RunPSet[3]: SPEED                       # 
    RunPSet[4]="SEC" #RunPSet[4]: UNITS :nL/SEC  or nL/MIN   #
    RunPSet[5]="200" #RunPSet[5]: PAUSE (x/100) SECS         #
    RunPSet[6]="0" #RunPSet[6]: # TIMES TO REPEAT, 0=NONE)   #
    
    xflag=0  # CHECKS FOR CANCEL
         
    while(xflag==0):
        ClrScrn()
        print(" **************************************")
        print(" *          SET UP PROGRAM CYCLE      *")
        print(" **************************************")
        print(" * [1] SET ACTIVE PUMP                *[NOW: PUMP #"+RunPSet[0]+"]")
        print(" * [2] PUMP DIRECTION INFUSE/WITHDRAW *[NOW:"+RunPSet[1]+"]")
        print(" * [3] AMOUNT nL                      *[NOW:"+RunPSet[2]+"]")
        print(" * [4] RATE                           *[NOW:"+RunPSet[3]+"]")
        print(" * [5] UNITS                          *[NOW:nL/"+RunPSet[4]+"]")
        print(" * [6] PAUSE X*0.01SECS               *[NOW:X="+RunPSet[5]+"]")
        print(" * [7] NUMBER OF CYCLES               *[NOW:"+RunPSet[6]+"]")      
        print(" *                                    *")
        print(" * [X] GO BACK/CANCEL                 *")
        print(" **************************************")
        print("\n")
        xSelect = input(" SELECT [1,2,3,4,5,6,7 or X]:")
        if(xSelect=="1"):
            # SET ACTIVE PUMP
            print("\n")
            xC=input(" ENTER ACTIVE PUMP:[1] or [2]:")
            if(xC=="1"):
                RunPSet[0]="1"
            elif(xC=="2"):
                RunPSet[0]="2"
            else:
                print(" #!NO CHANGE IN PUMP!# ")
            print(" ACTIVE PUMP SET AS #:"+RunPSet[0])
            print("\n")
            
        elif(xSelect=="2"):
            # DIRECTION
            print("\n")
            xC=input(" ENTER DIRECTION:[1] INFUSE or [2] WITHDRAW:")
            if(xC=="1"):
                RunPSet[1]="INFUSE"
            elif(xC=="2"):
                RunPSet[1]="WITHDRAW"
            else:
                print(" #!NO CHANGE IN DIRECTION!# ")
            print(" DIRECTION SET AS:"+RunPSet[1])
            print("\n")
            
        elif(xSelect=="3"):
            #AMOUNT
            print("\n")
            xC=input(" ENTER AMOUNT nL [RANGE 0-999999.9]:")
            try:
                if((float(xC)>0) and (float(xC)<1000000)):RunPSet[2]=xC
            except:
                print(" #!NO CHANGE IN TARGET AMOUNT!# ")
            print(" AMOUNT SET AS :"+RunPSet[2])
            print("\n")
            
        elif(xSelect=="4"):
            #RATE
            print("\n")
            xC=input(" ENTER RATE [RANGE 0-999999.9]:")
            try:
                if((float(xC)>0) and (float(xC)<1000000)):RunPSet[3]=xC
            except:
                print(" #!NO CHANGE IN RATE!# " )
            print(" RATE SET AS :"+RunPSet[3])
            print("\n")
            
        elif(xSelect=="5"):
            #UNITS
            print("\n")
            xC=input(" ENTER RATE UNITS [(1) nL/SEC or (2) nL/MIN]:")
            if(xC=="1"):
                RunPSet[4]="SEC"                
            elif(xC=="2"):
                RunPSet[4]="MIN"
            else:
                print(" #!NO CHANGE IN RATE UNITS!# ")
                
            print(" RATE UNITS SET AS:nL/"+RunPSet[4])
            print("\n")
            
        elif(xSelect=="6"):
            #PAUSE
            print("\n")
            xC=input(" ENTER PAUSE TIME X [X/100 SECS]:")
            try:
                if(float(xC)>0):RunPSet[5]=xC
            except:
                print(" #!NO CHANGE IN PAUSE TIME!# ")
            print(" PAUSE SET AS:"+RunPSet[5]+" /100 SECS")
            print("\n")
            
        elif(xSelect=="7"):
            #CYCLE COUNT
            print("\n")
            xC=input(" ENTER NUMBER OF CYCLES:")
            try:
                if(float(xC)>0):RunPSet[6]=xC
            except:
                print(" #!NO CHANGE IN NUMBER OF CYCLES!# ")
            print(" NUMBER OF CYCLES SET AS:"+RunPSet[6])
            print("\n")
            
        else:
            return

# END DEF

def RunProgram():
    # THIS FUNCTION EXECUTES A 'RECIPE' WITH A PUMP
    # SETS THE METHOD,AMOUNT TO DELIVER, SPEED, PAUSE AND CYCLE COUNT
    #RunPSet: SETTINGS FOR AUTOMATED PROGRAM
    #RunPSet[0]: ACTIVE PUMP                 #
    #RunPSet[1]:(I)NFUSE (W)ITHDRAW          #
    #RunPSet[2]: AMOUNT(nL)                  #
    #RunPSet[3]: PUMP RATE                   # 
    #RunPSet[4]: UNITS nL/SEC  OR nL/MIN     #
    #RunPSet[5]: PAUSE (x/100) SECS          #
    #RunPSet[6]: # TIMES TO REPEAT, 0=NONE)  # 
    global RunPSet,LogDump 
    xflag=0  # CHECKS FOR CANCEL
       
    while(xflag==0):
       
        print(" ***********************************************************")
        print(" *          RUN PROGRAM CYCLE                              *")
        print(" ***********************************************************")
        print(" *        [1] SET UP CYCLE                                 *")
        print(" *        [2] REVIEW CYCLE CONFIG                          *")
        print(" *        [3] RUN CYCLE                                    *")
        print(" *                                                         *")
        print(" *        [X] GO BACK/CANCEL                               *")
        print(" ***********************************************************")
        print("\n")
        xSelect = input(" SELECT [1,2,3 or X]:")
        
        if(xSelect =="1"):
            # SET UP CYCLE
            ClrScrn()
            SetProgram()
            print(" ***********************************************************")
            print(" *          CYCLE PROGRAM SET                              *")
            print(" ***********************************************************")
            print("\n")
            
        elif (xSelect =="2"):
            # REVIEW CYCLE SETTINGS
            ClrScrn()
            tag=[""]*7
            tag[0]=" * ACTIVE PUMP #:"+RunPSet[0]
            tag[1]=" * PUMP DIRECTION:"+RunPSet[1]
            tag[2]=" * AMOUNT (nL):"+RunPSet[2]
            tag[3]=" * RATE:"+RunPSet[3]
            tag[4]=" * RATE UNITS: nL/"+RunPSet[4]
            tag[5]=" * PAUSE "+RunPSet[5]+"/100 SECS"
            tag[6]=" * REPEAT CYCLE COUNT:"+RunPSet[6]
            print(" ***********************************************************")
            print(" *          REVIEW PROGRAM                                 *")
            print(" ***********************************************************")
            for i in range(7):print(tag[i])
            print(" ***********************************************************")
            print("\n")

        elif(xSelect =="3"):
            # RUN CYCLE PROGRAM
            ClrScrn()
            print(" ***********************************************************")
            print(" *          RUN CYCLE PROGRAM                              *")
            print(" ***********************************************************")
            print(" *        [1] RUN CYCLE                                    *")
            print(" *                                                         *")
            print(" *        [X] GO BACK/CANCEL                               *")
            print(" ***********************************************************")
            print("\n")
            RPS6=int(RunPSet[6])
            xC = input (" SELECT [1 or X]:")
            tag1="I"
            tag2="EI"
            tag3="S"
            if(RPS6<1):break
            if(xC=="1"):
                # SET UP TO RUN PROGRAM
                ChkOK=SendCmd("L"+RunPSet[0]) # SET ACTIVE PUMP
                if(RunPSet[1]=="INFUSE"):
                    tag1="I" # SET DIRECTION INFUSE
                    tag2="EI" # SET VOLUME COUNTER DELIVERED 
                if(RunPSet[1]=="WITHDRAW"):
                    tag1="W" # SET DIRECTION WITHDRAW
                    tag2="EN" # SET VOLUME COUNTER REMAINING
                    
                ChkOK=SendCmd(tag1) # SET DIRECTION
                ChkOK=SendCmd(tag2) # SET VOLUME COUNTER

                ChkOK=SendCmd("V"+RunPSet[2]) # SET TARGET AMOUNT
                ChkOK=SendCmd("R"+RunPSet[3]) # SET DELIVERY RATE
                if(RunPSet[4]=="SEC"): tag3="S"
                if(RunPSet[4]=="MIN"): tag3="M"
                ChkOK=SendCmd(tag3) # SET UNITS
                
                ###################################################
                #                                                 #
                #         PUMP NOW SET, RUN PROGRAM               #
                #                                                 #
                ###################################################
                print(" *****************************************************")
                print(" *          CYCLE START UP                           *")
                print(" *****************************************************")
                print(" *     RUNNING "+RunPSet[6]+" CYCLES           ")
                print(" *****************************************************")
                print(" *     PRESS 'x'-KEY TO HALT CYCLE                   *")
                print(" *****************************************************")
                
               
                xKey = 0 # KEYBOARD STOP
                # START PUMP
                
                while(xKey==0):# KEY NOT PRESSED
                    for j in range(RPS6):  #HAVE TO USE AN INTEGER FOR # CYCLES
                        if(keyboard.is_pressed('x')):
                            print(" !#CYCLE PROGRAM HALTED BY OPERATOR#! ")
                            xKey=1
                            break
                        # KEEP GOING, NO KEYBOARD STOP
                        tag="H" # MAKE SURE PUMP IS STOPPED
                        ChkOK=SendCmd(tag)
                        tag="G" # SEND START COMMAND
                        ChkOK=SendCmd(tag)
                        if(keyboard.is_pressed('x')):
                            xKey=1
                            print(" !#CYCLE PROGRAM HALTED BY OPERATOR#! ")
                            break
                        # CHECK IF PUMP IS DONE
                        xpump=0
                        while(xpump==0):
                            time.sleep(0.25) # CHECK PUMP EVERY 0.5 SECONDS
                            ChkOK=SendCmd("?G")
                            ## RER
                            print("\r *    PUMP OPERATING.... *",end="")
                            ## RER
                            if(ChkOK[0]!='Motor State: Running'):xpump=1
                        print("\n *    PUMP CYCLE #"+str(j+1)+" COMPLETED")
                        # PUMP HAS COMPLETED CYCLE, NOW PAUSE
                        if(keyboard.is_pressed('x')):
                            print(" !#CYCLE PROGRAM HALTED BY OPERATOR#! ")
                            xKey=1
                            break
                        ptime=(float(RunPSet[5])/100)/5 # take it in 5 steps
                        for i in range(5):
                            print("\r *    PAUSING FOR "+str(ptime*5)+" SECS",end="")
                            time.sleep(ptime)
                            if(keyboard.is_pressed('x')):
                                print(" !#CYCLE PROGRAM HALTED BY OPERATOR#! ")
                                xKey=1
                                break
                        # NEXT CYCLE
                    xKey=1
                print("\n")
                print(" *****************************************************")
                print(" *          CYCLE PROGRAM COMPLETED                  *")
                print(" *****************************************************")
                print("\n")
            else:
                xflag=1 #CANCEL FROM RUN LOOP
        else:
            xflag=1 #CANCEL FROM RUN LOOP
            
    return  
#END DEF
    
def ChgConfig():
    # THIS FUNCTION CHANGES PARAMETERS OF PUMP OPERATION
    global zcom, Config,ConfigOut1,ConfigOut2,ChgConfig
    ConfigY=[""]*10
    chgConfig=[""]*10 
    print(" ***********************************************************")
    print(" *          CHANGE  CONFIGURATION                          *")
    print(" ***********************************************************")
    print(" *        [1] SET SYRINGE TYPE         [NOW:CHECK DISPLAY] *")
    print(" *        [2] SET GROUP MODE           [NOW:CHECK DISPLAY] *")
    print(" *        [3] SET ACTIVE PUMP          [NOW:CHECK DISPLAY] *")
    print(" *        [4] SET UP SYRINGE                               *")
    print(" *        [5] SET UP PROGRAM CYCLE                         *")                                 
    print(" *                                                         *")
    print(" *        [X] TO CANCEL                                    *")
    print(" ***********************************************************")
    chkInput = input(" ENTER SELECTION:[1,2,3,4,5 or X]")
    
    iflag=False
    if((chkInput!="1") and(chkInput!="2")and (chkInput!="3")and(chkInput!="4")):return

    if(chkInput =="1"):
        # Set Syringe Type
        print(" *******************************************************")
        print(" * SEE MANUAL: CHOOSE SYRINGE TYPE: \n * TYPES [1-9], [10]= WPI NANOLITER,\n * [11], [12] or [13] FOR CUSTOM TYPE A,B OR C \n * [X] To CANCEL") 
        print(" *******************************************************")
        ST=input(" SELECT SYRINGE TYPE :")
        try:
            SyrType=int(ST)
            if((SyrType>0) and (SyrType<14)):chgConfig[1]="T"+ST
            if(chgConfig[1]!=""):
                ChkOK=SendCmd(chgConfig[1])
                print(ChkOK[0],ChkOK[1])
        except:
            chgConfig[1]=""
            print(" !PARAMETER NOT CHANGED !")
            return
        
    elif(chkInput =="2"):
        # Set Group Mode
        print(" ***************************************************************************************************")
        print("  CHANGE GROUP MODE: [1] FOR GROUPED,[2] FOR NON-GROUPED, [X] CANCEL                               *")      
        print(" ***************************************************************************************************")
        GM= input(" SELECT GROUP MODE [1,2 or X]: ")
        try:
            if(GM=="1"):chgConfig[2]="P"
            if(GM=="2"):chgConfig[2]="N"
            if(chgConfig[2]!=""):
                ChkOK=SendCmd(chgConfig[2])
                print(ChkOK[0],ChkOK[1])
        except:
            chgConfig[2]=""
            print(" !PARAMETER NOT CHANGED !")
            return
            
    elif(chkInput=="3"):
        # Set Active Pump
        print(" ************************************************************************")
        print(" * CHANGE ACTIVE PUMP: [1] FOR PUMP #1, [2] PUMP #2 or [X] CANCEL       *")      
        print(" ************************************************************************")
        AP= input("  SELECT ACTIVE PUMP [1,2 or X]: ")
        try:
            if((AP=="1") or (AP=="2")):chgConfig[3]="L"+AP
            if(chgConfig[3]!=""):
                ChkOK=SendCmd(chgConfig[3])
                print(ChkOK[0],ChkOK[1])
        except:
            chgConfig[3]=""
            print(" !PARAMETER NOT CHANGED !")
            return
        
    elif(chkInput=="4"):
        # Set Syringe Parameters
        #  NOTE: CHANGES TO SYRINGE ONLY AFFECTS CURRENTLY SELECTED SYRINGE!
        #  Load parameters
        
        for j in range(0,10):
            ChkOK=SendCmd(Config[j])
            ConfigY[j]=ChkOK[0]
        # Start Change Process
        iflag=True
        while (iflag):
            print(" ***********************************************************************")
            print(" *     SYRINGE SET UP: ONLY APPLIES TO ACTIVE SYRINGE! CHECK DISPLAY!  *") 
            print(" ***********************************************************************")
            print(" *        [1] SET DELIVERY RATE UNITS: [NOW:"+ConfigY[8]+"]")
            print(" *        [2] SET DELIVERY RATE        [RANGE 0-999999.9]")
            print(" *        [3] SET TARGET VOLUME IN nL  [NOW:"+ConfigY[1]+"]")
            print(" *        [4] SET PUMP DIRECTION       [NOW:"+ConfigY[7]+"]")
            print(" *        [5] SET COUNTER MODE         [NOW:"+ConfigY[4]+"]")
            print(" *        [6] SET MOTOR DRIVE          [NOW:"+ConfigY[3]+"]")
            print(" *                                                         ")
            print(" *        [X] TO CANCEL                                    ")
            print(" ***********************************************************************")
            
            SX= input("  SELECTION [ 1-6, or X] ")

            try:
                if(SX=="1"):
                    # Delivery Rate Units
                    print(" ***************************************************************************")
                    print(" * CHANGE DELIVERY RATE UNITS: [X] CANCEL [1] nL/SEC, [2] nL/MIN           *")      
                    print(" ***************************************************************************")
                    XX= input("  CHANGE DELIVERY RATE UNITS[X, 1, or 2]: ")
                    try:
                        chgConfig[4]=""
                        if (XX=="1"):chgConfig[4]="S"
                        if(XX=="2"):chgConfig[4]="M"
                        if(chgConfig[4]!=""):
                            ChkOK=SendCmd(chgConfig[4])
                            print("DELIVERY RATE UNITS SET",ChkOK[0],ChkOK[1])
                            ChkOK=SendCmd("?U")
                            ConfigY[8]=ChkOK[0]
                    except:
                        chgConfig[4]=""
                        print(" !PARAMETER NOT CHANGED !")
                        return
                    
                elif(SX=="2"):
                    # Delivery Rate
                    print(" *****************************************************************************")
                    print("  CHANGE DELIVERY RATE PARAMETER: \n  [X] NO CHANGE/CANCEL, \n DESIRED DELIVERY RATE RANGE [0 to 999999.9]")      
                    print(" *****************************************************************************")
                    XX= input("  CHANGE DELIVERY RATE PARAMETER[ X or RANGE [0 to 999999.9]: ")
                    try:
                        X3=float(XX)
                        if(X3>-0.01) and (X3<1000000):chgConfig[5]="R"+XX
                        if(chgConfig[5]!=""):
                            ChkOK=SendCmd(chgConfig[5])
                            print(ChkOK[0],ChkOK[1])
                    except:
                        chgConfig[5]=""
                        print(" !PARAMETER NOT CHANGED !")
                        return
                    
                elif(SX=="3"):
                    # Target Volume
                    print(" *****************************************************************************")
                    print("  CHANGE TARGET VOLUME PARAMETER: \n [X] NO CHANGE/CANCEL, \n DESIRED TARGET VOLUME in nL [0 to 999999.9] ")      
                    print(" *****************************************************************************")
                    XX= input("  CHANGE TARGET VOLUME PARAMETER[X, or  0 or RANGE [0 to 999999.9]: ")
                    try:
                        X3=float(XX)
                        if(X3>-0.01) and (X3<1000000):chgConfig[6]="V"+XX
                        if(chgConfig[6]!=""):
                            ChkOK=SendCmd(chgConfig[6])
                            print(ChkOK[0],ChkOK[1])
                            ChkOK=SendCmd("?V")
                            ConfigY[1]=ChkOK[0]
                    except:
                        chgConfig[6]=""
                        print(" !PARAMETER NOT CHANGED !")
                        return
                    
                elif(SX=="4"):
                    # Pump Direction
                    print(" *****************************************************************************")
                    print("  CHANGE ACTIVE PUMP DIRECTION INFUSE/WITHDRAW:\n [X] NO CHANGE/CANCEL, [1] INFUSE  [2] WITHDRAW     ")      
                    print(" *****************************************************************************")
                    XX= input("  CHANGE PUMP DIRECTION [X, 1 or 2]: ")
                    try:
                        chgConfig[7]=""
                        if(XX=="1"):chgConfig[7]="I"
                        if(XX=="2"):chgConfig[7]="W"
                        if(chgConfig[7]!=""):
                            ChkOK=SendCmd(chgConfig[7])
                            if(XX=="1"):print(" DIRECTION INFUSE SET",ChkOK[0],ChkOK[1])
                            if(XX=="2"):print(" DIRECTION WITHDRAW SET",ChkOK[0],ChkOK[1])
                            ChkOK=SendCmd("?D")
                            ConfigY[7]=ChkOK[0]
                    except:
                        chgConfig[7]=""
                        print(" !PARAMETER NOT CHANGED !")
                        return
                    
                elif(SX=="5"):
                    #Counter Mode
                    print(" *****************************************************************************")
                    print("  CHANGE VOLUME COUNTER: [X] NO CHANGE/CANCEL, [1] DELIVERED  [2] REMAINING  *")      
                    print(" *****************************************************************************")
                    XX= input("  CHANGE VOLUME COUNTER [X, 1 or 2]: ")
                    try:
                        chgConfig[8]=""
                        if(XX=="1"):chgConfig[8]="EN"
                        if(XX=="2"):chgConfig[8]="EI"
                        if(chgConfig[8]!=""):
                            ChkOK=SendCmd(chgConfig[8])
                            if(XX=="1"):print("COUNTER DELIVERED",ChkOK[1])
                            if(XX=="2"):print("COUNTER REMAINING",ChkOK[1])
                            ChkOK=SendCmd("?E")
                            ConfigY[4]=ChkOK[0]
                    except:
                        chgConfig[8]=""
                        print(" !PARAMETER NOT CHANGED !")
                        return
                    
                elif(SX=="6"):
                    #Motor Drive
                    print(" *****************************************************************************")
                    print("  CHANGE MOTOR DRIVE OPTION:\n [X] NO CHANGE/CANCEL, [1] SMOOTH DRIVE  [2] MAX LOAD DRIVE")      
                    print(" *****************************************************************************")
                    XX= input(" CHANGE DRIVE OPTION [X, 1 or 2]: ")
                    try:
                        chgConfig[9]=""
                        if(XX=="1"):chgConfig[9]="BS"
                        if(XX=="2"):chgConfig[9]="BT"
                        if(chgConfig[9]!=""):
                            ChkOK=SendCmd(chgConfig[9])
                            print(ChkOK[0],ChkOK[1])
                            ChkOK=SendCmd("?B")
                            ConfigY[3]=ChkOK[0]
                    except:
                        chgConfig[9]=""
                        print(" !PARAMETER NOT CHANGED !")
                        return
                        
                else:
                    print(" ")
                    iflag=False
            except:
                iflag=False
                return
    return
#END DEF
    


##############################################################################    
               #########################################
               #    START  MAIN ROUTINE                #
               #########################################
##############################################################################
# START BANNER #
ClrScrn()
print ("\n#########################################")
print ("###      MC2TCNX Version 2.B          ###")
print ("### World Precision Instruments, LLC  ###")
print ("###         AUGUST 2020               ###")
print ("#########################################\n")

#OPEN AND CHECK PORTS#
results=serial_ports()
rx=GetCnx(results)
if(rx==False):
    print ("MC2T PORT NOT FOUND.. \n 1. CHECK CONNECTION \n 2. ENSURE 'REMOTE' IS ENABLED ON MC2T CONFIG OPTIONS SCREEN \n 3. ENSURE MC2T IS ON OPERATIONS SCREEN THAT SHOWS PUMPS\n")
    time.sleep(2)
##
    
# MAIN MENU #

while (rx==True):
    print ("\n")
    print ("**********************************************************************")
    print ("*                        MICROTOUCH 2T REMOTE                        *")
    print ("*                                                                    *")
    print ("* [1] SET ACTIVE PUMP                                                *") 
    print ("* [2] DISPLAY CURRENT PUMP CONFIGURATIONS                            *") 
    print ("* [3] CHANGE PUMP CONFIGURATION                                      *")
    print ("* [4] USE CYCLE PROGRAM MODE                                         *") 
    print ("* [5] DISPLAY/STOP/START ACTIVE PUMP                                 *") 
    print ("*                                                                    *")
    print ("* [X] EXIT THIS PROGRAM                                              *")
    print ("**********************************************************************\n")
    select=input("SELECT COMMAND [1,2,3,4,5 or X]:")
    
    if(select=="1"):
        xPump=input( " SELECT ACTIVE PUMP [1] or [2] (SEE DISPLAY)  :")
        try:
            if ((xPump=="1") or (xPump=="2")):
                tag="L"+xPump
                chkOK=SendCmd(tag)
                print(" PUMP "+xPump+" SELECTED",chkOK[0])
            else:
                print(" ACTIVE PUMP UNCHANGED")
        except:
            print( "!# PUMP SELECTION ERROR #!")
    elif(select=="2"):
        # Display config
        x=GetConfig(1)
    elif(select=="3"):
        # Change Config
        x=ChgConfig()
    elif(select=="4"):
        # ENTER CYCLE PROGRAM MODE
        RunProgram()
        
    elif(select=="5"):
        # DISPLAY/STOP/CANCEL
        ClrScrn()
        print ("**********************************************************************")
        print ("* [1] DISPLAY ACTIVE PUMP CURRENT STATUS                             *")
        print ("* [2] STOP ACTIVE PUMP                                               *")
        print ("* [3] RUN ACTIVE PUMP                                                *")
        print ("*                                                                    *")
        print ("* [X] GO BACK/CANCEL                                                 *")
        print ("**********************************************************************")
        s3=input(" SELECT COMMAND [1,2,3 or X]:")
        if(s3=="1"):
            tag="?G"
            chkOK=SendCmd(tag)
            print ("\n")
            print ("**********************************************************************")
            print ("*      CURRENT PUMP STATE: ",chkOK[0])
            print ("**********************************************************************")
            print("\n")
        elif(s3=="2"):
            tag="H"
            chkOK=SendCmd(tag)
            tag="?G"
            chkOK=SendCmd(tag)
            print ("\n")
            print ("**********************************************************************")
            print ("* PUMP STOPPED:",chkOK[0])
            print ("**********************************************************************")
            print("\n")
        elif(s3=="3"):
            tag="G"
            chkOK1=SendCmd(tag)
            tag="?G"
            chkOK=SendCmd(tag)
            print ("\n")
            print ("**********************************************************************")
            print ("* PUMP STARTED:",chkOK[0])
            print ("**********************************************************************")
            print ("\n")
    else:
        
        rx=False
        break
 
#END MAIN

