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

set auto_path [linsert $auto_path 0 /usr/local/oag/apps/lib/$env(HOST_ARCH)]
set auto_path [linsert $auto_path 0 /usr/local/oag/lib_patch/$env(HOST_ARCH)]
APSDebugPath


set CVSRevisionAuthor "\$Revision: 1.286 $ \$Author: sajaev $"

APSApplication . -name SRBunchTrain -version $CVSRevisionAuthor \
  -overview {This application provides multibunch injection into the storage ring.  It injects trains of \
               bunches starting at a specified bucket number with a specified interval between buckets.  Normally, the storage ring\
               kicker discharge pulse is disabled while changing buckets.  However, fast mode may be used to disable this feature.}

set multiplicity 1
set multiplets 200
set multipletStart 72
set multipletSpacing 2
set multipletPattern ""
set gapLimit 55
set bpmGroupCurrent 10
set bpmGroupBuckets 6
set bpmGroupMode First
set pauseAfterBPMGroup 1
set IOCStopMode Estop
set softwareMode IOC
set currentStop 102
set cycles 1000

proc MakeBunchTrainWidget {widget args} {
    set parent ""
    APSStrictParseArguments {parent}
 
    global TimingModeMessage
    APSFrame $widget -parent $parent -label "Bunch Train Setup" \
      -relief flat \
      -contextHelp "Sets parameters for bunch train injection.  The default parameters give 10mA in a BPM-group of 6 bunches, plus 200 singlets spaced by 2 buckets for a total of 102mA."

    set w $parent$widget.frame

    global currentStop dwell cycles multiplicity multiplets multipletStart multipletSpacing 
    global bpmGroupCurrent bpmGroupBuckets bpmGroupMode bpmGroupSum bpmGroupSumUse gapLimit
    global multipletPattern
    global presetPatternList

    APSFrame .f1 -parent $w -label "" -relief flat
    set w1 $w.f1.frame
    set widgetList [APSTabFrame .patterns -parent $w1 -label "" \
                      -labelList {"Presets/Multiplets" "Limits" "BPM group"} \
                      -width 500 -height 200 ]
    grid $w1.patterns -column 0 -row 1 -sticky w -columnspan 2
    $w1.patterns.frame.tn select 0
    set windex 0
    set tabwidget [lindex $widgetList $windex]
    incr windex

    APSComboboxFrame .preset -parent $tabwidget -label Presets \
      -gridPack "-column 0 -row 0 -sticky w -columnspan 2" \
      -textVariable bunchPattern \
      -itemList $presetPatternList \
      -width 44 \
      -editable 0 \
      -callback "SetPresetBunchPattern -index" \
      -contextHelp "Select a preset bunch pattern for setting the values of the tcl variables that are then used to set up the injection ioc."

    APSLabeledEntry .number -parent $tabwidget -label "Number: " -width 6 -textVariable multiplets \
      -contextHelp "The number of multiplets to inject in addition to the group of consecutive bunches for the BPMs." -gridPack "-column 0 -row 1 -sticky e" 
    
    APSLabeledEntry .multiplicity -parent $tabwidget -label "Multiplicity: " -width 6 \
      -textVariable multiplicity -contextHelp \
      "The number of buckets in each multiplet.  For example, entering 2 gives a train of doublets.  Ignored if the Pattern is given." \
      -gridPack "-column 1 -row 1 -sticky e"
    
    APSLabeledEntry .multipletStart -parent $tabwidget -label "Start: " \
      -width 6 -textVariable multipletStart \
      -contextHelp "The starting bucket for the series of multiplets." \
      -gridPack "-column 0 -row 2 -sticky e"
    APSLabeledEntry .multipletSpacing -parent $tabwidget -label "Interval: " \
      -width 6 -textVariable multipletSpacing \
      -contextHelp "The interval between multiplets to be filled. May be any positive integer." \
      -gridPack "-column 1 -row 2 -sticky e"

    APSLabeledEntry .multipletPattern -parent $tabwidget -label "Pattern: " \
      -width 40 -textVariable multipletPattern \
      -contextHelp "An optional pattern for the multiplet train.  If this entry is given, then the Multiplicity factor is ignored.  For example, a pattern of 10101 would fill three buckets with an empty space between them." \
      -gridPack "-column 0 -row 3 -sticky e -columnspan 2"

    APSButton .genPattern -parent $tabwidget.multipletPattern \
      -size small -text G -command "GenerateMultipletPattern"
    
    # current limit
    set tabwidget [lindex $widgetList $windex]
    incr windex
    APSLabeledEntry .cycles -parent $tabwidget -label "Cycles:" \
      -width 6 -textVariable cycles \
      -contextHelp "The number of cycles to perform of injection of the number of buckets specified.  For example, to inject 10 times into a set of 5 buckets, use Number=5 and Cycles=10." \
      -gridPack "-column 0 -row 0 -sticky e"
    APSLabeledEntry .stop -parent $tabwidget -label "Stop (mA): " \
      -width 6 -textVariable currentStop \
      -contextHelp "The value in mA at which injection will be stopped.  The actual current will exceed this value due to time lags in reading the current and stopping injection." \
      -gridPack "-column 1 -row 0 -sticky e"
    APSLabeledEntry .gaplimit -parent $tabwidget -textVariable gapLimit \
      -label "Gap limit (mm): " -width 6 \
      -contextHelp "Minimum undulator gap that may exist and still start injection." \
      -gridPack "-column 0 -row 1 -sticky e"

    # BPM bunch train
    set tabwidget [lindex $widgetList $windex]
    incr windex
    APSRadioButtonFrame .bpmFirst -parent $tabwidget \
      -label "BPM-group:        " \
      -buttonList {First Last None} \
      -valueList {First Last None} -variable bpmGroupMode \
      -orientation horizontal -contextHelp \
      "Select whether to inject the BPM-group, and whether to do it or the multiplet train first.  The BPM-group is the group of consecutive bunches injected at bucket location 0, used to trigger the BPMs." \
      -gridPack "-column 0 -row 0 -sticky w -columnspan 2"
    APSLabeledEntry .bpmStop -parent $tabwidget -label "Current (mA): " \
      -width 6 -textVariable bpmGroupCurrent \
      -contextHelp \
      "The value in mA to fill for the group of bunches read by the BPMs.  Commonly, this is a group of 6 bunches.  This group always starts at bucket 0 location." \
      -gridPack "-column 0 -row 1 -sticky e"
    APSLabeledEntry .bpmSumStop -parent $tabwidget -label "BPM sum (DAC units): " \
      -width 6 -textVariable bpmGroupSum \
      -contextHelp \
      "The value of a bpm sum signal (S28B:P5:scdu:ave_sum) for the group of bunches read by the BPMs.  Commonly, this is a group of 6 bunches.  This group always starts at bucket 0 location." \
      -gridPack "-column 1 -row 1 -sticky e"
    APSLabeledEntry .bpmBunches -parent $tabwidget -label "Buckets: " -width 6 -textVariable \
      bpmGroupBuckets -contextHelp \
      "The number of buckets to fill for the group of bunches read by the BPMs.  Commonly, 6 buckets are filled.  This group always starts at bucket 0 location." \
      -gridPack "-column 0 -row 2 -sticky e"
    APSRadioButtonFrame .pause -parent $tabwidget -label "Pause after injecting: " \
      -buttonList {Yes No} -valueList {1 0} -variable pauseAfterBPMGroup \
      -orientation horizontal -contextHelp \
      "Select whether to pause injection after injecting the BPM group." \
      -gridPack "-column 0 -row 3 -sticky e -columnspan 2"
    if {0} {
        APSRadioButtonFrame .bpmGroupTopup -parent $tabwidget \
            -label "Use bpm sum signal: " \
            -buttonList {DCCT Sum BCM} -valueList {DCCT Sum BCM} -variable bpmGroupSumUse \
            -orientation horizontal -contextHelp \
            "Select whether to use the bpm sum signal to limit injection into the bpm group." \
            -gridPack "-column 0 -row 4 -sticky e -columnspan 2"
    }
#    MakeScraperWidget .scraper  -parent $parent$widget
    MakeInjectionOpsWidget .injops -parent $parent$widget
}

proc MakeScraperWidget {widget args} {
    set parent ""
    APSStrictParseArguments {parent}
    APSFrame $widget -parent $parent -label "Scraper In/Out Positions (cm)" 
    set w $parent$widget.frame
    global ScraperNameList ScraperPVList ScraperLVDTList ScraperStoppedList
    set ScraperNameList [list S36T S36B  S40H]
    set ScraperPVList [list S36:VTSC:SM:sm S36:VBSC:SM:sm \
                    S40:SCR2:SM:sm]
    set ScraperLVDTList [list S36:VTSC:EtoM:calcout S36:VBSC:EtoM:calcout  \
                    S40:SCR2:EtoM:calcout]
    set ScraperStoppedList [list S36:VTSC:SM:sm.DMOV S36:VBSC:SM:sm.DMOV \
                    S40:SCR2:SM:sm.DMOV]
    set scInList [list 2.76 1.000 3.90]

    set row 0
    set column 0
    foreach scraper $ScraperNameList PV $ScraperPVList LVDT $ScraperLVDTList InPos $scInList {
        global ${scraper}In ${scraper}Out
        set ${scraper}In $InPos
# move scraper by 1.5 cm
        set ${scraper}Out [expr $InPos-1.5]
        APSLabeledEntryFrame .x${scraper} -parent $w -width 6 \
          -orientation horizontal -gridPack "-column $column -row $row -sticky e" \
          -label "$scraper" \
          -variableList [list ${scraper}In ${scraper}Out]
        incr row
        if $row==2 {
            set row 0
            incr column
        }
    }
    global ToggleScrapers
    set ToggleScrapers 0
    APSRadioButtonFrame .rb -parent $w -orientation horizontal \
      -gridPack "-column $column -row $row -sticky e" \
      -label "Toggle: " -variable ToggleScrapers \
      -valueList {0 1} -buttonList {N Y} \
      -contextHelp "If Y(es) is selected, then scrapers are toggled between \
      the indicated positions.  They are inserted for injection and withdrawn \
      after injection."
    incr row
    APSButton .b1 -parent $w -text "Move In" -gridPack "-column 0 -row $row" \
      -command "DoToggleScrapers -position in -force 1"
    APSButton .b2 -parent $w -text "Move Out" -gridPack "-column 1 -row $row" \
      -command "DoToggleScrapers -position out -force 1"
    APSButton .b3 -parent $w -text "Stop" -gridPack "-column 2 -row $row" \
      -command "DoToggleScrapers -position stop -force 1"
}

proc SetPresetBunchPattern {args} {
    global currentStop dwell cycles multiplicity multiplets multipletStart multipletStartMode multipletSpacing
    global bpmGroupCurrent bpmGroupBuckets bpmGroupMode bpmGroupSum bpmGroupSumUse gapLimit
    global ToggleScrapers multipletPattern pauseAfterBPMGroup  bunchPatternLabel lifetimeFittingPoints

    global presetBunchPatternData apsTopupInterval apsTopupBunches

    set index ""
    APSStrictParseArguments {index}

    set bunchPatternLabel [lindex [lindex $presetBunchPatternData(Column.Label) 0] $index]
    setStatus "$bunchPatternLabel was selected."
    if [string match "*-----*" $bunchPatternLabel] {
	bell
        setStatus "Warning, you selected invalid pattern!"
    }
    set multiplicity        [lindex [lindex $presetBunchPatternData(Column.Multiplicity) 0] $index]
    set multiplets          [lindex [lindex $presetBunchPatternData(Column.Multiplets) 0] $index]
#  multipletStartMode must always follow multipletSpacing
#  because multipletSpacing is used in evaluating multipletStart
    set multipletSpacing    [lindex [lindex $presetBunchPatternData(Column.MultipletSpacing) 0] $index]
    set multipletStartMode  [lindex [lindex $presetBunchPatternData(Column.MultipletStartMode) 0] $index]
    switch -exact $multipletStartMode {
        Default {
            set multipletStart      [lindex [lindex $presetBunchPatternData(Column.MultipletStart) 0] $index]
            set gapLimit 55
        }
        LastInjected {
            if [catch {exec cavget -list=Mt:SRlastInjectedBunchM}  multipletStart] {
                return -code error "SetPreset: $multipletStart"
            }
            set multipletStart [expr $multipletStart + $multipletSpacing]
            if {[expr $multipletStart >= 1296]} {
                set multipletStart [expr $multipletStart - 1296]
            }
            set gapLimit 9
        }
    }
    set bpmGroupCurrent     [lindex [lindex $presetBunchPatternData(Column.BpmGroupCurrent) 0] $index]
    set bpmGroupSum         [lindex [lindex $presetBunchPatternData(Column.BpmGroupSum) 0] $index]
    set bpmGroupSumUse      [lindex [lindex $presetBunchPatternData(Column.BpmGroupSumUse) 0] $index]
    set bpmGroupBuckets     [lindex [lindex $presetBunchPatternData(Column.BpmGroupBuckets) 0] $index]
    set bpmGroupMode        [lindex [lindex $presetBunchPatternData(Column.BpmGroupMode) 0] $index]
    set pauseAfterBPMGroup  [lindex [lindex $presetBunchPatternData(Column.PauseAfterBPMGroup) 0] $index] 
    set currentStop         [lindex [lindex $presetBunchPatternData(Column.CurrentStop) 0] $index]
    set cycles              [lindex [lindex $presetBunchPatternData(Column.Cycles) 0] $index]
    set lifetimeFittingPoints [lindex [lindex $presetBunchPatternData(Column.LifetimeFittingPoints) 0] $index]
    set apsTopupInterval [lindex [lindex $presetBunchPatternData(Column.TopupInjectionInterval) 0] $index]
    set apsTopupBunches  [lindex [lindex $presetBunchPatternData(Column.TopupBunches) 0] $index]
    
}

