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

# $Log: not supported by cvs2svn $
# Revision 1.22  2010/02/01 19:06:37  soliday
# Updated to use oagwish8.4 until I can find a fix for BLT under Tcl/Tk 8.5.
#
# Revision 1.21  2008/06/24 22:31:57  emery
# Added APSDebugPath. Added status output of collecting
# multiple acquisitions for averaging. Indented some code.
#
# Revision 1.20  2008/02/12 12:07:37  emery
# Now that the RTFB can trigger on the injection trigger (external
# trigger) we don't have to pulse the S:IS1 septum.
#
# Revision 1.19  2008/01/25 05:19:49  emery
# Added ramping to S:IS1 down to 0 V plus a cawait for checking that
# the ramping is complete before pulsing the septum.
# Hopefully we will eventually get a proper trigger for RTFB so
# that we don't have to use the Freeze trigger.
#
# Revision 1.18  2007/01/02 21:08:38  emery
# Correct typos. Include more parameters in sddsxref commands.
# Changed *[HV]* wildcards into explicit *H*,*V* in order
# to avoid \ characters.
#
# Revision 1.17  2003/09/03 17:16:26  soliday
# Added package require BLT command.
#
# Revision 1.16  2003/07/24 22:13:53  emery
# Added averaging of the DSP scope waveform. Not tested.
#
# Revision 1.15  2002/11/04 18:27:12  emery
# Protected existing correction files. Added more context help
# to action buttons. Added "get CPU files" function at end of
# "create new files" button action.
#
# Revision 1.14  2002/11/04 14:46:09  emery
# Prevented corrector files from being overwritten.
#
# Revision 1.13  2002/10/23 14:09:30  soliday
# Replaced directory /home/helios/oagData/sr/orbitFeedback
#
# Revision 1.12  2002/09/17 20:11:02  soliday
# The top tab frame now fills the width of the application.
#
# Revision 1.11  2002/07/24 03:08:24  emery
# Fixed the problem of having the first page deleted
# when a page is supopsed to be inserted between the 1st and 2nd page.
#
# Revision 1.10  2002/07/24 02:28:28  emery
# When a new main current value is read, the corrector waveform to
# show will be that of the page for the MainCurrent above in value.
#
# Revision 1.9  2002/06/24 21:27:41  emery
# Changed default spacing to 2 ms.
#
# Revision 1.8  2002/06/10 22:22:24  soliday
# I commited a debugging version acidentally
#
# Revision 1.7  2002/06/10 22:17:59  soliday
# Fixed a small problem.
#
# Revision 1.6  2002/06/10 22:01:47  soliday
# Added Bob's and Louis' changes.
#
# Revision 1.5  2002/05/25 01:33:30  soliday
# Added the showPrevious option to the waveforms.
#
# Revision 1.4  2002/05/24 22:03:06  soliday
# Added the ability to use modifier waveforms
#
# Revision 1.3  2002/05/24 13:45:05  emery
# Added splitting of AC and DC part of waveform. AC and DC are joined
# for creating an output file.
# Moved some buttons into the tab widgets.
# Display AC part of corrector waveform.
# DC part is displayed in an entry box.
#
# Revision 1.2  2002/04/17 22:51:11  emery
# Changed height to reveal cursor widgets.
# Replaced multiple blocks of calls and sets with multiply-nested
# calls and sets. Changed the global names.
#
# Revision 1.1  2002/04/17 19:32:26  emery
# First installation
#

set auto_path [linsert $auto_path 0 /usr/local/oag/apps/lib/$env(HOST_ARCH)]
set auto_path [linsert $auto_path 0 /usr/local/oag/lib_patch/$env(HOST_ARCH)]
APSDebugPath
set CVSRevisionAuthor "\$Revision: 1.23 $ \$Author: lemery $"

APSApplication . -name "CPU Waveform Manual Adjuster" \
  -version $CVSRevisionAuthor \
  -overview "Operator Interface to the CPU Waveform adjuster."

package require BLT

bind all <Double-Button-3> { 
    APSInfoWindow .contextHelp -name "Context Help" \
      -infoMessage [APSNearestContextHelp %W] \
      -width 15
}

set ControlStatus "Ready"
set CPUMainRelAmplitude 0.1
proc MakeCPUStatus {} {
    global ControlStatus
    
    APSScrolledStatus .status -parent .userFrame -textVariable ControlStatus -width 75 -packOption {-side top}
}

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

proc MakeCPUSetupWidget {widget args} {
    global average
    set parent ""
    APSParseArguments {parent}
    
    APSFrame $widget -parent $parent -label "CPU Control and Measurement Functions" \
      -height 30 \
      -packOption {-side top -fill x} 
    set w $parent$widget.frame
    MakeCPUToggleWidget .cpuAmpFrame -parent $w
    MakeDSPFileSetupWidget .file -parent $w
    APSButton .dspScopeSetup -parent $w \
      -text "DSP Setup" \
      -command {DSPScopeSetup -statusCallback SetStatus} \
      -packOption {-side left} \
      -contextHelp "This button sets up the DSP scope for data collection."
    APSButton .collectData -parent $w \
      -text "Collect Data" \
      -command {incr index; \
                  CollectData -output $outputDir/$root-[format %03ld $index] -comment $comment -statusCallback SetStatus -average $average; \
                  UpdateGraphs -input $outputDir/$root-[format %03ld $index].ave \
                  -startTime $startTime \
                  -period $period \
                  -hperiod1 $hperiod1 } \
      -packOption {-side left} \
      -contextHelp "This button collect DSP scope data and updated readback graphs."
    APSButton .abortbutton -parent $w -text "Abort" \
      -packOption "-side left" \
      -command {set abort 1} \
      -contextHelp "Abort data collection."

    APSButton .process -parent $w \
      -text "Update Readback Graphs" \
      -command {UpdateGraphs -input $outputDir/$root-[format %03ld $index].ave \
                  -startTime $startTime \
                  -period $period \
                  -hperiod1 $hperiod1 } \
      -packOption {-side left} \
      -contextHelp "This button plots the zoomed-in DSP scope data."

    APSButton .autoscan -parent $w \
      -text "AutoScan" \
      -command {AutoScan} \
      -packOption {-side left} \
      -contextHelp "Scans a particular point in corrector waveforms and produced a summary of results."

}

proc DSPScopeSetup {args} {

    global ControlStatus configFile
    
    APSParseArguments {statusCallback}

    APSRestoreSRFBDspScope -filename $configFile -timing 1 \
      -channels 1 -clearMode 1 
    
    if {$statusCallback!=""} {
        $statusCallback "DSP scope setup complete."
    }
}

proc MakeCPUToggleWidget {widget args} {
    
    set parent ""
    APSParseArguments {parent}
    
    APSFrame $widget -parent $parent \
      -height 30 \
      -packOption {-side top -fill x} 
    
    set w $parent$widget.frame
    APSLabeledEntry .cpuMainRelAmp -parent $w -width 7 \
      -textVariable CPUMainRelAmplitude \
      -packOption {-side left} \
      -label "CPU Main Toggle Amplitude." \
      -contextHelp "Amplitude for toggling the CPU in order to re-read the waveforms."
    
    APSButton .toggleCPUAmplitude -parent $w \
      -text "Toggle" \
      -command {toggleCPURelAmplitude $CPUMainRelAmplitude -statusCallback SetStatus} \
      -packOption {-side left} \
      -contextHelp "This button toggles the CPU amplitude in order to re-read the waveforms."
}

