#!/bin/sh
# \
exec oagwish "$0" "$@"

set auto_path [linsert $auto_path 0 /usr/local/oag/apps/lib/$env(HOST_ARCH)]
set auto_path [linsert $auto_path 0 /usr/local/oag/lib_patch/$env(HOST_ARCH)]
APSStandardSetup

set status ""
APSApplication . -name LinacInterleavingLight -overview "Linac Interleaving light."
APSScrolledStatus .status -parent .userFrame -width 100 -height 10 \
        -textVariable status

proc SetStatus {text} {
    global status
    set status "[clock format [clock seconds] -format %H:%M:%S] $text"
    update
}

proc CheckInterleavingTime {args} {
    global interTime
    if {$interTime>240 || $interTime<0} {
        SetStatus "Interleaving time is out of 20 to 240 seconds range,  change to default 60 seconds"
        set interTime 60
        return
    }
}

proc RunInterleaving {args} {
    set state ""
    APSParseArguments {state}
    
    switch $state {
        on  {
            set comment "Beam to PAR."
        }
        off {
            set comment "Beam to BB."
        }
        
    }
    SetStatus "$comment"
    #ramp on/off LTP and PTB
    if [catch {RampOnOffPTB -state $state} result] {
        return -code error "Error ramping PTB: $result"
    }
    #SetStatus "done."
}


proc RampOnOffPTB {args} {
    set state ""
    APSParseArguments {state}
    global LTPB1 PTBB1 PTBB2
    global magList magVarList
   
    
    set putList ""
    set putList1 ""
    switch $state {
        on {
            SetStatus "set LTP and PTB power supplies for beam to PAR ..."
        }
        off {
            SetStatus "set LTP and PTB power supplies for beam to BB ..."
        }
    }
    
    foreach pv $magList var $magVarList {
	set m [regsub ":CurrentAO" $pv ""]
	
	global $var
	#Check to make sure we have a mode of "On" and a current of 0A and not in a fault state
        if [catch {APScavget -printErrors -list=$m \
		       -list=:CurrentAO,:CurrentAI,:StatusCALC,:psModeM } initValues] {
            return -code error "Error reading values for $m: $initValues"
        }
        set ao [lindex $initValues 0]
        set ai [lindex $initValues 1]
        set statuscalc [lindex $initValues 2]
        set mode [lindex $initValues 3]
       
	set state0 $state
	
        if {$state0=="on"} {
           # turn on
            if {$mode == "On"} {
                if {$statuscalc != 0} {
                    SetStatus  "Error: $m has a fault status"
                    continue
                }
            } else {
                #turn on
		
                if [catch {exec cavput -list=${m}:OnSEQ=1 -pend=20 } result] {
                    return -code error "Error turn on $m: $result"
                }
            }
            #ramp to current
            lappend putList ${m}:CurrentAO=[set ${var}(on)]
	    
        } else {
            #turn to standby mode
            if {$mode == "Standy"} {
                SetStatus "$m is on standby mode already."
                continue
            } else {
                if {$mode!="On"} {
                    #turn on first
                    if {$ao>0} {
                        #put the currentA0 to zero before turning it on
                       # if [catch {exec cavput -list=${m}:CurrentAO=0  -pend=20} result] {
                        #    return -code error "Error setting $m currentAO to 50A: $result"
                        #}
                    }
                    if [catch {exec cavput -list=${m}:OnSEQ=1 -pend=20 } result] {
                        return -code error "Error turn on $m: $result"
                    }
                    set ao 0
                }
                lappend putList1 ${m}:standbyC=1
                if {$ai>50} {
                   # lappend putList ${m}:CurrentAO=50
                }
            }
        }
    }
    
    
    if [llength $putList] {
        if [catch {exec cavput -list=[join $putList ,] -pend=30 -ramp=step=5,pause=1 } result] {
            return -code error "Error ramp LTP and PTB supplies: $result"
        }
    }
    if [llength $putList1] {
        if [catch {exec cavput -list=[join $putList1 ,] -pend=30 } result] {
            return -code error "Error puts LTP and PTB into standby mode: $result"
        }
    }
}