proc MakeInjectionOpsWidget {widget args} {
    global apsTopupTarget apsTopupInterval apsTopupWarningTime apsTopupInjectorWarningTime apsTopupTolerance apsTopupBunches
    global apsTopupTclVarList
    global $apsTopupTclVarList

    set parent ""
    APSStrictParseArguments {parent}
    APSFrame $widget -parent $parent -label "" -relief flat
    set w0 $parent$widget.frame
    APSFrame .f1 -parent $w0 -label "" -relief flat -packOption "-side top -expand true -fill x"
    APSFrame .f2 -parent $w0 -label "" -relief flat -packOption "-side top -expand true -fill x"
    APSFrame .f3 -parent $w0 -label "" -relief flat -packOption "-side bottom -expand true -fill x"
    
    set w $w0.f1.frame
    APSButton .start -parent $w -text START -command \
      {AbortTopup;  StartKickerLogging -topup 0;
          APSMpSIS1Feedforward -action start;
          RunTimingModeInjection \
         -stop $currentStop -cycles $cycles -multiplicity $multiplicity \
         -multiplets $multiplets -multipletStart $multipletStart \
         -multipletStartMode $multipletStartMode \
         -multipletSpacing $multipletSpacing \
         -multipletPattern $multipletPattern \
         -bpmGroupMode $bpmGroupMode -bpmGroupBuckets $bpmGroupBuckets \
         -bpmGroupCurrent $bpmGroupCurrent \
         -bpmGroupSum $bpmGroupSum -bpmGroupSumUse $bpmGroupSumUse \
         -stopMode $IOCStopMode \
         -softwareMode $softwareMode -pauseAfterBPMGroup $pauseAfterBPMGroup \
         -gapLimit $gapLimit } \
      -contextHelp "Starts injection of a bunch train."
    APSButton .start1 -parent $w -text "START w/o\nCHECKS" -command \
      {AbortTopup; 
          APSMpSIS1Feedforward -action start;
          RunTimingModeInjection \
         -stop $currentStop -cycles $cycles -multiplicity $multiplicity \
         -multiplets $multiplets -multipletStart $multipletStart \
         -multipletStartMode $multipletStartMode \
         -multipletSpacing $multipletSpacing \
         -multipletPattern $multipletPattern \
         -bpmGroupMode $bpmGroupMode -bpmGroupBuckets $bpmGroupBuckets \
         -bpmGroupCurrent $bpmGroupCurrent \
         -bpmGroupSum $bpmGroupSum -bpmGroupSumUse $bpmGroupSumUse \
         -stopMode $IOCStopMode \
         -softwareMode $softwareMode -pauseAfterBPMGroup $pauseAfterBPMGroup \
         -gapLimit $gapLimit -noChecks 1} \
      -contextHelp "Starts injection of a bunch train, but doesn't check MPS, beam switch, etc..."
    APSButton .resume -parent $w -text RESUME -command \
      "set exitFromPause 1" \
      -contextHelp "Resumes injection when paused (in timing mode)."
    APSButton .abort -parent $w -text ABORT -command \
      "set exitFromPause 1; set  apsScopeWaveformAbort 1; AbortInjection; AbortKickerLogging" \
      -contextHelp "Aborts injection of a bunch train. Active only during injection."
    APSButton .dump -parent $w -text DUMP -command DumpBeam \
      -contextHelp "Resets the feedback system, opens the loops, then dumps beam."

    global opsButtonFrame opsButtonFrame2
    set opsButtonFrame $w
    APSDisableButton $opsButtonFrame.abort.button
    APSDisableButton $opsButtonFrame.resume.button
    APSFrame .restore -parent $w
    APSButton .restore -parent $w.restore.frame -text "Restore Start Buttons" \
      -size small -packOption "-side top" -command \
      {APSEnableButton $opsButtonFrame.start.button;
          APSEnableButton $opsButtonFrame.start1.button;
          APSDisableButton $opsButtonFrame.abort.button;
          APSEnableButton $opsButtonFrame2.level.button \
     } \
      -contextHelp "Restores the START buttons."
    APSButton .restoreResume -parent $w.restore.frame -text "Restore Resume Button" \
      -size small -command \
      {APSEnableButton $opsButtonFrame.resume.button} \
      -contextHelp "Restores the RESUME button."

    set opsButtonFrame2 $w0.f2.frame
    APSButton .check -parent $w0.f2.frame -text "CHECK PVs" \
      -command CheckPVConnections \
      -contextHelp "Checks PV connections.  Use if you get complaints of channel access connection problems."

    APSButton .level -parent $w0.f2.frame -text "LEVEL BUNCHES..." \
      -command LevelBunchCharge \
      -contextHelp "Levels the charge in bunches by injecting into only those buckets with less than the median amount of charge.  Optionally tops-up the BPM group separately.  Only injects into buckets that already have charge."

    
    APSButton .restart -parent $w0.f2.frame -text "RESTART\nFROM LAST" -command \
      {  APSMpSIS1Feedforward -action start;
          RunTimingModeInjection \
         -restart 1 \
         -stop $currentStop -cycles $cycles -multiplicity $multiplicity \
         -multiplets $multiplets -multipletStart $multipletStart \
         -multipletSpacing $multipletSpacing \
         -multipletPattern $multipletPattern \
         -bpmGroupMode $bpmGroupMode -bpmGroupBuckets $bpmGroupBuckets \
         -bpmGroupCurrent $bpmGroupCurrent \
         -bpmGroupSum $bpmGroupSum -bpmGroupSumUse $bpmGroupSumUse \
         -stopMode $IOCStopMode \
         -softwareMode $softwareMode -pauseAfterBPMGroup $pauseAfterBPMGroup \
         -gapLimit $gapLimit -noChecks 1} \
      -contextHelp "Restarts injection of a bunch train, picking up where it left off.  This will not work properly unless you used the ABORT button to stop injection.  N.B.: If you want to change parameters besides the target current, you must use the START method."
     APSButton .dumps -parent $w0.f2.frame -text "DUMP SLOWLY" \
      -command "DumpBeamSlowly" \
      -contextHelp "call /home/helios/SR/bin/dumpBeamSlowly to dump the beam."
    
   
    APSButton .restartkicker -parent $w0.f3.frame -text "RESTART\nINJ KICKER\nLOGGER" \
      -command "StartKickerLogging -topup 0" -contextHelp "kick logging is started with START injection button; if problem occurs with kicker logging, use this button to restart it."
    APSButton .abortkicker -parent $w0.f3.frame -text "ABORT\nINJ KICKER\nLOGGER" \
      -command "AbortKickerLogging" -contextHelp "abort kicker logging for topup and non-topup kickers"
    APSButton .updatep0 -parent $w0.f3.frame -text "Update P0Feedback\n Bunch Pattern" \
      -command "APSUpdateP0FBBunchPattern -statusCallback setStatus"
}


proc DumpBeamSlowly {args} {
    #check if there is any gap is closed, if one gap is close, abort the dump.
    #the reason for this check is that if the bplds are armed the orbit change during the script execution is enough
    # to reach a bpld limit quickly and the MPS will dump beam quickly
    set tmpFile /tmp/[APSTmpString]
    APSAddToTmpFileList -ID bunchtrain -fileList $tmpFile
    if [catch {exec sddscasr -save -pipe=out -num /home/helios/oagData/sr/gapScans/inputFiles/gapstatus.mon \
                 | sddsprocess -pipe=in -nowarnings $tmpFile -scan=col,Value,ValueString,%lf "-test=col,Value 0 >" } result] {
        return -code error "Error reading gap open/close status: $result"
    }
    set rows [exec sdds2stream -rows=bar $tmpFile]
    if $rows {
        set gapName [exec sdds2stream -col=GapName $tmpFile]
        setStatus "[join $gapName ,] gaps are closed, the dump beam slowly script will not run." 
        return
    }
    if [APSYesNoPopUp "Are you sure to dump beam slowy now?"] {
        APSExecLog .dumpSlowly -lineLimit 1024 -width 70 -unixCommand /home/helios/SR/bin/dumpBeamSlowly -name DumpBeamSlowly
    } else {
       setStatus "dump beam slowly was cancelled." 
    }
}

proc PauseInterleaving {args} {
   
    if [catch {exec cavput -list=LI:iLeavePauseBO=1 -pend=10 } result] {
        return -code error "Error pause interleaving: $result"
    }
    #load RG2 setttings
    if [catch {exec sddsprocess /home/helios/oagData/linac/configFiles/InterleavingControl.sdds -pipe=out \
                 -print=col,ValueString,%s,on \
                 | sddscasr -pipe=in -restore } result] {
        setStatus "Error restore RG2 settings: $result"
        setStatus "if you are not asdops or linac account, you do not have permission writing to some linac pvs. "
        return
    }
    setStatus "interleaving paused."
}

proc ResumeInterleaving {args} {
    global status
    APSAlertBox .[APSTmpString] -type warning -errorMessage "Warning: If you made changes to the linac, please update the values in LinacInterleavingControl application before resuming interleaving." 
    #resume interleaving
    if [catch {exec cavput -list=LI:iLeavePauseBO=0,L1:RG1:KIK:chargeTrigC=1,L1:RG2:KIK:chargeTrigC=1 -pend=10 } result] {
        return -code error "Error resume interleaving: $result"
    }
    
    if [catch {exec cavget -list=LI:iLeavePermitCC -pend=10 } interleaving] {
        return -code error "Error reading interleaving state: $interleaving"
    }
    if {$interleaving} {
        
    } else {

    }
    setStatus "Operator interleaving is resumed, however, the interleaving state also depends on the ascis interleaving state."
}

