#!/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)]
APSDebugPath

proc GetOrbitInfo {args} {
    global orbitData orbitFileOpened dataDir orbitElementNames hcorrnames vcorrnames bpmnames
    global hLastCorr vLastCorr
    set orbitfile $dataDir/refMatrices/booster.twi
    
    if [catch {exec sddsprocess $orbitfile -pipe=out -match=col,ElementType=HKICK \
                   | sddssort -pipe -col=s \
                   | sddsprocess -pipe -clip=0,1,invert \
                   | sdds2stream -pipe -col=ElementName } hLastCorr] {
        return -code error $hLastCorr
    }
    set hLastCorr [string trim $hLastCorr]
    if [catch {exec sddsprocess $orbitfile -pipe=out -match=col,ElementType=VKICK \
                   | sddssort -pipe -col=s \
                   | sddsprocess -pipe -clip=0,1,invert \
                   | sdds2stream -pipe -col=ElementName } vLastCorr] {
        return -code error $vLastCorr
    }
    set vLastCorr [string trim $vLastCorr]
    if [catch {sdds load $orbitfile tmpData} result] {
        return -code error $result
    }
    set names [lindex $tmpData(Column.ElementName) 0]
    set types [lindex $tmpData(Column.ElementType) 0]
    set index 0
    set hcorrnames ""
    set vcorrnames ""
    set bpmnames ""
    foreach name $names type $types {
        set coord ""
        if {$type=="HKICK"} {
            if [string match "*H" $name] {
                set coord x
                lappend hcorrnames $name 
            }
        } elseif {$type=="VKICK"} {
            if [string match "*V" $name] {
                set coord y
                lappend vcorrnames $name
            }
        } elseif {$type=="MONI"} {
            set coord bmp
            lappend bpmnames $name
            set orbitData($name.betax) [lindex [lindex $tmpData(Column.betax) 0] $index]
            set orbitData($name.betay) [lindex [lindex $tmpData(Column.betay) 0] $index]
            set orbitData($name.psix) [lindex [lindex $tmpData(Column.psix) 0] $index]
            set orbitData($name.psiy) [lindex [lindex $tmpData(Column.psiy) 0] $index] 
        }
        if [string length $coord] { 
            set orbitData($name.s) [lindex [lindex $tmpData(Column.s) 0] $index] 
            switch $coord {
                x -
                y {
                    set orbitData($name.beta)  [lindex [lindex $tmpData(Column.beta$coord) 0] $index]
                    set orbitData($name.psi) [lindex [lindex $tmpData(Column.psi$coord) 0] $index]
                }
            }
        }
        incr index
    }
    set hcorrnames [lsort $hcorrnames]
    set vcorrnames [lsort $vcorrnames]
    set orbitFileOpened 1 
    unset tmpData
}

proc SetStatus {text} {
    global mainStatus
    set mainStatus "[exec date +%H:%M:%S] $text"
    update
}

proc bbconfigure {args} {
    global c1hname c2hname c3hname p1hname
    global c1hs c2hs c3hs p1hs
    global c1hbeta c2hbeta c3hbeta p1hbeta
    global c1hpsi c2hpsi c3hpsi p1hpsi
    global c1hk c2hk c3hk p1hk
    global c1hcur c2hcur c3hcur p1hcur
    global bbhconfigured 

    global c1vname c2vname c3vname p1vname
    global c1vs c2vs c3vs p1vs
    global c1vbeta c2vbeta c3vbeta p1vbeta
    global c1vpsi c2vpsi c3vpsi p1vpsi
    global c1vk c2vk c3vk p1vk
    global c1vcur c2vcur c3vcur p1vcur
    global bbvconfigured 

    global orbitfile xorbit orbitData orbitFileOpened hcorrnames vcorrnames bpmnames nux nuy PI
    global LastHCorr LastVCorr frameIndex
    set plane ""
    APSParseArguments {plane}
    
    # Setup
    if {!$orbitFileOpened} {
        if [catch {GetOrbitInfo} result] {
            return -code error $result
        }
    }
    if ![string length $plane] {
        if !$frameIndex {
            set plane h
        } else {
            set plane v
        }
    }
    switch $plane {
        H -
        h {
            set coord x
            set corrList [list $c1hname $c2hname $c3hname]
            set bpm $p1hname
            set varList {c1h c2h c3h}
            set corrnames $hcorrnames
            set plane h
        }
        V -
        v {
            set coord y
            set corrList [list $c1vname $c2vname $c3vname]
            set bpm $p1vname
            set varList {c1v c2v c3v}
            set corrnames $vcorrnames
            set plane v
        }
        default {
            return -code error "Unknown plane - $plane given."
        }
    }
    if [lsearch $bpmnames $bpm]<0 {
        return -code error "bpm name -- $name is not correct."
    }
    
    set p1${plane}s [format %.6f $orbitData($bpm.s)]  
    foreach var {beta psi} {
        set p1${plane}psi [format %.6f $orbitData($bpm.psi$coord)]
        set p1${plane}beta [format %.6f $orbitData($bpm.beta$coord)]
    }
    foreach name $corrList var $varList {
        if [lsearch $corrnames $name]<0 {
            return -code error "corrector name -- $name is not correct, it has to be a $plane kicker."
        }
        set ${var}s [format %.6f $orbitData($name.s)]
        set ${var}psi [format %.6f $orbitData($name.psi)]
        set ${var}beta [format %.6f $orbitData($name.beta)] 
    }
    if [catch {ComputeCoefficients -plane $plane} result] {
        SetStatus $result
        return
    }
    set bb${plane}configured 1
    bbsetstrength -plane $plane
}