proc AbortInterleaving {args} {
    global abort
    SetStatus "Turn on magnets when aborting..."
    if [catch {RunInterleaving -state on} result] {
        return -code error "Error turn on supplies: $result"
    }
    SetStatus "Close L1:GV1 and L1:PC1:GV1 gate volve."
    if [catch {exec cavput -list=L1:GV1,L1:PC1:GV1 -list=:closeCmdBO=1 -pend=20 } result] {
        return -code error "Error closing  L1:GV1 and L1:PC1:GV1 gate volve: $result"
    }
    SetStatus "Aborted."
    set abort 0
}



proc InterleavingWithTimeInterval {args} {
    global timer errorCode abort injection interTime gunInhibitVars inhibitA inhibitB errorCode testMode
    
    while {1} {
        if [pv getw $gunInhibitVars] {
            return -code error "Error reading gun inhibit status pvs: $errorCode"
        }
        if {$inhibitA || $inhibitB} {
            return -code error "There is a gun inhibit, abort interleaving."
        }
        
        if $abort {
            catch {AbortInterleaving}
            return -code error "aborted."
        }
        
        set startTime [clock seconds]
        #turn on supplies at 0 seconds
        if [catch {RunInterleaving -state on} result] {
            return -code error "Error turn on supplies: $result"
        }
        
        #wait for 30 seconds after turn on
        while {1} {
            if [pv getw $gunInhibitVars] {
                return -code error "Error reading gun inbitbit pvs: $errorCode"
            }
            if {$inhibitA || $inhibitB} {
                return -code error "There is a gun inhibit, abort interleaving."
            }
            
            if $abort {
                catch {AbortInterleaving}
                return -code error "aborted."
            }
            set time [expr [clock seconds] - $startTime]
            if {$time>=$interTime} {
                break
            } else {
                after 500
                update
            }
        }
        #turn off supplies
        set startTime [clock seconds]
        if [catch {RunInterleaving -state off} result] {
            return -code error "Error turn off supplies: $result"
        }
        #wait for 30 seconds after turn off
        while {1} {
            if [pv getw $gunInhibitVars] {
                return -code error "Error reading gun inbitbit pvs: $errorCode"
            }
            if {$inhibitA || $inhibitB} {
                return -code error "There is a gun inhibit, abort interleaving."
            }
            if $abort {
                catch {AbortInterleaving}
                return -code error "aborted."
            }
            set time [expr [clock seconds] - $startTime]
            if {$time>=$interTime} {
                break
            } else {
                after 500
                update
            }
        }
    }
}

proc StartInterleavingControl {args} {
    global timer errorCode abort injection interTime gunInhibitVars inhibitA inhibitB errorCode testMode
  
  
    SetStatus "Check if double stop is open..."
    
    while {1} {
        if [catch {exec cavget -list=ACIS:LAO_LTB_2S_CLSD -pend=10 -printErrors } doubleStopClosed] {
            return -code error "Error reading double stop open status: $doubleStopClosed."
        }
        
        if $doubleStopClosed {
            set answer  [APSMultipleChoice [APSUniqueName .] -title "Double Stop is Closed!" \
                           -question "Double stop is closed, please open it before start interleaving" \
                           -returnList {Check-Again Continue Abort} \
                           -labelList {Check-again Continue Abort} ]
            switch $answer {
                Continue {
                    break
                }
                Abort {
                    SetStatus "Interleaving was aborted because of double-stop is not open."
                    return
                }
                Check-again {
                    continue
                }
            }
        } else {
            break
        }
    }
    
    #check if gate volves are open
    set gateList {L1:GV1 L1:PC1:GV1}
    if [catch {exec cavget -list=[join $gateList ,] -list=:positionMI -printErrors -pend=20} valList] {
        SetStatus "Error reading L1:PC1:GV2,L1:GV1,L1:PC1:GV1 gate volves: $valList"
    }
    foreach gate $gateList val $valList {
        if {$gate=="Closed"} {
            SetStatus "Open $volve ..."
            if [catch {exec cavput -list=${gate}:openCmdBO=1 -pend=20} result] {
                SetStatus "Error opening $volve : $result"
                return
            }
        }
    }

    SetStatus "Start interleaving..."
    if {!$testMode} {
        if [catch {InterleavingWithTopup } result] {
            SetStatus "InterleavingWithAlphaMagnet : $result"
        }
    } else {
        if [catch {InterleavingWithTimeInterval } result] {
            SetStatus "InterleavingWithTimeInterval : $result"
        }
    }
    SetStatus "done."
}

