#!/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 LinacInterleavingControl -overview "Start SR ID auto steering server."
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 SaveConfig {args} {
    global configFile pvList varList saveDescription loadDescription interTime testMode
    
    if ![string length $saveDescription] {
        return -code error "Please provide a description to save the config."
    }
    set onList ""
    set offList ""
    foreach var $varList {
        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 $pvList ,] \
                 -col=VariableName,type=string -data=[join $varList ,] \
                 -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 InterleavingControl.sdds
    exec ln -s $newName InterleavingControl.sdds
    cd $oldDir
    SetStatus "$saveDescription    config saved."
    set loadDescription $saveDescription
    set saveDescription ""
}

proc RunInterleaving {args} {
    set state ""
    APSParseArguments {state}
    global configFile pvList varList
    eval global $varList
    
    set putList ""
    foreach pv $pvList var $varList {
        if {[regexp "PTB" $pv] || [regexp "LTP" $pv]} {
            #PTB and LTP need ramp
            continue
        } else {
            lappend putList $pv=[set ${var}($state)]
        }
    }
    switch $state {
        on  {
            set comment "RG2 beam time."
        }
        off {
            set comment "PCG beam time."
        }
        
    }
    SetStatus "$comment"
    if [catch {APScavput -list=[join $putList ,] -pend=30} result] {
        return -code error "Error in setting [join $putList ,]: $result"
    }
    #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
    
    if {![info exist LTPB1] && ![info exist PTBB1] && ![info exist PTBB2]} {
        #nothing to do
       # SetStatus "nothing to do."
        return
    }
    set putList ""
    set putList1 ""
    switch $state {
        on {
            SetStatus "set LTP and PTB power supplies for RG2..."
        }
        off {
            SetStatus "set LTP and PTB power supplies for PC gun..."
        }
    }
    foreach m {LTP:B1 PTB:B1 PTB:B2} var {LTPB1 PTBB1 PTBB2} {
        if ![info exist $var] {
            continue
        }
       #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]
        if {$ao!="standyBy" && $ao>50} {
            set state0 on
        } else {
            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 {$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 0: $result"
                    }
                }
                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"
        }
    }
    #SetStatus "done."
}

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 InterleavingWithAlphaMagnet {args} {
    global alphaCurrent timer errorCode abort injection interTime gunInhibitVars inhibitA inhibitB errorCode testMode

    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 $abort {
            catch {AbortInterleaving}
            return -code error "aborted."
        }
        if [pv getw alphaCurrent] {
            return -code error "Error reading alpha magnet current: $errorCode"
        }
        if {$alphaCurrent<15} {
            set state off
        } elseif {$alphaCurrent>=140} {
            set state on
        } 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 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..."
    if {0} {
    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 {InterleavingWithAlphaMagnet } result] {
            SetStatus "InterleavingWithAlphaMagnet : $result"
        }
    } else {
        if [catch {InterleavingWithTimeInterval } result] {
            SetStatus "InterleavingWithTimeInterval : $result"
        }
    }
    SetStatus "done."
}

set configDir  /home/helios/oagData/linac/configFiles

proc LoadConfig0 {TimeStamp Description Filename} {
    global pvList varList onList offList loadDescription  configDir
    
    set configFile $configDir/$Filename
    set loadDescription "$TimeStamp : $Description"
    
    set pvList [join [exec sdds2stream $configFile -col=ControlName]]
    set varList [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 $varList {
        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 pvList varList 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
    }
    set pvList [join [exec sdds2stream $configFile -col=ControlName]]
    set varList [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
    SetStatus "Loading interleaving config..."
    foreach var $varList {
        global $var
        set ${var}(on) [lindex $onList $index]
        set ${var}(off) [lindex $offList $index]
        incr index
    }
    SetStatus "done."
    update
}

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 configFile /home/helios/oagData/linac/configFiles/InterleavingControl.sdds
proc LoadConfig {args} {
    global pvList varList onList offList SelectFile interTime
  
    set oldDir [pwd]
    cd /home/helios/oagData/linac/configFiles
    set files [glob -nocomplain InterleavingControl.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 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 LoadSCRConfig0 {args} {
    set type ""
    APSParseArguments {type}
    global pvList varList Interleaving loadDescription
    set SCRFile /home/helios/oagData/SCR/snapshots/LPL/$Interleaving(ShortFilename)
    eval global $varList
    
    switch $type {
        RG2 {
            set item on
        }
        PCGun {
            set item off
        }
    }
    SetStatus "Doading $type config from $Interleaving(ShortFilename) ..."
    set loadDescription "[exec sdds2stream -par=SnapshotDescription $SCRFile]; $Interleaving(ShortFilename)"
   
    set tmpFile /tmp/[APSTmpString]
    set opt -match=col,ControlName=[lindex $pvList 0]
    for {set i 1} {$i<[llength $pvList]} {incr i} {
        append opt ,ControlName=[lindex $pvList $i],|
    }

    APSAddToTmpFileList -ID linac -fileList $tmpFile
    if [catch {exec sddsprocess $SCRFile $opt $tmpFile } result] {
        return -code error "Error1: $result"
    }
    set pvList1 [exec sdds2stream -col=ControlName $tmpFile]
    set valList1 [exec sdds2stream -col=ValueString $tmpFile]
    foreach pv $pvList1 val $valList1  {
        set i1 [lsearch -exact $pvList $pv]
        set var [lindex $varList $i1]
        if [catch {expr $val/2.0} result] {
            set ${var}($item) $val
        } else {
            set ${var}($item) [format %0.3f $val]
        }
    }
    SetStatus "done."
}
proc LoadSCRConfig {args} {
    set type ""
    APSParseArguments {type}
    global pvList varList Interleaving
    APSDialogBox .scr -name "Choose LPL SCR" \
        -okCommand "LoadSCRConfig0 -type $type;destroy .scr" \
        -cancelCommand "destroy .scr"
    APSAddSCRDialog .file -parent .scr.userFrame -system LPL \
        -arrayName Interleaving -defaultFile Interleaving(ShortFilename) \
        -label "Choose SCR file"
}

set timestamp [exec sdds2stream -par=TimeStamp $configFile]
set desc [exec sdds2stream -par=Description $configFile]
LoadConfig0 "$timestamp" "$desc" InterleavingControl.sdds

#if [catch {exec cavget -list=L1:RFG:RF:SW2:positionMI -pend=30 -printErrors} gun] {
#    puts stderr "Error reading RF gun selection: $gun"
#    exit 1
#}

#APSLabeledOutput .gun -parent .userFrame -label "RG Gun in use:" -textVariable gun -width 50



APSFrameGrid .grid -parent .userFrame -xList {x1 x2} -label "Parameter setting"
set w1 .userFrame.grid.x1
set w2 .userFrame.grid.x2
APSLabel .x0 -parent $w1 -text "RG2 beam time" -font bold  -packOption "-side top"
APSLabel .x0 -parent $w2 -text "PCG beam time" -font bold -packOption "-side top"
set index 1
foreach pv $pvList var $varList {
    if [catch {exec cavget -list=${pv}.EGU -pend=20} units] {
        set units ""
    } else {
        set units "(${units})"
    }
    APSLabeledEntry .x$index -parent $w1 -label "$pv $units" -textVariable ${var}(on) -width 40
    APSLabeledEntry .x$index -parent $w2 -label "" -textVariable ${var}(off) -width 40
    incr index
}
set interTime 60
set testMode 0
APSLabeledEntry .time -parent $w1 -textVariable interTime -width 40 -label "Interleaving time (0-240 seconds):"
bind $w1.time.entry <Leave> CheckInterleavingTime
APSRadioButtonFrame .test -parent $w2 -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 $w2.test.cb -relief flat \
    -command "APSEnableDisableWidget $w2.test.frame.button1 -toggle 1"
pack $w2.test.cb -side right
APSDisableWidget $w2.test.frame.button1

if [pv linkw alphaCurrent L1:RG2:LFA:CurrentAO] {
    puts stderr "Errror connecting L1:RG2:LFA:CurrentAO: $errorCode"
    exit 1
}
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
}
#if [pv linkw timer S:OPS:TopUpTime2EnableAlh] {
#    puts stderr "Error in connecting S:OPS:TopUpTime2EnableAlh: $errorCode"
#    exit 1
#}
#if [pv linkw injection Mt:SRinjectDoneBO] {
#     puts stderr "Error in connecting Mt:SRinjectDoneBO: $errorCode"
#    exit 1
#}


set saveDescription ""
APSLabeledEntry .desc1 -parent .userFrame -label "Load description:" -textVariable loadDescription \
  -width 90 -packOption "-fill x" -editButton 1
APSLabeledEntry .desc -parent .userFrame -label "Save description:" -textVariable saveDescription  \
   -width 90  -packOption "-fill x" -editButton 1
set abort 0
APSFrame .f1 -parent .userFrame -packOption "-expand true"
APSFrame .f2 -parent .userFrame -packOption "-expand true"
set w1 .userFrame.f1.frame
set w2 .userFrame.f2.frame

APSButton .start -parent $w1 -text "Start" -command "StartInterleavingControl"
APSButton .abort -parent $w1 -text "Abort" -command "set abort 1"
APSButton .save -parent $w1 -text "Save Configuraion" -command "SaveConfig"
APSButton .read -parent $w2 -text "Load Current Config for RG2" -command "ReadValue -state on"
APSButton .read2 -parent $w2 -text "Load Current Config  for PC Gun" -command "ReadValue -state off"
APSButton .read1 -parent $w2 -text "Load Interleaving Config" -command "LoadConfig"
APSButton .read3 -parent $w2 -text "Load RG2 Config" -command "LoadSCRConfig -type RG2"
APSButton .read4 -parent $w2 -text "Load PC Gun Config" -command "LoadSCRConfig -type PCGun"
APSButton .setpc -parent $w1 -text "Setup injector for PC Gun Beam" -command "RunInterleaving -state off;SetStatus \"done\""