proc MakeDSPFileSetupWidget {widget args} {
    global outputDir root index comment abort

    set parent ""
    APSParseArguments {parent}
    
    APSFrame $widget -parent $parent \
      -height 30 \
      -packOption {-side top -fill x} 
    
    set w $parent$widget.frame

    APSLabeledEntry .outputDir -parent $w \
      -label "Output directory:" -textVariable outputDir \
      -contextHelp "Enter a name for the output file directory." -width 55

    APSButton .daily -parent $w.outputDir -packOption "-anchor e" \
      -text "daily" -size small \
      -command {set outputDir [APSGoToDailyDirectory -subdirectory CPU]}

    APSLabeledEntry .root -parent $w -label "Root name of file: " \
      -textVariable root \
      -contextHelp "Enter the root name of the file for the corrector error output <root>-nnn."

    APSLabeledEntry .index -parent $w -label "Index (incremented just before execution): " -textVariable index \
      -contextHelp "Enter the index (minus 1) to be part of the name of the output file. The index is incremented by 1 before execution."

    APSLabeledEntry .comment -parent $w -label "Comment: " -textVariable comment \
      -width 50 \
      -contextHelp "Enter a comment."

    APSLabeledEntry .timeLimit -parent $w -label "Time limits for readback graphs (ms): " -textVariable timeLimit \
      -width 50 \
      -contextHelp "Enter a time limit. Usually the default of 127 ms is sufficieent. The waveform generators for the correctors don't go past 127 ms."

}

proc MakeCPUMeasWidget  {widget args} {

    set parent ""
    APSParseArguments {parent}
    
    APSFrame $widget -parent $parent \
      -height 30 \
      -packOption {-side top -fill x} 
    
    set w $parent$widget.frame

    APSLabeledEntry .mainCurrent -parent $w -label "Main Current (A):" \
      -textVariable mainCurrent \
      -packOption {-side left} \
      -width 50 \
      -contextHelp "Main current data extracted from measurement file after pressin update button."
    APSButton .calculate -parent $w \
      -text "Create New Files" \
      -command {incr outputVersion; Apply -root $outputDir/$root-[format %03ld $index] \
                  -outputRoot $outputRoot -outputVersion $outputVersion \
                  -mode calculate \
                  -statusCallback SetStatus} \
      -packOption {-side left} \
      -contextHelp "This button calculates new files based on the (modified) waveforms and the other data in the original files (CPU filenames in tab widget \"Original Waveforms\"). There must be a valid measurement file listed in the tab widget \"Setup and Measurement\". The CPU filenames are also updated in order to ensure that the correction for the previous MainCurrent measurement is saved and will be transfered to the data files of later corrections."
    
    APSButton .upload -parent $w \
      -text "Upload New Files" \
      -command {Apply -root $outputDir/$root-[format %03ld $index] \
                  -outputRoot $outputRoot -outputVersion $outputVersion \
                  -mode load \
                  -statusCallback SetStatus} \
      -packOption {-side left} \
      -contextHelp "This button uploads the new corrector files. There is no data file operation in this step."
    return

}

proc MakeCPUPlotActionWidget  {widget args} {

    set parent ""
    APSParseArguments {parent}
    
    APSFrame $widget -parent $parent \
      -height 30 \
      -packOption {-side top -fill x} 
    
    set w $parent$widget.frame

    APSButton .plotData -parent $w \
      -text "Plot Data" \
      -command {PlotData -file $outputDir/$root-[format %03ld $index]} \
      -packOption {-side left} \
      -contextHelp "This button plots the DSP scope data."

    APSButton .plotAvgData -parent $w \
      -text "Plot Averaged Data" \
      -command {PlotData -file $outputDir/$root-[format %03ld $index].ave} \
      -packOption {-side left} \
      -contextHelp "This button plots the averaged DSP scope data along with the original data."

    APSButton .plotDataZ -parent $w \
      -text "Plot Zoomed Data" \
      -command {PlotData -zoomed 1 \
                  -file $outputDir/$root-[format %03ld $index]} \
      -packOption {-side left} \
      -contextHelp "This button plots the zoomed-in DSP scope data."

}

proc toggleCPURelAmplitude {relAmpfactor args} {
    
    global ControlStatus

    APSParseArguments {statusCallback}

    if {$relAmpfactor > 1 || $relAmpfactor < 0} {
        if {$statusCallback!=""} {
            $statusCallback "CPU relative amplitude factor must be in the range 0 <= amplitude <= 1."
        }
        APSAlertBox .alert -errorMessage "CPU relative amplitude factor must be in the range 0 <= amplitude <= 1."
        return 0
    } else {
        catch {exec cavput -list=ID04b:set_V_coil=0.1 -delta=factor=1}
        catch {exec cavput -list=ID04b:set_V_coil=0.1 -delta=factor=-1}
        if {$statusCallback!=""} {
            $statusCallback "CPU main coil toggled with relative factor of $relAmpfactor."
        }
    }
}

# collects data and averages it over 0.5 seconds blocks
proc CollectData {args} {
    global configFile scalarsFile abort

    APSParseArguments {statusCallback output comment average}

    if ![string length $output] {
        return -code error "CollectData: No output file specified."
    }
    if [file exists $output] {
        return -code error "CollectData: File $output already exists!"
    }
    if {$statusCallback!=""} {
            $statusCallback "Collecting $average acquisition(s) from DSPscope...[exec date +%H:%M:%S]"
        }
    for {set i 0} {$i < $average} {incr i} {
        if {[exec cavget -pend=15 -list=SRFB:DS:SampleBO] == "Off"} {
            armDSPscope -statusCallback $statusCallback
        }
        set abort 0

        # Triggers from external trigger, which is the 2 Hz injection trigger.
        waitForTrigger -statusCallback $statusCallback
        if {$statusCallback!=""} {
            $statusCallback "Collecting data from DSPscope...[exec date +%H:%M:%S]"
        }
        update
        if [catch {exec sddswmonitor $configFile $output-[format %02ld $i] \
                     -comment=Comment,[APSMakeSafeQualifierString $comment] \
                     -pend=15 -scalars=$scalarsFile \
                 } result] {
            if {$statusCallback!=""} {
                $statusCallback "CollectData: Error with sddswmonitor: $result"
                $statusCallback "Continuing anyway..."
            }
        }
        lappend fileList $output-[format %02ld $i]
    }
    
    if [catch {exec cavget -list=ID04b:set_V_coil.VAL  \
             } mainCurrent ] {
        return -code error "CollectData: Problem with getting main current (V current) for CPU"
    }
    if [catch {eval exec sddscombine $fileList -pipe=out \
                 | sddsenvelope -pipe \
                 -mean=S*H*,S*V* -copy=Index \
                 | sddsxref -pipe [lindex $fileList 0] \
                 -leave=* -transfer=para,* \
                 | sddsconvert -pipe=in $output \
                 -edit=col,*Mean*,%/Mean// \
             } result ] {
        return -code error "CollectData: $result"
    }
    if [catch {exec sddsprocess $output -nowarn \
                 "-def=para,TickTime,SRFB:fsAO rec,units=sec" \
                 "-redef=col,t,Index TickTime *,units=sec" \
                 -define=para,MainCurrent,$mainCurrent \
             } result ] {
        return -code error "CollectData: $result"
    }
    
    if [catch {AverageCorrectorErrorData -inputFile $output} result] {
        return -code error "CollectData: Error with AverageCorrectorErrorData: $result"
    }

    file delete -force ${output}~
    if {$statusCallback!=""} {
        $statusCallback "Collecting done."
    }
}