proc ComputeCoefficients {args} {
    set plane h
    APSParseArguments {plane}
    
    global nux nuy PI hLastCorr vLastCorr
    set plane [string tolower $plane] 
    switch $plane {
        h {
            set nu [expr $nux*2*$PI]
            set coord x
        }
        v {
            set nu [expr $nuy*2*$PI]
            set coord y
        }
    }
    
    foreach nm {c1 c2 c3 p1} {
        foreach var {name s psi beta} {
            global ${nm}${plane}$var
            set ${nm}$var [set ${nm}${plane}$var]
        }
        global ${nm}${plane}k
    }
    set lastCorr [set ${plane}LastCorr]
    set error 0
    set corrList [list $c1name $c2name $c3name]
    set index [lsearch -exact $corrList $lastCorr] 
    if {$index==0} {
        #last corrector is c1, then has to be c2<c3<c1, (c1 is the max) so c2>c3 not allowed
        if {$c2s>$c3s} {
             set error 1
        } else {
            #if p1 is between c1 and c2, then p1<c2<c3<c1; if p1 is between c2 and c3, between c2<p1<c3<c1 
            #so p1>c3 not allowed
            if {$p1s>$c3s} {
                set error 1
            } else {
                set delta31 [expr $c3psi - $c1psi + $nu]
                set delta32 [expr $c3psi - $c2psi]
                set delta21 [expr $c2psi - $c1psi + $nu]
                if {$p1s<$c2s} {
                    set w12 1
                    set delta_p11 [expr $p1psi - $c1psi + $nu]
                } else {
                    set w12 0
                    set delta_p31 [expr $c3psi - $p1psi]
                }
            }
        }
    } elseif {$index==1} {
        #last corrector is c2, then c3<c1<c2, c1 and c3 can not be greater than c2, c3 must <c2
        if {$c3s>$c2s} {
            set error 1
        }  else {
            #if p1 is between c1 and c2, then c3<c1<p1<c2; if p1 is between c2 and c3, then p1<c3<c1<c2 
            #so p1>c3 and p1<c2 not allowed
            if {$p1s>$c3s && $p1s<$c2s} {
                set error 1
            } else { 
                set delta31 [expr $c3psi - $c1psi + $nu]
                set delta32 [expr $c3psi - $c2psi + $nu]
                set delta21 [expr $c2psi - $c1psi] 
                if {$p1psi>$c1psi} {
                    set w12 1
                    set delta_p11 [expr $p1psi - $c1psi]
                } else {
                    set w12 0
                    set detal_p31 [expr $c3psi - $p1psi]
                }
            }
        }
    } else {
        #last corrector is c3, or last corrector is not in the list, it has to be
        #c1<c2<c3
        if {$c1s>$c2s || $c2s>$c3s} {
            set error 1
        } else {
            #if p1 is between 1 and 2, then c1<p1<c2<c3, if p1 is between 2 and 3, then c1<c2<p1<c3
            #so p1>c3 not allowed
            if {$p1s>$c3s} {
                set error 1
            } else {
                set delta31 [expr $c3psi - $c1psi]
                set delta32 [expr $c3psi - $c2psi]
                set delta21 [expr $c2psi - $c1psi] 
                if {$p1psi<$c2psi} {
                    set w12 1
                    set delta_p11 [expr $p1psi - $c1psi]
                } else {
                    set w12 0
                    set detal_p31 [expr $c3psi - $p1psi]
                }
            }
        }
    }
    if $error {
        set c1${plane}k error
        set c2${plane}k error
        set c3${plane}k error
        set p1${plane}k error
        return -code error "corrector and bpm are not in the lattice order."
    }
    set c1${plane}k 1.0
    set c2${plane}k [format "%.6f" [expr -sqrt($c1beta/$c2beta)*sin($delta31)/sin($delta32)]]
    set c3${plane}k [format "%.6f" [expr sqrt($c1beta/$c3beta)*sin($delta21)/sin($delta32)]]
    set c3k [set c3${plane}k]
    if $w12 {
        set p1${plane}k [format "%.6f" [expr sqrt($c1beta*$p1beta)*sin($delta_p11)]]
    } else {
        set p1${plane}k [format "%.6f" [expr $c3k*sqrt($c3beta*$p1beta)*sin($detal_p31)]]
    }
    #monitor current values
    set pvvarlist [list c1${plane}cur c2${plane}cur c3${plane}cur p1${plane}cur]
    eval global $pvvarlist
    set pvnamelist [list $c1name:CurrentAO $c2name:CurrentAO $c3name:CurrentAO $p1name:ms:$coord]
    set res [pv linkw $pvvarlist $pvnamelist]
    if {$res != 0} {
        SetStatus "Cannot link to $pvnamelist" 
        return
    }
    set res [pv umon $pvvarlist]
    if {$res != 0} {
        SetStatus "Cannot set monitors on $pvnamelist" 
        return
    }
}