proc MakeTopupFrame {widget args} {
    APSParseArguments {parent}
    
    set w0 $parent$widget.frame
    APSFrame $widget -parent $parent -label "Topup Injection Controls" \
      -contextHelp "Setpoint and readback values for various injection-related PVs. One enters setpoints values in the right-hand boxes, and press the APPLY button to send all the values to EPICS. The readback boxes are then updated."

    APSFrame .entries -parent $w0 -relief flat 
    APSFrame .commands -parent $w0 -relief flat 
    APSFrame .checking -parent $w0 -relief flat 

    set stringFormat "%-35s" 
    set entry2option "-state disabled -relief ridge -borderwidth 2"
    set w $w0.entries.frame
    APSLabeledOutput .bunch -parent $w -label "Selected bunch pattern:" -textVariable bunchPatternLabel -width 35
    APSLabeledEntryFrame .topupInterval -parent $w \
      -orientation horizontal \
      -variableList {apsTopupInterval apsTopupIntervalReadback} \
      -label [format $stringFormat "Top-up interval (s):"] \
      -width 10 \
      -contextHelp "Sample interval of stored current. Injection occurs at the sample time when the stored current is below the target value. Injection is skipped when the stored current is above the target value.\n\nIf a new value is entered, then it is in effect after the current time interval times out."
    eval $w.topupInterval.frame.entry2 config $entry2option
    $w.topupInterval.frame config -relief flat -bd 0

    APSLabeledEntryFrame .topupTarget -parent $w \
      -orientation horizontal \
      -variableList {apsTopupTarget apsTopupTargetReadback} \
      -label [format $stringFormat "Top-up target current (mA):"] \
      -width 10 \
      -contextHelp "Top-up target current. Injection occurs at the sample time when the stored current is below the target value. Injection is skipped when the stored current is above the target value."
    eval $w.topupTarget.frame.entry2 config $entry2option
    $w.topupTarget.frame config -relief flat -bd 0

    APSLabeledEntryFrame .topupBunches -parent $w \
      -variableList {apsTopupBunches apsTopupBunchesReadback} \
      -orientation horizontal \
      -label [format $stringFormat "Bunches to inject:"] \
      -width 10 \
      -contextHelp "Top-up bunches to inject. This number of bunches will be injected into the SR as needed. Normally set to one. If set to greater than one, then the first pulse will not be effective.\n\nThe readback may be changed during non-topup mode of injection."
    eval $w.topupBunches.frame.entry2 config $entry2option
    $w.topupBunches.frame config -relief flat -bd 0

    APSLabeledEntryFrame .topupTolerance -parent $w \
      -variableList {apsTopupTolerance apsTopupToleranceReadback} \
      -orientation horizontal \
      -label [format $stringFormat "Top-up tolerance current (mA):"] \
      -width 10 \
      -contextHelp "Defines a range of values centered on the target current value outside of which the Mt:TopupOutOfRange pv will alarm. Minor alarm when the stored current is too high and major alarm when the stored surrent is too low."
    eval $w.topupTolerance.frame.entry2 config $entry2option
    $w.topupTolerance.frame config -relief flat -bd 0

    APSLabeledEntryFrame .topupWarning -parent $w \
      -variableList {apsTopupWarningTime apsTopupWarningTimeReadback} \
      -orientation horizontal \
      -label [format $stringFormat "Top-up warning Time (s):"] \
      -width 10 \
      -contextHelp "Warning time to preceed the actual injection event. Normally for the Users' benefit."
    eval $w.topupWarning.frame.entry2 config $entry2option
    $w.topupWarning.frame config -relief flat -bd 0

    APSLabeledEntryFrame .injectorWarning -parent $w \
      -variableList {apsTopupInjectorWarningTime apsTopupInjectorWarningTimeReadback} \
      -orientation horizontal \
      -label [format $stringFormat "Injector warning Time (s):"] \
      -width 10 \
      -contextHelp "Warning time for injectors before the actual injection event. A normal value of 20 seconds would allow for:\n1) turning on the RF Gun kicker,\n2) toggling the pulsed magnets, and\n3) calculating the bunch pattern deficits."
    eval $w.injectorWarning.frame.entry2 config $entry2option
    $w.injectorWarning.frame config -relief flat -bd 0

    APSLabeledEntryFrame .linacBunches -parent $w \
      -orientation horizontal \
      -variableList {apsLinacBunches apsLinacBunchesReadback}\
      -label [format $stringFormat "Set linac bunches:"] \
      -width 10 \
      -contextHelp "Number of linac bunches per injection cycles."
    eval $w.linacBunches.frame.entry2 config $entry2option
    $w.linacBunches.frame config -relief flat -bd 0
    
    set w $w0.commands.frame
    APSButton .applySetpoints -parent $w -text "APPLY\nVALUES" \
      -command "ApplyTopupSetpoints" \
       -contextHelp "Applies topup-related setpoints from the entry boxes above. Pressing the START button DOES NOT apply the setpoints."
    APSButton .refreshSetpoints -parent $w -text "REFRESH\nVALUES" \
      -command "RefreshTopupSetpoints" \
       -contextHelp "Reads topup-related setpoints into the entry 9setpoint) boxes above."

    APSButton .startTopup -parent $w -text "START" \
      -command {StartTopupInjection; APSMpSIS1Feedforward -action start; StartKickerLogging -topup 1} \
      -contextHelp "Starts topup injection mode. The ioc mtime will execute the multi-bunch inject procedure at a specified interval until a abort command is sent. Also an exec log window will pop-up which runs a script to monitor the bucket pattern continuously and calculate the bucket positions to inject into."

    APSButton .abortTopup -parent $w  -text "ABORT" \
      -command {AbortTopup;AbortKickerLogging} \
      -contextHelp "The ioc mtime will stop topup injection. This button is the preferred way to stop top-up injection. This there are some internal tcl variables that are reset. it is important to press the abort topup before starting again."
    APSButton .restartKicker -parent $w -text "RESTART\nTOPUP KICKER\nLOGGER" \
      -command "StartKickerLogging -topup 1" -contextHelp "kick logging is started with START topup button; if problem occurs with kicker logging, use this button to restart it."
    APSButton .abortkickerLogging -parent $w -text "ABORT\n\TOPUP KICKER\nLOGGER" -command "AbortKickerLogging" \
      -contextHelp "abort kicker logging for topup and non-topup kickers"
    set w $w0.checking.frame
    APSButton .readPattern -parent $w -text "MAKE TARGET\nPATTERN" \
      -command "MakeTargetBunchPattern" \
      -contextHelp "Reads bunch pattern from PV Mt:S:FillPatternWF and creates an idealized bunch pattern which will be used as the target bunch pattern.\n\nPress this button after an initial fill.\n\nIf somehow the initial injection from 0 mA didn't visit every target bucket, then the target bunch pattern created will be not what is intended, and topup will miss some buckets."

    APSButton .readBunchPattern -parent $w -text "READ\nPATTERN" \
      -command ReadBunchPattern \
      -contextHelp "Read bunch current and calculates the charge deficit in every bucket.\n\nThis type of operation is done periodically when the START button is pressed."
    APSButton .plotChargeTopup -parent $w -text "PLOT\nPATTERN" \
      -command PlotBunchPattern \
      -contextHelp "Plots the charge deficit in every bucket for the bucket pattern read most recently."
   
    APSButton .pause -parent $w -text "PAUSE\nINTERLEAVING" -command "PauseInterleaving"
    APSButton .resume -parent $w -text "RESUME\nINTERLEAVING" -command  "ResumeInterleaving"

}

proc StartKickerLogging {args} {
    set topup 1
    APSParseArguments {rootname topup}
    
    global  bunchPattern kickerLogger
    if [catch {exec cavget -list=SR:UserOpsKickerLogRC.RUN } running] {
        return -code error $running
    }
    if $running {
        if [catch {exec cavget -list=SR:UserOpsKickerLogRC.DESC } desc1] {
            return -code error $desc1
        }
    } else {
        set desc1 ""
    }
    set started 0
    if $topup {
        set desc "Topup kicker logging."
        if {$kickerLogger=="kicker"} {
            set rootname SRScopeMeasTopUpKickers
        } else {
            set rootname $kickerLogger
            set desc "Topup [regsub "SRScopeMeasTopUp" $rootname ""] logging."
        }
        if [string match "Topup*" $desc1] {
            set started 1
        } 
    } else {
        set desc "Non-topup kicker logging."
        if {$kickerLogger=="kicker"} {
            set rootname SRScopeMeasInjKickers
        } else {
            set rootname $kickerLogger
            set desc "Non-topup [regsub "SRScopeMeasTopUp" $rootname ""] logging."
        }
        
        if [string match "Non-topup*" $desc1] {
            set started 1
        }
    }
    if !$started {
        catch {exec cavput -list=SR:UserOpsKickerLogRC.CLR=1 -pend=10}
        APSExecLog .kickerlogging -lineLimit 1024 -width 70 \
          -unixCommand "collectUserOpsKickerWaveforms -userDescription \"$desc\" -rootname $rootname -topup $topup" \
          -cancelCallback "AbortKickerLogging" \
          -abortCallback "AbortKickerLogging" -name "$desc"
        wm geometry .kickerlogging "+1500+5"
    }
}

proc AbortKickerLogging {} {
    if [catch {exec cavget -list=SR:UserOpsKickerLogRC.RUN -num -pend=10} running] {
        return -code error "Error abort kicker logging1: $result"
    }
    
    if {$running} {
        if [catch {exec cavput -list=SR:UserOpsKickerLogRC.ABRT=1 -pend=10} result] {
            return -code error "Error aborting kicker logging: $result"
        }
    }
}

set SRTopupBucketAssignmentPID ""
proc StartTopupInjection {args} {
    global patternDir bunchPatternLabel lifetimeFittingPoints env
    global S35BeamCurrent BTSCharge SRTopupBucketAssignmentPID 

    set allowPARcleaning 1
    APSParseArguments {allowPARcleaning}

    set startTime [clock seconds]
    set Time $startTime
    set oldTime $startTime

    set targetPatternFile $patternDir/target.$env(USER)
    set patternFile $patternDir/pattern
    set sortedPatternFile $patternDir/sorted
    
    if [catch {CheckBunchPattern -bunchPatternLabel $bunchPatternLabel} result] {
        setStatus "$result"
        return -code ok
    }
    set currentThreshold 0.1
    pv getw S35BeamCurrent
    if {$S35BeamCurrent < $currentThreshold} {
        setStatus "Beam current is too low for topup injection."
        return -code ok
    }
    if [catch {SaveTempWaveforms -suffix topupStart} result] {
        setStatus "$result"
        return -code error "Error in reading waveforms: $result"
    }
    if [catch {SetupTopup} result ] {
        return -code error "StartTopupInjection: $result"
    }

    setStatus "Topup mode starting"
    # set string PV SR:bunchPatternSO
    
    if [catch {exec cavput -list=SR:bunchPatternSO=$bunchPatternLabel} result ] {
        return -code error "RunPatternModeInjection: $result"
    }
    
#    if [catch {exec cavput -list=S-DCCT:LifetimeStdesPoints2FitC=$lifetimeFittingPoints} result ] {
#        return -code error "RunPatternModeInjection: $result"
#    }
    # for 324 bunch topup this line may be useless, but go ahead anyway.
    MakeTargetBunchPattern
    if [string length $SRTopupBucketAssignmentPID] {
        APSInfoWindow .[APSTmpString] -infoMessage "Process TopupBucketAssignment already running. Check that topup in not already enabled."
        setStatus "Topup mode aborted."
        return
    }
    # value of 1 is topup 2 is normal
    if [catch {exec cavput -list=S:IS1:OperationsModeAO=1} result ] {
        return -code error "StartTopupInjection: $result"
    }
    global env
    catch {exec logMessage -sourceId=SRTopupAudit -tag=User $env(USER) -tag=Host $env(HOST) \
             -tag=BunchPattern "$bunchPatternLabel" \
             -tag=Action Start }
    set SRTopupBucketAssignmentPID [APSExecLog .measureBunches \
                                      -lineLimit 2048 -width 70 \
      -name "SR Topup bunch current measurement" \
      -unixCommand "SRTopupBucketAssignment -allowPARcleaning $allowPARcleaning" \
      -callback "SaveTempWaveforms -suffix topupEnd" \
      -cancelCallback "AbortTopup" \
      -abortCallback "AbortTopup"]
    wm geometry .measureBunches "+600+5"
    if [catch {exec cavput -list=Mt:TopUpAutoEnableC=Enable \
             } result ] {
        return -code error "AbortTopup: $result"
    }
    return -code ok
}

proc AbortTopup {} {
    global SRTopupBucketAssignmentPID env bunchPatternLabel
    
    catch {exec logMessage -sourceId=SRTopupAudit -tag=User $env(USER) -tag=Host $env(HOST) \
             -tag=BunchPattern "$bunchPatternLabel" \
             -tag=Action Abort }
    if [string length $SRTopupBucketAssignmentPID] {
        APSExecLogAbort -id $SRTopupBucketAssignmentPID -destroy 1
        set SRTopupBucketAssignmentFID ""
    }
    if [catch {exec cavput -list=Mt:TopUpAutoEnableC=Disable \
             } result ] {
        return -code error "AbortTopup: $result"
    }
    # value of 1 is topup 2 is normal
    if [catch {exec cavput -list=S:IS1:OperationsModeAO=2} result ] {
        return -code error "AbortTopup: $result"
    }
    RestoreNonTopupState
    set SRTopupBucketAssignmentPID ""
    
    return -code ok
}