proc armDSPscope {args} {
    global abort
    
    APSParseArguments {statusCallback}
    set dum 1
    while {[exec cavget -pend=15 -list=SRFB:GBL:FeedbackBlockedBI] == "Blocked"} {
        if {$dum == 1} {
            set status "Waiting for feedback block to clear... [exec date +%H:%M:%S]"
            set dum 0
        }
        APSWaitWithUpdate -waitSeconds 1 -updateInterval 0.5
    }
    
    set dum 1
    while {(([exec cavget -pend=15 -list=SRFB:GBL:HorzTripBI] == "Trip") || \
              ([exec cavget -pend=15 -list=SRFB:GBL:VertTripBI] == "Trip")) && \
             ($abort == 0) } {
        if {$dum == 1} {
            if {$statusCallback!=""} {
                $statusCallback "Waiting for trips to be reset... [exec date +%H:%M:%S]"
            }
            set dum 0
        }
        APSWaitWithUpdate -waitSeconds 1 -updateInterval 0.5
    }

    if {$abort == 0} {
        if {$statusCallback!=""} {
            $statusCallback "Arming DSPscope... [exec date +%H:%M:%S]"; update
        }
        exec cavput -pend=15 -list=SRFB:DS:SampleBO=1
        while {[exec cavget -pend=15 -list=SRFB:DS:SampleBO] == "Off"} {
            APSWaitWithUpdate -waitSeconds 1 -updateInterval 0.5
        }
    }
}

proc waitForTrigger {args} {
    global abort

    APSParseArguments {statusCallback}

    if {$abort == 0} {
        set triggered 0
        if {$statusCallback!=""} {
            $statusCallback "Waiting for dspscope to trigger... [exec date +%H:%M:%S]"; update
        }
        while {($triggered == 0) && ($abort == 0)} {
            if {[exec cavget -pend=15 -list=SRFB:DS:SampleBO] == "Off" } {
                set triggered 1
            }
            APSWaitWithUpdate -waitSeconds 1 -updateInterval 1
        }
        
        if {$triggered == 1} {
            if {$statusCallback!=""} {
            $statusCallback "Triggered [exec date +%H:%M:%S]"
            }
            update
        } else {
            if {$statusCallback!=""} {
                $statusCallback "Aborted... [exec date +%H:%M:%S]"
            }
            update
            set triggered 0
        }
    }
}


proc AverageCorrectorErrorData {args} {
    set inputFile ""
    APSStrictParseArguments {inputFile}

    # the input file is assumsed to have column t defined.
    if [catch {exec sddsprocess $inputFile  -pipe=out \
                 "-def=col,Period,t 0.5 / int,type=long" \
                 | sddsbreak -pipe \
                 -increaseof=Period \
                 | sddsconvert -pipe \
                 -keeppages=5 \
                 | sddsenvelope -pipe \
                 -mean=S*H*,S*V* -copy=Index,t \
                 | sddsprocess -pipe \
                 -process=t,first,tFirst \
                 -process=Index,first,IndexFirst \
                 "-redef=col,Index,Index IndexFirst -" \
                 "-redef=col,t,t tFirst -" \
                 | sddsxref -pipe $inputFile \
                 -transfer=para,* \
                 -leave=* \
                 | sddsconvert -pipe=in $inputFile.ave \
                 -edit=col,*Mean*,%/Mean// \
             } result ] {
        return -code error "AverageCorrectorErrorData: $result"
    }
    return
}

proc PlotData {args} {
    set file ""
    set zoomed 0
    APSParseArguments {file zoomed}
    if {![file exists $file]} {
        return -code error "PlotData: File $file doesn't exist"
    }
    if $zoomed {
        set options "-layout=1,2 -filter=col,t,0,0.5"
    } else {
        set options "-layout=1,2"
    }
    if [catch {eval exec sddsplot $options \
                 -col=t,(S*H*,S*V*) -grap=line,vary -sep $file \
                 -file -topline=@Comment &} \
          result] {
        return -code error $result
    }
    return
}

proc MakeCPUProcessWidget {widget args} {
    global outputDir root index comment abort firstWaveform startTime period tau

    set parent ""
    APSParseArguments {parent}
    
    APSFrame $widget -parent $parent \
      -height 30 \
      -packOption {-side top -fill x} 
    
    set w $parent$widget.frame

    APSRadioButtonFrame .firstWaveform -parent $w -orientation horizontal \
      -label "First waveform: " -variable firstWaveform \
      -buttonList {Pos Neg} \
      -valueList {Pos Neg} -contextHelp \
      "Choose the waveform type which occurs first in the RTFB dsp scope signal."

    APSLabeledEntry .start -parent $w -label "Start time (ms): " -textVariable startTime \
      -width 40 \
      -contextHelp "Start time of first waveform as it appears on the RTFB corrector error signal triggered on P0."

    APSLabeledEntry .period -parent $w -label "Full period (s): " \
      -textVariable period \
      -width 40 \
      -contextHelp "Period of CPU cycle. Usually 0.5 s if triggering externally from P0."

    APSLabeledEntry .triggerTime -parent $w -label "Duration of first waveform/state (s): " \
      -textVariable hperiod1 \
      -width 40 \
      -contextHelp "Time duration of first waveform. Not necessarily equal to one half of full period."

}

proc UpdateGraphs {args} {
    global graphWidth graphHeight timeLimit mainCurrent
    set startTime 0.012
    set period 0.5000
    set firstWaveform Pos
    set hperiod1 0.2
    APSParseArguments {input startTime period hperiod1 firstWaveform}

    if ![file exists $input] {
        return -code error "ProcessData: Can't find file $input"
    }

    APSSetVarAndUpdate ControlStatus "Processing data..."
    set mainCurrent [format %.3f [lindex [exec sdds2stream -para=MainCurrent $input] 0]]
    switch $firstWaveform {
        Pos {
            set rpnExpr ""
        }
        Neg {
            set rpnExpr "chs"
        }
        default {
            return -code error "ProcessData: Invalid firstWaveform: $firstWaveform"
        }
    }
    set tmpfile /tmp/[APSTmpString]
    # 127 ms is the limit for correction waveforms but we can
    # analyze more than that. We keep the first 127 ms of the 
    # deconvolved waveform for interpolation of the afg waveform.
    if [catch {exec sddsprocess $input -pipe=out \
                 "-redef=col,t,t $startTime -" \
                 | sddsprocess -pipe \
                 "-test=col,t 0 >" \
                 "-define=col,Sign,t $period 1.0 * mod $hperiod1 - sign $rpnExpr" \
                 | sddsbreak -pipe \
                 -changeOf=Sign \
                 | sddsprocess -pip=in $tmpfile.1 \
                 -proc=Sign,first,Sign \
                 -proc=t,first,tOffset \
                 "-redef=col,t,t tOffset -" \
                 -filter=col,t,0,$timeLimit\
             } result ] {
        return -code error "ProcessData(0): $result" 
    }
    foreach type {Neg Pos} sign {-1 1} {
        if [catch {exec sddsprocess $tmpfile.1 -pipe=out \
                     -filter=para,Sign,$sign,$sign \
                     | sddsenvelope -pipe \
                     -copy=t \
                     -mean=S?A:H3:Error \
                     -mean=S?A:V3:Error \
                     | sddsconvert -pipe=in $tmpfile.$type.2 \
                     -edit=col,*:ErrorMean,%/:ErrorMean// \
                 } result ] {
            return -code error "ProcessData(1): $result" 
        }
        set corrList {S4A:H3 S5A:H3 S4A:V3 S5A:V3}
        set varList {HreadChan1 HreadChan2 VreadChan1 VreadChan2}
        foreach corr $corrList var $varList {
            set varName ${var}${type}
            global $varName
            exec sddssmooth $tmpfile.$type.2 -pipe=out \
              -passes=2 -col=$corr -nowarnings \
              | sddsinterp -pipe=in $tmpfile.$type.3  \
              -col=t,$corr \
              -sequence=[expr int($timeLimit * 1e3) + 1],0,$timeLimit
            sdds load $tmpfile.$type.3 data
            set $varName [lindex $data(Column.$corr) 0] 
            APSSetVarAndUpdate ControlStatus "Setting $varName..."
        }
    }
    # need to update graph widgets here.
    #    MakeReadbackWidget .readback -parent .userFrame -width $graphWidth -height $graphHeight -static 1
    APSSetVarAndUpdate ControlStatus "Done."
    update
}

