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

set CVSRevisionAuthor "\$Revision: 1.1 $ \$Author: xiaoam $"

APSApplication . -name "IEX-Utility" -version $CVSRevisionAuthor \
  -overview "IEX-Utility includes the most used IEX tunning tools:\na)IEX Checkup: collect orbit and coupling perturbation while run IEX at various modes. Determine if a new correction table is needed; It's also used to check if IEX correction table is loaded correctly;\nb) IEX Orbit Correction: measure IEX dipole corrector's response matrix. Use the measured matrix to correct IEX orbit perturbation locally;\nc) IEX Tune Correction: measure IEX quad calibration and x-tune variation. Use calibrated quad response to correct tunes;\nd)IEX Coupling Correction: 2d scan IEX skew quads, then pick the proper settings to keep the coupling constant when run IEX."

APSStandardSetup

set IEXCheck(vsTime) 0
set IEXCheck(vsCoil) 1
set IEXCheck(Orbit) 1
set IEXCheck(Corrector) 1
set IEXCheck(Emittance) 1
set IEXCheck(Coupling) 1
set IEXCheck(FBOrbit) 0
set IEXCheck(FBCorrErr) 0
set IEXCheck(FBCorrDr) 0
set IEXCheck(All) 0

proc MakeIEXCheckWidget {widget args} {
    global IEX IEXCheck
    set parent ""
    APSParseArguments {parent}

    APSFrame $widget -parent $parent -label "Plot Option:" \
        -packOption "-side top -fill x"
    set w $parent$widget.frame

    APSCheckButtonFrame .checkXaxis -parent $w  -orientation horizontal \
        -contextHelp "Choose x-axis variables for all selected ploting requrests" \
        -label "Plot vs.:          " \
        -buttonList {"Time" "Coil Settings"} -variableList {IEXCheck(vsTime) IEXCheck(vsCoil)}
    APSCheckButtonFrame .checkYaxis -parent $w  -orientation horizontal \
        -contextHelp "Choose which parameters you want to look at"\
        -label "Data to plot:      " \
        -buttonList {"Orbit" "Corrector" "Emittance" "Coupling"} \
        -variableList {IEXCheck(Orbit) IEXCheck(Corrector) IEXCheck(Emittance) IEXCheck(Coupling)}
    APSCheckButtonFrame .checkFB -parent $w  -orientation horizontal \
        -contextHelp "Choose which RTFB parameters you want to look at" \
        -label "RTFB data to plot: " \
        -buttonList {"rms Orbit" "Corrector Error" "Corrector Drive"} \
        -variableList {IEXCheck(FBOrbit) IEXCheck(FBCorrErr) IEXCheck(FBCorrDr)}
    APSCheckButtonFrame .checkTable -parent $w -orientation horizontal \
        -contextHelp "Check all correction table at once?" \
        -label " " -buttonList {"CheckAllTableAtOnce"} -variableList {IEXCheck(all)}

    APSButton .checkrun -parent $parent -text Collect \
        -command "catch {IEXCheckCollectData -abortVar abortRun} status" \
        -contextHelp "Collects BPM data vs IEX main coils. Uses monitor file /home/helios/oagData/sr/IDs/IEX/IEX+orbit.mon"
    APSButton .checkprocess -parent $parent -text PROCESS... \
        -command IEXCheckProcessData \
        -contextHelp "Selects and processes the data collected today."
    APSButton .checkplot -parent $parent -text "PLOT..." \
        -command IEXCheckPlotData \
        -contextHelp "Selects and plots the data collected today."
    APSButton .checkTable -parent $parent -text "IEX Corr. Table Check" \
        -command "IEXCheckTableLoad -abortVar abortRun"
}

proc IEXCheckTableLoad {args} {
    APSParseArguments {abortVar}
    global $abortVar IEX IEXCheck

    cd $IEX(rootDir)
    file delete -force IEX-Table-Curr
    exec IEXMultIConvert -multFile IEX.sdds -corrFile IEX-Table-Curr -M2I 1

    cd $IEX(logDir)
    exec mkdir -p CheckTable
    cd CheckTable

    # Initial IEX for data taking
    exec caput -w 3 ID29:ReadSDDSfile.PROC 1
    IEXSetup -condition 0
    exec caput -w 3 ID29:ByStepLimit 2
    exec caput -w 3 ID29:BxStepLimit 2

    set ModeSet "C V H"
    set imodeSet "0 2 3"
    set coilSet "ID29:VcoilSet ID29:HcoilSet ID29:VcoilSet"
    set mainSet "Iv_main Ih_main Iv_main"
    set BeamlineSet "US DS"
    set ibeamlineSet "0 1"
    set QuasiSet "Off On"
    set qRatioSet "100 85"
    if !$IEXCheck(All) {
        if {[string match $IEX(Mode) C]} {
            set ModeSet C
            set imodeSet 0
            set coilSet ID29:VcoilSet
            set mainSet Iv_main
        }
        if {[string match $IEX(Mode) V]} {
            set ModeSet V
            set imodeSet 2
            set coilSet ID29:HcoilSet
            set mainSet Ih_main
        }
        if {[string match $IEX(Mode) H]} {
            set ModeSet H
            set imodeSet 3
            set coilSet ID29:VcoilSet
            set mainSet Iv_main
        }
        set BeamlineSet US
        set ibeamlineSet 0
        if {[string match $IEX(Beamline) DS]} {
            set BeamlineSet DS
            set ibeamlineSet 1
        } 
        set QuasiSet Off
        set qRatioSet 100
        if {[string match $IEX(Quasi) On]} {
            set QuasiSet On
            set qRatioSet 85
        } 
    }    
    foreach Quasi $QuasiSet qRatio $qRatioSet {
        #set IEX to desired quasi
        catch {
            if {[exec cavget -list=ID29:QuasiRatio.RVAL]!=$qRatio} {
                exec caput -w 3 ID29:Main_on_off.VAL 1
                after 2000
                exec cawait -interval=1 -waitFor=ID29:feedback.VAL,sameAs=Ready
                exec cawait -interval=1 -waitFor=ID29:BusyRecord.VAL,sameAs=Done
                exec caput -w 3 ID29:QuasiRatioIn.C $qRatio
                exec caput -w 3 ID29:Main_on_off.VAL 0
                after 5000
                exec cawait -interval=1 -waitFor=ID29:feedback.VAL,sameAs=Ready
                exec cawait -interval=1 -waitFor=ID29:BusyRecord.VAL,sameAs=Done
            }
            SetStatus "Set IEX to desired Quasi"
        }
        foreach Mode $ModeSet imode $imodeSet coil $coilSet main $mainSet {
            foreach Beamline $BeamlineSet ibeamline $ibeamlineSet {
                #set IEX to desired mode, beamline
                catch {exec caput -w 3 ID29:DesiredMode.VAL $imode
                    exec caput -w 3 ID29:Beamline  $ibeamline
                }
                after 2000
                exec cawait -interval=1 -waitFor=ID29:feedback.VAL,sameAs=Ready
                exec cawait -interval=1 -waitFor=ID29:BusyRecord.VAL,sameAs=Done
            
                # taking data
                IEXRamp -energy 3.8
                SetStatus "Launching sddsmonitor..."
                if [catch {set pid [exec sddsmonitor $IEX(moniDir)/ID-IEX.mon IEX-Corr-Check  \
                                        -step=1200 -interval=1 -erase &] \
                           } result ] {
                    SetStatus $result
                    return
                }
                after 3000

                IEXRamp -energy 2
                IEXRamp -energy 3.8
                if [catch {exec kill -KILL $pid} result] {
                    SetStatus $result
                }
                # post processing
                exec sddsprocess IEX-Corr-Check -nowarning -pipe=out \
                    -filt=col,ID29:TableDirection,0,0 \
                    "-redef=col,ID29:VcoilSet,ID29:ByPolarity 1 == ? ID29:VcoilSet : ID29:VcoilSet chs \$" \
                    | sddssort -pipe=in $Mode-$Beamline-$Quasi-Down -col=$coil
                exec sddsprocess IEX-Corr-Check -nowarning -pipe=out \
                    -filt=col,ID29:TableDirection,1,1 \
                    "-redef=col,ID29:VcoilSet,ID29:ByPolarity 1 == ? ID29:VcoilSet : ID29:VcoilSet chs \$" \
                    | sddssort -pipe=in $Mode-$Beamline-$Quasi-Up -col=$coil
                foreach Direction {Up Down} {
                    exec sddsplot -leg -topl=$Mode-$Beamline-$Quasi-$Direction  \
                        -col=$coil,*STSet $Mode-$Beamline-$Quasi-$Direction -grap=sym,vary=sub,type=2,fill \
                        -col=$main,*ST $IEX(rootDir)/IEX-Table-Curr -match=para,Mode=$Mode -match=para,Beamline=$Beamline \
                        -match=para,Quasi=$Quasi -match=para,Direction=$Direction -grap=line,vary \
                        -dev=lpng -out=$Mode-$Beamline-$Quasi-$Direction-1.png

                    exec sddsplot -leg -topl=$Mode-$Beamline-$Quasi-$Direction \
                        -col=$coil,*SBSet $Mode-$Beamline-$Quasi-$Direction -grap=sym,vary=sub,type=2,fill \
                        -col=$main,*SB $IEX(rootDir)/IEX-Table-Curr -match=para,Mode=$Mode -match=para,Beamline=$Beamline \
                        -match=para,Quasi=$Quasi -match=para,Direction=$Direction -grap=line,vary \
                        -dev=lpng -out=$Mode-$Beamline-$Quasi-$Direction-2.png

                    exec sddsplot -leg -topl=$Mode-$Beamline-$Quasi-$Direction  \
                        -col=$coil,*SQ1Set $Mode-$Beamline-$Quasi-$Direction -grap=sym,vary=sub,type=2,fill \
                        -col=$main,*SQ1 $IEX(rootDir)/IEX-Table-Curr -match=para,Mode=$Mode -match=para,Beamline=$Beamline \
                        -match=para,Quasi=$Quasi -match=para,Direction=$Direction -grap=line,vary \
                        -dev=lpng -out=$Mode-$Beamline-$Quasi-$Direction-3.png
                
                    exec sddsplot -leg -topl=$Mode-$Beamline-$Quasi-$Direction  \
                        -col=$coil,*SQ2Set $Mode-$Beamline-$Quasi-$Direction -grap=sym,vary=sub,type=2,fill \
                        -col=$main,*SQ2 $IEX(rootDir)/IEX-Table-Curr -match=para,Mode=$Mode -match=para,Beamline=$Beamline \
                        -match=para,Quasi=$Quasi -match=para,Direction=$Direction -grap=line,vary \
                        -dev=lpng -out=$Mode-$Beamline-$Quasi-$Direction-4.png

                    exec sddsplot -leg -topl=$Mode-$Beamline-$Quasi-$Direction  \
                        -col=$coil,*SQ3Set $Mode-$Beamline-$Quasi-$Direction -grap=sym,vary=sub,type=2,fill \
                        -col=$main,*SQ3 $IEX(rootDir)/IEX-Table-Curr -match=para,Mode=$Mode -match=para,Beamline=$Beamline \
                        -match=para,Quasi=$Quasi -match=para,Direction=$Direction -grap=line,vary \
                        -dev=lpng -out=$Mode-$Beamline-$Quasi-$Direction-5.png

                    exec sddsplot -leg -topl=$Mode-$Beamline-$Quasi-$Direction \
                        -col=$coil,*SQ4Set $Mode-$Beamline-$Quasi-$Direction -grap=sym,vary=sub,type=2,fill \
                        -col=$main,*SQ4 $IEX(rootDir)/IEX-Table-Curr -match=para,Mode=$Mode -match=para,Beamline=$Beamline \
                        -match=para,Quasi=$Quasi -match=para,Direction=$Direction -grap=line,vary \
                        -dev=lpng -out=$Mode-$Beamline-$Quasi-$Direction-6.png
                }
                if ([set $abortVar] || [lindex [exec caget -n ID29:AbortUtility] 1]) {
                    SetStatus "IEX Corr. Table check aborted."
                    set $abortVar 0
                    exec caput ID29:AbortUtility 0
                    return
                }
            }
        }
    }
    cd $IEX(logDir)
}

proc IEXCheckCollectData {args} {
    APSParseArguments {abortVar}
    global $abortVar IEX
    set wait 15

    cd $IEX(logDir)
    if ![file exist $IEX(monifile)] {
        IEXMakeMonitor
    }
    set IEX(comment) $IEX(Mode)-$IEX(Beamline)-$IEX(Quasi)
    set root $IEX(comment)
    exec caput ID29:AbortUtility 0

    #0 Set up measurement
    IEXSetup -condition 0
    set frame0 [lindex [exec caget S:VID1:numToAvgC] 1]
    set xLimit0 [lindex [exec caget -n ID29:BxStepLimit] 1]
    set yLimit0 [lindex [exec caget -n ID29:ByStepLimit] 1]	
    exec caput -w 3 S:VID1:numToAvgC 30
    exec cavput -list=ID29:BxStepLimit=2,ID29:ByStepLimit=2

    #1 Start measurement
    SetStatus "Launching sddsmonitor..."
    if [catch {set pid [exec sddsmonitor $IEX(monifile) $root -daily \
                            -step=1200 -interval=1 \
                            "-comment=Comment,[APSMakeSafeQualifierString $IEX(comment)]" &] \
               } result ] {
        SetStatus $result
        return
    }
    SetStatus "sddsmonitor launched..."

    #2 Ramp IEX Up to maximum value
    if {![set $abortVar] && ![lindex [exec caget -n ID29:AbortUtility] 1]} {
        APSWaitWithUpdate -waitSeconds $wait -updateInterval 1
        IEXRamp -energy $IEX(Emax)
        SetStatus "IEX-Check: Step 1 - Ramp to maximum"
    }

    # Ramp IEX down to zero for V mode, for others go to minimum max then back to zero
    if {![string match $IEX(Mode) V]} {
        if {![set $abortVar] && ![lindex [exec caget -n ID29:AbortUtility] 1]} {
            exec caput -w 3 ID29:DesiredMode.VAL $IEX(mode1)
            after 5000
            exec cawait -interval=1 -waitFor=ID29:feedback.VAL,sameAs=Ready
            exec cawait -interval=1 -waitFor=ID29:BusyRecord.VAL,sameAs=Done
            APSWaitWithUpdate -waitSeconds $wait -updateInterval 1
            SetStatus "IEX-Check: Step 2 - Change mode to $IEX(mode1)"
        }
        if {![set $abortVar] && ![lindex [exec caget -n ID29:AbortUtility] 1]} {
            IEXRamp -energy -$IEX(Emax)
            APSWaitWithUpdate -waitSeconds $wait -updateInterval 1
            SetStatus "IEX-Check: Step 3 - Ramp to minimum maximum"
        }
        if {![set $abortVar] && ![lindex [exec caget -n ID29:AbortUtility] 1]} {
            exec caput -w 3 ID29:DesiredMode.VAL $IEX(mode0)
            after 5000
            exec cawait -interval=1 -waitFor=ID29:feedback.VAL,sameAs=Ready
            exec cawait -interval=1 -waitFor=ID29:BusyRecord.VAL,sameAs=Done
            APSWaitWithUpdate -waitSeconds $wait -updateInterval 1
            SetStatus "IEX-Check: Step 4 -  Change mode to $IEX(mode0)"
        }
    } else {
        if {![set $abortVar] && ![lindex [exec caget -n ID29:AbortUtility] 1]} {
            IEXRamp -energy 3.8
            APSWaitWithUpdate -waitSeconds $wait -updateInterval 1
            SetStatus "IEX-Check: Step 2 -  V mode ramp to zero"
        }
    }

    SetStatus "Killing sddsmonitor..."
    if [catch {exec kill -KILL $pid} result] {
        SetStatus $result
    }

    exec caput -w 3 ID29:BxStepLimit $xLimit0
    exec caput -w 3 ID29:ByStepLimit $yLimit0
    exec caput -w 3 S:VID1:numToAvgC $frame0
    SetStatus "Done collecting data"
    set $abortVar 0
    exec caput ID29:AbortUtility 0
    
    cd $IEX(logDir)    
    return
}

