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

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

proc setStatus {text} {
    APSSetVarAndUpdate status $text
}


set BendFeedbackRunning 0
set L2PwrFeedbackRunning 0
set BendFeedbackFrame ""
set L2PwrFeedbackFrame ""
set positionTolerance 1

proc FeedbackPretest {args} {
    set id Bend
    set RCPV APS:RunControlSlot0RC
    APSStrictParseArguments {id RCPV}
    global ${id}FeedbackRunning 
    set FeedbackRunning [set ${id}FeedbackRunning]
    if $FeedbackRunning {
        return -code error "$id feedback already running."
    }
    if {[catch {exec cavget -list=$RCPV.RUN} result] || $result==1} {
        return -code error "$id feedback already running ($RCPV.RUN=$result)."
    }
    set ${id}FeedbackRunning 1
}

proc FeedbackCallback {args} {
    set mode ""
    set id Bend
    set RCPV APS:RunControlSlot0RC
    APSStrictParseArguments {mode id RCPV}
    global ${id}FeedbackRunning  ${id}FeedbackFrame
    set ${id}FeedbackRunning 0
    bell 
    switch $mode {
        ok {
            APSSetVarAndUpdate status "$id feedback process has completed."
            return
        }
        default {
            exec cavput -pend=30 -list=$RCPV.ABRT=1
            catch {after 2000 "destroy [set ${id}FeedbackFrame]"}
            APSSetVarAndUpdate status "$id feedback process has been terminated."
            return
        }
    }
}

proc ControlFeedback {args} {
    set mode ""
    set id Bend
    set RCPV APS:RunControlSlot0RC
    APSStrictParseArguments {mode id offset gain RCPV}
    global ${id}Setpoint ${id}Gain ${id}FeedbackFrame ${id}Samples
    global BendMagnetName
    set offset [set ${id}Setpoint]
    set gain [set ${id}Gain]
    set samples [set ${id}Samples]
    set interval [expr $samples+1]

    switch $mode {
        start {
            if [catch {FeedbackPretest -id $id -RCPV $RCPV} result] {
                setStatus "$result"
                return
            }
            switch $id {
                Bend {
                    global ${id}BPMPositionPVName
                    global ${id}Description ${id}Rootname
                    set readbackPV [set ${id}BPMPositionPVName ]
                    set description [set ${id}Description]
                    set gain [expr -0.5*$gain]
                    set rootname [set ${id}Rootname]
                    set deltaLimit 1
                    set mode integral
                }
                L2Pwr {
                    exec cavput -pend=30 -list=L:L2Stabilizer:SDDS.SUSP=1
                    set readbackPV L3:P1:x
                    set gain [expr 0.2*$gain]
                    set rootname L2Pwr-L3P1
                    set description "L3:P1:BPM using L2 power"
                    set deltaLimit 1
                    set mode proportional
                }
            }

            set tmpFile /tmp/[APSTmpString]
            APSAddToTempFileList $tmpFile
            exec sddssequence -pipe=out -define=index -sequence=beg=0,end=0,n=1 \
              | sddsprocess -pipe=in $tmpFile \
              -print=col,ControlName,$readbackPV \
              -define=column,Offset,$offset

            set dataDir /home/helios/oagData/linac/quickFeedbacks
            eval lappend cmd sddscontrollaw $dataDir/${rootname}.inv \
              -testValues=$dataDir/${rootname}.tests -offsets=$tmpFile \
              -gain=$gain -interval=$interval -steps=10000 -average=$samples,interval=1 \
              -delta=value=1 -verbose -$mode
            lappend cmd -runControlPV=string=$RCPV
            lappend cmd "-runControlDescription=string=$description"
            lappend cmd -actionLimit=value=0.1
            set ${id}FeedbackFrame \
              [APSExecLog [APSUniqueName .] \
                 -name "Feedback on $description" \
                 -callback "FeedbackCallback -mode ok -id $id -RCPV $RCPV" \
                 -abortCallback "FeedbackCallback -mode abort -id $id -RCPV $RCPV" \
                 -cancelCallback "FeedbackCallback -mode cancel -id $id -RCPV $RCPV" \
                 -width 120 -height 16 -lineLimit 1000 -contextHelp \
                 "Shows progress of sddscontrollaw process that is doing feedback." \
                 -unixCommand $cmd]
        }
        abort {
            exec cavput -pend=30 -list=$RCPV.ABRT=1
            if [string compare $id L2Pwr]==0 {
                # transfer L2 power setpoint and restart L2 power control if it was originally running
                if [catch {APSCAAverageAndTransfer -average 10 \
                             -interval 0.5 -readbackList L-K2:LLRF:SD:Fwd_PkPwrRemoteM \
                             -setpointList L2:SD:DC1ARF:SetpointP \
                             -statusCallback setStatus 
                    after 1000
                    exec cavput -pend=30 -list=L:L2Stabilizer:SDDS.SUSP=0} result] {
                    return -code error "$result"
                }
            }
            catch {after 2000 "destroy [set ${id}FeedbackFrame]"}
        }
        suspend {
            exec cavput -pend=30 -list=$RCPV.SUSP=1
        }
        resume {
            exec cavput -pend=30 -list=$RCPV.SUSP=0
        }
    }
}

proc ChangeGun {args} {
    set type RG1/RG2
    APSStrictParseArguments {type}
    global useRfPwrFdbck useRfPhsFdbck
    global L2PwrUseIt BendUseIt doCheckForBeam 
    foreach sector {1 2 3 4 5} {
        set useRfPwrFdbck($sector) 1
        set useRfPhsFdbck($sector) 1
    }
    switch $type {
        RG1/RG2 {
            set useRfPwrFdbck(2) 0
            set L2PwrUseIt 0
            set BendUseIt 0
            set doCheckForBeam 0
        }
        PC {
            set useRfPhsFdbck(2) 0
            set L2PwrUseIt 0
            set BendUseIt 0
            set doCheckForBeam 0
        }
    }
}