proc RestoreNonTopupState {} {
    global controllawMode 
    setStatus "Restoring non-topup state."
    if [string length $controllawMode] {
        if [catch {exec cavput -list=$controllawMode} result ] {
            return -code error "RestoreNonTopupState: $result"
        }
    }
    if [catch {exec cavput -list=Mt:SRinjectMultiExcludSIS1BO=0} result ] {
        return -code error "RestoreNonTopupState: $result"
    }
    return
}


proc PlotBunchPattern {} {
    global patternDir apsTopupTarget bpmGroupCurrent env
    set targetPatternFile $patternDir/target.$env(USER)
    set patternFile $patternDir/pattern
    set sortedPatternFile $patternDir/sorted
    if [file exists $patternFile] {
        if [catch {exec sddsplot $patternFile \
                     -filter=col,TargetCurrent,0,0,! -uns=y \
                     -col=Bucket,TargetCurrent -grap=impulse \
                     -col=Bucket,TargetCurrent -grap=sym,subtype=2,sca=2 -leg \
                     -col=Bucket,BunchCurrent -grap=impulse -newpanel \
                     -col=Bucket,BunchCurrent -grap=sym,subtype=2,sca=2  -leg \
                     -col=Bucket,CurrentDeficit -grap=impulse -newpanel \
                     -col=Bucket,CurrentDeficit -grap=sym,subtype=2,sca=2  -leg & \
                 } result] {
            return -code error "PlotBunchPattern: $result"
        }
    } else {
        setStatus "No pattern saved yet."
    }
    return
}

proc SetupTopup {} {
    global controllawMode
    if [catch {exec cavget \
                 -list=S:IS1:ControlLaw,BTS:ControlLawX,BTS:ControlLawY \
                 -list=RC.SUSP -cavputForm} \
          controllawMode] {
        return -code error "SetupTopup: $controllawMode"
    }
    if [catch {exec cavput -list=Mt:SRinjectMultiExcludSIS1BO=0 \
             } result ] {
        return -code error "SetupTopup: $result"
    }
    if 0 {
        # allow BTS correction now
        if [catch {exec cavput -list=S:IS1:ControlLaw,BTS:ControlLawX,BTS:ControlLawY \
                     -list=RC.SUSP=Suspend \
                 } result ] {
            return -code error "SetupTopup: $result"
        }
        after 1000
    }
    # make sure of beam switch state.
    if [catch {APSSRGetBeamSwitchState} switchState] {
        return -code error "SetupTopup: $switchState"
    }
    if [string compare $switchState dump]==0 {
        return -code error "SetupTopup: Error: beam is at the dump."
    }
    global apsBoosterEKexcluded
    pv getw apsBoosterEKexcluded
    if [string match $apsBoosterEKexcluded Include] {
        TogglePulsedMagnetEnables -location GuntoBoosterExtSpecial
    } else {
        TogglePulsedMagnetEnables -location GuntoBoosterExt
    }
    if [catch {SetupMultiBunchInject} result] {
        return  -code error "SetupTopup: $result"
    }
# removed since it takes fives seconds to complete
#    SetupBunchMonitor
    return -code ok
}

proc RefreshTopupSetpoints {} {
# this refreshes the left hand column with the 
# actual PV values.
    global apsTopupTarget apsTopupTolerance apsTopupInterval apsTopupWarningTime apsTopupInjectorWarningTime apsLinacBunches apsTopupBunches 
    global apsTopupTclVarList
    eval global $apsTopupTclVarList

    pv getw $$apsTopupTclVarList
    foreach variable $apsTopupTclVarList {
        regsub Readback $variable {} var1
        eval set $var1 $$variable
    }
    return -code ok
}

proc RefreshTopupReadbacks {} {
# this refreshed the <...>Readback variables with the
# PV values. Callback script for the umons on the topup
# PVs.
    global apsTopupTarget apsTopupTolerance apsTopupInterval apsTopupWarningTime apsTopupInjectorWarningTime apsLinacBunches apsTopupBunches
    global apsTopupTclVarList
    eval global $apsTopupTclVarList

    foreach variable $apsTopupTclVarList {
        eval set $variable $$variable
    }
    return -code ok
}

proc ApplyTopupSetpoints {} {
    global apsTopupTarget apsTopupTolerance apsTopupInterval apsTopupWarningTime apsTopupInjectorWarningTime apsLinacBunches apsTopupBunches
    if [catch {exec cavput -list=Mt:TopUp -list=CurrentTargetP=$apsTopupTarget,CurrentToleranceP=$apsTopupTolerance,IntervalP=$apsTopupInterval,WarnTimeP=$apsTopupWarningTime,WarnTime2InjectorP=$apsTopupInjectorWarningTime \
             } result ] {
        return -code error "ApplyTopupSetpoints: $result"
    }
    if [catch {exec cavput -pend=10 -list=Mt:SRinject \
                 -list=CurrentLimitAO=[expr $apsTopupTarget + 0.20],NumBunchesAO.VAL=$apsTopupBunches \
             } result] {
        return  -code error "ApplyTopupSetpoints: Error setting IOC parameters: $result"
    }
    if {$apsLinacBunches > 5} {
        if ![APSYesNoPopUp "The RF Gun will trip if you request more than 5 linac bunches per pulse.\n\nDo you still want to change the number of linac pulses to $apsLinacBunches?"] {
            # answer no
            setStatus "Setting linac pulses to maximum of 5."
            set apsLinacBunches 5
            return -code ok
        }
    }
    if {[tk_dialog .dialog "Warning" \
           "You are about to (possibly) change the linac bunch timing pattern. Do you want to continue?" \
           warning -1 continue-at-your-own-risk cancel]==1} {
        cd $oldDir
        return
    } else {   
        SetLinacBunches -number $apsLinacBunches
        return -code ok
    }
}

proc setupEfficiencyLog {} {
    global patternDir
    set logDataDir $patternDir
    set efficiencyLog $logDataDir/[exec date +%Y-%m%d-%H%M%S].elog
    if [catch {open $efficiencyLog w} effLogId] {
        return -code error "RunTopupInjection: $effLogId"
    }
    if [catch {puts $effLogId "SDDS1"
        puts $effLogId "&column name=Time type=double &end"
        puts $effLogId "&column name=S35DCCT type=double &end"
        puts $effLogId "&column name=S35DCCTPrev type=double &end"
        puts $effLogId "&column name=BTS:CM:q type=double &end"
        puts $effLogId "&column name=DeltaI type=double &end"
        puts $effLogId "&column name=Efficiency type=double &end"
        puts $effLogId "&column name=Bucket type=double &end"
        puts $effLogId "&data mode=ascii no_row_counts=1 &end" \
             } result ] {
        return -code error "RunTopupInjection: $result"
    }
    return $effLogId
}

proc SetupMultiBunchInject {} {
    global apsTopupTarget apsTopupTolerance apsTopupBunches
    # include SR thick septum in multi bunch inject.
    # set all bucket values to zero initially.
    if [catch {exec cavput -pend=10 -list=Mt:SR \
                 -list=injectCurrentLimitAO=[expr $apsTopupTarget + 0.20],injectMaxCyclesAO=1,injectModeBO=Single,CurrentLimitModeBO=Estop,injectNumBunchesAO.VAL=$apsTopupBunches \
             } result] {
        return  -code error "SetupMultiBunchInject: Error setting IOC parameters: $result"
    }
    if [catch {exec cavput \
                 -list=Mt:SRinjectMultiExcludSIS1BO=0,Mt:SRinjectZeroBucketsBO=1 \
             } result] {
        return  -code error "SetupMultiBunchInject: $result"
    }
    return
}

proc SaveTempWaveforms {args} {
    set suffix topup
    APSParseArguments {suffix}
    set tmpFile /tmp/[APSTmpString].$suffix
    setStatus "saving waveforms in $tmpFile ..."
    if [catch {exec sddswmonitor /home/helios/oagData/sr/bunchPatterns/topup.wmon $tmpFile \
                   -scalars=/home/helios/oagData/sr/bunchPatterns/topup.mon -step=1 } result] {
        return -code error $result
    }
    setStatus "done."
}

proc SetupBunchMonitor {} {
# set threshold to 32 counts
# set averaging to 48 
# Select bucket #0 for single bunch 
# "Fine tune"
# Calibrate
    if [catch {exec cavput -list=BNCHI:NumTurns2AveAO.VAL=48,BNCHI:ThresholdAO.VAL=-20,BNCHI:BunchISelectAO.VAL=0} result] {
        return  -code error "SetupBunchMonitor: $result"
    }
    if [catch {exec cavput -list=BNCHI:FineTuneTimingBO=1} result] {
        return  -code error "SetupBunchMonitor: $result"
    }
    after 5000
    if [catch {exec cavput -list=BNCHI:CalibrateBunchIBO=1} result] {
        return  -code error "SetupBunchMonitor: $result"
    }
    return
}
     
proc MakeTargetBunchPattern {} {
    global patternDir apsTopupTarget bpmGroupCurrent bpmGroupMode bpmGroupBuckets env
    set targetPatternFile $patternDir/target.$env(USER)
    file delete $targetPatternFile
    if [catch {APSSRMakeTargetFillPattern \
                 -targetCurrent $apsTopupTarget \
                 -BPMGroupCurrent $bpmGroupCurrent \
                 -BPMGroupLength $bpmGroupBuckets \
                 -BPMGroupMode $bpmGroupMode \
                 -output $targetPatternFile} result] {
        return -code error "MakeTargetBunchPattern: $result"
    }
    catch {exec chmod +666 $targetPatternFile}
    setStatus "Writing to file $targetPatternFile with -targetCurrent $apsTopupTarget \
                 -BPMGroupCurrent $bpmGroupCurrent \
                 -BPMGroupLength $bpmGroupBuckets \
                 -BPMGroupMode $bpmGroupMode.\nReady."
    return
}

proc ReadBunchPattern {} {
    global patternDir bpmGroupCurrent env
    set targetPatternFile $patternDir/target.$env(USER)
    set patternFile $patternDir/pattern
    file delete $patternFile
    if [catch {APSSRReadBunchCM -output $patternFile \
                 -average 1 -baselineCorrection 1 \
                 -targetFile $targetPatternFile} result] {
        return -code error "ReadBunchPattern: $result"
    }
    
    setStatus "Writing to file $patternFile.\nReady."
    return
}

proc DumpBeam {} {
    # Check if shutters are open
    if [catch {APSShutterPermitGiven} permit] {
            return -code error "DumpBeam: $result"
        }
    if $permit {
        APSInfoWindow .[APSTmpString] -infoMessage "Shutters are open. Shutters should be closed before dumping beam." -modal 1
        return
    }
    if {![APSMultipleChoice [APSUniqueName .] -question "Really? Dump beam?" \
            -labelList {Yes No} -returnList {1 0}]} {
        return
    }
    RTFBResetOpen
    after 1000
    exec cavput -list=S:MPS:dumpBeamSQ=1
    setStatus Dumped.
}

proc ControlGaps {args} {
    set mode open
    APSStrictParseArguments {mode}
    if {![APSMultipleChoice [APSUniqueName .] -question \
            "Really, $mode all gaps?" -name "AreYouSure?" \
            -labelList {Yes No} -returnList {1 0}]} {
        return
    }
    switch $mode {
        open {
            setStatus "Opening gaps..."
            if {[catch {APSIDOpenAllGaps} result]} {
                setStatus "Gap open commands sent."
                setStatus "Error: $result"
                bell
            } else {
                setStatus "Gap open commands sent."
            }
        }
        return {
            setStatus "Returning gaps..."
            if {[catch {APSIDReturnAllGaps} result]} {
                setStatus "Gap return commands sent."
                setStatus "Error: $result"
                bell
            } else {
                setStatus "Gap return commands sent."
            }
        }
    }
}