proc IEXCheckProcessData {} {
    global IEX
    cd $IEX(logDir)
    set IEX(comment) $IEX(Mode)-$IEX(Beamline)-$IEX(Quasi)
    set root $IEX(comment)

    set fileList [lsort [glob -nocomplain $root-????-???-????.????]]
    if ![llength $fileList] {
        SetStatus "No files match $root-????-???-????.????"
        return -code error "No files match $root-$year-$julian-$month$day.????"
    }
    set dataList [APSChooseItemFromList \
                      -name "Data Set Selection" \
                      -itemList $fileList \
                      -returnList $fileList \
                      -returnIndices 0 \
                      -multiItem 1 \
                      -contextHelp "Select a file for plotting."]

    foreach file $dataList {
        SetStatus "Processing $file..."
        exec sddsprocess $file -pipe=out \
            -process=S*:msAve:?:ErrorCC,first,%sFirst \
            -process=S*:msAve:?:ErrorCC,standarddeviation,%sStdDev \
            "-redef=col,%s,%s %sFirst -,sel=S*:msAve:?:ErrorCC" \
            | sddsrowstats -pipe=in \
            $file.rowstats \
            -standardDeviation=XOrbitRMS,S*:msAve:x:ErrorCC \
            -standardDeviation=YOrbitRMS,S*:msAve:y:ErrorCC 
    }
    SetStatus "IEXCheckProcessData Done."
    return
}

proc IEXCheckPlotData {} {
    global IEX IEXCheck
    cd $IEX(logDir)

#    set root IEX+orbit
    set IEX(comment) $IEX(Mode)-$IEX(Beamline)-$IEX(Quasi)
    set root $IEX(comment)
    set fileList [lsort [glob -nocomplain $root-????-???-????.????.rowstats]]
    set outputFile [APSChooseItemFromList \
                        -name "Data Set Selection" \
                        -itemList $fileList \
                        -returnList $fileList \
                        -returnIndices 0 \
                        -multiItem 0 \
                        -contextHelp "Select one file for plotting."]

    set coilName ID29:VcoilSet
    set coilLab "IEX:Vcoil (A)"
    set coil ID29:Vcoil
    if [regexp V [exec sdds2stream -para=Comment $outputFile]] {
        set coilName ID29:HcoilSet
        set coilLab "IEX:Hcoil (A)"
        set coil ID29:Hcoil
    }

    set options "-layout=1,2 -axes=x"
    if $IEXCheck(vsTime) {
        if $IEXCheck(Orbit) {
            lappend options -col=Time,?OrbitRMS $outputFile -topline=@Comment \
                -file -tick=xtime -factor=ymult=1e3 \
                {-ylabel=RMS Orbit (um)} -grap=line,vary -leg
            lappend options -col=Time,$coilName -tick=xtime -factor=ymult=-1 \
                $outputFile -topline=@Comment -file -ylabel=$coilLab \
                -grap=line,type=2 -yscale=id=${coil} -end
        }
        if $IEXCheck(Corrector) {
            # vertical plane
            lappend options -col=Time,(*S29A:H2*,*S29A:H3:DacAI,*S29B:H2*,*S30A:H2*,*S30A:H3:DacAI,*S30B:H2*) \
                {-ylabel=Horizontal Correctors (A)} \
                $outputFile -topline=@Comment -file -tick=xtime -grap=line,vary \
                -leg -mode=y=offset -axes=x
            lappend options -col=Time,$coilName -tick=xtime -factor=ymult=-1 \
                $outputFile -topline=@Comment -file -ylabel=$coilLab \
                -grap=line,type=2 -yscale=id=IEX -end

            #horizontal plane
            lappend options -col=Time,(*S29A:V2*,*S29A:V3:DacAI,*S29B:V4*,*S30A:V2*,*S30A:V3:DacAI,*S30B:V4*) \
                {-ylabel=Vertical Correctors (A)} \
                $outputFile -topline=@Comment -file -tick=xtime -grap=line,vary \
                -leg -mode=y=offset -axes=x
            lappend options -col=Time,$coilName -tick=xtime -factor=ymult=-1 \
                $outputFile -topline=@Comment -file -ylabel=$coilLab \
                -grap=line,type=2 -yscale=id=IEX -end
        }
        if $IEXCheck(Emittance) {
            lappend options -col=Time,emittanceX $outputFile -topline=@Comment \
                -file -tick=xtime -grap=line,vary -leg -yscale=id=xemit
            lappend options -col=Time,emittanceY $outputFile -topline=@Comment \
                -file -tick=xtime -grap=line,vary -leg -yscale=id=yemit
            lappend options -col=Time,$coilName -tick=xtime -factor=ymult=-1 \
                $outputFile -topline=@Comment -file -ylabel=$coilLab \
                -grap=line,type=2 -yscale=id=IEX -end
        }
        if $IEXCheck(Coupling) {
            lappend options -col=Time,Coupling $outputFile -topline=@Comment \
                -file -tick=xtime -grap=line,vary -leg -yscale=id=coup
            lappend options -col=Time,$coilName -tick=xtime -factor=ymult=-1 \
                $outputFile -topline=@Comment -file -ylabel=$coilLab \
                -grap=line,type=2 -yscale=id=IEX -end
        }
    }

    if $IEXCheck(vsCoil) {
        if $IEXCheck(Orbit) {
            lappend options -col=$coilName,?OrbitRMS $outputFile -topline=@Comment \
                -file -factor=ymult=1e3 \
                {-ylabel=RMS Orbit (um)} -grap=line,vary -leg -end
        }
        if $IEXCheck(Corrector) {
            lappend options -col=${coil}Rdbk,(*S29A:H2*,*S29A:H3:DacAI,*S29B:H2*,*S30A:H2*,*S30A:H3:DacAI,*S30B:H2*) \
                {-ylabel=Horizontal Correctors (A)} \
                $outputFile -topline=@Comment -file -grap=line,vary \
                -leg -mode=y=offset -axes=x  -end
            lappend options -col=${coil}Rdbk,(*S29A:V2*,*S29A:V3:DacAI,*S29B:V4*,*S30A:V2*,*S30A:V3:DacAI,*S30B:V4*) \
                {-ylabel=Vertical Correctors (A)} \
                $outputFile -topline=@Comment -file -grap=line,vary \
                -leg -mode=y=offset -axes=x -end
        }
        if $IEXCheck(Emittance) {
            lappend options -col=$coilName,emittanceX $outputFile -topline=@Comment -file \
                -grap=line,vary -leg -yscale=id=xemit 
            lappend options -col=$coilName,emittanceY $outputFile -topline=@Comment -file \
                -grap=line,vary -leg -yscale=id=yemit -end
        }
        if $IEXCheck(Coupling) {
            lappend options -col=$coilName,Coupling $outputFile -topline=@Comment -file \
                -grap=line,vary -leg -yscale=id=coupling
        }
    }

    if [catch {eval exec sddsplot \
                   $options & \
               } result] {
        SetStatus "IEXCHeck - PlotData: $result"
        return
    }

    if $IEXCheck(FBOrbit) {
        exec sddsplot $outputFile -tick=xtime -grap=line,vary -leg=edit=%/SR:// \
            -col=Time,SR:xRMS:FullBW -ys=id=1 -col=Time,SR:xRMS:VarBW -ys=id=2 \
            -col=Time,SR:xRMS:30HzBW -ys=id=3 "-topline=x Orbit RMS" -end \
            -col=Time,SR:yRMS:FullBW -ys=id=1 -col=Time,SR:yRMS:VarBW -ys=id=2 \
            -col=Time,SR:yRMS:30HzBW "-topline=y Orbit RMS" \
            -col=Time,$coilName -yscal=id=IEX -ylabel=$coilLab \
            -grap=line,type=3 -leg -omni &
    }
    if $IEXCheck(FBCorrErr) {
         exec sddsplot $outputFile "-topline=RMS correctors errors" \
            -tick=xtime -grap=line,vary -leg=edit=%/:StdDev// \
            -col=Time,S29A:H3:StdDev -col=Time,S3\[01\]A:H3:StdDev \
            -col=Time,$coilName -yscal=id=IEX -ylabel=$coilLab \
            -grap=line,type=3 -leg -end \
             $outputFile "-topline=RMS correctors errors" \
            -tick=xtime -grap=line,vary -leg=edit=%/:StdDev// \
            -col=Time,S29A:V3:StdDev -col=Time,S3\[01\]A:V3:StdDev \
            -col=Time,$coilName -yscal=id=IEX -ylabel=$coilLab \
            -grap=line,type=3 -leg &
    }
    if $IEXCheck(FBCorrDr) {
        exec sddsplot $outputFile "-topline=RMS correctors drives" \
            -tick=xtime -grap=line,vary -leg=edit=%/:StdDev// \
            -col=Time,S29A:H3:DriveStdDev -col=Time,S3\[01\]A:H3:DriveStdDev \
            -col=Time,$coilName -yscal=id=IEX -ylabel=$coilLab \
            -grap=line,type=3 -leg -end \
            $outputFile "-topline=RMS correctors drives" \
            -tick=xtime -grap=line,vary -leg=edit=%/:StdDev// \
            -col=Time,S29A:V3:DriveStdDev -col=Time,S3\[01\]A:V3:DriveStdDev \
            -col=Time,$coilName -yscal=id=IEX -ylabel=$coilLab \
            -grap=line,type=3 -leg &
    }
    SetStatus "IEXCheckPlotData Done."
    return
}

set IEXOrb(bpmPause) 10
set IEXOrb(correctorPause) 5
set IEXOrb(rooth) "hResponse"
set IEXOrb(corrh) "ID29:usHcorr ID29:dsHcorr"
set IEXOrb(amph) 0.2
set IEXOrb(coordh) x
set IEXOrb(factorh) 5
set IEXOrb(rootv) "vResponse"
set IEXOrb(corrv) "ID29:usVcorr ID29:dsVcorr"
set IEXOrb(ampv) 0.3
set IEXOrb(coordv) y
set IEXOrb(factorv) 5

set skipOrbit 0
set IEXOrb(monitorDir)  /home/helios/oagData/sr/sddsmonitorFiles
set IEXOrb(maxSteps) 30
set IEXOrb(Tolerance) 0.003
set IEXOrb(vMatrix) vResponse
set IEXOrb(hMatrix) hResponse