proc RecordPhase {args} {
    set point 0
    APSStrictParseArguments {point}

    global PhaseSetpoint PhaseFBSetpoint PhaseSetUp
    set dataList [exec cavget -list=L4,L5 -list=:PP:phaseAdjAO,:AUTO:PH:measuredPhaseAI]
    set PhaseSetpoint($point,4)   [lindex $dataList 0]
    set PhaseFBSetpoint($point,4) [lindex $dataList 1]
    set PhaseSetpoint($point,5)   [lindex $dataList 2]
    set PhaseFBSetpoint($point,5) [lindex $dataList 3]
    set PhaseSetUp($point,4) 1
    set PhaseSetUp($point,5) 1
}

proc GoToPhase {args} {
    set point 0
    APSStrictParseArguments {point}

    global PhaseSetpoint PhaseFBSetpoint PhaseSetUp BendUseIt UseV4Also

    if !$PhaseSetUp($point,5) {
        setStatus "Phase setpoint $point not recorded"
        return
    }
    setStatus "Suspending phase feedback based on beam-to-rf phase detector"
    exec cavput -list=L:BPD: -list=L3P3:L4AS1:,L4P1:L5AS1: -list=ClearErrCO.DISA=1
    if $BendUseIt {
        ControlFeedback -id Bend -mode suspend
    }
    set systemList 5
    set putList [list L5:PP:phaseAdjAO=$PhaseSetpoint($point,5) \
                   L5:AUTO:PH:SetpointAO=$PhaseFBSetpoint($point,5)]
    if $UseV4Also {
        lappend systemList 4
        lappend putList L4:PP:phaseAdjAO=$PhaseSetpoint($point,4) \
                   L4:AUTO:PH:SetpointAO=$PhaseFBSetpoint($point,4)
    }

    APSLinacLockInRFSystems -systemList $systemList -unlock 1 -power 0 
    exec cavput -pend=30 -list=[join $putList ,]
    APSLinacLockInRFSystems -systemList $systemList -unlock 0 -power 0 -transferPhase 0
    setStatus "Phase for [join $systemList ,] set to setpoint #$point"

    after 5000
    if $BendUseIt {
        ControlFeedback -id Bend -mode resume
    }
}

proc TurnLLRF {args} {
    set mode 0
    set sector 5
    APSStrictParseArguments {mode sector}

    if $mode {
        exec cavput -pend=30 -list=L${sector}:LL:lockRfOnBO=1
        setStatus "L$sector LLRF turned on"
        after 1000
        exec cavput -pend=30 -list=L${sector}:DA:resetBO=1
    } else {
        exec cavput -pend=30 -list=L${sector}:LL:lockRfOffBO=1
        setStatus "L$sector LLRF turned off"
    }
}