set bunchPatternLabel ""
proc RetimeP0IfAppropriate {} {
    if [catch {exec cavget -list=S-DCCT:CurrentM -repeat=number=4,pause=1,average} current] {
        return -code error "RetimeP0IfAppropriate: $current"
    }
    if [expr abs($current)>0.05] {
        return 0
    }
    if [catch {exec cavput -list=Mt:P0:resync_counters_bo=1} result] {
        return 0
    }
    #set P0 clock phase
    #do not do it for 324 singlets
    global bunchPatternLabel
    if [regexp {324} $bunchPatternLabel] {
        return 1
    }
    if [catch {exec cavget -list=S:P0FB:ClockFaultMI.B2 -printErrors -pend=20} p0Error] {
        return -code error "Error reading P0 FB status: $p0Error"
    }
    if !$p0Error {
        return 1
    }
    if {0} {
        if [catch {exec cavget -list=S:P0FB:pinger:P0select -printErrors -pend=20} phase] {
            return 0
            # return -code error "Error reading S:P0FB:pinger:P0select: phase"
        }
        switch $phase {
            0 {
                set otherPhase 180
            }
            180 {
                set otherPhase 0
            }
        }
        if [catch {exec cavput -list=S:P0FB:pinger:P0select=$otherPhase  -pend=20} p0Error] {
            return 0
            #return -code error "Error reading P0 FB status: $p0Error"
        }
    }
    if [catch {exec cavput -list=S:P0FB:ClockFaultClrLO=15 -pend=10 } result] {
        return 0
        #return -code error "Error clear P0 FB status: $result"
    }
    if [catch {exec cavget -list=S:P0FB:ClockFaultMI.B2 -printErrors -pend=20} p0Error] {
        return 0
      #  return -code error "Error reading P0 FB status: $p0Error"
    }
    if $p0Error {
        setStatus "Unable to clear P0 FB error!"
        return 0
        #return -code error "Unable to clear P0 FB error!"
    }
    
    return 1
}

proc RunTimingModeInjection {args} {
    global bunchTrainStatus S35BeamCurrent errorCode apsBPMGroupSum apsLinacBunches bunchPatternLabel lifetimeFittingPoints
    global errorCode
    set stop 0
    set cycles 1
    set multiplets 30
    set multiplicity 1
    set multipletStart 47
    set multipletStartMode Default
    set multipletSpacing 36
    set multipletPattern ""
    set bpmGroupCurrent 25
    set bpmGroupSum 1180
    set bpmGroupSumUse 0
    set bpmGroupBuckets 6
    set bpmGroupMode First
    set stopMode Estop
    set softwareMode IOC
    set pauseAfterBPMGroup 0
    set gapLimit 102
    set noChecks 0
    set manageLinacBunches 0
    set restart 0
    APSStrictParseArguments {stop cycles multiplicity multipletPattern \
                               multiplets multipletStart multipletStartMode \
                               multipletSpacing \
                               bpmGroupCurrent bpmGroupSum bpmGroupSumUse \
                               bpmGroupBuckets bpmGroupMode \
                               stopMode softwareMode pauseAfterBPMGroup \
                               gapLimit noChecks manageLinacBunches restart}

# abort topup in case topup is running.
    AbortTopup
    if {!$noChecks} {
        if {![DoPreinjectionChecks -gapLimit $gapLimit]} {
            return
        }

        if [catch {APSSRGetBeamSwitchState} switchState] {
            setStatus "Error: $switchState"
            return
        }
        if [string compare $switchState dump]==0 {
            setStatus "Error: beam is at the dump."
            return
        }
        # check bunch pattern selected and stored beam
        if [pv getw S35BeamCurrent] {
            setStatus "Error: $errorCode"
            return
        }
        if {$S35BeamCurrent> 0.05 && [string match $multipletStartMode Default]} {
            if ![APSYesNoPopUp "There is already $S35BeamCurrent stored in the ring and the preset bunch pattern appears to be one that is used for filling from 0 mA. The first bucket to inject into will be $multipletStart\n\nDo you want to continue injection?"] {
                setStatus "Injection discontinued."
                return
            }
        }
    }

    # set string PV SR:bunchPatternSO
    if [catch {exec cavput -list=SR:bunchPatternSO=$bunchPatternLabel} result ] {
        return -code error "RunPatternModeInjection: $result"
    }
#    if [catch {exec cavput -list=S-DCCT:LifetimeStdesPoints2FitC=$lifetimeFittingPoints} result ] {
#        return -code error "RunPatternModeInjection: $result"
#    }
    global InjectionAbort
    set InjectionAbort 0

    if {$multiplets && $multipletSpacing<$multiplicity} {
        setStatus "Error: mutliplets are too close together for multiplicity"
        return
    }
    if {[string length $multipletPattern]>$multipletSpacing} {
        setStatus "Error: the length of the multiplet pattern ([string length $multipletPattern]) is greater than the multiplet spacing ($multipletSpacing)"
        return 
    }
    if {[string compare $bpmGroupMode None] && \
          $multiplets && $multipletStart<$bpmGroupBuckets} {
        setStatus "Error: multiplets overlap with BPM group"
        return
    }
    if [string compare $bpmGroupMode None]==0 {
        set bpmGroupCurrent 0
    }
   
    set apsLinacBunches [GetLinacBunches]
    if !$restart {
        if {[string match $bpmGroupMode First]} {
            if [expr [pv getw S35BeamCurrent] || [pv getw apsBPMGroupSum] ] {
                setStatus "Error: $errorCode"
                return
            }
            set S35BeamCurrent $S35BeamCurrent
            set apsBPMGroupSum $apsBPMGroupSum
            set stopCurr $bpmGroupCurrent
            if {$bpmGroupSumUse=="BCM"} {
                set stopCurr [CheckBunchCurrentAndNumber]
            }
            if { [expr [string compare $bpmGroupSumUse "Sum"]!=0 && $S35BeamCurrent<$stopCurr] || \
                   [expr [string compare $bpmGroupSumUse "Sum"]==0 && $apsBPMGroupSum<$bpmGroupSum] } { 
                setStatus "Doing group of $bpmGroupBuckets..."
                if {$bpmGroupSumUse!="Sum"} {
                    # when not using bpm sum, the stop current is the bpmGroupCurrent
                    if ![RunPatternModeInjection -start 0 -interval 1 -number $bpmGroupBuckets \
                           -stopMode $stopMode -stop $stopCurr \
                           -stopAtSum $bpmGroupSum -useSum $bpmGroupSumUse \
                           -cycles $cycles \
                           -multiplicity 1 -softwareMode $softwareMode] {
                               # failed reach desired current (aborted or timed out).
                               TerminateInjection -manageLinacBunches $manageLinacBunches
                               return
                           }
                } else {
                    if $manageLinacBunches {
                        catch {SetLinacBunches -number $apsLinacBunches}
                    }
                    if {[catch {exec cavget -list=S:BPMOffsetAdjustmentsRC.SUSP} offsetAdjMode] || \
                          [catch {exec cavput -list=S:BPMOffsetAdjustmentsRC.SUSP=Suspend} result]
                    } {
                        setStatus {"Problem getting/setting offset adjustment mode."}
                        if $manageLinacBunches {
                            catch {SetLinacBunches -number $apsLinacBunches}
                        }
                        return 0
                    }
                    after 500
                    if $manageLinacBunches {
                        catch {SetLinacBunches -number $apsLinacBunches}
                    }
                    if [catch {exec cavput -blunderAhead=silently \
                                   -list=S:BPMOffsetAdjustmentsRC.SUSP=$offsetAdjMode\
                               } result] {
                        setStatus "Warning: problem with cavput: $result"
                    }
                    # when using bpm sum, the stop current is simply the stop variable
                    if ![RunPatternModeInjection -start 0 -interval 1 -number $bpmGroupBuckets \
                           -stopMode $stopMode -stop $stop \
                           -stopAtSum $bpmGroupSum -useSum $bpmGroupSumUse \
                           -cycles $cycles \
                           -multiplicity 1 -softwareMode $softwareMode] {
                               # failed reach desired current (aborted or timed out).
                               if $manageLinacBunches {
                                   catch {SetLinacBunches -number $apsLinacBunches}
                               }
                               if [catch {exec cavput -blunderAhead=silently \
                                              -list=S:BPMOffsetAdjustmentsRC.SUSP=$offsetAdjMode} result] {
                                   setStatus "Warning: problem with cavput: $result"
                               }
                               TerminateInjection -manageLinacBunches $manageLinacBunches
                               # check sum value now
                               if {$apsBPMGroupSum < $bpmGroupSum} {
                                   APSInfoWindow .[APSTmpString] -infoMessage "BPM sum value ($apsBPMGroupSum) less than target ($bpmGroupSum)." -modal 1
                               }
                               return
                           }
                    if {$manageLinacBunches && \
                          [catch {SetLinacBunches -number $apsLinacBunches} result]} {
                        setStatus "$result"
                        return 0
                    }
                    if {[catch \
                           {exec cavput -blunderAhead=silently \
                                -list=S:BPMOffsetAdjustmentsRC.SUSP=$offsetAdjMode} result]} {
                        setStatus "Problem with cavput: $result"
                        return 0
                    }
                }
            } else {
                if {$bpmGroupSumUse!="Sum" && $S35BeamCurrent > $bpmGroupCurrent} {
                    # not using bpm sum signal to fill bpm bunch train
                    if ![APSYesNoPopUp "There is already $S35BeamCurrent stored in the ring and the BPM Bunch train will probably not get any more charge.\n\nDo you want to continue injection?"] {
                        setStatus "Injection discontinued."
                        return
                    }
                }
                setStatus "Skipping group of $bpmGroupBuckets---"
                if {$bpmGroupSumUse!="Sum" && $S35BeamCurrent>$bpmGroupCurrent} {
                    setStatus "too much beam already ($S35BeamCurrent mA)."
                } else {
                    setStatus "too much beam already (sum: $apsBPMGroupSum)."
                }
                set pauseAfterBPMGroup 0
            }
            set limit1 $stop
        } else {
            set limit1 [expr $stop-$bpmGroupCurrent]
# Why is pauseAfterBPMGroup set to 0?
            set pauseAfterBPMGroup 0
        }
    } else {
        set limit1 $stop
        set pauseAfterBPMGroup 0
    }

    if {$limit1>0} {
        if [pv getw S35BeamCurrent] {
            setStatus "Error: $errorCode"
            TerminateInjection -manageLinacBunches $manageLinacBunches
            return
        } 
        set S35BeamCurrent $S35BeamCurrent
        if {$S35BeamCurrent<$limit1} {
            set start $multipletStart
            if $pauseAfterBPMGroup {
                global exitFromPause opsButtonFrame apsBunchTrainIS1Trig opsButtonFrame2
                set apsBunchTrainIS1Trig 1
                catch {pv putw apsBunchTrainIS1Trig}
                set exitFromPause 0
                setStatus "Injection paused.  Press RESUME to continue."
                APSEnableButton $opsButtonFrame.resume.button
                APSEnableButton $opsButtonFrame.abort.button
                APSDisableButton $opsButtonFrame.start.button
                APSDisableButton $opsButtonFrame.start1.button
                tkwait variable exitFromPause
                APSDisableButton $opsButtonFrame.resume.button
                if $InjectionAbort {
                    APSEnableButton $opsButtonFrame.start.button
                    APSEnableButton $opsButtonFrame.start1.button
                    APSDisableButton $opsButtonFrame.abort.button
                    setStatus "Injection aborted."
                    set apsBunchTrainIS1Trig 0
                    catch {pv putw apsBunchTrainIS1Trig}
                    TerminateInjection  -manageLinacBunches $manageLinacBunches
                    return
                }
            }
            if [catch {resetRFAGC} result] {
                setStatus "$result"
                return 0
            }
            exec resetMPS
            setStatus "Doing $multiplets multiplets..."
            set number $multiplets
            if ![RunPatternModeInjection -start $multipletStart -interval $multipletSpacing \
                    -number $multiplets -multiplicity $multiplicity -pattern $multipletPattern \
                    -stopMode $stopMode -restart $restart \
                    -stop $limit1 \
                    -cycles $cycles -softwareMode $softwareMode ] {
                TerminateInjection -manageLinacBunches $manageLinacBunches
                puts stdout "Terminated injection and return from procedure."
                return
            }
        } else {
            setStatus "Skipping multiplets---"
            setStatus "too much beam already ($S35BeamCurrent mA)."
        }

    }

    if !$restart {
        if {[string match $bpmGroupMode Last]} {
            if [pv getw S35BeamCurrent] {
                setStatus "Error: $errorCode"
                TerminateInjection -manageLinacBunches $manageLinacBunches
                return
            }
            set S35BeamCurrent $S35BeamCurrent
            puts stdout "S35BeamCurrent $S35BeamCurrent"
            puts stdout "stop $stop"
            if {$S35BeamCurrent<$stop} {
                setStatus "Doing group of $bpmGroupBuckets..."
                if {![RunPatternModeInjection -start 0 -interval 1 -number $bpmGroupBuckets \
                        -stop $stop -manageLinacBunches $manageLinacBunches \
                        -cycles $cycles -multiplicity 1 \
                        -softwareMode $softwareMode]} {
                    # failed reach desired current.
                    TerminateInjection
                    return
                }
            } else {
                setStatus "Skipping group of $bpmGroupBuckets---"
                setStatus "too much beam already ($S35BeamCurrent mA)."
            }
        }
    }
    TerminateInjection -manageLinacBunches $manageLinacBunches
    SetPulsedMagnetEnables -state 0 -location All
}

