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

#
# $Log: not supported by cvs2svn $
# Revision 1.60  2011/03/03 21:03:46  sajaev
# Added warning message about scraper position being important for the
# measurement.
#
# Revision 1.59  2011/01/31 09:39:03  sajaev
# Added recording of S38A fpga short waveforms. No processing yet.
#
# Revision 1.58  2010/08/18 08:16:09  xiaoam
# Fixed the bug that causes first pass injection cycle to 0
#
# Revision 1.57  2010/03/30 06:55:31  lemery
# New checking of monitor file. Correct terminal entry for toggle. By Shang.
#
# Revision 1.56  2010/03/02 06:20:42  lemery
# Set variable desiredBucket from the PV Mt:S:BunchSelect_lo at the start of the kicker scan procedure in order for the value to survive  several changes.
#
# Revision 1.55  2010/03/02 06:14:28  lemery
# Force injection bucket 000 to be the present value of Mt:S:BunchSelect_lo, which the user controls manually before the scan starts.
#
# Revision 1.54  2010/03/02 06:04:04  lemery
# Forgot a puts statement right after the toggle process is launched.
#
# Revision 1.53  2010/03/02 05:53:34  lemery
# Made default bpms different. Previous version had them the same unintentionally.
#
# Revision 1.52  2010/03/02 05:07:53  lemery
# Added injection reference lattice for loading when injecting as option. This is useful when a really bad DA lattice is being measured.
#
# Revision 1.51  2008/10/21 06:51:04  emery
# Changed assumed efficiency from 0.8 to 1.0 to be more conservative in
# number of pulses.
#
# Revision 1.50  2008/10/21 06:41:38  emery
# Corrected rpn expr syntax.
#
# Revision 1.49  2008/10/21 06:36:20  emery
# Added slow injection in order to get exact current before kicking hte beam.
# Also use APScavget commands.
#
# Revision 1.48  2007/06/12 19:56:13  emery
# Make not using IK5 the default.
#
# Revision 1.47  2007/03/28 06:30:17  emery
# Added 0.5 second wait time after setting up injection timing PVs
# for the single shot.
#
# Revision 1.46  2007/01/29 00:36:56  emery
# Changed default to reinject mode.
#
# Revision 1.45  2007/01/29 00:35:19  emery
# Changed the setting of Mt:SRinjectMultiExcludSIS1BO to Include.
#
# Revision 1.44  2007/01/29 00:27:56  emery
# Removed extraneous debugging statements from last version.
#
# Revision 1.43  2007/01/29 00:26:14  emery
# Changed defaults to values we typcially use now.
#
# Revision 1.42  2006/09/21 20:26:51  soliday
# Updated because of PV name changes.
#
# Revision 1.41  2004/09/29 09:42:43  emery
# Reverted to 0.5 wait time before triggering single shot,
# and made 1 s wait time after triggering single shot.
#
# Revision 1.40  2004/09/29 09:26:24  emery
# Moved the 0.5 second wait time after single shot bpm acquisition
# rather than before (for repeated mode).
#
# Revision 1.39  2002/10/15 08:42:50  emery
# removed extraneous catch statement.
#
# Revision 1.38  2002/03/26 17:14:32  emery
# Changed argument of togglePulseMagnet from PARto.. to Gunto..
#
# Revision 1.37  2001/10/30 08:24:17  emery
# Removed references to npw-defunct bpm timing PVs.
# Added factor 1.0 to delta calculation.
#
# Revision 1.36  2001/04/02 23:28:25  emery
# Added processing to convert history data to real dimensions (i.e. mm)
# and plotting of processed data.
#
# Revision 1.35  2001/04/01 09:07:01  emery
# I made several spelling corrections of variable.
# In repeated kicker mode, one page of data is saved for each point,
# in single shot mde, two pages of data is saved (for before and after).
#
# Revision 1.34  2001/03/31 21:57:20  emery
# Added RF frequency offset for experiment.
# Added bunch train reinjection as option.
# Added IK5 pulsing as option.
# Added nonlinear scan in kicker to make equal steps in x^2.
# Added procedures to save and restore RF offset and kicker settings.
# Changed the way bpms are selected.
#
# Revision 1.33  2001/03/22 04:21:02  emery
# Made outputDir a global variable in several procedures.
#
# Revision 1.32  2001/03/22 04:17:25  emery
# Repeat previous changes. (Somehow part of them weren't commited).
#
# Revision 1.30  2001/03/13 03:39:22  emery
# Added a wait time between enabling hte kickers and sending the single
# shot trigger.
#
# Revision 1.29  2001/02/27 05:31:22  emery
# Added a & for a sddsplot command.
#
# Revision 1.28  2001/02/17 00:08:29  emery
# Corrected daily directory button definition, and make
# sure that the daily directory button is placed next to the
# directory entry slot.
#
# Revision 1.27  2000/12/12 08:10:10  emery
# Added catch statements for restore bpm scdus.
#
# Revision 1.26  2000/12/11 22:50:06  emery
# Added processing for single shot mode to produce
# a plot of fraction surviving as a function of kicker strength.
#
# Revision 1.25  2000/11/29 11:41:34  emery
# Added sufficient time for injection to complete and for
# DCCT to settle (5 sec).
# Fixed problem with currentLimitAO being too low for the
# kickers to pulse once.
#
# Revision 1.24  2000/03/29 13:11:04  emery
# Corrected comment argument for sddswmonitor.
#
# Revision 1.23  2000/03/18 00:08:06  emery
# Added comment parameter to data.
#
# Revision 1.22  1999/11/23 20:24:08  emery
# Changed Mt:SRinjectCurrentLimitAO to just $beamCurrent
#
# Revision 1.21  1999/11/17 03:33:10  emery
# Added an udpate command.
#
# Revision 1.20  1999/06/16 05:41:28  emery
# replaced "exec rm" by file delete command.
#
# Revision 1.19  1998/12/01 08:04:23  emery
# Replace old DoBunchTrainInjection with the ioc-based
# injection DoIOCBunchTrainInjection.
#
# Revision 1.18  1998/12/01 07:32:52  emery
# Fixed variable kicker in procedure RestoreKickers
#
# Revision 1.17  1998/11/25 15:51:17  emery
# Wrote procedures RestoreBPMs RestoreKickers to clean up the code.
#
# Revision 1.16  1998/05/22 23:08:40  emery
# Make use of new APSGoToDailyDirectory procedure.
#
# Revision 1.15  1998/05/19 22:29:21  emery
# Added sector 3 as a default fully y-plane history waveform.
#
# Revision 1.14  1997/10/28 10:10:22  emery
# ADded missing cd $outputDir statement
#
# Revision 1.13  1997/10/28 10:05:12  emery
# Added daily directory button.
#
# Revision 1.12  1997/08/04 02:00:17  emery
# Added a PLOT buttons.
#
# Revision 1.11  1997/08/04 01:42:57  emery
# Added the possibility of starting a kicker scan
# with a no-zero value.
#
# Revision 1.10  1997/06/16 09:37:59  emery
# Removed an extraneous 'set index ""' statement at the start of
# MeasureDynamicAperture.
#
# Revision 1.9  1997/06/16 09:29:56  emery
# Replaced output file entry with root and index entries.
#
# Revision 1.8  1997/06/03 07:43:02  emery
# Added a "done" message at end of script.
#
# Revision 1.7  1997/06/03 07:38:26  emery
# Leave the kicker HV On at the end of the script.
# Leave RF12 power on after inejction.
# Enable the thick septum pulsing for warmup.
#
# Revision 1.6  1997/03/12 14:33:35  emery
# Fixed a bug in "ABORT INJECTION" definition
#
# Revision 1.5  1997/03/12 14:25:35  emery
# Added AbortInjection button.
#
# Revision 1.4  1997/03/12 12:50:34  emery
# Added setting FIFO fill mode to Stop on Full.
#
# Revision 1.3  1997/02/24 13:17:47  emery
# Added disabling of the kickers to ensure that the kicker are really
# disabled.  Re-injection and calls to DoBunchTrain occur only when
# needed.  Removed test for value of Step.  Made the result of the Stop
# buuton more graceful by disabling the kickers, turning on the HV, and
# restoring the original setpoints.  Changed defaults.
#
# Revision 1.2  1997/02/23 15:28:16  emery
# Partially tested version.
#
# Revision 1.1  1997/02/22 20:13:42  borland
# First version---not tested!
#
#

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