proc MeasureBunchLength {} {
    # 1. Measure energy change due to L5 with +/- 5 deg phase change
    #    This gives V5.
    # 2. Measure energy spread on L5 in three conditions:
    #       V5 = 0          :  dE0RMS
    #       zero crossing #1:  dE1RMS
    #       zero crossing #2:  dE2RMS
    # 3. Compute the rms bunch length:
    #    dphiRMS^2 = ( dE1RMS^2 + dE2RMS^2 - dE0RMS^2)/(2*V^2)
    global UseV4Also
    global V4 V5 PhaseSetpoint dERMS bunchLength L2PhaseReadback Time bunchLengthSigma
    global Rootname DataFileIndex Samples gunChoice PhaseSetUp abortMeasurement
    global BendSamples BendMagnetName doPrompting
    global extraMonitorFile doCheckForBeam

    set abortMeasurement 0
    set intervalMultiplier $BendSamples

    if !$UseV4Also {
        set V4 0
    }

    if ![string length $Rootname] {
        setStatus "Give an output rootname."
        return
    }
    foreach i {0 1 2} {
        set dERMS($i) 0
    }
    set bunchLength 0
    set bunchLengthSigma 0

    if $V5<=0 {
        setStatus "Error: V5 has not been calibrated."
        return
    }
    
    foreach i {1 2} {
        if !$PhaseSetUp($i,5) {
            setStatus "Error: phase setpoint $i not recorded."
            return
        }
    }

    setStatus "Performing measurement..."
    if $abortMeasurement {
        return [RecognizeAbortRequest -rootname $rootname]
    }
    global BendUseIt L2PwrUseIt
    if $BendUseIt {
        ControlFeedback -id Bend -mode start  -RCPV APS:RunControlSlot0RC
    }
    if $L2PwrUseIt {
        ControlFeedback -id L2Pwr -mode start  -RCPV APS:RunControlSlot1RC
    }

    global useRfPwrFdbck useRfPhsFdbck
    set powerLockList ""
    set phaseLockList ""
    foreach sector {1 2 3 4 5} {
        if $useRfPhsFdbck($sector) {
            lappend phaseLockList $sector
        }
        if $useRfPwrFdbck($sector) {
            lappend powerLockList $sector
        }
    }

    if [llength $phaseLockList] {
        setStatus "Locking in phase for rf systems: $phaseLockList"
        if $abortMeasurement {
            return [RecognizeAbortRequest -rootname $rootname]
        }
        APSLinacLockInRFSystems -systemList $phaseLockList -power 0 
    }

#    if [llength $powerLockList] {
#        setStatus "Locking in power for rf systems: $powerLockList"
#        if $abortMeasurement {
#            return [RecognizeAbortRequest -rootname $rootname]
#        }
#        APSLinacLockInRFSystems -systemList $powerLockList -phase 0
#    }

    incr DataFileIndex 
    set rootname [format $Rootname-%03ld $DataFileIndex]
    if [llength [glob -nocomplain $rootname.?]]!=0 {
        if [APSMultipleChoice [APSUniqueName .] -question \
              "Files matching $rootname.? exist.  What do you want to do?" \
              -labelList {Overwrite Abort} \
              -returnList {0 1}] {
            return
        }
        eval file delete -force [glob -nocomplain $rootname.?]
    }

    foreach i {1 2} {
        setStatus "Going to phase setting $i"
        GoToPhase -point $i 
        if [catch {exec cavput -pend=30 -list=LI:VD1:imageSourceC=Original} result] {
            return -code error "$result"
        }
        APSWaitWithUpdate -updateInterval 1 -waitSeconds [expr 10*$intervalMultiplier]
        if $abortMeasurement {
            return [RecognizeAbortRequest -rootname $rootname]
        }
        if {$doCheckForBeam} {
            while {![CheckForBeam]} {
                setStatus "Waiting for beam on dispersion BPM."
                APSWaitWithUpdate -updateInterval 1 -waitSeconds [expr 5*$intervalMultiplier]
                if $abortMeasurement {
                    return [RecognizeAbortRequest -rootname $rootname]
                }
               bell
            }
        } else {
            setStatus "Warning: not checking for beam at dispersion BPM."
        }
        bell
        setStatus "Reading energy spread at phase point $i."
        if $doPrompting {
            if ![APSQueryToProceed -message "Ok to proceed?"] {
                return [RecognizeAbortRequest -rootname $rootname]
            }
        }
        if [catch {exec cavput -pend=30 -list=LI:VD1:imageSourceC=Subtracted} result] {
            return -code error "$result"
        }
        exec sddswmonitor -logOnChange=waveform \
          /home/helios/oagData/linac/bunchLengthMeasurement/$BendMagnetName.wmon \
          -scalars=/home/helios/oagData/linac/bunchLengthMeasurement/$BendMagnetName.scalars \
          -comment=L5VoltageString,$V5 -comment=BendMagnetName,$BendMagnetName \
          -comment=L4VoltageString,$V4 \
          $rootname.$i -steps=$Samples -interval=0.5,s
        if $abortMeasurement {
            return [RecognizeAbortRequest -rootname $rootname]
        }
        bell
    }
    #set dERMS2Ave [expr ($dERMS(1)*$dERMS(1)+$dERMS(2)*$dERMS(2))/2.0]
    #set bunchLength [expr 350.0/(2*3.1415926)*sqrt($dERMS2Ave-$dERMS(0)*$dERMS(0))/$V5]

    setStatus "Reverting to phase setpoint 1."
    GoToPhase -point 1
    
    setStatus "Turning off L5 LLRF"
    TurnLLRF -sector 5 -mode 0
    if $UseV4Also {
        setStatus "Turning off L4 LLRF"
        TurnLLRF -sector 4 -mode 0
    }
    if [catch {exec cavput -pend=30 -list=LI:VD1:imageSourceC=Original} result] {
        return -code error "$result"
    }
    APSWaitWithUpdate -updateInterval 1 -waitSeconds [expr 10*$intervalMultiplier]
    if $abortMeasurement {
        return [RecognizeAbortRequest -rootname $rootname]
    }
    if {$doCheckForBeam} {
        while {![CheckForBeam]} {
            setStatus "Waiting for beam on dispersion BPM."
            if $abortMeasurement {
                return [RecognizeAbortRequest -rootname $rootname]
            }
            APSWaitWithUpdate -updateInterval 1 -waitSeconds [expr 5*$intervalMultiplier]
            if $abortMeasurement {
                return [RecognizeAbortRequest -rootname $rootname]
            }
            bell
        }
    } else {
        setStatus "Warning: not checking for beam at dispersion BPM"
    }
    update
    if $abortMeasurement {
        return [RecognizeAbortRequest -rootname $rootname]
    }
    bell
    if $doPrompting {
        if ![APSQueryToProceed -message "Adjust aperture now to remove saturation."] {
            return [RecognizeAbortRequest -rootname $rootname]
        }
    }
    if [catch {exec cavput -pend=30 -list=LI:VD1:imageSourceC=Subtracted} result] {
        return -code error "$result"
    }
    if $UseV4Also {
        setStatus "Reading energy spread with L4 and L5 off."
    } else {
        setStatus "Reading energy spread with L5 off."
    }
    if [string length $extraMonitorFile] {
        set monitorFile /tmp/[APSTmpString]
        APSAddToTempFileList $monitorFile
        if [catch {exec sddscombine \
                     /home/helios/oagData/linac/bunchLengthMeasurement/$BendMagnetName.scalars \
                     $extraMonitorFile -pipe=out -merge \
                     | sddsprocess -pipe -defi=column,Index,i_row,type=long \
                     | sddssort -pipe -column=ControlName,incr -col=Index,incr \
                     | sddsbreak -pipe -change=ControlName \
                     | sddsprocess -pipe -clip=1,0,invert \
                     | sddscombine -merge -pipe=in $monitorFile} result] {
            return -code error "$result"
        }
    } else {
        set monitorFile /home/helios/oagData/linac/bunchLengthMeasurement/$BendMagnetName.scalars
    }
    exec sddswmonitor \
      /home/helios/oagData/linac/bunchLengthMeasurement/$BendMagnetName.wmon -logOnChange=waveform \
      -scalars=$monitorFile \
      -comment=L5VoltageString,$V5 -comment=BendMagnetName,$BendMagnetName \
      -comment=L4VoltageString,$V4 \
      $rootname.0 -steps=$Samples -interval=0.5,s 
    if $abortMeasurement {
        return [RecognizeAbortRequest -rootname $rootname]
    }

#    TurnLLRF -sector 5 -mode 1
#    if $UseV4Also {
#        TurnLLRF -sector 4 -mode 1
#    }

    ProcessSetOfFiles -rootname $rootname
    ProcessAll
    setStatus "Measurement is done." 
}


proc RecognizeAbortRequest {args} {
    set rootname ""
    if [string length $rootname] {
        catch {eval file delete -force [glob -nocomplain $rootname.?]}
    }
    setStatus "Aborted measurement."
}