proc MakeReadbackWidget {widget args} {
    global graphReadWidgetList
    set parent ""
    set width 500
    set height 300
    set status 0
    APSParseArguments {parent width height static}

    set graphReadWidgetList [APSTabFrame .readbacks -parent $parent -label "" \
                               -labelList {"H plane Positive Pulse" "H plane Negative Pulse" "V plane Positive Pulse" "V plane Negative Pulse"} \
                               -width [expr 2 * $width] \
                               -height $height]

    # tabs for H plane channel readbacks
    set type read
    set index 0
    foreach Plane {H V} {
        foreach polarity {Pos Neg} {
            foreach chan {1 2} {
                set w [lindex $graphReadWidgetList $index]
                APSWaveformAdjuster .widget${chan} \
                  -parent $w \
                  -height 2 \
                  -static $static \
                  -showPrevious 1 \
                  -waveformVariable ${Plane}${type}Chan${chan}${polarity} \
                  -handleSpacing 2 \
                  -selectedYCoordVar ${Plane}${type}Chan${chan}var1${polarity} \
                  -selectedXCoordVar ${Plane}${type}Chan${chan}var2${polarity} \
                  -packOption "-side left -fill both -expand true"  \
                  -contextHelp "Readback for ${Plane} corrector error channel $chan. This is a read-only graph."
            }
            incr index
        }
    }
    return
}
proc MakeWaveformWidget {widget args} {
    global graphWaveWidgetList
    set parent ""
    set width 500
    set height 300
    APSParseArguments {parent width height}

    set graphWaveWidgetList [APSTabFrame .waveforms -parent $parent -label "" \
                               -labelList {"H correctors Positive Pulse" "H correctors Negative Pulse" "V correctors Positive Pulse" "V plane Negative Pulse"} \
                               -width [expr 2 * $width] \
                               -height $height]
    set type wave
    set index 0
    foreach Plane {H V} {
        foreach polarity {Pos Neg} {
            foreach chan {1 2} {
                set w [lindex $graphWaveWidgetList $index]
                APSWaveformAdjuster .widget${chan} \
                  -parent $w \
                  -height 2 \
                  -waveformVariable ${Plane}${type}Chan${chan}${polarity} \
                  -handleSpacing 2 \
                  -selectedYCoordVar ${Plane}${type}Chan${chan}var1${polarity} \
                  -selectedXCoordVar ${Plane}${type}Chan${chan}var2${polarity} \
                  -packOption "-side left -fill both -expand true"  \
                  -contextHelp "Readback for ${Plane} corrector error channel $chan. These vlaues will be written back to the corrector if edited."
                # DC part
                APSLabeledEntry .dc${chan} -parent $w.widget${chan} \
                  -label "DC part (A): " \
                  -textVariable ${Plane}${type}Chan${chan}${polarity}DC \
                  -packOption {-side top -fill x} \
                  -width 40 \
                  -contextHelp "DC part for waveform. (not included in graph)." 
            }
            incr index
        }
    }
    return
}

proc FileSelection {args} {
    set var ""
    APSStrictParseArguments {var}
    global $var
    set $var [APSFileSelectDialog .openDialog \
		  -path /home/helios/oagData/sr/IDs/CPU/ACmodes \
		  -pattern "*" \
		  -contextHelp "Select a file." \
		  -title "Select CPU correction file:"]
    set $var [os editstring %+/home/helios/oagData/sr/IDs/CPU/ACmodes/++ [set $var]]
    update idletasks
}

proc MakeCPUModifierWaveformWidget {widget args} {
    global baseDir
    global outputDir root index comment abort firstWaveform startTime period
    set parent ""
    APSParseArguments {parent}
    
    APSFrame $widget -parent $parent \
      -height 30 \
      -packOption {-side top -fill x} 
    
    set w $parent$widget.frame
    foreach Plane {H V} plane {h v} {
        foreach chan {1 2} {
            APSLabeledEntry .${plane}chan${chan} -parent $w -label "$Plane channel ${chan} file:" \
              -textVariable ${Plane}chan${chan}ModifierFile \
              -width 40 \
              -contextHelp "DC file for $plane plane channel $chan."
            APSButton .findFile -parent $w.${plane}chan${chan} \
              -text F -size small -packOption "-side right" \
              -command "FileSelection -var ${Plane}chan${chan}ModifierFile"
        }
    }
    global multiplier
    set multiplier 1
    APSLabeledEntry .multiplier -parent $w -label "Multiplier:" \
	-textVariable multiplier \
	-width 40 \
	-type real
    
#    APSRadioButtonFrame .useDC -parent $w -orientation horizontal \
      -label "Use DC waveform: " -variable useDC \
      -buttonList {No Yes} \
      -valueList {0 1} -contextHelp \
      "Select whether these DC waveforms will be added to the graphs modified below."
   
    APSButton .plot -parent $w \
      -text "Plot Modifier Correction Files" \
      -command {PlotCorrectorFiles -mode Modifier} \
      -packOption {-side left} \
      -contextHelp "This button plots the Modifier files selected."
    APSButton .addAC -parent $w \
      -text "Add AC Part" \
      -command {ReadFiles -mod "AddAC" -root $outputDir/$root-[format %03ld $index]} \
      -packOption {-side left} \
      -contextHelp "This button adds the AC part to the correctors."
    APSButton .replaceAC -parent $w \
      -text "Replace AC Part" \
      -command {ReadFiles -mod "ReplaceAC" -root $outputDir/$root-[format %03ld $index]} \
      -packOption {-side left} \
      -contextHelp "This button replaces the AC part to the correctors."
    APSButton .addDC -parent $w \
      -text "Add DC Part" \
      -command {ReadFiles -mod "AddDC" -root $outputDir/$root-[format %03ld $index]} \
      -packOption {-side left} \
      -contextHelp "This button adds the DC part to the correctors."
    APSButton .replaceDC -parent $w \
      -text "Replace DC Part" \
      -command {ReadFiles -mod "ReplaceDC" -root $outputDir/$root-[format %03ld $index]} \
      -packOption {-side left} \
      -contextHelp "This button replaces the DC part to the correctors."

    return
}

proc MakeCPUOriginalWaveformWidget {widget args} {
    global baseDir
    global outputDir root index comment abort firstWaveform startTime period
    set parent ""
    APSParseArguments {parent}
    
    APSFrame $widget -parent $parent \
      -height 30 \
      -packOption {-side top -fill x} 
    
    set w $parent$widget.frame
    APSButton .download -parent $w \
      -text "Get CPU Files" \
      -packOption "-side top" \
      -command {GetCPUfiles} \
      -contextHelp "This button determines which files are used by the CPU correctors, and updates the entry slots below. Then the data in the files are used to create the waveform data corresponding to the MainCurrent value, and replaces the waveforms. These waveforms are considered the \"original\" or baseline waveforms. The data in unused pages are used to write new files later."
    
    foreach Plane {H V} plane {h v} {
        foreach chan {1 2} {
            APSLabeledEntry .${plane}chan${chan} -parent $w -label "$Plane channel ${chan} file:" \
              -textVariable ${Plane}chan${chan}OriginalFile \
              -width 40 \
              -contextHelp "AC file for $plane plane channel $chan."
            APSButton .findFile -parent $w.${plane}chan${chan} \
              -text F -size small -packOption "-side right" \
              -command "FileSelection -var ${Plane}chan${chan}OriginalFile"
        }
    }
    
#    APSRadioButtonFrame .useAC -parent $w -orientation horizontal \
      -label "Use AC waveform: " -variable useAC \
      -buttonList {No Yes} \
      -valueList {0 1} -contextHelp \
      "Select whether these AC waveforms will be added to the graphs modified below."
   
    APSButton .readFiles -parent $w \
      -text "Read Files" \
      -command {ReadFiles -root $outputDir/$root-[format %03ld $index]} \
      -packOption {-side left} \
      -contextHelp "Read data in these files and create the waveform data corresponding to the MainCurrent value, overwriting the waveforms. These waveforms are considered the \"original\" or baseline waveforms. The data in unused pages are used to write new files later at the upload stage.\n\nThis button has to be pressed if the MainCurrent has changed, and you want to have the corrector waveforms used in the ioc copied to the waveform widget."

    APSButton .plot -parent $w \
      -text "Plot These Files" \
      -command {PlotCorrectorFiles -mode Original} \
      -packOption {-side left} \
      -contextHelp "This button plots the original files selected."

    return
}

proc MakeCPUOutputFileWidget {widget args} {
    global outputDir root index comment abort firstWaveform startTime period
    set parent ""
    APSParseArguments {parent}
    
    APSFrame $widget -parent $parent \
      -height 30 \
      -packOption {-side top -fill x} 
    
    set w $parent$widget.frame
    APSLabeledEntry .outputRoot -parent $w -label "Output root:" \
      -textVariable outputRoot \
      -width 40 \
      -contextHelp "Output root for data file for uploading."
    
    APSLabeledEntry .outputVersion -parent $w -label "Index (incremented just before creating new file):" \
      -textVariable outputVersion \
      -width 40 \
      -contextHelp "Index for output root for data file for uploading."
    
    APSButton .plot -parent $w \
      -text "Plot Data Files" \
      -command {Apply -root $outputDir/$root-[format %03ld $index] \
                  -outputRoot $outputRoot -outputVersion $outputVersion \
                  -mode plot \
                  -statusCallback SetStatus} \
      -packOption {-side left} \
      -contextHelp "This button plots the new files to be uploaded."

    return
}

proc GetCPUfiles {args} {
    global baseDir
    global graphWidth graphHeight numPoints

    APSParseArguments {root}

    if [catch {exec cavget -list=ID04b:corrFile_ -list=1,2,3,4} loadedFiles] {
        return -code error "Apply: $loadedFile"
    }
    # Note that loaded file need to have /CPU/ACmodes/
    # as part of the name, which is not convenient.
    global Hchan1OriginalFile Hchan2OriginalFile Vchan2OriginalFile Vchan1OriginalFile
    set Vchan2OriginalFile [file tail [file tail [lindex $loadedFiles 0]]]
    set Hchan2OriginalFile [file tail [file tail [lindex $loadedFiles 1]]]
    set Hchan1OriginalFile [file tail [file tail [lindex $loadedFiles 2]]]
    set Vchan1OriginalFile [file tail [file tail [lindex $loadedFiles 3]]]
    return
}


proc ReadFiles {args} {
    global baseDir
    global graphWidth graphHeight numPoints multiplier
    global Hchan1OriginalFile Hchan2OriginalFile Vchan2OriginalFile Vchan1OriginalFile
    global Hchan1ModifierFile Hchan2ModifierFile Vchan2ModifierFile Vchan1ModifierFile
    set mod ""
    APSParseArguments {root mod}

    set varList {HwaveChan1 HwaveChan2 VwaveChan1 VwaveChan2}
    if {[llength $mod]} {
        set pageIndices [FindBracketingPageIndices \
                           -baseFile $baseDir/$Hchan1ModifierFile \
                           -inputFile $root]
        if {[llength $pageIndices] != 2} {
            return -code error "Download: Something wrong with returned page indices of file $tmpfile.sorted ($pageIndices): $result"
        }
        set pageIndex [lindex $pageIndices 0]
        set fileList {Hchan1ModifierFile Hchan2ModifierFile Vchan1ModifierFile Vchan2ModifierFile}
        set varListMod {HwaveChan1Mod HwaveChan2Mod VwaveChan1Mod VwaveChan2Mod}
        foreach file $fileList var $varListMod {
            APSSetVarAndUpdate ControlStatus "Reading page \#$pageIndex from ${baseDir}/[set ${file}]..."
            foreach type {Pos Neg} {
                set varName ${var}${type}
                global $varName
                if [catch {exec sddsconvert ${baseDir}/[set ${file}] -pipe=out \
                             -keepPages=$pageIndex \
                             | sddsinterp -pipe \
                             -col=TimeIndex,Current${type} \
                             -sequence=$numPoints,0,[expr $numPoints - 1] \
                             | sdds2stream -pipe -col=Current${type} \
                         } $varName ] {
                    return -code error "ReadFiles(1): [set $varName]" 
                }
                global ${varName}DC
                set ${varName}DC [lindex [set $varName] end]
                set ${varName} [RemoveDCpart -waveform [set ${varName}]]
            }
        }
        foreach varMod $varListMod var $varList {
            foreach type {Pos Neg} {
                global ${var}${type} ${var}${type}DC
                if {$mod == "AddAC"} {
                    set v ""
                    foreach v1 [set ${varMod}${type}] v2 [set ${var}${type}] {
                        lappend v [expr "$v1 + $v2"]
                    }
                    set ${var}${type} $v
                    if {$multiplier != "1"} {
                        set v ""
                        foreach v1 [set ${var}${type}] {
                            lappend v [expr {$v1 * $multiplier}]
                        }
                        set ${var}${type} $v
                    }
                } elseif {$mod == "ReplaceAC"} {
                    set ${var}${type} [set ${varMod}${type}]
                    if {$multiplier != "1"} {
                        set v ""
                        foreach v1 [set ${var}${type}] {
                            lappend v [expr {$v1 * $multiplier}]
                        }
                        set ${var}${type} $v
                    }
                } elseif {$mod == "AddDC"} {
                    set ${var}${type}DC [expr {([set ${var}${type}DC] + [set ${varMod}${type}DC]) * $multiplier}]
                } elseif {$mod == "ReplaceDC"} {
                    set ${var}${type}DC [expr {[set ${varMod}${type}DC] * $multiplier}]
                }
            }
        }
    } else {
    # no modifications
        set pageIndices [FindBracketingPageIndices \
                           -baseFile $baseDir/$Hchan1OriginalFile \
                           -inputFile $root]

        if {[llength $pageIndices] != 2} {
            return -code error "Download: Something wrong with returned page indices of file $tmpfile.sorted ($pageIndices): $result"
        }
        # if this is a new mainCurrent value, 
        # select a page that is above in current. This will
        # avoid duplicating a zero waveform from the 0 A page.
        set pageIndex [lindex $pageIndices 1]
        set fileList {Hchan1OriginalFile Hchan2OriginalFile Vchan1OriginalFile Vchan2OriginalFile}
        foreach file $fileList var $varList {
            APSSetVarAndUpdate ControlStatus "Reading page \#$pageIndex from ${baseDir}/[set ${file}]..."
            foreach type {Pos Neg} {
                set varName ${var}${type}
                global $varName
                if [catch {exec sddsconvert ${baseDir}/[set ${file}] -pipe=out \
                             -keepPages=$pageIndex \
                             | sddsinterp -pipe \
                             -col=TimeIndex,Current${type} \
                             -sequence=$numPoints,0,[expr $numPoints - 1] \
                             | sdds2stream -pipe -col=Current${type} \
                         } $varName ] {
                    return -code error "ReadFiles(1): [set $varName]" 
                }
                # split waveform into AC and DC parts. Use waveform to
                # store the AC part.
                global ${varName}DC
                set ${varName}DC [lindex [set $varName] end]
                set ${varName} [RemoveDCpart -waveform [set ${varName}]]
            }
        }
    }
    APSSetVarAndUpdate ControlStatus "Done downloading."
}