proc RunPatternModeInjection {args} {
    set start 0
    set restart 0
    set interval 1
    set number 1
    set stop 1
    set stopAtSum 0
    set useSum DCCT
    set cycles 1
    set multiplicity 1
    set pattern ""
    set groupsOf 0
    set stopMode Estop
    set softwareMode IOC 
    set manageLinacBunches 0
#    puts stderr "RunPatternModeInjection: $args"
    APSStrictParseArguments {start interval number stop stopAtSum useSum \
                               cycles multiplicity groupsOf stopMode \
                               softwareMode manageLinacBunches restart pattern}

    global bunchTrainStatus opsButtonFrame opsButtonFrame2 S35BeamCurrent
    
    APSEnableButton $opsButtonFrame.abort.button
    APSDisableButton $opsButtonFrame.start.button
    APSDisableButton $opsButtonFrame.start1.button
    
    # this code appears to be pointless...
    if [string compare $multiplicity Custom]==0 {
        set multiplicity0 $groupsOf
    } else {
        set multiplicity0 [string trim $multiplicity]
        if ![string length $multiplicity0] {
            setStatus "Number of buckets per group is invalid."
            update
            return 0
        }
    }
    if {$multiplicity0>$interval} {
        setStatus "The number of bunches in a multiplet is greater than the spacing."
        update
        return 0
    }
    # value of 1 is topup 2 is normal
    if [catch {exec cavput -list=S:IS1:OperationsModeAO=2} result ] {
        return -code error "RunPatternModeInjection: $result"
    }
    if {0} {
        if [catch {RetimeP0IfAppropriate} result] {
            setStatus "$result"
            return 0
        }
        if $result {
            setStatus "P0 retimed."
        } else {
            setStatus "P0 not retimed---current over threshold."
        }
    }
    setStatus "Reseting the RF AGC..."
    if [catch {resetRFAGC} result ] {
        setStatus "$result"
        return 0
    }
    setStatus "Doing fill."
    setStatus "Reseting pulsed magnet timeouts..."
    global apsBoosterEKexcluded
    pv getw apsBoosterEKexcluded
    if [string match $apsBoosterEKexcluded Include] {
        TogglePulsedMagnetEnables -location GuntoBoosterExtSpecial
    } else {
        TogglePulsedMagnetEnables -location GuntoBoosterExt
    }
    TogglePulsedMagnetEnables -location GuntoBoosterExt
    setStatus "Injecting...."
    if [string compare $softwareMode IOC]==0 {
        if !$restart {
            if [catch {DoIOCBunchTrainInjection -start $start -interval $interval -number $number \
                         -callback SetBunchTrainStatus -stopAt $stop  -stopAtSum $stopAtSum \
                         -useSum $useSum -cycles $cycles \
                         -multiplet $multiplicity0 -pattern $pattern -stopMode $stopMode \
                     } returnValue] {
                setStatus "$returnValue"
                return 0
            }
        } else {
            if [catch {RestartIOCBunchTrainInjection -stopAt $stop -stopAtSum $stopAtSum \
                     -useSum $useSum  \
                     -callback SetBunchTrainStatus  } returnValue] {
                setStatus "$returnValue"
                return 0
            }
        }
    } else {
        if $restart {
            setStatus "Restarts not supported except in IOC mode injection"
            return 0
        }
        if [string length $pattern] {
            setStatus "Multiplet pattern entry not supported except in IOC mode injection"
            return 0
        }
        set returnValue \
          [DoBunchTrainInjection -start $start -interval $interval -number $number \
             -callback SetBunchTrainStatus -stopAt $stop -stopAtSum $stopAtSum \
             -useSum $useSum -cycles $cycles \
             -multiplet $multiplicity0 -stopMode $stopMode -fast 1 -dwell 2 ]
    }
    
    if [string compare $returnValue 1]==0 {
        setStatus "Done."
    } elseif [string compare $returnValue 0]==0 {
        setStatus "Aborted."
        TerminateInjection -manageLinacBunches $manageLinacBunches
    } else {
        set returnValue 1
    }
 
    setStatus "Disabling pulsed magnets..."
    SetPulsedMagnetEnables -state 0 -location All

    APSDisableButton $opsButtonFrame.abort.button
    APSDisableButton $opsButtonFrame.resume.button
    APSEnableButton $opsButtonFrame.start.button
    APSEnableButton $opsButtonFrame.start1.button

    setStatus "Done."
    return $returnValue
}


proc setStatus {text} {
    APSSetVarAndUpdate bunchTrainStatus "[exec date +%H:%M:%S] $text"
}

proc SetBunchTrainStatus { text } {
    APSSetVarAndUpdate bunchTrainStatus "[exec date +%H:%M:%S] Injecting....$text"
}

set snapDir /home/helios/oagData/SCR/snapshots/SR

proc MakeInjectionControlWidget {widget args} {
    set parent ""
    APSStrictParseArguments {parent}

    APSFrame $widget -parent $parent -label "" -relief flat
    set widgetList [APSTabFrame .controls -parent $parent$widget.frame \
                      -label "Controls" \
                      -labelList {"BTS:BX" "ID\nGaps" "Kickers &\nSeptum" "Related\nScreens" "Momentary\nCharge"} \
                      -width 400 -height 176 ]
    pack $parent$widget.frame.controls 
    $parent$widget.frame.controls.frame.tn select 0
    set windex 0

    set tabwidget [lindex $widgetList $windex]
    incr windex
    APSFrame .f -parent $tabwidget -label "BTS:BX"
    APSButton .toBeamDump -parent $tabwidget.f.frame -text "GO TO DUMP" \
      -command "switchRingAndDump dump" \
      -contextHelp "Sets the BTS:BX dipole to 315A and BTS:AB to 0, directing the beam to the BTX dump." 
    APSButton .toRing -parent $tabwidget.f.frame -text "GO TO RING" \
      -command "switchRingAndDump ring" \
      -contextHelp "Sets the BTS:BX dipole to 0A and BTS:AB to 319.05A directing the beam to the storage ring." 

    set tabwidget [lindex $widgetList $windex]
    incr windex
    APSFrame .f -parent $tabwidget -label "Gap Control"
    APSButton .openGaps -parent $tabwidget.f.frame -text "Open" -command \
      "ControlGaps -mode open" \
      -contextHelp "Opens all undulator gaps.  Use before filling."
    APSButton .returnGaps -parent $tabwidget.f.frame -text "Return" -command \
      "ControlGaps -mode return" \
      -contextHelp "Returns all undulator gaps to their operating values.  Use after filling."

    set tabwidget [lindex $widgetList $windex]
    incr windex
    APSFrame .f -parent $tabwidget -label "SR Kickers"
    APSButton .kickWarm -parent $tabwidget.f.frame -text "KICKER WARMUP"  \
      -command "DoKickerWarmup" \
      -contextHelp "Warms up all of the kickers for the storage ring." 
    APSButton .hvon -parent $tabwidget.f.frame -text "HV ON" \
      -command "exec SRHVset 1" \
      -contextHelp "Turns on the high voltage for storage ring kickers IK1 through IK4." 
    APSButton .hvoff -parent $tabwidget.f.frame -text "HV OFF" \
      -command "exec SRHVset 0" \
      -contextHelp "Turns off the high voltage for storage ring kickers IK1 through IK4." 

    APSFrame .f1 -parent $tabwidget -label "SR Septum Startup"
    APSButton .startup0 -parent $tabwidget.f1.frame -text "IS1" \
      -command "DoSeptumStartup IS1" \
      -contextHelp "Executes startup procedure for IS1.  Note that it is best to start up only one septum at a time.  Use the BOTH button to do the septa sequentially."
    APSButton .startup1 -parent $tabwidget.f1.frame -text "IS2" \
      -command "DoSeptumStartup IS2" \
      -contextHelp "Executes startup procedure for IS2.  Note that it is best to start up only one septum at a time. Use the BOTH button to do the septa sequentially."
    APSButton .startup2 -parent $tabwidget.f1.frame -text "BOTH" \
      -command "DoSeptumStartup both" \
      -contextHelp "Executes startup procedure for IS1 then IS2."

    APSFrame .f2 -parent $tabwidget -label "SR Kickers and Septa" 
    APSButton .chargeOn -parent $tabwidget.f2.frame -text "CHARGE ON" \
      -command "exec SRChargeSet 1" \
      -contextHelp "Turns on the charge pulses for storage ring kickers and septa." 
    APSButton .chargeOff -parent $tabwidget.f2.frame -text "CHARGE OFF" \
      -command "exec SRChargeSet 0" \
      -contextHelp "Turns off the charge pulses for storage ring kickers and septa." 
    
    global kickerLogger
    set kickerLogger kicker
    APSRadioButtonFrame .logger -parent $parent -label "Choose kicker/IS for logging during injection:" -orientation horizontal \
        -valueList {kicker SRScopeMeasTopUpIS1 SRScopeMeasTopUpIS2} -buttonList {Kicker IS1 IS2} -variable kickerLogger
    
    set tabwidget [lindex $widgetList $windex]
    incr windex
    APSFrame .f -parent $tabwidget -label "Related Screens"
    APSButton .resetTimeouts -parent $tabwidget.f.frame \
      -text "PULSED MAG RESETS..." \
      -packOption "-side top" \
      -command "exec timingResets & " \
      -contextHelp "Brings up an application for pulsed magnet charge enable control."

    APSButton .fillmon -parent $tabwidget.f.frame \
      -text "FILL MONITOR..." \
      -packOption "-side top" \
      -command {exec FillMonitor &; exec medm -x ./srbpm/miscApp/injEfficiency.adl /home/helios/SR/op/adl/BPMBunchTrainIMonitor.adl >& /dev/null &}  \
      -contextHelp "Brings up the fill monitor, showing charge, current, and delta current in the injector and ring."

    APSButton .topupRC -parent $tabwidget.f.frame \
      -text "TOPUP RunControl..." \
      -packOption "-side top" \
      -command {exec medm -x -cleanup -attach -dg +250+50 -macro RCPV=S:TopupBucketAssignmentRC ./sr/psApp/APSRunControlSingle.adl &}  \
      -contextHelp "Brings up the run control window for Topup Bucket Assignment."
    APSButton .kickerlog -parent $tabwidget.f.frame \
      -text "KICKER LOGGER RunControl..." \
      -packOption "-side top" \
      -command {exec medm -x -cleanup -attach -dg +500+50 -macro RCPV=SR:UserOpsKickerLogRC ./sr/psApp/APSRunControlSingle.adl &}  \
      -contextHelp "Brings up the run control window for kicker logging."

    set tabwidget [lindex $widgetList $windex]
    incr windex
    APSFrame .f -parent $tabwidget -label "Charge momentary on"
    APSCheckButtonFrame .cb -parent $tabwidget.f.frame -label "System" \
      -orientation horizontal -buttonList {IS1 IS2 IK} \
      -variableList {IS1MomentaryOn IS2MomentaryOn IKMomentaryOn} \
      -allNone 1 
    APSLabeledEntry .duration -parent $tabwidget.f.frame -label "Duration (s) " \
      -textVariable MomentaryOnTime -width 10 -contextHelp \
      "Enter the duration in seconds of the momentary turn-on of pulsed magnet charge signals."
    APSLabeledOutput .left -parent $tabwidget.f.frame -label "Time left (s) " \
      -textVariable MomentaryTimeLeft -width 10 -contextHelp \
      "Shows the duration left in seconds of the momentary turn-on of pulsed magnet charge signals."
    APSButton .momOn -parent $tabwidget.f.frame -text "Go" \
      -contextHelp "Turns on selected pulsed supplies for the given time." \
      -command "DoMomentaryOn $tabwidget.f.frame.momOn.button"

}