proc InterleavingWithTopup {args} {
    global topupList gunInhibitVars abort
    eval global $topupList
    eval global $gunInhibitVars

    pv getw topupState
    update
    if {$topupState=="Disable"} {
	return -code "Topup is disabed, can not start interleaving with topup."
    }

    set prevState ""
    
    while {1} {
        if [pv getw $gunInhibitVars] {
            return -code err "Error reading gun inhibit status pvs: $errorCode"
        }
        if {$inhibitA || $inhibitB} {
            return -code error "There is a gun inhibit, abort interleaving."
        }
        if [pv getw $topupList] {
	    return -code error "Error reading topup pvs: $errorCcode"
	}
	if {$topupState=="Disable"} {
	    return -code error "Topup is disabled, aborting interleaving."
	}
        if $abort {
            catch {AbortInterleaving}
            return -code error "aborted."
        }
        if {$inj2WarnCalc<=1} {
            set state on
        } elseif {$injecting=="NoWarning"} {
            set state off
         } else {
            set state ""
        }
        if [string length $state] {
            if {![string length $prevState] || [string compare $prevState $state]!=0 } {
                switch $state {
                    on {
                        SetStatus "Switch to RG2 gun beam"
                    }
                    off {
                        SetStatus "Switch to PC gun beam"
                    }
                }
                if [catch {RunInterleaving -state $state} result] {
                    return -code error "Error interleaving with alpha magnet: $result"
                }
            }
            set prevState $state
        }
        after 1000
        update
    }
}


proc ReadValue {args} {
    set state ""
    APSParseArguments {state}
    global pvList varList
    eval global $varList
    if [catch {exec cavget -list=[join $pvList ,] -pend=20} valueList] {
        return -code error "Error reading pv values: $result"
    }
    foreach var $varList val $valueList {
        if {$state=="off"} {
            if {$var=="LTPB1" || $var=="PTBB1" || $var=="PTBB2"} {
                continue
                #do not change the pvs for PC gun, keep them in standby mode
            }
        }
        set ${var}($state) $val
    }
}

proc EditConfig {args} {
    global configFile
    exec sddsedit $configFile
}

proc InstallConfig {args} {
    global configFile
    if ![APSYesNoPopUp "are you sure to install config file for other PVs?"] {
	SetStatus "install cancelled."
	return
    }
    set dir /home/helios/oagData/linac/configFiles
    set oldDir [pwd]
    set newFile [APSNextGenerationedName -directory $dir -name InterleavingLight.sdds-0000 -newFile 1 -separator -]
    exec cp $configFile $dir/$newFile
    cd $dir
    exec rm $newFile
    exec ln -s $newFile InterleavingLight.sdds
    cd $oldDir
}

proc BringUpScreens {args} {
    exec medm -x -attach /usr/local/iocapps/adlsys/par/psApp/PTBB2.adl &
    exec medm -x -attach /usr/local/iocapps/adlsys/par/psApp/PTBB1.adl &
    exec medm -x -attach /usr/local/iocapps/adlsys/par/psApp/LTPB1.adl &
    
}


set topupList {inj2WarnCalc topupState injecting}
set topupPVList {Mt:TopUpTime2WarnCalc Mt:TopUpAutoEnableC.VAL Mt:TopUpWarning2InjectorM.VAL}

if [pv linkw $topupList $topupPVList] {
    puts stderr "Error link $topuPVList: $errorCode"
    exit 1
}

set magList {LTP:B1:CurrentAO PTB:B1:CurrentAO PTB:B2:CurrentAO}
set magVarList {LTPB1 PTBB1 PTBB2}
set magVarList $magList
if [catch {exec cavget -list=[join $magList ,] -pend=10 } valList] {
    puts stderr "Error reading $magList: $valList"
    exit 1
}

foreach var $magVarList val $valList {
    set ${var}(on) $val
    set ${var}(off) Standby
}