if {[info exists tcl_pkgPath]} {
    set rpnLib [file join [lindex $tcl_pkgPath 0] rpn libtclRPN.so]
    if {($tcl_platform(os) == "Linux") && [file exists $rpnLib]} {
        load $rpnLib
    } else {
        package require rpn
    }
} else {
    package require rpn
}

proc EnableWidgets {args} {
    global sensitiveWidgets
    APSParseArguments {option}
    if {-1 < [lsearch [array names sensitiveWidgets] $option]} {
        foreach widget $sensitiveWidgets($option) {
            $widget configure -state normal
        }
    }
    return
}
proc DisableWidgets {args} {
    global sensitiveWidgets
    APSParseArguments {option}
    if {-1 < [lsearch [array names sensitiveWidgets] $option]} {
        foreach widget $sensitiveWidgets($option) {
            $widget configure -state disabled
        }
    }
    return
}

proc MakeInputFrame {widget args} {
    set parent ""
    global kicker start end delta steps stepMode output 
    global historyBPM xHistoryBPM xHistoryBPM2 yHistoryBPM 
    global comment kickerMode reinjectMode sensitiveWidgets
    global beamCurrent mulitplet rfFrequencyOffset outputDir
    APSStrictParseArguments {parent}

    set outputDir .
    APSFrame $widget -parent $parent -label "Input parameters"
    set w $parent$widget.frame
    APSRadioButtonFrame .ik -parent $w -orientation horizontal \
      -label "Kicker: " -variable kicker -buttonList {IK1 IK2 IK3 IK4 } \
      -valueList {IK1 IK2 IK3 IK4 } \
      -contextHelp "Choose the kicker to use for the experiment.  IK2 is not recommended."
    APSRadioButtonFrame .kickerMode -parent $w -orientation horizontal \
      -label "Kicker mode: " -variable kickerMode -buttonList {repeated "single-shot"} \
      -valueList {repeated singleShot} \
      -contextHelp \
      "Choose whether the kicker will fire repeatedly at each setpont or fire only once.\n\nRepeated mode is used in kicker scans where the same beam is kept in the ring. With large charge per bunch, the limiting charge per bunch is a function of kicker strength, so a large charge per bunch is not recommended.\n\nSingle-shot mode is used with small charge per bunch to measure fractional loss for one kicker firing. Recommended minimum current of 0.2 mA is automatically set."
    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 dynamicAperture]}
    APSLabeledEntry .root -parent $w -label "Root name of file: " \
      -textVariable root \
      -contextHelp "Enter the root name of the file for the processed output <root>-nnn.sdds.  The raw data is saved in another file with the extension .raw appended to this filename."
    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 .start -parent $w -label "Start (kV): " -textVariable start \
      -contextHelp "Enter the starting strength of the scan.  The kicker is scanned starting from this value."
    APSLabeledEntry .end -parent $w -label "End (kV): " -textVariable end \
      -contextHelp "Enter the strength limit for the scan.  The kicker is scanned ending at this value."
    APSRadioButtonFrame .stepMode -parent $w -orientation horizontal \
      -label "Stepping mode: " -variable stepMode -buttonList {linear "square root"} \
      -valueList {linear squareRoot} \
      -contextHelp \
      "Select stepping mode. If linear is selected then the steps are equal. If square root is selected, then the steps in kicker strength squared is equal"
    APSLabeledOutput .delta -parent $w -label "Delta (kV): " -textVariable delta \
      -contextHelp "Enter the delta for the kicker strength."
    APSLabeledEntry .steps -parent $w -label "Points : " -textVariable steps \
      -contextHelp "Number of points in the kicker scan."
    bind $w.start.entry <Return> "UpdateDelta"
    bind $w.end.entry <Return> "UpdateDelta"
    bind $w.steps.entry <Return> "UpdateDelta"
    APSLabeledEntry .rfOffset -parent $w -label "RF frequency offset: " \
      -textVariable rfFrequencyOffset \
      -contextHelp "Enter the rf frequency offset to be used for the scan. Note that this quantity is not scanned. The initial rf frequency restored for injection purposes."

    APSLabeledEntry .pause -parent $w -label "DCCT settling time (s): " \
      -textVariable DCCTpause \
      -contextHelp "Enter the time one expects the DCCCT to settle. This is for the measurement of the fractional loss after a kick. Some days it could take 7 seconds! "
    APSRadioButtonFrame .collectBPMdata -parent $w -orientation horizontal \
      -label "collect FPGA BPM histories: " -variable collectBPMs -buttonList {"no, No, NO!" yes} \
      -valueList {0 1} \
      -contextHelp \
      "If yes, then bpm histories from FPGA will be collected. Note that the repeated arming can be some trouble. Not all data may be collected."
    APSRadioButtonFrame .injectionMode -parent $w -orientation horizontal \
      -label "Reinjection as needed: " -variable reinjectMode -buttonList {no yes} \
      -valueList {0 1} \
      -contextHelp \
      "If no reinjected is selected, then the scan will proceed to the end without any reinjection. If reinjection is selected then injection will occur as needed during the scan. The initial configuration (supposedly appropriate for injection) will be restored everytime for injection."
    APSRadioButtonFrame .latticeMode -parent $w -orientation horizontal \
      -label "Restore a reference lattice on injection: " -variable latticeMode -buttonList {no yes} \
      -valueList {0 1} \
      -contextHelp \
      "If \"restore a lattice\" is selected, then the file specified below will be restored before reinjection is attempted. This helps in measuring lattices that have small DA, which don't inject into so well. This works with \"reinjection as need\""
    APSLabeledEntry .injectionLattice -parent $w -label "Reference lattice for injection: " \
	-width 50 \
	-textVariable injectionLattice \
	-contextHelp "Enter the filename for configuring the lattice for injection. This smay include quadrupole, sextupoles, skew quadrupoles, and perhaps correctors. It is possible that correctors may be in vector mode; in that case there will be no effect. "
    APSLabeledEntry .cycles -parent $w -label "Cycles for multiplet: " \
      -textVariable cycles \
      -contextHelp "Enter the number of cycles for injecting the multiplets"
    APSLabeledEntry .multiplets -parent $w -label "Multiplets: " \
      -textVariable multiplet \
      -contextHelp "Enter the number of multiplets (number of consecutive buckets to fill) for a bunch train"
    APSLabeledEntry .beam -parent $w -label "Minimum current (mA): " \
      -textVariable beamCurrent \
      -contextHelp "Enter the desired beam current to start the experiment or to maintain during the experiment. If no reinjection is requested then the injection takes place only at the start of the scan. If reinjection is requested, then injection takes place before each measurement if needed."
    return
}