proc ProcessSetOfFiles {args} {
    set rootname ""
    APSStrictParseArguments {rootname}
    global dERMS bunchLength bunchLengthSigma FlagCal

    set fileList [lsort [glob -nocomplain $rootname.?]]
    if [llength $fileList]!=3 {
        setStatus "No files or wrong number of files for $rootname ([llength $fileList] found)."
        return
    }
    set bendName [lindex [split [exec sdds2stream -parameter=BendMagnetName [lindex $fileList 0]]] 0]
    set parameterList [APSGetSDDSNames -class parameter -fileName [lindex $fileList 0]]
    set tmpFile /tmp/[APSTmpString]
    APSAddToTempFileList $tmpFile
    foreach file $fileList index {0 1 2} {
        if {[string compare $bendName LTPB1]==0} {
            if {[lsearch -exact $parameterList Energy]==-1} {
                APSSetVarAndUpdate status "Calibrating dipole"
                # have to calibrate current->energy 
                exec sddscollapse $file $tmpFile.c
                exec sddsinterp /home/helios/oagData/linac/magnets/LTP:B1.excitation -pipe=out \
                  -column=Current,IntegratedStrength \
                  -fileValues=$tmpFile.c,column=LTPB1CurrentSetpoint \
                  | sddsprocess -pipe \
                  "-define=column,Energy,IntegratedStrength 0.2 / 299.79 *,units=MeV" \
                  | sddsexpand -pipe=in $tmpFile 
                exec sddsxref -nowarning $file $tmpFile -leave=* -transfer=parameter,Energy
                eval file delete ${file}~ $tmpFile $tmpFile.c
            }
            set sigmaName xRawSigma
        } else {
            set sigmaName yRawSigma
        }
        exec sddssort $file -pipe=out -col=Index,decr \
          | sddsprocess -pipe -process=profileData,first,profileData0 -nowarning \
          -define=col,origProfileData,profileData,type=float \
          {-rpnexpr=0 sto Changed,repeat} \
          {-test=column,profileData profileData0 - abs Changed + sto Changed 0 >} \
          {-test=param,n_rows 10 >} \
          | sddssort -pipe -col=Index,incr \
          | sddssmooth -pipe -col=profileData -despike \
          | sddsbaseline -pipe -column=profileData -select=antioutlier=1 -nonnegative -method=average \
          | sddsprocess -pipe \
          -process=Index,stand,profileSigma0,weight=profileData \
          -process=Index,ave,profileCentroid0,weight=profileData \
          {-define=col,NormAbsOffset,Index profileCentroid0 - profileSigma0 / abs} \
          {-redefine=col,profileData,NormAbsOffset 3 > ? 0 : profileData $ } \
          -process=Index,stand,profileSigma,weight=profileData \
          | tee $file.proc1 \
          | sddscollapse -pipe \
          | sddssort -pipe -column=profileSigma \
          | sddsprocess -pipe -nowarning \
          "-define=parameter,FlagCal,$FlagCal,units=%/pixel" \
          "-scan=column,L5Voltage,L5VoltageString,%le,units=MeV" \
          "-scan=column,L4Voltage,L4VoltageString,%le,units=MeV" \
          "-define=column,L4L5Voltage,L4Voltage L5Voltage +" \
          "-define=column,fracPosition,i_row n_rows /" \
          -filter=column,fracPosition,0.25,0.75 \
          "-define=column,MV200EnergySpread,$sigmaName Energy * FlagCal 100 / *,units=MeV" \
          "-define=column,EnergySpread,profileSigma Energy * FlagCal 100 / *,units=MeV" \
          | sddsprocess -pipe -delete=column,*TimeStamp,L?VoltageString,BendMagnetName,*Label \
          -process=*,ave,%sMean \
          -process=*,sigma,%sSigma \
          | sddscollapse -pipe \
          | sddsconvert -pipe=in -edit=column,*,ei/$index/ $file.proc
        # exec sddsplot $file $file.proc1 -col=Index,profileData -filename -filter=col,Index,0,479 \
        # -split=page -sep=page -same=y -group=page -graph=line,vary &
        lappend procList $file.proc
    }
    eval exec sddsxref $procList -pipe=out -take=* \
      | sddsprocess -pipe=in $rootname.proc \
      {"-define=column,Factor1,EnergySpreadMean1 sqr EnergySpreadMean2 sqr + 2 / EnergySpreadMean0 sqr - 0 < ? 1e-6 : pop \$ "} \
      {"-define=column,Factor2,EnergySpreadMean1 EnergySpreadSigma1 * sqr EnergySpreadMean2 EnergySpreadSigma2 * sqr + 4 / EnergySpreadMean0 EnergySpreadSigma0 * sqr +"} \
      {"-define=column,BunchLength,Factor1 sqrt 350 * 2 / pi / L4L5VoltageMean1 /,units=ps"} \
      {"-define=column,BunchLengthSigma,Factor2 Factor1 / sqrt 350 * 2 / pi / L4L5VoltageMean1 /,units=ps"} \
      {"-define=column,FractionalBunchLengthSigma,BunchLengthSigma BunchLength /"}

    eval file delete -force $procList

    if [catch {exec sdds2stream $rootname.proc \
                 -column=BunchLength,BunchLengthSigma,EnergySpreadMean0,EnergySpreadMean1,EnergySpreadMean2} \
          dataList] {
        setStatus "$dataList"
        return
    }
    foreach var {bunchLength bunchLengthSigma dERMS(0) dERMS(1) dERMS(2)} \
      value $dataList {
          set $var [format %.4g $value]
    }
}

proc CalibrateV5 {} {
    global phaseVoltTweak V5Calibrated V5

    setStatus "Calibrating..."
    setStatus "Reading the energy."
    set E0 \
      [exec cavget -list=L5:AM1:PS2:beamEnergyCC -repeat=n=10,average,pause=1]

    setStatus "Doing phase tweak 1."
    exec cavput -pend=30 -list=L5:PP:phaseAdjAO=$phaseVoltTweak -delta
    after 10000
    set data [exec cavget -list=LI:VD1:y:raw:centroidM,L5:KY:DC2AIQ.VAL -repeat=n=10,average,pause=1]
    set centroid1 [lindex $data 0]
    set phase1 [lindex $data 1]

    setStatus "Doing phase tweak 2."
    exec cavput -pend=30 -list=L5:PP:phaseAdjAO=$phaseVoltTweak -delta=factor=-2
    after 10000
    set data [exec cavget -list=LI:VD1:y:raw:centroidM,L5:KY:DC2AIQ.VAL -repeat=n=10,average,pause=1]
    set centroid2 [lindex $data 0]
    set phase2 [lindex $data 1]
    exec cavput -pend=30 -list=L5:PP:phaseAdjAO=$phaseVoltTweak -delta

    set deltaPhase [expr ($phase2-$phase1)/180*3.1415926]
    setStatus "Phase change was $deltaPhase rad"
    set deltaE [expr $E0*($FlagCal/100.0)*($centroid2-$centroid1)]
    setStatus "Energy change was $deltaE MeV"
    
    set V5 [expr abs($deltaE/$deltaPhase)]
}