# **************************** bbsetramp *******************************
proc bbsetramp {args} {
    global c1hname c2hname c3hname p1hname
    global c1hstrength c2hstrength c3hstrength p1hval 
    global bbhconfigured 

    global c1vname c2vname c3vname p1vname
    global c1vstrength c2vstrength c3vstrength p1vval 
    global bbvconfigured

    global loadRamp bumpStart hcorrnames vcorrnames outputDir

    set plane h  
    APSParseArguments {plane}
    set plane [string tolower $plane]
    if {![set bb${plane}configured]} {
        SetStatus "Horizontal not configured"
        return
    }
    if {$plane=="h"} {
        set orbitChange $p1hval
    } else {
        set orbitChange $p1vval
    }
    foreach nm {c1 c2 c3} {
        set ${nm}name [set ${nm}${plane}name]
        set ${nm}strength [set ${nm}${plane}strength]
    } 
    set origtable $outputDir/booster_orig_ramp.$plane
    if ![file exist $origtable] {
       # APSAddToTmpFileList -ID boosterbump -fileList $origtable
        SetStatus "Saving original $plane plane ramp table..."
        if [catch {SaveRampTable -corrList [set ${plane}corrnames] -ramptable $origtable \
                       -description "original ramp table" } result] {
            SetStatus $result
            return
        }
    }
    set index 1
    while {1} {
        set rampfile $outputDir/ramp_${c1name}_${c2name}_${c3name}_[format %03d ${index}].bump
        if ![file exist $rampfile] {
            break
        }
        incr index
    }
   
    regsub {bump} $rampfile before beforeBump
    #APSAddToTmpFileList -ID boosterbump -fileList $beforeBump
    SetStatus "Save current ramp table before bump:"
    if [catch {SaveRampTable -corrList [list $c1name $c2name $c3name] \
                   -ramptable $beforeBump -description "before bump" } result] {
        SetStatus $result
        return
    }
    SetStatus "Generating ramptable - $rampfile"
   # APSAddToTmpFileList -ID boosterbump -fileList "$rampfile"
    if [catch {exec  sddsboosterbump \
		   $rampfile -corrector=name=$c1name,bump=$c1strength -orbitChange=$orbitChange \
		   -corrector=name=$c2name,bump=$c2strength \
		   -corrector=name=$c3name,bump=$c3strength -bumptime=$bumpStart} result] {
        SetStatus $result
        return
    } 
    exec sddsplot -grap=sym,vary=sub,fill,conn=sub -split=page -group=page -sep=2 \
        -title=@ControlName -legend=par=Description \
        -col=RampTime,RampSetpoint $beforeBump  $rampfile &
    if $loadRamp {
        if ![APSYesNoPopUp "Load the ramp table with bump now?"] {
            return
        }
        SetStatus "Loading ramptable - $rampfile"
        if [catch {exec loadbramp -${plane} $rampfile} result] {
            SetStatus "Cannot load $rampfile\n$result"
            return
        }
    }
    SetStatus "done."
}