APSFrame .f0 -parent .userFrame 
.userFrame.f0.frame configure -bd 0
set w .userFrame.f0.frame
APSLabel .x0 -parent $w -text "                                                   Beam To PAR                                           Beam to BB" -font bold

set pvFrame $w
foreach pv $magList var $magVarList {
    if [catch {exec cavget -list=${pv}.EGU -pend=20} units] {
        set units ""
    } else {
        set units "(${units})"
    }
    APSLabeledEntryFrame .x$var -parent $w -label "$pv $units" -variableList [list ${var}(on) ${var}(off)] \
	-width 40 -orientation horizontal
    #APSLabeledEntry .x$var -parent $w1 -label "$pv $units" -textVariable ${var}(on) -width 40
    #APSButton .delete -parent $w1.x$var -packOption "-side right" -size small -text "delete" -command "Delete -var $var"
    #APSLabeledEntry .x$var -parent $w2 -label "" -textVariable ${var}(off)  -width 40
    APSButton .delete -parent $w.x$var.frame -text "delete" -size small -command "Delete -var $var"
    incr index
}

proc Delete {args} {
    set var ""
    APSParseArguments {var}
    global magList pvFrame magVarList
    set index [lsearch -exact $magList $var]
    set magList [lreplace $magList $index $index]
    set magVarList $magList
  #  puts $magList
    destroy $pvFrame.x$var
  #  destroy $pvFrame.x2.x$var
}

proc Add {args} {
    global AddPVName
    set AddPVName ""
    
    APSDialogBox .pv -name "Add PV Box" -width 30 \
	-cancelCommand "" -okCommand AddNewPV
    APSLabeledEntry .name -parent .pv.userFrame -width 30 -textVariable AddPVName -label "PV Name:"
}
    
proc AddNewPV {args} {
    global AddPVName pvFrame magList magVarList
    if ![string length $AddPVName] {
	return
    }
    if [lsearch -exact $magList $AddPVName]>=0 {
	return -code error "$AddPVName already exist!"
    }
    if [catch {exec cavget -list=$AddPVName -pend=10 -printErrors} value] {
	return -code error "Error: problem reading $AddPVName:: $value!"
    }
    set var $AddPVName
    global $var
    set ${var}(on) $value
    set ${var}(off) Standby
    set pv $var
    if [catch {exec cavget -list=${pv}.EGU -pend=20} units] {
        set units ""
    } else {
        set units "(${units})"
    }
    APSLabeledEntryFrame .x$var -parent $pvFrame -label "$pv $units" -variableList [list ${var}(on) ${var}(off)] \
	-width 40 -orientation horizontal
    APSButton .delete -parent $pvFrame.x$var.frame -packOption "-side right" -size small -text "delete" -command "Delete -var $var"
    
    lappend magList $var
    set magVarList $magList
}

proc SaveConfig {args} {
    global configFile saveDescription loadDescription interTime testMode outDir magList magVarList
    set configFile0 /home/helios/oagData/linac/configFiles/InterleavingLight.sdds
    if ![string length $saveDescription] {
        return -code error "Please provide a description to save the config."
    }
    set onList ""
    set offList ""
    foreach var $magVarList {
        global $var
        lappend onList [set ${var}(on)]
        lappend offList [set ${var}(off)]
    }
    
    set oldDir [pwd]
    set dir [file dir $configFile]
    set newName [APSNextGenerationedName -directory $dir -name [file tail $configFile]-0000 -separator - -newFile 1]
    SetStatus "saving configuration ..."
    if [catch {exec sddsmakedataset -pipe=out  -col=ControlName,type=string -data=[join $magList ,] \
                 -col=VariableName,type=string -data=[join $magVarList ,] \
                 -col=on,type=string -data=[join $onList ,] \
                 -col=off,type=string -data=[join $offList ,] \
                 -par=InterleavingTime,type=double -data=$interTime \
                 -par=TestMode,type=short -data=$testMode \
                 | sddsprocess -pipe=in $dir/$newName "-print=par,Description,[APSMakeSafeQualifierString $saveDescription]" \
                 "-print=par,TimeStamp,[exec date]" } result] {
        return -code error "Error in saving configuration: $result"
    }
    cd $dir 
    exec rm InterleavingLight.sdds
    exec ln -s $newName InterleavingLight.sdds
    cd $oldDir
    if [APSYesNoPopUp "Save new configuration into database?"] {
	set dir /home/helios/oagData/linac/configFiles
	set newName [APSNextGenerationedName -directory $dir -name [file tail $configFile]-0000 -separator - -newFile 1]
	cd $dir
	exec cp $configFile $newName
	exec rm InterleavingLight.sdds
	exec ln -s $newName InterleavingLight.sdds
	cd $oldDir
    }
    SetStatus "$saveDescription    config saved."
    set loadDescription $saveDescription
    set saveDescription ""
}

set configDir /home/helios/oagData/linac/configFiles/
proc LoadConfig {args} {
    global magList magVarList onList offList loadDescription configDir
    set oldDir [pwd]
    cd $configDir
    
    
}

proc LoadConfig0 {TimeStamp Description Filename} {
    global magList magVarList onList offList loadDescription  configDir
    
    set configFile $configDir/$Filename
    set loadDescription "$TimeStamp : $Description"
    
    set magList [join [exec sdds2stream $configFile -col=ControlName]]
    set magVarList [join [exec sdds2stream $configFile -col=VariableName]]
    set onList [join [exec sdds2stream $configFile -col=on]]
    set offList [join [exec sdds2stream $configFile -col=off]]
    set index 0
    foreach var $magVarList {
        global $var
        set ${var}(on) [lindex $onList $index]
        set ${var}(off) [lindex $offList $index]
        incr index
    }
    update
    SetStatus "config restored."
}

proc LoadSelectedConfig {args} {
    global SelectFile configDir magList magVarList onList offList loadDescription interTime testMode
    set selection [.files.list.lbox curselection]
    set oldDir [pwd]
    set filename [lindex [exec sdds2stream -col=Filename $SelectFile] $selection]
    set configFile $configDir/$filename
    set TimeStamp [exec sdds2stream -par=TimeStamp $configFile]
    set Description [exec sdds2stream -par=Description $configFile]
    set loadDescription "$TimeStamp : $Description"
    if [catch {exec sdds2stream -par=InterleavingTime $configFile} time] {
      # do nothing if does not exist
    } else {
        set interTime [format %.2f $time]
    }
    if [catch {exec sdds2stream -par=TestMode $configFile} test] {
        # do nothing if does not exit
        set testMode 0
    } else {
        set testMode $test
    }
    global pvFrame
    foreach var $magVarList {
	if [winfo exist $pvFrame.x$var] {
	    destroy $pvFrame.x$var
	}
    }
    set magList [join [exec sdds2stream $configFile -col=ControlName]]
    #set magVarList [join [exec sdds2stream $configFile -col=VariableName]]
    set magVarList $magList
    set onList [join [exec sdds2stream $configFile -col=on]]
    set offList [join [exec sdds2stream $configFile -col=off]]
    set index 0
    SetStatus "Loading interleaving config..."
    global pvFrame
    
    foreach var $magVarList pv $magList {
        global $var
        set ${var}(on) [lindex $onList $index]
        set ${var}(off) [lindex $offList $index]
        incr index
	if [catch {exec cavget -list=${pv}.EGU -pend=20} units] {
	    set units ""
	} else {
	    set units "(${units})"
	}
	APSLabeledEntryFrame .x$var -parent $pvFrame -label "$pv $units" -variableList [list ${var}(on) ${var}(off)] \
	    -width 40 -orientation horizontal
	APSButton .delete -parent $pvFrame.x$var.frame -packOption "-side right" -size small -text "delete" -command "Delete -var $var"
    }
    SetStatus "done."
    update
}
set configFile /home/helios/oagData/linac/configFiles/InterleavingLight.sdds
proc LoadConfig {args} {
    global magList magVarList onList offList SelectFile interTime
  
    set oldDir [pwd]
    cd /home/helios/oagData/linac/configFiles
    set files [glob -nocomplain InterleavingLight.sdds-????]
    set tmpFile /tmp/[APSTmpString]
    if [catch {eval exec sddscombine $files -pipe=out -collapse \
                 | sddssort -pipe=in $tmpFile  -col=Filename,decr -col=Description,incr } result] {
        return -code error "Error1: $result"
    }
    set SelectFile $tmpFile
    APSMakeSDDSListbox $tmpFile .files -tilte "Choose a config file to restore" \
      -page 0  -packOption "-side top -expand true -fill y" \
      -callback LoadConfig0 TimeStamp Description Filename
    APSButton .delete -parent .files.ops -text "DELETE" -command "DeleteConfig"
    APSButton .ok -parent .files.ops -text "OK" -command "LoadSelectedConfig;destroy .files"
    
    #exec rm $tmpFile
    cd $oldDir
   # set configFile /home/helios/oagData/linac/configFiles/InterleavingControl.sdds
    
}

proc DeleteConfig {args} {
    global SelectFile configDir
    set selection [.files.list.lbox curselection]
    set oldDir [pwd]
    set filename [lindex [exec sdds2stream -col=Filename $SelectFile] $selection]
    cd $configDir
    set linkFile [file readlink InterleavingControl.sdds]
    set desc [exec sdds2stream -par=Description $configDir/$filename]
    SetStatus "deleting config: $desc "
    if [string compare $linkFile $filename]==0 {
        exec rm $filename
        exec rm InterleavingControl.sdds
        set files [glob InterleavingControl.sdds-????]
        set newestFile [lindex [lsort -decreasing $files] 0]
        exec ln -s $newestFile InterleavingControl.sdds
    } else {
        exec rm $filename
    }
    .files.list.lbox delete $selection $selection
    cd $oldDir
    SetStatus "done."
}
set interTime 60
set testMode 0
APSLabeledEntry .time -parent .userFrame -textVariable interTime -width 60 -label "Interleaving time (0-240 seconds):"
bind .userFrame.time.entry <Leave> CheckInterleavingTime
APSRadioButtonFrame .test -parent .userFrame -variable testMode -label "Test mode?" -buttonList {Yes No} \
  -valueList {1 0} -orientation horizontal -contextHelp "In testing mode, the interleaving is done in interleaving time interval, otherwise, it is automatically decided by the alpha magnet current (140 -- RG2 beam time, 0 - PC gun beam time.)"
checkbutton .userFrame.test.cb -relief flat \
    -command "APSEnableDisableWidget .userFrame.test.frame.button1 -toggle 1"
pack .userFrame.test.cb -side left
APSDisableWidget .userFrame.test.frame.button1

set gunInhibitPVs {ACIS:LAL_CE_INHIBIT ACIS:LBL_GUN_INH}
set gunInhibitVars {inhibitA inhibitB}
if [pv linkw $gunInhibitVars $gunInhibitPVs] {
    puts stderr "Error in connecting ACIS:LAL_CE_INHIBIT ACIS:LBL_GUN_INH : $errorCode"
    exit 1
}

set saveConfiguration ""
set loadDescription ""
set saveDescription ""
set abort 0
set outDir [APSGoToDailyDirectory -subdirectory interleave]
#set  configFile0 /home/helios/oagData/linac/configFiles/InterleavingLight.sdds
exec cp /home/helios/oagData/linac/configFiles/InterleavingLight.sdds $outDir/InterleavingLight.sdds
set configFile $outDir/InterleavingLight.sdds
#APSLabeledEntry .file -parent .userFrame -label "Config file for others:" -textVariable configFile -width 75
#APSButton .edit -parent .userFrame.file -text "E" -size small -command EditConfig  -packOption "-side right"
#APSButton .in -parent .userFrame.file -packOption "-side right" -text "I" -size small -command InstallConfig
APSLabeledEntry .desc -parent .userFrame -label "Save description:" -textVariable saveDescription -width 70
APSButton .start -parent .userFrame -text "Start" -command "StartInterleavingControl"
APSButton .abort -parent .userFrame -text "Abort" -command "set abort 1"
APSButton .medm -parent .userFrame -text "Screens" -command "BringUpScreens"
APSButton .add -parent .userFrame -text "Add" -command "Add"
APSButton .save -parent .userFrame -text "Save Config" -command "SaveConfig"
APSButton .load -parent .userFrame -text "Load Config" -command "LoadConfig"