proc UpdateDelta {} {
    global start end delta steps
    set delta [expr 1.0 * ($end - $start) / ($steps - 1) ]
    return
}

proc MeasureDynamicAperture {args} {
    global index
    set kicker ""
    set kickerMode ""
    set start 0
    set end 5
    set stepMode ""
    set steps 0
    set outputDir .
    set root ""
    set rfFrequencyOffset 0
    set comment ""
    set beamCurrent 0
    set multiplet 1
    set cycles 2
    set reinjectMode 0
    set statusCallback ""
    set abortVariable ""
    set DCCTpause 5
    set collectBPMs 0
    set saveArgs $args
    if {[APSStrictParseArguments \
           {parent outputDir root kicker kickerMode  \
              start end steps stepMode rfFrequencyOffset \
              comment DCCTpause collectBPMs \
              beamCurrent multiplet cycles reinjectMode latticeMode injectionLattice \
              statusCallback abortVariable}] || \
          [string length $comment]==0 || [string length $kickerMode]==0 ||  \
          [string length $root]==0 || $start<0 || $end<$start || $steps<=0 || \
          [string length $stepMode]==0 ||  \
          $beamCurrent<0 || $multiplet<0 || $cycles<0 || $DCCTpause<0 } {
        return -code error "MeasureDynamicAperture(2): Invalid parameters: $saveArgs"
    }
    if [string length $abortVariable] {
        global $abortVariable
        set $abortVariable 0
    }
    set desiredBucket [exec cavget -list=Mt:S:BunchSelect_lo]
    set disableSeptum 1

    set oldDir [pwd]
    if ![file exists $outputDir] {exec mkdir $outputDir}
    cd $outputDir
    set kickValue ""
    switch -exact $stepMode {
        squareRoot {
            set delta Variable
            set end2 [expr pow($end,2)]
            set start2 [expr pow($start,2)]
            set delta2 [expr 1.0 * ($end2 - $start2)/($steps - 1)]
            for {set i 0} {$i<$steps} {incr i} {
                lappend kickValue [expr sqrt($start2 + $i * $delta2)]
            }
        }
        linear -
        default {
            set delta [expr 1.0 * ($end - $start) / ($steps - 1 )]
            for {set i 0} {$i<$steps} {incr i} {
                lappend kickValue [expr ($start + $i * $delta)]
            }
        }
    }
    APSSetVarAndUpdate delta $delta
    incr index
    set output $root-[format %03ld $index].sdds
    if [file exists $output.raw] {
        return -code error "MeasureDynamicAperture: file $output.raw exists"
    }
    
    if [lsearch -exact [list IK1 IK2 IK3 IK4] $kicker]==-1 {
        return -code error "MeasureDynamicAperture: invalid kicker"
    }

    # suspend various controllaw
    if [catch {APScavput -list=S:RC:OrbitControlLawXC,S:RC:OrbitControlLawYC,S:rfFreqControlLawRC -list=.SUSP=1} result] {
        SetStatus "Problem suspending controllaw: $result"
        return
    }

    set scalarMonFile  /home/helios/oagData/sr/sddsmonitorFiles/dynapScan.mon
    if [catch {CheckMonitorFile -filename $scalarMonFile} result] {
        return -code error $result
    }

    if [string length $statusCallback] {
        eval $statusCallback {"Setting up kicker"}
    }

    # save the setpoint for the kicker being used
    SaveKicker -kicker $kicker

    set kickerSetpoint [lindex $kickValue 0]

# This is first injection before the scan starts
    if [catch {SlowInjection -beamCurrent $beamCurrent \
                 -multiplet $multiplet \
                 -cycles $cycles \
                 -DCCTpause $DCCTpause \
                 -statusCallback $statusCallback} result] {
        return -code error "MeasureDynamicAperture: $result"
    }
    # if no reinjection is requested then setup the kickers for experiment 
    # immediately
    if {!$reinjectMode} {
        # disable kicker triggering
        # turn off HV on all kickers
        # turn HV back on for the one being used
        if {[catch {after 100
            APScavput -pend=10 -list=Mt:Ddg -list=3chan4.GATE=Disabled} result] || \
              [catch {after 100
                  APScavput -pend=10 -list=S:IK -list=1,2,3,4 -list=:OnBO=OFF} result] || \
              [catch {after 100
                  APScavput -pend=10 -list=S:$kicker:VoltageSetSendAO=$kickerSetpoint} result] || \
              [catch {after 1000
                  APScavput -pend=10 -list=S:$kicker:OnBO=ON} result] } {
            return -code error "MeasureDynamicAperture: $result"
        }
        # repeat commanad because there is a chance the value didn't take.
        if {[catch {after 500 
            APScavput -pend=10 -list=S:$kicker:VoltageSetSendAO=$kickerSetpoint} result] } {
            return -code error "MeasureDynamicAperture: $result"
        }
            
        if $disableSeptum {
            if [catch {after 100; APScavput -pend=10 -list=Mt:Ddg3chan2.GATE=0} result] {
                return -code error "MeasureDynamicAperture: $result"
            }
        }
    }
    SaveRFfrequency
    SetRFfrequency -offset $rfFrequencyOffset
    if $collectBPMs {
        set waveformList [list S1A:P2:turnHistorySmall:Xposition S1A:P2:turnHistorySmall:Xsum S1A:P4:turnHistorySmall:Yposition \
                            S1A:P4:turnHistorySmall:Ysum S38A:P2:turnHistorySmall:Xposition S38A:P2:turnHistorySmall:Xsum \
                            S38A:P4:turnHistorySmall:Yposition S38A:P4:turnHistorySmall:Ysum]
        exec sddsmakedataset $outputDir/dynap.wmon \
          -col=WaveformPV,type=string -data=[join $waveformList ,] \
          -col=WaveformName,type=string -data=[join $waveformList ,] \
          -para=WaveformLength,type=long -data=4096
        exec sddsmakedataset $outputDir/dynap.mon -col=ControlName,type=string -data=S:${kicker}:VoltageSetSendAO \
          -col=ReadbackName,type=string -data=KickerSetting \
          -para=Interval,type=double -data=1 -para=Steps,type=long -data=1
        after 100
        exec cavput -list=S1A:turn:gtr:numberPTS=261144,S38A:turn:gtr:numberPTS=261144 -pend=5
        after 100
        exec cavput -list=S38A:clientEvent2d.OUT4=0,S38A:clientEvent2d.OUT5=1,S38A:clientEvent33.OUT4=0 -pend=5
        after 100
        exec cavput -list=S1A:clientEvent2d.OUT4=0,S1A:clientEvent2d.OUT5=1,S1A:clientEvent33.OUT4=0 -pend=5
    } else {
        set waveformFileList ""
    }
    for {set step 0} {$step<$steps} {incr step} {
        set kickerSetpoint [lindex $kickValue $step]
        if [string length $statusCallback] {
            eval $statusCallback {"\nDoing step $step of experiment: Kicker setpoint: $kickerSetpoint"}
        }
        update
        # if aborting, then restore the kicker original setpoint.
        if {[string length $abortVariable] && [subst \$$abortVariable]} {
            if [catch {RestoreKicker -kicker $kicker
                RestoreRF} result] {
                return -code error "MeasureDynamicAperture: $result"
            }
            set $abortVariable 0
            break
        }
        # Check if reinjection is necessary.
        # Otherwise just keep the same beam in the machine.
        set S35DCCT [APScavget -list=S-DCCT:CurrentM]
        if {$reinjectMode && [expr $S35DCCT > $beamCurrent]} {
            if [string length $statusCallback] {
                eval $statusCallback {"S35DCCT > $beamCurrent mA. No need to fill for step $step."}
            }
        }
        if {$reinjectMode && [expr $S35DCCT < $beamCurrent]} {
            if [string length $statusCallback] {
                eval $statusCallback {"Filling to $beamCurrent mA for step $step"}
            }
            if $collectBPMs {
                if [catch {ArmFPGA -armList "S1A S38A" -value 0 -statusCallback $statusCallback} result] {
                    if [string length $statusCallback] {
                        eval $statusCallback {"ArmFPGA: $result"}
                        eval $statusCallback {"Continue the loop..."}
                    }
                }
            }
            # setup kickers for injection:
            # turn off kicker triggering 
            # restore the scanned kicker to the original setpoint.
            # enable thick septum pulsing for its warmup.
            # turn on HV on all kickers
            # restore lattice if requested
            if {[catch {after 100
                APScavput -pend=10 -list=Mt:Ddg -list=3chan4.GATE=Disabled} result] || \
                  [catch {RestoreKicker -kicker $kicker} result] || \
                  [catch {after 100
                      APScavput -pend=10 -list=Mt:Ddg3chan0.GATE=Enabled} result] || \
                  [catch {after 100
                      APScavput -pend=10 -list=S:IK -list=1,2,3,4 -list=:OnBO=ON} result]} {
                return -code error "MeasureDynamicAperture: $result"
            }
            if $disableSeptum {
                if [catch {after 100; APScavput -pend=10 -list=Mt:Ddg3chan2.GATE=1} result] {
                    return -code error "MeasureDynamicAperture: $result"
                }
            }
            if $latticeMode {
                if [catch {CheckMonitorFile -filename $injectionLattice} result] {
                    return -code error $result
                }
                set toggleID [open "|toggle $injectionLattice -cycle=1 -prompt" w]
                puts $toggleID "y"
                if [catch {flush $toggleID} result] {
                    eval $statusCallback {"Problem with the toggle prompt input.\nProblem with flush \$toggleID: $result"}
                }
                eval $statusCallback {"Waiting two seconds for injection reference file to load...\nNote there is no check that loading occured in time for injection"}
                after 2000
            }
            # Note that there is no check that the GuntoBoosterExt pulsed magnets 
            # are actually turned on. Wait 2 seconds?
            TogglePulsedMagnetEnables -location GuntoBoosterExt
            after 2000
            RestoreRF
            # this call may need to be updated. The "Mt:SRinjectMultiBO.VAL" PV doesn't seem to work form the script anymore.
            if [catch {SlowInjection -beamCurrent $beamCurrent \
                         -multiplet $multiplet \
                         -cycles $cycles \
                         -DCCTpause $DCCTpause \
                         -statusCallback $statusCallback} result] {
                return -code error "MeasureDynamicAperture: $result"
            }
            if $latticeMode {
                puts $toggleID "y"
                # at this point the program toggle would terminate by itself and 
                # close the file descriptor, so we don't need the flush command
                # I think.
               
		#it does need to flush (H. Shang)
                if [catch {flush $toggleID} result] {
                    eval $statusCallback {"Problem with the toggle prompt input.\nProblem with flush \$toggleID: $result"}
                }
                eval $statusCallback {"Waiting for experimental lattice to load back again."}
                after 2000
            }
        }
        if $reinjectMode {
            # kicker triggering is supposed to be turned off automatically
            # by the ioc when the procedure DoBunchTrainInjection exits.
            # turn off HV on all kickers
            # turn HV back on for the one being used.
            # removed IK5
            if {[catch {after 500
                APScavput -pend=10 -list=S:IK -list=1,2,3,4 -list=:OnBO=OFF} result] || \
                  [catch {after 500
                      APScavput -pend=10 -list=S:$kicker:OnBO=ON} result] } {
                return -code error "MeasureDynamicAperture: $result"
            }
            if $disableSeptum {
                if [catch {after 100; APScavput -pend=10 -list=Mt:Ddg3chan2.GATE=0} result] {
                    return -code error "MeasureDynamicAperture: $result"
                }
            }
            SetRFfrequency -offset $rfFrequencyOffset 
        }
        if $collectBPMs {
            #------ Arm FPGA (sometimes it does not connect to the channel):
            if [string length $statusCallback] {
                eval $statusCallback {"Arm FPGA"}
            }
            if [catch {ArmFPGA -armList "S1A S38A" -value 1 -statusCallback $statusCallback} result] {
                if [string length $statusCallback] {
                    eval $statusCallback {"ArmFPGA: $result"}
                    eval $statusCallback {"Continue the loop..."}
                }
            }
        }
        # change kicker setpoint
        if [string length $statusCallback] {
            eval $statusCallback {"Change kicker to desired setpoint."}
        }
        if [catch {after 100
            APScavput -pend=10 -list=S:${kicker}:VoltageSetSendAO=$kickerSetpoint} result] {
            return -code error "MeasureDynamicAperture: $result"
        }
        
        if {[string length $abortVariable] && [subst \$$abortVariable]} {
            if [catch {RestoreKicker -kicker $kicker
                RestoreRF } result] {
                return -code error "MeasureDynamicAperture: $result"
            }
            set $abortVariable 0
            break
        }
        
        if {[string length $abortVariable] && [subst \$$abortVariable]} {
            if [catch {RestoreKicker -kicker $kicker
                RestoreRF } result] {
                return -code error "MeasureDynamicAperture: $result"
            }
            set $abortVariable 0
            break
        }
        
        if [string length $statusCallback] {
            eval $statusCallback {"Step $step of $steps"}
        }
       
        # fire kickers (using trigger enable or injection script)
        # trigger single shot waveforms
        if [string length $statusCallback] {
            eval $statusCallback {"Fire kickers in mode $kickerMode"}
        }
        switch $kickerMode {
            "repeated" {
                # need to allow 1.0 second for the bpms history to
                # process before turning off kickers.
                if [catch {APScavput -pend=5 -list=Mt:Ddg -list=3chan4.GATE=Enabled
                    after 1000 } result]  {
                    return -code error "MeasureDynamicAperture: $result"
                }
            }
            "singleShot" {
                # use injection ioc process to trigger once only.
                # set current limit to 10 plus whatever the present value of 
                # current is. This is to make the pulsing unconditional.
                if ![file exists $output.raw] {
                    exec sddsmonitor $scalarMonFile $output.raw -steps=1 -comment=Comment,[APSMakeSafeQualifierString $comment]
                } else {
                    exec sddsmonitor $scalarMonFile $output.raw -steps=1 -append
                }
                if [catch {APScavput -pend=5 -list=Mt:SR -list=injectMaxCyclesAO=1,injectNumBunchesAO=1,bunch000=$desiredBucket,injectMultiExcludSIS1BO=0,injectCurrentLimitAO=[expr $beamCurrent + 10 ]\
                         } result]  {
                    return -code error "MeasureDynamicAperture: $result"
                }
                # I suspect that the injection timing 
                # ioc need time to respond to the above changes.
                after 500
                if [catch {APScavput -pend=5 -list=Mt:SRinjectMultiBO=1
                    set waitIntervals 2
                    after [expr 500 * $waitIntervals] } result]  {
                    return -code error "MeasureDynamicAperture: $result"
                }
                # wait some time for the DCCT measurement to settle.
                after [expr $DCCTpause * 1000]
                exec sddsmonitor $scalarMonFile $output.raw -steps=1 -append=topage
            }
        }
        if $collectBPMs {
            #------ Record FPGA motion history:
            after 1000
            eval $statusCallback {"record FPGA"}
            if [catch {exec sddswmonitor $outputDir/dynap.wmon $output.$step -scalars=$outputDir/dynap.mon -comment=Comment,[APSMakeSafeQualifierString $comment]} result] {
                eval $statusCallback {"Error recording FPGA waveforms: $result"}
                eval $statusCallback {"Repeat FPGA reading..."}
                file delete $output.$step
                after 1000
                if [catch {exec sddswmonitor $outputDir/dynap.wmon $output.$step -scalars=$outputDir/dynap.mon -comment=Comment,[APSMakeSafeQualifierString $comment]} result] {
                    eval $statusCallback {"Error recording FPGA waveforms again: $result"}
                }
            }
            lappend waveformFileList $output.$step
        }
    }
    # command sddswmonitor to stop
    
    if [catch {RestoreKicker -kicker $kicker} result] {
        eval $statusCallback {"$result"}
    }
    if [catch {RestoreRF} result] {
        eval $statusCallback {"$result"}
    }
    # thick and thin septum
    # kicker 
    catch {APScavput -pend=5 -list=Mt:Ddg3chan -list=0,1 -list=.GATE=Disabled}
    catch {APScavput -pend=5 -list=Mt:Ddg -list=3chan4.GATE=Disabled}
    catch {APScavput -pend=10 -list=S:IK -list=1,2,3,4 -list=:OnBO=ON}

    if [string length $statusCallback] {
        eval $statusCallback {"Postprocessing..."}
    }
    if {$collectBPMs && [info exist waveformList]} {
        eval exec sddscombine $waveformFileList $output.turnHist -overwrite
        eval file delete $waveformFileList
    }
    if [catch {PostProcess} result] {
        eval $statusCallback {"$result"}
    }
    if [string length $statusCallback] {
        eval $statusCallback {"Done."}
    }
}