proc RemoveDCpart {args} {
    APSParseArguments {waveform}
    set DC [lindex $waveform end]
    set result ""
    foreach item $waveform {
        lappend result [expr $item - $DC]
    }
    return $result
}

proc PlotCorrectorFiles {args} {
    global baseDir
    set mode Original
    APSParseArguments {mode}
    foreach Plane {H V} {
        foreach chan {1 2} {
            global ${Plane}chan${chan}${mode}File 
        }
    }
    set baseDir
    set hchan1file $baseDir/[set Hchan1${mode}File]
    set hchan2file $baseDir/[set Hchan2${mode}File]
    set vchan1file $baseDir/[set Vchan1${mode}File]
    set vchan2file $baseDir/[set Vchan2${mode}File]
    if {![file exists $hchan1file] || \
          ![file exists $hchan2file] || \
          ![file exists $vchan1file] || \
          ![file exists $vchan2file]} {
        return -code error "Apply: At least one of the files $hchan1file $hchan2file $vchan1file $vchan2file doesn't exist."
    }
    exec sddsplot -layout=2,2 \
      -split=pages -sep=pages,request -group=pages \
      -same -topline=@CurrentString -grap=line,vary \
      -leg=edit=%/Current// \
      -col=TimeIndex,Current??? $hchan1file \
      -ylabel=hcorr1  \
      -col=TimeIndex,Current??? $hchan2file \
      -ylabel=hcorr2  \
      -col=TimeIndex,Current??? $vchan1file \
      -ylabel=vcorr1  \
      -col=TimeIndex,Current??? $vchan2file \
      -ylabel=vcorr2  \
      &
    return
}

proc Apply {args} {
    global baseDir
    global Hchan1OriginalFile Hchan2OriginalFile Vchan2OriginalFile Vchan1OriginalFile
    set root ""
    set statusCallback ""
    set mode calculate
    set outputRoot ""
    set outputVersion ""
    APSParseArguments {root mode statusCallback \
                         outputRoot outputVersion}

    if {![string length $outputRoot] || \
          ![string length $outputVersion]} {
        return -code error "Apply: invalid syntax"
    }
    set tmpfile /tmp/[APSTmpString]
   
    set originalFile(vcorr2) $Vchan2OriginalFile
    set originalFile(hcorr2) $Hchan2OriginalFile
    set originalFile(hcorr1) $Hchan1OriginalFile
    set originalFile(vcorr1) $Vchan1OriginalFile
    
    set newFile(hcorr1) $outputRoot.hcorr1.[format %03ld $outputVersion]
    set newFile(hcorr2) $outputRoot.hcorr2.[format %03ld $outputVersion]
    set newFile(vcorr1) $outputRoot.vcorr1.[format %03ld $outputVersion]
    set newFile(vcorr2) $outputRoot.vcorr2.[format %03ld $outputVersion]

    set corrList {hcorr1 hcorr2 vcorr1 vcorr2}
    set varList {HwaveChan1 HwaveChan2 VwaveChan1 VwaveChan2}

    switch $mode {
        calculate {
            if {[file exists $baseDir/$newFile(hcorr1)] || \
                  [file exists $baseDir/$newFile(hcorr2)] || \
                  [file exists $baseDir/$newFile(vcorr1)] || \
                  [file exists $baseDir/$newFile(vcorr2)]} {
                return -code error "Apply: At least one of files $newFile(hcorr1) $newFile(hcorr2) $newFile(vcorr1) $newFile(vcorr2) exists already!."
            }
    
            if [string length $statusCallback] {
                $statusCallback "Calculating files to upload..."
            }
            set tmpfile /tmp/[APSTmpString]
            foreach corr $corrList var $varList {
                APSSetVarAndUpdate ControlStatus "Transfering $var graph..."
                MakeFileFromListVariables -output $tmpfile.${var} \
                  -namePos ${var}Pos \
                  -nameNeg ${var}Neg
                exec sddsxref $tmpfile.${var} $root \
                  -transfer=para,MainCurrent -noWarning -leave=*
                exec sddsprocess $tmpfile.${var} -noWarning \
                  "-reprint=para,CurrentString,Current = %lf A,MainCurrent"
                # input file is $tmpfile.${var} 
                # original data file (to modify) $baseDir$originalFile($corr)
                # output file is $baseDir/$newFile($corr)
                if [catch {CalculateNewFile -input $tmpfile.${var} \
                             -original $baseDir/$originalFile($corr) \
                             -output $baseDir/$newFile($corr) \
                         } result ] {
                    return -code error "Apply: $result"
                }
            }
            if [string length $statusCallback] {
                $statusCallback "Correction files calculated."
            }
            global Hchan1OriginalFile Hchan2OriginalFile Vchan2OriginalFile Vchan1OriginalFile
            set Vchan2OriginalFile $newFile(vcorr2)
            set Hchan2OriginalFile $newFile(hcorr2)
            set Hchan1OriginalFile $newFile(hcorr1)
            set Vchan1OriginalFile $newFile(vcorr1)
        }
        plot {
            if {![file exists $baseDir/$newFile(hcorr1)] || \
                  ![file exists $baseDir/$newFile(hcorr2)] || \
                  ![file exists $baseDir/$newFile(vcorr1)] || \
                  ![file exists $baseDir/$newFile(vcorr2)]} {
                return -code error "Apply: One of the files $baseDir/$newFile(hcorr1) $baseDir/$newFile(hcorr2) $baseDir/$newFile(vcorr1) $baseDir/$newFile(vcorr2) doesn't exist."
            }
            exec sddsplot -layout=2,2 \
              -split=pages -sep=pages,request -group=pages \
              -same -topline=@CurrentString -grap=line,vary \
              -leg=edit=%/Current// \
              -col=TimeIndex,Current??? $baseDir/$newFile(hcorr1) \
              -ylabel=hcorr1  \
              -col=TimeIndex,Current??? $baseDir/$newFile(hcorr2) \
              -ylabel=hcorr2  \
              -col=TimeIndex,Current??? $baseDir/$newFile(vcorr1) \
              -ylabel=vcorr1  \
              -col=TimeIndex,Current??? $baseDir/$newFile(vcorr2) \
              -ylabel=vcorr2   \
              &
        }
        load {
            #load in the files
            if [catch {exec cavput -list=ID04b:corrFile_ -list=1=/sr/IDs/CPU/ACmodes/$newFile(vcorr2),2=/sr/IDs/CPU/ACmodes/$newFile(hcorr2),3=/sr/IDs/CPU/ACmodes/$newFile(hcorr1),4=/sr/IDs/CPU/ACmodes/$newFile(vcorr1)} results] {
                return -code error $result
            }
            if [catch {exec cavput -list=ID04b:loadCorr_=1 -list=1,2,3,4 \
                     } results] {
                return -code error $result
            } 
            if [string length $statusCallback] {
                $statusCallback "Correction files uploaded."
            }
        }
        default {
            return -code error "Apply: mode $mode unknown"
        }
    }
}

# AC part and DC part are joined here to make a file
proc MakeFileFromListVariables {args} {
    APSParseArguments {output namePos nameNeg}

    global $namePos $nameNeg ${namePos}DC ${nameNeg}DC
    # this is to remove the linefeed characters
    set negData [join [split [subst \$$nameNeg]]]
    set posData [join [split [subst \$$namePos]]]
    set data(ColumnNames) "CurrentPos CurrentNeg"
    set data(ColumnInfo.CurrentPos) "units A type SDDS_DOUBLE"
    set data(ColumnInfo.CurrentNeg) "units A type SDDS_DOUBLE"
    set data(Column.CurrentPos) [list $posData]
    set data(Column.CurrentNeg) [list $negData]

    if [catch {sdds save $output data} result] {
        return -code error "MakeFileFromListVariables: $result"
    }

    if [catch {exec sddsprocess $output -noWarning \
                 "-redef=col,CurrentPos,CurrentPos [set ${namePos}DC] +" \
                 "-redef=col,CurrentNeg,CurrentNeg [set ${nameNeg}DC] +" \
                 -def=col,TimeIndex,i_row \
             } result] {
        return -code error "MakeFileFromListVariables: $result"
    }
    return
}

proc CalculateNewFile {args} {
    set input ""
    set original ""
    set output ""
    APSParseArguments {input original output}
    set tmpfile /tmp/[APSTmpString]
    
# This version of the procedure does no adjusting of data in a file.
# It simply replaces a page.

    if [catch {exec sddssort $original $tmpfile.sorted \
                 -parameter=MainCurrent,increasing \
             } result ] {
        return -code error "CalculateNewFile: $result"
    }
    set pageIndices [FindBracketingPageIndices \
                       -baseFile $tmpfile.sorted \
                       -inputFile $input]
    if {[llength $pageIndices] != 2} {
        return -code error "CalculateNewFile: Something wrong with returned page indices of file $tmpfile.sorted ($pageIndices): $result"
    }

    if {[lindex $pageIndices 0] == [lindex $pageIndices 1]} {
        # MainCurrent is found as one of page of base file.
        # split base file into three parts and process middle part
        set pageIndex [lindex $pageIndices 0]
        if {$pageIndex > 1} {
            if [catch {exec sddsconvert $tmpfile.sorted $tmpfile.1 \
                         -toPage=[expr $pageIndex - 1]\
                     } result ] {
                return -code error "CalculateNewFile: $result"
            }
        }
        if [catch {exec sddsconvert $tmpfile.sorted $tmpfile.2 \
                     -fromPage=[expr $pageIndex + 1]\
                 } result ] {
            return -code error "CalculateNewFile: $result"
        }
    } else {
        # MainCurrent is not one of the values found in the base file
        # so a new page has to be interpolated and inserted
        # into the base file.
        set pageIndex [lindex $pageIndices 0]
        if [catch {exec sddsconvert $tmpfile.sorted $tmpfile.1 \
                     -toPage=$pageIndex\
                 } result ] {
            return -code error "CalculateNewFile: $result"
        }
        if [catch {exec sddsconvert $tmpfile.sorted $tmpfile.2 \
                     -fromPage=[expr $pageIndex + 1]\
                 } result ] {
            return -code error "CalculateNewFile: $result"
        }
    }
    # combine everything
    if {$pageIndex > 0} {
        exec sddscombine $tmpfile.1 $input $tmpfile.2 \
          $output -overwrite \
          -retain=col,TimeIndex,CurrentNeg,CurrentPos
    } else {
    # if page index is zero then this data precedes all the other data page.
        exec sddscombine $input $tmpfile.2 \
          $output -overwrite \
          -retain=col,TimeIndex,CurrentNeg,CurrentPos
    }
    return
}

proc FindBracketingPageIndices {args} {
    set baseFile ""
    set inputfile ""
    APSStrictParseArguments {baseFile inputFile}

    if ![file exists $baseFile] {
        return -code error "FindBracketingPageIndices: File $baseFile does not exist!"
    }
    if ![file exists $inputFile] {
        return -code error "FindBracketingPageIndices: File $inputFile does not exist!"
    }

    set valueList [exec sdds2stream -param=MainCurrent $baseFile]
    set targetValue [exec sdds2stream -param=MainCurrent $inputFile]
    if {[llength $targetValue] > 1} {
        return -code error "FindBracketingPageIndices: Found more than on page in $inputFile"
    }
    if {$targetValue < [lindex $valueList 0]} {
        # value is lower than the first page value
        return [list 0 1]
    }
    for {set i 0} {$i < [llength $valueList]} {incr i} {
        if {[lindex $valueList $i] == $targetValue } {
            # page numbers are numbered starting with 1
            incr i
            return [list $i $i]
        }
        if {[lindex $valueList $i] < $targetValue && \
              $targetValue < [lindex $valueList [expr $i + 1]]} {
            incr i
            return [list $i [expr $i + 1]]
        }
    }
    if {$targetValue > [lindex $valueList end]} {
        # value is higher than the last page value
        # then return the last page.
        return [list [llength $valueList] [llength $valueList]]
    }
    return -code error "FindBracketingPageIndices: Target value of $targetValue was not found in list $valueList."
}

