
proc APSMpSRSteerOrbitInfo {} {
    return "This procedure helps to do steering for ID, CU or BM lines.  Note that it will normally transfer the existing adjusted orbit values to the steering setpoints, which should lock in the beamlines at their present positions.\n\nA new local steering method has been implemented where light source setpoints for which steering was not made is preserved throughout the multiple orbit transfers to setpoints."
}

proc APSMpSRSteerOrbit {} {
    global status mpInterface execMode
    global IOCControllawXOption IOCControllawYOption

    set dataDir /home/helios/oagData/sr/localSteering
    set orbitSnapDir /home/helios/oagData/SCR/snapshots/SROrbitSetPt
    set corrSnapDir /home/helios/oagData/SCR/snapshots/SRCorSetpts

    set plane both
    set IDsource 0
    set IDOptsource 0
    set BMsource 0
    set AMsource 0
    set CUsource 0
    set gain 0.25
    set lockInOldOrbit 1
    set lockInNewOrbit 0
    set doLaunchADT 1

    # somewhat independent choices one can make:
    # 1) server mode       switches between Fine and Global efficiently.
    #                      irrelevant when Local bump matrix steering is used to do steering.
    #    non-server mode   very slow start-up and switching of correction modes.
    # 2) Fine correction matrix      correction plane selectable.
    #    local correction matrix     correction plane not selectable. 
    #                                x and y planes are corrected.
    # 3) Preserve light source BPMs setpoints for light sources that were not steered
    #                                this is used to prevent unwanted steering of light sources
    #                                that have BPMs with offset drifts. This necessitates several setpoint saves
    #                                and orbit transfers.
    #    The alternatives are:
    #    lock in old orbit           this is not recommended anymore because, after several cycles,
    #                                the setpoints at light sources BPMs may walk away.
    #    nothing                     this is not recommended for steering.
    #    lock in new orbit           necessary in order to have global correction resume (or start) with
    #                                fresh new zero ErrorCC's.
    #    nothing                     not recommended. global will start with some general orbit to
    #                                punch down compromising what the local steering has accomplished.
    # In non-server mode, the APSSRStartStandardCorrection procedure is used to start the correction. 
    # The APSSRControlOrbitCorrection procedure is used to abort the correction
    # In server mode, new procedures were written:
    # APSChangeSROrbitCorrectionMode to switch between Fine and Global modes.
    # APSChangeSROrbitCorrectionMode will also start up a correction
    # if none was running.
    # procedures that involve Fine and Global mode of correction are no longer available.
    set serverMode 0
    set steeringMode Local
    set preserveLSsetpoints 0
    set preserveGlobalOrbit 1
    set doLaunchADT 1
    set DoNotRemoveXrayBPMs 1
    APSMpStep init -promptDialog APSMpSRSteerOrbitInitDialog \
      -vars {plane IDsource IDOptsource CUsource DoNotRemoveXrayBPMs \
               BMsource AMsource lockInOldOrbit lockInNewOrbit \
               serverMode steeringMode preserveGlobalOrbit gain doLaunchADT}
    
    
    foreach param "plane IDsource IDOptsource CUsource DoNotRemoveXrayBPMs BMsource AMsource lockInOldOrbit lockInNewOrbit serverMode steeringMode preserveGlobalOrbit gain doLaunchADT" {
        append paramString "$param=\"[set $param]\" "
    }
    if {[catch {APSLogScriptAction \
                    -procedure APSMpSRSteerOrbit \
                    -action initialize \
                    -state ok \
                    -parameters $paramString} results]} {
        #        APSMpReturn error "APSMpSRSteerOrbit(1): $results"
    }

    if ![string compare $steeringMode Fine] {
        APSMpReturn error "APSMpSRSteerOrbit(2): Fine steering mode no longer available"
    }
    
    # Check that global correction is running now.  If not, orbit might be bad.
    APSMpStep CheckGlobalCorrection -desc "Verify that global correction is running." \
      -proc APSSRCheckOrbitCorrection
    if [catch {APSSRCheckOrbitCorrection -mode Global -plane $plane -queryIfMissing 1} result] {
        APSMpReturn error "APSMpSRSteerOrbit(3 - APSSRCheckOrbitCorrection): $result"
    }
    # 2x2 grid for runcontrol action
    #                    method
    #                    Local     Fine
    # server mode        suspend   switch to Fine
    # non-server mode    suspend   abort; start Fine
    # no more Fine corection
    APSMpStep "Check corrector mode..."
    if [catch {APSSRCheckCorrectorMode -plane h} hCorrMode] {
        APSMpReturn error "APSMpSRSteerOrbit(4 - APSSRCheckCorrectorMode): $hCorrMode"
    }
    
   # APSMpStep "Suspend ID gap feedforward..."
   # if [catch {APScavput -list=S:ID:GapFeedForwardRC.SUSP=1} result] {
   #     APSMpReturn error "APSMpSRSteerOrbit(4.5 - APScavput): $result"
   # }
    
    if [string match $hCorrMode vector] {
        APSMpStep SuspendOrbitCorrection -desc "Suspend orbit correction in DP mode" \
            -proc APSSuspendSRControllaw
        
        foreach pl {X Y} {
            if [catch {APSSuspendSRControllaw -runControlPV DP:S:OrbitControlLaw${pl}SDDS } result] {
                APSMpReturn error "APSMpSRSteerOrbit(5 - suspendSRcontrollaw): $result"
            }
        }
        set localSteeringControlPV DP:S:SteeringSDDS
    } else {
        APSMpStep SuspendOrbitCorrection -desc "Suspend orbit correction in WS mode" \
            -proc APSSRControlOrbitCorrection
        # procedure APSSRControlOrbitCorrection is for WS OC only (not DP)
        if [catch {APSSRControlOrbitCorrection -plane both -action suspend \
                   } result] {
            APSMpReturn error "APSMpSRSteerOrbit(6 - APSSRControlOrbitCorrection): $result"
        }
        set localSteeringControlPV S:SteeringSDDS
    }
    
    if {$preserveLSsetpoints || $preserveGlobalOrbit} {
        set statusCallback APSNoOp
        APSMpStep SaveAllSetpoints -desc "Do SCR save of orbit setpoints (setpoint preservation mode)"
        set result ""
        eval $statusCallback {"Taking initial snapshot of bpms and correctors."}
        set retry 1
        while {$retry} {
            if [catch {APSSaveMachine -machine SROrbitSetPt \
                           -description "Routine save: save prior to setpoint transfer and local steering."} result] {
                APSMpReturn error "APSMpSRSteerOrbit(7 - APSSaveMachine SROrbitSetPt): $result"
            }
            set retry 0
            set orbit [lindex $result 0]
            # error log from APSSave
            if {[llength $result] > 1 } {
                APSFileDisplayWindow [APSUniqueName .burtlog ] -fileName [lindex $result 1] \
                    -comment "Log file returned when attempting to save bpm setpoints." \
                    -width 80 \
                    -contextHelp "Log file returned when attempting to save bpm setpoints."
                # query for retry, quit or blunder ahead
                switch [APSMultipleChoice [APSUniqueName .] -modal 1 -question "An error has been detected during the initial BPM setpoint save.  The most convenient thing to do is to retry a save.  If after several retries the problem still occurs, you should investigate possible BPM IOC problems." -labelList {"Retry save" "Quit" "Blunder ahead"} -returnList {retry quit blunderAhead} -name "Retry save operation or quit steering?"] {
                    retry {
                        set retry 1
                    }
                    quit {
                        set retry 0
                        APSMpReturn error "APSMpSRSteerOrbit(8): See info in [lindex $result 1]"
                    }
                    blunderAhead {
                        set retry 0
                    }
                }
            }
        }
        set result ""
        set retry 1
        while {$retry} {
            if [catch {APSSaveMachine -machine SRCorSetpts \
                           -description "Routine save: save prior to local steering."} result] {
                APSMpReturn error "APSMpSRSteerOrbit(9 - SCR save SRCorSetpts): $result"
            }
            set retry 0
            set correctors [lindex $result 0]
            # error log from APSSave
            if {[llength $result] > 1 } {
                APSFileDisplayWindow [APSUniqueName .burtlog] -fileName [lindex $result 1] \
                    -comment "Log file returned when attempting to save correctors." \
                    -width 80 \
                    -contextHelp "Log file returned when attempting to save correctors."
                # query for retry, quit or blunder ahead
                switch [APSMultipleChoice [APSUniqueName .] -modal 1 -question "An error has been detected during the initial corrector save. The most convenient thing to do is to retry a save.  If after several retries the problem still occurs, you should investigate possible PS IOC problems." -labelList {"Retry save" "Quit" "Blunder ahead"} -returnList {retry quit blunderAhead} -name "Retry save operation or quit steering?"] {
                    retry {
                        set retry 1
                    }
                    quit {
                        set retry 0
                        APSMpReturn error "APSMpSRSteerOrbit(10): See info in [lindex $result 1]"
                    }
                    blunderAhead {
                        set retry 0
                    }
                }
            }
        }
        eval $statusCallback {"Snapshots done."}
        APSMpStep TransferToAllSetpoints -desc "Transfer orbit to setpoints (setpoint preservation mode)" \
            -proc APSSRTransferBPMAdjustedValues
        if [catch {APSSRTransferBPMAdjustedValues -msType mswAve -plane $plane \
                       -saveSnapshot 0} result] {
            if [catch {APSMpRestoreSteeringFailSafeReturn error "APSMpSRSteerOrbit(11 - APSSRTransferBPMAdjustedValues): $result" \
                         -orbit $orbit } results] {
                APSMpReturn error "APSMpSRSteerOrbit(11a -- APSSRTransferBPMAdjustedValues): $results"
            }
            APSMpReturn error "APSMpSRSteerOrbit(11 - APSSRTransferBPMAdjustedValues): $result"
        }

        APSMpStep SaveSetpointsAfterTransfer -desc "Do SCR save of setpoints after transfer (setpoint preservation mode)"
        set result ""
        eval $statusCallback {"Taking snapshot after transfer."}
        set retry 1
        while {$retry} {
            if [catch {APSSaveMachine -machine SROrbitSetPt \
                           -description "Routine save: save after transfer but before local steering."} result] {
                if [catch {APSMpRestoreSteeringFailSafeReturn error "APSMpSRSteerOrbit(12 - SCR save SROrbitSetPt): $result" -orbit $orbit} results] {
                    APSMpReturn error "APSMpSRSteerOrbit(12 - APSSaveMachine SROrbitSetPt): $results"
                }
                APSMpReturn error "APSMpSRSteerOrbit(12 - APSSaveMachine SROrbitSetPt): $result"
            }
            set retry 0
            set snapshot2 [lindex $result 0]
            if {[llength $result] > 1 } {
                APSFileDisplayWindow [APSUniqueName .burtlog] -fileName [lindex $result 1] \
                    -comment "Log file returned when attempting to save BPM setpoints." \
                    -width 80 \
                    -contextHelp "Log file returned when attempting to save BPM setpoints."
                # query for retry, quit or blunder ahead
                switch [APSMultipleChoice [APSUniqueName .] -modal 1 -question "An error has been detected during the BPM setpoint save after transfer.  The most convenient thing to do is to retry a save.  If after several retries the problem still occurs, you should investigate possible BPM IOC problems." -labelList {"Retry save" "Quit" "Blunder ahead"} -returnList {retry quit blunderAhead} -name "Retry save operation or quit steering?"] {
                    retry {
                        set retry 1
                    }
                    quit {
                        set retry 0
                        if [catch {APSMpRestoreSteeringFailSafeReturn error "APSMpSRSteerOrbit(13): $result" -orbit $orbit} result] {
                            APSMpReturn error "APSMpSRSteerOrbit(13a): $result"
                        }
                    }
                    blunderAhead {
                        set retry 0
                    }
                }
            }
        }
        eval $statusCallback {"Snapshot done."}
    } else {
        if $lockInOldOrbit {
            APSMpStep TransferOrbitSetpoints -desc "Transfer orbit to setpoints (lock in mode)" \
                -proc APSSRTransferBPMAdjustedValues
            if [catch {APSSRTransferBPMAdjustedValues -msType mswAve -plane $plane \
                           -saveSnapshot 1} result] {
                APSMpReturn error "APSMpSRSteerOrbit(13 - APSSRTransferBPMAdjustedValues) : $result"
            }
        }
    }
    
    # 2x2 grid for runcontrol action
    #                    method
    #                    Local     Fine
    # server mode        suspend   switch to Fine
    # non-server mode    suspend   abort; start Fine
    # no more Fine corection
    #put modify RTFBRegulators back, which was missed due to the lack of updating latest version
    APSMpStep ModifyRTFBRegulator -desc "Modify RTFB Regulator" \
        -proc APSMpSRSetFBfilters
    # don't need to ramp if the high pass filters are increased.
    if [catch {APSMpSRSetFBfilters -ramp 0 -hFactor 2 -vFactor 3 } result] {
        APSSetVarAndUpdate status "APSMpSRSteerOrbit: Warning: Problem setting RT feedback high-pass filters. $result"
        APSSetVarAndUpdate status "One may continue but at a 5x lower gain."
        APSInfoWindow .info -name "Operational Recommendations" -infoMessage "Warning: Problem setting RT feedback high-pass filters. $result\n\nOne may proceed with steering but with a 5x lower gain." -modal 1
    }
    
    APSMpStep LaunchSteeringScreens -desc "Launch steering screens"
    if $IDsource {
        catch {exec SRIDSteering -allowTransfer 0 -adjustOnly 0 -allowAllSectors 1 &} IDSteeringPID
    }
    if $IDOptsource {
        catch {exec SRIDIntensityOptimization -allowTransfer 1 -adjustOnly 0 -allowAllSectors 1 -includeSourcePoints 1 &} IDOptPID
    }
    if $CUsource {
        catch {exec SRCUSteering -allowTransfer 0 -adjustOnly 0 &} CUSteeringPID
    }
    if $BMsource {
        catch {exec SRBMSteering -allowTransfer 0 -adjustOnly 0 -allowAllSectors 1 &} BMSteeringPID
    }
    if $AMsource {
        catch {exec SRAMSteering -allowTransfer 0 -adjustOnly 0 -allowAllSectors 1 &} AMSteeringPID
    }
    
    if {$doLaunchADT} {
        APSMpStep LaunchADT -desc "Launch ADT for averaged SR bpm errors"
        set statusDir /home/helios/oagData/sr/BPMStatus
        set adtFile /home/helios/oagData/ADTFiles/srBpm/sr.bpm.ave.error.pv
        set tmpRoot /tmp/[APSTmpString]
        exec sddsprocess $statusDir/config.sdds $tmpRoot.good.x \
          -filter=col,NonexistentH,0,0
        exec sddsprocess $statusDir/config.sdds $tmpRoot.good.y \
            -filter=col,NonexistentV,0,0
        APSAddToTmpFileList -ID LaunchADT -fileList "$tmpRoot.good.x $tmpRoot.good.y $tmpRoot.x $tmpRoot.y $tmpRoot.xy $tmpRoot.pv"
        foreach coord {x y} {
            if [catch {exec sddscombine $adtFile -pipe=out -merge \
                           -delete=column,BPMName \
                           | sddsprocess -pipe \
                           -match=column,ControlName=S*:P\[012345\]:*:$coord:ErrorCC \
                           -edit=column,BPMName,ControlName,2S/:/100D \
                           -redef=para,ADTUnitsPerDiv,0.020 \
                           | sddsselect -pipe=in $tmpRoot.good.$coord $tmpRoot.$coord \
                           -match=BPMName=DeviceName} result] {
                APSMpReturn error "APSMpSRSteerOrbit(14 - sddscombine)$result"
            }
        }
        if [catch {eval exec sddscombine $tmpRoot.x $tmpRoot.y $tmpRoot.xy -overWrite
            exec sddsconvert $adtFile -pipe=out -dele=para,ADTUnitsPerDiv \
                       | sddsxref -pipe=in $tmpRoot.xy $tmpRoot.pv \
                       -leave=* -transfer=para,ADTUnitsPerDiv } result] {
            APSMpReturn error "APSMpSRSteerOrbit(15 - sddsxref): $result (sddsxref)"
        }
        set display :0.1
        catch {exec adt -display $display -geometry +30+0 -f $tmpRoot.pv & \
               } adtPID
      # catch {exec adt -geometry +30+0 -f $tmpRoot.pv & \
       #       } adtPID
    }
    APSMpStep "Set steering mode ..."
    if [catch {exec cavput -list=S:OrbitControlLaw -list=X,Y -list=FineBO=1 -pend=30 \
               } result ] {
        APSSetVarAndUpdate status "APSMpSRSteerOrbit(16 - cavput): $result"
        if [catch {APSMpRestoreSteeringFailSafeReturn error "APSMpSRSteerOrbit(16 - cavput): $result" -orbit $orbit} results] {
            APSMpReturn error "APSMpSRSteerOrbit(16 - cavput): $results"
        }
        APSMpReturn error "APSMpSRSteerOrbit(16 - cavput): $result"
    }

    # This is how pem is paused while we do steering
    set oldMode $execMode
    set execMode Manual
    APSSetVarAndUpdate status "Press continue when done steering..."
    APSMpStep QueryContinueAdjustment -desc "Wait for completion of steering."

    #
    #
    #
    #    Steering is supposed to be done during the above manual step
    #
    #

    # now steering is finished
    set execMode $oldMode

    if [catch {exec cavget -list=$localSteeringControlPV.RUN -pend=30} localSteerRunning] {
        APSMpReturn error "APSMpSRSteerOrbit(17 - cavget): $localSteerRunning"
    }
    if $localSteerRunning {
        APSMpStep AbortLocalSteering -desc "Abort local steering."
        if [catch {exec cavput -list=$localSteeringControlPV.ABRT=1 \
                       -pend=30} result] {
            APSMpReturn error "APSMpSRSteerOrbit(18 - cavput): Unable to abort local steering: $result"
        }
        after 2000
        if [catch {exec cavput -list=$localSteeringControlPV.CLR=1 \
                       -pend=30} result] {
            APSMpReturn error "APSMpSRSteerOrbit(19 - cavput): Unable to abort local steering: $result"
        }
    }
    
    if $IDsource {
        catch {exec kill $IDSteeringPID}
    }
    if $IDOptsource {
        catch {exec kill $IDOptPID}
    }
    if $CUsource {
        catch {exec kill $CUSteeringPID}
    }
    if $BMsource {
        catch {exec kill $BMSteeringPID}
    }
    if $AMsource {
        catch {exec kill $AMSteeringPID}
    }
    if $doLaunchADT {
        catch {exec kill $adtPID}
    }
    APSMpStep "Unset steering mode..."
    if [catch {exec cavput -list=S:OrbitControlLaw -list=X,Y -list=FineBO=0 -pend=30 \
               } result ] {
        APSSetVarAndUpdate status "APSMpSRSteerOrbit(20 - cavput): $result"
        if [catch {APSMpRestoreSteeringFailSafeReturn error "APSMpSRSteerOrbit(20 - cavput): $result" -orbit $orbit} results] {
            APSMpReturn error "APSMpSRSteerOrbit(20a - cavput): $result"
        }
        APSMpReturn error "APSMpSRSteerOrbit(20 - cavput): $result"
    }
    APSMpStep SaveSetpointsAfterSteering -desc "SCR save of setpoints after steering"
    set result ""
    eval $statusCallback {"Taking snapshot after steering."}
    set retry 1
    while {$retry} {
        if [catch {APSSaveMachine -machine SROrbitSetPt \
                       -description "Routine save: save setpoints after steering."} result] {
            if [catch {APSMpRestoreSteeringFailSafeReturn error "APSMpSRSteerOrbit(save setpoints after steering): $result" \
                         -orbit $orbit -correctors $correctors } results] {
                APSMpReturn error "APSMpSRSteerOrbit(21a - save setpoints after steering): $results"
            }
            APSMpReturn error "APSMpSRSteerOrbit(21 - save setpoints after steering): $result"
        }
        set retry 0
        set snapshot3 [lindex $result 0]
        if {[llength $result] > 1 } {
            APSFileDisplayWindow [APSUniqueName .burtlog] -fileName [lindex $result 1] \
                -comment "Burt log file returned when attempting to save BPM setpoints." \
                -width 80 \
                -contextHelp "Burt log file returned when attempting to save BPM setpoints."
            # query for quit or blunder ahead
            switch [APSMultipleChoice [APSUniqueName .] -modal 1 -question "An error has been detected during the BPM setpoint save after steering. The most convenient thing to do is to retry a save.  If after several retries the problem still occurs, you should investigate possible BPM IOC problems. Pressing \"Quit\" restores the orignal bpm and corrector setpoints." -labelList {"Retry save" "Quit" "Blunder ahead"} -returnList {retry quit blunderAhead} -name "Retry save operation or quit steering?"] {
                retry {
                    set retry 1
                }
                quit {
                    set retry 0
                    if [catch {APSMpRestoreSteeringFailSafeReturn error "APSMpSRSteerOrbit(22 - quit after saving setpoints after steering failed): $result" \
                                 -orbit $orbit -correctors $correctors } results] {
                        APSMpReturn error "APSMpSRSteerOrbit(22a: error in restoring orbit after quit): $results"
                    }
                    APSMpReturn error "APSMpSRSteerOrbit(22 - quit after saving setpoints after steering failed): $result"
                }
                blunderAhead {
                    set retry 0
                }
            }
        }
    }
    eval $statusCallback {"Snapshot done."}
    if [expr $lockInNewOrbit || $preserveLSsetpoints] {
        APSMpStep TransferOrbitSetpoints -desc \
            "Transfer orbit to setpoints" -proc APSSRTransferBPMAdjustedValues
        if [catch {APSSRTransferBPMAdjustedValues -msType mswAve -plane $plane \
                       -saveSnapshot 0} result] {
            if [catch {APSMpRestoreSteeringFailSafeReturn error "APSMpSRSteerOrbit(23 - transfer BPM adjusted values failed after steering): $result" \
                         -orbit $orbit -correctors $correctors } results] {
                APSMpReturn error "APSMpSRSteerOrbit(23a -- error in restoring orbit after BPM adjusted values transfer failed - $result): $results"
            }
            APSMpReturn error "APSMpSRSteerOrbit(23 - transfer BPM adjusted values failed after steering): $result"
        }
    }
    
    if {$preserveLSsetpoints || $preserveGlobalOrbit} {
        # restore setpoints for all sources from various sources.
        APSMpStep RestoreSetpoints -desc "Restore preserved lightsource setpoints"
        set temproot /tmp/[APSTmpString]
        set tempFile2 ${temproot}snap2
        set tempFile3 ${temproot}snap3
        set tempFile5 ${temproot}snap5
        set tempFile6 ${temproot}polluted
        # file of original light source setpoints only
        set tempFileInitLS ${temproot}snapInitLS
        # file to contain the setpoints of light sources that was just steered.
        # i.e. the difference of snapshot2 and snapshot3
        set tempFileSteeredLS ${temproot}snapSteeredLS
        
        # make file of original light source setpoints only.
        # take light source data from saved orbit using lightSources.config as a filter
        # APSMpStep MakeInitialLSFile -desc "Make initial LS file"
        set tempLSFile0 ${temproot}LS0
        set tempLSFilex ${temproot}LSx
        set tempLSFiley ${temproot}LSy
        set tempLSFile ${temproot}LS
        if [catch {exec sddsprocess $dataDir/lightSources.config $tempLSFile0 \
                       -match=para,NameType=MonitorNames
            exec sddsprocess $tempLSFile0 $tempLSFilex \
                       -edit=col,ControlName,Name,ei/:ms:x:SetpointAO/
            exec sddsprocess $tempLSFile0 $tempLSFiley \
                       -edit=col,ControlName,Name,ei/:ms:y:SetpointAO/ 
            exec sddscombine $tempLSFilex $tempLSFiley $tempLSFile -merge -overWrite
            file delete ${temproot}LS0 ${temproot}LSx ${temproot}LSy \
                   } result] {
            if [catch {APSMpRestoreSteeringFailSafeReturn error "APSMpSRSteerOrbit(24 - sddsprocess1): $result" \
                         -orbit $orbit -correctors $correctors } results] {
                APSMpReturn error "APSMpSRSteerOrbit(24a - sddsprocess1 -- $result): $results"
            }
            APSMpReturn error "APSMpSRSteerOrbit(24 - sddsprocess1): $result"
        }
        if [catch {exec sddsselect $orbitSnapDir/$orbit $tempLSFile $tempFileInitLS \
                       -match=ControlName \
                   } result] {
            if [catch {APSMpRestoreSteeringFailSafeReturn error "APSMpSRSteerOrbit (sddsselect2): $result" \
                         -orbit $orbit -correctors $correctors } results] {
                APSMpReturn error "APSMpSRSteerOrbit(25a - sddsselect2 -- $result): $results"
            }
            APSMpReturn error "APSMpSRSteerOrbit(25 - sddsselect2): $result"
        }
        
        # make file of setpoints of light sources that was just steered.
        # take the difference between snapshot2 and snapshot3 
        # APSMpStep MakeJustSteeredLSFile -desc "Make just steered LS file"
        if [catch {exec sddsselect $orbitSnapDir/$snapshot2 $tempLSFile -pipe=out \
                       -match=ControlName \
                       | sddsconvert -pipe=in $tempFile2 \
                       -rename=col,ValueString=OldValueString \
                   } result] {
            if [catch {APSMpRestoreSteeringFailSafeReturn error "APSMpSRSteerOrbit (sddsselect3): $result" \
                         -orbit $orbit -correctors $correctors } results] {
                APSMpReturn error "APSMpSRSteerOrbit(26a - sddsselect3 - $result): $results"
            }
            APSMpReturn error "APSMpSRSteerOrbit(26 - sddsselect3): $result"
        }
        if [catch {exec sddsselect $orbitSnapDir/$snapshot3 $tempLSFile -pipe=out \
                       -match=ControlName \
                       | sddsprocess -pipe=in $tempFile3 \
                       -print=col,NewValueString,%s,ValueString \
                   } result] {
            if [catch {APSMpRestoreSteeringFailSafeReturn error "APSMpSRSteerOrbit (sddsselect4): $result" \
                         -orbit $orbit -correctors $correctors } results] {
                APSMpReturn error "APSMpSRSteerOrbit(27a - sddsselect4 - $result): $results"
            }
            APSMpReturn error "APSMpSRSteerOrbit(27 - sddsselect4): $result"
        }
        # File $tempFileSteeredLS may have zero rows if no steering was done.
        # Using -noWarning will prevent an error code from sddsprocess.
        if [catch {exec sddsxref $tempFile2 $tempFile3 -pipe=out \
                       -match=ControlName -take=NewValueString,ValueString \
                       | sddsprocess -pipe=in $tempFileSteeredLS \
                       -match=col,NewValueString=@OldValueString,! -nowarnings \
                   } result] {
            if [catch {APSMpRestoreSteeringFailSafeReturn error "APSMpSRSteerOrbit(28 - sddsxref): $result" \
                         -orbit $orbit -correctors $correctors } results] {
                APSMpReturn error  "APSMpSRSteerOrbit(28a - sddsxref - $result): $results"
            }
            APSMpReturn error  "APSMpSRSteerOrbit(28 - sddsxref): $result"
        }
    }
    
    # It is always the case now that preserveGlobalOrbit == 1
    if $preserveGlobalOrbit {
        # 1. restore global orbit for all bpms
        # 2. transfer polluted bpms
        # 3. change setpoints of moved light sources to the new values
        APSMpStep RestoreGlobalOrbit -desc "Restore preserved global orbit setpoints"
        if [catch {exec sddscasr -restore -pend=100 $orbitSnapDir/$orbit \
                   } result] {
            if [catch {APSMpRestoreSteeringFailSafeReturn error "APSMpSRSteerOrbit (RestoreGlobalOrbit1): $result" \
                         -orbit $orbit -correctors $correctors } results] {11
                APSMpReturn error "APSMpSRSteerOrbit(29 - RestoreGlobalOrbit1 - $result): $results"
            }
            APSMpReturn error "APSMpSRSteerOrbit(29 - RestoreGlobalOrbit1): $result"
        }
        # use data from $tempFileSteeredLS to determine which bpm to
        # transfer to setpoint.
        APSMpStep FixSourcePointPollution -desc "Fix source point pollution"
        foreach coord {x y} {
            if [catch {exec sddsprocess $tempFileSteeredLS ${tempFileSteeredLS}.${coord} \
                           -match=col,ControlName=*${coord}* -nowarning \
                           -reedit=col,ControlName,%/:ms:${coord}:SetpointAO// \
                       } result] {
                if [catch {APSMpRestoreSteeringFailSafeReturn error "APSMpSRSteerOrbit(30 - RestoreGlobalOrbit2): $result" \
                             -orbit $orbit -correctors $correctors } results] {
                    APSMpReturn error "APSMpSRSteerOrbit(30 - RestoreGlobalOrbit2 - $result): $results"
                }
                APSMpReturn error "APSMpSRSteerOrbit(30 - RestoreGlobalOrbit2): $result"
            }
            if [catch {exec sddsselect $dataDir/sourcePointPollution.xref \
                           ${tempFileSteeredLS}.${coord} ${tempFile6}.${coord} \
                           -match=SourcePointBPM=ControlName -reuse -nowarning \
                       } result] {
                if [catch {APSMpRestoreSteeringFailSafeReturn error "APSMpSRSteerOrbit(31 - RestoreGlobalOrbit-sddsselect): $result" \
                             -orbit $orbit -correctors $correctors } results] {
                    APSMpReturn error "APSMpSRSteerOrbit(31 - RestoreGlobalOrbit-sddsselect - $result): $results"
                }
                APSMpReturn error "APSMpSRSteerOrbit(31 - RestoreGlobalOrbit-sddsselect): $result"
            }
            if [catch {set bpmList [exec sdds2stream ${tempFile6}.${coord} \
                                        -col=AffectedBPM]
                # remove BM bpms from list if doing x-plane
                if [string match $coord x] {
                    set bpmList0 $bpmList
                    set bpmList ""
                    foreach bpm $bpmList0 {
                        if [regexp {BM:P} $bpm] continue
                        lappend bpmList $bpm
                    }
                }
                APSSRTransferBPMList -list $bpmList -plane $coord -msType mswAve \
                       } result] {
                if [catch {APSMpRestoreSteeringFailSafeReturn error "APSMpSRSteerOrbit(32 - RestoreGlobalOrbit3): $result" \
                             -orbit $orbit -correctors $correctors } results] {
                     APSMpReturn error "APSMpSRSteerOrbit(32 - RestoreGlobalOrbit3: $result): $results"
                }
                APSMpReturn error "APSMpSRSteerOrbit(32 - RestoreGlobalOrbit3): $result"
            }
        }
        # use data from $tempFileSteeredLS restore LS bpm setpoints
        APSMpStep RestoreLSSetpoints -desc "Change light source setpoints to the new values"
        if [catch {exec sddscasr -restore $tempFileSteeredLS  \
                   } result] {
            if [catch {APSMpRestoreSteeringFailSafeReturn error "APSMpSRSteerOrbit(33 - RestoreGlobalOrbit4): $result" \
                         -orbit $orbit -correctors $correctors} results] {
                APSMpReturn error "APSMpSRSteerOrbit(33a - RestoreGlobalOrbit4: $result) $results"
            }
            APSMpReturn error "APSMpSRSteerOrbit(33 - RestoreGlobalOrbit4): $result"
        }
    }
    
    if $preserveLSsetpoints {
        APSMpStep "Restore preserved light source setpoints"
        if [catch {exec sddsselect $tempFile5A $tempFileSteeredLS $tempFile5 \
                       -match=ControlName -invert \
                   } result] {
            if [catch {APSMpRestoreSteeringFailSafeReturn error "APSMpSRSteerOrbit(34 - sddsselect1): $result" \
                         -orbit $orbit -correctors $correctors } results] {
                APSMpReturn error "APSMpSRSteerOrbit(34a - sddsselect1 - $result): $results"
            }
            APSMpReturn error "APSMpSRSteerOrbit(34 - sddsselect1): $result"
        }
        if [catch {exec sddscasr -restore $tempFile5 \
                   } result] {
            if [catch {APSMpRestoreSteeringFailSafeReturn error "APSMpSRSteerOrbit(35 - sddscasr): $result" \
                         -orbit $orbit -correctors $correctors } results] {
                APSMpReturn error "APSMpSRSteerOrbit(35 - sddscasr - $result): $results"
            }
            APSMpReturn error "APSMpSRSteerOrbit(35 - sddscasr): $result"
        }
    }
    
    APSMpStep "Do SCR save of final setpoints"
    eval $statusCallback {"Taking snapshot of final values."}
    set result ""
    set retry 1
    while {$retry} {
        if [catch {APSSaveMachine -machine SROrbitSetPt \
                       -description "Routine save: save final setpoints."} result] {
            APSMpReturn error "APSMpSRSteerOrbit(36 - SCR save SROrbitSetPt): $result"
        }
        set retry 0
        set snapshot4 /home/helios/oagData/SCR/snapshots/SROrbitSetPt/[lindex $result 0]
        if {[llength $result] > 1 } {
            APSFileDisplayWindow [APSUniqueName .burtlog] -fileName [lindex $result 1] \
                -comment "Log file returned when attempting to save BPM setpoints." \
                -width 80 \
                -contextHelp "Log file returned when attempting to save BPM setpoints."
            # query for retry, quit or blunder ahead
            switch [APSMultipleChoice [APSUniqueName .] -modal 1 -question "An error has been detected during the final BPM setpoint save. The most convenient thing to do is to retry a save.  If after several retries the problem still occurs, you should investigate possible BPM IOC problems. Pressing \"Quit\" restores the orignal bpm and corrector setpoints." -labelList {"Retry save" "Quit" "Blunder ahead"} -returnList {retry quit blunderAhead} -name "Retry save operation or quit steering?"] {
                retry {
                    set retry 1
                }
                quit {
                    set retry 0
                    if [catch {APSMpRestoreSteeringFailSafeReturn error "APSMpSRSteerOrbit(37 -SR save after steering failed): $result" -orbit $orbit } results] {
                        APSMpReturn error "APSMpSRSteerOrbit(37a - SR save after steering failed - $result): $results"
                    }

                    APSMpReturn error "APSMpSRSteerOrbit(37 - SR save after steering failed): $result"
                }
                blunderAhead {
                    set retry 0
                }
            }
        }
    }
    
    # query for undo.
    switch [APSMultipleChoice [APSUniqueName .] -modal 1 -question "Do you want to keep this steering or do you want to undo it?" -labelList {"Keep" "Undo"} -returnList {keep undo} -name "Undo steering?"] {
        keep {
            #
            # Send the new setpoints and offsets to the datapool vectors because there is no harming doing it.
            APSMpStep TransferVectorAdjust \
                -desc "Transfer vector adjust values from bpm ioc to datapool ioc" \
                -proc APSTransferVectorAdjust
            if [catch {APSTransferVectorAdjust -snapshot $snapshot4 } result] {
                APSMpReturn error "APSMpSRSteerOrbit(38 - APSTransferVectorAdjust): $result"
            }
        }
        undo {
            if [catch {APSMpRestoreSteeringFailSafeReturn ok "APSMpSRSteerOrbit(38 - APSTransferVectorAdjust): restored." \
                         -orbit $orbit -correctors $correctors} result] {
                APSMpReturn error "APSMpSRSteerOrbit(38a - APSMpRestoreSteeringFailSafeReturn): $result"
            }
        }
    }
    
    APSMpStep "SCR save after local steering."
    if [catch {APSSaveMachine -machine SRCorSetpts \
                   -description "Routine save: save after local steering."} result] {
        APSMpReturn error "APSMpSRSteerOrbit(39 - SCR save SRCorSetpts): $result"
    }
    APSMpStep TransferCorrectorReference \
        -desc "Setting reference corrector values for both planes" \
        -proc APSMpSRTransferCorrectorReference
    
    set finalCorrectorSCR /home/helios/oagData/SCR/snapshots/SRCorSetpts/[lindex $result 0]
    if [catch {APSMpSRTransferCorrectorReference -SCRFile $finalCorrectorSCR \
               } result ] {
        APSSetVarAndUpdate status "APSMpSRSteerOrbit: $result"
        APSMpReturn error "APSMpSRSteerOrbit(40 - APSMpSRTransferCorrectorReference): $result"
    }
    
    #put RestoreRTFBRegulators back, which was missed due to the lack of updating latest version
    APSMpStep RestoreRTFBRegulators -desc "Restore Default RTFB Regulators" \
        -proc APSMpSRSetFBfilters
    if [catch {APSMpSRSetFBfilters -ramp 1 -steps 5 -interval 1 \
                   -restore 1 \
               } result] {
        APSSetVarAndUpdate status "APSMpSRSteerOrbit: Warning: Problem restoring RT feedback high-pass filters. $result"
        APSInfoWindow .info -name "Operational Recommendations" -infoMessage "Warning: Problem setting RT feedback high-pass filters. $result\n\nOne may manually restore the regulator filter manually with SRFBControl application now." -modal 1
    }
    
    # 2x2 grid for runcontrol action
    #                    method
    #                    Local     Fine
    # server mode        resume   switch to Global
    # non-server mode    resume   abort; start Global
    # no more Fine steering mode !
    # For datapool, we do an abort and a re-start.
    # For non-datapool OC, we do a suspend and resume.

    # Assume that XRDP was used, then we have to find out if the
    # sector or sectors that were steered used xray bpms.
    # If not, then we can just resume datapool with defaultXRDP.
    # If yes, then regenerate a new XRDP without the xray
    # bpm of the sector or sectors that was steered.
    # We determine the steered sectors by any B:P[01] bpms that appear in
    # $tempFileSteeredLS. 
    # To check if the steered sectors had xray bpms, we have to search the
    # defaultXR and defaultXRDP config files.

    APSMpStep CheckOrbitCorrectionConfiguration -desc "Check to see if xray bpms need to be removed" 

    # list of sectors that were steered.
    if [catch {exec sddsprocess $tempFileSteeredLS.x -pipe=out -noWarning \
                 -match=col,ControlName=S*B:P? \
                 -scan=col,Sector,ControlName,S%ld,type=long \
                 | sdds2stream -pipe -col=Sector } sectorsX ] {
        APSSetVarAndUpdate status "APSMpSRSteerOrbit(40.1 - sddsprocess): $sectorsX"
        APSMpReturn error "APSMpSRSteerOrbit(40.1 - sddsprocess): $sectorsX"
    }
    if [catch {exec sddsprocess $tempFileSteeredLS.y -pipe=out -noWarning \
                 -match=col,ControlName=S*B:P? \
                 -scan=col,Sector,ControlName,S%ld,type=long \
                 | sdds2stream -pipe -col=Sector } sectorsY ] {
        APSSetVarAndUpdate status "APSMpSRSteerOrbit(40.2 - sddsprocess): $sectorsY"
        APSMpReturn error "APSMpSRSteerOrbit(40.2 - sddsprocess): $sectorsY"
    }
    set sectorList [lsort -unique -integer [concat $sectorsX $sectorsY]]

    # List of sectors in defaultXRDP with xray bpms
    # Assume the same for defaultXR because thay are normally created together.
    set contollawDir /home/helios/oagData/sr/orbitControllaw/lattices/default/
    if [catch {exec sddsprocess $contollawDir/h.defaultXRDP/config  -pipe=out -noWarning \
                 -match=para,NameType=MonitorNames \
                 -match=col,Name=S*ID:P? \
                 -scan=col,Sector,Name,S%ld,type=long \
                 | sdds2stream -pipe -col=Sector } XRDPsectorsX ] {
        APSSetVarAndUpdate status "APSMpSRSteerOrbit(40.3 - sddsprocess): $XRDPsectorsX"
        APSMpReturn error "APSMpSRSteerOrbit(40.3 - sddsprocess): $XRDPsectorsX"
    }
    if [catch {exec sddsprocess $contollawDir/v.defaultXRDP/config  -pipe=out -noWarning \
                 -match=para,NameType=MonitorNames \
                 -match=col,Name=S*ID:P? \
                 -scan=col,Sector,Name,S%ld,type=long \
                 | sdds2stream -pipe -col=Sector } XRDPsectorsY  ] {
        APSSetVarAndUpdate status "APSMpSRSteerOrbit(40.4 - sddsprocess): $XRDPsectorsY"
        APSMpReturn error "APSMpSRSteerOrbit(40.4 - sddsprocess): $XRDPsectorsY"
    }
    set XRDPsectorList [lsort -unique -integer [concat $XRDPsectorsX $XRDPsectorsY]]

    set sectorToRemove ""
    # need at least some sectors to be steered and some xray bpms
    if {[llength $sectorList] && [llength $XRDPsectorList]} {
        foreach sector $sectorList {
            if {-1 < [lsearch $XRDPsectorList $sector ]} {
                lappend sectorToRemove $sector
            }
        }
    }

    if {[llength $sectorToRemove] && !$DoNotRemoveXrayBPMs } {
        APSMpStep RemoveXrayBpms -desc "Removing xray bpms"
        # Somehow globals sectorsS<n>Sector need to be defined
        global env
        for {set i 1} {$i<41} {incr i} {
            global sectorsS${i}Sector
            set sectorsS${i}Sector 1
        }
        if [catch {APSRemoveIDfromDefaultConfigs -turnedOffSectorList $sectorToRemove  \
                     -confirm 0 \
                 } result] {
            APSSetVarAndUpdate status "APSMpSRSteerOrbit(43 - APSRemoveIDfromDefaultConfigs): $result"
            APSMpReturn error "APSMpSRSteerOrbit(43 - APSRemoveIDfromDefaultConfigs): $result"
        }

    }
    
    if {[llength $sectorToRemove] && !$DoNotRemoveXrayBPMs } {
       # operators need to regenerate auxiliary files for defaultXRDP manually
        if [string match $hCorrMode vector] {
            APSInfoWindow .info -infoMessage "Regenerate and start pvtest of defaultXRDP in both planes, and then press OK.\n\nDo not press the Full Start button since the BPM setpoint reference file is now wrong." -modal 1 -width 50
    
            APSMpStep RestartDefaultXRDP -desc "Datapool aborted and restarted without BPM setpoints reference file"
            # datapool aborted and started while in suspend mode.
            exec cavput -list=DP:S:OrbitControlLaw -list=X,Y -list=SDDS.CMND=0
            exec cavput -list=DP:S:OrbitControlLaw -list=X,Y -list=SDDS.CMND=1
            # now defaultXRDP is restarted
            # Note that the BPM setpoint reference file in SROrbitControllaw 
            # still is the present UBOP which has not yet been updated.
        } else {
            # for WS mode, orbit correction has to be completely restarted manually
            APSMpStep RestartDefaultXR -desc "Request restarting of defaultXR"
            APSInfoWindow .info -infoMessage "Press full start for both planes of defaultXR, and then press OK" -modal 1 
    
        }
        set execMode $oldMode
    } 

    if [string match $hCorrMode vector] {
        APSMpStep ResumeIOCOrbitCorrection -desc "Resume orbit correction in ioc" \
            -proc APSResumeSRControllaw
        foreach pl {X Y} {
            if [catch {APSResumeSRControllaw -runControlPV DP:S:OrbitControlLaw${pl}SDDS } result] {
                APSMpReturn error "APSMpSRSteerOrbit(41 - resumeSRcontrollaw): $result"
            }
        }
    } else {
        APSMpStep ResumeOrbitCorrection -desc "Resume orbit correction" \
            -proc APSSRControlOrbitCorrection
        if [catch {APSSRControlOrbitCorrection -plane both -action resume \
                   } result] {
            APSMpReturn error "APSMpSRSteerOrbit(42 - APSSRControlOrbitCorrection): $result"
        }
    }
   # APSMpStep "Resume ID gap feedforward..."
   # if [catch {APScavput -list=S:ID:GapFeedForwardRC.SUSP=0} result] {
   #     APSMpReturn error "APSMpSRSteerOrbit(42.5 - APScavput): $result"
   # }
    

    if [catch {exec cavput -list=S:OrbitControlLaw -list=X,Y -list=FineBO=0 \
               } result ] {
        APSSetVarAndUpdate status "APSMpSRSteerOrbit(43 - cavput): $result"
        APSMpReturn error "APSMpSRSteerOrbit(43 - cavput): $result"
    }
    APSMpReturn ok 0
}