proc ArmFPGA {args} {
    set armList "S1A S38A" 
    set value 0
    set statusCallback ""
    APSParseArguments {armList value statusCallback}
    set tryAgain 1
    set counter 0
    while {$tryAgain == 1} {
        if [catch {exec cavput -list=[join $armList ,] -list=:turn:gtr:arm=$value -pend=5} result] {
            if [string length $statusCallback] {
                eval $statusCallback {"Error arming"}
            }
            after 500
            catch {exec cavput -list=[join $armList ,] -list=:turn:gtr:arm=$value -pend=5}
        }
        after 1000
        set tryAgain 0
        set readbackList [exec cavget -list=[join $armList ,] -list=:TurnHistoryIsArmed -pend=5]
        foreach readback $readbackList {if {$readback != $value} {set tryAgain 1}}
        if {$counter > 20} {return -code error "Error: could not arm FPGAs after 20 tries."}
        incr counter
    }
}

proc CheckMonitorFile {args} {
    set filename ""
    set waveform 0
    APSParseArguments {filename waveform}
    if ![file exist $filename] {
        return -code error "$filename does not exist!"
    }
    if $waveform {
        set pvs [exec sdds2stream -col=WaveformPV $filename]
        set errorMsg ""
        foreach pv $pvs {
            catch {exec caget $pv} result
            if [regexp {Invalid channel name} $result] {
                append errorMsg "$result\n"
            }
        }
        if [string length $errorMsg] {
            return -code error "Channel access errors:\n$errorMsg"
        }
    } else {
        set tmpRoot /tmp/[APSTmpString]
        if [catch {exec sddscasr $filename  -save -pend=60 -pipe=out \
                     | sddsprocess -pipe=in $tmpRoot.error -match=col,CAError=y -nowarnings } result] {
            return -code error "Unable to read $filename: $result"
        }
        set rows [exec sdds2stream -rows=bar $tmpRoot.error]
        if $rows {
            if [catch {exec sddsprintout $tmpRoot.error $tmpRoot.print \
                         "-title=Invalid PVs found in $filename" \
                         -col=ControlName } result] {
                return -code error $result
            }
            APSFileDisplayWindow [APSUniqueName .] \
              -fileName $tmpRoot.print -deleteOnClose 1 -width 100 \
              -comment "Channel Access errors" \
              -printCommand "enscript -r"
            return -code error "Channel access errors in $filename."
        }
    }
}

