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

# $Log: not supported by cvs2svn $

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.1 $ \$Author: emery $"

APSApplication . -name SRSingleBunchParameterScan -version $CVSRevisionAuthor

set status Ready.
APSScrolledStatus .status -parent .userFrame -textVariable status -width 80 -height 8

set IDNumber 1
set RunIndex 0
set LowerLimit 7000
set UpperLimit 9500
set DeltaVoltage 500
set outputDirectory [pwd]
set TimeToWait 120
set NumberInGroup 1
set collectVSA 0

APSLabeledEntry .dir -parent .userFrame -label "Directory: " \
    -textVariable outputDirectory -width 70 -contextHelp  \
    "Directory in which to put data files."
APSButton .daily -parent .userFrame -packOption "-side top -anchor w" \
   -text "Go to daily directory" \
   -command {set outputDirectory [APSGoToDailyDirectory -subdirectory energyAperture]} \
   -contextHelp "Setting daily directory in which to put data files."
APSLabeledEntry .index -parent .userFrame -label "Index: " -textVariable RunIndex -width 10 \
    -numberButtons 1 -contextHelp \
    "The index of the run.  The file rootname will be freqScan-<Index> ."

APSLabeledEntry .lowerLimit -parent .userFrame -label "Lower Limit (kV): " \
    -textVariable LowerLimit -width 10 -contextHelp \
    "Lower voltage value for the scan."
APSLabeledEntry .upperLimit -parent .userFrame -label "Upper Limit (kV): " \
    -textVariable UpperLimit -width 10 -contextHelp \
    "Upper voltage value for the scan."
APSLabeledEntry .delta -parent .userFrame -label "Delta (kV): " \
  -textVariable DeltaVoltage -width 10 -contextHelp \
  "Voltage step to use in scan."
APSLabeledEntry .timeToWait -parent .userFrame -label "Time to wait (s): " \
  -textVariable TimeToWait -width 10 -contextHelp \
  "Time to wait for data collection at each voltage."
APSLabeledEntry .numInGroup -parent .userFrame -label "Number in BPM group: " \
  -textVariable NumberInGroup -width 10 -contextHelp \
  "Time to wait for data collection at each voltage."
APSRadioButtonFrame .vsaRB -parent .userFrame \
  -label "Use VSA: " -variable collectVSA \
  -buttonList {Yes No} -valueList {1 0} \
  -orientation horizontal 

APSFrame .controllaw -parent .userFrame -label "Controllaw" 
set w .userFrame.controllaw.frame
set voltageToSend 9000

proc SendRFVoltage {args} {
    APSParseArguments {voltage}
   # 15 is number of cavities working
   # S37 has a cavity blanked out
    set adjustedV37 [expr 3 * $voltage / 15.0 ]
    set adjustedV [expr 4 * $voltage / 15.0 ]
    exec cavput -list=S -list=3 -list=:C:cavTotalGV:SetptAO=$adjustedV37
    exec cavput -list=S -list=1,2,4 -list=:C:cavTotalGV:SetptAO=$adjustedV
    return
}
    
APSLabeledEntry .vset -parent $w -label "Voltage to send" \
  -textVariable voltageToSend \
  -contextHelp "Entry the voltage that you want to send to the controllaw PVs to regulate the rf voltage." 
APSButton .vsend -parent $w -text "Send voltage" \
  -command {SendRFVoltage -voltage $voltageToSend} \
  -contextHelp "Sends the above voltage to the PVs where controllaw will see them."
APSButton .controllaw -parent $w -text "Launch controllaw" \
  -command {exec controllaw -controlList srrfErr &
      exec SRSetRFControllawMode & \
          } \
  -contextHelp "Launches application for starting and controlling rf controllaw for gap voltage regulation. Also launches application for setting the mode of the RF controllaw."


APSButton .setup -parent .userFrame -text "Setup VSA" \
  -command {setupVSA}  \
  -contextHelp "Sets up the hpvec VSA for synchrotron frequency measurement."

APSButton .run -parent .userFrame -text Run -command \
  {RunEnergyApertureScan -index $RunIndex -outputDir $outputDirectory \
     -lowerLimit $LowerLimit -upperLimit $UpperLimit \
     -voltageSpacing $DeltaVoltage -numInGroup $NumberInGroup \
     -timeToWait $TimeToWait -abortVariable AbortEnergyApertureScan \
     -statusCallback "APSSetVarAndUpdate status" \
     -collectVSA $collectVSA; bell ; bell } 
set AbortEnergyApertureScan 0
APSButton .abort -parent .userFrame -text Abort -command \
    {APSSetVarAndUpdate status "Aborting..."; set AbortEnergyApertureScan 1}

APSButton .proc -parent .userFrame -text Process -command \
    {APSSetVarAndUpdate status "Processing data..."; processScanData -outputDir $outputDirectory -index $RunIndex}

proc processScanData {args} {
    set index 0
    set outputDir ""
    APSStrictParseArguments {index outputDir}
    scan $index "%ld" index
    set index [format "%02ld" $index]
    set outputRoot $outputDir/voltScan-$index
    set dataFile $outputRoot.scalars.gz
    if ![file exists $dataFile] {
        set dataFile $outputRoot.scalars
        if ![file exists $dataFile] {
            return -code error "Can't find file $outputRoot.scalars or $outputRoot.scalars.gz"
        }
    }
    exec sddsbreak $outputRoot.scalars.gz -pipe=out \
      -changeof=S4:C:cavTotalGV:SetptAO,amount=50 \
      | sddsprocess -pipe \
      -proc=S35DCCT,slope,%sSlope,functionOf=TimeOfDay \
      -proc=S35DCCT,average,%sAverage \
      -proc=?emittance,average,%sAverage \
      -proc=Coupling,average,%sAverage \
      -proc=S?:C:cavTotalGapVolt,average,%sAverage \
      -proc=S*cerenkovCount,average,%sAverage \
      | sddsprocess -pipe \
      "-def=para,Rate,S35DCCTSlope S35DCCTAverage / chs" \
      "-def=para,Lifetime,Rate 1 swap /,units=hours" \
      | sddscollapse -pipe \
      | sddsconvert -pipe \
      -edit=col,*Average,%/Average// \
      | sddsprocess -pipe=in $outputRoot.proc \
      "-def=col,NormLifetime,Lifetime Coupling sqrt /,description=Lifetime normalized to 1% coupling,units=h" \
      "-def=col,GapVoltage,rfVoltage 1e3 /,units=MV"

    exec sddsplot $outputRoot.proc \
      -grap=sym,conn=sub,sca=2,vary=sub -leg \
      -col=GapVoltage,Lifetime \
      -col=GapVoltage,NormLifetime -yscale=id=norm \
      -col=GapVoltage,S39:cerenkovCount  -yscale=id=counts \
      &
    return
}

proc RunEnergyApertureScan {args} {
    set index 0
    set statusCallback APSNoOp
    set outputDir ""
    set lowerLimit 0
    set upperLimit 10000
    set voltageSpacing 100
    set timeToWait 120
    set abortVariable ""
    set numInGroup 1
    set collectVSA 0
    APSStrictParseArguments {index statusCallback outputDir lowerLimit upperLimit \
                               timeToWait voltageSpacing abortVariable numInGroup \
                               collectVSA}

    scan $index "%ld" index
    set index [format "%02ld" $index]
    if ![file exists $outputDir] {
        exec mkdir -p $outputDir
    }
    set outputRoot $outputDir/voltScan-$index

    if {![file exists $outputRoot.scalars.gz]} {
        set tmpRoot /tmp/[APSTmpString]

        if [catch {MakeSweep -lower $lowerLimit -upper $upperLimit \
                     -voltageSpacing $voltageSpacing -numInGroup $numInGroup \
                     -timeToWait $timeToWait -abortVariable $abortVariable \
                     -outputRoot $outputRoot -statusCallback $statusCallback \
                     -collectVSA $collectVSA \
                 } result] {
            eval $statusCallback {"$result"}
            return -code error $result
        }
        if [catch {SendRFVoltage -voltage 9000} result] {
            return -code error $result
        }
        eval $statusCallback {"Measurement done."}
    } else {
        eval $statusCallback {"Data already exists with that name."}
    }
}

proc InjectToLimit {args} {
    set statusCallback APSNoOp
    set initialCurrentTarget 5
    set targetIncrement 0.5
    set useEmittance 0
    set xEmittanceThreshold 2.8
    set yEmittanceThreshold 2.8e-2
    APSStrictParseArguments {statusCallback abortVariable \
                               multiplets initialCurrentTarget targetIncrement \
                               useEmittance xEmittanceThreshold yEmittanceThreshold}
    # units of mA    
    set currentDropCriterion 1

    if [string length $abortVariable] {
        global $abortVariable
        set $abortVariable 0
    }

    set targetCurrent $initialCurrentTarget
    global S35BeamCurrent
    if [pv getw S35BeamCurrent] {
        global errorCode
        return -code error "InjectToLimit: $errorCode"
    }
    if [catch {exec cavget -list=S:VID1:filtered -list=X,Y -list=emittance} emittances] {
        return -code error "InjectToLimit: $emittances"
    }
    set xEmittance [lindex $emittances 0]
    set yEmittance [lindex $emittances 1]

    set previousCurrent $S35BeamCurrent
    set previousXemittance $xEmittance
    set previousYemittance $yEmittance

    set LimitReached 0
    if {$targetCurrent < $S35BeamCurrent} {
        set targetCurrent [expr $S35BeamCurrent + $targetIncrement]
        eval $statusCallback {"Setting target to $targetCurrent mA..."}
    }
    set repeat 10
    while {$repeat != 0} {
        if {[string length $abortVariable] && [subst \$$abortVariable]} {
            break
        }
        eval $statusCallback {"Filling to target of $targetCurrent mA..."}
        TogglePulsedMagnetEnables -location GuntoBoosterExt
        if [catch {DoIOCBunchTrainInjection -start 0 -interval 100 -number 1 \
                     -stopAt $targetCurrent -cycles 10 \
                     -multiplet $multiplets -callback $statusCallback} result] {
            return -code error "InjectToLimit: $result"
        }
        SetPulsedMagnetEnables -state 0 -location All
        
        eval $statusCallback {"Disabling thick septum pulsing."}
        if [catch {exec cavput -list=Mt:Ddg3chan0.GATE=Disabled} result] {
            return -code error "InjectToLimit: $result"
        }

# check whether current increased since last time
        if [pv getw S35BeamCurrent] {
            global errorCode
            return -code error "InjectToLimit: $errorCode"
        }
        set presentCurrent $S35BeamCurrent
        if [catch {exec cavget -list=S:VID1:filtered -list=X,Y -list=emittance} emittances] {
            return -code error "InjectToLimit: $emittances"
        }
        set xEmittance [lindex $emittances 0]
        set yEmittance [lindex $emittances 1]
        
        incr repeat -1
        if {$repeat > 1 && $presentCurrent >= $previousCurrent} {
            eval $statusCallback {"Single bunch limit not reached yet at $presentCurrent mA..."}
            if {$presentCurrent >= $targetCurrent} {
            set targetCurrent [expr $presentCurrent + $targetIncrement]
            }
        } elseif {$presentCurrent < $previousCurrent - $currentDropCriterion}  {
            eval $statusCallback {"Single bunch current dropped to $presentCurrent mA. Reinjecting..."}
        } elseif {$presentCurrent < $previousCurrent}  {
            eval $statusCallback {"Single bunch limit reached at $previousCurrent mA..."}
            set repeat 0
        }
        set previousCurrent $presentCurrent
    }
}

proc MakeSweep {args} {
    set lower 0
    set upper 10000
    set outputRoot ""
    set current 20
    set statusCallback APSNoOp
    set voltageSpacing 100
    set timeToWait 120
    set abortVariable ""
    set numInGroup 1
    set collectVSA 0
    APSStrictParseArguments {lower upper outputRoot current \
                               statusCallback voltageSpacing timeToWait \
                               abortVariable numInGroup collectVSA}

    set tmpRoot /tmp/[APSTmpString]

    set inputDir /home/helios/oagData/sr/energyApertureScan/monitorInputs

    if [string length $abortVariable] {
        global $abortVariable
        set $abortVariable 0
    }
    if [catch {APSBunchTrainConnect} result] {
        return -code error "MakeSweep: $result"
    }
    # remove this injection since it is going to be done on the
    # first iteration of the loop.
    if {0} {
    if [catch {InjectToLimit -statusCallback $statusCallback -abortVariable $abortVariable \
                 -multiplets $numInGroup} result] {
        return -code error "MakeSweep: $result"
    }
    }
    eval $statusCallback {"Starting asynchronous scalar monitor process."}
    set smPID [exec sddsmonitor $inputDir/scalars.mon $outputRoot.scalars -erase -precision=double \
                 -interval=1,s -time=1,h &]
    if [catch {exec kill -STOP $smPID} result] {
        return -code error "MakeSweep: $result"
    }

    set index 0
    set voltage $upper

    while {$voltage>=$lower} {
        if {[string length $abortVariable] && [subst \$$abortVariable]} {
            break
        }
        if [catch {SendRFVoltage -voltage $voltage} result] {
            catch {exec kill $smPID}
            return -code error "MakeSweep: $result"
        }
        eval $statusCallback {"Waiting for voltage to settle to $voltage MV."}
        APSWaitWithUpdate -waitSeconds 10 -updateInterval 1

        if [catch {InjectToLimit -statusCallback $statusCallback -abortVariable $abortVariable \
                     -multiplets $numInGroup} result] {
            return -code error "MakeSweep: $result"
        }

        # start data collection
        if [catch {exec kill -CONT $smPID} result] {
            catch {exec kill $smPID}
            return -code error "MakeSweep: $result"
        }
        if $collectVSA {
            if [catch {exec hpSocketSend hpvecsr.aps4.anl.gov "ABOR;*WAI"} result] {
                catch {exec kill $smPID}
                return -code error "MakeSweep: $result"
            }
        }
        eval $statusCallback {"Taking data and waiting for averaging."}
        APSWaitWithUpdate -waitSeconds $timeToWait -updateInterval 1
        if $collectVSA {
            eval $statusCallback {"Taking synch tune data."}
            if [catch {exec hpSocketSend hpvecsr.aps4.anl.gov PAUS 
                after 1000
                exec hpVecTrace -hpvecsr a $outputRoot.stune-[format %03ld $index]} result] {
                catch {exec kill $smPID}
                return -code error "MakeSweep: $result"
            }
            if ![file exists $outputRoot.stune-[format %03ld $index]] {
                eval $statusCallback {"Warning: file $outputRoot.stune-[format %03ld $index] was not created."}
            }
        }
        
        # check for no beam left
        global S35BeamCurrent
        if [pv getw S35BeamCurrent] {
            catch {exec kill $smPID}
            global errorCode
            return -code error "MakeSweep: $errorCode"
        }
        eval $statusCallback {"$S35BeamCurrent mA left."}
        if {[expr $S35BeamCurrent<0.1]} {
            eval $statusCallback {"Insufficient current--stopping scan."}
            break
        }

        # halt data collection for ramping
        if [catch {exec kill -STOP $smPID} result] {
            catch {exec kill $smPID}
            return -code error "MakeSweep: $result"
        }

        set voltage [expr $voltage-abs($voltageSpacing)]
        incr index
    }
    # resume data collection process
    if [catch {exec kill -CONT $smPID} result] {
        catch {exec kill $smPID}
        return -code error "MakeSweep: $result"
    }

    eval $statusCallback {"Killing data collection processes."}
    catch {exec kill $smPID}

    eval $statusCallback {"Enabling thick septum pulsing."}
    if [catch {exec cavput -list=Mt:Ddg3chan0.GATE=Enabled} result] {
        return -code error $result
    }

    catch {eval exec gzip $outputRoot.scalars}
    catch {eval exec gzip [glob $outputRoot.stune*]}
    eval $statusCallback {"Sweep done."}
}

proc setupVSA {} {
    set hpvsaSelection hpvecsr
    set streamID [APSOpenTelnetStream -IPaddress $hpvsaSelection]
    APSWriteToTelnetStream -command "MMEM:LOAD:STAT 1,'NVRAM:FSYNCH'" -streamID $streamID

    APSSetVarAndUpdate status "Wait for VSA setup..."
    update
    set operationCondition 1
    while {$operationCondition} {
        after 1000
        set operationCondition [APSWriteToTelnetStream -command "STAT:OPER:COND?" -streamID $streamID]
        update
    }
    APSCloseTelnetStream -streamID $streamID
    APSSetVarAndUpdate status "VSA setup done."
    return 1
}