proc APSMpSRTransferCorrectorReference {args} {
    global rangeSectors
    set SCRFile /home/helios/oagData/SCR/snapshots/SR/SR-UserBeamPreferred.gz
    set plane both
    APSStrictParseArguments {plane SCRFile}
    set Plane [string toupper $plane]
    switch $Plane {
        H -
        V {
            set PlaneList $Plane
        }
        BOTH {
            set PlaneList "H V"
        }
        default {
            set PlaneList ""
        }
    }
    foreach Plane $PlaneList {
        if [catch {exec sddsprocess $SCRFile -pipe=out \
                       -match=col,ControlName=S*:${Plane}?:CurrentAO \
                       -match=col,ControlName=SFB*,! \
                       | sdds2stream -col=ControlName,ValueString -pipe} readbackList] {
            APSSetVarAndUpdate status "APSMpSRTransferCorrectorReference(1 - sddsprocess): $readbackList"
            APSMpReturn error "APSMpSRTransferCorrectorReference(1 - sddsprocess): $readbackList"
        }
        set length [llength $readbackList]
        set setlist ""
        for {set index 0} {$index < $length} {incr index 2} {
            set index1 [expr $index + 1]
            regsub {:CurrentAO} [lindex $readbackList $index] "" name
            lappend setlist $name:RangeErrorCALC.B=[lindex $readbackList $index1]
        }
	if [catch {exec cavput -list=[join $setlist ","] -pend=15 \
                 } results] {
            APSSetVarAndUpdate status "APSMpSRTransferCorrectorReference(2 - cavput): $results"
            APSMpReturn error "APSMpSRTransferCorrectorReference(2 - cavput): $results"
        }
    }
}