proc KickTheBeamOut {args} {
    set kicker IK3
    if {[catch {after 100
        APScavput -pend=10 -list=S:$kicker:OnBO=OFF} result] || \
            [catch {after 100
                APScavput -pend=10 -list=Mt:Ddg -list=3chan4.GATE=Enabled} result] || \
            [catch {after 600
                APScavput -pend=10 -list=Mt:Ddg -list=3chan4.GATE=Disabled} result] || \
            [catch {after 100
                APScavput -pend=10 -list=S:$kicker:OnBO=ON} result] } {
        return -code error "KickTheBeamOut: $result"
    }
}

proc SlowInjection {args} {
    set multiplet 1
    set cycles 3
    set beamCurrent 1
    # conservative value
    set efficiency 1.0
    set DCCTpause 3
    set statusCallback ""
    APSParseArguments {beamCurrent multiplet cycles statusCallback efficiency DCCTpause}


    set S35DCCT [APScavget -list=S-DCCT:CurrentM]
    set firstPass 1
# check to see if we need injection
    while {[expr $S35DCCT < $beamCurrent]} {
        # if there was some remnant beam from a previous injection, then dump the beam and start from zero
        if $firstPass {
            if { $multiplet > 1} {
                if [string length $statusCallback] {
                    eval $statusCallback {"  With $multiplet multiplets, we need to fill from zero to get the right bunch pattern. Kick the beam out..."}
                } 
                # This always work. No worries here.
                KickTheBeamOut
            } else {
                # multiplet == 1
                set deficitCurrent [expr $beamCurrent - $S35DCCT]
                # the BTS is not guaranteed to transport a charge. Set the
                # charge to 0.5 nC
                set BTScharge 0.5
                set efficiency 0.9
                set cyclesBTSq [rpn expr "$deficitCurrent $BTScharge         0.271 * $efficiency * / int "]
                set cycles $cyclesBTSq
            }
            set firstPass 0
        } else {
            if { $multiplet > 1} {
                # for multiplets, if somehow we got through a second while loop, no more injection and simply return.
                return
            } else {
                # multiplet == 1
                # for subsequent passes, approach the target slower.
                set cycles 1
            }
        }
        # this should be re-examined. Some weeks we didn't get BTS charge.
        if [string length $statusCallback] {
            eval $statusCallback {"  Call: DoIOCBunchTrainInjection -start 0 -interval 1 -number $multiplet \
                     -stopAt [expr $beamCurrent + 0.2] -cycles $cycles"}
        }
        if [catch {DoIOCBunchTrainInjection -start 0 -interval 1 -number $multiplet \
                     -stopAt [expr $beamCurrent + 0.2] -cycles $cycles  \
                     -callback $statusCallback} result] {
            return -code error $result
        }
        if [string length $statusCallback] {
            eval $statusCallback {"  Wait $DCCTpause seconds for DCCT measurement to settle..."}
        }
        after [expr $DCCTpause * 1000]
        set S35DCCTold $S35DCCT
        set S35DCCT [APScavget -list=S-DCCT:CurrentM]
        if {[expr abs($S35DCCTold - $S35DCCT) < .01 ] || [expr $S35DCCT < $beamCurrent] } {
            if [string length $statusCallback] {
                eval $statusCallback {"  Not enough charge injected. Something wrong with injection. Look for the pop-up window."}
            }
            set answer [APSMultipleChoice .notEnough -question "Not enough charge injected. What would you like to do?" \
                            -labelList {"Manually inject and return to the measurement" "Abort -- call the whole thing off!"} \
                            -returnList {continue abort}]
            switch $answer {
                continue {
                    # I thought this break would break out of the bigger while loop, but it doens't.
                    # break
                    if [string length $statusCallback] {
                        eval $statusCallback {"  Continue with measurement..."}
                    }
                    return
                }
                abort {
                    if [string length $statusCallback] {
                        eval $statusCallback {"  Abort the injection..."}
                    }
                    global abortRun
                    set abortRun 1
                    return
                }
            }
        }
    }
}