proc MakeIEXOrbitWidget {args} {
    global IEX IEXOrb
    set parent ""    
    APSStrictParseArguments {RM Orbit parent}
    
    APSFrame $RM -parent $parent -label "IEX Corr. Response Matrix Measurement" \
        -contextHelp "Run this with IEX is off! The inverse matrix is calculated for following cases: a) use both US/DS correctors; b) reduce gain on the second sv (more important for the H plane due to less phase advance); c) use the corrector has largest sv; d) use US corrector only; e) use DS corrector only."
    set w $parent$RM.frame

    APSLabeledEntry .bpmPause -parent $w -label "BPM average time (s): " \
        -textVariable IEXOrb(bpmPause) \
        -contextHelp "Enter the time for the averaging of bpms."
    APSLabeledEntry .correctorPause -parent $w -label "Corrector pause time (s): " \
        -textVariable IEXOrb(correctorPause) \
        -contextHelp "Enter the time for the correctors to settle."
    APSFrameGrid .iexOrbit -parent $w -xList {x1 x2 x3}
    set w0 $w.iexOrbit.x1
    APSLabeledEntry .rooth -parent $w0 -label "H-plane: Root name: " \
        -textVariable IEXOrb(rooth) \
        -contextHelp "Enter the root name of the horizontal raw data files and processed output <root><index>."
    APSLabeledEntry .rootv -parent $w0 -label "V-plane: Root name: " \
        -textVariable IEXOrb(rootv) \
        -contextHelp "Enter the root name of the horizontal raw data files and processed output <root><index>."
    set w0 $w.iexOrbit.x2
    APSLabeledEntry .deltah -parent $w0 -label "Amplitude: " \
        -textVariable IEXOrb(amph) -width 6 \
        -contextHelp "Enter the desired value for H corrector amplitude."
    APSLabeledEntry .deltav -parent $w0 -label "Amplitude: " \
        -textVariable IEXOrb(ampv) -width 6 \
        -contextHelp "Enter the desired value for V corrector amplitude."
    set w0 $w.iexOrbit.x3
    APSLabeledEntry .gainReductionh -parent $w0 -label "2nd SV gain reduction: " \
        -textVariable IEXOrb(factorh) -width 6 \
        -contextHelp "Enter the desired reduction factor of second SV of the H-plane matrix."
    APSLabeledEntry .gainReductionv -parent $w0 -label "2nd SV gain reduction: " \
        -textVariable IEXOrb(factorv) -width 6 \
        -contextHelp "Enter the desired reduction factor of second SV of the V-plane matrix."
    APSFrame .rmMeas -parent $parent$RM.frame \
        -height 30 -relief flat \
        -packOption {-side top -fill x } 
    set w $parent$RM.frame.rmMeas.frame
    APSButton .setup -parent $w -text "Setup" -packOption "-side left" \
        -command IEXRMSetup \
        -contextHelp "Sets up data collection,i.e. creates a monitor file of bpms and sets up bpm averaging.\n\nAnd turns off IEX. Very important as orbit created can be larger than BPLD limits."
    APSButton .collect -parent $w -text "Collect Data"  -packOption "-side left" \
        -command {IEXRMCollectData -abortVar abortRun } \
        -contextHelp "Collects orbit data for two values of the IEX dipole correctors. Horizontal plane is run first, then vertical plane."
    APSButton .plot -parent $w -text "Plot Response" -packOption "-side left" \
        -command IEXRMPlotResponses -contextHelp "Plot responses."
    APSButton .calcInv -parent $w -text "Calculate Inverses" -packOption "-side left" \
        -command IEXRMCalculateInverses \
        -contextHelp "Calculates inverses using only the good bpms as determined by the bpm status management database. This function is already included in the \"Collect Data\" button."

    # Here start of orbit correction
    APSFrame $Orbit -parent $parent -label "IEX Corr. Response Matrix Measurement" \
        -contextHelp "Two functions inside this procedure. 1. Measure IEX dipole correctors's response matrix: run this with IEX is off! The inverse matrix is calculated for following cases: a) use both US/DS correctors; b) use the corrector has largest sv; c) reduce gain on the second sv (more important for the H plane due to less phase advance); d) use US corrector only; e) use DS corrector only.\n 2. Scan IEX energy setpoints and correct orbit using the dipole corrector coils."
    set w $parent$Orbit.frame
    APSLabeledEntry .maxStep -parent $w -label "Max.# of Steps: " \
        -textVariable IEXOrb(maxSteps) \
        -contextHelp "Enter the maximum number of correction steps for each IEX settings."
    APSLabeledEntry .orbitTol -parent $w -label "Orbit tolerance (mm): " \
        -textVariable IEXOrb(Tolerance) \
        -contextHelp "Enter the orbit tolerance, the minimum orbit before delcaring the orbit converged, and going to the next step."
    APSRadioButtonFrame .hMatrix -parent $w -orientation horizontal \
        -label "H-matrix :" -variable IEXOrb(hMatrix) -buttonList {Normal "1SV" "Reduced-gain 2nd integral" "Upstream only" "Downstream only"} \
        -valueList {hResponse hResponse.1SV hResponseMod hResponse#1 hResponse#2} -contextHelp \
        "Use one of the matrices prepared for orbit correction in H plane."
    APSRadioButtonFrame .vMatrix -parent $w -orientation horizontal \
        -label "V-matrix :" -variable IEXOrb(vMatrix) -buttonList {Normal "1SV" "Reduced-gain 2nd integral"  "Upstream only" "Downstream only"} \
        -valueList {vResponse vResponse.1SV vResponseMod vResponse#1 vResponse#2} -contextHelp \
        "Use one of the matrices prepared for orbit correction in V plane."

    APSFrame .orbCorr -parent $w \
        -height 30 -relief flat \
        -packOption {-side top -fill x } 
    set w $parent$Orbit.frame.orbCorr.frame
    APSButton .orbrun -parent $w -text "SCAN IEX" \
        -command {catch {IEXorbitScan -skipVar skipOrbit -abortVar abortRun } status} 
    APSButton .skipOrbit -parent $w -text "SKIP ORBIT" \
        -command {set skipOrbit 1 } -contextHelp "Skips waiting for orbit convergence."
    APSButton .startcontrollaw -parent $w -text "Start orbit controllaw" \
        -command {startControllaw} -contextHelp "Start special IEX orbit controllaw"
    APSButton .proc -parent $w -text "Process..." \
        -command {IEXorbProc} -contextHelp "Process results"
}

proc IEXRMSetup {} {
    global IEX IEXOrb
    set interval $IEXOrb(bpmPause)
    cd $IEX(logDir)
    set subDir RM
    exec mkdir -p $subDir
    cd $subDir

    # turn off IEX
    if [catch {exec caput -w 3 ID29:Main_on_off.VAL 1 \
               } result] {
        return -code error "IEXRMSetup: $result"
    }
    after 1000
    exec cawait -interval=1 -waitFor=ID29:feedback.VAL,sameAs=Ready
    exec cawait -interval=1 -waitFor=ID29:BusyRecord.VAL,sameAs=Done

    # set all correction coils to zero.
    set IEX(CorrValueNew) "0 0 0 0 0 0 0 0 0 0 0 0"
    PutIEXCorrValues -energy 3.8

    # set BPM averaging number
    if [catch {APSSRSetIOCAveraging -num2Ave [expr $interval * 10] \
                   -filterCoeff 1.0 -enable 1 \
               } result] {
        return -code error "IEXRMSetup: $result"
    }
    # setup BPM monitor file.
    if [catch {exec sddsprocess $IEXOrb(monitorDir)/SROrbit-msAve.vmon \
                   SROrbit-msAve.vmon \
                   -match=col,Suffix=:scdu:ave_sum.VAL,! \
               } result] {
        APSSetVarAndUpdate Status "IEXRMSetup: $result"        
        return -code error "IEXRMSetup: $result"
    }
    cd $IEX(logDir)
    SetStatus "IEXRMSetup Done."
    return
} 

proc IEXRMCollectData {args} {
    APSParseArguments {abortVar}
    global IEX IEXOrb $abortVar
    set comment "IEX Dipole RM Measurement"
    cd $IEX(logDir)/RM

    #0) Suspend DP and open RTFB loops.
    if [catch {DPRTFBOnOff -mode Off} result] {
        return -code error "$result"
    }

    #1) Doing the measurements.
    set interval [expr 1 + $IEXOrb(bpmPause) + $IEXOrb(correctorPause)]
    foreach plane {h v} {
        set corrFileList ""
        set output [file join $IEX(logDir)/RM $IEXOrb(root$plane)]
        foreach corr $IEXOrb(corr$plane) {
            if {[set $abortVar] || [lindex [exec caget -n ID29:AbortUtility] 1]} {
                SetStatus "IEXRMCollectData: Aborted"
                set $abortVar 0
                exec caput ID29:AbortUtility 0
                if {$IEX(DPOn)} {
                    if [catch {DPRTFBOnOff -mode On} result] {
                        return -code error "$result"
                    }
                }
                cd $IEX(logDir)
                return
            }
            exec sddsmakedataset corr.mon \
                -column=ControlName,type=string -data=$corr
            set delta $IEXOrb(amp$plane)
            #################################
            # +delta tweek
            #################################
            SetStatus "cavput -delta=factor=1 -list=${corr}=$delta"
            exec cavput -delta=factor=1 -list=${corr}=$delta -pend=5
            after 1000
            exec caput -w 3 ID29:MP2I.PROC 1
            after 1000
            exec caput -w 3 ID29:SetOutputSeq.PROC 1

            # wait for some time for corrector and bpms to settle
            SetStatus "wait for $IEXOrb(correctorPause) for correctors and $IEXOrb(bpmPause) seconds for bpms"
            APSWaitWithUpdate -waitSeconds $interval -updateInterval 1
            exec sddsvmonitor  -erase -rootname=$IEXOrb(monitorDir)/SROrbit-Rootnames.vmon \
                -suffixes=SROrbit-msAve.vmon ${output}.${corr}.raw \
                -scalars=corr.mon -steps=1
            #################################
            # -delta tweek
            #################################
            SetStatus "cavput -delta=factor=-2 -list=${corr}=$delta"
            exec cavput -delta=factor=-2 -list=${corr}=$delta -pend=5
            after 1000
            exec caput -w 3 ID29:MP2I.PROC 1
            after 1000
            exec caput -w 3 ID29:SetOutputSeq.PROC 1

            # wait for some time for corrector and bpms to settle
            SetStatus "wait for $IEXOrb(correctorPause) for correctors and $IEXOrb(bpmPause) seconds for bpms"
            APSWaitWithUpdate -waitSeconds $interval -updateInterval 1
            exec sddsvmonitor -append -rootname=$IEXOrb(monitorDir)/SROrbit-Rootnames.vmon \
                -suffixes=SROrbit-msAve.vmon ${output}.${corr}.raw \
                -scalars=corr.mon -steps=1
            #################################
            # restore corrector
            #################################
            SetStatus "cavput -delta=factor=1 -list=${corr}=$delta"
            exec cavput -delta=factor=1 -list=${corr}=$delta -pend=5
            after 1000
            exec caput -w 3 ID29:MP2I.PROC 1
            after 1000
            exec caput -w 3 ID29:SetOutputSeq.PROC 1
            # Do some processing for each corrector during loop
            exec sddschanges ${output}.${corr}.raw -pipe=out \
                -change=:msAve:? -cop=Index,Rootname \
                | sddsprocess -pipe \
                "-redef=col,%s,ChangeIn%s $delta / -2.0 /,select=ChangeIn:msAve:?,edit=%/ChangeIn//" \
                |  sddsconvert -pipe \
                -retain=col,Rootname,:msAve:$IEXOrb(coord$plane) \
                -rename=col,:msAve:$IEXOrb(coord$plane)=$IEXOrb(coord$plane) \
                | sddstranspose -pipe -newcolumnnames=Rootname \
                | sddsconvert -pipe -dele=col,OldColumnNames \
                | sddsprocess -pipe=in ${output}.${corr}.proc \
                -print=col,Corrector,${corr} \
                -print=para,Comment,[APSMakeSafeQualifierString $comment]
            lappend corrFileList ${output}.${corr}.proc
        }

        eval exec sddscombine $corrFileList -pipe=out \
            -merge -overWrite \
            | sddsconvert -pipe \
            -retain=col,Corrector,[join [FindGoodBpms -plane $plane] ,] \
            | sddstranspose -pipe=in ${output} \
            -oldColumnNames=BPMNames
    }
    IEXRMCalculateInverses

    #2) Resume DP and RTFB
    if {$IEX(DPOn)} {
        if [catch {DPRTFBOnOff -mode On} result] {
            return -code error "$result"
        }
    }
    cd $IEX(logDir)
    SetStatus "IEXRMCollectData Done"
}

proc IEXRMPlotResponses {} {
    global IEX IEXOrb
    cd $IEX(logDir)/RM

    exec sddsplot -layout=1,2 \
        -axes=x -grap=sym,conn -sep \
        -col=BPMNames,ID29:usHcorr $IEXOrb(rooth) \
        "-ylabel=Upstream hcorr" -enum=int=18 \
        -col=BPMNames,ID29:dsHcorr $IEXOrb(rooth) \
        "-ylabel=Downstream hcorr" -enum=int=18 \
        -col=BPMNames,ID29:dsHcorr $IEXOrb(rooth) \
        "-ylabel=Upstream hcorr" "-match=col,BPMNames=S29?:P*,BPMNames=S30?:P*,|" \
        -col=BPMNames,ID29:dsHcorr $IEXOrb(rooth) \
        "-ylabel=Downstream hcorr" "-match=col,BPMNames=S29?:P*,BPMNames=S30?:P*,|" \
        -col=BPMNames,ID29:usVcorr $IEXOrb(rootv) \
        "-ylabel=Upstream vcorr" -enum=int=18 \
        -col=BPMNames,ID29:dsVcorr $IEXOrb(rootv) \
        "-ylabel=Downstream vcorr" -enum=int=18 \
        -col=BPMNames,ID29:usVcorr $IEXOrb(rootv) \
        "-ylabel=Upstream vcorr" "-match=col,BPMNames=S29?:P*,BPMNames=S30?:P*,|" \
        -col=BPMNames,ID29:dsVcorr $IEXOrb(rootv) \
        "-ylabel=Downstream vcorr" "-match=col,BPMNames=S29?:P*,BPMNames=S30?:P*,|" \
        &
    cd $IEX(logDir)
    return
}

proc IEXRMCalculateInverses {} {
    global IEX IEXOrb
    cd $IEX(logDir)/RM

    foreach plane {h v} coord {x y} {
        set output $IEXOrb(root$plane)
        # regular inverse
        exec sddspseudoinverse ${output} -pipe=out \
            -sFile=s,matrix -uMatrix=u -vMatrix=v \
            -economy \
            -oldColumnNames=ActuatorName \
            | sddsconvert -pipe=in ${output}.inv \
            -edit=col,S*,ei/:msAve:${coord}:ErrorCC/
        # keep 1 SV
        exec sddspseudoinverse ${output} -pipe=out \
            -oldColumnNames=ActuatorName \
            -largestSingularValues=1 \
            | sddsconvert -pipe=in ${output}.1SV.inv \
            -edit=col,S*,ei/:msAve:${coord}:ErrorCC/

        # reduce gain of 2nd SV
        exec sddsconvert ${output}.inv -pipe=out \
          -dele=col,ActuatorName \
          | sddsquery -pipe=in -col -sddsoutput=${output}.inv.columns
        exec sddstranspose u -pipe=out \
          | sddsconvert -pipe=in ut \
          -dele=col,OldColumnNames
        
        exec sddsprocess  s -pipe=out \
          -clip=2,0,invert \
          | sddspseudoinverse -pipe \
          | sddsconvert -pipe \
          -retain=col,Column* \
          | sddsprocess -pipe=in sinvModified \
          "-redef=col,Column001,Column001 $IEXOrb(factor$plane) /"

        # For inverse with specially-edited singular values
        exec sddsconvert v -pipe=out \
          -dele=col,ActuatorName \
          | sddsmatrixop -pipe -verbose \
          -push=sinvModified -multiply -push=ut -multiply \
          -columnNames=filename=${output}.inv.columns,column=Name \
          | sddsxref -pipe=in ${output}.inv ${output}Mod.inv \
          -take=ActuatorName


        # first corrector
        exec sddsconvert  ${output} -pipe=out \
          -retain=col,BPMNames,[lindex $IEXOrb(corr$plane) 0] \
          | sddspseudoinverse -pipe \
          -oldColumnNames=ActuatorName \
          | sddsconvert -pipe=in ${output}\#1.inv \
          -edit=col,S*,ei/:msAve:${coord}:ErrorCC/
        
        # second corrector
        exec sddsconvert  ${output} -pipe=out \
          -retain=col,BPMNames,[lindex $IEXOrb(corr$plane) 1] \
          | sddspseudoinverse -pipe \
          -oldColumnNames=ActuatorName \
          | sddsconvert -pipe=in ${output}\#2.inv \
          -edit=col,S*,ei/:msAve:${coord}:ErrorCC/
        APSSetVarAndUpdate Status "Created files ${output}.inv  ${output}.1SV.inv ${output}Mod.inv ${output}\#1.inv ${output}\#2.inv"
    }
    APSSetVarAndUpdate Status "CalculateInverses done."
    cd $IEX(logDir)
    return
}

proc IEXorbitScan {args} {
    APSParseArguments {skipVar abortVar}
    global IEX IEXOrb $abortVar $skipVar

    cd $IEX(logDir)
    exec mkdir -p Orbit
    cd Orbit
    set IEX(fileroot) $IEX(Mode)-$IEX(Beamline)-$IEX(Quasi)-$IEX(Direction)
    GetIEXCorrFilePageNumber

    #0 make sure no orbit correction is running.
    if {!$IEX(testrun)} {
        if [catch {DPRTFBOnOff -mode Off} result] {
            return -code error "$result"
        }
    }

    foreach setpoint $IEX(setpoints) {
        set output $IEX(fileroot)[format "%+05d" [expr int($setpoint*1000)]]
        if [file exists $output] {
            SetStatus "StartScan: existing file $output deleted."
            file delete -force $output
        }

        #1 set IEX to desired setpoint.
        if {$IEX(ramp)} {
            # Down curve, from positive side to negative side 
            if {[string match $IEX(Direction) Down] && ($setpoint <0 || $setpoint > 3.7) } {
                set actualmode [lindex [exec caget -n ID29:ActualMode] 1]
                if {$actualmode != $IEX(mode1)} {
                    exec caput -w 3 ID29:DesiredMode.VAL $IEX(mode1)
                    after 1000
                    exec cawait -interval=1 -waitFor=ID29:feedback.VAL,sameAs=Ready
                    exec cawait -interval=1 -waitFor=ID29:BusyRecord.VAL,sameAs=Done
                    if {$setpoint > 3.7} {set setpoint [expr -$setpoint]}
                }
            }
            # Up curve, start from negative side
            if {[string match $IEX(Direction) Up] && $setpoint <0} {
                set actualmode [lindex [exec caget -n ID29:ActualMode] 1]
                if {$actualmode != $IEX(mode1)} {
                    exec caput -w 3 ID29:DesiredMode.VAL $IEX(mode1)
                    after 1000
                    exec cawait -interval=1 -waitFor=ID29:feedback.VAL,sameAs=Ready
                    exec cawait -interval=1 -waitFor=ID29:BusyRecord.VAL,sameAs=Done
                    IEXRamp -energy $IEX(Emax)
                }
            }
            # Up curve, from negative side to positive side
            if {[string match $IEX(Direction) Up] && $setpoint > 0} {
                set actualmode [lindex [exec caget -n ID29:ActualMode] 1]
                if {$actualmode != $IEX(mode0)} {
                    exec caput -w 3 ID29:DesiredMode.VAL $IEX(mode0)
                    after 1000
                    exec cawait -interval=1 -waitFor=ID29:feedback.VAL,sameAs=Ready
                    exec cawait -interval=1 -waitFor=ID29:BusyRecord.VAL,sameAs=Done
                }
            }

            IEXRamp -energy $setpoint -touch 1
            after 2000
            SetStatus "IEXorbitScan: Ramp to desired setpoint $setpoint."
        }
        if {[set $abortVar] || [lindex [exec caget -n ID29:AbortUtility] 1]} {
            SetStatus "IEXorbitScan: scan aborted."
            set $abortVar 0
            exec caput ID29:AbortUtility 0
            cd $IEX(logDir)
            return
        }

        #2 run sddscontrollaw for the pairs of IEX corrector coils in each plane.
        set noskip 1
        if {$setpoint==3.8 && [string match $IEX(Direction) Up]} {set noskip 0}
        if {$noskip} {
        set slowOCinterval 8
        APSExecLog .srXOrbitControllaw \
            -lineLimit 2048  -width 100 \
            -name "SR x-orbit Correction for IEX" \
            -unixCommand "sddscontrollaw $IEX(rootDir)/$IEXOrb(hMatrix).inv \
                     -runControlPV=string=S:RC:OrbitControlLawXC \
                     -test=$IEX(rootDir)/hMatrix.tests \
                     \"-runControlDescription=string=special for IEX\" \
                     -despike -deltaLimit=value=0.05 \
                     -steps=1000 -gain=0.5 -verb -interval=$slowOCinterval \
            -postChangeExecution=/home/helios/oagData/sr/IDs/IEX/OrbitCorrection-post"
        APSExecLog .srYOrbitControllaw \
            -lineLimit 2048 -width 100 \
            -name "SR y-orbit Correction for IEX" \
            -unixCommand "sddscontrollaw $IEX(rootDir)/$IEXOrb(vMatrix).inv \
                     -runControlPV=string=S:RC:OrbitControlLawYC \
                     -test=$IEX(rootDir)/vMatrix.tests \
                     \"-runControlDescription=string=special for IEX\" \
                     -despike -deltaLimit=value=0.05  \
                     -steps=1000 -gain=0.5 -verb -interval=$slowOCinterval \
            -postChangeExecution=/home/helios/oagData/sr/IDs/IEX/OrbitCorrection-post" 
        SetStatus "sddscontrollaws started..."
         
        #3 wait for orbit convergence.
        SetStatus "Wait for orbit convergence..."
        after 5000
        if [catch {WaitForOrbitConvergence -orbitTolerance $IEXOrb(Tolerance) \
                       -maxSteps $IEXOrb(maxSteps) \
                       -skipVar $skipVar} result] {
            if [string match $result timeout] {
                SetStatus "Orbit convergence timed out. Proceeding with next point..."
            } else {
                return -code error "IEXorbitScan: $result"
            }
        }
         
        #4 stop sddscontrollaws 
        SetStatus "IEXorbitScan: abort sddscontrollaw"
        if [catch {exec cavput -list=S:RC:OrbitControlLawXC,S:RC:OrbitControlLawYC,S:rfFreqControlLawRC -list.ABRT=1} result ] {
            return -code error "IEXorbitScan: $result"
        }
        }
        #5 record the *[DU]S_Hor/Vert* PVs for IEX energy setpoint.
        set comment "IEX setpoint $setpoint"
        SetStatus "Recording corrector values..."
        if [catch {exec sddsstatmon $IEX(rootDir)/Monitor/ID-IEX.mon $output \
                     -interval=0.5 -includeStatistics=mean \
                     -samplesPerStatistic=10 -updateInterval=10 -steps=1 \
                     "-comment=Comment,[APSMakeSafeQualifierString $comment]" \
                 } result] {
            return -code error "StartScan: $result"
        }
        if {$setpoint<-3.5} {set setpoint -$setpoint}
        set index [exec sddsprocess $IEX(rootDir)/IEX.sdds -redef=col,Index,i_row -pipe=out \
                     -redef=para,Page,i_page -filt=para,Page,$IEX(CorrPage),$IEX(CorrPage) \
                     -filt=col,Energy,[expr $setpoint-0.01],[expr $setpoint+0.01] \
                     | sdds2stream -pipe=in -col=Index]
        exec sddsprocess $output -nowar -pipe=out \
          -proc=ID29:Hcorr_USMean,first,Hcorr10 -proc=ID29:Hcorr_DSMean,first,Hcorr20 \
          -proc=ID29:Vcorr_USMean,first,Vcorr10 -proc=ID29:Vcorr_DSMean,first,Vcorr20 \
          -redef=para,Index,$index -redef=para,Energy,$setpoint -redef=para,Page,$IEX(CorrPage) \
          | sddscollapse -pipe \
          | sddsprocess -pipe=in $output.proc -retain=col,Index,Energy,Page,*corr*0
    }
    cd $IEX(logDir)
    SetStatus "IEXorbitScan: Done."
    return
}

proc IEXorbProc {} {
    global IEX IEXOrb
    cd $IEX(logDir)/Orbit

    # 1 obtain corrector values at energy=3.8, we want them to be zero always
    SetStatus "Performing for $IEX(fileroot) ... "
    set IEX(fileroot) $IEX(Mode)-$IEX(Beamline)-$IEX(Quasi)-$IEX(Direction)
    set fileList [glob $IEX(fileroot)+????.proc $IEX(fileroot)-????.proc]
    set page [exec sdds2stream -col=Page $IEX(fileroot)+3800.proc]
    if {[string match $IEX(Direction) Up]} {
        set hcorr1 [exec sdds2stream -col=Hcorr10 $IEX(fileroot)+3800.proc]
        set hcorr2 [exec sdds2stream -col=Hcorr20 $IEX(fileroot)+3800.proc]
        set vcorr1 [exec sdds2stream -col=Vcorr10 $IEX(fileroot)+3800.proc]
        set vcorr2 [exec sdds2stream -col=Vcorr20 $IEX(fileroot)+3800.proc]
SetStatus "h1=$hcorr1, h2=$hcorr2, v1=$vcorr1, v2=$vcorr2"
        catch {eval exec sddscombine $fileList -merge -pipe=out \
                 | sddsprocess -pipe \
                 \"-redef=col,Hcorr10,Hcorr10 $hcorr1 -\" \"-redef=col,Hcorr20,Hcorr20 $hcorr2 -\" \
                 \"-redef=col,Vcorr10,Vcorr10 $vcorr1 -\" \"-redef=col,Vcorr20,Vcorr20 $vcorr2 -\" \
                 | sddssort -pipe=in $IEX(fileroot).proc -col=Index } results
    } else {
        catch {eval exec sddscombine $fileList -merge -pipe=out \
                 | sddssort -pipe=in $IEX(fileroot).proc -col=Index } results
    }

    catch {exec sddsprocess $IEX(rootDir)/IEX.sdds -pipe=out \
             -redef=para,Page,i_page -filt=para,Page,$page,$page -redef=col,Index,i_row \
             | sddsxref -pipe $IEX(fileroot).proc -take=*corr* -equa=Index \
             | sddsprocess -pipe \
             -redef=col,Hcorr1,Hcorr10 -redef=col,Hcorr2,Hcorr20 \
             -redef=col,Vcorr1,Vcorr10 -redef=col,Vcorr2,Vcorr20 \
             | sddsprocess -pipe=in $IEX(fileroot).out -del=col,*corr*0} results

    SetStatus "IEXorbProc: Done."
    cd $IEX(logDir)
    return
}

proc WaitForOrbitConvergence {args} {
    set orbitTolerance 0.010
    set maxSteps 10
    APSParseArguments {orbitTolerance maxSteps skipVar}
    
    global $skipVar IEXOrb
    set orbitTolerance [expr abs($orbitTolerance)]
    if ![file exist orbit.mon] {
        exec sddsmakedataset -pipe=out -col=BPMName,type=string \
          -data=[join [FindGoodBpms -plane H] ,] \
          | sddsprocess -pipe=in xBPM.mon \
          -edit=col,ControlName,BPMName,ei/:msAve:x:ErrorCC/ \
          -edit=col,ReadbackName,BPMName,ei/:msAve:x:ErrorCC/
        exec sddsmakedataset -pipe=out -col=BPMName,type=string \
          -data=[join [FindGoodBpms -plane V] ,] \
          | sddsprocess -pipe=in yBPM.mon \
          -edit=col,ControlName,BPMName,ei/:msAve:y:ErrorCC/ \
          -edit=col,ReadbackName,BPMName,ei/:msAve:y:ErrorCC/
        exec sddscombine xBPM.mon yBPM.mon orbit.mon  -merge
    }

    set steps $maxSteps
    while {$steps} {
        SetStatus "Waiting $steps more steps..."
        update
        if [set $skipVar] {
            SetStatus "Skipping orbit convergence. Exiting WaitForOrbitConvergence..."
            set $skipVar 0
            return 
        }
        update
        after 6000
#        set monID [open "|sddsvmonitor -erase \
#                -rootname=$IEXOrb(monitorDir)/SROrbit-Rootnames.vmon \
#                -suffixes=SROrbit-msAve.vmon \
#                test-$steps  -singleShot=noprompt -steps=1" w]
#        puts $monID "y"
#        after 2000
        incr steps -1
        update
    }
    SetStatus "Returned from WaitForOrbitConvergence beacsue of timeout."
    return -code error "timeout"
}

proc startControllaw {args} {
    global IEX IEXOrb
    set slowOCinterval 6
    if [catch {exec cavput -list=S:RC:OrbitControlLawXC.CLR=1} result ] {
        return -code error "startControllaw: $result"
    }
    APSExecLog .srXOrbitControllaw \
        -lineLimit 2048  -width 100 \
        -name "SR x-orbit Correction for IEX" \
        -unixCommand "sddscontrollaw $IEX(rootDir)/$IEXOrb(hMatrix).inv \
                     -runControlPV=string=S:RC:OrbitControlLawXC \
                     -test=$IEX(rootDir)/hMatrix.tests \
                     \"-runControlDescription=string=special for IEX\" \
                     -despike -deltaLimit=value=0.05 \
                     -steps=1000 -gain=0.5 -verb -interval=$slowOCinterval" \
        -postChangeExecution=/home/helios/oagData/sr/IDs/IEX/OrbitCorrection-post        
    if [catch {exec cavput -list=S:RC:OrbitControlLawYC.CLR=1} result ] {
        return -code error "startControllaw: $result"
    }
    APSExecLog .srYOrbitControllaw \
        -lineLimit 2048 -width 100 \
        -name "SR y-orbit Correction for IEX" \
        -unixCommand "sddscontrollaw $IEX(rootDir)/$IEXOrb(vMatrix).inv \
                     -runControlPV=string=S:RC:OrbitControlLawYC \
                     -test=$IEX(rootDir)/vMatrix.tests \
                     \"-runControlDescription=string=special for IEX\" \
                     -despike -deltaLimit=value=0.05 \
                     -steps=1000 -gain=0.5 -verb -interval=$slowOCinterval" \
        -postChangeExecution=/home/helios/oagData/sr/IDs/IEX/OrbitCorrection-post        
    SetStatus "sddscontrollaws started..."
    return
}

set IEXTune(Instrument) NASA
set IEXTune(output) ""
set IEXTune(xTune) 0.2
set IEXTune(yTune) 0.22
set IEXTune(xTune0) 0.14
set IEXTune(xTune1) 0.22
set IEXTune(cali1) 0
set IEXTune(cali2) 0
set IEXTune(USQ) 0.5
set IEXTune(DSQ) [expr 1.0-$IEXTune(USQ)]
set IEXTune(root) ""

proc MakeIEXTuneWidget {widget args} {
    global IEX IEXTune

    set parent ""
    APSParseArguments {parent}
    APSFrame $widget -parent $parent -label "IEX Tune Measurement Input Parameters" \
        -contextHelp "Collects tune readbacks at various IEX settings. Then the quad correction is calculated and the correction file is updated."
    set w $parent$widget.frame

    APSRadioButtonFrame .instrument -parent $w -orientation horizontal \
        -label "Instrument for tune measurement: " -variable IEXTune(Instrument) \
        -limitPerRow 2 -buttonList {NASA VSA} -valueList {NASA VSA} \
        -contextHelp \
        "Choose Instrument for tune measurement."
    set cali1 [lindex [exec sdds2stream $IEX(rootDir)/IEX.sdds -para=CaliQ1] 0]
    set cali2 [lindex [exec sdds2stream $IEX(rootDir)/IEX.sdds -para=CaliQ2] 0]
    set IEXTune(cali1) [format %.5g $cali1]
    set IEXTune(cali2) [format %.5g $cali2]
    APSFrameGrid .fg -parent $w -xList {x y}
    set wx $w.fg.x
    APSLabeledEntry .xtune -parent $wx -label  "xTune:     " -textVariable IEXTune(xTune) \
        -contextHelp "Estimated x tune without IEX."
    APSLabeledEntry .xtune0 -parent $wx -label "xTuneLow:  " -textVariable IEXTune(xTune0) \
        -contextHelp "Lower limit of xtune search window"
    APSLabeledEntry .cali1 -parent $wx -label "US-Quad Calibration Number: " \
        -textVariable IEXTune(cali1) \
        -width 10 -contextHelp "US-Quad calibration coefficient"
    APSLabeledEntry .usq -parent $wx -label "US-Quad Strength Ratio: " \
        -textVariable IEXTune(USQ) \
        -width 10 -contextHelp "How much correction do you want to use for USQ?"
    set wy $w.fg.y
    APSLabeledEntry .ytune -parent $wy -label  "yTune:     " -textVariable IEXTune(yTune) \
        -contextHelp "Estimated y tune without IEX."
    APSLabeledEntry .xtune1 -parent $wy -label "xTuneHigh: " -textVariable IEXTune(xTune1) \
        -contextHelp "Upper limit of xtune search window"
    APSLabeledEntry .cali2 -parent $wy -label "DS-Quad Calibration number: " \
        -textVariable IEXTune(cali2) \
        -width 10 -contextHelp "DS-Quad calibration coefficient"
    APSLabeledEntry .dsq -parent $wy -label "DS-Quad Strength Ratio: " \
        -textVariable IEXTune(DSQ) \
        -width 10 -contextHelp "How much correction do you want to use for DSQ?, always 1-US strength ratio"

    APSLabeledOutput .outroot -parent $w -label "File Root: " \
        -textVariable IEXTune(root) \
        -width 40 \
        -contextHelp "The output file root, it has been set by the code."
    APSLabeledEntry .newfile -parent $w -label "New Correction File: " \
        -textVariable IEXTune(output) \
        -width 40 \
        -contextHelp "New correction file resulting from measurement"
    APSButton .calibrate -parent $parent -text Calibration \
        -command {IEXTuneQuadCalibrate -abortVar abortRun} \
        -contextHelp "Calibrate quads settings."
    APSButton .process1 -parent $parent -text Cali-PROCESS \
        -command IEXTuneQuadCaliProc \
        -contextHelp "re-processing the calibration data."
    APSButton .run -parent $parent -text Collect \
        -command {IEXTuneCollectData -abortVar abortRun} \
        -contextHelp "Collects tunes at various IEX setting points"
    APSButton .plot -parent $parent -text Plot \
        -command IEXTunePlotData \
        -contextHelp "Plot measurement data to see if it's OK."
    APSButton .process -parent $parent -text PROCESS \
        -command IEXTuneProcessData \
        -contextHelp "Selects and processes the data collected today."
}

proc IEXTuneCollectData {args} {
    APSParseArguments {abortVar}
    global IEX IEXTune $abortVar

    cd $IEX(logDir)
    set subDir Tune
    exec mkdir -p $subDir
    cd $subDir

    set IEXTune(root) $IEX(Mode)-$IEX(Beamline)-$IEX(Quasi)-$IEX(Direction)
    GetIEXCorrFilePageNumber

    set fileList ""
    foreach setpoint $IEX(setpoints) {
        set output $IEXTune(root)[format "%+05d" [expr int($setpoint*1000)]]
        if {[set $abortVar] || [lindex [exec caget -n ID29:AbortUtility] 1]} {
            set $abortVar 0
            exec caput ID29:AbortUtility 0
            cd $IEX(logDir)
            SetStatus "IEXTuneCollectData aborted"
            return
        }
        if {$IEX(ramp)} {
            # Down curve, from positive side to negative side 
            if {[string match $IEX(Direction) Down] && ($setpoint <0 || $setpoint > 3.7) } {
                set actualmode [lindex [exec caget -n ID29:ActualMode] 1]
                if {$actualmode != $IEX(mode1)} {
                    exec caput -w 3 ID29:DesiredMode.VAL $IEX(mode1)
                    after 1000
                    exec cawait -interval=1 -waitFor=ID29:feedback.VAL,sameAs=Ready
                    exec cawait -interval=1 -waitFor=ID29:BusyRecord.VAL,sameAs=Done
                    if {$setpoint > 3.7} {set setpoint [expr -$setpoint]}
                }
            }
            # Up curve, start from negative side
            if {[string match $IEX(Direction) Up] && $setpoint <0} {
                set actualmode [lindex [exec caget -n ID29:ActualMode] 1]
                if {$actualmode != $IEX(mode1)} {
                    exec caput -w 3 ID29:DesiredMode.VAL $IEX(mode1)
                    after 1000
                    exec cawait -interval=1 -waitFor=ID29:feedback.VAL,sameAs=Ready
                    exec cawait -interval=1 -waitFor=ID29:BusyRecord.VAL,sameAs=Done
                    IEXRamp -energy $IEX(Emax)
                }
            }
            # Up curve, from negative side to positive side
            if {[string match $IEX(Direction) Up] && $setpoint > 0} {
                set actualmode [lindex [exec caget -n ID29:ActualMode] 1]
                if {$actualmode != $IEX(mode0)} {
                    exec caput -w 3 ID29:DesiredMode.VAL $IEX(mode0)
                    after 1000
                    exec cawait -interval=1 -waitFor=ID29:feedback.VAL,sameAs=Ready
                    exec cawait -interval=1 -waitFor=ID29:BusyRecord.VAL,sameAs=Done
                }
            }
            IEXRamp -energy $setpoint -touch 1
        }        

        SetStatus "Doing energy=$setpoint" 
        catch {collectTune -setpoint $setpoint -fileRoot $output } status

        # also recort IEX settings
        if [catch {exec sddsmonitor $IEX(moniDir)/ID-IEX.mon \
                     $IEXTune(root).mon  -append=toPage -inter=1 -steps=1 \
                     "-comment=Comment,[APSMakeSafeQualifierString $IEX(comment)]" \
                 } result ] {
            SetStatus "CollectTune - start sddsmonitor: $result"
        }
    }

    SetStatus "Tune collection finished"
    cd $IEX(logDir)
    return
}

proc IEXTunePlotData {} {
    global IEX IEXTune
    cd $IEX(logDir)/Tune

    set IEXTune(root) $IEX(Mode)-$IEX(Beamline)-$IEX(Quasi)-$IEX(Direction)
    GetIEXCorrFilePageNumber

    set fileList [glob $IEXTune(root)-???? $IEXTune(root)+????]
    set delFile ""
    foreach file $fileList {
        SetStatus "Now doing $file"
        set e0 [exec sdds2stream $file -para=Energy]
        set index [exec sddsprocess $IEX(rootDir)/IEX.sdds -redef=col,Index,i_row -pipe=out \
                     -redef=para,Page,i_page -filt=para,Page,$IEX(CorrPage),$IEX(CorrPage) \
                     -filt=col,Energy,[expr $e0-0.001],[expr $e0+0.001] \
                     | sdds2stream -pipe=in -col=Index]
        exec sddsprocess $file -nowar -redef=para,Index,$index
        lappend delFile $file~
    }
    eval exec sddscombine $fileList -pipe=out \
      | sddscollapse -pipe \
      | sddssort -pipe -col=Index \
      | sddsprocess -pipe=in $IEXTune(root).peaks  \
      -redef=para,Page,$IEX(CorrPage)
    exec sddsplot -col=Index,xTune $IEXTune(root).peaks -gra=sym,sca=3,conn=sub,sub=0 &

    set colName ID29:ByDesired
    if [string match $IEX(Mode) V] {
        set colName ID29:BxDesired
    }
    exec sddssort $IEXTune(root).mon -col=$colName -nowarnings
    eval file delete -force $delFile $IEXTune(root).mon~ 
    cd $IEX(logDir)
}

proc IEXTuneProcessData {} {
    global IEX IEXTune
    cd $IEX(logDir)/Tune

    set fileList [glob *peaks]
    catch {set files [APSChooseItemFromList -name "Select data file for processing" \
                           -itemList $fileList -returnList $fileList \
                           -returnIndices 0 -multiItem 1 \
                           -contextHelp "Select files for processing."]} result 

    set IEXTune(DSQ) [expr 1.0-$IEXTune(USQ)]
    set output ""
    exec sddsprocess $IEX(rootDir)/IEX.sdds tune.1 -nowarning -redef=para,Page,i_page
    foreach filename $files {
        SetStatus "Now processing file $filename"    
        set root [file root [file root $filename]]
        set ipage [exec sdds2stream $filename -para=Page]
        catch {exec sddsprocess $filename -pipe=out \
                 -filte=col,Energy,3.75,3.85 -proc=xTune,first,Tune0 \
                 | sdds2stream -pipe=in -para=Tune0} referenceTune
        exec sddsprocess $filename $filename.corr \
          "-def=col,xTuneDiff,xTune $referenceTune -" \
          "-def=col,dQ1,xTuneDiff $IEXTune(USQ) * $IEXTune(cali1) / chs,units=A" \
          "-def=col,dQ2,xTuneDiff $IEXTune(DSQ) * $IEXTune(cali2) / chs,units=A"
        exec sddsprocess $IEX(rootDir)/IEX.sdds tune.0 -nowarning \
          -redef=para,Page,i_page -filt=para,Page,$ipage,$ipage
        exec sddsprocess tune.1 -nowarning \
          -redef=para,Page,i_page "-filt=para,Page,$ipage,$ipage,\!"
        exec sddsxref tune.0 $filename.corr -pipe=out -take=dQ\[12\] \
          | sddsprocess -pipe=in $root.out \
          "-redef=col,NormalQ1,NormalQ1 dQ1 +" \
          "-redef=col,NormalQ2,NormalQ2 dQ2 +"
        lappend output $root.out
    }
    if {![string length $IEXTune(output)]} {
        set IEXTune(output) IEX-[exec date +%Y-%m-%d]
    }
    eval exec sddscombine $output tune.1 -pipe=out \
      | sddssort -pipe -para=Page \
      | sddsprocess -pipe=in $IEXTune(output) -del=col,dQ1,dQ2
    eval file delete -force "tune.0 tune.1 tune.1~"
    cd $IEX(logDir)
    SetStatus "IEXTuneProcessData Done"
}

proc IEXTuneQuadCalibrate {args} {
    APSParseArguments {abortVar}
    global IEX IEXTune $abortVar

    cd $IEX(logDir)
    set subDir QuadCali
    exec mkdir -p $subDir
    cd $subDir
    
    # Turn off IEX
    if [catch {exec caput -w 3 ID29:Main_on_off.VAL 1 \
               } result] {
        APSSetVarAndUpdate Status "IEXTuneQuad: $result"        
        return -code error "IEXTuneQuadCali: $result"
    }

    set points 5
    set limits 1

    # Measure US Quad
    set IEX(CorrValueNew) "0 0 0 0 0 0 0 0 0 0 0 0"
    PutIEXCorrValues -energy 3.8
    for {set i 0} {$i < $points} {incr i} {
        if {[set $abortVar] || [lindex [exec caget -n ID29:AbortUtility] 1]} {
            set $abortVar 0
            exec caput ID29:AbortUtility 0
            cd $IEX(logDir)
            set IEX(CorrValueNew) "0 0 0 0 0 0 0 0 0 0 0 0"
            PutIEXCorrValues -energy 3.8
            SetStatus "IEXTuneQuadCalibrate aborted"
            return
        }
        set setpoint [expr -$limits + $i * 2.0 * $limits / ($points - 1)]
        lset IEX(CorrValueNew) 4 $setpoint
        PutIEXCorrValues -energy 3.8
        SetStatus "Doing US setpoint [format %.2f $setpoint] A ..."

        after 1000
        set if [format %02ld $i]
        catch {collectTune -setpoint $setpoint -fileRoot US-quadCali-${if}} status     
    }
    eval exec sddscombine [lsort [glob US-quadCali-??]] -pipe=out \
        | sddscollapse -pipe=in US-quadCali.peaks 
    exec sddspfit US-quadCali.peaks US-quadCali.fit \
        -col=Energy,xTune \
        -range=-1,1 -terms=2 \
        -evaluate=US-quadCali.eval,begin=-1,end=1.number=50
    exec sddsplot -col=Energy,xTune US-quadCali.peaks \
        -gra=sym,sca=3,conn=sub,sub=0 \
        -leg=spec=Peaks \
        -col=Energy,xTune US-quadCali.eval -grap=line,type=1 -leg=spec=Fit \
        &
    # Measure DS Quad
    set IEX(CorrValueNew) "0 0 0 0 0 0 0 0 0 0 0 0"
    PutIEXCorrValues -energy 3.8 
    for {set i 0} {$i < $points} {incr i} {
        if {[set $abortVar] || [lindex [exec caget -n ID29:AbortUtility] 1]} {
            set $abortVar 0
            exec caput ID29:AbortUtility 0
            cd $IEX(logDir)
            set IEX(CorrValueNew) "0 0 0 0 0 0 0 0 0 0 0 0"
            PutIEXCorrValues -energy 3.8
            SetStatus "IEXTuneQuadCalibrate aborted"
            return
        }
        set setpoint [expr -$limits + $i * 2.0 * $limits / ($points - 1)]
        lset IEX(CorrValueNew) 10 $setpoint
        PutIEXCorrValues -energy 3.8
        SetStatus "Doing DS setpoint [format %.2f $setpoint] A ..."

        after 1000
        set if [format %02ld $i]
        catch {collectTune -setpoint $setpoint -fileRoot DS-quadCali-${if}} status     
    }
    eval exec sddscombine [lsort [glob DS-quadCali-??]] -pipe=out \
        | sddscollapse -pipe=in DS-quadCali.peaks 
    exec sddspfit DS-quadCali.peaks DS-quadCali.fit \
        -col=Energy,xTune \
        -range=-1,1 -terms=2 \
        -evaluate=DS-quadCali.eval,begin=-1,end=1.number=50
    exec sddsplot -col=Energy,xTune DS-quadCali.peaks \
        -gra=sym,sca=3,conn=sub,sub=0 \
        -leg=spec=Peaks \
        -col=Energy,xTune DS-quadCali.eval -grap=line,type=1 -leg=spec=Fit \
        &

    set IEXTune(cali1) [format %.5g [exec sdds2stream -para=Slope US-quadCali.fit]]
    set IEXTune(cali2) [format %.5g [exec sdds2stream -para=Slope DS-quadCali.fit]]

    #return everything to zero
    set IEX(CorrValueNew) "0 0 0 0 0 0 0 0 0 0 0 0"
    PutIEXCorrValues -energy 3.8

    SetStatus "Calibration coefficient: $IEXTune(cali1) (US) $IEXTune(cali2) (DS)" 
    SetStatus "Calibration done."
    cd $IEX(logDir)
    return
} 

proc IEXTuneQuadCaliProc {} {
    global IEX IEXTune
    cd $IEX(logDir)/QuadCali

    eval exec sddscombine [lsort [glob US-quadCali-??]] -pipe=out \
        | sddscollapse -pipe=in US-quadCali.peaks 
    exec sddspfit US-quadCali.peaks US-quadCali.fit \
        -col=Energy,xTune \
        -range=-1,1 -terms=2 \
        -evaluate=US-quadCali.eval,begin=-1,end=1.number=50
    exec sddsplot -col=Energy,xTune US-quadCali.peaks \
        -gra=sym,sca=3,conn=sub,sub=0 \
        -leg=spec=Peaks \
        -col=Energy,xTune US-quadCali.eval -grap=line,type=1 -leg=spec=Fit \
        &
    eval exec sddscombine [lsort [glob DS-quadCali-??]] -pipe=out \
        | sddscollapse -pipe=in DS-quadCali.peaks 
    exec sddspfit DS-quadCali.peaks DS-quadCali.fit \
        -col=Energy,xTune \
        -range=-1,1 -terms=2 \
        -evaluate=DS-quadCali.eval,begin=-1,end=1.number=50
    exec sddsplot -col=Energy,xTune DS-quadCali.peaks \
        -gra=sym,sca=3,conn=sub,sub=0 \
        -leg=spec=Peaks \
        -col=Energy,xTune DS-quadCali.eval -grap=line,type=1 -leg=spec=Fit \
        &

    set IEXTune(cali1) [format %.5g [exec sdds2stream -para=Slope US-quadCali.fit]]
    set IEXTune(cali2) [format %.5g [exec sdds2stream -para=Slope DS-quadCali.fit]]

    cd $IEX(logDir)
}

proc collectTune {args} {
    APSParseArguments {setpoint fileRoot}
    global IEX IEXTune 
    set xPower 9.0
    set yPower 3.0
    set span 40000
    set rfFrequency [exec cavget -list=A014-IETS:BTC:SRSetFreqM -floatFormat=%.1f -pendIoTime=5]
    set revFrequency [expr $rfFrequency / 1296]
    set measHarmonic 1390
    set sideband -1
    set xFreq [format %.1f [expr $revFrequency * ($measHarmonic + $IEXTune(xTune) * $sideband)]]
    set yFreq [format %.1f [expr $revFrequency * ($measHarmonic + $IEXTune(yTune) * $sideband)]]
    set dividingLine [format %.3f [expr ($IEXTune(xTune) + $IEXTune(yTune))/2.0]]

    SetStatus "Getting tunes from $IEXTune(Instrument) ..."
    if [catch {APSSetTuneMultiplexer -mode $IEXTune(Instrument)} result] {
        SetStatus $result
        return
    }

    switch $IEXTune(Instrument) {
        VSA {
            if [catch {exec getxytunes -root $fileRoot -only x -plotTunes 0 \
                           -description "Setpoint $setpoint" -xTune $IEXTune(xTune) -yTune $IEXTune(yTune)  \
                       } results] {
                SetStatus "$results"
                return 
            }
            if [catch {exec sddsprocess ${fileRoot}-x.sdds -pipe=out -noWarning \
                           -def=para,Energy,$setpoint \
                           | sddsprocess -pipe -fil=col,Tune,$IEXTune(xTune0),$IEXTune(xTune1) \
                           | sddssmooth -pipe -col=Waveform -points=3 -pass=6 \
                           | sddsprocess -pipe=in ${fileRoot} \
                           -proc=Waveform,largest,xTune,position,functionOf=Tune \
                       } results] {
                SetStatus "sddsprocess ${fileRoot}-x.sdds ...: $results"
            }
            file delete ${fileRoot}-x.sdds
        }
        NASA { 
            APSMpMeasureTunes -ring SR -averagingSeconds -1
            APSMpMeasureTunes -ring SR -useExistingData 1 -averagingSeconds -1 -measureXYtuneTogether 0 \
                -plotData 0 -dividingLine $dividingLine -xPower $xPower -yPower $yPower -span $span \
                -xFreq $xFreq -yFreq $yFreq -outputFile $fileRoot
            if [catch {exec sddsprocess ${fileRoot} -noWarning -match=para,plane=x \
                           -def=para,Energy,$setpoint \
                       } results] {
                SetStatus "sddsprocess ${fileRoot} ...: $results"
            }
            
#            file delete ${fileRoot}~
        }
    }    
    update
    return
}

set IEXCoup(xmin) -0.4
set IEXCoup(xmax)  0.4
set IEXCoup(ymin) -0.4
set IEXCoup(ymax)  0.4
set IEXCoup(xpoints) 5
set IEXCoup(ypoints) 5
set IEXCoup(coup0) 1.3
set IEXCoup(coupRange) 0.01
set IEXCoup(SQUS0) 0
set IEXCoup(SQDS0) 0
set IEXCoup(SQUS1) 0
set IEXCoup(SQDS1) 0
set IEXCoup(input) ""
set IEXCoup(output) ""
set IEXCoup(dataIndex) ""
set IEXCoup(wait) 7
set IEXCoup(monStep) 5

proc MakeIEXCoupWidget {widget args} {
    global IEX IEXCoup

    set parent ""
    APSParseArguments {parent}
    APSFrame $widget -parent $parent -label "IEX Couping Measurement Input Parameters" -contextHelp "This is used for coupling correction. "
    set w $parent$widget.frame

    APSFrameGrid .fg -parent $w -xList {x1 x2 x3 x4}
    set w0 $w.fg.x1
    APSLabel .sq1name -parent $w0 -text "SkewQuad (US)  " -contextHelp "Relative US SQ tweak amplitude."
    APSLabel .sq2name -parent $w0 -text "SkewQuad (DS)  " -contextHelp "Relative DS SQ tweak amplitude."
    set w0 $w.fg.x2
    APSLabeledEntry .sq1L -parent $w0 -width 10 -textVariable IEXCoup(xmin) \
        -label "Negative Range:" \
        -contextHelp "Low limitation on the relative US skewQuad scan."
    APSLabeledEntry .sq2L -parent $w0 -width 10 -textVariable IEXCoup(ymin) \
        -label "Negative Range:" \
        -contextHelp "Low limitation on the relative DS skewQuad scan."
    set w0 $w.fg.x3
    APSLabeledEntry .sq1H -parent $w0 -width 10 -textVariable IEXCoup(xmax) \
        -label "Positive Range:" \
        -contextHelp "High limitation on the relative US skewQuad scan."
    APSLabeledEntry .sq2H -parent $w0 -width 10 -textVariable IEXCoup(ymax) \
        -label "Positive Range:" \
        -contextHelp "High limitation on the relative DS skewQuad scan."
    set w0 $w.fg.x4
    APSLabeledEntry .sq1Step -parent $w0 -width 5 -textVariable IEXCoup(xpoints) \
        -label "Steps:" \
        -contextHelp "Steps of US skewQuad scan"
    APSLabeledEntry .sq2Step -parent $w0 -width 5 -textVariable IEXCoup(ypoints) \
        -label "Steps:" \
        -contextHelp "Steps of DS skewQuad scan"

    APSFrame .coupP -parent $parent -label "IEX Couping Processing Parameters"
    set w $parent.coupP.frame

    APSFrameGrid .fg1 -parent $w -xList {x2 x3}
    set w0 $w.fg1.x2
    APSLabeledEntry .coup0 -parent $w0 -width 10 -textVariable IEXCoup(coup0) \
        -label "Desired Coupling:" -contextHelp "Desired coupling:"
    APSButton .setcoup0 -parent $w0.coup0 -packOption "-anchor e" \
        -text "Set" -size small -command {SetCoupBase}
    set w0 $w.fg1.x3
    APSLabeledEntry .coup1 -parent $w0 -width 10 -textVariable IEXCoup(coupRange) \
      -label "        Allowed Relative Error:"
   
    APSFrameGrid .fg2 -parent $w -xList {x1 x2 x3 x4}
    set w0 $w.fg2.x1
    APSLabeledOutput .squs0 -parent $w0 -width 10 -textVariable IEXCoup(SQUS0) \
        -label "Old SQUS:" -contextHelp "US SQ settings from file"
    set w0 $w.fg2.x2
    APSLabeledOutput .sqds0 -parent $w0 -width 10 -textVariable IEXCoup(SQDS0) \
        -label "Old SQDS:" -contextHelp "DS SQ settings from file"
    set w0 $w.fg2.x3
    APSLabeledEntry .squs1 -parent $w0 -width 10 -textVariable IEXCoup(SQUS1) \
        -label "New SQUS:" -contextHelp "New US SQ settings"
    set w0 $w.fg2.x4
    APSLabeledEntry .sqds1 -parent $w0 -width 10 -textVariable IEXCoup(SQDS1) \
        -label "New SQDS:" -contextHelp "New DS SQ settings"
    APSLabeledEntry .input -parent $w -width 20 -textVariable IEXCoup(input) \
        -label "File for Plot and Process:" -contextHelp "Please select the file for review and processing. First press Plot Single filtered button. After reviwe the data, write down new result to New SQ value field, then press Process Single button."
    APSLabeledOutput .index -parent $w -width 20 -textVariable IEXCoup(dataIndex) \
        -label "Row Index of the processing data" -contextHelp "Show which row the new data will get into the correction table for double checking."
    APSButton .inputsel -parent $w.input -packOption "-anchor e" \
        -text "Select" -size small -command {IEXCoupSelect}
    APSLabeledEntry .out -parent $w -width 20 -textVariable IEXCoup(output) \
        -label "Output Root:" -contextHelp "Input the output file name."    

    APSButton .runCoup -parent $parent -text Scan-2D -command \
        { \
            catch {CollectCouplingData -abortVar abortRun} status
            cd $IEX(logDir)
            update idletasks
        } \
        -contextHelp "2D-Scan US/DS skew quad settings and record coupling change."
    APSButton .plot0 -parent $parent -text "PLOT Single" -command \
        { \
            catch {PlotCouplingData -plotMode single} status
            cd $IEX(logDir)
            update idletasks
        } \
        -contextHelp "Plot the single page contour plot."
    APSButton .plot1 -parent $parent -text "PLOT All" -command \
        { \
            catch {PlotCouplingData -plotMode all} status
            cd $IEX(logDir)
            update idletasks
        } \
        -contextHelp "Plot all data collected today."
    APSButton .plot2 -parent $parent -text "PLOT Single Filtered" -command \
        { \
            catch {PlotCouplingData -plotMode filtered} status
            cd $IEX(logDir)
            update idletasks
        } \
        -contextHelp "Plot filtered data of each page for processing."
    APSButton .proc1 -parent $parent -text "Process Single" -command \
        { \
            catch {ProcessCouplingData -mode single} status
            cd $IEX(logDir)
            update idletasks
        } \
        -contextHelp "Modify correction table using new SQ settings for 1 point."
    APSButton .proc2 -parent $parent -text "Process All" -command \
        { \
            catch {ProcessCouplingData -mode all} status
            cd $IEX(logDir)
            update idletasks
        } \
        -contextHelp "Combine all results to get new file."
}

proc CollectCouplingData {args} {
    APSParseArguments {abortVar}
    global IEX IEXCoup $abortVar

    cd $IEX(logDir)
    set IEXCoup(output) $IEX(Mode)-$IEX(Beamline)-$IEX(Quasi)-$IEX(Direction)
    exec mkdir -p $IEXCoup(output)
    cd $IEXCoup(output)

    set frame0 [lindex [exec caget S:VID1:numToAvgC] 1]
    set ave0 [lindex [exec caget S:VID1:filterWidth] 1]
    if {!$IEX(dryrun)} {
        # set S:VID1 averaging
        if {!$IEX(testrun)} {
            exec caput -w 3 S:VID1:numToAvgC 30
            exec caput -w 3 S:VID1:filterWidth 10
        }

        # Loop over setpoints
        set dx [expr ($IEXCoup(xmax)-$IEXCoup(xmin))/($IEXCoup(xpoints)-1.0)]
        set dy [expr ($IEXCoup(ymax)-$IEXCoup(ymin))/($IEXCoup(ypoints)-1.0)]
        foreach setpoint $IEX(setpoints) {
            if {[set $abortVar] || [lindex [exec caget -n ID29:AbortUtility] 1]} {
                set $abortVar 0
                exec caput ID29:AbortUtility 0
                cd $IEX(logDir)
                SetStatus "CollectCouplingData aborted"
                return
            }
            if {$IEX(ramp)} {
                # Up curve, start from negative side
                if {[string match $IEX(Direction) Up] && $setpoint <0} {
                    set actualmode [lindex [exec caget -n ID29:ActualMode] 1]
                    if {$actualmode != $IEX(mode1)} {
                        exec caput -w 3 ID29:DesiredMode.VAL $IEX(mode1)
                        after 1000
                        exec cawait -interval=1 -waitFor=ID29:feedback.VAL,sameAs=Ready
                        exec cawait -interval=1 -waitFor=ID29:BusyRecord.VAL,sameAs=Done
                        IEXRamp -energy $IEX(Emax)
                    }
                }
                # Up curve, from negative side to positive side
                if {[string match $IEX(Direction) Up] && $setpoint > 0} {
                    set actualmode [lindex [exec caget -n ID29:ActualMode] 1]
                    if {$actualmode != $IEX(mode0)} {
                        exec caput -w 3 ID29:DesiredMode.VAL $IEX(mode0)
                        after 1000
                        exec cawait -interval=1 -waitFor=ID29:feedback.VAL,sameAs=Ready
                        exec cawait -interval=1 -waitFor=ID29:BusyRecord.VAL,sameAs=Done
                    }
                }
                # Down curve, from positive side to negative side 
                if {[string match $IEX(Direction) Down] && ($setpoint <0 || $setpoint > 3.7) } {
                    set actualmode [lindex [exec caget -n ID29:ActualMode] 1]
                    if {$actualmode != $IEX(mode1)} {
                        exec caput -w 3 ID29:DesiredMode.VAL $IEX(mode1)
                        after 1000
                        exec cawait -interval=1 -waitFor=ID29:feedback.VAL,sameAs=Ready
                        exec cawait -interval=1 -waitFor=ID29:BusyRecord.VAL,sameAs=Done
                        if {$setpoint > 3.7} {set setpoint [expr -$setpoint]}
                    }
                }
                IEXRamp -energy $setpoint -touch 1
                after 2000
                SetStatus "IEXorbitScan: Ramp to desired setpoint $setpoint."
            }

            set output $IEXCoup(output)[format "%+05d" [expr int($setpoint*1000)]]

            # scan IEX skew quads
            GetIEXCorrValues -energy $setpoint
            set IEX(CorrValueNew)  $IEX(CorrValueOld)
            set xOld [lindex $IEX(CorrValueOld) 1]
            set yOld [lindex $IEX(CorrValueOld) 7]
            set x0 [expr $xOld+$IEXCoup(xmin)]
            set y0 [expr $yOld+$IEXCoup(ymin)]
            SetStatus "Original_SQ1=$xOld, Original_SQ2=$yOld"
            for {set ix 0} {$ix<$IEXCoup(xpoints)} {incr ix} {
                lset IEX(CorrValueNew) 1 [format %.3f [expr $x0+$dx*$ix]]           
                PutIEXCorrValues -energy $setpoint
                for {set iy 0} {$iy<$IEXCoup(ypoints)} {incr iy} {
                    SetStatus "Now doing data point ix=$ix, iy=$iy"
                    if {[set $abortVar] || [lindex [exec caget -n ID29:AbortUtility] 1]} {
                        set $abortVar 0
                        exec caput ID29:AbortUtility 0
                        cd $IEX(logDir)
                        SetStatus "CollectCouplingData aborted"
                        return
                    }
                    lset IEX(CorrValueNew) 7 [format %.3f [expr $y0+$dy*$iy]]
                    PutIEXCorrValues -energy $setpoint
                    # wait long for coupling reading to settle
                    APSWaitWithUpdate -waitSeconds $IEXCoup(wait) -updateInterval 1
                    if [catch {exec sddsmonitor $IEX(moniDir)/IEX-coup.mon \
                                   $output -inter=1 -append -steps=$IEXCoup(monStep) \
                                   "-comment=Comment,[APSMakeSafeQualifierString $IEX(comment)]" \
                               } result ] {
                        SetStatus "CollectCoupling - start sddsmonitor: $result"
                    }
                } 
            }
            # Set corrector back to original value
            lset IEX(CorrValueNew) 1 [format %.3f $xOld]           
            lset IEX(CorrValueNew) 7 [format %.3f $yOld]           
            PutIEXCorrValues -energy $setpoint
            # Process the result
            set IEXCoup(SQUS0) [lindex [exec caget ID29:usSQ1] 1]
            set IEXCoup(SQDS0) [lindex [exec caget ID29:dsSQ1] 1]
            set hcoil [lindex [exec caget ID29:BxSet.VAL] 1]
            set vcoil [lindex [exec caget ID29:BySet.VAL] 1]

            if [catch {exec sddsprocess $output -pipe=out \
                           -proc=ID29:SQ_US,ave,SQ_US -proc=ID29:SQ_DS,ave,SQ_DS \
                           -proc=Coupling,ave,Coupling -retain=para,SQ*,Couping \
                           | sddscollapse -pipe \
                           | sdds2dinterpolate -pipe -dep=Coupling \
                           -independentColumn=xcol=SQ_US,ycol=SQ_DS \
                           -outDimen=xdim=201,ydim=201 \
                           | sddsprocess -pipe=in $output.2d \
                           -redef=para,Hcoil,$hcoil -redef=para,Vcoil,$vcoil \
                           -redef=para,USSQ0,$IEXCoup(SQUS0) -redef=para,DSSQ0,$IEXCoup(SQDS0) \
                           "-redef=para,Energy,$setpoint"
            } result] {
                return -code error "CollectCoupling: $output process error. $result"
            }           

            if {$setpoint==3.8} {
                catch {exec sddsprocess $output.2d -clip=20200,20200 -pipe=out \
                           | sdds2stream -pipe=in -col=Coupling} result
                set IEXCoup(coup0) [format %5.3f $result]
            }
        }
    }

    # Update message to status window.
    cd $IEX(logDir)
    if !$IEX(testrun) {
        exec caput -w 3 S:VID1:numToAvgC $frame0
        exec caput -w 3 S:VID1:filterWidth $ave0
    }
    SetStatus "CollectCouplingData done."
    return
}

proc SetCoupBase {} {
    global IEX IEXCoup

    set IEXCoup(output) $IEX(Mode)-$IEX(Beamline)-$IEX(Quasi)-$IEX(Direction)
    cd $IEX(logDir)/$IEXCoup(output)
    set output $IEXCoup(output)+3800
    catch {exec sddsprocess $output.2d -del=para,c0 -pipe=out \
               -filt=col,SQ_US,-0.01,0.01 -filt=col,SQ_DS,-0.01,0.01 \
               | sddsprocess -pipe -proc=Coupling,ave,c0 \
               | sdds2stream -pipe=in -para=c0} result
    set IEXCoup(coup0) [format %5.3f $result]
    GetIEXCorrFilePageNumber
    exec sddsprocess $IEX(rootDir)/IEX.sdds $IEXCoup(output).org \
      -redef=para,Page,i_page -filt=para,Page,$IEX(CorrPage),$IEX(CorrPage) \
      -redef=col,Index,i_row "-redef=col,Energy,Energy abs Iv_main sign *"
    cd $IEX(logDir)
}

proc IEXCoupSelect {} {
    global IEX IEXCoup

    set IEXCoup(output) $IEX(Mode)-$IEX(Beamline)-$IEX(Quasi)-$IEX(Direction)
    cd $IEX(logDir)/$IEXCoup(output)
    set fileList [glob $IEXCoup(output)?????.2d]
    catch {set IEXCoup(input) [APSChooseItemFromList -name "Select data file for processing" \
                                   -itemList $fileList -returnList $fileList \
                                   -returnIndices 0 -multiItem 0 \
                                   -contextHelp "Select a file for processing."]} result 
    set IEXCoup(SQUS0) [format %.3f [exec sdds2stream $IEXCoup(input) -para=USSQ0]]
    set IEXCoup(SQDS0) [format %.3f [exec sdds2stream $IEXCoup(input) -para=DSSQ0]]

    set e0 [exec sdds2stream $IEXCoup(input) -para=Energy]
    set index [exec sddsprocess $IEXCoup(output).org -pipe=out \
                 -filt=col,Energy,[expr $e0-0.001],[expr $e0+0.001] \
                 | sdds2stream -pipe=in -col=Index]
    set IEXCoup(dataIndex) [format %2.0f $index]
    SetStatus "Input file: $IEXCoup(input)"    
    return
}

proc PlotCouplingData {args} {
    global IEX IEXCoup
    APSParseArguments {plotMode}

    set IEXCoup(output) $IEX(Mode)-$IEX(Beamline)-$IEX(Quasi)-$IEX(Direction)
    cd $IEX(logDir)/$IEXCoup(output)
    set couplingLow  [expr $IEXCoup(coup0)*(1. - $IEXCoup(coupRange))]
    set couplingHigh [expr $IEXCoup(coup0)*(1. + $IEXCoup(coupRange))]
    if [string match $plotMode all] {
        set fileList [glob $IEXCoup(output)?????.2d]
        eval exec sddscombine $fileList $IEXCoup(output).2d.all -over
        exec sddscontour $IEXCoup(output).2d.all \
            -quant=Coupling -shades=16,$couplingLow,$couplingHigh -topline=@Filename &
    } else {
        set index $IEXCoup(dataIndex)
 #       set IEXCoup(input) C-US-Off-Up+3800.2d
 #       set IEXCoup(output) C-US-Off-Up
 #       set index 0
        if [string match $plotMode single] {
            catch {exec sddsplot "-col=SQ_US,SQ_DS" "-topline=$IEXCoup(input)" $IEXCoup(input) \
                     -grap=sym,vary=sub,type=2,fill -order=spec -split=col=Coupling \
                     -col=SkewQ1,SkewQ2 $IEXCoup(output).org -grap=sym,type=2,thick=2,scale=2,con -filt=col,Index,$index,$index \
                     -col=SkewQ1,SkewQ2 $IEXCoup(output).org -grap=sym,type=1,thick=2,scale=2,con -filt=col,Index,0,$index \
                     -col=SkewQ1,SkewQ2 $IEXCoup(output).org -grap=sym,type=3,thick=2,scale=2,con -filt=col,Index,$index,50 \
                     &}
        }
        if [string match $plotMode filtered] {
            catch {exec sddsplot "-col=SQ_US,SQ_DS"  "-topline=$IEXCoup(input)" $IEXCoup(input) \
                     -grap=sym,vary=sub,type=2,fill -order=spec -split=col=Coupling \
                     -filt=col,Coupling,$couplingLow,$couplingHigh \
                     -col=SkewQ1,SkewQ2 $IEXCoup(output).org -grap=sym,type=2,thick=2,scale=2,con -filt=col,Index,$index,$index \
                     -col=SkewQ1,SkewQ2 $IEXCoup(output).org -grap=sym,type=1,thick=2,scale=2,con -filt=col,Index,0,$index \
                     -col=SkewQ1,SkewQ2 $IEXCoup(output).org -grap=sym,type=3,thick=2,scale=2,con -filt=col,Index,$index,50 \
                     &}
        }
    }
    cd $IEX(logDir)
}

proc ProcessCouplingData {args} {
    global IEX IEXCoup
    APSParseArguments {mode}

    set IEXCoup(output) $IEX(Mode)-$IEX(Beamline)-$IEX(Quasi)-$IEX(Direction)
    cd $IEX(logDir)/$IEXCoup(output)
    if [string match $mode all] {
        set fileList [glob $IEXCoup(output)?????.2d.out]
        eval exec sddscombine $fileList -collapse -pipe=out \
          | sddsprocess -pipe -nowarning \
          -print=para,Mode,$IEX(Mode) -print=para,Beamline,$IEX(Beamline) \
          -print=para,Quasi,$IEX(Quasi) -print=para,Direction,$IEX(Direction) \
          -redef=para,Page,$IEX(CorrPage) "-retain=col,In*,En*,*SQ0,*SQ1" \
          | sddssort -pipe=in temp.out -col=Index 
        exec sddsxref $IEXCoup(output).org temp.out -pipe=out -take=*SQ* -equa=Index \
          | sddsprocess -pipe "-redef=col,dsq1,SkewQ1 USSQ0 -" "-redef=col,dsq2,SkewQ2 DSSQ0 -" \
          | sddsprocess -pipe -redef=col,SkewQ1,USSQ1 -redef=col,SkewQ2,DSSQ1 \
          | tee $IEXCoup(output)-all.out1 \
          | sddsprocess -pipe=in $IEXCoup(output).result -del=col,dsq*,*SQ1,*SQ0
    } else {
        exec sddsprocess -nowarning $IEXCoup(input) $IEXCoup(input).out \
          -redef=para,USSQ1,$IEXCoup(SQUS1)  -redef=para,DSSQ1,$IEXCoup(SQDS1) \
          -redef=para,Index,$IEXCoup(dataIndex)
    }
    SetStatus "ProcessCouplingData Done"
}

proc MakeIEXInputWidget {widget args} {
    global IEX

    set parent ""
    APSParseArguments {parent}
    APSFrame $widget -parent $parent -label "Input parameters" -contextHelp "Set up main measurement parameters and IEX status."
    set w $parent$widget.frame

    APSLabeledEntry .logDir -parent $w -label "Log directory:" -textVariable IEX(logDir) \
        -contextHelp "Enter the path for the output file directory." -width 60
    APSButton .daily -parent $w.logDir -packOption "-anchor e" \
        -text "daily" -size small \
        -command {set IEX(logDir) [APSGoToDailyDirectory -subdirectory IEX]}
    APSLabeledEntry .comment -parent $w -width 60 -textVariable IEX(comment) \
        -label "Comment:" \
        -contextHelp "Put comment for output file. Sometime override by inside program."

    APSFrameGrid .iexSetup -parent $w -xList {x1 x2}
    set w0 $w.iexSetup.x1
    APSRadioButtonFrame .mode -parent $w0 -orientation horizontal \
        -label "IEX Mode:    " -variable IEX(Mode) -limitPerRow 4 \
        -buttonList {C V H} -valueList {C V H} \
        -contextHelp "Choose which IEX mode to work on."
    APSRadioButtonFrame .beamline -parent $w0 -orientation horizontal \
        -label "IEX Beamline:" -variable IEX(Beamline) -limitPerRow 2 \
        -buttonList {US DS} -valueList {US DS} \
        -contextHelp "Choose which IEX beamline to work on."
    set w0 $w.iexSetup.x2
    APSRadioButtonFrame .quasi -parent $w0 -orientation horizontal \
        -label "IEX Quasi On/Off:  " -variable IEX(Quasi) -limitPerRow 2 \
        -buttonList {Off On} -valueList {Off On} \
        -contextHelp "Choose IEX Quasi on or off."
    APSRadioButtonFrame .direction -parent $w0 -orientation horizontal \
        -label "IEX Ramp Direction:" -variable IEX(Direction) -limitPerRow 2 \
        -buttonList {Up Down} -valueList {Up Down} \
        -contextHelp "Choose which IEX ramp direction to work on."

    APSLabeledEntry .corrfile -parent $w -width 60 \
        -textVariable IEX(CorrFile) \
        -label "IEX Correction Filename:" \
        -contextHelp "Enter the desired IEX correction filename (located at /home/helios/oagData/sr/IDs/IEX). The new file will be linked to IEX.sdds"
    APSButton .setCorr -parent $w.corrfile -packOption "-anchor e" \
        -text "Set" -size small \
        -command {SetIEXCorrFile}
    APSLabeledEntry .current -parent $w -width 60 \
        -textVariable IEX(setpoints) \
        -label "Photon Energy Settings" \
        -contextHelp "IEX photon energy list for doing measurement."
    APSButton .select -parent $w.current -packOption "-anchor e" \
        -text "Select" -size small \
        -command {SelectIEXSettings}
    APSLabeledEntry .page -parent $w -width 60 \
        -textVariable IEX(CorrPage) \
        -label "Correction page used" \
        -contextHelp "IEX photon energy list for doing measurement."

    APSLabeledEntry .ncondi -parent $w  -width 10 \
        -textVariable IEX(condiNum) \
        -label "Number of condition cycle:" \
        -contextHelp "Number of condition cycles for IEX setup."
    APSCheckButtonFrame .dryrun -parent $w -packOption "-side left" -contextHelp "Perform Dryrun?" \
        -label " " -buttonList {"Dryrun"} -variableList {IEX(dryrun)}
    APSCheckButtonFrame .testrun -parent $w -packOption "-side left" -contextHelp "Perform testun? run on debugging ioc." \
        -label " " -buttonList {"Testrun"} -variableList {IEX(testrun)}
    APSCheckButtonFrame .condi -parent $w -packOption "-side left" -contextHelp "Perform Condition for selected mode?" \
        -label " " -buttonList {"Condition"} -variableList {IEX(condi)}
    APSCheckButtonFrame .ramp -parent $w -packOption "-side left" -contextHelp "Perform ramp? or IEX is already ramped to desire value?" \
        -label " " -buttonList {"Ramp?"} -variableList {IEX(ramp)}
    APSCheckButtonFrame .dpon -parent $w -packOption "-side left" -contextHelp "Turn on DP and RTFB for IEX setup?" \
        -label " " -buttonList {"DP-On"} -variableList {IEX(DPOn)} \
        -commandList {"DPRTFBOnOff -mode $IEX(DPOn)"}

    APSFrame .action -parent $parent
    set w $parent.action.frame
    $w configure -relief flat -bd 0

    APSButton .setup -parent $w  -text Setup \
        -command { SetStatus "Start Initial IEXSetup" 
            IEXSetup -condition $IEX(condiNum) } \
        -contextHelp "Setup IEX to the desired work condition. If you want to condition IEX with beam you have to select condition every time"
    APSButton .goto -parent $w  -text "Goto Energy ..." \
        -command { set energy [lindex $IEX(setpoints) 0]
            IEXRamp -energy $energy -touch 1 } \
        -contextHelp "Ramp IEX directly to the first abs energy setpoint without question mode or ramping direction."
    APSButton .mkmon -parent $w -text "Make Monitor File" -command IEXMakeMonitor \
        -contextHelp "Make sddsmonitor file for future measurement use"
    APSButton .getCorr -parent $w -text "GetCorrVal" \
        -command {set energy [lindex [exec caget ID29:EnergySet.VAL] 1]
            set mode [lindex [exec caget -n ID29:ActualMode] 1]
            if {$mode==1 || $mode==4} {set energy [expr $energy*-1]}
            GetIEXCorrValues -energy $energy
            SetStatus "Old corr: $IEX(CorrValueOld)"} \
        -contextHelp "Get correctors used for current IEX setpoint."
    APSButton .putCorr -parent $w -text "PutCorrVal" \
        -command {set energy [lindex [exec caget ID29:EnergySet.VAL] 1]
            set mode [lindex [exec caget -n ID29:ActualMode] 1]
            if {$mode==1 || $mode==4} {set energy [expr $energy*-1]}
            GetIEXCorrValues -energy $energy
            set IEX(CorrValueNew) $IEX(CorrValueOld)
            PutIEXCorrValues -energy $energy
            SetStatus "New corr: $IEX(CorrValueNew)"} \
        -contextHelp "put correctors used for current IEX setpoint."
    APSButton .putCorr0 -parent $w -text "PutCorrVal-0" \
        -command {set IEX(CorrValueNew) "0 0 0 0 0 0 0 0 0 0 0 0"
            PutIEXCorrValues -energy 3.8 
            SetStatus "New corr: $IEX(CorrValueNew)"} \
        -contextHelp "set all correctors to zero."
    APSButton .abort -parent $w -text "Abort Run" -command {set abortRun 1} \
        -contextHelp "Send global abort signal to all the measurement."
}

proc SetIEXCorrFile {} {
    global IEX
    
    cd $IEX(rootDir)
    set file $IEX(CorrFile)
    if {[string length $IEX(CorrFile)]==0} {
        SetStatus "Please provide the file name"
    }
    if [string match $IEX(CorrFile) IEX.sdds] {
        SetStatus "Couldn't use IEX.sdds as input." 
    }
    if ![file exist $IEX(CorrFile)] {
        return -code error "SetIEXCorrFile: file $IEX(CorrFile) is not exist."
    }
    if {!$IEX(testrun)} {
        file delete -force IEX.sdds
        exec ln -s $IEX(CorrFile) IEX.sdds
    }
    if [catch {exec caput -w 3 ID29:ReadSDDSfile.PROC 1} result] {
        return -code error "SetIEXCorrFile: $result"
    }
    cd $IEX(logDir)
}

proc SelectIEXSettings {} {
    global IEX

    set sortDir inc
    if [string match $IEX(Direction) Down] {
        set sortDir dec
    }

    GetIEXCorrFilePageNumber
    set value [exec sddsprocess $IEX(rootDir)/IEX.sdds -pipe=out -redef=para,Page,i_page \
                   -filt=para,Page,$IEX(CorrPage),$IEX(CorrPage) \
                   "-redef=col,Energy,Energy abs Iv_main sign *" \
                   "-redef=col,Index,i_row" -print=col,value,%4.3f,Energy \
                   | sddssort -pipe -col=Index,$sortDir \
                   | sdds2stream -pipe=in -col=value]
    set setpointList [join $value " "]
    catch {set IEX(setpoints) [APSChooseItemFromList -itemList $setpointList -returnList $setpointList \
                                   -name "Select photon energy value for measurement" -returnIndices 0 -multiItem 1]} result
}

proc IEXSetup {args} {
    global IEX
    APSParseArguments {condition}
    if {!$IEX(dryrun)} {
        #0 Turn on DP and RTFB
        DPRTFBOnOff -mode $IEX(DPOn)

        #1 Turn off IEX, setup quasi, then turn it on
        if {[exec cavget -list=ID29:QuasiRatio.RVAL]==100} {
            set currState Off
            set value 85
        } else {
            set currState On
            set value 100
        }
        if {![string match $currState $IEX(Quasi)] } {
            exec caput -w 3 ID29:Main_on_off.VAL 1
            after 2000
            exec cawait -interval=1 -waitFor=ID29:feedback.VAL,sameAs=Ready
            exec cawait -interval=1 -waitFor=ID29:BusyRecord.VAL,sameAs=Done
            exec caput -w 3 ID29:QuasiRatioIn.C $value
            exec caput -w 3 ID29:Main_on_off.VAL 0
            after 2000
            exec cawait -interval=1 -waitFor=ID29:feedback.VAL,sameAs=Ready
            exec cawait -interval=1 -waitFor=ID29:BusyRecord.VAL,sameAs=Done
        }
        SetStatus "Set IEX to desired Quasi"

        #2 Set IEX to desired mode
        if [string match $IEX(Mode) C] {
            set IEX(Emax) 0.4
            set IEX(mode0) 0
            set IEX(mode1) 1
        } 
        if [string match $IEX(Mode) V] {
            set IEX(Emax) 0.44
            set IEX(mode0) 2
            set IEX(mode1) 2
        }
        if [string match $IEX(Mode) H] {
            set IEX(Emax) 0.25
            set IEX(mode0) 3
            set IEX(mode1) 4
        }
        SetStatus "Set IEX to desired mode"
        exec caput -w 3 ID29:DesiredMode.VAL $IEX(mode0)
        after 2000
        exec cawait -interval=1 -waitFor=ID29:feedback.VAL,sameAs=Ready
        exec cawait -interval=1 -waitFor=ID29:BusyRecord.VAL,sameAs=Done

        #3 Set IEX to the desired beamline and quasi status
        switch $IEX(Beamline) {
            US {
                exec caput -w 3 ID29:Beamline 0
            }
            DS {
                exec caput -w 3 ID29:Beamline 1
            }
        }
        SetStatus "Set IEX to desired beamline"
       
        # If condition is checked condition IEX 
        if {$IEX(condi) && $condition} {
            IEXCondition -cycle $condition
        }
        SetStatus "IEXSetup Done."
    }
    return
}

proc IEXCondition {args} {
    global IEX
    APSParseArguments {cycle}

    for {set i 0} {$i < $cycle} {incr i} {
        IEXRamp -energy $IEX(Emax)
        after 1000
        IEXRamp -energy 3.8
        after 1000
    } 
    return
}

proc IEXRamp {args} {
    set touch 0
    set main 0
    APSParseArguments {energy touch}
    global IEX
    #SetStatus "Start Ramp, touch=$touch"
    
    if {$energy<0} {
        exec caput -w 3 ID29:DesiredMode.VAL $IEX(mode1)
    } else {
        exec caput -w 3 ID29:DesiredMode.VAL $IEX(mode0)
    }
    if {$energy>3.7} {
        if [string match $IEX(Direction) Down] {
            exec caput -w 3 ID29:DesiredMode.VAL $IEX(mode1)
        } else {
            exec caput -w 3 ID29:DesiredMode.VAL $IEX(mode0)
        }
    }
    after 1000
    exec cawait -interval=1 -waitFor=ID29:feedback.VAL,sameAs=Ready
    exec cawait -interval=1 -waitFor=ID29:BusyRecord.VAL,sameAs=Done
    set value [expr max(abs($energy),$IEX(Emax))]
    exec caput -w 3 ID29:EnergySet.VAL $value
    after 1000
    SetStatus "Start Ramp, touch=$touch, energy=$energy, value=$value"
    exec caput -w 3 ID29:StartRamp.VAL 0
    exec caput -w 3 ID29:StartRamp.VAL 1
    after 1000
    exec cawait -interval=1 -waitFor=ID29:feedback.VAL,sameAs=Ready
    exec cawait -interval=1 -waitFor=ID29:BusyRecord.VAL,sameAs=Done
    if {$touch} {
        if {[expr abs($energy)]<$IEX(Emax)} {set main 1}
        GetIEXCorrValues -energy $energy
        set IEX(CorrValueNew) $IEX(CorrValueOld)
        PutIEXCorrValues -energy $energy -main $main
    }
    SetStatus "Ramp done"
}

proc GetIEXCorrFilePageNumber {} {
    global IEX

    if [string match $IEX(Mode) C] {set iPage 0}
    if [string match $IEX(Mode) V] {set iPage 8}
    if [string match $IEX(Mode) H] {set iPage 16}
    if [string match $IEX(Beamline) US] {set iPage [expr $iPage+0]}
    if [string match $IEX(Beamline) DS] {set iPage [expr $iPage+4]}
    if [string match $IEX(Quasi) Off] {set iPage [expr $iPage+0]}
    if [string match $IEX(Quasi) On] {set iPage [expr $iPage+2]}
    if [string match $IEX(Direction) Up] {set iPage [expr $iPage+0]}
    if [string match $IEX(Direction) Down] {set iPage [expr $iPage+1]}
    set IEX(CorrPage) [expr $iPage+1]
}

proc GetIEXCorrValues {args} {
    global IEX
    APSParseArguments {energy}

    set tmpfile /tmp/[APSTmpString]
    GetIEXCorrFilePageNumber
    set value [exec sddsprocess $IEX(rootDir)/IEX.sdds -pipe=out -redef=para,Page,i_page \
                   -filt=para,Page,$IEX(CorrPage),$IEX(CorrPage) \
                   -print=col,value,%4.3f,Energy \
                   | sdds2stream -pipe=in -col=value]
    set setpointList [join $value " "]

    if {[lsearch $setpointList [format %.3f $energy]]<0} {
        set Keff [expr 3.7225656/abs($energy) - 1.0]
        if {$Keff < 0} {
            set bField 0
        } else {
            set bField [expr sqrt(2.0*$Keff)/0.93372875/12.5*$energy/abs($energy)*1e4]
        }   
        if {$IEX(mode0) < 2} {
            set bField [expr $bField/sqrt(2.0)]
        }
        exec sddsprocess $IEX(rootDir)/IEX.sdds -pipe=out -redef=para,Page,i_page \
            -filt=para,Page,$IEX(CorrPage),$IEX(CorrPage) \
            | sddsinterp -pipe -columns=Beff,* -atValues=$bField \
            | sddsprocess -pipe=in $tmpfile.1
        SetStatus "Using other data"
    } else {
        exec sddsprocess $IEX(rootDir)/IEX.sdds -pipe=out -redef=para,Page,i_page \
            -filt=para,Page,$IEX(CorrPage),$IEX(CorrPage) \
            -filt=col,Energy,[expr $energy-0.0001],[expr $energy+0.0001] \
            | sddsprocess -pipe=in $tmpfile.1
    }

    lset IEX(CorrValueOld) 0 [format %.3f [exec sdds2stream -col=Vcorr1 $tmpfile.1]]           
    lset IEX(CorrValueOld) 1 [format %.3f [exec sdds2stream -col=SkewQ1 $tmpfile.1]]           
    lset IEX(CorrValueOld) 2 [format %.3f [exec sdds2stream -col=SkewOct1 $tmpfile.1]]           
    lset IEX(CorrValueOld) 3 [format %.3f [exec sdds2stream -col=Hcorr1 $tmpfile.1]]           
    lset IEX(CorrValueOld) 4 [format %.3f [exec sdds2stream -col=NormalQ1 $tmpfile.1]]           
    lset IEX(CorrValueOld) 5 [format %.3f [exec sdds2stream -col=Sext1 $tmpfile.1]]           
    lset IEX(CorrValueOld) 6 [format %.3f [exec sdds2stream -col=Vcorr2 $tmpfile.1]]           
    lset IEX(CorrValueOld) 7 [format %.3f [exec sdds2stream -col=SkewQ2 $tmpfile.1]]           
    lset IEX(CorrValueOld) 8 [format %.3f [exec sdds2stream -col=SkewOct2 $tmpfile.1]]           
    lset IEX(CorrValueOld) 9 [format %.3f [exec sdds2stream -col=Hcorr2 $tmpfile.1]]           
    lset IEX(CorrValueOld) 10 [format %.3f [exec sdds2stream -col=NormalQ2 $tmpfile.1]]           
    lset IEX(CorrValueOld) 11 [format %.3f [exec sdds2stream -col=Sext2 $tmpfile.1]]           
    lset IEX(MainPVValue) 0 [format %.3f [exec sdds2stream -col=Energy $tmpfile.1]]
    lset IEX(MainPVValue) 1 [format %.3f [exec sdds2stream -col=Ih_main $tmpfile.1]]
    lset IEX(MainPVValue) 2 [format %.3f [exec sdds2stream -col=Iv_main $tmpfile.1]]
    return
}

proc PutIEXCorrValues {args} {
    global IEX
    set main 0
    APSParseArguments {energy main}

    # Get nominal multipole corrector values
    GetIEXCorrValues -energy $energy

    # Put new corrector coil values
    foreach PV $IEX(CorrPV) value $IEX(CorrValueNew) {
        exec cavput -list=$PV=$value
    }
    after 1000
    
    exec caput -w 3 ID29:MP2I.PROC 1
    after 1000
    
    # Check new corrector values
    set corrSet ""
    set corrSet [concat $corrSet [exec cavget -list=ID29:SetUS -list=T,B,Q1,Q2,Q3,Q4 -list=Corr]]
    set corrSet [concat $corrSet [exec cavget -list=ID29:SetDS -list=T,B,Q1,Q2,Q3,Q4 -list=Corr]]
    set corrSet [join $corrSet " "]
    foreach value $corrSet {
        if {[expr abs($value)]>1.5} {
            SetStatus "PutIEXCorrValues: New values $corrSet exceed limitation."
            foreach PV $IEX(CorrPV) value $IEX(CorrValueOld) {
                exec cavput -list=$PV=$value
            }
            exec caput -w 3 ID29:MP2I.PROC 1
            SetStatus "PutIEXCorrValues: Return to old corrector coil values: $IEX(CorrValueOld)"
            return 1
        }
    }
    if $main {
        set Bx [expr abs([lindex $IEX(MainPVValue) 1])]
        set By [expr abs([lindex $IEX(MainPVValue) 2])]
        exec caput -w 3 ID29:BxSet.VAL $Bx
        exec caput -w 3 ID29:BySet.VAL $By
    }
    # If dry run output to status window, otherwise set it to the IEX device
    if $IEX(dryrun) {
        SetStatus "PutIEXCorrValues: Set correctors to $corrSet"
    } else {
        exec caput -w 3 ID29:SetOutputSeq.PROC 1
        after 1000
    }
}

proc IEXMakeMonitor {} {
    global IEX
    cd $IEX(logDir)
    exec sddsmakedataset -pipe=out -col=BPMName,type=string \
        -data=[join [FindGoodBpms -plane H] ,] \
        | sddsprocess -pipe=in xBPM.mon \
        -edit=col,ControlName,BPMName,ei/:msAve:x:ErrorCC/ \
        -edit=col,ReadbackName,BPMName,ei/:msAve:x:ErrorCC/
    exec sddsmakedataset -pipe=out -col=BPMName,type=string \
        -data=[join [FindGoodBpms -plane V] ,] \
        | sddsprocess -pipe=in yBPM.mon \
        -edit=col,ControlName,BPMName,ei/:msAve:y:ErrorCC/ \
        -edit=col,ReadbackName,BPMName,ei/:msAve:y:ErrorCC/
    if [catch {exec sddscombine $IEX(moniDir)/ID-IEX.mon \
                   /home/helios/oagData/monitoring/RTFBCorrectorErrors/RTFBCorrectorErrors.mon \
                   $IEX(moniDir)/IEX-rms.mon $IEX(moniDir)/IEX-emittance.mon \
                   xBPM.mon yBPM.mon $IEX(monifile) -merge -over \
               } result ] {
        return -code error "MakeMonitorFile: $result"
    }
    SetStatus "Done make IEX monitor."
    return
}

proc FindGoodBpms {args} {
    set plane ""
    APSParseArguments {plane}
    set Plane [string toupper $plane]
    set statusFile /home/helios/oagData/sr/BPMStatus/config.sdds
    if [catch {exec sddsprocess $statusFile -pipe=out \
                   -match=col,DeviceName=S*\[ABC\]:P\[012345\] \
                   -filter=col,Nonexistent$Plane,0,0,OkForDCOrbitCorrection$Plane,1,1,& \
                   | sdds2stream -pipe -col=DeviceName \
               } goodBpms] {
        return -code error $goodBpms
    }
    return $goodBpms
}

proc DPRTFBOnOff {args} {
    APSParseArguments {mode}

    global apshpFilterCutoffVert apshpFilterCutoffHoriz apshpFilterInitialVert apshpFilterInitialHoriz
    global apshpFilterRampPoints apshpFilterRampInterval
    APSMpSetRTFBGlobalParameters -readonly 1

    if {$mode==1} {set mode On}
    if {$mode==0} {set mode Off}
    switch $mode {
        On {
            set xstatus [exec caget -t -n DP:S:OrbitControlLawXSDDS.RUN]
            set ystatus [exec caget -t -n DP:S:OrbitControlLawYSDDS.RUN]
            if {!$xstatus || !$ystatus} {
                if [catch {exec cavput -list=DP:S: \
                             -list=OrbitControlLawX,OrbitControlLawY \
                             -list=SDDS.SUSP=0 \
                         } result ] {
                    return -code error "$result"
                }
            }
            if {![exec caget -t -n SRFB:GBL:LoopStatusBI.VAL]} {
                if [catch {APSMpRTFBCloseLoops -hpFilterCutoffVert $apshpFilterCutoffVert \
                             -hpFilterCutoffHoriz $apshpFilterCutoffHoriz \
                             -hpFilterInitialVert $apshpFilterInitialVert \
                             -hpFilterInitialHoriz $apshpFilterInitialHoriz \
                             -hpFilterRampPoints $apshpFilterRampPoints \
                             -hpFilterRampInterval $apshpFilterRampInterval \
                             -statusCallback "set status" } result] {
                    set status "APSMpRTFBCloseLoops: $result"
                    return
                }   
            }
            SetStatus "Done DPRTFBOnOff On, mode =$mode"
        }
        Off {
            if [catch {exec cavput -list=S:RC:OrbitControlLawXC,S:RC:OrbitControlLawYC,S:rfFreqControlLawRC -list.SUSP=1} result ] {
                return -code error "$result"
            }
            if [catch {exec cavput -list=DP:S: \
                         -list=OrbitControlLawX,OrbitControlLawY \
                         -list=SDDS.SUSP=1 \
                     } result ] {
                return -code error "$result"
            }
            if [catch {APSMpRTFBResetOpenLoops -hpFilterInitialVert $apshpFilterInitialVert \
                         -hpFilterInitialHoriz $apshpFilterInitialHoriz \
                         -runHloop 1 -runVloop 1 } result] {
                return -code error "$result"
            }   
            SetStatus "Done DPRTFBOnOff Off, mode =$mode"
        }
    }
    after 1000
    return
}

proc SetStatus {text} {
    global status
    set status $text
    update
}

set abortRun 0
set IEX(rootDir) /home/helios/oagData/sr/IDs/IEX
set IEX(moniDir) /home/helios/oagData/sr/IDs/IEX/Monitor/
set IEX(logDir) [pwd]
set IEX(comment) ""
set IEX(dryrun) 0
set IEX(testrun) 0
set IEX(DPOn) 1
set IEX(condi) 0
set IEX(condiNum) 3
set IEX(ramp) 1
set IEX(monifile) IEX.mon

set IEX(Mode) C
set IEX(Beamline) US
set IEX(Quasi) Off
set IEX(Direction) Up
set IEX(fileroot) $IEX(Mode)-$IEX(Beamline)-$IEX(Quasi)-$IEX(Direction)
set IEX(setpoints) 3.8
set IEX(Emax) 0.4
set IEX(mode0) 0
set IEX(mode1) 1

set IEX(CorrFile) ""
set IEX(CorrPage) 0
set USPV "ID29:usVcorr ID29:usSQ1 ID29:usSO ID29:usHcorr ID29:usNQ1 ID29:usNS"
set DSPV "ID29:dsVcorr ID29:dsSQ1 ID29:dsSO ID29:dsHcorr ID29:dsNQ1 ID29:dsNS"
set IEX(CorrPV) [concat $USPV $DSPV]
set IEX(CorrValueOld) "0 0 0 0 0 0 0 0 0 0 0 0"
set IEX(CorrValueNew) "0 0 0 0 0 0 0 0 0 0 0 0"
set IEX(MainPV) "ID29:EnergySet.VAL ID29:DesiredBxSet.VAL ID29:DesiredBySet.VAL"
set IEX(MainPVValue) "3.8 0 0"

set status "Working."
APSScrolledStatus .status -parent .userFrame -textVariable status -packOption "-expand yes -fill both"
MakeIEXInputWidget .input -parent .userFrame
set TaskWidgetList [APSTabFrame .task -parent .userFrame -label "" \
                        -labelList { \
                                         "IEX Checkup" "IEX Orbit Correction" \
                                         "IEX Tune Correction" "IEX Coupling Correction" \
                                     } \
                        -width 900 -height 400 -tabPosition n]
pack configure .userFrame.task -fill x -expand true
MakeIEXCheckWidget .check -parent [lindex $TaskWidgetList 0]
MakeIEXOrbitWidget -RM .rm -Orbit .orbit -parent [lindex $TaskWidgetList 1]
MakeIEXTuneWidget .tune -parent [lindex $TaskWidgetList 2] 
MakeIEXCoupWidget .coup -parent [lindex $TaskWidgetList 3]
.userFrame.task.frame.tn select 3
set status "Ready."

# Local Variables:
# mode: tcl
# indent-tabs-mode: nil
# End:
