''' Company :Licel GmbH.
    Author: Elyes Harzallah 
'''
#!/usr/bin/env python
import datetime
import os
import socket
import sys
import time
# ---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
# remarks and definitions
# ---------------------------------------------------------------------------
# 
# call this script using 
# perl LicelMainBasic.pl <ip_address> <port>
# - ip_address is the IP address of the PC running Licel Main
# - port is set in the ini file Licel Main.ini (see below)
#
# this script will work if the file Licel Main.ini is prepared as follows 
# the script will write a log file 
#
# [TCPIP_API]
# Active = TRUE
# Port = 2058
# ...
# [Module0]
# Active = TRUE
# Path = Control APD-PMT.vi
# Name = Detectors
# Controllers = 0
# CAPs = PMT,APD
# Push = FALSE
# TCPIP_API = TRUE
# ...
# [Module1]
# Active = TRUE
# Path = Control Timing.vi
# Name = "Timing Control"
# Controllers = 0
# CAPs = TIMER
# Push = FALSE
# TCPIP_API = FALSE
# ...
# [Module5]
# Active = TRUE
# Path = TCPIP Acquis.vi
# Name = Acquisition
# Controllers = 0
# CAPs = TR
# Push = FALSE
# TCPIP_API = TRUE


ScriptInfo = "2021-04-28 sr" 
resp = ""
answer = ""
# depends on Licel Main.ini and the visible tabs in Licel Main 
LicelMainTab = 7
PmtTab = 0
TimingTab = 1 
AcqTab = 5 

# there are sub-tabs in control apd-pmt, we need the pmt tab! 
PmtInternalTab = 0

# there are sub-tabs in control timing, we need the Acquisition Timing tab! 
TimingInternalTab = 2

# target shots for the acquisitions are defineable: 
TargetShots = 500  

# target records (number of acqusitions (files) are defineable: 
# targetRecords = 0: never stop (manual, only)
TargetRecords = 1  

# define dataset info for analog and photon counting at 4 tzrtansient recorders 
# DATASET <trAddress> <memory> <mode> <active> <noBins> <reduction> <wavelength> [Laser]
SubmitDataset = ""

TrDataset = ("0 A AN 1 16380 0 532", 
                 "0 A PC 1 16380 0 532",
                 "1 A AN 1 16380 0 405",
                 "1 A PC 1 16380 0 405",
                 "2 A AN 1 16380 0 1064",
                 "2 A PC 1 16380 0 1064",
                 "3 A AN 1 16380 0 566",
				 "3 A PC 1 16380 0 566")

# let us add some custum info 
# CUSTOMINFO <trAddress> <memory> <mode> <"customInfo">
SubmitCustomInfo = ""
TrCustomInfo = ("0 A AN \"this is my channel 1\"", 
                 "0 A PC \"this is my channel 2\"",
                 "1 A AN \"this is my channel 3\"",
                 "1 A PC \"this is my channel 4\"",
                 "2 A AN \"this is my channel 5\"",
                 "2 A PC \"this is my channel 6\"",
                 "3 A AN \"this is my channel 7\"",
				 "3 A PC \"this is my channel 8\"")

# Frequency to estimate a timeout value and for submission to Control Timing
Freq = 100 

# other timing values 
RepetitionRate = 100 
Lamp2Acquisition = 200 
AcquisitionLength = 10
Acqusition2QSwitch = 100
QSwitchLength = 300 
StartDelay = 50 

# pmt number, indices, set voltages 
PmtNum = 4
PmtIndex = (0, 1, 2, 3)
PmtVoltage = (770.0, 780.0, 790.0, 800.0) 

# sleep settings (wait time between steps to beeter see what happens)
SleepActive = 1
SleepWaitSec = 1.0 


# ---------------------------------------------------------------------------
# log file
# ---------------------------------------------------------------------------
ThisSkript = os.path.basename(__file__)
FileTokens = ThisSkript.split(".")
Filename =FileTokens[0]+".log"
File = open(Filename,"w")



def createlog(a_logs):
    ''' Write the passed argument to a log file.'''
    File.write(str(a_logs)+"\n")
    print(a_logs)

def sendcommand(a_sock,a_receivedfile,a_command):
    ''' Help function to send and recive commandes via TCP/IP sockets. '''
    createlog(a_command)
    commandtosend = a_command+"\r\n"
    a_sock.send(commandtosend.encode())
    resp = a_ file.readline()
    createlog(resp.split("\n")[0])
    return resp

def getstatusvalue(a_sock, a_receivedfile, a_search):
    ''' Retrive the status of the selected transient recorder .'''
    retval = -1
    resp   = sendcommand(a_sock,a_receivedfile,"STATUS?")
    token  = resp.split(" ")
    for ftoken in token: 
        if ftoken.split("=")[0] == a_search : 
            retval=ftoken.split("=")[1]
    return retval