set IS1MomentaryOn 0
set IS2MomentaryOn 1
set IKMomentaryOn 0
set MomentaryOnTime 10
set MomentaryTimeLeft ?
proc DoMomentaryOn {widget} {
    global IS1MomentaryOn IS2MomentaryOn IKMomentaryOn MomentaryOnTime
    global MomentaryTimeLeft
    set PVList ""
    if $IS1MomentaryOn {
        lappend PVList Mt:Ddg3chan0
    }
    if $IS2MomentaryOn {
        lappend PVList Mt:Ddg3chan1
    }
    if $IKMomentaryOn {
        lappend PVList Mt:Ddg3chan4
    }
    bell
    if [catch {exec cavput -list=[join $PVList ,] -list=.GATE=1} result] {
        return -code error "$result"
    }
    set MomentaryTimeLeft $MomentaryOnTime
    APSDisableButton $widget
    update idletasks
    while {$MomentaryTimeLeft>0} {
        after [expr 1000]
        set MomentaryTimeLeft [expr $MomentaryTimeLeft-1]
        update idletasks
    }
    catch {exec cavput -list=[join $PVList ,] -list=.GATE=0}
    APSEnableButton $widget
    update idletasks
    bell
}

proc switchRingAndDump {mode} {
    if [catch {APSSRChangeBeamSwitchState -goTo $mode} result] {
        APSAlertBox [APSUniqueName .] \
          -errorMessage "Unable to send beam to $mode: $result"
        return
    }
    setStatus "Beam switched to $mode."
}

set septumStartupFile "SR-InjectionReference.gz"
proc DoSeptumStartup {septum} {
    global snapDir septumStartupFile
    set septumStartupFile \
      [APSInfoDialog .[APSTmpString] \
         -name "Septum startup query" \
         -default $septumStartupFile \
         -infoMessage "Enter the snapshot filename from the SCR tool." -width 50]
    if [string length $septumStartupFile]==0 return

    switch $septum {
        IS1 {
            APSExecLog .[APSTmpString] -unixCommand "SRSeptumStartup IS1 $snapDir/$septumStartupFile" \
              -callback "SignalDone IS1 startup"
        }
        IS2 {
            APSExecLog .[APSTmpString] -unixCommand "SRSeptumStartup IS2 $snapDir/$septumStartupFile" \
              -callback "SignalDone IS2 startup"
        }
        both {
            APSExecLog .[APSTmpString] -unixCommand "SRSeptumStartup IS1 $snapDir/$septumStartupFile" \
              -callback "DoSeptumStartup IS2"
        }
    }
}

proc DoKickerWarmup {} {
    APSExecLog .[APSTmpString] -unixCommand "SRKickerWarmup" \
      -callback "SignalDone kickers warmup"
}

proc SignalDone {thing action} {
    APSInfoWindow .[APSTmpString] -infoMessage "Done with $action for $thing.  Check status in unix command window."
}


# from J. Carwardine
proc RTFBResetOpen {} {
    global bunchTrainStatus
    set hpFilterInitial 10
    if [catch {exec cavput -pend=15 -list=SRFB:GBL:Hor:CloseLoopBO=0,SRFB:GBL:Vert:CloseLoopBO=0
	    exec cavput -pend=15 -list=SRFB:GBLM:Hor:CloseLoopBO=1,SRFB:GBLM:Vert:CloseLoopBO=1
	    exec cavput -pend=15 -list=SRFB:GBLM:Hor:CloseLoopBO=1,SRFB:GBLM:Vert:CloseLoopBO=1
	    exec cavput -pend=15 -list=SRFB:GBLM:DSPLoadParamsBO=1
	    exec cavput -pend=15 -list=SRFB:GBL:Hor:CloseLoopBO=1,SRFB:GBL:Vert:CloseLoopBO=1
	    exec cavput -pend=15 -list=SRFB:GBL:Hor:CloseLoopBO=0,SRFB:GBL:Vert:CloseLoopBO=0 } error] {
	setStatus $error; update
    }
    if [catch {exec cavput -pend=15 -list=SRFB:fhAO=$hpFilterInitial
	    exec cavput -pend=15 -list=SRFB:GBL:loadBO=1} error] {
	setStatus $error; update
    }
}

proc DoPreinjectionChecks {args} {
    global snapDir bpmGroupSumUse
    set gapLimit 0
    APSStrictParseArguments {gapLimit}

    setStatus "Pre-injection Checks:"
    setStatus "Resetting MPS..."
    set BPLDList [APSIDSectorList -numberFormat %ld]
    if [catch {exec cavput -list=S -list=[join $BPLDList ,] \
                 -list=BPLD:ResetBO=1 -pendIoTime=5
        exec cavput -list=S -range=beg=1,end=40,interval=2 -list=:MPS:resetBO=1 -pendIoTime=5
        exec cavput -list=S:MPS:resetSQ.PROC=1  -pendIoTime=5
        exec cavput -list=S: -range=beg=1,end=40,format=%02ld -list=:MPS:LC: -list=1 \
                 -list=:RstLatchBO=1 -pend=10
#        exec cavput -list=S: -range=beg=25,end=34,format=%02ld -list=:MPS:LC: -list=2 -list=:RstLatchBO=1 -pend=10
    } result] {
        setStatus "Problem resetting the MPS: $result"
        return 0
    }
    setStatus "Checking ID gap and BTS state prior to injection..."
    if {$gapLimit>0} {
        set gapLimitData [APSIDMinimumActualGap]
        if {[lindex $gapLimitData 0]<$gapLimit} {
            setStatus "Can't inject with gap below $gapLimit (ID[lindex $gapLimitData 1])"
            return 0
        }
    }
    if [catch {exec cavget -numerical -list=BTS:FS -list=2,3,4,5,6 \
                 -list=:act_cont_bo} BTSFlagStatus] {
         setStatus "Can't read BTS flag statuses: $BTSFlagStatus."
         return 0
    }
    set i 0
    set flagIsIn 0
    foreach flag {2 3 4 5 6} {
        if [expr [lindex $BTSFlagStatus $i] == 1] {
            setStatus "BTS Flag BTS:FS${flag} is in."
            set flagIsIn 1
            return 0
        }
        incr i
    }
    if $flagIsIn {
        return 0
    }
    setStatus "Checking kicker status..."
    if [catch {exec cavget -pend=5 -numerical -list=S:IK -list=1,2,3,4 \
                 -list=:StatusCALC} kickerStatus] {
         setStatus "Can't read kicker statuses: $kickerStatus"
         return 0
    }
    set i 0
    set kickerIsOff 0
    foreach kicker {1 2 3 4} {
        if [expr [lindex $kickerStatus $i] != 0] {
            setStatus "Kicker S:IK${kicker} is off."
            set kickerIsOff 1
            return 0
        }
        incr i
    }
    if $kickerIsOff {
        return 0
    }
    # compare present kicker settings with User beam prefered file
    # for re-fills only.
    if {$bpmGroupSumUse=="Sum"} {
        setStatus "Checking kicker setpoint against SR-UserBeamPreferred.gz..."
        set kickerFile /tmp/[APSTmpString]
        exec sddsprocess $snapDir/SR-UserBeamPreferred.gz ${kickerFile}.userSetpoint \
          -match=col,ControlName=S:IK?:VoltageSetSendAO \
          -scan=col,UserValue,ValueString,%lf
        exec sddsprocess ${kickerFile}.userSetpoint ${kickerFile}.userDAC \
          -reedit=col,ControlName,%/VoltageSetSendAO/DacAI/ 
        exec sddscombine ${kickerFile}.userSetpoint ${kickerFile}.userDAC \
          ${kickerFile}.user -merge
        exec burtrb  -f ${kickerFile}.user -o ${kickerFile}.settings
        exec sddsxref ${kickerFile}.settings $kickerFile.user -pipe=out \
          -match=ControlName -take=UserValue \
          | sddsprocess -pipe=in ${kickerFile}.diff \
          -scan=col,PresentValue,ValueString,%lf \
          "-def=col,DeltaValue,PresentValue UserValue -,description=Difference between present settings and saved User setting" \
          -proc=DeltaValue,largest,DeltaValueLargest \
          -proc=DeltaValue,largest,BadKicker,position,functionOf=ControlName
        set largestDelta [exec sdds2stream ${kickerFile}.diff -para=DeltaValueLargest]
        set badKicker [exec sdds2stream ${kickerFile}.diff -para=BadKicker]
        if {abs($largestDelta) > 1.0 } {
            setStatus "Kicker PV $badKicker is different from the corresponding UserBeamPrefered setpoint by $largestDelta"
            return 0
        }
    }
    setStatus "Checking RF gap voltage AGC status..."
    if [catch {exec cavget -list=S6:WGS:modeSelect -pend=15} WGSMode] {
        setStatus "Problem with reading RF switch mode: $WGSMode."
        return 0
    }
    if ![string length $WGSMode] {
        setStatus "resetRFAGC: Problem with reading S6:WGS:modeSelect: An empty string was returned."
        return 0
    }
    switch $WGSMode {
        Mode7 -
        Mode11 {
            set rf3637Station 2
            set rf3840Station 4
        }
        Mode8 -
        Mode9 {
            set rf3637Station 2
            set rf3840Station 1
        }
        Mode10 {
            set rf3637Station 3
            set rf3840Station 4
        }
        Mode12 {
            set rf3637Station 3
            set rf3840Station 1
        }
        default {
            setStatus "Unknown rf mode: $WGSMode"
            APSAlertBox [APSUniqueName .] -errorMessage \
              "Unknown rf mode: $WGSMode" 
            return 0
        }
    }        
    if [catch {exec cavget -numerical -pend=15 -list=S \
                 -list=$rf3637Station,$rf3840Station \
                 -list=:K:FB_OpsManualEnaBI,:K:FB_EngageBI} AGCstatus] {
        setStatus "Can't read RF gap voltage AGC status: $AGCstatus."
        return 0
    }
    if {![lindex $AGCstatus 0] || ![lindex $AGCstatus 1] || \
          ![lindex $AGCstatus 2] || ![lindex $AGCstatus 3]} {
        setStatus "RF gap voltage AGC is not closed.  Can't inject."
        return 0
    }
    setStatus "Checking BPLD status..."
    if [catch {exec cavget -list=S:MPS:readyCC} MPSstatus] {
         setStatus "Can't read MPS status: $MPSstatus."
         return 0
    }
    if !$MPSstatus {
        setStatus "MPS is tripped.  Can't inject."
        return 0
    }
    set nameList [list "lock off" "pending trip"]
    set PVSuffixList [list BPLD:limitsLockBI.VAL BPLD:TripPendingCC.VAL]
    set index -1
    foreach name $nameList {
        incr index
        if [catch {exec cavget -list=S -list=[join $BPLDList ,] \
                     -list=[lindex $PVSuffixList $index]} result] {
            setStatus "Problem reading [lindex $nameList $index] status: $result"
            return 0
        }
        if [string first 1 [join $result] ]!=-1 {
            setStatus "One or more BPLDs have a [lindex $nameList $index]."
            return 0
        }
    }
    return 1
}

proc resetRFAGC {} {
    global S35BeamCurrent
    global env
    if [string compare $env(USER) borland]==0 {
        setStatus "Skipping reseting of RF AGC."
        return 0
    }
    if [expr [pv getw S35BeamCurrent] ] {
        return -code error "resetRFAGC: Problem with reading S35DCCT"
    }
    set S35BeamCurrent $S35BeamCurrent
    if {$S35BeamCurrent > 0.05} {
        setStatus "Skipping reseting of RF AGC. Got stored beam already."
        return 0
    }        
    if [catch {exec cavput -list=SRF:AGC:FB_SoftEngage -list=1,2 -list=BO=1 \
                 -pend=10 \
             } result] {
        return -code error "resetRFAGC: Problem setting AGC on RF systems: $result"
    }
    return
}

proc CheckPVConnections {} {
    setStatus "Checking PV connections."
    if [catch {APSBunchTrainConnect -checkPVs 1} result] {
        setStatus "$result"
    }
    if [catch {APSTopupReadbackConnect -checkPVs 1} result] {
        setStatus "$result"
    }
    setStatus "Done."
}