proc SaveControllawState {args} {
    global controllawMode
    if [catch {APScavget -list=S:RC:OrbitControlLawXC,S:RC:OrbitControlLawYC,S:rfFreqControlLawRC -list=.SUSP -cavputForm} controllawMode] {
        return -code error "Problem getting orbit controllaw status: $controllawMode"
    }
}
proc RestoreControllawState {args} {
    global controllawMode
    if [catch {APScavput -list=$controllawMode} \
          result] {
        return -code error "RestoreControllawState: $result"
    }
}

proc SaveKicker {args} {
    global setpointOrig
    APSParseArguments {kicker}
    set setpointOrig [APScavget -pend=10 -list=S:$kicker:VoltageSetSendAO \
                           -pendIoTime=5]
    return
}

proc SaveRFfrequency {} {
    global rfFrequencyOrig
    set rfFrequencyOrig [APScavget -list=A014-IETS:BTC:SRSetFreqM \
                           -floatFormat=%.1f \
                           -pendIoTime=15]
    return
}

proc SetRFfrequency {args} {
    global rfFrequencyOrig
    APSParseArguments {offset}
    set PV A014-IETS:BTC:SRSetFreqM
    if {$offset==0} return
    set STT [exec rpnl "$offset 10 /"]
    for {set i 0} {$i < 10} {incr i} { 
        if [catch {APScavput -list=$PV=[expr $rfFrequencyOrig + $i * $STT] \
                     -pendIoTime=15 \
                 } result] {
            return -code error "SetRFfrequency: $result"
        }
        after 1000
    }
}