proc APSMpSRSteerOrbitInitDialog {frame} {
    global APSMpSRSteerOrbit
    #    APSRadioButtonFrame .rb1 -parent $frame -orientation horizontal \
      -variable APSMpSRSteerOrbit(plane) \
      -label "Plane(s):               " \
      -buttonList {x y both} -valueList {x y both} \
      -contextHelp "Choose which planes to do steering in.  This will determine which planes of orbit correction are disabled and which planes have global changes to orbit setpoints to lock in the orbit. Plane setting is irrelevant when Local steering mode is selected."
    
    #    APSRadioButtonFrame .rb1a -parent $frame -orientation horizontal \
      -variable APSMpSRSteerOrbit(lockInOldOrbit) \
      -label "Zero out existing orbit:    " \
      -buttonList {Yes No} -valueList {1 0} \
      -contextHelp \
      "Choose whether to lock in the existing orbit at all BPMs by zeroing out the errors.  If you choose No, then the orbit may move in some locations as the fine correction steers back to zero error."
    
    #    APSRadioButtonFrame .rb1b -parent $frame -orientation horizontal \
      -variable APSMpSRSteerOrbit(lockInNewOrbit) \
      -label "Zero out new orbit:         " \
      -buttonList {Yes No} -valueList {1 0} \
      -contextHelp \
      "Choose whether to lock in the existing orbit at all BPMs after doing correction.  If you choose No, then the orbit may move more in some locations when the global correction is run."
    
    APSRadioButtonFrame .rb2 -parent $frame -orientation horizontal \
      -variable APSMpSRSteerOrbit(IDsource) \
      -label "ID sources:             " \
      -buttonList {Yes No} -valueList {1 0} \
      -commandList {"set APSMpSRSteerOrbit(CUsource) 0"  "set a 0"} \
      -contextHelp "Choose whether ID steering will be done or not.  If so, then a screen will be brought up to allow you to steer at the IDs. Can not start CU steering if ID steering is selected. If you already have the ID steering main screen up, then you should choose No."
    APSRadioButtonFrame .rb2a -parent $frame -orientation horizontal \
      -variable APSMpSRSteerOrbit(IDOptsource) \
      -label "ID Optimization sources:" \
      -buttonList {Yes No} -valueList {1 0} \
      -contextHelp "Choose whether ID intensity optimization will be done or not.  If so, then a screen will be brought up to allow you to otpimize the IDs."
    APSRadioButtonFrame .rbcu -parent $frame -orientation horizontal \
      -variable APSMpSRSteerOrbit(CUsource) \
      -label "CU sources:             " \
      -buttonList {Yes No} -valueList {1 0} \
      -commandList {"set APSMpSRSteerOrbit(IDsource) 0"  "set a 0"} \
      -contextHelp "Choose whether CU steering will be done or not.  If so, then a screen will be brought up to allow you to steer at the CUs. ID steering will be disabled if CU steering is selected. If you already have the CU steering main screen up, then you should choose No."

    APSRadioButtonFrame .rb3b -parent $frame -orientation horizontal \
      -variable APSMpSRSteerOrbit(BMsource) \
      -label "BM sources:             " \
      -buttonList {Yes No} -valueList {1 0} \
      -contextHelp "Choose whether BM steering will be done or not.  If so, then a screen will be brought up to allow you to steer at the BMs. If you already have the BM steering main screen up, then you should choose No."

    APSRadioButtonFrame .rb3a -parent $frame -orientation horizontal \
      -variable APSMpSRSteerOrbit(AMsource) \
      -label "AM sources:             " \
      -buttonList {Yes No} -valueList {1 0} \
      -contextHelp "Choose whether AM steering will be done or not.  If so, then a screen will be brought up to allow you to steer at the AMs. If you already have the AM steering main screen up, then you should choose No."

    APSRadioButtonFrame .launch -parent $frame -orientation horizontal \
      -variable APSMpSRSteerOrbit(doLaunchADT) \
      -label "Launch ADT with sr.ave.error.pv" \
      -buttonList {Yes No} -valueList {1 0} \
      -contextHelp "Launches adt with SR BPM average errors."
    
   # APSRadioButtonFrame .rbremoveXray -parent $frame -orientation horizontal \
      -variable APSMpSRSteerOrbit(DoNotRemoveXrayBPMs) \
      -label "Remove ID Xray bpms if ID steered?:" \
      -buttonList {Yes No} -valueList {0 1} \
      -contextHelp "Choose whether to automatically remove ID xray bpms for an ID that was steered. Normally leave it as \"Yes\", but in studies one could switch to \"No\"."
    
    #    APSRadioButtonFrame .rb6 -parent $frame -orientation horizontal \
      -variable APSMpSRSteerOrbit(preserveLSsetpoints) \
      -label "Preserve setpoints of other sources:" \
      -buttonList {Yes No} -valueList {1 0} \
      -contextHelp "Choose whether to preserve the BPM setpoints for the light sources that will not be steered (recommended). "

    #    APSRadioButtonFrame .rb6 -parent $frame -orientation horizontal \
      -variable APSMpSRSteerOrbit(preserveGlobalOrbit) \
      -label "Preserve setpoints of the global orbit:" \
      -buttonList {Yes No} -valueList {1 0} \
      -contextHelp "Choose whether to preserve the BPM setpoints for the whole orbit except for the light sources that will has been steered (recommended). "

    #    APSRadioButtonFrame .rb4 -parent $frame -orientation horizontal \
      -variable APSMpSRSteerOrbit(serverMode) \
      -label "Server mode:            " \
      -buttonList {Yes No} -valueList {1 0} \
      -contextHelp "Choose whether server mode of sddscontrollaw will be used. Server mode should be selected for efficient switching between Global and Fine correction. Server mode setting is irrelevant when Local steering mode is selected."

    #    APSRadioButtonFrame .rb5 -parent $frame -orientation horizontal \
      -variable APSMpSRSteerOrbit(steeringMode) \
      -label "Steering mode:          " \
      -buttonList {Local Fine} -valueList {Local Fine} \
      -contextHelp "Choose whether to use a local bump matrix (currently recommended) or Fine orbit correction for the steering method.\n\nThe local bump matrix method is available through the SRIDSteering, SRBMSteering, or SRAMSteering dialog boxes. The local correction matrices work in both x and y planes.\n\nFine orbit correction corrects the readbacks of BPMs at all source points and more. One may choose x or y plane or both."

    #    APSLabeledEntry .le1 -parent $frame \
      -textVariable APSMpSRSteerOrbit(gain) -width 12 \
      -label "Gain for correction:   " \
      -contextHelp \
      "Set the gain for feedback for orbit correction.  This is a value between 0 and 1 that determines how much of the correction is applied at each step. Used in non-server mode only."

}

proc getRunControlUser {args} {
    set rcPV ""
    APSStrictParseArguments {rcPV}
    if ![string length $rcPV] {
        APSMpReturn error "getRunControlUser(1): rcPV not specified."
    }
    if [catch {exec cavget -list=$rcPV -list=.USER} result] {
        APSMpReturn error "getRunControlUser(2 - cavget): $result"
    }
    APSMpReturn ok $result
}

proc getRunControlPid {args} {
    set rcPV ""
    APSStrictParseArguments {rcPV}
    if ![string length $rcPV] {
        APSMpReturn error "getRunControlPid(1): rcPV not specified."
    }
    if [catch {exec cavget -list=$rcPV -list=.PID} result] {
        APSMpReturn error "getRunControlPid(2 - cavget): $result"
    }
    APSMpReturn ok $result
}

proc APSSRCheckOrbitCorrection {args} {
    set plane ""
    set mode ""
    set queryIfMissing 0
    set corrMode ""
    APSStrictParseArguments {plane mode queryIfMissing}

    # do this to eliminate problems with lack of mode
    # info in run control descriptions now.
    # should modify all the code to no longer refer to this parameter.
    set mode ""
    
    if ![string length $plane] {
        APSMpReturn error "APSSRCheckOrbitCorrection(1): plane not specified."
    }
    switch $plane {
        h -
        v { 
            if [catch {APSSRCheckCorrectorMode -plane $plane} corrMode] {
                APSMpReturn error "APSSRCheckOrbitCorrection(2 - APSSRCheckCorrectorMode): $corrMode"
            }
        }
        both {
            if [catch {APSSRCheckCorrectorMode -plane h} hCorrMode] {
                APSMpReturn error "APSSRCheckOrbitCorrection(3 - APSSRCheckCorrectorMode): $hCorrMode"
            }
            if [catch {APSSRCheckCorrectorMode -plane v} vCorrMode] {
                APSMpReturn error "APSSRCheckOrbitCorrection(4 - APSSRCheckCorrectorMode): $vCorrMode"
            }
            if [string compare $hCorrMode $vCorrMode]!=0 {
                APSMpReturn error "APSSRCheckOrbitCorrection(5): corrector mode in x and y plane do not match!"
            }
            set corrMode $hCorrMode
        }
    }
    switch $corrMode {
        scalar {
            if [catch {exec cavget -list=S:OrbitControlLaw -list=X,Y -list=RC.RUN} result] {
                APSMpReturn error "APSSRCheckOrbitCorrection(6 - cavget): $result"
            }
        }
        vector {
            if [catch {exec cavget -list=DP:S:OrbitControlLaw -list=X,Y -list=SDDS.RUN} result] {
                APSMpReturn error "APSSRCheckOrbitCorrection(7 - cavget): $result"
            }
        }
        default {
            APSMpReturn error "APSSRCheckOrbitCorrection(8): Unknown corrector mode: $corrMode (should be scalar or vector)!"  
        }
    }
    # cavget returns 1 if runcontrol is running
    set problemFound 0
    switch $plane {
        x {
            if ![lindex $result 0] {
                set problemFound 1
            }
        }
        y {
            if ![lindex $result 1] {
                set problemFound 1
            }
        }
        both {
            if {![lindex $result 0] || ![lindex $result 1]} {
                set problemFound 1
            }
        }
    }

    if {!$problemFound && [string compare $corrMode scalar]==0} {
        if [catch {exec cavget -list=S:OrbitControlLaw -list=X,Y -list=RC.DESC} result] {
            APSMpReturn error "APSSRCheckOrbitCorrection(9 - cavget): $result"
        }
        switch $plane { 
            x {
                if {[string length $mode] && ![regexp $mode [lindex $result 0]] } {
                    APSSetVarAndUpdate status \
                      "Problem found in regexp $mode [lindex $result 0]"
                    set problemFound 1
                }    
            }
            y {
                if {[string length $mode] && ![regexp $mode [lindex $result 1]] } {
                    APSSetVarAndUpdate status \
                      "Problem found in regexp $mode [lindex $result 1]"
                    set problemFound 1
                }    
            }
            both {
                if {[string length $mode] && 
                    ![regexp $mode [lindex $result 0]] &&
                    ![regexp $mode [lindex $result 1]] } {
                    APSSetVarAndUpdate status \
                      "Problem found in regexp $mode [lindex $result 0] or regexp $mode [lindex $result 1]"
                    set problemFound 1
                }
            }
        }
    }

    if {$problemFound && $queryIfMissing} {
        switch \
          [APSMultipleChoice [APSUniqueName .] -modal 1 \
             -question "$mode orbit correction is not presently running in one or more of the planes you are about to steer.  This may result in locking in of an undesirable orbit, and is not recommended." \
             -labelList {Proceed Abort} -returnList {Proceed Abort} \
             -name "Are you sure, human?"] {
                 Abort {
                     APSMpReturn error "APSSRCheckOrbitCorrection(10): Steering aborted."
                 }
                 Proceed {
                     APSMpReturn ok 0
                 }
             }
    }
    # returns 1 if all requested corrections are running.
    APSMpReturn ok !$problemFound
}

# This procedure only seems to handle the non-datapool (i.e. WS) runcontrol PVs.
proc APSSRControlOrbitCorrection {args} {
    set plane both
    set action abort
    APSStrictParseArguments {plane action}
    set action [string tolower $action]
    
    switch $action {
        abort {
            set action Abort
        }
        suspend {
            set action Suspend
        }
        resume {
            set action Resume
        }
        default {
            APSMpReturn error "APSSRControlOrbitCorrection(1): unknown action $action"
        }
    }

    switch $plane {
        h {
            if [catch {APS${action}Controllaw -runControlPV S:OrbitControlLawXRC} result] {
                APSMpReturn error "APSSRControlOrbitCorrection(2 - h controllaw): $result"
            }
        }
        v {
            if [catch {APS${action}Controllaw -runControlPV S:OrbitControlLawYRC} result] {
                APSMpReturn error "APSSRControlOrbitCorrection(3 - v controllaw): $result"
            }
        }
        both {
            if {[catch {APS${action}Controllaw -runControlPV S:OrbitControlLawXRC
                APS${action}Controllaw -runControlPV S:OrbitControlLawYRC} result]} {
                APSMpReturn error "APSSRControlOrbitCorrection(4 - controllaw): $result"
            }
        }
        default {
            APSMpReturn error "APSSRControlOrbitCorrection: unknown plane $plane"
        }
    }
    APSMpReturn ok 0
}

proc APSChangeSROrbitCorrectionMode {args} {
    APSMpReturn error "APSChangeSROrbitCorrectionMode: This procedure no longer available."
}

proc APSStartSROrbitControllawMode {args} {
    APSMpReturn error "APSStartSROrbitControllawMode: This procedure no longer available."
}

proc APSSROrbitControllawSendSignal {args} {
    APSMpReturn error "APSSROrbitControllawSendSignal: This procedure no longer available."
}

proc APSMpRestoreSteeringFailSafeReturn {code results args} {
    set correctors ""
    set orbit ""
    APSStrictParseArguments {correctors orbit} 
    # query for return to previous state
    switch \
        [APSMultipleChoice [APSUniqueName .] -modal 1 \
             -question "An error - \"$results\" has been detected that may compromise the generation of new bpm setpoints.\n\nOR\n\nA request has been made to restore original setpoints.\n\nYou have the choice of restoring: the original BPM setpoints, the original corrector setpoints, both or none. Restoring correctors is necessary if any actual beam steering has been done. Choosing both is fail-safe." \
             -labelList {"BPM setpoints" "Corrector setpoints" Both None} -returnList {bpm corrector both none} \
             -name "Restore steering?"] {
                 bpm {
                     if [catch {APSSRRestoreSteering -orbit $orbit} result] {
                         APSMpReturn error "APSMpRestoreSteeringFailSafeReturn(1 - APSSRRestoreSteering bpm): $result"
                     }
                 }
                 corrector {
                     if [catch {APSSRRestoreSteering -correctors $correctors} result] {
                         APSMpReturn error "APSMpRestoreSteeringFailSafeReturn(2 - APSSRRestoreSteering corrector): $result"
                     }
                 }
                 both {
                     if [catch {APSSRRestoreSteering -correctors $correctors -orbit $orbit} result] {
                         APSMpReturn error "APSMpRestoreSteeringFailSafeReturn(3 - APSSRRestoreSteering both): $result"
                     }
                 }
                 none {
                 }
             }
    APSMpReturn $code $results
}

proc APSSRRestoreSteering {args} {
    set orbitSnapDir /home/helios/oagData/SCR/snapshots/SROrbitSetPt
    set corrSnapDir /home/helios/oagData/SCR/snapshots/SRCorSetpts
    set correctors ""
    set orbit ""
    APSStrictParseArguments {correctors orbit}
    
    set mode [APSSRCheckCorrectorMode -plane h]
    if [string length $orbit] {
        if [string compare $mode vector]==0 {
            if [catch {APSTransferVectorAdjust -snapshot $orbitSnapDir/$orbit} result] {
                APSMpReturn error "APSSRRestoreSteering(1 - APSTransferVectorAdjust): $result"
            }
        }
        set tempFile /tmp/[APSTmpString]
        if [catch {exec gzip -dcn $orbitSnapDir/$orbit > $tempFile
            exec  sddscasr -restore $tempFile \
                 } result] {
            APSMpReturn error "APSSRRestoreSteering(2 - sddscasr): $result"
        }
        file delete $tempFile
    }
    if [string length $correctors] {
        if [string compare $mode vector]==0 {
            foreach plane {h v} {
                if [catch {APSRampCorrVectorToSnapshot -plane $plane \
                             -snapshot $corrSnapDir/$correctors \
                         } result] {
                    APSMpReturn error "APSSRRestoreSteering(3 - APSRampCorrVectorToSnapshot): $result"
                }
                #if [catch {APSInitializeCorrVector -plane $plane \
                  #             -correctorFile $corrSnapDir/$correctors} result] {
                #   APSMpReturn error "APSSRRestoreSteering: $result"
                #}
            }
        } else {
            set tempFile /tmp/[APSTmpString]
            if [catch {exec gzip -dcn $corrSnapDir/$correctors > $tempFile
                APSRampToSnapshot -initDialog 1 -fileName $tempFile \
                     } result] {
                APSMpReturn error "APSSRRestoreSteering(4 - APSRampToSnapsho): $result"
            }
            file delete $tempFile
        }
    }
    APSMpReturn ok 0
}


proc APSCorrectorSanityCheck {args} {
    set plane ""
    set AOAItolerance 0.5
    set dacAOtolerance 0.0023
    set dacFile ""
    #DacAI and CurrentAO should be close, so use AOAItolerance for DacAI and CurrentAI difference too
    APSStrictParseArguments {plane AOAItolerance dacAOtolerance dacFile}
    set wfDir /home/helios/oagData/sr/orbitControllaw/waveforms

     switch $plane {
        x -
        y {
            set coord $plane
            set Coord [string toupper $plane]
        }
        h {
            set coord x
            set Coord X
        }
        v {
            set coord y
            set Coord Y
        }
    }
    switch $plane {
        x {
            set plane h
        }
        y {
            set plane v
        }
    }
    
    set tmpRoot /tmp/[APSTmpString]
    set vectorfile $wfDir/${plane}corrInfo.sdds
    set fileList ""
    if ![string length $dacFile] {
        set dacFile $tmpRoot.dacAI
    }
    set configFile /home/helios/oagData/sr/[string toupper $plane]CorrectorStatus/config.sdds
    if [catch {exec sddsprocess $vectorfile -pipe=out \
                 -edit=col,ControlName,DeviceName,ei/:DacAI/ \
                 | sddscasr -save -pipe -pend=50 \
                 | sddsprocess -pipe=in $dacFile  \
                 -scan=col,DacAI,ValueString,%lf } result] {
        return -code error "APSCorrectorSanityCheck(3 - sddsprocess):$result"
    }
    if [catch {exec sddsprocess $vectorfile -pipe=out \
                 -edit=col,ControlName,DeviceName,ei/:CurrentAO/ \
                 | sddscasr -save -pipe -pend=50 \
                 | sddsprocess -pipe=in $tmpRoot.currentAO \
                 -scan=col,CurrentAO,ValueString,%lf } result] {
        return -code error "APSCorrectorSanityCheck(4 - sddsprocess):$result"
    }
    if [catch {exec sddsprocess $vectorfile -pipe=out \
                 -edit=col,ControlName,DeviceName,ai/SFB:/ei/:FFCurrentAO/ \
                 | sddscasr -save -pipe -pend=50 \
                 | sddsprocess -pipe=in $tmpRoot.FFcurrentAO \
                 -scan=col,FFCurrentAO,ValueString,%lf } result] {
        return -code error "APSCorrectorSanityCheck(4 - sddsprocess):$result"
    }
    if [catch {exec sddsprocess $vectorfile -pipe=out \
                 -edit=col,ControlName,DeviceName,ei/:CurrentAI/ \
                 | sddscasr -save -pipe -pend=50 \
                 | sddsprocess -pipe=in $tmpRoot.currentAI  \
             -scan=col,CurrentAI,ValueString,%lf } result] {
        return -code error "APSCorrectorSanityCheck(5 - sddsprocess):$result"
    }
    if [catch { exec sddsxref $dacFile $tmpRoot.currentAO $tmpRoot.FFcurrentAO $tmpRoot.currentAI \
		    -take=*Current* -pipe=out \
		    | sddsxref -pipe $configFile -match=DeviceName -take=Nonexistent \
		    | sddsprocess -pipe \
		    "-filter=col,Nonexistent,0,0" \
		    "-define=col,DacAI-CurrentAI,DacAI CurrentAI - abs" \
		    "-define=col,DacAI-CurrentAO-FFCurrentAO,DacAI CurrentAO - FFCurrentAO - abs" \
		    "-define=col,CurrentAI-CurrentAO-FFCurrentAO,CurrentAO FFCurrentAO + CurrentAI - abs" \
		    | sddsprocess -pipe=in -nowarnings \
		    "-test=col,DacAI-CurrentAI $AOAItolerance > DacAI-CurrentAO-FFCurrentAO $dacAOtolerance > || CurrentAI-CurrentAO-FFCurrentAO $AOAItolerance > ||" $tmpRoot.outrange }  result] {
        return -code error "APSCorrectorSanityCheck(6 - sddsprocess):$result"
    }
    set rows [exec sdds2stream -rows=bar $tmpRoot.outrange]
  
    if $rows {
        if [catch {exec sddsprintout $tmpRoot.outrange $tmpRoot.print \
                     "-title=Following correctors have difference bigger than $AOAItolerance for DacAI-CurrentAI or FFCurrentAO+CurrentAO-CurrentAI\\, or $dacAOtolerance for DacAI-\(CurrentAO+FFCurrentO\)" \
                     -col=DeviceName,format=%10s -col=DacAI-CurrentAI,format=%20.2f \
                     -col=DacAI-CurrentAO-FFCurrentAO,format=%20.2f -col=CurrentAI-CurrentAO-FFCurrentAO,format=%20.2f } result] {
            return -code error "APSCorrectorSanityCheck(7 - sddsprintout):$result"
        }
        APSFileDisplayWindow [APSUniqueName .error] -modal 1 -fileName $tmpRoot.print -width 100
        file delete -force $tmpRoot.print
    }
    file delete -force $tmpRoot.currentAO $tmpRoot.currentAI $tmpRoot.outrange $tmpRoot.FFcurrentAO
    if !$rows {
        return ok
    } else {
        return -code error "Some correctors' current values (DacAI, CurrentAI, and CurrentAO) have difference bigger than requested tolerances."
    }
}

# Note that this procedure also initializes the datapool BPM setpoint vector
proc APSInitializeCorrVector {args} {
    set plane ""
    set correctorFile ""
    set stepSizeLimit 5
    set rampSteps 0
    set AOAItolerance 0.5
    set dacAOtolerance 0.0023
    set setpoint 0
    APSStrictParseArguments {plane correctorFile stepSizeLimit rampStep AOAItolerance dacAOtolerance setpoint}
    set wfDir /home/helios/oagData/sr/orbitControllaw/waveforms
    #puts [exec date]
    set plane [string tolower $plane] 
    switch $plane {
        x -
        y {
            set coord $plane
            set Coord [string toupper $plane]
        }
        h {
            set coord x
            set Coord X
        }
        v {
            set coord y
            set Coord Y
        }
    }
    switch $plane {
        x {
            set plane h
        }
        y {
            set plane v
        }
    }
    #before initialization, check does corrector's sanity check
    
    # Transfer vector correctors from corrector ioc to datapool ioc
    set tmpFile /tmp/[APSTmpString].corr1
    APSAddToTmpFileList -ID initializecorrvector -fileList "$tmpFile $tmpFile.setpoint $tmpFile.set"
    set vectorfile $wfDir/${plane}corrInfo.sdds
    # read present FF vector. It is required no matter how the setpoints are read in below.
    if [catch {exec sddsconvert $vectorfile $tmpFile.FFwfm  \
                 -rename=par,FeedforwardWaveformPV=WaveformPV } result] {
        APSMpReturn error "APSInitializeCorrVector(1a - sddsprocess):$result"
    }
    if [catch {exec sddswget -inputStyle=wget $tmpFile.FFwfm  $tmpFile.FFwfm.read } result] {
        APSMpReturn error "APSInitializeCorrVector(1b - sddsprocess):$result"
    }
    if {![string length $correctorFile] && !$setpoint} {
        # the most common case
        while {1} {
            if [catch {APSCorrectorSanityCheck -plane $plane -AOAItolerance $AOAItolerance -dacAOtolerance $dacAOtolerance -dacFile $tmpFile.1} result] {
                set answer [APSMultipleChoice .corrInfo -modal 1 -question "CheckCurrentDifference: $result\nInitialze CorrVector will put value of DacAI into CurrentAO (setpoint) and datapool corrector waveform PV. \n\nWhat would you like to do?" \
                              -labelList {Retry Contine-And-Initialize-CorrVector Continue-without-Initialize-CorrVector Abort} -returnList {retry continue ok abort}]
                
                if {$answer == "abort"} {
                    APSMpReturn error "APSInitializeCorrVector(1a): corrector sanity check failed: $result"
                } elseif {$answer == "continue"} {
                    break
                } elseif {$answer == "ok"} {
                    APSMpReturn ok
                }
            } else {
                break
            }
        }
        if [catch {exec sddsprocess $tmpFile.1 -pipe=out \
                     -scan=col,Waveform,ValueString,%lf \
                     | sddsconvert -pipe=in $tmpFile -retain=col,Index,DeviceName,Waveform \
                     -retain=par,SetpointWaveformPV \
                     -rename=par,SetpointWaveformPV=WaveformPV } result] {
            APSMpReturn error "APSInitializeCorrVector(1 - sddsprocess):$result"
        }
        # file $tmpFile has the FF value subtracted from the DAC value adjusted
        if [catch {exec sddsxref $tmpFile $tmpFile.FFwfm.read -pipe=out \
                     -rename=col,Waveform=FFWaveform \
                     | sddsprocess -pipe=in $tmpFile \
                     "-redef=col,Waveform,Waveform FFWaveform -" } result] {
            APSMpReturn error "APSInitializeCorrVector(1c - sddsprocess):$result"
        }
        
        #puts "[exec date]: process corr vector done"
        if [catch {exec sddswput $tmpFile -pend=30} result] {
            APSMpReturn error "APSInitializeCorrVector(2 - sddswput): $result"
        }
        #puts "[exec date]: set corr vector done."
    } elseif [string length $correctorFile] {
        if ![file exist $correctorFile] {
            return -code error "APSInitializeCorrVector(3): $correctorFile does not exist!"
        }
        if [catch {exec sddsprocess $correctorFile $tmpFile.1 \
                     "-edit=col,DeviceName,ControlName,%/SFB://%/:CurrentAO//%/:DacAI//" \
                     -scan=col,Waveform,ValueString,%lf } result] {
            return -code error "APSInitializeCorrVector(4 - sddsprocess): $result"
        }
        if [catch {exec sddsxref $vectorfile $tmpFile.1 -match=DeviceName -take=Waveform -pipe=out -fill \
                     | sddsconvert -pipe=in $tmpFile -rename=par,SetpointWaveformPV=WaveformPV } result] {
            return -code error "APSInitializeCorrVector(5 - sddsxref): $result"
        }
        if [catch {exec sddswput $tmpFile -pend=30} result] {
            APSMpReturn error "APSInitializeCorrVector: $result"
        }
        APSAddToTmpFileList -ID TransferCorrectors -fileList "$tmpFile $tmpFile.1"
    } elseif {$setpoint} {
        #initialize corrector vector from setpoint
        if [catch {exec sddsprocess $vectorfile -pipe=out \
                     -edit=col,ControlName,DeviceName,ei/:CurrentAO/ \
                     | sddscasr -pipe -save \
                     | sddsprocess -pipe -scan=col,Waveform,ValueString,%lf \
                     | sddsconvert -pipe=in $tmpFile -retain=col,Index,DeviceName,Waveform \
                     -retain=par,SetpointWaveformPV \
                     -rename=par,SetpointWaveformPV=WaveformPV } result] {
            APSMpReturn error "APSIntializeCorrVector from setpoint: $result"
        }
        # # file $tmpFile has the FF value subtracted from the DAC value adjusted
        # if [catch {exec sddsxref $tmpFile.1 $tmpFile.FFwfm.read -pipe=out \
        #              -rename=col,Waveform=FFWaveform \
        #              | sddsprocess -pipe=in $tmpFile \
        #              "-redef=col,Waveform,Waveform FFWaveform -" } result] {
        #     APSMpReturn error "APSInitializeCorrVector(1c - sddsprocess):$result"
        # }
        if [catch {exec sddswput $tmpFile -pend=30} result] {
            APSMpReturn error "APSInitializeCorrVector from setpoint(2 - sddswput): $result"
        }
    }
    # Transfer scalar BPM setpoints to the datapool BPM setpoint vector
    set setpointfile $wfDir/RTFB${Coord}bpmInfo.sdds
    if [catch {exec sddsprocess $setpointfile -pipe=out \
                 -edit=col,ControlName,DeviceName,ei/:${coord}:RefAO \
                 | sddscasr -pipe -save -pend=50 \
                 | sddsprocess -pipe -scan=col,Waveform,ValueString,%lf \
                 | sddsconvert -pipe=in $tmpFile.setpoint \
                 -retain=col,Index,DeviceName,Waveform -retain=par,FFSetWaveformPV \
                 -rename=par,FFSetWaveformPV=WaveformPV } result] {
        APSMpReturn error "APSInitializeCorrVector(6 - sddsprocess): $result"
    }
    # puts "[exec date]: process corr setpoint vector done."
    #if [catch {exec sddscombine $tmpFile $tmpFile.setpoint $tmpFile.set -overWrite
    #    exec sddswput $tmpFile.set } result] {
    #    APSMpReturn error "APSInitializeCorrVector(7 - sddswput): $result"
    #}
    if [catch {exec sddswput $tmpFile.setpoint} result] {
        APSMpReturn error "APSInitializeCorrVector(8 - sddswput): $result"
    }
    #puts "[exec date]: set corr setpoint done."
    APSMpReturn ok 0
}

proc APSRampCorrVectorToSnapshot {args} {
    set plane ""
    set snapshot ""
    set stepSizeLimit 5
    set rampSteps 0
    set pause 1
    APSStrictParseArguments {plane snapshot stepSizeLimit rampSteps pause}
    set wfDir /home/helios/oagData/sr/orbitControllaw/waveforms
    
    set plane [string tolower $plane] 
    switch $plane {
        x -
        h {
            set coord x
            set Coord X
            set Plane H
            set vectorfile $wfDir/hcorrInfo.sdds
        }
        y -
        v {
            set coord y
            set Coord Y
            set Plane V
            set vectorfile $wfDir/vcorrInfo.sdds
        }
        default {
            return -code error "The plane has to be x/y or h/v!"
        }
    }
    #transfer vector correctors from corrector ioc to datapool ioc
    set tmpfile /tmp/[APSTmpString]
    if ![file exist $snapshot] {
        return -code error "APSRampCorrVectorToSnapshot(1): File $snapshot doesn't exist."
    }
    APSAddToTmpFileList -ID 1 -fileList "$tmpfile.current $tmpfile.ramp $tmpfile"
    # remove tee's and a cp to fixed /tmp files introduced in version 1.126 for debugging
    if [catch {exec sddsprocess $snapshot -pipe=out \
                 -match=col,ControlName=SFB*:${Plane}*:CurrentAO \
                 -scan=col,RampToValue,ValueString,%lf \
                 | sddsconvert -pipe -retain=col,ControlName,RampToValue \
                 | sddscasr -pipe -save -pend=100 \
                 | sddsprocess -pipe -match=col,CAError=n \
                 -scan=col,CurrentValue,ValueString,%lf \
                 | sddsprocess -pipe \
                 "-define=col,AbsDiff,RampToValue CurrentValue - abs" \
                 | sddsprocess -pipe=in $tmpfile.current \
                 "-process=AbsDiff,max,MaxDiff" } result] {
        return -code error "APSRampCorrVectorToSnapshot(2 - sddsprocess): $result"
    }
    set correctors [exec sdds2stream $tmpfile.current -col=ControlName -page=1]
    set rampToVals [exec sdds2stream $tmpfile.current -col=RampToValue -page=1]
    set currentVals [exec sdds2stream $tmpfile.current -col=CurrentValue -page=1]
    set maximumChange [exec sdds2stream $tmpfile.current -par=MaxDiff -page=1]
    
    set steps [format %.0f [expr abs($maximumChange)/$stepSizeLimit +1]]
    if [catch {exec sddsconvert $vectorfile -rename=par,SetpointWaveformPV=WaveformPV $tmpfile.vector
        exec sddswget $tmpfile.vector $tmpfile.vector.value } result] {
        return -code error "APSRampCorrVectorToSnapshot(2a -- read corrector vector value): $result"
    }
    APSAddToTmpFileList -ID 1 -fileList "$tmpfile.vector  $tmpfile.vector.value"
    for {set i 1} {$i<=$steps} {incr i} {
        APSAddToTmpFileList -ID 1 -fileList $tmpfile.$i
        set valueList ""
        foreach target $rampToVals curr $currentVals {
            set delta [expr 1.0 * ($target - $curr) / $steps ]
            set setval [expr $curr + $delta * $i]
            lappend valueList $setval
        }
        if [catch {exec sddsmakedataset -pipe=out \
                     -col=ControlName,type=string -data=[join $correctors ,] \
                     -col=Waveform,type=double -data=[join $valueList ,] \
                     | sddsprocess -pipe=in $tmpfile.ramp \
                     -edit=col,DeviceName,ControlName,%/SFB://%/:CurrentAO// } result] {
            return -code error "APSRampCorrVectorToSnapshot(3 - sddsmakedataset): $result"
        }
        #fill in caused to fill in zeros for correctors that are not in the snapshot,
        #these caused beam dump if the missing corrector is on, therefore, change to
        #not fill in zeros for correctors that are not in snapshot  10/3/2014
        
        if [catch {exec sddsxref $vectorfile $tmpfile.ramp \
                     -match=DeviceName -take=Waveform -pipe=out  -nowarnings \
                     | sddsconvert -pipe=in $tmpfile.$i \
                     -rename=par,SetpointWaveformPV=WaveformPV } result] {
            return -code error "APSRampCorrVectorToSnapshot(4 - sddsxref): $result"
        }
        if [catch {exec sddsselect $tmpfile.vector.value $tmpfile.$i -match=DeviceName -invert $tmpfile.$i.not } result] {
             return -code error "APSRampCorrVectorToSnapshot(4a - sddsselect): $result"
        }
        APSAddToTmpFileList -ID 1 -fileList $tmpfile.$i.not
        set rows [exec sdds2stream -rows=bar $tmpfile.$i.not]
        if $rows {
            if [catch {exec sddscombine $tmpfile.$i $tmpfile.$i.not -merge -pipe=out \
                         | sddssort -pipe=in -col=Index $tmpfile.$i.1 } result] {
                return -code error "APSRampCorrVectorToSnapshot(4b - sddscombine): $result"
            }
            exec mv $tmpfile.$i.1 $tmpfile.$i
        }
        #Add -pend=100 for next test
        if [catch {exec sddswput $tmpfile.$i} result] {
            return -code error "APSRampCorrVectorToSnapshot(5 - sddswput): $result"
        }
        after [expr 1000 * $pause]
    }
    return 0
}

proc APSSRCheckCorrectorMode {args} {
    set mode ""
    set unmatch 0
    set plane ""
    APSStrictParseArguments {plane}
    switch $plane {
        h {
            set Plane H
            set Coord X
        }
        v {
            set Plane V
            set Coord Y
        }
        default {
            return -code error "APSSRCheckCorrectorMode(1): input plane should be h or v!"
        }
    }
    
    if [catch {exec cavget -list=DP:EnableWrite${Coord}bpmSetPt2RMC -pend=30 -num} result1] {
        return -code error "APSSRCheckCorrectorMode(2 - cavget): $result1"
    }
    if [catch {exec cavget -list=DP:${Coord}bpmSetPtVectorModeC -pend=30 -num} result2] {
        return -code error "APSSRCheckCorrectorMode(3 - cavget): $result2"
    }
    
    if {$result1 != $result2} {
        return -code error "APSSRCheckCorrectorMode(4): the datapool bpm setpoints mode switches in $Plane does not match!"
    }
    if [catch {exec cavget -list=DP:EnableWrite${Plane}Cor2RMC -pend=30 -num} result3] {
        return -code error "APSSRCheckCorrectorMode(5 - cavget): $result3"
    }
    if {$result1 != $result3} {
        return -code error "APSSRCheckCorrectorMode(6): the setpoint and corrector vector mode in $Plane do not match!"
    }
    if [catch {exec cavget -list=DP:${Plane}CorrVectorModeC -pend=30 -num} result4] {
        return -code error "APSSRCheckCorrectorMode(7 - cavget): $result4"
    }
    if {$result1 != $result4} {
        return -code error "APSSRCheckCorrectorMode(8): the corrector mode in $Plane do not match!"
    }
    
    if $result1 {
        set mode vector
    } else {
        set mode scalar
    }
    return $mode
}

proc APSTransferVectorAdjust {args} {
    set offsetReferenceFile /home/helios/oagData/SCR/snapshots/SR/SR-BPMOffsetReference.gz
    set requestFile /home/helios/oagData/SCR/requestFiles/SROrbitSetPt.req
    set snapshot ""
    set coord ""
    APSStrictParseArguments {snapshot coord}
    set tmpRoot /tmp/[APSTmpString]
    if ![string length $snapshot] {
        #read the offset and setpoint values from ioc
        APSAddToTmpFileList -ID APSTransferVectorAdjust -fileList "$tmpRoot.snap"
        if [catch {exec sddscasr -save -pend=70 $requestFile $tmpRoot.snap \
                 } result] {
            return -code error "APSTransferVectorAdjust(1 - sddscasr): $result"
        }
        switch $coord {
            x -
            y {
                if [catch {exec sddsprocess $tmpRoot.snap $tmpRoot.snap.$coord \
                             -match=col,ControlName=*${coord}* } result] {
                    return -code error "APSTransferVectorAdjust(2 - sddsprocess): $result"
                    
                }
                set snapshot $tmpRoot.snap.$coord
            }
            default {
                set snapshot $tmpRoot.snap
            }
        }
    }
    APSAddToTmpFileList -ID APSTransferVectorAdjust \
      -fileList "$tmpRoot.setpoint $tmpRoot.adjust $tmpRoot.adjust.x $tmpRoot.offset $tmpRoot.adjust.y"
    if [catch {exec sddsprocess $snapshot $tmpRoot.setpoint \
                 -match=col,ControlName=*SetpointAO \
                 -edit=col,BPMName,ControlName,%/:SetpointAO// \
                 -scan=col,Setpoint,ValueString,%lf \
                 -edit=col,DeviceName,BPMName,%/:ms:x//%/:ms:y//
        exec sddsprocess $snapshot $tmpRoot.offset \
                 -match=col,ControlName=*OffsetAO \
                 -edit=col,BPMName,ControlName,%/:OffsetAO// \
                 -scan=col,Offset,ValueString,%lf \
                 -edit=col,DeviceName,BPMName,%/:ms:x//%/:ms:y// 
        exec sddsxref $tmpRoot.offset $tmpRoot.setpoint -pipe=out \
                 -match=BPMName -take=Setpoint \
                 | sddsprocess -pipe \
                 "-define=col,Waveform,Setpoint Offset +" \
                 | sddsconvert -pipe=in $tmpRoot.adjust \
                 -retain=col,BPMName,DeviceName,Waveform 
        exec sddsprocess $tmpRoot.adjust $tmpRoot.adjust.x \
                 -match=col,BPMName=*x -noWarnings
        exec sddsprocess $tmpRoot.adjust $tmpRoot.adjust.y \
                 -match=col,BPMName=*y -noWarnings \
             } result] {
        return -code error "APSTransferVectorAdjust(3 - sddsxref): $result"
    }
    set rowx [exec sdds2stream $tmpRoot.adjust.x -rows=bare]
    set rowy [exec sdds2stream $tmpRoot.adjust.y -rows=bare]
    set planes ""
    if $rowx {
        lappend planes x
    }
    if $rowy {
        lappend planes y
    }
    set waveformFileDir /home/helios/oagData/sr/orbitControllaw/waveforms
    
    foreach plane $planes {
        set waveformFile ${waveformFileDir}/adjust[string toupper $plane]BpmInfo.sdds
        APSAddToTmpFileList -ID APSTransferVectorAdjust -fileList $tmpRoot.$plane
        if [catch {exec sddsxref $waveformFile $tmpRoot.adjust.$plane \
                     $tmpRoot.$plane -reuse \
                     -fillIn -nowarning \
                     -match=DeviceName -take=Waveform  
            exec sddswput $tmpRoot.$plane } result] {
            APSDeleteTmpFileList -ID RestoreDPOffset
            return -code error "APSTransferVectorAdjust(4 - sddsxref): $result"
        }
    }
}

proc APSRefreshVectorAdjust {args} {
    #refresh vector adjust values due to changes of setpoints of several pvs in ioc.
    set pvList ""
    set plane ""
    set waveformFile ""
    set waveformDir /home/helios/oagData/sr/orbitControllaw/waveforms
    APSStrictParseArguments {pvList plane waveformFile}
    
    set tmpRoot /tmp/[APSTmpString]
    set tmpList ""
    if {![llength $pvList]} {
        return -code error "APSRefreshVectorAdjust(1): pvList is not provided !"
    }
    set plane [string tolower $plane]
    switch $plane {
        x -
        y {
            set planes $plane
        }
        both {
            set planes {x y}
        }
        default {
            return -code error "APSRefreshVectorAdjust(2): plane should be x or y or both !"
        }
    }
    foreach coord $planes {
        set Data(ColumnNames) "DeviceName Waveform"
        set Data(ColumnInfo.DeviceName) "type SDDS_STRING"
        set Data(ColumnInfo.Waveform) "type SDDS_DOUBLE"
        set Data(Column.DeviceName) ""
        set Data(Column.Waveform) ""
        if [catch {exec cavget -list=[join $pvList ,] -list=:ms:${coord}:OffsetAO -pend=30} Offsets] {
            return -code error "APSRefreshVectorAdjust(3 - cavget): $Offsets"
        }
        if [catch {exec cavget -list=[join $pvList ,] -list=:ms:${coord}:SetpointAO -pend=30} Setpoints] {
            return -code error "APSRefreshVectorAdjust(4 - cavget): $Setpoints"
        }
        set Adjust ""
        foreach setpoint $Setpoints offset $Offsets {
            lappend Adjust [expr $setpoint + $offset]
        }
        set Data(Column.DeviceName) [list $pvList]
        set Data(Column.Waveform) [list $Adjust]
        if [catch {sdds save $tmpRoot.${coord}0 Data} result] {
            return -code error "APSRefreshVectorAdjust(5 - sdds save): $result"
        }
        
        APSAddToTmpFileList -ID APSRefreshVectorAdjust -fileList "$tmpRoot.${coord}0 $tmpRoot.all $tmpRoot.data"
        APSAddToTmpFileList -ID APSRefreshVectorAdjust -fileList "$tmpRoot.${coord}1 $tmpRoot.${coord}2 "
        if [catch {exec sddsconvert $waveformDir/$waveformFile \
                     -rename=par,[string  toupper $coord]AdjustWaveformPV=WaveformPV \
                     $tmpRoot.all 
            exec sddswget $tmpRoot.all $tmpRoot.data } result] {
            return -code error "APSRefreshVectorAdjust(6 - sddswget): $result"
        }
        if [catch {exec sddsxref $tmpRoot.all $tmpRoot.${coord}0 $tmpRoot.${coord}1 -match=DeviceName \
                     -nowarnings -take=Waveform 
            exec sddsselect $tmpRoot.data $tmpRoot.${coord}0 $tmpRoot.${coord}2 -invert -match=DeviceName 
            exec sddscombine $tmpRoot.${coord}1 $tmpRoot.${coord}2 -merge -pipe=out | \
                     sddssort -pipe=in -col=Index $tmpRoot.${coord}3} result] {
            return -code error "APSRefreshVectorAdjust(7 - sddsselect5, sddsxref): $result"
        }
        lappend tmpList $tmpRoot.${coord}3
        
    }
    APSAddToTmpFileList -ID APSRefreshVectorAdjust -fileList "$tmpRoot.xy"
    APSAddToTmpFileList -ID APSRefreshVectorAdjust -fileList $tmpList
    if [catch {eval exec sddscombine $tmpList $tmpRoot.xy -overWrite
        exec sddswput $tmpRoot.xy } result] {
        return -code error "APSRefreshVectorAdjust(8 - sddswput): $result"
    }
}

proc APSSRGenerateWaveformFilesForLocalSteering {args} {
    set dataDir ""
    set definitionFile ""
    #this procedure is to generate waveform files for local steering
    APSStrictParseArguments {dataDir definitionFile}
    if {![string length $dataDir] || ![string length $definitionFile] } {
        return -code error "APSSRGenerateWaveformFilesForLocalSteering(1): dataDir or definitionFile is not given!"
    }
    
    set oldDir [pwd]
    cd $dataDir
    if ![file exist $definitionFile] {
        cd $oldDir
        return -code error "APSSRGenerateWaveformFilesForLocalSteering: $definitionFile does not exist!"
    }
    set waveformFileDir /home/helios/oagData/sr/orbitControllaw/waveforms
    if [catch {exec sdds2stream $definitionFile -col=SymbolicName } pvNames] {
        cd $oldDir
        return -code error "APSSRGenerateWaveformFilesForLocalSteering(2): $pvNames"
    }
    
    set pvTypes ""
    if {[string match *P0* $pvNames]} {
        lappend pvTypes p0
    }
    if {[string match {S*[AB]:P[1-5]*} $pvNames]} {
        lappend pvTypes rf
    }
    if [string match *BM* $pvNames] {
        lappend pvTypes bm
    }
    if [string match *ID* $pvNames] {
        lappend pvTypes id
    }
    set planes ""
    if [string match *x* $pvNames] {
        lappend planes x
    }
    if [string match *y* $pvNames] {
        lappend planes y
    }
    set corrFiles ""
    if [string match *H* $pvNames] {
        lappend corrFiles $waveformFileDir/hcorrInfo.sdds
    }
    if [string match *V* $pvNames] {
        lappend corrFiles $waveformFileDir/vcorrInfo.sdds
    }
    if ![llength $corrFiles] {
        cd $oldDir
        return -code error "APSSRGenerateWaveformFilesForLocalSteering(3): no corrector waveform files provided for orbit correction!"
    }
    if [catch {eval exec sddscombine $corrFiles -pipe=out \
                 | sddsprocess -pipe=in corrWaveform \
                 "-reprint=par,WaveformPV,%s,DeltaWaveformPV" } result] {
        cd $oldDir
        return -code error "APSSRGenerateWaveformFilesForLocalSteering(4 - sddscombine): $result"
    }
    set tmpRoot /tmp/[APSTmpString]
    # make a test file with only DP:MpsTripM, the only useful current-related 
    # PV residing in the datapool ioc.
    
    if [catch {exec sddsprocess tests $tmpRoot.test \
                 -match=col,ControlName=*CurrentAO,!,ControlName=*ErrorCC,!,& 
        exec sddsprocess tests $tmpRoot.corrTest -match=col,ControlName=*CurrentAO \
                 "-define=col,Ignore,1" -edit=col,DeviceName,ControlName,%/:CurrentAO//
        exec sddsprocess tests $tmpRoot.bpmTest -match=col,ControlName=*ErrorCC \
                 -define=col,Ignore,1 -edit=col,DeviceName,ControlName,%/:msAve//%/:ErrorCC//  \
             } result] {
        cd $oldDir
        return -code error "APSSRGenerateWaveformFilesForLocalSteering(5 - sddsprocess): $result"
    }
    set fileList ""
    set testFileList ""
    foreach corrFile $corrFiles set {1 2} {
        if [catch {exec sddsxref $corrFile $tmpRoot.corrTest -pipe=out \
                     -match=DeviceName \
                     -take=MaximumValue,MinimumValue,Ignore -fillIn -nowarnings \
                     | sddsprocess -pipe=in $tmpRoot.tests$set \
                     "-redefine=col,Ignore,Ignore 1 - abs,type=short" \
                     "-reprint=par,WaveformPV,%s,SetpointWaveformPV" \
                 } result] {
            cd $oldDir
            return -code error "APSSRGenerateWaveformFilesForLocalSteering(6 - sddsxref): $result"
        }
        lappend testFileList $tmpRoot.tests$set
    }
    
    foreach plane $planes {
        foreach type $pvTypes {
            if {[string compare $type bm]==0} {
                if [string compare $plane x]==0 continue
                set waveformFile $waveformFileDir/bmBpmInfo.sdds
            } else {
                set waveformFile $waveformFileDir/${type}[string toupper $plane]BpmInfo.sdds
            }
            if [catch {exec sddsprocess $waveformFile $tmpRoot.$type.$plane \
                         -reedit=col,DeviceName,ei/:$plane/
                exec sddsxref $tmpRoot.$type.$plane $tmpRoot.bpmTest -pipe=out \
                         -match=DeviceName -nowarnings -fillIn \
                         -take=MinimumValue,MaximumValue,Ignore \
                         | sddsprocess -pipe=in $tmpRoot.$type.$plane.test \
                         "-redefine=col,Ignore,Ignore 1 - abs,type=short" } result] {
                cd $oldDir
                return -code error "APSSRGenerateWaveformFilesForLocalSteering(7 - sddsxref): $result"
            }
            lappend fileList $tmpRoot.$type.$plane
            lappend testFileList $tmpRoot.$type.$plane.test
        }
    }
    APSAddToTmpFileList -ID APSSRGenerateWaveformFile -fileList $fileList
    APSAddToTmpFileList -ID APSSRGenerateWaveformFile -fileList $testFileList
    if [catch {eval exec sddscombine $fileList bpmWaveform -overwrite } result] {
        cd $oldDir
        return -code error "APSSRGenerateWaveformFile(8 - sddscombine): $result"
    }
    if [catch {eval exec sddscombine $testFileList waveformTest -overwrite } result] {
        cd $oldDir
        return -code error "APSSRGenerateWaveformFile(9 - sddscombine): $result"
    }
}

proc APSSRStartLocalSteeringInIOC {args} {
    set interval 1.5
    set average 1
    set gain 0.05
    set controllawDir ""
    set timeLimit 1
    set definition dynamic.defs
    set runControlPV DP:S:SteeringSDDS
    set infiniteLoop 0
    APSStrictParseArguments {interval average gain timeLimit \
                           controllawDir definition runControlPV infiniteLoop}
    
    if ![string length $controllawDir] {
        return -code error "APSSRStartLocalSteeringInIOC(1): Controllaw dir is not provided!"
    }
    set generate 0
    if {![file exist $controllawDir/irm] || ![file exist $controllawDir/dynamic.defs] \
          || ![file exist $controllawDir/plain.defs] || ![file exist $controllawDir/tests] } {
        return -code error "APSSRStartLocalSteeringInIOC(2): Controllaw matrices files do not exist!"
    }
    set refTime [file mtime $controllawDir/irm]
    if {![file exist $controllawDir/corrWaveform] || ![file exist $controllawDir/bpmWaveform] || \
          ![file exist $controllawDir/waveformTest] } {
        set generate 1
    } else {
        set time [clock seconds]
        foreach file {corrWaveform bpmWaveform waveformTest} {
            set ftime [file mtime $controllawDir/$file] 
            if {$ftime<$time} {
                set time $ftime
            }
        }
        if {$time<$refTime} {
            set generate 1
        }
    }
    if $generate {
        if [catch {APSSRGenerateWaveformFilesForLocalSteering -dataDir $controllawDir \
                     -definitionFile $definition} result] {
            return  -code error "APSSRStartLocalSteeringInIOC(3 - APSSRGenerateWaveformFilesForLocalSteering): Error in generating waveform files: $result"
        }
    }
    #removed -searchPath option since it did not work properly
    set options "$controllawDir/irm -waveforms=$controllawDir/bpmWaveform,readback -proportional"
    append options " -waveform=$controllawDir/corrWaveform,actuator -waveforms=$controllawDir/waveformTest,test"
    append options " -testValues=/home/helios/oagData/sr/localSteering/configs/datapool.test"
    if $infiniteLoop {
        append options " -infiniteLoop"
    } else {
        append options " -steps=[expr int( $timeLimit / $interval)]"
    }
    append options " -gain=PVname=${runControlPV}.GAIN -interval=PVname=${runControlPV}.INTR"
    append options " -average=PVname=${runControlPV}.AVER -launcherpv=${runControlPV}"
    append options " -deltaLimit=value=0.5 -controlQuantityDefinition=$controllawDir/$definition"
    if [exec cavget -list=${runControlPV}.RUN -pend=30] {
        return -code error "APSSRStartLocalSteeringInIOC(4 - cavget): Datapool ioc run control ${runControlPV} is running. Cannot start datapool ioc local steering!"
    }
    if [exec cavget -list=DP:S:OrbitControlLawYSDDS.RUN -pend=30] {
        return -code error "APSSRStartLocalSteeringInIOC(5 -cavget): Datapool ioc run control DP:S:OrbitControlLawYSDDS is running. Cannot start datapool ioc local steering!"
    }
    #set the gain, interval and averages
    if [catch {exec cavput -list=${runControlPV}.GAIN=$gain,${runControlPV}.INTR=$interval,${runControlPV}.AVER=1 -pend=30} result] {
        return -code error "APSSRStartLocalSteeringInIOC(6 - cavput): $result!"
    }
    
    if [catch {APScaputTextToWaveform -pvName ${runControlPV}.OPTN -text $options} result] {
        APSAlertBox .alert -errorMessage "Can not set controllaw options: $result"
        return -code error "APSSRStartLocalSteeringInIOC(7 - APScaputTextToWaveform): Can not set controllaw options: $result"
    }
    #start ioc controllaw
    if [catch {exec cavget -list=${runControlPV}.SUSP -pend=30 -num} suspend] {
         return -code error "APSSRStartLocalSteeringInIOC(7a - cavget): Can not read ${runControlPV}.SUSP: $suspend"
    }
    if $suspend {
        if [catch {exec cavput -list=${runControlPV}.SUSP=0 -pend=30} result] {
            return -code error "APSSRStartLocalSteeringInIOC(7b - cavput): Can not resume ${runControlPV} controllaw: $result"
        }
    }
    if [catch {exec cavput -list=${runControlPV}.CMND=1 -pend=30} result] {
        return -code error "APSSRStartLocalSteeringInIOC(8 - cavput): Can not start controllaw in IOC: $result"
    }
    return 0
}

# sets corrector source mode
proc APSSetCorrMode {args} {
    set plane ""
    set sourceMode ""
    set corrMode ""
    set unified 0
    #sourceMode is either Operation or Maintenance
    #corrMode is either scalar or vector
    APSStrictParseArguments {plane sourceMode corrMode unified}
    if ![string length $plane] {
        return -code error "APSSetCorrMode: plane is not given!"
    }
    set plane [string tolower $plane]
    switch $plane {
        h {
            set planes H
            set coords X
        }
        v {
            set planes V
            set coords Y
        }
        both {
            set planes {H V}
            set coords {X Y}
        }
        default {
            return -code error "APSSetCorrMode(1): Invalid plane given!"
        }
    }
    set source 0
    set type 0
    if {[string length $sourceMode]} {
        if {[string compare $sourceMode Operation]!=0 && [string compare $sourceMode Maintenance]!=0} {
            return -code error "APSSetCorrMode(2): Unknown corrector source mode provided!"
        }
        set source 1
    }
    set corrMode [string tolower $corrMode]
    if [string length $corrMode] {
        if {[string compare $corrMode scalar]!=0 && [string compare $corrMode vector]!=0} {
            return -code error "APSSetCorrMode(3): Unknown corrector mode (should be scalar or vector) provided!"
        }
        set type 1
    }
    if $source {
        foreach plane $planes {
            if [catch {exec cavput -list=S -range=beg=1,end=40 \
                         -list=A:${plane}1,A:${plane}2,A:${plane}4,B:${plane}4,B:${plane}3,B:${plane}2,B:${plane}1 \
                         -list=:ControlSrcBO=$sourceMode -pend=30} result] {
                return -code error "APSSetCorrMode(4 - cavput): $result"
            }
            # add CU magnet
            set CUdataDir /home/helios/oagData/sr/XAXSTF
            if [catch {exec sddsprocess $CUdataDir/sectors.sdds -pipe=out \
                         -filter=col,CUflag,1,1 \
                         | sdds2stream -pipe -col=Sector} CUlist] {
                return -code error "APSSetCorrMode(5 - sddsprocess): $CUlist"
            }
            if [catch {exec cavput -list=S -list=[join $CUlist ,] \
                         -list=C:${plane}1 \
                         -list=:ControlSrcBO=$sourceMode -pend=30} result] {
                return -code error "APSSetCorrMode(6 - cavput): $result"
            }
        }
    } 
    if $type {
        switch $corrMode {
            vector {
                set vector 1
                set enable 1
                set enableWrite 1
            }
            scalar {
                set vector 0
                set enable 0
                set enableWrite 0
            }
            default {
                return -code error "APSSetCorrMode(7):Unknow corrMode provided!"
            }
        }
        foreach plane $planes Coord $coords {
            if !$unified { 
                # unified is 0 for normal operations
                if [catch {exec cavput -list=DP:EnableWrite${Coord}bpmSetPt2RMC=$enable -pend=30} result] {
                    return -code error "APSSetCorrMode(8 - cavput change): $result"
                }
                if [catch {exec cavput -list=DP:${Coord}bpmSetPtVectorModeC.VAL=$vector -pend=30} result] {
                    return -code error "APSSetCorrMode(9 - cavput change): $result"
                }
            } else { 
                # in unified mode, bpm setpoints are not transmitted from datapool
                if [catch {exec cavput -list=DP:EnableWrite${Coord}bpmSetPt2RMC=0 -pend=30} result] {
                    return -code error "APSSetCorrMode(8 - cavput change): $result"
                }
                if [catch {exec cavput -list=DP:${Coord}bpmSetPtVectorModeC.VAL=0 -pend=30} result] {
                    return -code error "APSSetCorrMode(9 - cavput change): $result"
                }
            }
            if [catch {exec cavput -list=DP:EnableWrite${plane}Cor2RMC=$enable -pend=30} result] {
                return -code error "APSSetCorrMode(10 -cavput change): $result"
            }
            if $enableWrite {
                if [catch {exec cavput -list=DP:Reload${plane}Cor2RMC=1 -pend=30} result] {
                    return -code error "APSSetCorrMode(11 -cavput change): $result"
                }
            }
            if [catch {exec cavput -list=DP:${plane}CorrVectorModeC.VAL=$vector -pend=30} result] {
                return -code error "APSSetCorrMode(12 - cavput change): $result"
            }
            if $enableWrite {
                if [catch {exec cavput -list=DP:Write${plane}Cor2RMC=1 -pend=30} result] {
                    return -code error "APSSetCorrMode(13 - cavput change): $result"
                }
            }
        }
    }
    return
}


proc APSGenerateControllawFiles {args} {
    # Takes the irm file in the specified directory and creates a test
    # files, a inUse file, an average enable file and a control
    # quantity definitions file. For the tests file some limits must
    # be passed.  The variables coord are set in the setCorrPlane
    # procedure and they are frozen during the call to
    # StartControllaw.

    #if workstation is set to 1, then corrector mode is dynamic and bpm is plain
    #otherwise, their types are determined by the config file
    set dataDir /home/helios/oagData/sr/orbitControllaw/lattices/default
    set directory ""
    set PVSuffix ms
    set BPMLimit 10
    set XBPMLimit 0.5
# These two are no longer used
    set corrLimit 148
    set CUCorrLimit 3.5
    set rangeErrorLimit 300
    set S35DCCTLimitH 0.5
    set S35DCCTLimitV 15
    set regenerateFiles 0
    set workstation 0
    set unified 0
    APSStrictParseArguments {regenerateFiles dataDir directory PVSuffix BPMLimit XBPMLimit \
                        corrLimit CUCorrLimit  rangeErrorLimit S35DCCTLimitH S35DCCTLimitV \
                         workstation unified}
    
    set testFileList ""
    if ![string length $directory] {
        APSSound -type alert
        return -code error "APSGenerateControllawFiles(1): Select a directory for the response file."
    }
    if ![regexp {([^.]*).} $directory match plane] {
        return  -code error "APSGenerateControllawFiles(2): Can't determine plane from directory name."
    }
    
    switch $plane {
        h {
            set Plane H
            set coord x
            set Coord X
            set corrStatusFile /home/helios/oagData/sr/HCorrectorStatus/config.sdds
        }
        v {
            set Plane V
            set coord y
            set Coord Y
            set corrStatusFile /home/helios/oagData/sr/VCorrectorStatus/config.sdds
        }
        default {
            return -code error "APSGenerateControllawFiles(3): The input directory is not h.* or v.*!"
        }
    }
    
    set oldDir [pwd]
    if ![file exists ${dataDir}/$directory] {
        return -code error "APSGenerateControllawFiles(4): Can't find directory ${dataDir}/$directory"
    }
    cd $dataDir/$directory

    if ![file exists irm] {
        cd $oldDir
        return -code error "APSGenerateControllawFiles(5): File $directory/irm doesn't exist." 
    }
    if ![file exists config] {
        cd $oldDir
        return -code error "APSGenerateControllawFiles(6): File $directory/config doesn't exist." 
    }
    
    if $regenerateFiles {
        file delete definitions.ms definitions.msAve definitions.mswAve \
          tests enables inUse despike
        file delete bpmWaveform waveformTest corrWaveform ffWaveform \
          DPHtests DPVtests
    }
    
    set names [APSGetSDDSParameter -fileName config -parameter NameType]
    set types [APSGetSDDSParameter -fileName config -parameter PVType]
    set i [lsearch -exact $names MonitorNames]
    set j [lsearch -exact $names CorrectorNames]
    if {$i==-1 || $j==-1} {
        cd $oldDir
        return -code error "APSGenerateControlLawFiles(7): NameType paremeter in config file is not correct!"
    }
    set bpmType [lindex $types $i]
    set corrType [lindex $types $j]
    #corrType accepts dynamic and scalar as scalar mode, vector and DP as vector mode
    #bpmType accepts both DP and vector as vector mode
    if [string compare $corrType scalar]==0 {
        set corrType dynamic
    }
    if [string compare $corrType vector]==0 {
        set corrType DP
    }
    if [string compare $bpmType vector]==0 {
        set bpmType DP
    }
    if $workstation {
        if { [string compare $corrType DP]==0 || [string compare bpmType DP]==0 } {
            set regenerateFiles 1
            file delete definitions.ms definitions.msAve definitions.mswAve tests enables inUse despike
        }
        set bpmType plain
        set corrType dynamic
    }
    #create test files for -testValues of sddscontrollaw if pvTest=1
    if {$bpmType=="DP" || $corrType=="DP"} {
        set datapool 1
    } else {
        set datapool 0
    }
    if [catch {APSCreateDPTestFiles -plane $plane -datapool $datapool -unified $unified} result] {
        cd $oldDir
        return -code error "APSGenerateControllawFiles(8 - APSCreateDPTestFiles): $result"
    }
    if {[file exists definitions.ms] && \
          [file exists definitions.msAve] && \
          [file exists definitions.mswAve] && \
          [file exists tests] && \
          [file exists enables] && [file exists inUse] && \
          [file exists despike]} {
        set exist 1
        if {[string compare $bpmType DP]==0} {
            if {![file exists bpmWaveform] || ![file exist waveformTest]} {
                set exist 0
            }
        }
        if {[string compare $corrType DP]==0} {
            if {![file exists corrWaveform] || ![file exist waveformTest]} {
                set exist 0
            }
        }
        if $exist {
            cd $oldDir
            return "Using existing controllaw files."
        }
    }
    
    
    ########################
    # create definitions files for various bpm pv types
    ########################
    if [catch {exec sddsprocess config -pipe=out \
                 -match=para,NameType=CorrectorNames \
                 -filter=col,Flag,1,1 \
                 | sddsconvert -pipe \
                 -delete=col,Flag -rename=col,Name=SymbolicName \
                 | sddsprocess -pipe=in corr.names \
                 -edit=col,ControlName,SymbolicName,ei/:CurrentAO/ \
             } result ] {
        cd $oldDir
        return -code error "APSGenerateControllawFiles(9 - sddsprocess): $result"
    }
    APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList corr.names

    #create dynamic correctors
    if [catch {exec sddsprocess corr.names corr.dynamic \
                 -reedit=col,ControlName,ai/SFB:/ \
             } result ] {
        cd $oldDir
        return -code error "APSGenerateControllawFiles(10 - sddsprocess): $result"
    }
    #create dynamic correctors
    if [catch {exec sddsprocess corr.names corr.range \
                 -reedit=col,ControlName,%/CurrentAO/RangeErrorCALC/ \
             } result ] {
        cd $oldDir
        return -code error "APSGenerateControllawFiles(11 - sddsprocess): $result"
    }
    APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList "corr.dynamic corr.range"

    if {[string compare $corrType dynamic]==0} {
        set corrFile corr.dynamic
    } else {
        set corrFile corr.names
    }
    foreach PVSuffix {ms msAve mswAve} {
        if [catch {exec sddsprocess config -pipe=out \
                     -match=para,NameType=MonitorNames \
                     -filter=col,Flag,1,1 \
                     | sddsconvert -pipe \
                     -delete=col,Flag -rename=col,Name=SymbolicName \
                     | sddsprocess -pipe=in bpm.names.$PVSuffix \
                     -edit=col,ControlName,SymbolicName,ei/:${PVSuffix}:${coord}:ErrorCC/  \
                 } result ] {
            APSDeleteTmpFileList -ID APSGenerateControllawFiles
            cd $oldDir 
            return -code error "APSGenerateControllawFiles(12 -sddsprocess): $result"
        }
        APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList bpm.names.$PVSuffix
        
        #create definition files
        
        if [catch {exec sddscombine -merge -overWrite \
                     $corrFile bpm.names.$PVSuffix definitions.$PVSuffix \
                 } result ] {
            APSDeleteTmpFileList -ID APSGenerateControllawFiles
            cd $oldDir
            return -code error "APSGenerateControllawFiles(13  sddscombine): $result"
        }
    }
    
    
    #################################
    # create tests file from config file
    #################################
    # this first file contains just two PVs: S35DCCT and the injection status bit.
    # the second file sets the limits of the correctors.
    # the third file sets the limits of the bpms.
    # the fourth file sets the status of the bpms.
    # create waveform test file if waveform exists
    set waveList ""
    if $datapool {
	set testsfile /home/helios/oagData/sr/orbitControllaw/tests/basicTemplateDP.tests 
    } else {
	set testsfile /home/helios/oagData/sr/orbitControllaw/tests/basicTemplate.tests 
    }
    if [catch {exec replaceText $testsfile \
                 S35DCCT.tests1 \
                 -original=<S35DCCTLimit> -replace=[set S35DCCTLimit${Plane}] \
             } result] {
        cd $oldDir
        return -code error "APSGenerateControllawFiles(14 - replace): $result"
    }
    if [catch {exec sddsprocess S35DCCT.tests1 -pipe=out \
                 -match=col,ControlName=S35DCCT* \
                 | sddsprocess -pipe=in S35DCCT.tests2 \
                 -print=col,Monitor,y,type=character
        exec sddsprocess S35DCCT.tests1 -pipe=out \
                 -match=col,ControlName=S35DCCT*,! \
                 | sddsprocess -pipe=in S35DCCT.tests3 \
                 -print=col,Monitor,y,type=character
        exec sddscombine S35DCCT.tests2 S35DCCT.tests3 S35DCCT.tests -merge \
                 -overwrite} result] {
        cd $oldDir
        return -code error "APSGenerateControllawFiles(15 - sddsprocess, sddscombine): $result"
    }
    eval file delete -force "S35DCCT.tests1 S35DCCT.tests2 S35DCCT.tests3"
    lappend testFileList S35DCCT.tests
    APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList S35DCCT.tests
    #tests for run control status of DP offset-transfer
    if {[string compare $bpmType DP]==0} {
        APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList DPcontrollaw.tests
        if [catch {exec sddsmakedataset DPcontrollaw.tests \
                     -col=ControlName,type=string -data=SR:CorrectorFFTransferRC.RUN \
                     -col=MinimumValue,type=double -data=0.5 \
                     -col=MaximumValue,type=double -data=1.5 \
                     -col=Despike,type=long -data=0 \
                     -col=SymbolicName,type=string -data= \
                     -col=Monitor,type=character -data=y -nowarnings } result] {
            cd $oldDir
            return -code error "APSGenerateControllawFiles(16 - sddsmakedataest): $result"
        }
        #lappend testFileList DPcontrollaw.tests
    }
    # corrector limits
    set wfDir /home/helios/oagData/sr/orbitControllaw/waveforms
    if {[string compare $corrType DP]==0} {
        #create corrector waveform and waveform test file
        APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList "corrWaveform~ ffWaveform~"
        # file copy unfortunately makes the target also a link.
        # file copy -force $wfDir/${plane}corrInfo.sdds corrWaveform
        exec cp $wfDir/${plane}corrInfo.sdds corrWaveform
        if [catch {exec sddsconvert corrWaveform -rename=par,DeltaWaveformPV=WaveformPV \
                     -nowarning} result] {
            cd $oldDir
            return -code error "APSGenerateControllawFiles(17 - sddsconvert): $result"
        }
        # file copy unfortunately makes the target also a link.
        #file copy -force $wfDir/RTFB${Plane}bpmInfo.sdds ffWaveform
        exec cp $wfDir/RTFB${Plane}bpmInfo.sdds ffWaveform
        if [catch {APSCreateWaveformFiles -configFile corr.names -type corr -plane $plane \
                     -testFile corrWave.test -corrStatusFile $corrStatusFile }  result] {
            APSDeleteTmpFileList -ID APSGenerateControllawFiles
            cd $oldDir
            return -code error "APSGenerateControllawFiles(18 - APSCreateWaveformFiles): $result"
        }
        lappend waveList corrWave.test
        APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList "corrWave.test"
        #following is to add corr.rang tests to tests file
        APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList "corr.range.tests1  corr.range.tests2  corr.range.tests3"
        if [catch {eval exec  sddsconvert corr.range -pipe=out \
                     -retain=col,ControlName \
                     | sddsprocess -pipe=in corr.range.tests1 \
                     -edit=col,LowControlName,ControlName,ei/.LOLO/ \
                     -edit=col,HighControlName,ControlName,ei/.HIHI/ \
                     -print=col,LowValueString,-${rangeErrorLimit} \
                     -print=col,HighValueString,${rangeErrorLimit} } result] {
            APSDeleteTmpFileList -ID APSGenerateControllawFiles
            cd $oldDir
            return -code error "APSGenerateControllawFiles(19 - sddsconvert): $result"
        }
        if [catch {exec sddsconvert corr.range.tests1 corr.range.tests2 \
                     -retain=col,Low* -edit=col,Low*,%/Low//
            exec sddsconvert corr.range.tests1 corr.range.tests3 \
                     -retain=col,High* -edit=col,High*,%/High// 
            exec sddscombine corr.range.tests2 corr.range.tests3 \
                     -merge corrRangeError.tests -overwrite} result] {
            APSDeleteTmpFileList -ID APSGenerateControllawFiles
            cd $oldDir
            return -code error "APSGenerateControllawFiles(20 - sddscombine): $result"
        }
        eval file delete -force "corr.range.tests1 corr.range.tests2 corr.range.tests1 corr.range.tests3"
        if [catch {eval exec  sddsconvert corr.range -pipe=out \
                     -retain=col,ControlName \
                     | sddsprocess -pipe=in corr.range.tests \
                     -reedit=col,ControlName,ei/.SEVR/ \
                     -define=col,MinimumValue,0 \
                     -define=col,MaximumValue,1 \
                     -print=col,Monitor,y,type=character } result] {
            APSDeleteTmpFileList -ID APSGenerateControllawFiles
            cd $oldDir
            return -code error "APSGenerateControllawFiles(21 - sddsconvert): $result"
        }
        lappend testFileList corr.range.tests
        APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList corr.range.tests
    } else {
        #corrType=dynamic, plain 
        APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList "corr.abs.tests corr.abs.tests1 corr.abs.tests2 corr.range.tests"
        if [catch {exec sddsconvert $corrFile -pipe=out \
                     -retain=col,ControlName \
                     | sddsprocess -pipe -edit=col,DeviceName,ControlName,%/:CurrentAO//%/SFB:// \
                     | sddsxref -pipe $corrStatusFile -match=DeviceName -take=CorrectorLimit \
                     | sddsprocess -pipe=in corr.abs.tests -print=col,Monitor,n,type=character \
                     "-define=col,MinimumValue,-1  CorrectorLimit *" \
                     "-define=col,MaximumValue,CorrectorLimit"
            # testing error range only works for plain and dynamic
            # correctors corr.range
            exec sddsconvert corr.range -pipe=out \
                     -retain=col,ControlName \
                     | sddsprocess -pipe=in corr.range.tests \
                     -define=col,MinimumValue,-$rangeErrorLimit \
                     -define=col,MaximumValue,$rangeErrorLimit \
                     -print=col,Monitor,n,type=character
            exec sddscombine corr.abs.tests corr.range.tests corr.tests -overWrite \
                 } result] {
            APSDeleteTmpFileList -ID APSGenerateControllawFiles
            cd $oldDir
            return -code error "APSGenerateControllawFiles(22 - sddsconvert): $result"
        }
        lappend testFileList corr.tests
        APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList corr.tests
    }
    

    # bpm limits using msAve PVs
    if [catch {exec sddsprocess config -pipe=out \
                 -match=para,NameType=MonitorNames \
                 -filter=col,Flag,1,1 \
                 | sddsconvert -pipe \
                 -delete=col,Flag -rename=col,Name=SymbolicName \
                 | sddsprocess -pipe=in bpm.names \
                 -edit=col,ControlName,SymbolicName,ei/:msAve:${coord}:ErrorCC/  \
             } result ] {
        APSDeleteTmpFileList -ID APSGenerateControllawFiles
        cd $oldDir
        return -code error "APSGenerateControllawFiles(23 - sddsprocess): $result"
    }
    APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList bpm.names

    if {[string compare $bpmType DP]==0} {
        #create bpm waveform and waveform test file
        if [catch {APSCreateWaveformFiles -configFile bpm.names -type bpm \
                     -testFile bpmWave.test -BPMLimit $BPMLimit -plane $plane \
                     -XBPMLimit $XBPMLimit -corrStatusFile $corrStatusFile }  result] {
            
            APSDeleteTmpFileList -ID APSGenerateControllawFiles 
            cd $oldDir
            return -code error "APSGenerateControllawFiles(24 - APSCreateWaveformFiles): $result"
        }
        lappend waveList bpmWave.test
        APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList bpmWave.test
    } else {
        #for plain RF bpms
        APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList "bpm.tests bpm.tests~"
        if [catch {exec sddsconvert bpm.names -pipe=out \
                     -retain=col,ControlName \
                     | sddsprocess -pipe=in bpm.tests -noWarning \
                     -match=col,ControlName=S*ID:P?:*,!,ControlName=S*BM:P?:*,!,& \
                     -define=col,MinimumValue,-${BPMLimit} \
                     -define=col,MaximumValue,${BPMLimit} 
            exec sddsprocess bpm.tests -ifnot=col,Despike -noWarning \
                     -define=col,Despike,1,type=long \
                     -print=col,Monitor,n,type=character \
                 } result] {
            APSDeleteTmpFileList -ID APSGenerateControllawFiles  
            cd $oldDir
            return -code error "APSGenerateControllawFiles(25 - sddsprocess): $result"
        }
        lappend testFileList bpm.tests
    }

    #create waveform test file
    if {[llength $waveList]>0} {
        if [catch {eval exec sddscombine $waveList waveformTest -overWrite} result ] {
            cd $oldDir
            return -code error "APSGenerateControllawFiles(26 - sddscombine): $result"
        }
    }

    # Xray bpms. no despiking of Xray bpms.
    if [catch {exec sddsconvert bpm.names -pipe=out \
                 -retain=col,ControlName,SymbolicName \
                 | sddsprocess -pipe=in IDXbpm.tests -noWarning \
                 -match=col,ControlName=S*ID:P?:* \
                 -define=col,MinimumValue,-${XBPMLimit} \
                 -define=col,MaximumValue,${XBPMLimit} \
             } result] {
        APSDeleteTmpFileList -ID APSGenerateControllawFiles
        cd $oldDir
        return -code error "APSGenerateControllawFiles(27 - sddsconvert): $result"
    }
    if [catch {exec sddsconvert bpm.names -pipe=out \
                 -retain=col,ControlName,SymbolicName \
                 | sddsprocess -pipe=in BMXbpm.tests -noWarning \
                 -match=col,ControlName=S*BM:P?:* \
                 -define=col,MinimumValue,-${XBPMLimit} \
                 -define=col,MaximumValue,${XBPMLimit} \
             } result] {
        APSDeleteTmpFileList -ID APSGenerateControllawFiles
        cd $oldDir
        return -code error "APSGenerateControllawFiles(28 - sddsconvert): $result"
    }
    
    APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList "IDXbpm.tests BMXbpm.tests"
    set IDXrayBPMs [exec sdds2stream IDXbpm.tests -rows=bare -pipe ]
    if $IDXrayBPMs {
        #incude S:ID:GapFeedForwardRC.RUN into tests file
        #lappend testFileList /home/helios/oagData/sr/orbitControllaw/tests/gapFF.tests
       
        # make test for shutter closure only if P2 xray bpm exists
        APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList IDPS1Shutter.sdds 
        if [catch {exec sddsprocess /home/helios/oagData/sr/IDs/sectors.sdds -nowarnings \
                     IDPS1Shutter.sdds -filter=col,IgnorePS1Shutter,-0.5,0.5 } result] {
            return -code error "APSGenerateControllawFiles(30 - sddsprocess): $result"
        }
        set rows [exec sdds2stream -rows=bar IDPS1Shutter.sdds]
        if $rows {
            APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList IDXbpm.test.p2
            if [catch {exec sddsprocess IDXbpm.tests IDXbpm.test.p2 -noWarning \
                         -match=col,ControlName=S*ID:P2:* \
                         -scan=col,Sector,ControlName,S%ld,type=long \
                         -print=col,SectorString,%02ld,Sector } result] {
                return -code error "APSGenerateControllawFiles(31 - sddsprocess): $result"
            }
            set rows1 [exec sdds2stream IDXbpm.test.p2 -rows=bar]
            if $rows1 {
                if [catch {exec sddsselect IDXbpm.test.p2 \
                             IDPS1Shutter.sdds -pipe=out \
                             -equate=Sector \
                             | sddsconvert -pipe -dele=col,ControlName \
                             | sddsprocess -pipe -noWarning \
                             -print=col,ControlName,EPS:%s:ID:PS1:POS:CL,SectorString \
                             -redefine=col,MinimumValue,-0.5 \
                             -redefine=col,MaximumValue,0.5 \
                             -print=col,Monitor,y,type=character \
                             | sddsconvert -pipe=in IDXbpmShutter.tests \
                             -retain=col,ControlName,MinimumValue,MaximumValue,Despike,Monitor \
                         } result] {
                    APSDeleteTmpFileList -ID APSGenerateControllawFiles
                    cd $oldDir
                    return -code error "APSGenerateControllawFiles(32 - sddsselect): $result"
                }
                lappend testFileList IDXbpmShutter.tests
            }
        }
        if {[string compare $bpmType plain]==0} {
            if [catch {exec sddsprocess IDXbpm.tests -print=col,Monitor,n,type=character -nowarnings} result] {
                APSDeleteTmpFileList -ID APSGenerateControllawFiles
                cd $oldDir
                return -code error "APSGenerateControllawFiles(33 - sddsprocess): $result"
            }
            lappend testFileList IDXbpm.tests
        }
    }
    APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList "IDXbpmShutter.tests IDXbpm.tests~"
    APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList "BMXbpm.tests BMXbpm.tests~ BMShutter.tests BMXbpm+Shutter.tests"
    
    set BMXrayBPMs [exec sdds2stream BMXbpm.tests -rows=bare -pipe]

    if $BMXrayBPMs {
        # test shutters if and only if P2 BPM is being *used*
        if [catch {exec sddsprocess BMXbpm.tests -ifnot=col,Despike -noWarning \
                     -define=col,Despike,0,type=long
            exec sddsprocess BMXbpm.tests -pipe=out -noWarning \
                     -match=column,SymbolicName=S*BM:P2 \
                     -scan=col,Sector,SymbolicName,S%ld,type=long \
                     | sddssort -pipe -col=Sector -unique \
                     | sddsprocess -pipe -nowarning \
                     -reprint=col,ControlName,EPS:%02ld:BM:PS1:POS:CL,Sector \
                     -redefine=col,MinimumValue,-0.5 \
                     -redefine=col,MaximumValue,0.5 \
                     -print=col,Monitor,y,type=character \
                     | sddsconvert -pipe=in BMShutter.tests \
                     -retain=col,ControlName,MinimumValue,MaximumValue,Despike,Monitor \
                 } result] {
            APSDeleteTmpFileList -ID APSGenerateControllawFiles
            cd $oldDir
            return -code error "APSGenerateControllawFiles(34 - sddsprocess): $result"
        }
        lappend testFileList BMShutter.tests
        if {[string compare $bpmType plain]==0} {
            if [catch {exec sddsprocess BMXbpm.tests \
                         -print=col,Monitor,n,type=character -nowarnings} result] {
                APSDeleteTmpFileList -ID APSGenerateControllawFiles
                cd $oldDir
                return -code error "APSGenerateControllawFiles(35 -sddsprocess): $result"
            }
            lappend testFileList BMXbpm.tests
        }
    }

    # bpm despike file. 
    if [catch {exec sddscombine bpm.names -pipe=out -merge \
                 | sddssort -pipe=in despike -unique -col=ControlName \
             } result] {
        APSDeleteTmpFileList -ID APSGenerateControllawFiles
        cd $oldDir
        return -code error "APSGenerateControllawFiles(36 - sddsoombine): $result"
    }
    # regular bpm status
    # meaning of status values: invalid 0 valid 1 olddata 2
    
    if {[catch {exec sddsconvert bpm.names -pipe=out \
                  -retain=col,SymbolicName \
                  | sddsprocess -pipe -noWarning \
                  -edit=col,ControlName,SymbolicName,ei/:ms.${Coord}AVS/ \
                  -define=col,MinimumValue,0.5 \
                  -define=col,MaximumValue,1.5 \
                  -print=col,Monitor,y,type=character \
                  | sddsconvert -pipe=in bpmStatus.tests \
                  -dele=col,BPMName \
              } result ]} {
        APSDeleteTmpFileList -ID APSGenerateControllawFiles
        cd $oldDir
        return -code error "APSGenerateControllawFiles(37 - sddsconvert): $result"
    }
    lappend testFileList bpmStatus.tests
    APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList bpmStatus.tests
    # test the ID gaps for the ID bpms
    catch {file delete IDbpm.names IDgap.tests}
    set rows [exec sdds2stream -rows=bar /home/helios/oagData/sr/orbitControllaw/tests/IDGapLimits.tests]
    if $rows {
        if [catch {exec sddsprocess config -pipe=out -noWarning \
                     -match=para,NameType=MonitorNames \
                     -match=col,Name=S*ID:P? \
                     -filter=col,Flag,1,1 \
                     | sddsconvert -pipe -noWarning  \
                     -delete=col,Flag -rename=col,Name=SymbolicName \
                     | sddsprocess -pipe -noWarning \
                     -edit=col,ControlName,SymbolicName,ei/:msAve:${coord}:ErrorCC/  \
                     | tee IDbpm.names \
                     | sddsprocess -pipe -noWarning \
                     -scan=col,IDNumber,SymbolicName,S%ld,type=long \
                     -reprint=col,ControlName,ID%02ld:Gap,IDNumber \
                     | sddssort -pipe -noWarning -col=IDNumber -unique \
                     | sddsxref -pipe /home/helios/oagData/sr/orbitControllaw/tests/IDGapLimits.tests \
                     -take=MinimumValue,MaximumValue -noWarnings \
                     -equate=IDNumber=Sector \
                     | sddsprocess -pipe -print=col,Monitor,y,type=character -nowarnings \
                     | sddsconvert -pipe=in IDgap.tests \
                     -noWarnings -retain=col,ControlName,MinimumValue,MaximumValue,Monitor \
                 } result ] {
            APSDeleteTmpFileList -ID APSGenerateControllawFiles
            cd $oldDir
            return -code error "APSGenerateControllawFiles(38 - sddsxref): $result"
        }
        lappend testFileList IDgap.tests
        APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList "IDbpm.names IDgap.tests"
    }
    # combine the partial tests files
    if [catch {eval exec sddscombine -merge -overWrite \
                 $testFileList  tests \
             } result ] {
        APSDeleteTmpFileList -ID APSGenerateControllawFiles
        cd $oldDir
        return -code error "APSGenerateControllawFiles(39 - sddscombine): $result"
    } 

    set bpmStatusFile /home/helios/oagData/sr/BPMStatus/config.sdds
    ########################
    # create burt file for enabling averaging
    ########################
    file delete -force -- enables
    #remove the FPGA bpms from average enables
    set nonFPGAFile /tmp/[APSTmpString].nonFPGA 
    if [catch {exec sddsprocess $bpmStatusFile  $nonFPGAFile "-match=col,ElectronicsType=FPGA,!" } result] {
        APSDeleteTmpFileList -ID APSGenerateControllawFiles
        cd $oldDir
        return -code error "APSGenerateControllawFiles(39a - sddsprocess): $result"
    }
    
    if [catch {exec sddsconvert bpm.names -pipe=out \
                 -retain=col,SymbolicName \
                 | sddsselect -pipe $nonFPGAFile -match=SymbolicName=DeviceName \
                 | sddsprocess -pipe -noWarning \
                 -edit=col,ControlName,SymbolicName,ei/:msAve:AveEnbBO/ \
                 -print=col,ControlType,pv \
                 -print=col,Lineage,- \
                 -define=col,Count,1,type=long \
                 -print=col,ValueString,Enable \
                 -print=para,SnapType,Absolute \
                 | sddsconvert -pipe=in enables \
                 -dele=col,SymbolicName \
             } result ] {
        APSDeleteTmpFileList -ID APSGenerateControllawFiles
        cd $oldDir
        return -code error "APSGenerateControllawFiles(40 - sddsconvert): $result"
    }
    ########################
    # create a burt file for setting InUseBO PV
    ########################
    if [catch {exec sddsprocess config -pipe=out -noWarning \
                 -match=para,NameType=MonitorNames \
                 -edit=col,ControlName,Name,ei/:ms:${coord}:InUseBO/ \
                 -print=col,ValueString,%ld,Flag \
                 -print=col,ControlType,pv \
                 -print=col,Lineage,- \
                 -define=col,Count,1,type=long \
                 -print=para,SnapType,Absolute \
                 | sddsconvert -pipe=in inUse \
                 -dele=col,Name,Flag -dele=para,NameType \
             } result ] {
        APSDeleteTmpFileList -ID APSGenerateControllawFiles
        cd $oldDir
        return -code error "APSGenerateControllawFiles(41 - sddsprocess): $result"
    }
    ########################
    # create a burt file for setting InUseBO PV to not used
    ########################
    if [catch {exec sddsprocess $bpmStatusFile -pipe=out \
                 -filter=col,Nonexistent${Plane},0,0 \
                 -edit=col,ControlName,DeviceName,ei/:ms:${coord}:InUseBO/ \
                 | sddsconvert -pipe -retain=col,ControlName \
                 | sddsselect -pipe inUse -match=ControlName -invert \
                 | sddsprocess -pipe=in notUse \
                 -reprint=col,ValueString,0 \
                 -reprint=col,ControlType,pv \
                 -reprint=col,Lineage,- \
                 -redefine=col,Count,1,type=long \
                 -reprint=para,SnapType,Absolute } result ] {
        APSDeleteTmpFileList -ID APSGenerateControllawFiles
        cd $oldDir
        return -code error "APSGenerateControllawFiles(42 - sddsprocess): $result"
    }
    
    APSAddToTmpFileList -ID APSGenerateControllawFiles -fileList "inUse~"
    APSDeleteTmpFileList -ID APSGenerateControllawFiles

    # define definitions file for FF matrix.
    if [catch {APSMakeFFDefinitions -coord $coord \
             } result ] {
        APSDeleteTmpFileList -ID APSGenerateControllawFiles
        cd $oldDir
        return -code error "APSGenerateControllawFiles(43 - APSMakeFFDefinitions): $result"
    }
    
    cd $oldDir
    return
}

proc APSMakeFFDefinitions {args} {
    set coord ""
    APSStrictParseArguments {coord}
    if {$coord == "" } {
        return -code error "MakeFFDefinitions: No value for coord given."
    }
    catch {file delete FFdefs}
    if [catch {open FFdefs.bpm w} fid] {
        return -code error "MakeFFDefinitions(1): $fid"
    }
    set crateList ""
    for {set i 1} {$i<41} {incr i} {
        set ioc [expr ($i - 1) / 2 * 2 + 1]
        lappend crateList S${ioc}FB:GB${i}
    }
    puts $fid SDDS1
    puts $fid "&column name=ControlName type=string &end"
    puts $fid "&column name=SymbolicName type=string &end"
    puts $fid "&data mode=ascii no_row_counts=1 &end"
    if [catch {exec cavget -list=[join $crateList ,] -list=:bpm -list=1,2,3,4 \
                   -list=:${coord}:SelectedMI -label -pend=30} result] {
        return -code error "MakeFFDefinitions(2 - cavget): $result"
    }
    puts $fid $result
    close $fid

    APSAddToTmpFileList -ID FFfiles -fileList "FFdefs.bpm FFdefs.bpm~"
    if [catch {exec sddsprocess FFdefs.bpm -noWarnings \
                 -match=col,SymbolicName=None,! \
                 "-match=col,SymbolicName=Illegal Value,!" \
                 -reedit=col,ControlName,%/SelectedMI/RefAO/ \
             } results ] {
        return -code error "MakeFFDefinitions(3 - sddsprocess): $results"
    }
    if [catch {exec sddsprocess FFdefs.bpm -pipe=out -noWarnings \
                 -match=col,SymbolicName=? \
                 | sdds2stream -pipe -rows=bare \
             } CAerrors ] {
        return -code error "MakeFFDefinitions(4 - sddsprocess): $CAerrors"
    }
    if $CAerrors {
        return -code error "MakeFFDefinitions(5): $CAerrors CA errors occured when reading RTFB crate PVs. Try again."
    }
    # grab definitions for correctors
    if ![file exists definitions.msAve] {
        return -code error "MakeFFDefinitions(6): Can't find file definitions.msAve"
    }
    # I simplify the filtering by filtering for both H and V correctors.
    APSAddToTmpFileList -ID FFfiles -fileList FFdefs.corr
    if [catch {exec sddsprocess definitions.msAve FFdefs.corr \
                 -noWarnings \
                 -match=col,SymbolicName=S*H*,SymbolicName=S*V*,| \
             } results ] {
        return -code error "MakeFFDefinitions(7 - sddsprocess): $results"
    }
    
    if [catch {exec sddscombine FFdefs.corr FFdefs.bpm \
                 FFdefs -merge \
             } results ] {
        return -code error "MakeFFDefinitions(8 - sddscombine): $results"
    }
    APSDeleteTmpFileList -ID FFfiles
    return
}

proc APSCreateDPTestFiles {args} {
    set plane ""
    set datapool 0
    set unified 0
    APSStrictParseArguments {plane datapool unified}
    if ![string length $plane] {
        return -code error "APSCreateDPTestFiles: the plane is not given"
    }
    set plane [string tolower $plane]
    switch $plane {
        h {
            set filename DPHtests
            if $datapool {
                set testPVs [list DP:HOCtestStatus DP:HOCtestRC.RUN DP:MpsTripM \
                               DP:EnableWriteHCor2RMC DP:HCorrVectorModeC.VAL \
                               DP:EnableWriteXbpmSetPt2RMC DP:XbpmSetPtVectorModeC.VAL ]
            } else {
                set testPVs [list DP:HOCtestStatus DP:HOCtestRC.RUN DP:MpsTripM ]
            }
        }
        v {
            set filename DPVtests
            if $datapool {
                set testPVs [list DP:VOCtestStatus DP:VOCtestRC.RUN DP:MpsTripM \
                               DP:EnableWriteVCor2RMC DP:VCorrVectorModeC.VAL \
                               DP:EnableWriteYbpmSetPt2RMC DP:YbpmSetPtVectorModeC.VAL ]
            } else {
                set testPVs [list DP:VOCtestStatus DP:VOCtestRC.RUN DP:MpsTripM ]
            }
        }
        default {
            return -code error "APSCreateDPTestFiles(1): unknown plane"
        }
    }
    if $datapool {
        if !$unified {
            set minValues [list -0.5 0.5 -0.5 0.5 0.5 0.5 0.5 -0.5]
            set maxValues [list  0.5 1.5  0.5 1.5 1.5 1.5 1.5 0.5]
        } else {
            # unified mode requires BPM setpoint feedforward to be off
            set minValues [list -0.5 0.5 -0.5 0.5 0.5 -0.5 -0.5 -0.5]
            set maxValues [list  0.5 1.5  0.5 1.5 1.5  0.5  0.5 0.5]
        }
    } else {
        set minValues [list -0.5 0.5 -0.5 -0.5 -0.5]
        set maxValues [list  0.5 1.5 0.5   0.5  0.5]
    }
    exec sddsmakedataset $filename -ascii \
      -col=ControlName,type=string  -data=[join $testPVs   ,] \
      -col=MinimumValue,type=double -data=[join $minValues ,] \
      -col=MaximumValue,type=double -data=[join $maxValues ,] 
}

proc APSCreateWaveformFiles {args} {
    set configFile ""
    set testFile ""
    set BPMLimit 10
    set XBPMLimit 0.5
# These two are no longer used
    set corrLimit 148
    set CUCorrLimit 3.5
    set corrStatusFile ""
    set type ""
    set plane ""
    set wfDir /home/helios/oagData/sr/orbitControllaw/waveforms
    APSStrictParseArguments {configFile testFile corrLimit CUCorrLimit BPMLimit XBPMLimit type plane corrStatusFile}
    set wavelist ""
    if {![string length $configFile] || ![string length $testFile] } {
        return -code error "APSCreateWaveformFiles(1): the configure file or test file name is not given."
    }
    if ![string length $plane] {
        return -code error "APSCreateWaveformFiles(2): plane is not given!"
    }
    switch $plane {
        h {
            set Coord X
        }
        v {
            set Coord Y
        }
        default {
            return -code error "APSCreateWaveformFiles(3): plane should be h or v!"
        }
    }
    switch $type {
        corr {
            set tmp1 /tmp/[APSTmpString]
            if [catch {exec sddsprocess $configFile $tmp1 \
                         -define=col,temp,-1,type=short \
                     } result] {
                return -code error "APSCreateWaveformFiles(4 - sddsprocess): $result"
            }
            if [catch {exec sddsxref $wfDir/${plane}corrInfo.sdds $tmp1 -pipe=out \
                         -match=DeviceName=SymbolicName -take=temp \
                           -nowarning -fillin \
                         | sddsprocess -pipe \
                         "-define=col,Ignore,temp 1 +,type=short" \
                         | sddsxref -pipe $corrStatusFile -match=DeviceName -take=CorrectorLimit \
                         | sddsprocess -pipe "-define=col,MinimumValue,-1 CorrectorLimit *" \
                         "-define=col,MaximumValue,CorrectorLimit" \
                         | sddsconvert -pipe=in $testFile -delete=col,temp \
                         -rename=par,SetpointWaveformPV=WaveformPV } result] {
                return -code error "APSCreateWaveformFiles(5 - sddsxref): $result"
            }
            return 0
        }
        bpm {
            set tmpFile /tmp/[APSTmpString]
            APSAddToTmpFileList -ID APSCreateWaveformFiles -fileList  $tmpFile.id
            if [catch {exec sddsprocess $configFile $tmpFile.id \
                         -define=col,Ignore,1,type=short \
                         -match=col,SymbolicName=S*ID:P* \
                         -scan=col,Sector,SymbolicName,S%ld,type=long \
                         -nowarnings} result] {
                APSDeleteTmpFileList -ID APSCreateWaveformFiles 
                return -code error "APSCreatWaveformFiles(6 - sddsprocess): $result" 
            }
            set IDXrayBPMS [exec sdds2stream $tmpFile.id -rows=bare]
            
            if [catch {exec sddsprocess $configFile $tmpFile.bm \
                         -define=col,Ignore,1,type=short \
                         -match=col,SymbolicName=S*BM:P* \
                         -nowarnings} result] {
                APSDeleteTmpFileList -ID APSCreateWaveformFiles 
                return -code error "APSCreateWaveformFiles(7 - sddsprocess): $result" 
            }
            set BMXrayBPMS [exec sdds2stream $tmpFile.bm -rows=bare]
            APSAddToTmpFileList -ID APSCreateWaveformFiles -fileList  $tmpFile.bm
            
            if [catch {exec sddsprocess $configFile $tmpFile.p0 \
                         -define=col,Ignore,1,type=short \
                         -match=col,SymbolicName=S*:P0* \
                         -nowarnings} result] {
                APSDeleteTmpFileList -ID APSCreateWaveformFiles 
                return -code error "APSCreateWaveformFiles(8 - sddsprocess): $result" 
            }
            set P0XrayBPMS [exec sdds2stream $tmpFile.p0 -rows=bare]
            APSAddToTmpFileList -ID APSCreateWaveformFiles -fileList  $tmpFile.p0
            
            if [catch {exec sddsprocess $configFile $tmpFile.rf \
                         -define=col,Ignore,1,type=short \
                         -match=col,SymbolicName=S*A:P*,SymbolicName=S*B:P*,| \
                         -nowarnings} result] {
                APSDeleteTmpFileList -ID APSCreateWaveformFiles 
                return -code error "APSCreateWaveformFiles(9 - sddsprocess): $result" 
            }
            set rfXrayBPMS [exec sdds2stream $tmpFile.rf -rows=bare]
            APSAddToTmpFileList -ID APSCreateWaveformFiles -fileList  $tmpFile.rf
            set waveformList ""
            if $IDXrayBPMS {
                lappend waveformList $wfDir/id${Coord}BpmInfo.sdds
            }
            if $P0XrayBPMS {
                lappend waveformList $wfDir/p0${Coord}BpmInfo.sdds
            }
            if $rfXrayBPMS {
                lappend waveformList $wfDir/rf${Coord}BpmInfo.sdds
            }
            if $BMXrayBPMS {
                lappend waveformList $wfDir/bmBpmInfo.sdds
            }
            if [catch {eval exec sddscombine $waveformList bpmWaveform -overWrite} result] {
                return -code error "APSCreateWaveformFiles(10 - sddscombine): $result" 
            }
            set waveList ""
            if $IDXrayBPMS {
                APSAddToTmpFileList -ID APSCreateWaveformFiles -fileList $tmpFile.idTest
                if [catch {exec sddsprocess $wfDir/id${Coord}BpmInfo.sdds  -pipe=out \
                             -define=col,MaximumValue,$XBPMLimit,type=double \
                             -define=col,MinimumValue,-$XBPMLimit,type=double \
                             -define=col,Despike,0,type=long \
                             | sddsxref -pipe $tmpFile.id \
                             -match=DeviceName=SymbolicName -take=Ignore \
                             -nowarnings -fillIn  \
                             | sddsprocess -pipe=in $tmpFile.idTest \
                             "-redefine=col,Ignore,Ignore 1 - abs,type=short" \
                         } result] {
                    return -code error "APSCreateWaveformFiles(11 - sddsxref): $result" 
                }
                lappend waveList $tmpFile.idTest
            }
            if $P0XrayBPMS {
                APSAddToTmpFileList -ID APSCreateWaveformFiles -fileList $tmpFile.p0Test
                if [catch {exec sddsprocess $wfDir/p0${Coord}BpmInfo.sdds  -pipe=out \
                             -define=col,MaximumValue,$BPMLimit,type=double \
                             -define=col,MinimumValue,-$BPMLimit,type=double \
                             -define=col,Despike,0,type=long \
                             | sddsxref -pipe $tmpFile.p0 \
                             -match=DeviceName=SymbolicName -take=Ignore \
                             -nowarnings -fillIn \
                             | sddsprocess -pipe=in $tmpFile.p0Test \
                             "-redefine=col,Ignore,Ignore 1 - abs,type=short" \
                         } result] {
                    return -code error "APSCreateWaveformFiles(12 - sddsxref): $result" 
                }
                lappend waveList $tmpFile.p0Test
            }
            #rf bpm waveform test
            if $rfXrayBPMS {
                APSAddToTmpFileList -ID APSCreateWaveformFiles -fileList $tmpFile.rfTest
                if [catch {exec sddsprocess $wfDir/rf${Coord}BpmInfo.sdds  -pipe=out \
                             -define=col,MaximumValue,$BPMLimit,type=double \
                             -define=col,MinimumValue,-$BPMLimit,type=double \
                             -define=col,Despike,0,type=long \
                             | sddsxref -pipe $tmpFile.rf \
                             -match=DeviceName=SymbolicName -take=Ignore \
                             -nowarnings -fillIn \
                             | sddsprocess -pipe=in $tmpFile.rfTest \
                             "-redefine=col,Ignore,Ignore 1 - abs,type=short" \
                         } result] {
                    return -code error "APSCreateWaveformFiles(13 - sddsxref): $result" 
                }
                lappend waveList $tmpFile.rfTest
            }
            
            if $BMXrayBPMS {
                APSAddToTmpFileList -ID APSCreateWaveformFiles -fileList $tmpFile.bmTest
                if [catch {exec sddsprocess $wfDir/bmBpmInfo.sdds  -pipe=out \
                             -define=col,MaximumValue,$XBPMLimit,type=double \
                             -define=col,MinimumValue,-$XBPMLimit,type=double \
                             -define=col,Despike,0,type=long \
                             | sddsxref -pipe $tmpFile.bm \
                             -match=DeviceName=SymbolicName -take=Ignore \
                             -nowarnings -fillIn \
                             | sddsprocess -pipe=in $tmpFile.bmTest \
                             "-redefine=col,Ignore,Ignore 1 - abs,type=short" \
                         } result] {
                    return -code error "APSCreateWaveformFiles(14 - sddsxref): $result" 
                }
                lappend waveList $tmpFile.bmTest
            }
            if [catch {eval exec sddscombine $waveList $testFile -overWrite } result] {
                return -code error "APSCreateWaveformFiles(15 - sddscombine):$result"
            }
        }
        default {
            return -code error "APSCreateWaveformFiles: unknown type"
        }
    }
    APSDeleteTmpFileList -ID APSCreateWaveformFiles
    return 0
}

proc APSAbortSRControllaw {args} {
    set runControlPV ""
    set sourceId ""
    
    APSStrictParseArguments {runControlPV sourceId} 
    if ![string length $runControlPV] {
        return -code error "run control pv is not provided!"
    }
    if [catch {exec cavput -list=${runControlPV}.ABRT=1 -pend=30} result ] {
        return -code error "APSAbortSRControllaw(1): $result"
    }
    if [catch {exec cavput -list=${runControlPV}.SUSP=0 -pend=30} result ] {
        return -code error "APSAbortSRControllaw(2): $result"
    }
    if [string length $sourceId] {
        if [catch {exec logMessage  -sourceId=$sourceId \
                     -tag=Instance $runControlPV -tag=Action Abort \
                 } result ] {
            return -code error "APSAbortSRControllaw(3): $result"
        }
    }
    after 2000
}

proc APSSuspendSRControllaw {args} {
    set runControlPV ""
    set sourceId ""
    
    APSStrictParseArguments {runControlPV sourceId} 
    if ![string length $runControlPV] {
        return -code error "run control pv is not provided!"
    }
    if [catch {exec cavput -list=${runControlPV}.SUSP=1 -pend=30} result ] {
        return -code error "APSSuspendSRControllaw(2): $result"
    }
    if [string length $sourceId] {
        if [catch {exec logMessage  -sourceId=$sourceId \
                     -tag=Instance $runControlPV -tag=Action Suspend \
                 } result ] {
            return -code error "APSSuspendSRControllaw(3): $result"
        }
    }
    after 2000
}
proc APSResumeSRControllaw {args} {
    set runControlPV ""
    set sourceId ""
    
    APSStrictParseArguments {runControlPV sourceId} 
    if ![string length $runControlPV] {
        return -code error "run control pv is not provided!"
    }
    if [catch {exec cavput -list=${runControlPV}.SUSP=0 -pend=30} result ] {
        return -code error "APSResumeSRControllaw(2): $result"
    }
    if [string length $sourceId] {
        if [catch {exec logMessage  -sourceId=$sourceId \
                     -tag=Instance $runControlPV -tag=Action Resume \
                 } result ] {
            return -code error "APSResumeSRControllaw(3): $result"
        }
    }
    after 2000
}

proc APSTransferVectorGain {args} {
    set coord ""
    set gainReferenceFile /home/helios/oagData/SCR/snapshots/SBPMs/SBPMs-Preferred.gz
    set waveformFileDir /home/helios/oagData/sr/orbitControllaw/waveforms
    APSStrictParseArguments {coord}
    
    set tmpRoot /tmp/[APSTmpString]
    APSAddToTmpFileList -ID APSTransferVectorGain -fileList $tmpRoot.gain.$coord
    if [catch {exec sddsprocess $gainReferenceFile $tmpRoot.gain.$coord \
                 -match=col,ControlName=*${coord}*GainAO } result] {
        APSDeleteTmpFileList -ID APSTransferVectorGain
        return -code error "APSTransferVectorGain(1 - sdddsprocess): $result"
    }
    
    if [catch {exec sddsprocess $gainReferenceFile -pipe=out \
                 -match=column,ControlName=*${coord}*GainAO \
                 -edit=col,DeviceName,ControlName,%/:ms:${coord}:GainAO// \
                 -scan=col,Waveform,ValueString,%lf \
                 | sddsconvert -pipe=in $tmpRoot.gain \
                 -retain=column,BPMName,Waveform,DeviceName} result] {
        APSDeleteTmpFileList -ID APSTransferVectorGain
        return -code error "APSTransferVectorGain(2 - sddsprocess): $result"
    }
    set fileList ""
    foreach bpmType {p0 id bm rf} {
        if {[string compare $bpmType bm]==0} {
            if [string compare $coord x]==0 continue
            set waveformFile $waveformFileDir/bmBpmInfo.sdds
        } else {
            set waveformFile $waveformFileDir/${bpmType}[string toupper $coord]BpmInfo.sdds
        }
        if [catch {exec sddsconvert $waveformFile -pipe=out \
                     -retain=column,Index,DeviceName -retain=parameter,WaveformPV \
                     | sddsxref -pipe=in $tmpRoot.gain -fillIn -nowarning \
                     -match=DeviceName -take=Waveform $tmpRoot.$bpmType \
                 } result] {
            APSDeleteTmpFileList -ID APSTransferVectorGain
            return -code error "APSTransferVectorGain(3 - sddsconvert): $result"
        }
        lappend fileList $tmpRoot.$bpmType
    }
    APSAddToTmpFileList -ID APSTransferVectorGain -fileList $fileList
    set output $tmpRoot.vectorgain.$coord
    APSAddToTmpFileList -ID APSTransferVectorGain -fileList $output
    if [catch {eval exec sddscombine $fileList -pipe=out \
                 | sddsprocess -pipe=in $output \
                 -reedit=parameter,WaveformPV,%/:Out:/:Gain:/
        exec sddswput $output} result] {
        APSDeleteTmpFileList -ID APSTransferVectorGain
        
        return -code error "APSTransferVectorGain(3 - sddswput): $result"
    }
    if [catch {exec sddscasr -restore $tmpRoot.gain.$coord }  result] {
        return -code error "APSTransferVectorGain(3 - sddscasr): $result"
    }
}

# this procedure is moved from OAGapps
proc APSSRChangeSteeringIndicator {args} {
    set toggle 0
    set mode -1
    APSStrictParseArguments {toggle mode} 
    
    if {!$toggle && $mode==-1} {
        return
    }
    if $toggle {
        if [catch {exec cavget -num -list=S:OrbitControlLaw -list=X,Y -list=FineBO} result] {
            return -code error "APSSRChangeSteeringIndicator(1 - cavget): $result"
        }
        set mode [expr !([lindex $result 0] || [lindex $result 1])]
    } 

    if [catch {exec cavput -list=S:OrbitControlLaw -list=X,Y -list=FineBO=$mode} result] {
        return -code error "APSSRChangeSteeringIndicator(2 - cavput): $result"
    }
}


proc APSCheckAndUpdateLocalSteeringConfigs {args} {
    set steeringType ""
    set sector ""
    set stream ""
    set statusCallback ""
    APSStrictParseArguments {steeringType sector stream statusCallback}
    
    if [string length $statusCallback] {
        eval $statusCallback {"Checking $steeringType $sector configs and bpm status mtime."}
    }
    if ![string length $steeringType] {
        return -code error "APSCheckAndUpdateLocalSteeringConfigs(1): steering type is not provided."
    }
    if ![string length $sector] {
        return -code error "APSCheckAndUpdateLocalSteeringConfigs(2): sector is not provided."
    }
    switch $steeringType {
        ID -
        AM -
        BM {}
        CU {
            if ![string length $stream] {
                return -code error "APSCheckAndUpdateLocalSteeringConfigs(3): stream (US or DS) is not provide for CU steering"
            }
            set stream [string toupper $stream]
            switch $stream {
                DS -
                US {}
                default {
                    return -code error "APSCheckAndUpdateSteeringConfigs(4): unknown stream - $stream given for CU steering."
                }
            }
            set steeringType ${steeringType}${stream}P0
        }
        default {
            return -code error "APSCheckAndUpdateLocalSteeringConfigs(5): unknown steering type - $steeringType"
        }
    }
    
    set controllawDir /home/helios/oagData/sr/localSteering/lattices/default/${steeringType}s/[format %02ld ${sector}]$steeringType
    if {![file exist $controllawDir]} {
        return -code error "APSCheckAndUpdateLocalSteeringConfigs(6): $controllawDir does not exist!"
    }
    #check the bpm status file and local steering config file modification time
    set bpmTime [file mtime /home/helios/oagData/sr/BPMStatus/config.sdds]
    set hCorrTime [file mtime /home/helios/oagData/sr/HCorrectorStatus/config.sdds]
    set vCorrTime [file mtime /home/helios/oagData/sr/VCorrectorStatus/config.sdds]
    set refTime 0
    if $bpmTime>$refTime {set refTime $bpmTime}
    if $hCorrTime>$refTime {set refTime $hCorrTime}
    if $vCorrTime>$refTime {set refTime $vCorrTime}
    set configDir /home/helios/oagData/sr/localSteering/configs
    
    if {$steeringType=="ID"} {
        set linkDir [file link $controllawDir]
        if [regexp "P0" $linkDir] {
            set steeringType P0
        } elseif [regexp "P1" $linkDir] {
            set steeringType P1
        } else {
            return -code error "Invalid ID steering type - $linkDir!"
        }
        #set steeringType [string range $linkDir end-1 end]
    }
    set subDir ${steeringType}s/[format %02ld $sector]$steeringType
    set createConfig 0
    if ![file exist $configDir/$subDir] {
        set createConfig 1
    } elseif {![file exist $configDir/$subDir/config.x] || ![file exist $configDir/$subDir/config.y]} {
        set createConfig 1
    } else {
        set xtime [file mtime $configDir/$subDir/config.x]
        set ytime [file mtime $configDir/$subDir/config.y]
        if {$refTime>$xtime || $refTime>$ytime} {
            set createConfig 1
        }
    }
    if $createConfig {
        if [string length $statusCallback] {
            eval $statusCallback {"Creating local steering config files for $steeringType $sector."}
        }
        if [catch {exec makeLocalSteeringConfigs -steeringType $steeringType -sourceList $sector} result] {
            return -code error "APSCheckAndUpdateLocalSteeringConfigs(7: makeLocalSteeringConfigs): $result"
        }
        set xtime [file mtime $configDir/$subDir/config.x]
        set ytime [file mtime $configDir/$subDir/config.y]
    }

    set createMatrices 0
    if {![file exist $controllawDir/irm] || \
          ![file exist $controllawDir/irm.x] || \
          ![file exist $controllawDir/irm.y] || \
          ![file exist $controllawDir/h.steering.rm] || \
          ![file exist $controllawDir/v.steering.rm] } {
        set createMatrices 1
    } else {
        set time [clock seconds]
        foreach file {irm irm.x irm.y h.steering.rm v.steering.rm} {
            set ftime [file mtime $controllawDir/$file]
            if {$ftime<$time} {
                set time $ftime
            }
        }
        if {$time<$refTime} {
            set createMatrices 1
        }
    }
    if $createMatrices {
        if [string length $statusCallback] {
            eval $statusCallback {"Creating local steering matrices for $steeringType $sector."}
        }
        if [catch {exec makeLocalSteeringMatrices -sourceList $sector \
                     -steeringType $steeringType -lattice default} result] {
            return -code error "APSCheckAndUpdateLocalSteeringConfigs(7a: makeLocalSteeringMatrices): error in creating steering matrices: $result!"
        }
    }
}



proc APSCheckLocalSteeringTests {args} {
    set sector ""
    set steeringType ""
    set stream ""
    set statusCallback ""
    APSStrictParseArguments {sector steeringType statusCallback stream}
    
    if {![string length $sector] || ![string length $steeringType]} {
        return -code error "APSCheckLocalSteeringTests(1): sector or steeringType is not given"
    }
    if [string compare CU $steeringType]==0 {
        if ![string length $stream] {
            return -code error "APSCheckLocalSteeringTests(2): stream is not given for CU steering."
        }
        set type CU${stream}P0
    } else {
        set type $steeringType
    }
    set controllawDir /home/helios/oagData/sr/localSteering/lattices/default/${type}s/[format %02ld ${sector}]$type
    if [catch {APSSRCheckCorrectorMode -plane h} hCorrMode] {
        return -code error "APSCheckLocalSteeringTests(3): $hCorrMode"
    }
    set tmpRoot /tmp/[APSTmpString]
    if [string compare $hCorrMode vector]==0 {
        set testsFile  /home/helios/oagData/sr/localSteering/configs/datapool.test
    } else {
        set testsFile $controllawDir/tests
    }
    if ![file exist $testsFile] {
        return -code error "APSCheckLocalSteeringTests(4): $testsFile does not exist!"
    }
    if [catch {exec sddssnapshot $testsFile -pipe=out \
                 | sddsprocess -pipe=in $tmpRoot.1 -nowarning \
                 "-test=column,MinimumValue Value > MaximumValue Value < ||" } result] {
        return -code error "APSCheckLocalSteeringTests(5): $result"
    }
    set rows [exec sdds2stream $tmpRoot.1 -rows=bar]
    if !$rows {
        if [string length $statusCallback] {
            eval $statusCallback {"All tests for [file tail $testsFile] are satisfied."}
        }
    } else {
        if [catch {exec sddsprintout $tmpRoot.1 $tmpRoot.print \
                     "-title=Test values out of range for $steeringType $sector" \
                     -column=ControlName,format=%32s -column=MinimumValue,format=%8.2f -column=Value,format=%8.2f \
                     -column=MaximumValue,format=%8.2f } result] {
            return -code error "APSCheckLocalSteeringTests(6): $result"
        }
        if $rows<10 {
            set rows 10
        } else {
            set rows [expr $rows+5]
        }
        if $rows>40 {
            set rows 40
        }
        APSFileDisplayWindow [APSUniqueName .] -fileName $tmpRoot.print \
          -sddsExportableFile $tmpRoot.1 -deleteOnClose 1 -width 80 -height $rows
    }
    if [string compare $hCorrMode vector]==0 {
        if [string length $statusCallback] {
            eval $statusCallback {"Checking waveformTest for $steeringType $sector datapool steering."}
        }
        if ![file exist $controllawDir/waveformTest] {
            return -code error "APSCheckLocalSteeringTests(7): waveformTest does not exist yet -- You need press APPLY/START button to create the waveform files!"
        }
        if [catch {exec sddswget $controllawDir/waveformTest $tmpRoot.2} result] {
            return -code error $result
        }
        if [catch {exec sddsprocess $tmpRoot.2 -pipe=out -nowarnings \
                     -filter=col,Ignore,-0.5,0.5 \
                     "-test=column,MinimumValue Waveform > MaximumValue Waveform < ||" \
                     | sddscombine -pipe=in -merge  $tmpRoot.waveform } result] {
            return -code error "APSCheckLocalSteeringTests(8): $result"
        }
        set rows [exec sdds2stream -rows=bar $tmpRoot.waveform]
        if !$rows  {
            if [string length $statusCallback] {
                eval $statusCallback {"All tests for waveformTest are satisfied."}
            }
        } else {
            if [catch {exec sddsprintout $tmpRoot.waveform $tmpRoot.print.waveform \
                         "-title=Waveform test values out of range for $steeringType $sector" \
                         -column=DeviceName,format=%16s -column=MinimumValue,format=%8.2f -column=Waveform,format=%8.2f \
                         -column=MaximumValue,format=%8.2f } result] {
                return -code error "APSCheckLocalSteeringTests(9): $result"
            }
            if $rows<10 {
                set rows 10
            } else {
                set rows [expr $rows+5]
            }
            if $rows>40 {
                set rows 40
            }
            APSFileDisplayWindow [APSUniqueName .] -fileName $tmpRoot.print.waveform \
              -sddsExportableFile $tmpRoot.waveform -deleteOnClose 1 -width 80 \
              -height $rows
        }
    }
    set tmpFiles [glob $tmpRoot.*]
    APSAddToTmpFileList -ID checkSteeringTest -fileList "$tmpFiles"
}


proc APSMpSRUnstickCorrectorsInfo {args} {
    return "This procedure is used when a SR corrector has exceeded its \
            pre-defined limit in CurrentAO or RangeErrorCALC PVs because of the \
            actions of orbit correction or steering. For both PV types an \
            \"out-of-range\" error is displayed in a run control PV message box. \
            For the correction or steering to continue (with presumably new bpm \
            setpoints to avoid repeating the situation), the vector values of the \
            correctors must be changed outside orbit correction and steering. \n\n\
            This unsticking procedure first determines all the correctors that: \n\
            1) are outside their limits (typically +/-148 A for normal correctors)\n\
            2) have rangeErrorCALC alarms. \n\
            and moves their setpoints back by some amount specified by the user in \
            a dialog box.  The CU correctors (C:H1, C:V1) are only checked for \
            their  +/- 4.5 A limits, as their range error limit of +/- 5 A is not that useful."
}

proc APSMpSRUnstickCorrectorsInitDialog {frame} {
    global APSMpSRUnstickCorrectors
    
    APSRadioButtonFrame .plane -parent $frame -orientation horizontal \
        -variable APSMpSRUnstickCorrectors(plane) \
        -label "Plane: " \
        -buttonList "h v both" -valueList "h v both"  \
        -contextHelp "unstick correctors in the selected plane (or both planes if both is selected)"
    APSLabeledEntry .delta -parent $frame -label "delta (A): " \
        -width 20 -textVariable APSMpSRUnstickCorrectors(delta) \
        -contextHelp "the delta of the corrector setting value from the limit."
    APSLabeledEntry .cu -parent $frame -label "CU corrector limit (A): " \
        -width 20 -textVariable APSMpSRUnstickCorrectors(CUcorrLimit) \
        -contextHelp "the limit of CU correctors, will be considered as out of range if the CU corrector's current AO is beyond this limit"
}

#This procedure has been placed in the attic
proc APSMpSRUnstickCorrectors {args} {
    set plane both
    set delta 0.5
    set CUcorrLimit 3.4
    APSStrictParseArguments {plane delta CUcorrLimit}
    
    APSMpStep init -promptDialog APSMpSRUnstickCorrectorsInitDialog \
      -vars {plane delta CUcorrLimit}
    set wfDir /home/helios/oagData/sr/orbitControllaw/waveforms
    switch $plane {
        both {
            if [catch {APSSRCheckCorrectorMode -plane h} hCorrMode] {
                APSMpReturn error "APSMpSRUnstickCorrectors(1 - APSSRCheckCorrectorMode h plane): $hCorrMode"
            }
            if [catch {APSSRCheckCorrectorMode -plane v} vCorrMode] {
                APSMpReturn error "APSMpSRUnstickCorrectors(2 - APSSRCheckCorrectorMode v plane): $vCorrMode"
            }
            if [string compare $hCorrMode $vCorrMode]!=0 {
                APSMpReturn error "APSMpSRUnstickCorrectors(3 - APSSRCheckCorrectorMode): the h and v plane corrector modes are different!"
            }
            set planeList {h v}
            set corrMode $hCorrMode
        }
        default {
            if [catch {APSSRCheckCorrectorMode -plane $plane} corrMode] {
                APSMpReturn error "APSMpSRUnstickCorrectors(4 - APSSRCheckCorrectorMode): $corrMode"
            }
            set planeList $plane
        }
    }
    set tmpRoot /tmp/[APSTmpString]
    foreach plane $planeList {
        APSAddToTmpFileList -ID unstickcorr -fileList "$tmpRoot.$plane.outRange $tmpRoot.$plane.sdds $tmpRoot.$plane.outRange.1 $tmpRoot.$plane.outRange.2 $tmpRoot.$plane.sdds.1 $tmpRoot.$plane.sdds.2 $tmpRoot.$plane.exist"
        if [catch {exec sddsprocess /home/helios/oagData/sr/[string toupper $plane]CorrectorStatus/config.sdds \
                     -filter=col,Nonexistent,0,0 $tmpRoot.$plane.exist } result] {
            APSMpReturn error "APSMpSRUnstickCorrectors(5 - sddsprocess): $result"
        }
        if [catch {exec sddsprocess $wfDir/${plane}corrInfo.sdds  -pipe=out \
                     -match=col,DeviceName=S*C:*,! \
                     -edit=col,ControlName,DeviceName,ei/:RangeErrorCALC/ \
                     | sddscasr -pipe -save \
                     | sddsprocess -pipe -scan=col,Value,ValueString,%lf \
                     | sddsprocess -pipe -reedit=col,ControlName,ei/.HIHI/ \
                     | sddscasr -pipe -save \
                     | sddsprocess -pipe -scan=col,UpperLimit,ValueString,%lf \
                     -reedit=col,ControlName,%/.HIHI/.LOLO/ \
                     | sddscasr -pipe -save \
                     | sddsprocess -pipe -scan=col,LowerLimit,ValueString,%lf \
                     | sddsprocess -pipe "-define=col,OutOfRange,Value LowerLimit < ? 1 : Value UpperLimit > ? -1 : 0 $ $ " \
                     | tee $tmpRoot.$plane.sdds.1 \
                     | sddsprocess -pipe=in $tmpRoot.$plane.outRange.1  \
                     -filter=col,OutOfRange,0,0,! -nowarnings 
            # for CU correctors only
            exec sddsprocess $wfDir/${plane}corrInfo.sdds  -pipe=out \
                     -match=col,DeviceName=S*C:* \
                     -edit=col,ControlName,DeviceName,ai/SFB:/ei/:CurrentAO/ \
                     | sddscasr -pipe -save \
                     | sddsprocess -pipe -scan=col,Value,ValueString,%lf \
                     | sddsprocess -pipe "-define=col,OutOfRange,Value -$CUcorrLimit < ? 1 : Value $CUcorrLimit > ? -1 : 0 $ $ " \
                     | tee $tmpRoot.$plane.sdds.2 \
                     | sddsprocess -pipe=in $tmpRoot.$plane.outRange.2  \
                     -filter=col,OutOfRange,0,0,! -nowarnings  
            exec sddscombine $tmpRoot.$plane.sdds.1  $tmpRoot.$plane.sdds.2 -merge -pipe=out \
                     | sddssort -pipe=in -col=Index $tmpRoot.$plane.sdds 
            exec sddscombine $tmpRoot.$plane.outRange.1 $tmpRoot.$plane.outRange.2 \
                     -merge $tmpRoot.$plane.outRange} result] {
            APSMpReturn error "APSMpSRUnstickCorrectors(6 - sddsprocess, sddscombine, sddscasr): $result"
        }
        set rows [exec sdds2stream -rows=bare $tmpRoot.$plane.outRange]
        if $rows {
            APSAddToTmpFileList -ID unstickcorr -fileList "$tmpRoot.$plane.outRange.exist"
            if [catch {exec sddsselect $tmpRoot.$plane.outRange $tmpRoot.$plane.exist -pipe=out \
                         -match=DeviceName -nowarnings \
                         | sddssort -pipe=in -col=Index $tmpRoot.$plane.outRange.exist} result] {
                APSMpReturn error "APSMpSRUnstickCorrectors(7 - sddsselect): $result"
            }
            set rows [exec sdds2stream -rows=bare $tmpRoot.$plane.outRange.exist]
        }
        if $rows {
            APSMpStep "[join [exec sdds2stream -col=DeviceName $tmpRoot.$plane.outRange]] are out of range."
            if ![APSYesNoPopUp "Unstick the out-of-range correctors?"] {
                continue
            }
            #  APSAddToTmpFileList -ID unstickcorr -fileList "$tmpRoot.$plane.set"
            switch $corrMode {
                vector {
                    if [catch {exec sddsselect $tmpRoot.$plane.sdds $tmpRoot.$plane.exist \
                                 -match=DeviceName -pipe=out -invert -nowarnings  \
                                 | sddsprocess -pipe=in $tmpRoot.$plane.1 -define=col,Waveform,0 
                        exec sddsselect $tmpRoot.$plane.sdds $tmpRoot.$plane.exist \
                                 -match=DeviceName -pipe=out -nowarnings \
                                 | sddsprocess -pipe=in $tmpRoot.$plane.2 \
                                 "-define=col,Waveform,OutOfRange $delta *" 
                        exec sddscombine $tmpRoot.$plane.1 $tmpRoot.$plane.2 -merge -pipe=out \
                                 | sddssort -pipe -col=Index \
                                 | sddsconvert -pipe=in -rename=par,DeltaWaveformPV=WaveformPV \
                                 $tmpRoot.$plane.set } result] {
                        APSMpReturn error "APSMpSRUnstickCorrectors(8 - sddsselect): $result"
                    }
                    APSMpStep "Set $plane plane out of range correctors delta to $delta"
                    if [catch {exec sddswput $tmpRoot.$plane.set} result] {
                        APSMpReturn error "APSMpSRUnstickCorrectors(9 - sddswput): $result"
                    }
                }
                scalar {
                    if [catch {exec sddsprocess $tmpRoot.$plane.outRange -pipe=out -nowarnings \
                                 -reedit=col,ControlName,ai/SFB:/%/:RangeErrorCALC/:CurrentAO/ \
                                 | sddsprocess -pipe=in $tmpRoot.$plane.set \
                                 -print=col,PutCommand,%s=%ld,ControlName,OutOfRange } result] {
                        APSMpReturn error "APSMpSRUnstickCorrectors(10 - sddsprocess): $result"
                    }
                    set putCommand [exec sdds2stream -col=PutCommand $tmpRoot.$plane.set]
                    APSMpStep "Set $plane out of range correctors delta to $delta..."
                    if [catch {exec cavput -list=[join $putCommand ,] -delta=factor=$delta -pend=30} result] {
                        APSMpReturn error "APSMpSRUnstickCorrectors(11 - cavput): $result"
                    }
                }
            }
        } else {
            APSMpStep "No $plane-correctors are out of range."
        }
    }
    APSMpReturn ok
}

proc APSMpRestoreCorrectorsFromSCRInfo {args} {
    return "it restores selected correctors from SCR file for datapool operation."
}

proc APSMpRestoreCorrectorsFromSCR {args} {
    set filter "*"
    set ShortFilename ""
    APSMpStep init -promptDialog  APSMpRestoreCorrectorsFromSCRInitDialog \
        -desc "Input corrector filter and cgoose SCR file" \
        -vars "filter ShortFilename"
    if ![string length $ShortFilename] {
        APSMpReturn error "APSMpRestoreCorrectorsFromSCR(Error1): No SCR file chosen."
    }
    set fullFilename /home/helios/oagData/SCR/snapshots/SR/$ShortFilename
    if {[string length $ShortFilename] && ![file exists $fullFilename]} {
        APSMpReturn error "APSMpRestoreCorrectorsFromSCR: SCR file $ShortFilename not found"
    }
    
    foreach par {filter ShortFilename} {
        append paramString "$par=\"[set $par]\" "
    }
    catch {APSLogScriptAction  \
               -procedure  APSMpRestoreCorrectorsFromSCR  \
               -action initialize \
               -state ok \
               -parameters $paramString \
           }
    set tmpFile /tmp/[APSTmpString]
    APSMpStep "Step 1: Make corrector filter."
    if ![regexp "CurrentAO" $filter] {
        set filter ${filter}*CurrentAO
    }
    if [catch {exec sddsprocess $fullFilename $tmpFile "-match=col,ControlName=$filter" } result] {
        APSMpReturn error "APSMpRestoreCorrectorsFromSCR(Error2 -- sddsprocess): $result\nThe filter may be not correct, please check it"
    }
    APSMpStep "Step 2: abort datapool controllaw."
    if [catch {exec cavget -list=DP:S:OrbitControlLaw -list=X,Y -list=SDDS.RUN} runList] {
        APSMpReturn error "APSMpRestoreCorrectorsFromSCR(Error3 -- cavget): $runList"
    }
    set XRunning [lindex $runList 0]
    set YRunning [lindex $runList 1]
    if {!$XRunning || !$YRunning} {
        APSMpReturn error "APSMpRestoreCorrectorsFromSCR(Error4 -- at least one of the datapool is not running!"
    }
    foreach plane {X Y} { 
        APSMpStep "Abort $plane plane DP controllaw."
          # if [catch {exec cavput -list=DP:S:OrbitControlLaw${plane}SDDS.ABRT=1} result] {
          #      APSMpReturn error "APSMpRestoreCorrectorsFromSCR(Error5 -- cavput  -list=DP:S:OrbitControlLaw${plane}SDDS.ABRT=1): $runList"
          #  } 
    }
# No need to disable vector mode since the values in the vector 
# can be updated by waveform file
#    APSMpStep "Step 3: disable vector mode."
#    foreach plane {x y} {
       # if [catch {APSSetCorrMode -corrMode scalar -plane $plane} result] {
       #     APSMpRetunr error "APSMpRestoreCorrectorsFromSCR(Error6 - APSSetCorrMode scalar for $plane plane): $result"
       # }
#    }
    APSMpStep "Step 4: Restore correctors from SCR"
    if [catch {exec sddsprocess $tmpFile -scan=col,Value2,ValueString,%lf -pipe=out \
                   | sddscasr -pipe -save \
                   | sddsprocess -pipe -scan=col,Value1,ValueString,%lf \
                   | sddsprocess -pipe "-define=col,Delta,Value2 Value1 -" -reedit=col,ControlName,ai/SFB:/ \
                   | sddsprocess -pipe=in $tmpFile -process=Delta,max,DeltaMax -process=Delta,min,DeltaMin } result] {
        APSMpRetunr error "APSMpRestoreCorrectorsFromSCR(Error7 - sddsprocess, sddscasr): $result"
    }
    set delta1 [expr abs([exec sdds2stream $tmpFile -par=DeltaMax])]
    set delta2 [expr abs([exec sdds2stream $tmpFile -par=DeltaMin])]
   
    if $delta1>$delta2 {
        set delta $delta1
    } else {
        set delta $delta2
    }
    lappend fileList "$tmpFile" 
    if $delta>0 {
        set steps [expr int($delta/0.5) + 1]
        if [catch {exec sddsprocess $tmpFile $tmpFile.0 \
                       "-redefine=col,Delta,Value2 Value1 - $steps /" } result] {
            APSMpReturn error "APSMpRestoreCorrectorsFromSCR(Error8 - sddsprocess): $result"
        }
        for {set i 1} {$i<=$steps} {incr i} {
            if [catch {exec sddsprocess $tmpFile.0 -pipe=out \
                           "-redefine=col,Value,Delta $i * Value1 +" \
                           | sddsprocess -pipe  \
                           -reprint=col,ValueString,%lf,Value \
                           | tee $tmpFile.$i \
                           | sddsconvert -pipe=in $tmpFile.a$i } result] { 
                APSMpReturn error "APSMpRestoreCorrectorsFromSCR(Error8 - step $i: sddsprocess, sddscasr): $result"
            }
            lappend fileList $tmpFile.$i
        }
    } else {
        APSMpStep "All selected correctors have desired values, no need to restore."
    }
    APSMpReturn ok
    APSMpStep "Step 6: enable vector mode."
    foreach plane {x y} {
        if [catch {APSSetCorrMode -corrMode vector -plane $plane} result] {
             APSMpReturn error "APSMpRestoreCorrectorsFromSCR(Error10 - APSSetCorrMode vector for $plane plane): $result"
         }
    }
    APSMpStep "Step 7: resume datapool orbit correction."
    if [catch {exec cavget -list=DP:S:OrbitControlLaw -list=X,Y -list=.CMND=1} runList] {
        APSMpReturn error "APSMpRestoreCorrectorsFromSCR(Error3 -- cavget): $runList"
    }
    APSMpReturn ok
}

proc APSMpRestoreCorrectorsFromSCRInitDialog {frame} {
    global  APSMpRestoreCorrectorsFromSCR
    
    APSAddSCRDialog .scr -parent $frame -system SR \
        -arrayName APSMpRestoreCorrectorsFromSCR \
        -defaultFile $APSMpRestoreCorrectorsFromSCR(ShortFilename)
    APSLabeledEntry .filter -parent $frame -label "Corrector Filter:" -width 80 -textVariable APSMpRestoreCorrectorsFromSCR(filter) \
        -contextHelp "filter for correctors to be restored."
    
    
}

proc APSMpRampCorrVectorToSCRInfo {args} {
    return "ramp selected correctors by sectors to selected SCR in vector mode." 
}

proc APSMpRampCorrVectorToSCRInitDialog {frame} {
    global APSMpRampVectorToSCR
    APSRadioButtonFrame .plane -parent $frame -orientation horizontal \
      -variable APSMpRampCorrVectorToSCR(plane) \
      -label "Plane: " \
      -buttonList "h v both" -valueList "h v both"  \
      -contextHelp "ramp correctors in the selected plane (or both planes if both is selected)"
    APSLabeledEntry .delta -parent $frame -label "Correcto Change Limit in each Step (A): " \
      -width 20 -textVariable APSMpRampCorrVectorToSCR(stepSizeLimit) \
      -contextHelp "the maximum changing of corrector current in each ramp step."
    for {set sector 1} {$sector<=40} {incr sector} {
        lappend buttonList [format %02ld $sector]
        lappend varList APSMpRampCorrVectorToSCR(S$sector)
    }
    APSCheckButtonFrame .sector -parent $frame -label "Choose corrector sectors:" \
      -orientation vertical -limitPerRow 4  -allNone 1 \
      -buttonList $buttonList -variableList $varList
    
    set APSMpRampCorrVectorToSCR(ShortFilename) ""
    APSAddSCRDialog .scr -parent $frame -system SR \
      -arrayName APSMpRampCorrVectorToSCR \
      -defaultFile $APSMpRampCorrVectorToSCR(ShortFilename)
}

#This procedure has been placed in the attic
proc APSMpRampCorrVectorToSCR {args} {
    set plane h
    set stepSizeLimit 5
    set ShortFilename ""
    for {set sector 1} {$sector<=40} {incr sector} {
        set S$sector 0
    }

    APSMpStep init -promptDialog APSMpRampCorrVectorToSCRInitDialog \
      -vars {plane stepSizeLimit ShortFilename S1 S2 S3 S4 S5 S6 S7 S8 S9 S10 S11 S12 S13 S14 S15 S16 S17 S19 S20 \
           S21 S22 S23 S24 S25 S26 S27 S28 S29 S30 S31 S32 S33 S34 S35 S36 S37 S38 S39 S40}
    
    if ![string length $ShortFilename] {
        APSMpReturn "No SCR is chosen."
    }
    set sectorList ""
    set opt ""
    for {set sector 1} {$sector<=40} {incr sector} {
        if [set S$sector] {
            lappend sectorList $sector
            if [string length $opt] {
                append opt "DeviceName \"S${sector}*\" strmatch || "
            } else {
                append opt "DeviceName \"S${sector}*\" strmatch "
            }
        }
    }
    
    if ![llength $sectorList] {
        APSMpReturn error "No sectors were chosen for ramping corrector vector."
    }
    if {[llength $sectorList]==40} {
        set opt ""
    }
   
    if {$plane=="both"} {
        set planeList {h v} 
        set PlaneList {H V}
    } else {
        set planeList $plane
        set PlaneList [string toupper $plane]
    }

    set scrDir /home/helios/oagData/SCR/snapshots/SR
    set wfDir /home/helios/oagData/sr/orbitControllaw/waveforms
    foreach plane $planeList Plane $PlaneList {
        set tmpRoot /tmp/${plane}[APSTmpString]
        if [catch {APSSRCheckCorrectorMode -plane $plane} corrMode] {
            APSMpReturn error "APSMpRampCorrVectorToSCR(1 - APSSRCheckCorrectorMode): $corrMode"
        }
        if {$corrMode!="vector"} {
            APSMpReturn error "$plane is not in vector mode, return without any action."
        }
        if [catch {exec sddsprocess $scrDir/$ShortFilename  -pipe=out \
                     "-match=col,ControlName=SFB:*${Plane}*:CurrentAO" \
                     -scan=col,RampToValue,ValueString,%lf \
                     | sddsconvert -pipe -retain=col,ControlName,RampToValue \
                     | sddscasr -pipe -save -pend=100 \
                     | sddsprocess -pipe -match=col,CAError=n \
                     -scan=col,CurrentValue,ValueString,%lf \
                     | sddsprocess -pipe \
                     "-define=col,Diff,RampToValue CurrentValue -" \
                     "-define=col,AbsDiff,RampToValue CurrentValue - abs" \
                     -edit=col,DeviceName,ControlName,%/SFB://%/:CurrentAO// \
                     | sddsprocess -pipe=in $tmpRoot.current \
                     "-process=AbsDiff,max,MaxDiff" } result] {
            APSMpReturn error "APSMpRampCorrVectorToSCR(2 - process SCR and read current values): $result"
        }
        set maximumChange [exec sdds2stream $tmpRoot.current -par=MaxDiff -page=1]
        set steps [format %.0f [expr abs($maximumChange)/$stepSizeLimit +1]]
        if [string length $opt] {
            if [catch {exec sddsconvert $wfDir/${plane}corrInfo.sdds -pipe=out \
                         -rename=par,DeltaWaveformPV=WaveformPV \
                         | sddsxref -pipe $tmpRoot.current -match=DeviceName -nowarnings -fillIn \
                         -take=Diff \
                         | sddsprocess -pipe=in $tmpRoot.ramp \
                         "-define=col,Waveform,$opt ? Diff $steps / : 0 $ " } result] {
                APSMpReturn error "APSMpRampCorrVectorToSCR(3 - sddsxref): $result"
            }
        } else {
            if [catch {exec sddsconvert $wfDir/${plane}corrInfo.sdds -pipe=out \
                         -rename=par,DeltaWaveformPV=WaveformPV \
                         | sddsxref -pipe $tmpRoot.current -macth=DeviceName -nowarnings -fillIn \
                         -take=Diff \
                         | sddsprocess -pipe=in $tmpRoot.ramp \
                         "-define=col,Waveform,Diff $steps /" } result] {
                APSMpReturn error "APSMpRampCorrVectorToSCR(3 - sddsxref): $result"
            }
        }
        if {0} {
            APSMpStep "Ramping $plane corrector vector ..."
            for {set i} {$i<=$steps} {incr i} {
                if [catch {exec sddswput $tmpRoot.ramp } result] {
                    APSMpReturn error "APSMpRampCorrVectorToSCR(4 - sddswput): $result"
                }
            }
            APSMpStep "Ramping $plane corrector vector done."
        }
    }
    

   
    APSMpReturn ok
}