proc APSLinacLockInRFSystems {args} {
    set systemList ""
    set unlock 0
    set phase 1
#    set power 1
    set power 0
    set transferPhase 1
#    APSStrictParseArguments {systemList unlock phase power transferPhase}
    APSStrictParseArguments {systemList unlock phase transferPhase}

    if [llength $systemList]==0 return

    if $phase {
        # lock in or unlock phase
        if $unlock {
            exec cavput -pend=30 -list=L -list=[join $systemList ,] -list=:AUTO:PH:modeMO=Disabled
        } else {
            if $transferPhase {
                exec cavput -pend=30 -list=L -list=[join $systemList ,] -list=:AUTO:PH:holdAtMeasdBO=1
            } else {
                exec cavput -pend=30 -list=L -list=[join $systemList ,] -list=:AUTO:PH:modeMO=Hold
            }
        }
    }

    if $power {
        # lock in or unlock power
        if $unlock {
            exec cavput -pend=30 -list=L:L -list=[join $systemList ,] -list=Stabilizer:SDDS.SUSP=1
        } else {
            set allReadbackList {L1:KY:DC2ARF.VAL L3:KY:DC2ARF.VAL \
                                   L4:SD:DC1ARF.VAL L5:SD:DC1ARF.VAL}
            set allSetpointList {L1:KY:DC2ARF:SetpointP L3:KY:DC2ARF:SetpointP \
                                   L4:SD:DC1ARF:SetpointP L5:SD:DC1ARF:SetpointP}
            # find out which systems from the list are not already regulated
            set newSystemList ""
            foreach system {1 3 4 5} readback $allReadbackList setpoint $allSetpointList {
                if [lsearch -exact $systemList $system]!=-1 {
#                    set status [exec cavget -list=L:L${system}StabilizerRC.RUN]
                    set status [exec cavget -list=L:L${system}Stabilizer:SDDS.RUN]
                    if !$status {
                        lappend readbackList $readback
                        lappend setpointList $setpoint
                        lappend newSystemList $system
                    }
                }
            }
            if [llength $newSystemList] {
                set systemList $newSystemList
                APSCAAverageAndTransfer -average 20 -interval 0.5 \
                  -readbackList $readbackList -setpointList $setpointList
#                exec cavput -pend=30 -list=L:L -list=[join $systemList ,] -list=StabilizerRC.SUSP=0
                exec cavput -pend=30 -list=L:L -list=[join $systemList ,] -list=Stabilizer:SDDS.SUSP=0
                after 1000
#                set statusList [exec cavget -list=L:L -list=[join $systemList ,] -list=StabilizerRC.RUN]
                set statusList [exec cavget -list=L:L -list=[join $systemList ,] -list=Stabilizer:SDDS.RUN]
                foreach system $systemList status $statusList {
                    if !$status {
                        setStatus "Warning: failed to resume controllaw on L$system"
                    }
                }
            }
        }
    }
}

proc ProcessAll {} {
    global Rootname bunchLength bunchLengthSigma
    set file0List [lsort [glob -nocomplain $Rootname-*.0]]
    if [llength $file0List]==0 {
        setStatus "Not found: $Rootname-*.0"
        return
    }
    set procList ""
    foreach file0 $file0List {
        set rootname [file rootname $file0]
        if ![file exists $rootname.proc] {
            setStatus "Processing $rootname"
            ProcessSetOfFiles -rootname $rootname
            setStatus "$bunchLength +/- $bunchLengthSigma"
        } else {
            setStatus "$rootname already processed"
        }
        lappend procList $rootname.proc
    }
    if ![llength $procList] {
        setStatus "No files found"
        return
    }
    eval exec sddscombine $procList -pipe=out \
        | sddsexpand -pipe \
        | sddscollapse -pipe \
        | sddsprocess -pipe=in $Rootname.proc \
        -edit=column,Rootname,Filename,%/.proc// 

    exec sddsplot -unsup=y \
        -col=Rootname,BunchLength,BunchLengthSigma -graph=error $Rootname.proc \
        -filter=column,FractionalBunchLengthSigma,0,.5 "-title=Error/Value<0.5" -end \
        -col=Rootname,BunchLength,BunchLengthSigma -graph=error $Rootname.proc \
        -filter=column,FractionalBunchLengthSigma,0,1 "-title=Error/Value<1" -end \
        -col=Rootname,BunchLength,BunchLengthSigma -graph=error $Rootname.proc \
        -filter=column,FractionalBunchLengthSigma,0,2 "-title=Error/Value<2" -end \
        -col=Rootname,BunchLength,BunchLengthSigma -graph=error $Rootname.proc \
        -filter=column,FractionalBunchLengthSigma,0,4 "-title=Error/Value<4" &

}

proc GoToDailyDirectory {} {
    global Rootname
    set DD [APSGoToDailyDirectory]
    cd $DD
    set Rootname [file join $DD $Rootname]
}

proc CheckForBeam {} {
    global BendMagnetName BendSetpoint
    global BendBPMPositionPVName BendBPMIntensityPVName
    global positionTolerance
    switch $BendMagnetName {
        LTPB1 {
            set intensityLimit 100
        }
        default {
            set intensityLimit 0.00005
        }
    }
    if [catch {exec cavget \
                 -list=$BendBPMPositionPVName,$BendBPMIntensityPVName} \
          dataList] {
        setStatus "$dataList"
        return 0
    }
    APSSetVarsFromList -variableList "xpos intensity" \
      -valueList $dataList
    if {$intensity<$intensityLimit} {
        setStatus "Insufficient intensity on bpm."
        return 0
    }
    if [expr abs($xpos-$BendSetpoint)>$positionTolerance] {
        setStatus "Beam position of $xpos more than ${positionTolerance}mm off setpoint of $BendSetpoint on bpm."
        return 0
    }
    return 1
}

proc CalibrateFlag {} {
    global BendSetpoint FlagCal BendMagnetName BendBPMPositionPVName
    global BendCurrentSetpointPVName FlagCentroidPVName BendUseIt
    set oldBendSetpoint $BendSetpoint

    setStatus "Peforming flag calibration"

    switch $BendMagnetName {
        LTPB1 {
            set locationList {-5 5}
        }
        default {
            set locationList {-8.3 1.4}
        }
    }
    foreach location $locationList index {1 2} {
        if $BendUseIt {
            ControlFeedback -mode abort -id Bend -RCPV APS:RunControlSlot0RC
        }
        APSWaitWithUpdate -waitSeconds 5 -updateInterval 1
        set BendSetpoint $location
        if $BendUseIt {
            ControlFeedback -mode start -id Bend -RCPV APS:RunControlSlot0RC
        }
        while {![CheckForBeam]} {
            APSWaitWithUpdate -waitSeconds 5 -updateInterval 1 
        }
        APSWaitWithUpdate -waitSeconds 5 -updateInterval 1
        setStatus "Taking data at location $index"
        set dataList [exec cavget -repeat=num=10,ave,pause=1,sigma \
                        -list=$BendCurrentSetpointPVName,$FlagCentroidPVName,$BendBPMPositionPVName]
        APSSetVarsFromList -variableList "E$index ESigma$index pix$index pixSigma$index x$index xSigma$index" -valueList $dataList
        
        setStatus "positioning error: [expr [set x$index]-$location] +/- [set xSigma$index] mm"
    }
    set dE [expr $E1-$E2]
    set dESigma [expr sqrt(pow($ESigma1,2)+pow($ESigma2,2))]
    set EAve [expr ($E1+$E2)/2.0]
    set EAveSigma $dESigma
    set dpix [expr $pix1-$pix2]
    set dpixSigma [expr sqrt(pow($pixSigma1,2)+pow($pixSigma2,2))]
    set FlagCal [expr abs(100.0*$dE/$EAve/$dpix)]
    set FlagCalSigma [expr abs($FlagCal)*sqrt(pow($dESigma/$dE,2)+pow($EAveSigma/$EAve,2)+pow($dpixSigma/$dpix,2))]
    setStatus "Calibration is $FlagCal +/- $FlagCalSigma %/pixel"
    
    ControlFeedback -mode abort -id Bend -RCPV APS:RunControlSlot0RC
    APSWaitWithUpdate -waitSeconds 5 -updateInterval 1
    set BendSetpoint $oldBendSetpoint
    ControlFeedback -mode start -id Bend -RCPV APS:RunControlSlot0RC
}


APSApplication . -name MeasureBunchLength 
set status ""
APSScrolledStatus .status -parent .userFrame -textVariable status -height 5 -width 60

APSFrame .voltage -parent .userFrame -label "" -packOption "-fill x"
set voltFrame .userFrame.voltage.frame
set V5 0
set V4 0
set UseV4Also 0
set phaseVoltTweak 0.1
APSLabeledEntry .valV5 -parent $voltFrame -label "L5 Voltage (MV): "  \
  -textVariable V5 -contextHelp "Enter the rf voltage available from L5."
APSRadioButtonFrame .useV4 -parent $voltFrame -label "Use V4 also?" \
  -variable UseV4Also -buttonList "Yes No" -valueList "1 0" -orientation horizontal \
  -contextHelp "Indicate whether L4 should be used for the measurement in addition to L5.  Useful if the bunch is very short."
APSLabeledEntry .valV4 -parent $voltFrame -label "L4 Voltage (MV): "  \
  -textVariable V4 -contextHelp "Enter the rf voltage available from L4.  Not relevant if L4 is not used along with L5."

foreach sector {4 5} {
    APSButton .offV$sector -parent $voltFrame -text "L$sector off (LLRF)" \
      -command "TurnLLRF -sector $sector -mode 0" -contextHelp \
      "Turn off the LLRF on L$sector.  Useful for setting up the experiment"
    APSButton .onV$sector -parent $voltFrame -text "L$sector on (LLRF)" \
      -command "TurnLLRF -sector $sector -mode 1" -contextHelp \
      "Turn on the LLRF on L$sector.  Useful for setting up the experiment"
}

APSFrame .phase -parent .userFrame -label "" -packOption "-fill x"
set phaseFrame .userFrame.phase.frame

foreach phase {1 2} {
    APSFrame .ph$phase -parent $phaseFrame -label "" -relief flat
    foreach sector {4 5} {
        set PhaseSetpoint($phase,$sector) -2
        set PhaseSetUp($phase,$sector) 0
        set PhaseFBSetpoint($phase,$sector) 0
        APSLabeledOutput .phase$phase$sector \
          -parent $phaseFrame.ph$phase.frame -label "L$sector phase setpoint \#$phase: " \
          -textVariable PhaseSetpoint($phase,$sector) -packOption "-side left" \
          -contextHelp "Shows the phase setpoint acquired for phase \#$phase for L$sector"
    }
}

foreach phase {1 2} {
    APSButton .recPhase${phase} -parent $phaseFrame -text "Record Phase${phase}" \
      -command "RecordPhase -point $phase" -contextHelp "Records phase \#$phase for L4 and L5"
    APSButton .setPhase${phase} -parent $phaseFrame -text "Send Phase${phase}" \
      -command "GoToPhase -point $phase" -contextHelp "Sends phase \#$phase for L4 and L5"
}

proc FillRfFeedbackFrame {widget} {
    global lockPhase lockPower
    global unlockPhase unlockPower
    global useRfPwrFdbck useRfPhsFdbck

#    APSFrame .auto -parent $widget -label "Automatic modes" \
#      -contextHelp "Select which systems to automatically use rf feedback on when the measurement is performed.  Will override feedback settings established with the manual mode controls."
#    set w $widget.auto.frame
#    APSCheckButtonFrame .cb1 -parent $w \
#      -label "Use feedback on rf power: " -orientation horizontal \
#      -variableList "useRfPwrFdbck(1) useRfPwrFdbck(2) useRfPwrFdbck(3) useRfPwrFdbck(4) useRfPwrFdbck(5)" \
#      -buttonList "1 2 3 4 5"
#    APSCheckButtonFrame .cb2 -parent $w \
#      -label "Use feedback on rf phase: " -orientation horizontal \
#      -variableList "useRfPhsFdbck(1) useRfPhsFdbck(2) useRfPhsFdbck(3)" \
#      -buttonList "1 2 3"
#    foreach sector {1 2 3 4 5} {
#        set useRfPhsFdbck($sector) 0
#        set useRfPwrFdbck($sector) 0
#    }

    APSFrame .manual -parent $widget -label "Immediate manual controls"

    APSFrame .lock -parent $widget.manual.frame -label "" -relief flat \
      -contextHelp "Use these widgets to activate and deactivate rf feedbacks for various systems.  N.B.: actions performed with these buttons may be overridden when the measurement is started!"

    set w $widget.manual.frame.lock.frame
    foreach sector {1 2 3 4 5} {
        APSButton .$sector -parent $w -text "Lock L$sector" \
            -command "APSLinacLockInRFSystems -systemList $sector -power \$lockPower -phase \$lockPhase" \
            -contextHelp "Locks phase and/or power, as selected, for L$sector"
    }
    set lockPhase 1
    set lockPower 0
    APSCheckButtonFrame .cb -parent $w -label "" -orientation horizontal \
        -variableList "lockPhase lockPower" -buttonList "Phase Power" -contextHelp \
        "Provides selection of what to lock."
        
    APSFrame .unlock -parent $widget.manual.frame -label "" -relief flat
    set w $widget.manual.frame.unlock.frame
    foreach sector {1 2 3 4 5} {
        APSButton .$sector -parent $w -text "Unlock L$sector" \
            -command "APSLinacLockInRFSystems -systemList $sector -unlock 1 -power \$unlockPower -phase \$unlockPhase" \
            -contextHelp "Unlocks phase and/or power, as selected, for L$sector"
    }
    set unlockPhase 1
    set unlockPower 0
    APSCheckButtonFrame .cb -parent $w -label "" -orientation horizontal \
        -variableList "unlockPhase unlockPower" -buttonList "Phase Power" \
        -contextHelp "Provides selection of what to lock."

}

proc FillFeedbackFrame {widget args} {
    set label ""
    set RCPV ""
    set id ""
    set bend 0
    APSStrictParseArguments {label RCPV id bend}

    set w $widget
    global ${id}Setpoint ${id}Gain ${id}Samples
    if $bend {
        global BendMagnetName ${id}UseIt positionTolerance
        set ${id}UseIt 0
        APSFrame .lef0 -parent $w -label "" -relief flat
        APSRadioButtonFrame .use -parent $w.lef0.frame -label "Use: " \
          -orientation horizontal -variable ${id}UseIt -packOption "-side left" \
          -valueList {1 0} -buttonList {Yes No} -contextHelp \
          "Select whether this feedback should be used during measurements."
        APSRadioButtonFrame .bend -parent $w.lef0.frame -label "Bend Magnet: " \
          -orientation horizontal -variable BendMagnetName -packOption "-side left" \
          -valueList {L5AM1 LTPB1} -buttonList {L5:AM1 LTP:B1} \
          -commandList [list "ChangeBendMagnet -id $id -name L5AM1" \
                          "ChangeBendMagnet -id $id -name LTPB1"] \
          -contextHelp "Select which dipole is used for the bunch length measurement."
        APSLabeledEntry .le0 -parent $w -label "Tolerance (mm): " \
          -textVariable positionTolerance  -contextHelp \
          "Select position range on the BPM that is acceptable for data taking."

    } else {
        global L2PwrUseIt
        set L2PwrUseIt 0
        APSFrame .lef0 -parent $w -label "" -relief flat
        APSRadioButtonFrame .use -parent $w.lef0.frame -label "Use: " \
          -orientation horizontal -variable ${id}UseIt -packOption "-side left" \
          -valueList {1 0} -buttonList {Yes No} \
          -contextHelp "Select whether this feedback should be used during measurements."
    }

    APSLabeledEntry .le1 -parent $w -label "BPM setpoint: " \
      -textVariable ${id}Setpoint \
      -contextHelp "Enter the BPM setpoint to feed back to." 
    APSLabeledEntry .le2 -parent $w -label "Gain factor: " \
      -textVariable ${id}Gain \
      -contextHelp "Enter the gain factor for feedback."
    APSLabeledEntry .le3 -parent $w -label "Samples: " \
      -textVariable ${id}Samples \
      -contextHelp "Enter the number of samples to average between corrections.  \
A sample is taken every second.  The interval between corrections is 1*Samples+1."

    APSButton .start -parent $w -text Start -command \
      "ControlFeedback -mode start -id $id -RCPV $RCPV" \
      -contextHelp "Start the feedback"
    APSButton .abort -parent $w -text Abort -command \
      "ControlFeedback -mode abort -id $id -RCPV $RCPV" \
      -contextHelp "Abort the feedback"
    APSButton .suspend -parent $w -text Suspend -command \
      "ControlFeedback -mode suspend -id $id -RCPV $RCPV" \
      -contextHelp "Suspend the feedback"
    APSButton .resume -parent $w -text Resume -command \
      "ControlFeedback -mode resume -id $id -RCPV $RCPV" \
      -contextHelp "Resume after suspending."
    APSButton .info -parent $w -text Info -command \
      "exec medm -attach -x -macro \"RCPV=$RCPV\" ./sr/psApp/APSRunControlSingle.adl &" \
      -contextHelp "Show MEDM screen with feedback status."
}

proc ChangeBendMagnet {args} {
    set name L5:AM1
    set id Bend
    APSStrictParseArguments {name id}
    global BendSetpoint BendGain BendSamples
    global BendBPMPositionPVName BendBPMIntensityPVName \
      BendDescription BendRootname FlagCal
    global BendCurrentSetpointPVName FlagCentroidPVName
    switch $name {
        L5AM1 {
            set FlagCal 0.054
            set BendRootname L5AM1-BPM
            set BendSetpoint 0
            set BendGain -0.5
            set BendSamples 2
            set BendBPMPositionPVName L5:FCN:BPM.XPOS
            set BendBPMIntensityPVName L5:FCN:BPM.VAL
            set BendDescription "L5:AM1->L5:FCN:BPM"
            set BendCurrentSetpointPVName L5:AM1:PS2:setCurrentAO
            set FlagCentroidPVName LI:VD1:y:raw:centroidM
        }
        LTPB1 {
            set FlagCal 0.045
            set BendRootname LTPH3-LTPPH3
            set BendSetpoint 0
            set BendGain 0.25
            set BendSamples 3
            set BendBPMPositionPVName LTP:PH3:x
            set BendBPMIntensityPVName LTP:PH3:sum
            set BendDescription "LTP:H3->LTP:PH3"
            set BendCurrentSetpointPVName L5:AM1:PS2:setCurrentAO
            set FlagCentroidPVName LI:VD1:x:raw:centroidM
        }
        default {
            return -code error "Invalid bend name: $name"
        }
    }
}

set L2PwrSetpoint 0
set L2PwrGain 0.5
set L2PwrSamples 1
set BendMagnetName L5AM1
ChangeBendMagnet -id Bend -name $BendMagnetName

set feedbackFrameList [APSTabFrame .feedback -parent .userFrame -label "Feedbacks" \
    -labelList [list "L2Pwr->L3:P1" "rf systems"] \
    -width 800 -height 250 ]

#FillFeedbackFrame [lindex $feedbackFrameList 0] \
#  -id Bend -RCPV APS:RunControlSlot0RC -bend 1
FillFeedbackFrame [lindex $feedbackFrameList 0] -id L2Pwr -RCPV APS:RunControlSlot1RC
FillRfFeedbackFrame [lindex $feedbackFrameList 1] 

APSFrame .main -parent .userFrame -label "Run Measurement"
set runFrame .userFrame.main.frame
set Rootname ""
set DataFileIndex -1
set Samples 20
set extraMonitorFile ""
APSLabeledEntry .le1 -parent $runFrame -label "Output rootname: " -textVariable Rootname \
    -width 60
APSLabeledEntry .le2 -parent $runFrame -label "Output index: " -textVariable DataFileIndex
APSLabeledEntry .le3 -parent $runFrame -label "Samples per point: " -textVariable Samples 
APSLabeledEntry .le4 -parent $runFrame -label "Extra monitor file: " \
    -textVariable extraMonitorFile  -width 60 

APSFrame .de -parent $runFrame -label "Measured Results" 
foreach i {0 1 2} {
    set dERMS($i) 0
    APSLabeledEntry .e${i}rms -parent $runFrame.de.frame -label "dERMS$i (MeV): " \
        -textVariable dERMS($i) -gridPack "-column $i -row 0" -width 6
}

set bunchLength 0
set bunchLengthSigma 0
APSLabeledEntry .bl -parent $runFrame.de.frame -label "Bunch length (ps) mean: " \
  -textVariable bunchLength -gridPack "-column 0 -columnspan 2 -row 1" -width 6
APSLabeledEntry .blsig -parent $runFrame.de.frame -label "sigma: " \
  -textVariable bunchLengthSigma -gridPack "-column 2 -row 1" -width 10


set gunChoice RG1/RG2
APSRadioButtonFrame .rbgun -parent $runFrame -label "Gun: " \
  -variable gunChoice \
  -orientation horizontal -valueList {RG1/RG2 PC} \
  -buttonList {RG1/RG2 PC} \
  -commandList [list "ChangeGun -type RG1/RG2" "ChangeGun -type PC"] \
  -contextHelp \
  "Indicate which kind of gun you are using.  Affects the default power and phase locking."
ChangeGun -type $gunChoice

set doPrompting 1
APSRadioButtonFrame .prompt -parent $runFrame -label "Prompting: " \
  -variable doPrompting \
  -orientation horizontal -valueList {1 0} \
  -buttonList {Yes No}  -contextHelp \
  "Select whether to prompt between measurement steps.  A good idea if you need to adjust the camera, for example."

set doCheckForBeam 0
#APSRadioButtonFrame .check -parent $runFrame -label "Check for beam on BPM: " \
#  -variable doCheckForBeam \
#  -orientation horizontal -valueList {1 0} \
#  -buttonList {Yes No}  -contextHelp \
#  "Select whether to check for beam on the BPM.  A good idea for RG1/RG2, but not for PC gun."

set FlagCal 0.0534
APSLabeledEntry .lefl10 -parent $runFrame -label "FL10 Cal (%/pixel): " \
    -textVariable FlagCal -contextHelp "Allows entry of the FL10 flag calibration.  Also displays the calibration result from auto calibration."
APSButton .cal -parent $runFrame -text "Calibrate FL10" -command CalibrateFlag \
  -contextHelp "Calibration the flag in %/pixel."
APSButton .meas -parent $runFrame -text "Measure bunch length" \
  -command MeasureBunchLength  -contextHelp "Actually do a measurement."
set abortMeasurement 0
APSButton .abort -parent $runFrame -text Abort -command \
    {set abortMeasurement 1} -contextHelp "Abort measurement in progress"
APSButton .proc -parent $runFrame -text "Process all" -command "ProcessAll" \
  -contextHelp "Process all of the data in the series so far."
APSButton .gtdd -parent $runFrame -text "Go to daily dir" -command \
  GoToDailyDirectory