set autoscanWaveform AutoHwaveChan1var1Pos
set autoscanMinY 0
set autoscanMaxY 1
set autoscanSteps 3
proc AutoScan {args} {
    APSDialogBox .autoscan
    APSRadioButtonFrame .waveform \
      -parent .autoscan.userFrame \
      -label "Select waveform to modify" \
      -variable "autoscanWaveform" \
      -buttonList {"H Corrector Positive Pulse 1" "H Corrector Positive Pulse 2" "H Corrector Negative Pulse 1" "H Corrector Negative Pulse 2" "V Corrector Positive Pulse 1" "V Corrector Positive Pulse 2" "V Corrector Negative Pulse 1" "V Corrector Negative Pulse 2"} \
      -limitPerRow 1 \
      -valueList "AutoHwaveChan1var1Pos AutoHwaveChan2var1Pos AutoHwaveChan1var1Neg AutoHwaveChan2var1Neg AutoVwaveChan1var1Pos AutoVwaveChan2var1Pos AutoVwaveChan1var1Neg AutoVwaveChan2var1Neg" \
      -orientation horizontal
    APSLabeledEntry .minY \
      -parent .autoscan.userFrame \
      -label "Min Y" \
      -textVariable autoscanMinY
    APSLabeledEntry .maxY \
      -parent .autoscan.userFrame \
      -label "Max Y" \
      -textVariable autoscanMaxY
    APSLabeledEntry .steps \
      -parent .autoscan.userFrame \
      -label "Scan Steps" \
      -textVariable autoscanSteps
    APSButton .startscan \
      -parent .autoscan.userFrame \
      -text "Start Scan" \
      -command "StartScan"
}

proc StartScan {args} {
    destroy .autoscan
    global autoscanWaveform autoscanMinY autoscanMaxY autoscanSteps
    set yvar [string range $autoscanWaveform 4 end]
    global $yvar
    if {[string first Chan1 $yvar] != -1} {
        set chan 1
    } else {
        set chan 2
    }
    set i [string first var1 $yvar]
    set waveVar [string range $yvar 0 0]read[string range $yvar 5 [expr {$i - 1}]][string range $yvar [expr {$i + 4}] end]
    global $waveVar
    set range [expr [eval math::max [set $waveVar]] - [eval math::min [set $waveVar]]]
    global graphWaveWidgetList
    set index [expr [lsearch -exact "HwaveChan1var1Pos HwaveChan2var1Pos HwaveChan1var1Neg HwaveChan2var1Neg VwaveChan1var1Pos VwaveChan2var1Pos VwaveChan1var1Neg VwaveChan2var1Neg" $yvar] / 2]
    set w [lindex $graphWaveWidgetList $index].widget${chan}.ycoord.entry
    set increment [expr {($autoscanMaxY - $autoscanMinY) / ($autoscanSteps * 1.0)}]
    set yvarList [set $yvar]
    APSSetVarAndUpdate $yvar $autoscanMinY
    lappend yvarList [set $yvar]
    focus $w
    event generate $w <Return>
    APSWaitWithUpdate -waitSeconds .1
    .userFrame.meas.frame.calculate.button flash
    .userFrame.meas.frame.calculate.button invoke
    APSWaitWithUpdate -waitSeconds .1
    .userFrame.meas.frame.upload.button flash
    .userFrame.meas.frame.upload.button invoke
    APSWaitWithUpdate -waitSeconds .1
    .userFrame.parameters.frame.tn.canvas.notebook.cs.page1.cs.setup.frame.collectData.button flash
    .userFrame.parameters.frame.tn.canvas.notebook.cs.page1.cs.setup.frame.collectData.button invoke
    APSWaitWithUpdate -waitSeconds 1
    lappend range [expr [eval math::max [set $waveVar]] - [eval math::min [set $waveVar]]]
    for {set i 1} {$i <= $autoscanSteps} {incr i} {
        APSSetVarAndUpdate $yvar [expr {[set $yvar] + $increment}]
        lappend yvarList [set $yvar]
        focus $w
        event generate $w <Return>
        APSWaitWithUpdate -waitSeconds .1
        .userFrame.meas.frame.calculate.button flash
        .userFrame.meas.frame.calculate.button invoke
        APSWaitWithUpdate -waitSeconds .1
        .userFrame.meas.frame.upload.button flash
        .userFrame.meas.frame.upload.button invoke
        APSWaitWithUpdate -waitSeconds .1
        .userFrame.parameters.frame.tn.canvas.notebook.cs.page1.cs.setup.frame.collectData.button flash
        .userFrame.parameters.frame.tn.canvas.notebook.cs.page1.cs.setup.frame.collectData.button invoke
        APSWaitWithUpdate -waitSeconds 1
        lappend range [expr [eval math::max [set $waveVar]] - [eval math::min [set $waveVar]]]
    }
    set bestrun [lsearch -exact $range [eval math::min $range]]
    APSSetVarAndUpdate $yvar [lindex $yvarList $bestrun]
    APSSetVarAndUpdate ControlStatus "The best run YCoord=[lindex $yvarList $bestrun], range=[eval math::min $range]"
    focus $w
    event generate $w <Return>
    
}

# Initialize waveforms
set timeLimit 0.060
set numPoints [expr int($timeLimit * 1000 + 1)]

foreach Plane {H V} {
    foreach type {read wave} {
        foreach chan {1 2} {
            foreach polarity {Neg Pos} {
                foreach var {1 2} {
                    # example variable:
                    # set HreadChan1var1Pos 0
                    set ${Plane}${type}Chan${chan}var${var}${polarity} 0
                }
                # example variable:
                # set HreadChan1Pos 0
                set ${Plane}${type}Chan${chan}${polarity} \
                  [APSReplicateItem -item 0 -number $numPoints]
            }
        }
    }
}
foreach Plane {H V} {
    foreach chan {1 2} {
        foreach polarity {Neg Pos} {
            set ${Plane}waveChan${chan}${polarity}DC 0
        }
    }
}

set Hchan1ModifierFile zero.sdds
set Hchan2ModifierFile zero.sdds
set Vchan1ModifierFile zero.sdds
set Vchan2ModifierFile zero.sdds

# Build Application
set graphWidth 500
set graphHeight 250
MakeCPUStatus

set widgetList [APSTabFrame .parameters -parent .userFrame -label "" \
                          -labelList {"Setup and Measurement" \
                                        "Plot commands" \
                                        "Processing parameters" \
                                        "Original waveforms" \
                                        "Modifier waveforms" \
                                        "Output file parameters"} \
                          -width 900 -height 260]
pack configure .userFrame.parameters -fill x -expand true

MakeCPUSetupWidget .setup -parent [lindex $widgetList 0]

MakeCPUMeasWidget .meas -parent .userFrame

MakeReadbackWidget .readback -parent .userFrame \
  -width $graphWidth -height $graphHeight -static 1
MakeWaveformWidget .waveform -parent .userFrame \
  -width $graphWidth -height [expr $graphHeight + 50]

MakeCPUPlotActionWidget .plot -parent [lindex $widgetList 1]
MakeCPUProcessWidget .proc -parent [lindex $widgetList 2]
MakeCPUOriginalWaveformWidget .wave -parent [lindex $widgetList 3]
MakeCPUModifierWaveformWidget .wave -parent [lindex $widgetList 4]
MakeCPUOutputFileWidget .modParam -parent [lindex $widgetList 5]

set configFile /home/helios/oagData/sr/RTFB/monitorFiles/S4S5CorrError.dspscope
set scalarsFile /home/helios/oagData/sr/RTFB/monitorFiles/SRFBTrigReadStatus.monitor

set index -1
set root corrError
set comment test
set outputVersion 0
set outputRoot [clock format [clock seconds] -format "%y%m%d"]

set baseDir /home/helios/oagData/sr/IDs/CPU/ACmodes
set startTime 0.008
set firstWaveform Neg
set period 0.5000
set hperiod1 0.202
set tau 0.007
set outputDir .
#set outputDir /home/helios/SR/daily/0204/15/3/cpu
set index 2
set abort 0
set useDC 1

set average 4


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