proc LevelBunchCharge {} {
    global opsButtonFrame opsButtonFrame2 S35BeamCurrent
    global bpmGroupCurrent bpmGroupBuckets currentStop multiplicity multiplets
    APSDisableButton $opsButtonFrame.start.button
    APSDisableButton $opsButtonFrame.start1.button
    APSEnableButton $opsButtonFrame.abort.button
    APSDisableButton $opsButtonFrame2.level.button
    
    global LevelBunchChargeDialogStatus  LevelBunchChargeCycles LevelBunchChargeThreshold
    global LevelBunchChargeBBCMAverages LevelBunchChargeDoubleFirst LevelBunchUniformCurrent
    set LevelBunchChargeDialogStatus 0
    set LevelBunchChargeCycles 5
    set LevelBunchChargeThreshold 0.03
    set LevelBunchChargeBBCMAverages 1
    set LevelBunchChargeDoubleFirst 0
    set LevelBunchUniformCurrent 1

    pv getw S35BeamCurrent
    if {$S35BeamCurrent < 3} {
        setStatus "Beam current is too low for leveling.  You should have at least 30mA."
        APSEnableButton $opsButtonFrame.start.button
        APSEnableButton $opsButtonFrame.start1.button
        APSDisableButton $opsButtonFrame.abort.button
        APSEnableButton $opsButtonFrame2.level.button
        return -code ok
    }

    set w [APSUniqueName .]
    APSDialogBox $w -name "Level Bunch Charge Dialog" \
      -okCommand "set LevelBunchChargeDialogStatus 1" \
      -cancelCommand "set LevelBunchChargeDialogStatus 2" 
    APSLabeledEntry .bbcmave -parent $w.userFrame \
      -label "BB CM readings to average: " -textVariable LevelBunchChargeBBCMAverages \
      -contextHelp "Number of readings of the bunch-by-bunch current monitor to average to determine deficient bunches."
    APSLabeledEntry .cycles -parent $w.userFrame \
      -label "Cycles: " -textVariable LevelBunchChargeCycles \
      -contextHelp "Number of cycles to perform.  Each cycle consists of reading the BBCM, finding the deficient bunches, and giving one injector shot to each."
    APSLabeledEntry .threshold -parent $w.userFrame \
      -label "Threshold (mA): " -textVariable LevelBunchChargeThreshold \
      -contextHelp "Amount by which a bucket has to be deficient in order to be considered for filling."
    APSRadioButtonFrame .double -parent $w.userFrame \
      -label "Double-up on first bucket? " -variable LevelBunchChargeDoubleFirst \
      -buttonList "Yes No" -valueList "1 0" -orientation horizontal \
      -contextHelp "Choose whether to double-up on the first bucket. Typically, the first bucket injected into has very low efficiency, so it is worthwhile to inject into it twice in a row."
    APSRadioButtonFrame .uniform -parent $w.userFrame \
      -label "Same current in all bunches?" \
      -variable LevelBunchUniformCurrent -buttonList "Yes No" -valueList "1 0" \
      -orientation horizontal -contextHelp \
      "Choose whether to make all bunches the same.  If no, then the BPM group current given under the BPM group tab is used for the BPM group bunches."

    tkwait variable LevelBunchChargeDialogStatus  
    if $LevelBunchChargeDialogStatus==1 {
        if [catch {APSLevelBunchCharge -desiredCurrent $currentStop \
                     -BBCMAverages $LevelBunchChargeBBCMAverages \
                     -bpmGroupCurrent $bpmGroupCurrent -bpmGroupBuckets $bpmGroupBuckets \
                     -otherBuckets [expr $multiplicity*$multiplets] \
                     -deficitThreshold $LevelBunchChargeThreshold -cycles $LevelBunchChargeCycles \
                     -doubleFirst $LevelBunchChargeDoubleFirst \
                     -uniformCurrent $LevelBunchUniformCurrent \
                     -statusCallback setStatus} result] {
            setStatus "$result"
        }
    }
    APSEnableButton $opsButtonFrame.start.button
    APSEnableButton $opsButtonFrame.start1.button
    APSDisableButton $opsButtonFrame.abort.button
    APSEnableButton $opsButtonFrame2.level.button
}

proc GenerateMultipletPattern {} {
    global multipletPattern multipletSpacing multipletStart
    global generateMultipletPatternStatus multiplicity multiplets
    global GenerateMultipletPatternGap1 GenerateMultipletPatternGap2
    global currentStop bpmGroupCurrent GenerateMultipletMode
    global GenerateMultipletPatternLength1

    APSDialogBox .mpattern -name "Pattern Generation Dialog"  \
      -cancelCommand "set generateMultipletPatternStatus cancel" \
      -okCommand "set generateMultipletPatternStatus ok"
    set w .mpattern.userFrame
    set GenerateMultipletMode HSB
    set GenerateMultipletPatternGap1 1600
    set GenerateMultipletPatternLength1 100
    APSRadioButtonFrame .mode -parent $w -label "Mode: " -variable GenerateMultipletMode \
      -buttonList "Hybrid+SuperBunch" -valueList HSB -orientation horizontal
    APSLabeledEntry .gap1 -parent $w -label "Gap 1 (ns, min): " -textVariable GenerateMultipletPatternGap1 \
      -width 10
    APSLabeledEntry .gap2 -parent $w -label "Super-bunch length (ns, max): " \
        -textVariable GenerateMultipletPatternLength1 -width 10
    set generateMultipletPatternStatus -1
    update
    tkwait variable generateMultipletPatternStatus
    if [string compare $generateMultipletPatternStatus cancel]==0 return

    set gap1Buckets [expr int($GenerateMultipletPatternGap1/1e9*351.927e6+0.5)]
    set availableBuckets [expr int($GenerateMultipletPatternLength1/1e9*351.927e6+0.5)]
    set nTrains 0
    set length 0
    while {$length<$availableBuckets} {
        incr nTrains
        set length [expr $nTrains*$multiplicity+($nTrains-1)*($multipletSpacing-$multiplicity)]
    }
    incr nTrains -1
    if $nTrains<1 {
        return -code error "Gaps are too large: number of available buckets less than multiplicity ($multiplicity)"
    }
    set pattern ""
    set bucketsFilled 0
    for {set i 0} {$i<$nTrains} {incr i} {
        set pattern [concat $pattern [APSReplicateItem -item 1 -number $multiplicity]]
        incr bucketsFilled [expr 2*$multiplicity]
        if {$i<[expr $nTrains-1]} {
            set pattern [concat $pattern [APSReplicateItem -item 0 -number [expr $multipletSpacing-$multiplicity]]]
        }
    }
    set multipletPattern [join $pattern ""]
    set multipletStart $gap1Buckets
    set hgap2Buckets [expr 648-$gap1Buckets-[llength $pattern]]
    set multipletSpacing [expr [llength $pattern]+2*$hgap2Buckets]
    set multiplets 2
    set multiplicity 1
    setStatus "Actual gaps: [expr int($multipletStart/351.927e6*1e9)]ns   [expr int(2*$hgap2Buckets/351.927e6*1e9)]ns"
    setStatus "$bucketsFilled filled buckets in bunch trains, [format %.2f [expr ($currentStop-$bpmGroupCurrent)/$bucketsFilled]] mA each"
}

proc CheckBunchCurrentAndNumber {} {
    global S35BeamCurrent bpmGroupCurrent
    if [catch {exec cavget -list=BNCHI:BunchISelectAO.VAL -pend=20 -printErrors} bunchNum] {
        return -code error "Unable to read BNCHI:BunchISelectAO.VAL: $bunchNum"
    }
    if {$bunchNum!=0} {
        return -code error "BNCHI:BunchISelectAO.VAL has to be zero bunch for using BCM mode."
    }
    if [catch {exec cavget -list=BNCHI:BunchCurrentAI.VAL -pend=10} bunch0Curr] {
        return -code error "Unable to read bunch current: $bunch0Curr"
    }
    set limit [expr $S35BeamCurrent + $bpmGroupCurrent - $bunch0Curr]
    if {$limit>102} {
        set limit 102
    }
    return $limit
}

proc CheckBunchPattern {args} {
    set bunchPatternLabel ""
    APSParseArguments {bunchPatternLabel}

    if [regexp {8x7} $bunchPatternLabel] {
        set requiredBunches 57
        set ref hybrid561
    } elseif  [regexp {324} $bunchPatternLabel] {
        set requiredBunches 324
        set ref 324singlets
    } elseif [regexp {24} $bunchPatternLabel] {
        set requiredBunches 24
        set ref 24singlets
    } elseif [regexp {48} $bunchPatternLabel] {
        set requiredBunches 48
        set ref 48singlets
    } else {
        #no reference
        return
    }
    
    # a basic reality check
    if [catch {exec cavget -list=Mt:S:NumBucketsFilledAI} bunches] {
        return -code error $bunches
    }
    if { $bunches != $requiredBunches } {
        set answer [APSMultipleChoice [APSUniqueName .] -name Warning \
                        -type warning -beep continuous \
                        -question "The selected bunch pattern ($bunchPatternLabel) does not match the number of bunches in the ring Mt:S:NumBucketsFilledAI.\n\n Continue or abort?" \
                        -returnList {Continue Abort} -labelList {Continue Abort} ]
        
        if {$answer=="Abort"} {
            return -code error "Abort topup due to mis-match of bunch number in bunch pattern!"
        }
    }

    # continue with a more sophisticated reality check
    set refFile /home/helios/oagData/sr/bunchPatterns/bunchPatternFillRef.$ref
    set tmpFile /tmp/[APSTmpString]
    if [catch {exec sddswget -pv=Mt:S:FillPatternWF.VAL $tmpFile} result] {
        return -code error $result
    }
    if [catch {exec sddsdiff -col=Index,Waveform $refFile $tmpFile} result] {
        puts $result
        set answer [APSMultipleChoice [APSUniqueName .] -name Warning \
                        -type warning -beep continuous \
                        -question "The selected bunch pattern ($bunchPatternLabel) does not match the current fill pattern.\n\n Continue or abort?" \
                        -returnList {Continue Abort} -labelList {Continue Abort} ]
        
        if {$answer=="Abort"} {
            return -code error "Abort topup due to mis-match of fill-pattern!"
        }
    }
}

set injectionMode timing

set args $argv
set VIPMode 0
set VIPEmulate 0
set usage "SRBunchTrain \[-VIPMode 1\] \[-VIPEmulate 1\]\nUse VIPEmulate for simulation mode."
if [APSStrictParseArguments {VIPMode VIPEmulate}] {
    return -code error "Bad arguments\n$usage"
}

set bunchTrainStatus "Ready"
APSScrolledStatus .status -parent .userFrame -textVariable bunchTrainStatus  \
  -width 80 -height 6 -lineLimit 1000 -withButtons 1 -packOption "-fill x"
if {$VIPEmulate} {
    APSCreateADLScreen .adl -parent .userFrame -filename /home/helios/OAG/oagData/adl/VIP.adl
}

set patternDir /home/helios/oagData/sr/bunchPatterns
set patternDir /tmp
set apsTopupTarget 102
set apsTopupInterval 60
set apsTopupWarningTime 10
set apsTopupInjectorWarningTime 2
# one 1nC pulse
# set apsTopupTolerance 0.27
set apsTopupTolerance 0.5
set apsTopupBunches 1

# This procedure is called in order to have
# access to apsBoosterEKexcluded.
if [catch {APSTopupConnect} result] {
    setStatus "$result"
}
if [catch {APSTopupReadbackConnect} result] {
    setStatus "$result"
}

if [catch {APSBunchTrainConnect} result] {
    setStatus "$result"
}

set presetFile /home/helios/oagData/sr/bunchPatterns/presetPatterns
sdds load $presetFile presetBunchPatternData
set presetPatternList [lindex $presetBunchPatternData(Column.Label) 0]
set bunchPattern ""
set controllawMode ""

APSFrameGrid .fg -parent .userFrame -xList {x1 x2}
.userFrame.fg.x1 configure -relief ridge -bd 2
MakeBunchTrainWidget .timing -parent .userFrame.fg.x1 
MakeInjectionControlWidget .inj -parent .userFrame.fg.x2
MakeTopupFrame .topup -parent .userFrame.fg.x2 
# The default bunch pattern is index 0, which happens to be 24 singlets
SetPresetBunchPattern -index 0
set apsLinacBunches [GetLinacBunches]

if $VIPMode {
    APSMakeVIPFillInterface -emulation $VIPEmulate
}


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