# **************************** bbsetstrength ***************************
proc bbsetstrength {args} {
    global c1hname c2hname c3hname p1hname
    global c1hs c2hs c3hs p1hs
    global c1hbeta c2hbeta c3hbeta p1hbeta
    global c1hpsi c2hpsi c3hpsi p1hpsi
    global c1hk c2hk c3hk p1hk
    global c1hstrength c2hstrength c3hstrength p1hval 
    global bbhconfigured

    global c1vname c2vname c3vname p1vname
    global c1vs c2vs c3vs p1vs
    global c1vbeta c2vbeta c3vbeta p1vbeta
    global c1vpsi c2vpsi c3vpsi p1vpsi
    global c1vk c2vk c3vk p1vk
    global c1vstrength c2vstrength c3vstrength p1vval 
    global bbvconfigured

    global BMRAD2AMP

    set plane H
    APSParseArguments {plane}
    set plane [string toupper $plane]
    if {$plane=="H"} {
        if {$bbhconfigured} {
            set delta [expr $p1hval/$p1hk]
            set c1hstrength [format "%.6f" [expr $delta*$c1hk*$BMRAD2AMP]]
            set c2hstrength [format "%.6f" [expr $delta*$c2hk*$BMRAD2AMP]]
            set c3hstrength [format "%.6f" [expr $delta*$c3hk*$BMRAD2AMP]]
        } else {
            set delta "Not Configured"
            set c1hstrength "Not Configured"
            set c2hstrength "Not Configured"
            set c3hstrength "Not Configured"
        }
    } else { 
        if {$bbvconfigured} {
            set delta [expr $p1vval/$p1vk]
            set c1vstrength [format "%.6f" [expr $delta*$c1vk*$BMRAD2AMP]]
            set c2vstrength [format "%.6f" [expr $delta*$c2vk*$BMRAD2AMP]]
            set c3vstrength [format "%.6f" [expr $delta*$c3vk*$BMRAD2AMP]]
        } else {
            set delta "Not Configured"
            set c1vstrength "Not Configured"
            set c2vstrength "Not Configured"
            set c3vstrength "Not Configured"
        }
    }
}

proc MakeBumpWidget {args} {
    global c1hname c2hname c3hname p1hname
    global c1hs c2hs c3hs p1hs
    global c1hbeta c2hbeta c3hbeta p1hbeta
    global c1hpsi c2hpsi c3hpsi p1hpsi
    global c1hk c2hk c3hk p1hk
    global c1hstrength c2hstrength c3hstrength p1hval 
    global bbhconfigured

    global c1vname c2vname c3vname p1vname
    global c1vs c2vs c3vs p1vs
    global c1vbeta c2vbeta c3vbeta p1vbeta
    global c1vpsi c2vpsi c3vpsi p1vpsi
    global c1vk c2vk c3vk p1vk
    global c1vstrength c2vstrength c3vstrength p1vval 
    global bbvconfigured 
    
    global p1hmax p1vmax whscale wvscale orbitfile

    set parent ""
    set plane H
    APSParseArguments {parent plane}
    global bold
    switch $plane {
        H -
        h {
            set plane h
            set coord x
        }
        V -
        v {
            set plane v
            set coord y
        }
    }
        
    set widget .[string tolower $plane]
    APSFrame $widget -parent $parent
    set parent $parent$widget.frame
    
    APSFrameGrid .grid -parent $parent -xList {1 2 3 4 5} -yList {0 1 2 3 4 5 6 7} 
    set w $parent.grid
    set j 1
    for {set i 2} {$i<=5} {incr i} {
        if {$i==5} {
            set text P1
        } else {
            set text C$j
        }
        incr j
        APSLabel .c -parent $w.$i.0 -text $text
    }
    set nameList {"Name" "s, m" "Beta" "Psi" "K" "Current Values, Amp, mm" "Bump Values, Amp, mm"}
    set c1varList [list c1${plane}name c1${plane}s c1${plane}beta c1${plane}psi c1${plane}k c1${plane}cur c1${plane}strength]
    set c2varList [regsub -all {1} $c1varList 2]
    set c3varList [regsub -all {1} $c1varList 3]
    set p1varList [list p1${plane}name p1${plane}s p1${plane}beta p1${plane}psi p1${plane}k p1${plane}cur p1${plane}val]
    for {set j 1} {$j<=7} {incr j} {
        set i [expr $j -1]
        APSLabel .label -parent $w.1.$j -text [lindex $nameList $i] -packOption "-side right" 
        if {$j==1} {
            APSLabeledEntry .c1 -parent $w.2.$j -label "" -textVariable [lindex $c1varList $i] -width 15 
            APSLabeledEntry .c2 -parent $w.3.$j -label "" -textVariable [lindex $c2varList $i] -width 15
            APSLabeledEntry .c3 -parent $w.4.$j -label "" -textVariable [lindex $c3varList $i] -width 15
        } else {
            APSLabeledOutput .c1 -parent $w.2.$j -label "" -textVariable [lindex $c1varList $i] -width 15 
            APSLabeledOutput .c2 -parent $w.3.$j -label "" -textVariable [lindex $c2varList $i] -width 15
            APSLabeledOutput .c3 -parent $w.4.$j -label "" -textVariable [lindex $c3varList $i] -width 15
        }
        if {$j==7 || $j==1} {
            APSLabeledEntry .p1 -parent $w.5.$j -label "" -textVariable [lindex $p1varList $i] -width 15
        } else {
            APSLabeledOutput .p1 -parent $w.5.$j -label "" -textVariable [lindex $p1varList $i] -width 15
        }
    }
    for {set i 1} { $i <= 5} {incr i} {
        APSSetContextHelp $w.${i}.1 -contextHelp \
            "Names of the correctors and monitor.  These may be changed.  The\
       correctors must be in lattice order and the monitor must be\
       between the correctors."
    }
    for {set i 1} {$i<=5} {incr i} {
        APSSetContextHelp $w.${i}.5 -contextHelp \
            "Bump coefficients for correctors and a similar coefficient for the\
       monitor.  These are set during configuration.  The actual strengths\
       in mrad for correctors are these values times the bump amplitude.  The\
       monitor coefficient is defined so that the required bump amplitude\
       is the ratio of the monitor coefficient and the desired monitor value\
       in mm.  These coefficients do not depend on the corrector settings nor\
       the desired monitor value.  They are used to determine the corrector\
       settings given the desired bump value."
    } 
    APSButton .configbuttonh -parent $parent -text "Configure" -command "bbconfigure -plane $plane" \
        -contextHelp "Fills in the bump parameters (s, beta, psi, K) for the specified\
             names.  The parameters are obtained from $orbitfile."
   # APSRepack $parent.configbuttonh -fill x

    APSButton .setbuttonh -parent $parent -text "Set" -command "bbsetramp -plane $plane" \
        -contextHelp "Sets the bump in the ramps."
    #APSRepack $parent.setbuttonh -fill x
  
    scale $parent.${plane}scale -from -[set p1${plane}max] -to [set p1${plane}max] -orient horizontal  \
        -label "$coord, mm" -resolution .001 -variable p1${plane}val
    pack $parent.${plane}scale -fill x
    set w${plane}scale $parent.${plane}scale
    
    APSSetContextHelp $parent.${plane}scale -contextHelp \
        "Sets the desired monitor value.  (It may also be set in the entry box for\
   the P1 strength).  Clicking on either side of the slider will change the\
   value by the minimum increment, and can be used for fine tuning."

    APSLabeledEntry .p1${plane}maxentry -parent $parent \
        -label "Maximum $coord:" \
        -textVariable p1${plane}max -width 3 -contextHelp \
        "Sets the limits on the slider."
    APSRepack $parent.p1${plane}maxentry.entry -side left
}

proc MakeBoosterConstantsFrame {args} {
    set parent ""
    APSParseArguments {parent}

    global loadRamp BMRAD2AMP BCORMAX nux nuy bumpStart outputDir

    APSFrame .const -parent $parent -label "Booster constants"
    set parent $parent.const.frame
    APSFrameGrid .grid -parent $parent -xList {x1 x2}
    set w1 $parent.grid.x1
    set w2 $parent.grid.x2 
    APSLabeledEntry .curr -parent $w1 -label "Maximum corr. current (Amp)" -textVariable BCORMAX -width 20
    APSLabeledEntry .curr1 -parent $w2 -label "Booster mini-rad to amp converter (Amp)" -textVariable BMRAD2AMP -width 20
    bind $w2.curr1.entry <Return> "UpdateCoefficients"
    APSLabeledEntry .nux -parent $w1 -label "X tune (rad):" -textVariable nux -width 20
    APSLabeledEntry .nuy -parent $w2 -label "Y tune (rad):" -textVariable nuy -width 20
    APSLabeledEntry .lattice -parent $w1 -label "Lattice:" -textVariable lattice
    APSButton .load -parent $w1.lattice -text "Load Orbit Data" -size small -packOption "-side right" \
        -command "GetLattice;bbconfigure -plane h;bbconfigure -plane v" \
        -contextHelp "press to load the orbit data if lattice changes."
    APSLabeledEntry .time -parent $w2 -label "Bump start time (ms):" -textVariable bumpStart -width 20
    APSRadioButtonFrame .load -parent $w1 -label "Load bump table?" -buttonList {Yes No} -valueList {1 0} -variable loadRamp \
        -orientation horizontal -contextHelp "If No is choosen, then the ramptable will be generated without loading. Otherwise, the generated ramptable will be loaded."
    APSButton .compare -parent $w2 -text "Plot" -command "PlotRampTables" \
        -contextHelp "plot the ramp table with bump and ramp table before the bump." 
    APSButton .loadorig -parent $w2 -text "Load Orig. Table" -command "LoadOrigRampTable"
    APSButton .savebump -parent $w2 -text "Save Bump" -command "SaveBumpTable"
    APSLabeledEntry .dir -parent $parent -label "Output directory:" -width 80 -textVariable outputDir \
	-fileSelectButton 1  -fileSelectDirectory 1
}

proc UpdateCoefficients {args} {
    bbconfigure -plane h
    bbsetstrength h
    bbconfigure -plane v
    bbsetstrength -plane v
}

proc SaveBumpTable {args} {
    global outputDir

    cd $outputDir
    
    set bumpList [glob -nocomplain ramp_*.bump]
    if ![llength $bumpList] {
        SetStatus "No bump table generated for $c1name, $c2name, and $c3name yet, press the \"set\" button to generate."
        return
    }
    global bumpTableList
    set bumpTableList ""
    APSScrolledListWindow .fileselect -name "Select ramp tables for comparison" \
        -label "Select files"  \
        -itemList $bumpList -selectionVar bumpTableList \
        -directory ./ -appendDescription 1 \
        -rootnameOnly 1 -descriptionName Description 
    tkwait variable bumpTableList
    if ![string length $bumpTableList] {
        return -code error "No bump table is selected."
    }
    set corrList ""
    set bumpList ""
    foreach bump $bumpTableList {
	set bump [lindex $bump 0]
	lappend bumpList $bump
	set corrList1 [exec sdds2stream -par=CorrName $bump]
	foreach corr $corrList1 { 
	    if [lsearch $corrList $corr]>=0 {
		return -code error "$corr already been included in previous bump table."
	    }
	}
	set corrList [concat $corrList [exec sdds2stream -par=CorrName $bump]]
    }
    set option ""
    foreach corr $corrList {
	if [string length $option] {
	    append option ,CorrName=$corr,!,&
	} else {
	    set option -match=par,CorrName=$corr,!
	}
    }
    SetStatus "Merge [join $bumpList ,] into the default ramp table..."
    global lattice dataDir
    set lattice [file tail [file readlink $dataDir]]
    set rampDir /home/helios/oagData/booster/ramps/correctors/lattices/$lattice
    
    set lastRamp [lindex [lsort [glob $rampDir/HVCorr-????.ramp]] end]
    set lastID [scan $lastRamp $rampDir/HVCorr-%ld]
    set newFile HVCorr-[format %04d [expr $lastID+1]].ramp
    set tmpRoot /tmp/[APSTmpString]
    APSAddToTmpFileList -ID boosterbump -fileList "$tmpRoot.1 $tmpRoot.2"
    if [catch {exec sddsprocess $rampDir/HVCorr.ramp $tmpRoot.1 $option 
        eval exec sddscombine $tmpRoot.1 $bumpList -pipe=out  \
                   | sddsprocess -pipe -edit=par,plane,CorrName,4d \
                   | sddssort -pipe=in -par=plane -par=CorrName $rampDir/$newFile} result] {
        SetStatus "$result"
        return
    }
    if ![APSYesNoPopUp "New ramptable $newFile was generated, make it as default?"] {
        return
    }
    set oldDir [pwd]
    cd $rampDir
    if [catch {file delete -force HVCorr.ramp} result] {
        SetStatus $result
        cd $oldDir
        return
    }
    if [catch {exec ln -s $newFile HVCorr.ramp} result] {
        SetSatus "$result"
        cd $oldDir
        return
    }
    cd $oldDir
    SetStatus "done."
}

proc PlotRampTables {args} {
    global c1hname c2hname c3hname p1hname 
    global c1vname c2vname c3vname p1vname  
    global frameIndex outputDir
    
    cd $outputDir
    if {$frameIndex==0} {
        set plane h
    } else {
        set plane v
    }
    SetStatus "Plot ramptables for $plane plane."
    foreach nm {c1 c2 c3} {
        set ${nm}name [set ${nm}${plane}name]
    } 
    set bumpList [lsort -decreasing [glob -nocomplain ramp_${c1name}_${c2name}_${c3name}_???.bump]]
    if ![llength $bumpList] {
        SetStatus "No bump table generated for $c1name, $c2name, and $c3name yet, press the \"set\" button to generate."
        return
    }
    global fileSelection
    set fileSelection ""
    APSScrolledListWindow .fileselect -name "Select ramp tables for comparison" \
        -label "Select files"  \
        -itemList $bumpList -selectionVar fileSelection \
        -directory ./ -appendDescription 1 \
        -rootnameOnly 1 -descriptionName Description 
    tkwait variable fileSelection
    foreach filesel $fileSelection {
        set file [lindex $filesel 0]
        regsub {bump} $file "before" before
        exec sddsplot -grap=sym,vary=sub,fill,conn=sub -split=page -group=page -sep=2 \
            -title=@ControlName -legend=par=Description \
            -col=RampTime,RampSetpoint $before  $file &
    }
}

proc SaveRampTable {args} {
    set ramptable ""
    set corrList ""
    set description ""
    APSParseArguments {ramptable corrList description}
    
   
    set tmpRoot /tmp/[APSTmpString]
    APSAddToTmpFileList -ID boosterbump -fileList "$tmpRoot.1 $tmpRoot.2"
    if [catch {exec sddswget -pv=[join $corrList :rampRAMPTABLE.TIMA,]:rampRAMPTABLE.TIMA \
                   -pend=30 $tmpRoot.1} result] {
        return -code error $result
    }
    
    if [catch {exec sddswget -pv=[join $corrList :rampRAMPTABLE.VAL,]:rampRAMPTABLE.VAL \
                   -pend=30 $tmpRoot.2} result] {
        return -code error $result
    }
    if [catch {exec sddsxref $tmpRoot.1 $tmpRoot.2 -rename=col,Waveform=RampSetpoint -pipe=out  \
                    | sddsconvert -pipe -rename=col,Waveform=RampTime \
                    | sddsprocess -pipe -process=RampTime,max,MaxIndex,pos,functionOf=Index \
                    | sddsprocess -pipe "-define=col,keep,Index MaxIndex > ? 0 : 1 $ " \
                    | sddsprocess -pipe=in $ramptable -filter=col,keep,1,1 \
                    -edit=par,ControlName,WaveformPV,%/.TIMA// \
                    -print=par,ControlType,pv \
                    "-print=par,Description,[APSMakeSafeQualifierString $description]" \
                    -define=par,RampBias,0.0 } result] {
        return -code error $result
    }
}

proc LoadOrigRampTable {args} {
    global frameIndex outputDir
    if !$frameIndex {
        set plane h
    } else {
        set plane v
    }
    set ramptable  $outputDir/booster_orig_ramp.$plane
    if ![file exist $ramptable] {
        SetStatus "The original table of $plane plane has not been generated yet."
        return
    }
    if ![APSYesNoPopUp "Load original ramp table for $plane plane now?"] {
        return
    }
    SetStatus "Loading original ramp table for $plane plane"
    if [catch {exec loadbramp -${plane} $ramptable} result] {
        SetStatus "Cannot load $ramptable\n$result"
        return
    }
}

proc GetLattice {} {
    global lattice dataDir
    set lattice [file tail [file readlink $dataDir]]
    GetOrbitInfo
}