def waittr_setdone(a_sock, a_receivedfile, a_waitseconds):
    ''' Wait until transient recorder set is done.'''
    _tr_setdone = 0
    starttime=datetime.datetime.now().second
    elapsed = 0
    while( int(_tr_setdone) <= 0 and elapsed <= a_waitseconds ):
        elapsed= datetime.datetime.now().second - starttime
        _tr_setdone = getstatusvalue(a_sock, a_receivedfile, "TR_SET")
        createlog("Elapsed seconds:"+str(elapsed))
        time.sleep(1)   
    return elapsed
    
def switchhv(a_sock, a_receivedfile, a_pmtnumber, a_index, a_active):
    ''' Switch the PMT Hardware .'''
    i=0
    for i in range(a_pmtnumber):
        sendcommand(a_sock, a_receivedfile, "PMT: HV "+ str(a_index[i])+ " "+ a_active)
        sendcommand(a_sock, a_receivedfile, "PMT: READ "+ str(a_index[i]))
        sendcommand(a_sock, a_receivedfile, "PMT: HV? "+str(a_index[i])) 

def getacqtimeout(a_freq, a_targetshots, a_targetrecords):
    ''' Calculate the Acquistion Timeout . '''
    return (2* a_targetshots* a_targetrecords/ a_freq)

def waitforrecords(a_sock, a_receivedfile, a_waitrecords, a_waitseconds):
    ''' Retrive the statusvalue of Records. '''
    record=0
    starttime=datetime.datetime.now().second
    record= 0
    while( int(record) < a_waitrecords and ( ( (datetime.datetime.now().second- starttime <= a_waitseconds ) or a_waitseconds < 0) )):
        record=getstatusvalue(a_sock, a_receivedfile, "RECORDS")
        createlog("Record: "+record)
        #createlog(record)
        time.sleep(0.1)

    return record

def waitforautorun(a_sock, a_receivedfile, a_waitautostatus, a_waitseconds):
    ''' Retrive the statusvalue of Auto run . '''
    autostatus = -1 
    starttime=datetime.datetime.now().second
    while ((autostatus != a_waitautostatus) and  (((datetime.datetime.now().second- starttime <= a_waitseconds ) or a_waitseconds < 0))):
        autostatus = getstatusvalue(a_sock, a_receivedfile,"AUTO")
        createlog("autostatus: "+autostatus)
        time.sleep(0.1)
    return autostatus

def main():
    ''' Use this script to interact via TCPIP with the Licel software .'''
    createlog("*** Run script "+ThisSkript+"["+ScriptInfo+"]***\n")
    starttime=datetime.datetime.now()
    createlog("Start :"+str(starttime)+"\n")

    if len(sys.argv) != 3:
        createlog("Too few arguments, call skript using\n"+ThisSkript+ " <IP> <Port>")
        return -1 
    ip = sys.argv[1]
    port = sys.argv[2]
    
    #Open Socket 
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((ip,int(port)))
    receivedfile=sock.makefile('rw')
    createlog("Connecting to: IP: "+ip+" PORT "+port+"\n")

    sendcommand(sock, receivedfile, "MAIN: TAB 7")
    sendcommand(sock, receivedfile, "MAIN: VER?")
    sendcommand(sock, receivedfile, "MAIN: *IDN?")
    time.sleep(1)

    createlog("\n***Switch to PMT tab***\n")
    sendcommand(sock, receivedfile, "MAIN: TAB "+ str(PmtTab))
    sendcommand(sock, receivedfile, "PMT: VER?")
    time.sleep(1)

    createlog("\n***Switch to the timing tab***\n")
    sendcommand(sock, receivedfile, "MAIN: TAB "+ str(TimingTab))
    sendcommand(sock, receivedfile, "TIMING: VER?")
    time.sleep(1)

    createlog("\n***Switch to Acquis tab***\n")
    sendcommand(sock, receivedfile, "MAIN: TAB "+ str(AcqTab))
    # this is the first tab switch to Acquis, the configuration dialog may pops up 
    # if the acquis.ini settings does not match the identified transient recorders 
    # we should wait until the TR parameters have been set, give the user 10 minutes time 
    waitfortr_time = 10
    if(waittr_setdone(sock, receivedfile, waitfortr_time) > waitfortr_time ):
        createlog("wait time for the TR configuration elapsed")
        return
    sendcommand(sock, receivedfile, "VER?")
    # stop a runnign acquisition commented out 
    #sendcommand(sock, receivedfile, "STOP")
    time.sleep(1)

    createlog("\n***Set PMT target voltage values***\n")
    sendcommand(sock, receivedfile, "MAIN: TAB "+ str(PmtTab))
    sendcommand(sock, receivedfile, "PMT: TAB "+ str(PmtInternalTab))
    for i in range(PmtNum):
        sendcommand(sock, receivedfile, "PMT: HVSETTING "+str(PmtIndex[i])+" "+str(PmtVoltage[i]))
    time.sleep(1)
    
    # set trigger times 
    createlog("\n***Switch the trigger times***\n")
    sendcommand(sock, receivedfile, "MAIN: TAB "+ str(TimingTab))
    sendcommand(sock, receivedfile, "TIMING: TAB "+ str(TimingInternalTab))
    sendcommand(sock, receivedfile, "TIMING: TIMES "+ str(RepetitionRate)+ " "+ str(Lamp2Acquisition)+ " "+    
                                  str(AcquisitionLength)+ " "+ str(Acqusition2QSwitch)+ " " + str(QSwitchLength)+ " "+ str(StartDelay) + " "+ str(Freq))
    time.sleep(1)

    #define my datasets 
    createlog("\n***Transfer my datasets***\n")
    sendcommand(sock, receivedfile, "MAIN: TAB "+ str(AcqTab))
    for trdata in TrDataset:
        sendcommand(sock, receivedfile, "DATASET "+ str(trdata))
    for custominfo in TrCustomInfo:
        sendcommand(sock, receivedfile, "CUSTOMINFO "+ str(custominfo))
    time.sleep(1)          

    records = 0 
    autostatus = -1 
    timeout = -1
    # Frequency described above 	

    #Voltage ON
    createlog("\n***Switch the voltages ON***\n")
    sendcommand(sock, receivedfile, "MAIN: TAB "+ str(PmtTab))
    sendcommand(sock, receivedfile, "PMT: TAB "+ str(PmtInternalTab))
    switchhv(sock, receivedfile, PmtNum, PmtIndex, "ON")
    time.sleep(1)

    # set trigger switchhes 
    # bitwise Lamp - Acquisition - QSwitch - Gating - Master 
    sendcommand(sock, receivedfile, "MAIN: TAB "+ str(TimingTab))
    sendcommand(sock, receivedfile, "TIMING: TAB "+ str(TimingInternalTab))
    sendcommand(sock, receivedfile, "TIMING: MODE 7")
    time.sleep(1)

    #acq TAB 
    createlog("\n***Start the acquisition(s)***\n")
    sendcommand(sock, receivedfile, "MAIN: TAB "+ str(AcqTab))
    sendcommand(sock, receivedfile, "RECORDS "+ str(TargetRecords))
    sendcommand(sock, receivedfile, "SHOTS "+ str(TargetShots))
    timeout = getacqtimeout(Freq, TargetShots, TargetRecords)
    sendcommand(sock, receivedfile, "AUTO 1")

    # wait until the number of records in the STATUS? is like RECORDS=$TargetRecords
    if TargetRecords >0 :
        records = waitforrecords(sock, receivedfile, TargetRecords, timeout)
        if int(records) != int(TargetRecords):
            createlog("wait time for the acquisiton elapsed "+ str(timeout))
            return

    # target records == 0? we must wait for that somebody manually stops (wait time -1)
    # then AUTO in the STATUS? must be like AUTO=0 
    if TargetRecords == 0 : 
        time.sleep(1)
        autostatus = waitforautorun(sock, receivedfile, 0, -1)
        if autostatus != 0 :
            createlog("wait acquisiton stop failed")
            return 
    
    #voltage off 
    createlog("\n***Switch the voltages OFF***\n")
    sendcommand(sock, receivedfile, "MAIN: TAB "+str(PmtTab))
    sendcommand(sock, receivedfile, "PMT: TAB "+str(PmtInternalTab))
    switchhv(sock, receivedfile, PmtNum, PmtIndex, "OFF")
    time.sleep(1)

    #triggers off 
    createlog("\n***Switch the triggers OFF***\n")
    sendcommand(sock, receivedfile, "MAIN: TAB "+str(TimingTab))
    sendcommand(sock, receivedfile, "TIMING: TAB "+str(TimingInternalTab))
    sendcommand(sock, receivedfile, "TIMING: MODE 0")
    time.sleep(1)

    #let's switch back to the acquis tab 
    createlog("\n***Go back to the acquis tab*** \n")
    sendcommand(sock, receivedfile, "MAIN: TAB "+str(AcqTab))

    #Terminate Programe 
    sock.close()
    createlog("*** " + ThisSkript +" end ***\n")    
    elapsedtime = datetime.datetime.now().second - starttime.second 
    if elapsedtime < 0 :
        elapsedtime = elapsedtime * -1
        
    createlog("Elapsed time : "+ str(elapsedtime) +" seconds \n")
    File.close()


if __name__ == "__main__":
    main()