proc RestoreRF {args} {
    global rfFrequencyOrig
    set PV A014-IETS:BTC:SRSetFreqM
    if [catch {APScavput -pend=15 -list=$PV=$rfFrequencyOrig \
             } result] {
        return -code error "RestoreRF: $result"
    }
}

proc RestoreKicker {args} {
    global setpointOrig
    set kicker ""
    set setpoint ""
    APSParseArguments {kicker}
    if {![string length $kicker] } {
        return -code error "RestoreKicker: no value given to kicker or setpoint."
    }
    if {[catch {after 100
        # 3chan4 is SR kicker
        # 3chan2 is SR thick septum discharge
        APScavput -pend=10 -list=Mt:Ddg -list=3chan4.GATE=Disabled} result] || \
          [catch {after 100
              APScavput -pend=10 -list=S:${kicker}:VoltageSetSendAO=$setpointOrig} result] || \
          [catch {after 100
              APScavput -pend=10 -list=S:$kicker:OnBO=ON} result] || \
          [catch {after 100
              APScavput -pend=10 -list=Mt:Ddg3chan2.GATE=1} result]} {
        return -code error "RestoreKicker: $result"
    }
    # do it a second time. Some chance that the first one didn't take.
    if {[catch {after 500
        APScavput -pend=10 -list=S:${kicker}:VoltageSetSendAO=$setpointOrig} result]} {
        return -code error "RestoreKicker: $result"
    }
}

proc PostProcess {} {
    global kicker root index outputDir collectBPMs status

    cd $outputDir
    set file $root-[format %03ld $index].sdds.raw
    if ![file exists $file] {
        return -code error "PostProcess(0): File $file doesn't exist!"
    }
    set fileroot [file rootname $file]
    exec sddsprocess $file -pipe=out -ifnot=para,KickerUsed \
      -print=para,KickerUsed,S:$kicker \
      -process=S35DCCT,first,Current0 -process=S35DCCT,last,Current1 \
      -process=S:$kicker:Dac,last,KickerSetting \
      "-redef=para,Fraction,Current1 Current0 /" \
      | sddscollapse -pipe=in $root-[format %03ld $index].proc


    if $collectBPMs {
        if [catch {glob $root-[format %03ld $index].sdds.*} waveformFileList] {
            return -code error "PostProcess(1): $waveformFileList"
        }
        if [llength $waveformFileList] {
            eval exec sddscombine $waveformFileList $root-[format %03ld $index].turnHist -overwrite
        }
    }
    
    return
}

set index -1
set kicker IK3
set kickerMode repeated
set kickerMode singleShot
set start 3.0 
set end 4.5
set steps 16
set delta [expr 1.0 * ($end - $start) / ($steps - 1) ]
set stepMode linear
set outputDir .
set root dynap
set rfFrequencyOrig [APScavget -list=A014-IETS:BTC:SRSetFreqM -floatFormat=%.1f \
                       -pendIoTime=15]
set rfFrequencyOffset 0
set comment "first try"
set beamCurrent 0.3
set multiplet 6
set cycles 1
set reinjectMode 1
set latticeMode 0
set injectionLattice ""
set DCCTpause 5
set collectBPMs 0
set args $argv

APSStrictParseArguments {kicker kickerMode start end delta root index \
                           output \
                           comment \
                           beamCurrent multiplet cycles reinjectMode DCCTpause collectBPMs}

APSApplication . -name SRDynamicAperture -version $CVSRevisionAuthor \
  -overview {}
set status Working
APSScrolledStatus .status -parent .userFrame -textVariable status -width 80 -height 10
MakeInputFrame .input -parent .userFrame
set status Ready
set abortRun 0
update

APSButton .run -parent .userFrame -text Run -command \
    { \
      APSDisableButton .userFrame.run.button 
      APSEnableButton .userFrame.stop.button 
        catch {MeasureDynamicAperture -outputDir $outputDir -root $root \
                 -kicker $kicker -kickerMode $kickerMode \
                 -start $start -end $end \
                 -steps $steps -stepMode $stepMode \
                 -rfFrequencyOffset $rfFrequencyOffset \
                 -comment $comment \
                 -beamCurrent $beamCurrent -reinjectMode $reinjectMode \
                 -multiplet $multiplet -cycles $cycles \
                 -latticeMode $latticeMode -injectionLattice $injectionLattice \
                 -DCCTpause $DCCTpause \
                 -rfFrequencyOffset $rfFrequencyOffset \
                 -collectBPMs $collectBPMs \
                 -statusCallback "APSSetVarAndUpdate status" \
                 -abortVariable abortRun} status
        update idletasks
        APSDisableButton .userFrame.stop.button 
        APSEnableButton .userFrame.run.button }

APSButton .stop -parent .userFrame -text Stop -command \
    { \
	  set abortRun 1
	APSDisableButton .userFrame.stop.button
	APSEnableButton .userFrame.run.button } \
    -contextHelp "Aborts whole run."

APSButton .abort -parent .userFrame -text "ABORT INJECTION" -command AbortInjection \
  -contextHelp "Aborts injection of a bunch train. Active only during injection."

APSButton .postProcess -parent .userFrame -text "Postprocess" \
  -command PostProcess \
  -contextHelp "Does the post-processing that occurs at the end of the scan. Use this if the scan was interrupted."

APSButton .plotDA -parent .userFrame -text "Plot DA scan" \
  -command PlotDAScan \
  -contextHelp "Process current data assuming that single shot mode was selected. The result is the beam loss fraction as a function of kicker strength."

APSButton .plotMotion -parent .userFrame -text "Plot beam motion" \
  -command PlotBeamMotion \
  -contextHelp "Plots beam motion recorded by S38A bpms."

proc PlotDAScan {args} {
    global root index outputDir
    if [catch {PostProcess} result ] {
        APSSetVarAndUpdate status $result
        return -code error $result
    }
    exec sddsplot $root-[format %03ld $index].proc -col=KickerSetting,Fraction -graph=sym,con &
}

proc PlotBeamMotion {args} {
    global root index outputDir
    exec sddsplot -col=Index,S38A:P2:turnHistorySmall:Xposition $root-[format %03ld $index].sdds.turnHist "-topline=X motion" -split=page -sep &
    exec sddsplot -col=Index,S38A:P4:turnHistorySmall:Yposition $root-[format %03ld $index].sdds.turnHist "-topline=Y motion" -split=page -sep &
    exec sddsplot -col=Index,S38A:P2:turnHistorySmall:Xsum $root-[format %03ld $index].sdds.turnHist "-topline=X sum signal" -split=page -sep &
    exec sddsplot -col=Index,S38A:P4:turnHistorySmall:Ysum $root-[format %03ld $index].sdds.turnHist "-topline=Y sum signal" -split=page -sep &
    exec sddsplot -col=Index,S1A:P2:turnHistorySmall:Xposition $root-[format %03ld $index].sdds.turnHist "-topline=X motion" -split=page -sep &
    exec sddsplot -col=Index,S1A:P4:turnHistorySmall:Yposition $root-[format %03ld $index].sdds.turnHist "-topline=Y motion" -split=page -sep &
    exec sddsplot -col=Index,S1A:P2:turnHistorySmall:Xsum $root-[format %03ld $index].sdds.turnHist "-topline=X sum signal" -split=page -sep &
    exec sddsplot -col=Index,S1A:P4:turnHistorySmall:Ysum $root-[format %03ld $index].sdds.turnHist "-topline=Y sum signal" -split=page -sep &
}

APSInfoWindow .info -name "Operational Recommendations" -infoMessage "Be aware of the present scraper positions!!!\n\nUsually, all scrapers are retracted except S37 horizontal scraper. S37 horizontal scraper can actually affect the DA measurement. It is recommended to retract it. If you do retract it, don't forget to return it back after the measurements." -modal 1

# Local Variables:
# mode: tcl
# End:
