#!/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
APSApplication . -name SRFindSextupoleCenter -version 1 \
    -overview {This application finds the magnetic center of a storage ring sextupole by a combination of tune measurement and beam bumps.}

set sextConfigStatus ""
APSScrolledStatus .status -parent .userFrame -textVariable sextConfigStatus  \
    -width 100
set sextList {A:S1 A:S2 A:S3 B:S3 B:S2 B:S1}
APSSRSectorButtons .sextButtons -parent .userFrame -rootname sext -orientation horizontal \
        -label "Sextupole selections" -description "Sextupole selections" \
        -itemList $sextList -packOption "-side top" \
        -itemLabelList $sextList

APSFrame .controls -parent .userFrame -label "Experiment controls" -packOption "-side top"
set w .userFrame.controls.frame
set setCurrent(1) 0
set postChangeWait(1) 10
set setCurrent(2) 200
set postChangeWait(2) 10
set outputDir [pwd]
set outputRoot ""
set tunePlane y
set twissFile /home/helios/oagData/sr/sextPosition/inputFiles/nuy19.3-01.twi
set iterationLimit 10
set centeringTolerance 0.02
set steeringTimeLimit 60
set steeringInterval 1
set steeringGain 0.4

APSFrameGrid .current -parent $w -xList {x1 x2} -yList {y1 y2 y3 y4}
set w2 $w.current
APSLabeledEntry .firstCurrent -parent $w2.x1.y1 -label "First current (A): " \
  -textVariable setCurrent(1) -contextHelp \
  "First value of sextupole current to use."
APSLabeledEntry .minWait -parent $w2.x1.y2 -label \
  "Wait time for orbit correction   \nafter setting first current (s): " \
  -textVariable postChangeWait(1) -contextHelp \
  "Seconds to wait after setting sextupole to first current. During this time, orbit correction should be on-going.  Allow enough time for the power supply to settle and orbit correction to converge.  The wait time may be reduced if the first setpoint is known to be close to the nominal setpoint of the power supply."
APSLabeledEntry .secondCurrent -parent $w2.x2.y1 -label "Second current (A): " \
  -textVariable setCurrent(2) -contextHelp \
  "Second value of sextupole current to use."
APSLabeledEntry .secondWait -parent $w2.x2.y2 -label \
  "Wait time for orbit correction   \nafter setting second current (s): " \
  -textVariable postChangeWait(2) -contextHelp \
  "Seconds to wait after setting sextupole to second current. During this time, orbit correction should be on-going.  Allow enough time for the power supply to settle and orbit correction to converge."

APSLabeledEntry .iterLimit -parent $w2.x1.y3 -label \
  "Maximum number of centering iterations" -textVariable iterationLimit \
  -contextHelp \
  "The maximum number of iterations of the centering algorithm.  Each iteration consists of setting the sextupole to the minimum and maximum values, measuring the tunes, then steering to the predicted center."
APSLabeledEntry .toler -parent $w2.x2.y3 -label \
  "Centering tolerance (mm)" -textVariable centeringTolerance \
  -contextHelp \
  "The tolerance on finding the center.  When a measurement of the beam position yields a value less than this amount, the centering iteration terminates."

APSLabeledEntry .timeLim -parent $w2.x1.y4 -label \
    "Time for steering (s)" -textVariable steeringTimeLimit -contextHelp \
    "The number of seconds taken to steer the beam toward the center for each iteration."
APSLabeledEntry .interval -parent $w2.x1.y4 -label \
    "Interval for steering (s)" -textVariable steeringInterval -contextHelp \
    "The number of seconds between iterations of the steering correction for centering."
APSLabeledEntry .gain -parent $w2.x2.y4 -label \
    "Gain for steering" -textVariable steeringGain -contextHelp \
    "The gain of the steering controllaw used to center the orbit in the sextupole."

APSRadioButtonFrame .tunePlane -parent $w -label "Tune measurement plane: " -orientation horizontal \
    -variable tunePlane -buttonList {x/H y/V} -valueList {x y} \
    -contextHelp "Choose the plane for tune measurements.  You must indicate to this script which tune you've set up to measure.  Use x (y) tune when the beta function is expected to be larger in the x (y) plane."

APSFrameGrid .output -parent $w -xList {x1 x2} -yList {y1 y2}
set w1 $w.output
APSLabeledEntry .dirname -parent $w1.x1.y1 -label \
  "Output directory: " -textVariable outputDir -width 60 -contextHelp \
  "Enter the name of the directory in which to put files."
APSButton .daily -parent $w1.x2.y1 -packOption "-side top -anchor e" \
   -text "Go to daily directory" \
   -command {set outputDir [APSGoToDailyDirectory -subdirectory sextupoleOffset]} \
   -contextHelp "Setting daily directory in which to put data files."
APSLabeledEntry .rootname -parent $w1.x1.y2 -label \
  "Output rootname: " -textVariable outputRoot -width 60 -contextHelp \
  "Enter the rootname for the output files.  The sextupole name will be appended to the rootname to make filenames."
APSLabeledEntry .twissfile -parent $w -label \
  "Twiss parameters file: " -textVariable twissFile -width 80 -contextHelp \
  "Enter the name of the directory in which to put files."

APSButton .run -parent $w -text Run -contextHelp \
  "Runs the measurements starting at the lowest numbered sectors and proceeding to higher numbered sectors." \
  -command "RunMeasurements $w"
APSButton .pause -parent $w -text Pause -contextHelp \
  "Pauses the present measurement." -command "PauseMeasurement $w"
APSButton .resume -parent $w -text Resume -contextHelp \
  "Resumes measurements following a pause." -command "ResumeMeasurement $w"
APSButton .abort -parent $w -text Abort -contextHelp \
  "Aborts the present measurement and all subsequent measurements." \
  -command "AbortMeasurement $w"
set pauseRequested 0
set abortRequested 0
set resumeRequested 0
APSDisableButton $w.abort.button
APSDisableButton $w.pause.button
APSDisableButton $w.resume.button
update

set inputFileDir /home/helios/OAG/oagData/sr/sextPosition/inputFiles
set xrefDir $OAGGlobal(SRLatticesDirectory)/default
set hpSID ""

proc PauseMeasurement {widget} {
    global pauseRequested resumeRequested
    set pauseRequested 1
    set resumeRequested 0
    APSEnableButton $widget.resume.button
    APSDisableButton $widget.pause.button
}

proc ResumeMeasurement {widget} {
    global pauseRequested resumeRequested
    set pauseRequested 0
    set resumeRequested 1
    APSEnableButton $widget.pause.button
    APSDisableButton $widget.resume.button
}

proc AbortMeasurement {widget} {
    global abortRequested
    set abortRequested 1
    APSSetVarAndUpdate sextConfigStatus "Abort requested..."
    APSDisableButton $widget.abort.button
    APSDisableButton $widget.pause.button
    APSDisableButton $widget.resume.button
    APSEnableButton $widget.run.button
}

proc RunMeasurements {widget} {
    global abortRequested pauseRequested resumeRequested sextList
    global hpSID outputDir outputRoot
    global setCurrent postChangeWait tunePlane
    global iterationLimit centeringTolerance
    global steeringTimeLimit steeringInterval steeringGain twissFile
    if ![string length $hpSID] {
        if [catch {APSOpenTelnetStream -IPaddress hpvecsr.aps4.anl.gov} hpSID] {
            set sextConfigStatus $hpSID
        } else {
            set sextConfigStatus "Connected to HPVEC"
        }
    }
    
    if ![string length $outputRoot] {
        APSSetVarAndUpdate sextConfigStatus "Output root not given."
        return
    }

    set pauseRequested 0
    set abortRequested 0
    set resumeRequested 0
    APSEnableButton $widget.abort.button
    APSEnableButton $widget.pause.button
    APSDisableButton $widget.resume.button
    APSDisableButton $widget.run.button

    set alreadyThere ""
    for {set sector 1} {$sector<=40} {incr sector} {
        foreach sext $sextList {
            set flagName sextS${sector}$sext
            global $flagName
            if ![set $flagName] continue
            set sName S${sector}$sext
            set fileRoot $outputDir/$outputRoot-$sName
            set filesFound [glob -nocomplain $fileRoot.\[12\].*]
            if [llength $filesFound] {
                lappend alreadyThere $filesFound
            }
        }
    }
    if [llength $alreadyThere] {
        switch \
          [APSMultipleChoice [APSUniqueName .] -question \
             "[llength $alreadyThere] $outputDir/$outputRoot.* files exist for sextpoles you've chosen." \
             -labelList {"Remove old files" "Skip those sextupoles" "Abort"} \
             -returnList {remove skip abort}] {
                 remove {
                     if [catch {eval exec rm -f $alreadyThere} result] {
                         APSSetVarAndUpdate sextConfigStatus "$result"
                         return
                     }
                 }
                 abort {
                     APSSetVarAndUpdate sextConfigStatus "Measurement aborted."
                     return
                 }
             }
    }

    for {set sector 1} {$sector<=40} {incr sector} {
        foreach sext $sextList {
            set flagName sextS${sector}$sext
            if ![set $flagName] continue
            set sName S${sector}$sext
            set fileRoot $outputDir/$outputRoot-$sName
            set filesFound [glob -nocomplain $fileRoot.\[12\].*]
            if [llength $filesFound] {
                APSSetVarAndUpdate sextConfigStatus "Skipping $sName---data already exists"
                continue
            }
            if [catch {exec cavget -list=$sName -list=:CurrentAO} origValue] {
                APSSetVarAndUpdate sextConfigStatus "Skipping $sName due to error: $origValue"
                continue
            }
            if [string compare $origValue ?]==0 {
                APSSetVarAndUpdate sextConfigStatus "Skipping $sName: no connection."
                continue
            }
            APSSetVarAndUpdate sextConfigStatus "Doing $sName"
            if [catch {FindSextupoleCenter -sector $sector -twissFile $twissFile \
                         -currentList [list $setCurrent(1) $setCurrent(2)] \
                         -origValue $origValue  \
                         -streamID $hpSID -tunePlane $tunePlane \
                         -sext $sext -rootname $outputDir/$outputRoot-$sName \
                         -iterationLimit $iterationLimit -tolerance $centeringTolerance \
                         -steeringTimeLimit $steeringTimeLimit \
                         -steeringInterval $steeringInterval \
                         -steeringGain $steeringGain \
                         -postChangeWaitList [list $postChangeWait(1) $postChangeWait(2)] \
                         -abortVariable abortRequested} result] {
                APSSetVarAndUpdate sextConfigStatus "Error for $sName: $result"
                break
            }
            if $pauseRequested {
                APSDisableButton $widget.pause.button
                APSEnableButton $widget.resume.button
                set resumeRequested 0
                set pauseRequested 0
                APSSetVarAndUpdate sextConfigStatus "Measurement paused. [exec date]"
                while 1 {
                    after 1000
                    update
                    if $abortRequested {
                        break
                    }
                    if $resumeRequested {
                        break
                    }
                }
            }
            if $abortRequested {
                break
            }
            if [catch {exec cavput -list=$sName:CurrentAO=$origValue} result] {
                APSSetVarAndUpdate sextConfigStatus "Problem restoring sextupole: $result"
                set abortRequested 1
            }
            if $abortRequested {
                set sector 41
                break
            }
            APSSetVarAndUpdate sextConfigStatus "Done with $sName"
        }
        if $abortRequested {
            break
        }
    }
    if $abortRequested {
        APSSetVarAndUpdate sextConfigStatus "Aborted."
    }
    APSSetVarAndUpdate sextConfigStatus "Done with series."

    APSDisableButton $widget.abort.button
    APSDisableButton $widget.pause.button
    APSDisableButton $widget.resume.button
    APSEnableButton $widget.run.button
}

proc FindSextupoleCenter {args} {
    set currentList ""
    set postChangeWaitList ""
    set origValue 0
    set streamID ""
    set tunePlane x
    set sext ""
    set rootname ""
    set abortVariable ""
    set tolerance 0.1
    set iterationLimit 10
    set steeringTimeLimit 60
    set steeringInterval 1
    set steeringGain 0.4
    set sector 0
    set twissFile ""
    APSStrictParseArguments {currentList postChangeWaitList origValue \
                               streamID tunePlane sext rootname \
                               abortVariable tolerance iterationLimit \
                               steeringInterval steeringTimeLimit steeringGain sector twissFile}

    set sName S${sector}$sext
    set current(1) [lindex $currentList 0]
    set current(2) [lindex $currentList 1]
    set wait(1) [lindex $postChangeWaitList 0]
    set wait(2) [lindex $postChangeWaitList 1]
   
    set sextFamilyList   [list A:S1 A:S2 A:S3 A:S4 B:S3 B:S2 B:S1]
    set nearestBPMList   [list A:P2 A:P3 A:P4 B:P5 B:P4 B:P3 B:P2]
    set otherBPMList     [list A:P2 A:P3 A:P4 B:P5 B:P4 B:P3 B:P2]
    set steeringTypeList [list   P2   AM   AM  BP5   BM   BM   P2]
    set steeringSectorOffset [list -1 0 0 0 0]
    set sextPolarityList [list 1 -1 -1 1 -1 -1 1]

    set index [lsearch $sextFamilyList $sext]
    set polarity [lindex $sextPolarityList $index]
    set nearestBPM [lindex $nearestBPMList $index]
    set steeringType [lindex $steeringTypeList $index]
    set steeringDir /home/helios/oagData/sr/localSteering/lattices/default/${steeringType}s/[format %02ld $sector]$steeringType
    if {![file exists $steeringDir] || \
          ![file exists $steeringDir/irm] || ![file exists $steeringDir/tests]} {
        return -code error "Steering directory ($steeringDir) or files missing for $sName"
    }
    APSSetVarAndUpdate sextConfigStatus "Steering: $steeringDir"

    # Transfer orbit to setpoints and restart correction 
    if {[catch {
        exec cavput -list=$sName:RateDividerAO=50 
        APSSRTransferBPMAdjustedValues -msType mswAve -plane both -saveSnapshot 0
        exec cavput -list=S:RC:OrbitControlLawXC,S:RC:OrbitControlLawYC,S:rfFreqControlLawRC -list=.SUSP=0
    } result]} {
        return -code error "FindSextupoleCenter: $result"
    }

    set firstPass 1
    set steeringWidget [APSUniqueName .]
    while {$iterationLimit>0} {
        foreach level {1 2} {
            if [catch {SetSextAndTakeData -value $current($level) -origValue $origValue \
                         -streamID $streamID -polarity $polarity -tunePlane $tunePlane \
                         -nearestBPM S${sector}$nearestBPM \
                         -sext $sName -rootname $rootname.$level \
                         -orbCorrWait $wait($level) \
                         -abortVariable abortRequested} result] {
                return -code error "FindSextupoleCenter: $result"
            }
        }
        if [catch {ProcessSextCenterData -rootname $rootname \
                     -output $rootname.proc -twissFile $twissFile} result] {
            return -code error "FindSextupoleCenter: $result"
        }
        # accumulate the data and compute the point to steer to in order to
        # center the beam
        if $firstPass {
            file copy -force $rootname.proc $rootname.results
            if [catch {exec sdds2stream -column=SextPosition $rootname.results} desiredPosition] {
                return -code error "FindSextupoleCenter: $desiredPosition"
            }
            set desiredPosition [expr -1*$desiredPosition]
        } else {
            if [catch {exec sddscombine $rootname.proc $rootname.results -merge \
                         $rootname.newResults -overwrite
                file copy -force $rootname.results $rootname.oldResults 
                file copy -force $rootname.newResults $rootname.results} result] {
                return -code error "FindSextupoleCenter: $result"
            }
            if [catch {exec sddsinterp $rootname.results /dev/null -print=bare,stdout \
                         -column=SextPosition,NearestBPMAdjusted \
                         -atValues=0 \
                         -belowRange=extrapolate -aboveRange=extrapolate} desiredPosition] {
                return -code error "FindSextupoleCenter: $desiredPosition"
            }
            set desiredPosition [lindex $desiredPosition end]
        }
        # Check if the result is good enough
        if [catch {exec sdds2stream -column=SextPosition $rootname.proc} result] {
            return -code error "FindSextupoleCenter: $result"
        }
        if [expr abs($result)<=$tolerance] {
            break
        }
        APSSetVarAndUpdate sextConfigStatus "Steering to $desiredPosition"
        # Suspend orbit correction and send the new desired position to the nearest BPM
        if [catch {
            exec cavput -list=S:RC:OrbitControlLawXC,S:RC:OrbitControlLawYC -list=.SUSP=1
            exec cavput -list=S$sector$nearestBPM:ms:x:SetpointAO=$desiredPosition
        } result] {
            return -code error "FindSextupoleCenter: $result"
        }
        # Start steering on the nearest BPM
        set oldDir [pwd]
        cd $steeringDir
        global steeringDone
        set steeringDone ""
        APSExecLog $steeringWidget -width 100 -lineLimit 1000 \
          -name "$steeringDir steering for sextupole centering" \
          -unixCommand "sddscontrollaw irm \
    -test=tests -gain=$steeringGain -interval=$steeringInterval -deltaLimit=value=2 \
    -verbose=1 -steps=[expr int($steeringTimeLimit/$steeringInterval)]" \
          -callback "set steeringDone ok" \
          -abortCallback "set steeringDone abort" \
          -cancelCallback "set steeringDone cancel" 
        cd $oldDir

        # Wait for steering
        while 1 {
            if [string length $steeringDone] break
            after 1000
            update
        }
        if [string compare $steeringDone ok]!=0 {
            return -code error "Steering: $steeringDone"
        }

        APSSetVarAndUpdate sextConfigStatus "Done steering"

        # Transfer orbit to setpoints and restart correction 
        if {[catch {
            APSSRTransferBPMAdjustedValues -msType mswAve -plane both -saveSnapshot 0
            exec cavput -list=S:RC:OrbitControlLawXC,S:RC:OrbitControlLawYC,S:rfFreqControlLawRC -list=.SUSP=0
        } result]} {
            return -code error "FindSextupoleCenter: $result"
        }

        incr iterationLimit -1
        set firstPass 0
    }
}


proc ProcessSextCenterData {args} {
    APSStrictParseArguments {rootname output twissFile}
    
    set tmpRoot /tmp/[APSTmpString]
    APSAddToTempFileList $tmpRoot.1 $tmpRoot.2
    set file1 $rootname.1
    set file2 $rootname.2
    foreach level {1 2} {
        # clip off 30 points on each end to remove odd baseline from chirp
        if [catch {exec sddsprocess [subst \$file${level}].tune -pipe=out \
                     -clip=30,30 \
                     | sddssmooth -pipe -pass=0 -despike=pass=3 -column=Waveform \
                     | sddsprocess -pipe \
                     "-define=column,Tune,Frequency 0.27154863888888 / = int - 0.5 > pop ? 1 swap - : $ " \
                     -process=Waveform,maximum,Tune,position,functionOf=Tune \
                     | sddssort -column=Tune -pipe \
                     | tee $tmpRoot.$level \
                     | sdds2stream -pipe -parameter=CurrentAO,Tune} result] {
            return -code error "ProcessSextCenterData: $result"
        }
        set current$level [lindex $result 0]
        set tune$level [lindex $result 1]
    }
    if [catch {exec sdds2stream $file1.tune -param=NearestBPM} nearestBPM] {
        return -code error "ProcessSextCenterData: $result"
    }
    if [catch {exec sddsprocess $file1.orbit -match=column,BPMName=$nearestBPM -pipe=out \
                 | sdds2stream -pipe -column=x:Adjusted} xAdjusted] {
        return -code error "ProcessSextCenterData: No match for $nearestBPM in data: $xAdjusted"
    }
    if [catch {exec sdds2stream -parameter=TunePlaneSign,SextPolarity,SextName $file1.tune} result] {
        return -code error "ProcessSextCenterData: $result"
    }
    set tuneSign [lindex $result 0]
    set sextPolarity [lindex $result 1]
    set sextName [lindex $result 2]
    if $tuneSign==1 {
        set betaName betax
    } else {
        set betaName betay
    }
    if [catch {exec sddsprocess $twissFile -pipe=out -match=column,ElementName=$sextName \
                 -process=beta?,first,%s \
                 | sddsconvert -pipe -retain=parameter,beta* \
                 | sddsxref -pipe -leave=* -transfer=parameter,TunePlaneSign,SextPolarity,SextName \
                 $file1.tune \
                 | sddscollapse -pipe \
                 | sddsprocess -pipe=in $output \
                 "-define=column,NearestBPMAdjusted,$xAdjusted,units=mm" \
                 "-define=column,deltaTune,$tune2 $tune1 -" \
                 "-define=column,deltaCurrent,$current2 $current1 -" \
                 "-define=column,betaTunePlane,TunePlaneSign 1 == ? betax : betay $ " \
                 "-define=column,SextPosition,deltaTune TunePlaneSign * 4 * pi * betaTunePlane / 0.0247 deltaCurrent * / 1e3 * SextPolarity *,units=mm"} result] {
        return -code error "$result"
    }
}

proc SetSextAndTakeData {args} {
    set streamID ""
    set sext ""
    set value 0
    set origValue 0
    set orbCorrWait 60
    set rootname ""
    set abortVariable ""
    set polarity 1
    set nearestBPM ""
    set tunePlane x
    APSStrictParseArguments {value origValue rootname sext streamID orbCorrWait abortVariable \
                           polarity nearestBPM tunePlane} 
    if [string length $abortVariable] {
        global $abortVariable
    }
    global inputFileDir xrefDir

    APSSetVarAndUpdate sextConfigStatus "Setting $sext to $value..."
    if [catch {exec cavput -list=$sext -list=:CurrentAO=$value} result] {
        catch {exec cavput -list=$sext -list=:CurrentAO=$origValue}
        return -code error "$result"
    }
    APSSetVarAndUpdate sextConfigStatus "Waiting for PS to change..."
    if [catch {exec cawait -waitfor=$sext:CurrentAI,lower=[expr $value-1],upper=[expr $value+1] -timeLimit=60} result] {
        catch {exec cavput -list=$sext -list=:CurrentAO=$origValue}
        return -code error "$result"
    }
    APSSetVarAndUpdate sextConfigStatus "Waiting $orbCorrWait sec for orbit correction...[exec date]"
    APSWaitWithUpdate -waitSeconds $orbCorrWait -updateInterval 1 \
      -abortVariable $abortVariable
    APSSetVarAndUpdate sextConfigStatus "Taking orbit data..."
    if [catch {exec sddsmonitor $inputFileDir/srBPMAve.mon $rootname.tmp -steps=1
        exec sddscollect $rootname.tmp -pipe=out \
                 -collect=suffix=:mswAve:x:ErrorCC,column=x:Error \
                 -collect=suffix=:mswAve:y:ErrorCC,column=y:Error \
                 -collect=suffix=:mswAve:x:AdjustedCC,column=x:Adjusted \
                 -collect=suffix=:mswAve:y:AdjustedCC,column=y:Adjusted \
                 -collect=suffix=:ms:x:SetpointAO,column=x:Setpoint \
                 -collect=suffix=:ms:y:SetpointAO,column=y:Setpoint \
                 -collect=suffix=:ms:x:OffsetAO,column=x:Offset \
                 -collect=suffix=:ms:y:OffsetAO,column=y:Offset \
                 | sddsconvert -pipe -rename=col,Rootname=BPMName \
                 | sddsxref -pipe=in $xrefDir/SRBPMPosition.xref $rootname.orbit \
                 -match=BPMName -take=s 
        exec rm $rootname.tmp} result] {
        catch {exec cavput -list=$sext -list=:CurrentAO=$origValue}
        return -code error "$result"
    }
#    APSWriteToTelnetStream -streamID $streamID -command "AVER OFF"
#    APSWriteToTelnetStream -streamID $streamID -command "AVER ON"
    APSWriteToTelnetStream -streamID $streamID -command "ABOR;*WAI"
     APSSetVarAndUpdate sextConfigStatus  "Waiting for hpvec..."
    APSWaitWithUpdate -waitSeconds 8 -updateInterval 1 \
      -abortVariable $abortVariable
    set filename $rootname.tune
    APSSetVarAndUpdate sextConfigStatus  "Taking tune data..."
    switch $tunePlane {
        x {
            set tunePlaneSign 1
        } 
        y {
            set tunePlaneSign -1
        }
    }
    if [catch {exec hpVecTrace a $filename -unit=sr
        exec sddsprocess $filename -defi=param,CurrentAO,$value -nowarning \
                 -print=parameter,SextName,$sext \
                 -define=parameter,TunePlaneSign,$tunePlaneSign \
                 -print=parameter,TunePlane,$tunePlane \
                 -print=parameter,NearestBPM,$nearestBPM \
                 -define=parameter,SextPolarity,$polarity 
        exec rm ${filename}~} result] {
        catch {exec cavput -list=$sext -list=:CurrentAO=$origValue}
        return -code error "$result"
    }
}

APSSetVarAndUpdate sextConfigStatus "Ready."