# **************************** overviewtext ****************************
proc overviewtext {} {
    return "
                     Booster Bump Generator

     This interface can be used to set bumps in the Booster.  The
procedure is to specify the names of the correctors and the monitor,
\"Configure\" the bump by getting the Twiss parameters, specify the
bump amplitude at the monitor, and then \"Set\" the bump into the ramp
tables and load the AFG.  There are buttons for Configure and Set.

     By specifying -x on the command line, Xorbit will be used rather
than the real machine.

     The new ramps are loaded using the current values in the
:rampRAMPTABLE process variables.  These are not necessarily the
values that are loaded into the AFG foreground (and hence the ones
being used by the power supplies to actually ramp the correctors), but
they are the only values available.  It is necessary to insure that
the values in the :rampRAMPTABLE process variable are valid and have
been loaded into the AFG foreground.  This should be the case if a
rampload has been done recently."
}

# **********************************************************************
# **************************** main ************************************
# **********************************************************************

set name "Booster Bump Generator"
set overview [overviewtext]
set version "$name\n\n\$Revision: 1.3 $\n\$Author: shang $"

APSApplication . -name $name -version $version -overview $overview \
  -contextHelp "Sorry, there is no context help available for this widget."

set dataDir $OAGGlobal(BoosterLatticesDirectory)/default
set outputDir [APSGoToDailyDirectory]
set orbitfile $dataDir/refMatrices/booster.twi
set args $argv
set xorbit 0

# **************************** bbconfigure *****************************
set orbitFileOpened 0
set orbitElementNames ""

set mainStatus ""
APSScrolledStatus .status -parent .userFrame -textVariable mainStatus -width 70

set tcl_precision 17
set pi [expr acos(-1.)]
set twopi [expr 2*$pi]
set bold -adobe-courier-bold-r-normal-*-14-*-*-*-*-*-*-*

# Set Booster constants
setbconstants

# Defaults
set framepad 2

# Setup
set c1hname B1C0H
set c2hname B1C1H
set c3hname B1C2H
set p1hname B1C0P1
set p1hval 0.10
set p1hmax 1.0
set bbhconfigured 0
set bumpStart 150.0

set c1vname B1C0V
set c2vname B1C1V
set c3vname B1C2V
set p1vname B1C1P1
set p1vval 0.1
set p1vmax 1.0
set bbvconfigured 0

# Set traces

trace variable p1hval w {bbsetstrength -plane h}
trace variable c1hname w {global bbhconfigured; set bbhconfigured 0; bbsetstrength -plane h}
trace variable c2hname w {global bbhconfigured; set bbhconfigured 0; bbsetstrength -plane h}
trace variable c3hname w {global bbhconfigured; set bbhconfigured 0; bbsetstrength -plane h}
trace variable p1hname w {global bbhconfigured; set bbhconfigured 0; bbsetstrength -plane h}
trace variable p1hmax w \
  {global p1hmax whscale; $whscale configure -from -$p1hmax -to $p1hmax}

trace variable p1vval w {bbsetstrength -plane v}
trace variable c1vname w {global bbvconfigured; set bbvconfigured 0; bbsetstrength -plane v}
trace variable c2vname w {global bbvconfigured; set bbvconfigured 0; bbsetstrength -plane v}
trace variable c3vname w {global bbvconfigured; set bbvconfigured 0; bbsetstrength -plane v}
trace variable p1vname w {global bbvconfigured; set bbvconfigured 0; bbsetstrength -plane v}
trace variable p1vmax w \
  {global p1vmax wvscale; $wvscale configure -from -$p1vmax -to $p1vmax}

# Set Booster constants
setbconstants
set loadRamp 0
set frameIndex -1
GetLattice
set nux [format "%.6f" [exec sdds2stream -par=nux $orbitfile]]
set nuy [format "%.6f" [exec sdds2stream -par=nuy $orbitfile]]
set PI 3.141592653589793
MakeBoosterConstantsFrame -parent .userFrame
set widgetList [APSTabFrame .tab -parent .userFrame -label "Coefficients" -labelList "Horizontal Vertical" \
                    -width 900 -height 400 -packOption "-expand true" -frameIndexVariable frameIndex]

MakeBumpWidget -parent [lindex $widgetList 0] -plane h
MakeBumpWidget -parent [lindex $widgetList 1] -plane v

