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

# $Log: not supported by cvs2svn $
# Revision 1.73  2011/06/30 19:19:32  shang
# removed special calibration factor for ID34 since it is the same as others now.
#
# Revision 1.72  2011/06/28 21:15:29  shang
# doubled the ID34 calibration factor because its half-length is doubled.
#
# Revision 1.71  2011/06/03 17:35:06  shang
# added displaying the datapool steering controllaw options in a pop-up window when starting controllaw.
#
# Revision 1.70  2010/04/08 14:47:41  shang
# added error message returned from APSCheckAndUpdateLocalSteeringConfigs to alert box to help debugging.
#
# Revision 1.69  2010/02/16 20:58:27  shang
# added checking BPLD limits to "Apply" setpoint button
#
# Revision 1.68  2009/10/14 05:20:27  lemery
# Changed default interval to 1.5 (until S24A:H3 is fixed).
#
# Revision 1.67  2009/10/13 23:13:28  lemery
# Changed the steering limit of correctors to be consistent with
# corrector limits in global orbit correction.
#
# Revision 1.66  2006/08/08 23:49:50  emery
# Changed the APPLY/START button to just a START button. (Shang.)
#
# Revision 1.65  2006/08/02 00:59:39  emery
# Shang's changes: use -infiniteLoop argument to sddscontrollaw.
#
# Revision 1.64  2006/02/07 21:06:24  shang
# fixed bug [lsearch $gainList "?"] which should be [lsearch $gainList "\\?"] instead.
#
# Revision 1.63  2005/10/24 20:48:14  shang
# added TEST button to sector dialogs.
#
# Revision 1.62  2005/10/12 05:42:25  emeryS
# Added timeDebug commandline argument so that the time stamps
# added in the previous two versions appear under this debug mode.
# Removed bell from status call.
# Fixed medm commands for displaying steering runcontrol PVs.
#
# Revision 1.61  2005/10/11 23:22:41  emery
# Moved logMessage after checking correctors.
#
# Revision 1.60  2005/10/11 22:58:56  emery
# Added more time stamped messages.
# Increased condition number threshold from 1e3 to 2e3.
# Added newline at end.
#
# Revision 1.59  2005/10/11 22:31:40  emery
# Added time stamps to all statuCallback arguments.
#
# Revision 1.58  2005/09/30 21:45:04  shang
# added automatically updating controllaw files when sector button is clicked.
#
# Revision 1.57  2005/08/22 14:44:35  shang
# fixed a bug in previous where the controllawDir variable was missing for checking controllaw files.
#
# Revision 1.56  2005/08/22 14:34:09  shang
# added checking if controllaw directory and controllaw files exits
#
# Revision 1.55  2005/05/31 14:11:53  shang
# added CompareSetpointsWithBPLDLimit proc to check if the steering will trip BPLD system.
#
# Revision 1.54  2004/09/28 17:57:07  emery
# Moved the evaluation of IDSectorList before the creation
# of widgets.
#
# Revision 1.53  2004/09/24 17:08:44  emery
# Removed special case for sector 11 for forcing a P0 steering.  Wrapped
# the code for checking for "in use" ID xray bpms (most recent change in
# cvs) inside of an if statement checking that the sector in question is
# expected to have ID xray bpms. Otherwise the cavget command for a
# non-light source sector, say, sector 39, would timeout on S39ID xray
# bpms, and give a CA error.
#
# Revision 1.52  2004/07/26 21:18:23  shang
# added a reminder if X-ray BPMs in the chosen sector are in used for orbit
# correction
#
# Revision 1.51  2002/10/09 20:46:48  emery
# Removed extraneous return command between a tranfer and the
# sddscontrollaw in StartControllaw
#
# Revision 1.50  2002/10/09 10:19:19  emery
# Added status messages for bpms setpoint changes.
#
# Revision 1.49  2002/10/09 06:51:40  shang
# replaced APSTransferVectorAdjust by APSRefreshVectorAdjust which takes shorter time
#
# Revision 1.48  2002/10/07 17:03:43  soliday
# Restored the auto_path to the correct value.
#
# Revision 1.47  2002/10/06 21:13:31  emery
# Added status message before the call to APSTransferVectorAdjust
# is made.
#
# Revision 1.46  2002/10/06 05:53:53  emery
# Simplified the coding of the cavput in ApplySetpoint.
# Replaced the string values PSCU and Diag with
# Maintenance and Operation.
#
# Revision 1.45  2002/09/04 21:47:41  shang
# added running in IOC by using datapool bpms feature
#
# Revision 1.44  2002/02/27 23:54:39  emery
# Added controlQuantityDefinition option to sddscontrollaw to that the
# appropriate definition file will be selected based on the
# setpoint sources (Diag or PSCU) of correctors.
#
# Revision 1.43  2002/02/26 21:07:18  emery
# Changed default steering interval to 0.5 sec since we are now
# using speedy corrector PVs.
#
# Revision 1.42  2001/05/24 17:04:12  emery
# Changed R12, R34 for special case of sector 34 where
# P0 bpms are 2.5 m apart.
#
# Revision 1.41  1999/04/29 15:22:09  emery
# Now that the P0 and P1 PVs are attached to the respective buttons,
# the assignment of the light source coordinate coefficients is simplified.
#
# Revision 1.40  1999/02/15 16:24:01  emery
# Made variable errorCode global.
#
# Revision 1.39  1999/02/05 22:28:16  emery
# Updated the section where different matrix elements
# are assigned to ID because of using new bpms.
#
# Revision 1.38  1999/01/28 08:51:24  emery
# Removed another extraneous puts statement.
#
# Revision 1.37  1999/01/28 08:50:33  emery
# Removed extraneous puts stderr statement.
#
# Revision 1.36  1999/01/28 08:49:43  emery
# Fixed mix-up with what 0 and 1 values for P0Sector means.
#
# Revision 1.35  1999/01/28 08:22:37  emery
# Used local steering directory links to determine
# which BPMS are used for steering.
#
# Revision 1.34  1999/01/20 15:25:07  borland
# Added -fastClick 1 option to the In/Out/Up/Down buttons on the steering
# dialog.
#
# Revision 1.33  1998/10/18 20:58:01  borland
# Added P0 support.  Uses new data directory.
#
# Revision 1.32  1998/05/21 20:55:21  emery
# Changed data file for light source point bpm
# used in DoTransfer procedure.
#
# Revision 1.31  1997/08/21 04:54:25  emery
# Made variable includeSourcePoints a command line argument.
#
# Revision 1.30  1997/07/24 23:09:36  emery
# By default only the light sources will have the sector button
# enabled. The command line option allowAllSectors
# may be used to allow steering in all sectors.
#
# Revision 1.29  1997/04/25 19:38:45  emery
# Added -lineLimit 1024 to APSExecLog
#
# Revision 1.28  1997/04/21 18:25:53  emery
# Changed button label START to APPLY/START
#
# Revision 1.27  1997/03/26 17:00:38  emery
# Added more context help messages.
#
# Revision 1.26  1997/03/21 22:31:36  emery
# Added accumulator output boxes for the x and y
# steering.
#
# Revision 1.25  1997/03/03 15:39:07  emery
# Added continuation char. at end of -unixCommand option
#
# Revision 1.24  1997/03/03 15:27:08  emery
# Added name to sddscontrollaw exec log.
#
# Revision 1.23  1997/03/01 22:28:38  borland
# Removed previous changes.  Now start controllaw "locally" as before in
# an APSExecLog window.  Sets averaging for BPMs being controlled.  Uses
# new directory and names for matrix and test files.
#
# Revision 1.21  1996/12/13 18:17:29  borland
# Added option to not transfer source point orbit to setpoints.
#
# Revision 1.20  1996/10/08 08:05:48  emery
# Changed the labeling of the DECR INCR buttons to
# read UP DOWN IN OUT for x xp y yp adjustments.
# Adjusted borderwith of LabeledOutputFrame boxes and the
# spacing of the UP DOWN IN OUT buttons to match the
# spacing of the individual labeled entry.
# Replaced $var=="" type statements with ![string length $var]
# statements.
#
# Revision 1.19  1996/10/08 01:07:35  borland
# Added -allowTransfer and -adjustOnly options for control of what the user
# is allowed to do.
#
# Revision 1.18  1996/10/07 15:44:13  borland
# Added widgets to allow transferring all setpoints for x, y, or both.
#
# Revision 1.17  1996/09/28 05:32:10  emery
# Use mswAve bpm PVs for displaying the AdjustedCC
# and ErrorCC PV in the dialog box.
# Use matrices that use msAVe PVs for the bpms. The matrix
# directory is /home/helios/oagData/controllaw/ID/P1HV2.
# The ioc averaging is enabled for the bpms in the local correection.
# Use caputs to suspend the SR orbit controllaw.
#
# Revision 1.16  1996/09/28 01:21:08  emery
# Restored input variables interval and gain in the Options widget.
#
# Revision 1.15  1996/09/28 00:40:36  emery
# Added logMessage commands to log the instances of
# starting sddscontrollaw of the various ID sections.
# Force the SR orbit correction runcontrol PVs in suspend
# mode, therefore some of the testing of the SR orbit
# runcontrol has been removed in this version.
#
# Revision 1.14  1996/09/18 20:26:23  borland
# Removed disabling of start buttons.  Replaced with test of whether
# orbit controllaw is running.
#
# Revision 1.13  1996/09/18 20:09:41  borland
# Added APSDisableButton for Start button, to prevent use of screen for
# anything other than changing steering setpoints.
#
# Revision 1.12  1996/09/12 18:35:38  emery
# Changed controllaw data directories to
# /home/helios/oagData/controllaw/ID/P1 for P1 steering and
# /home/helios/oagData/controllaw/ID/P2 for P2 steering and
#
# Revision 1.11  1996/09/12 06:05:48  emery
# Removed the "ID" part of the button labels to make the main
# window narrower, and to make a more consitent labeling
# across SR BPM tcl interfaces.
#
# Revision 1.10  1996/08/19 06:21:49  emery
# Simplified interface so that the function of the MORE button
# (i.e. releaving AdjustedCC and ErrorCC values) is always
# in effect. ALso, the delta x x' y y' steering dialog is
# now a permanent part of the ID dialog box.
#
# Revision 1.9  1996/08/03 05:21:36  emery
# Added the three dots in a button text to indicate a new window.
#
# Revision 1.8  1996/07/02  11:24:18  emery
# Added x x' y y' increment dialog box for changing
# setpoint variables. One still has to press Apply button
# to send the values to EPICS.
#
# Revision 1.7  1996/07/02  08:09:52  emery
# Corrected the -deltaLimit syntax
#
# Revision 1.6  1996/07/02  05:24:35  emery
# Added a -deltalimit=2 option value for sddscontrollaw
#
# Revision 1.5  1996/05/08  06:52:08  emery
# Added buttons MORE LESS and TRANSFER to the dialog boxes.
# Added offset, adjusted, and error values of the bpms.
# These values appear after pressing the MORE button.
#
# Revision 1.4  1996/05/06  19:16:36  borland
# Set tcl_precision to 6 to avoid ridiculously long displays of setpoint
# values.
#
# Revision 1.3  1996/05/05  08:29:41  emery
# Added a callback to the APSExecLog of the sddscontrnollaw so that
# the user will be notified when the sddscontrollaw is completed.
#
# Revision 1.2  1996/05/05  05:09:34  emery
# commented out a personal auto_path command
#
# Revision 1.1  1996/05/04  13:48:42  emery
# First commit of SRIDSteering, an interface for steering the SR beam
# in any ID section.
#

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


set args $argv
set adjustOnly 0
set allowTransfer 1
set allowAllSectors 0
set includeSourcePoints 0
# timeDebug has values of 0 or 1
set timeDebug 0
APSStrictParseArguments {adjustOnly allowTransfer allowAllSectors includeSourcePoints timeDebug}
set CVSRevisionAuthor "\$Revision: 1.74 $ \$Author: shang $"


set APSExecIDList ""
proc KillAPSExecLogs {} {
    global APSExecIDList
    forech id $APSExecIDList {
        APSExecLogAbort -id $id -destroy 1
    }
}

APSApplication . -name SRIDSteering \
  -overview "SRIDSteering provides convenience controls for ID steering using sddscontrollaw."
dp_atexit append KillAPSExecLogs

set tcl_precision 6

set IDSteeringStatus Ready.
APSScrolledStatus .status -parent .userFrame -width 60\
        -textVariable IDSteeringStatus 

proc SetIDSteeringStatus {text} {
    global IDSteeringStatus
    set IDSteeringStatus $text
    update
#    bell
}

proc MakeSectorsWidget {widget args} {
    global sector allowAllSectors IDSectorList

    set parent ""
    APSParseArguments {parent}

    set w $parent$widget
    APSFrame $widget -parent $parent \
      -label "Storage Ring ID selection for steering." \
      -contextHelp {ID selection frame} 

    if !$allowAllSectors {
        set sectorList $IDSectorList 
    }
    
    for {set quad 1} {$quad<5} {incr quad} {
        set start [expr ($quad-1)*10+1]
        set end   [expr $start+9]
        set buttonList {}
        set valueList {}
        APSFrame .sector$quad -parent $w.frame 
        $w.frame.sector$quad.frame configure -relief flat
        for {set sector $start} {$sector<=$end} {incr sector} {
            set cbLabel $sector
            if {$sector<10} {set cbLabel "0$sector"}
            APSButton .sector$sector -parent $w.frame.sector$quad.frame \
              -text "$cbLabel" \
              -command "IDSteeringDialog -sector $sector" \
              -contextHelp "Brings up dialog box for ID$cbLabel steering."
            if !$allowAllSectors {
                if [expr -1 == [lsearch $sectorList $sector]] {
                    APSDisableButton $w.frame.sector$quad.frame.sector$sector.button
                }
            }
        }
    }
}

proc MakeOptionWidget {widget args} {
    global sector tolerance timeLimit statusCallback
    global interval gain 

    set parent ""
    APSParseArguments {parent}

    set w $parent$widget
    APSFrame $widget -parent $parent -label "Options" \
        -contextHelp "Options in IO steering."
    
    APSFrame .parameters -parent $w.frame  -packOption  {-side top}
    set w $parent$widget.frame.parameters
    $w.frame configure -relief flat

    APSFrame .misc -parent $w.frame -packOption  {-side left}
    $w.frame.misc.frame configure -relief flat
    APSLabeledEntry .timeLimit -parent $w.frame.misc.frame -label "Time limit (sec)" \
      -textVariable timeLimit  -width 8 \
      -contextHelp "Time allowed for sddscontrollaw to steer the beam to the desired setpoints."
    APSLabeledEntry .interval -parent $w.frame.misc.frame -label "Interval (sec)" \
      -textVariable interval  -width 8 \
      -contextHelp "Time interval between sddscontrollaw interations."
    APSLabeledEntry .gain -parent $w.frame.misc.frame -label "Gain" \
      -textVariable gain  -width 8 \
      -contextHelp "Fraction of correction to be applied at every iterations. Use a value of 0 for safe debugging."
}

proc MakeTransferWidget {widget args} {
    set parent ""
    APSParseArguments {parent}

    set w $parent$widget
    APSFrame $widget -parent $parent -label "Orbit transfer to setpoints" \
        -contextHelp "Buttons to transfer x, y, or both orbits to setpoint values.  Not part of ID steering per se, but useful for related operations."
    
    APSFrame .parameters -parent $w.frame  -packOption  {-side top}
    set w $parent$widget.frame.parameters
    $w.frame configure -relief flat

    APSFrame .misc -parent $w.frame -packOption  {-side left}
    $w.frame.misc.frame configure -relief flat
    APSButton .xferAdjValues -parent $w.frame.misc.frame -text "Transfer both" \
      -command {DoTransfer -msType mswAve -plane both -includeSourcePoints $includeSourcePoints} \
      -contextHelp "Transfers present adjusted orbit values to steering setpoints, in order to lock in the present orbit.  Not part of ID steering per se, but useful for related operations."
    APSButton .hxferAdjValues -parent $w.frame.misc.frame -text "Transfer x" \
      -command {DoTransfer -msType mswAve -plane x -includeSourcePoints $includeSourcePoints} \
      -contextHelp "Transfers present adjusted x orbit values to steering setpoints, in order to lock in the present horizontal orbit.  Not part of ID steering per se, but useful for related operations."
    APSButton .vxferAdjValues -parent $w.frame.misc.frame -text "Transfer y" \
      -command {DoTransfer -msType mswAve -plane y -includeSourcePoints $includeSourcePoints} \
      -contextHelp "Transfers present adjusted y orbit values to steering setpoints, in order to lock in the present vertical orbit.  Not part of ID steering per se, but useful for related operations."
    global includeSourcePoints
    APSRadioButtonFrame .mode -parent $w.frame.misc.frame -label "Include source points" \
      -variable includeSourcePoints -buttonList "Yes No" -valueList "1 0" -orientation horizontal \
      -contextHelp "Determines whether setpoints for BPMs used by the fine orbit correction are also changed.  These setpoints are important in pinning down the source points for BM and ID lines, and should not normalling be included in the transfer."
}

proc DisplaySteeringControllawOption {args} {
    global env
    set runControlPV DP:S:SteeringSDDS
    set option [APScagetTextFromWaveform -pvName $runControlPV.OPTN]
    APSInfoWindow .infosteering -name "Steering Controllaw Options" -width 70 -infoMessage "$option"  \
	-packOption "-side top -fill both" -copyable 1
    
}
proc DoTransfer {args} {
    set msType ""
    set plane ""
    set includeSourcePoints 0
    APSStrictParseArguments {msType plane includeSourcePoints} 

    set dataDir /home/helios/oagData/sr/localSteering
    set excludeConfigFile ""
    SetIDSteeringStatus "Transferring present adjusted orbit to setpoints for $plane plane(s)"
    if !$includeSourcePoints {
        if [string compare $plane both]==0 {
            lappend excludeConfigFile \
              $dataDir/userLightSources.config \
              $dataDir/userLightSources.config
        } else {
            set excludeConfigFile $dataDir/userLightSources.config
        }
        SetIDSteeringStatus "Setpoints for ID/BM source points are not being changed."
    } else {
        SetIDSteeringStatus "Setpoints for ID/BM source points *are* being changed."
    }
    
    if [catch {APSSRTransferBPMAdjustedValues  -msType $msType -plane $plane \
                 -statusCallback SetIDSteeringStatus -excludeConfigFileList $excludeConfigFile} result] {
        SetIDSteeringStatus "$result"
        return 
    }
    if [catch {APSTransferVectorAdjust -coord $plane} result] {
        SetIDSteeringStatus "$result"
        return 
    }
    SetIDSteeringStatus "Transfer done."
}


proc IDSteeringDialog {args} {
    global P0Sector IDSectorList timeDebug

    set sector ""
    APSParseArguments {sector}
    if ![string length $sector] {
        APSAlertBox [APSUniqueName .] -errorMessage \
          "No sector variable specified in IDSteeringDialog"
        return 1
    }
    if {$sector==29} {
        set answer [APSMultipleChoice .alert29 -width 100 -type warning -question "*** There shall be no steering of ID29 requested by the users. ***\n\nIf this steering is requested by users during a run, then exit this steering, press \"exit\" button.\n\nSteering by machine studies who understand the large vertical beam size of the IEX photon beam in relation to the vertical aperture limits is allowed, press \"continue\" button to steer ID29." -labelList {exit continue} -returnList {exit continue} -name "Warning steering IEX"]
        switch $answer {
            exit {
                return
            }
            continue {
            }
        }
    }
    if [catch {APSCheckAndUpdateLocalSteeringConfigs -steeringType ID -sector $sector -statusCallback SetIDSteeringStatus} result] {
        SetIDSteeringStatus "$result"
        APSAlertBox [APSUniqueName .] -errorMessage "Error in updating local steering configs for sector $sector: $result"
        return 1
    }
    global waveformFile
    if $P0Sector($sector) {
        set digit 0
        set waveformFile($sector) p0BpmInfo.sdds
    } else {
        set digit 1
        set waveformFile($sector) rfBpmInfo.sdds
    }

    # Check whether ID xray bpms are already in use in global steering.
    # In that case we can't proceed with local steering safely.
    # In most cases local steering runs within a pem, which
    # causes the previous configuration of global correction 
    # to resume immediately after the local steering.
    if {-1<[lsearch $IDSectorList $sector]} {
        set checkPVList [list S${sector}ID:P1:ms:x:InUseBO S${sector}ID:P2:ms:x:InUseBO \
                           S${sector}ID:P1:ms:y:InUseBO S${sector}ID:P2:ms:y:InUseBO]
        if [catch {exec cavget -list=[join $checkPVList ,] -pend=30 -numerical} checkPVValues] {
            APSAlertBox .alert -errorMessage "$checkPVValues"
            exit
        }
        set inUsePVList ""
        foreach pv $checkPVList val $checkPVValues {
            if {$val=="?"} {
                APSAlertBox .alert -errorMessage "Unable to read $pv value."
                exit
            }
            if {$val} {
                regexp {(.*):InUseBO} $pv a b
                lappend inUsePVList $b
            }
        }
        if [llength $inUsePVList] {
            set answer [APSMultipleChoice [APSUniqueName .] -name "Warning" \
                          -question "The X-ray BPMs [join $inUsePVList " "]  are in the orbit correction configuration, please switch the orbit controllaw to defaultDP before steering and remove these BPMs from orbit correction configuration after steering." -returnList {Proceed Quit} \
                          -labelList {Proceed Quit} ]
            if {$answer=="Quit"} {
                exit
            }
        }
    }
    #check
    
    global BPx BPy APx APy
    global statusCallback adjustOnly allowTransfer errorCode

    set dialogFrame .dialogID$sector.userFrame
    APSDialogBox .dialogID$sector  \
      -name "ID$sector Dialog" \
      -contextHelp "Dialog box for steering in ID$sector."

    set Sn $sector
    set Sn1 [exec rpnl "$Sn 1 + 40 > pop ? 40 - : \$"]
    if {[pv linkw \
           [list BPx($sector) BPy($sector) APx($sector) APy($sector)] \
           [list S${Sn}B:P${digit}:ms:x:SetpointAO S${Sn}B:P${digit}:ms:y:SetpointAO \
              S${Sn1}A:P${digit}:ms:x:SetpointAO S${Sn1}A:P${digit}:ms:y:SetpointAO ]] != 0} {
        APSAlertBox .alert -errorMessage "linkw error $errorCode"
        exit
    }
    if {[pv umon BPx($sector)] != 0} {
        APSAlertBox .alert -errorMessage "umon error $errorCode"
        exit
    }
    if {[pv umon BPy($sector)] != 0} {
        APSAlertBox .alert -errorMessage "umon error $errorCode"
        exit
    }
    if {[pv umon APx($sector)] != 0} {
        APSAlertBox .alert -errorMessage "umon error $errorCode"
        exit
    }
    if {[pv umon APy($sector)] != 0} {
        APSAlertBox .alert -errorMessage "umon error $errorCode"
        exit
    }
    
    APSFrame .parameters -parent $dialogFrame -packOption "-side left"
    $dialogFrame.parameters.frame configure -relief flat
    APSFrame .setpoints -parent $dialogFrame.parameters.frame -packOption "-side left" \
      -label "Setpoints for bpms"  -width 10 \
      -contextHelp "Enter setpoints for the four bpm readbacks of ID$sector"
    $dialogFrame.parameters.frame.setpoints.frame configure -relief flat
    APSLabeledEntry .bp1x \
      -parent $dialogFrame.parameters.frame.setpoints.frame \
      -label "S${Sn}B:P${digit}:x (mm)"  -width 10 \
      -textVariable BPx($sector) \
      -contextHelp "Enter value for S${Sn}B:P${digit}:ms:x:SetpointAO to be applied. These values change when one of the associated coordinate delta button is pressed."
    APSLabeledEntry .bpy \
      -parent $dialogFrame.parameters.frame.setpoints.frame \
      -label "S${Sn}B:P${digit}:y (mm)"  -width 10 \
      -textVariable BPy($sector) \
      -contextHelp "Enter value for S${Sn}B:P${digit}:ms:y:SetpointAO to be applied. These values change when one of the associated coordinate delta button is pressed."
    APSLabeledEntry .apx \
      -parent $dialogFrame.parameters.frame.setpoints.frame \
      -label "S${Sn1}A:P${digit}:x (mm)"  -width 10 \
      -textVariable APx($sector) \
      -contextHelp "Enter value for S${Sn1}A:P${digit}:ms:x:SetpointAO to be applied. These values change when one of the associated coordinate delta button is pressed."
    APSLabeledEntry .apy \
      -parent $dialogFrame.parameters.frame.setpoints.frame \
      -label "S${Sn1}A:P${digit}:y (mm)"  -width 10 \
      -textVariable APy($sector) \
      -contextHelp "Enter value for S${Sn1}A:P${digit}:ms:y:SetpointAO to be applied. These values change when one of the associated coordinate delta button is pressed."

   # APSButton .update -parent .dialogID$sector.buttonRow -text "UpdateFFAdjust"  -command "UpdateFFAdjust -plane both" -contextHelp  "Updates FF adjust waveform from scalar readings."
    APSButton .apply -parent .dialogID$sector.buttonRow -text APPLY  -command "catch {ApplySetpoints -sector $sector}" -contextHelp  "Applies above setpoint values to setpoint PVs of ID$sector bpms."

    if !$adjustOnly {
     #   APSButton .start -parent .dialogID$sector.buttonRow -text "START"  -command "StartControllaw -sector $sector" -contextHelp  "Starts sddscontrollaw on ID$sector. Note that the cancel button in this button row does not cancel the sddscontrollaw process."
    }
    if $allowTransfer {
    #    APSButton .transfer -parent .dialogID$sector.buttonRow -text TRANSFER  -command "TransferValues -sector $sector" -contextHelp  "Transfers present adjusted bpm values to the setpoint values for ID $sector."
    }
   # APSButton .test -parent .dialogID$sector.buttonRow -text TEST  -command "APSCheckLocalSteeringTests -sector $sector -steeringType ID -statusCallback SetIDSteeringStatus" -contextHelp  "Check the tests for ID $sector."
    #APSButton .opt -parent .dialogID$sector.buttonRow -text OPTION  -command "DisplaySteeringControllawOption" -contextHelp  "display the steering controllaw options."
    if {[pv linkw \
           [list BPxOffset($sector) BPyOffset($sector) APxOffset($sector) APyOffset($sector)] \
           [list S${Sn}B:P${digit}:ms:x:OffsetAO S${Sn}B:P${digit}:ms:y:OffsetAO \
              S${Sn1}A:P${digit}:ms:x:OffsetAO S${Sn1}A:P${digit}:ms:y:OffsetAO ]] != 0} {
        APSAlertBox .alert -errorMessage "$errorCode"
        exit
    }
    if {[pv linkw \
           [list BPxAdjusted($sector) BPyAdjusted($sector) APxAdjusted($sector) APyAdjusted($sector)] \
           [list S${Sn}B:P${digit}:mswAve:x:AdjustedCC S${Sn}B:P${digit}:mswAve:y:AdjustedCC \
              S${Sn1}A:P${digit}:mswAve:x:AdjustedCC S${Sn1}A:P${digit}:mswAve:y:AdjustedCC ]] != 0} {
        APSAlertBox .alert -errorMessage "$errorCode"
        exit
    }
    if {[pv linkw \
           [list BPxError($sector) BPyError($sector) APxError($sector) APyError($sector)] \
           [list S${Sn}B:P${digit}:mswAve:x:ErrorCC S${Sn}B:P${digit}:mswAve:y:ErrorCC \
              S${Sn1}A:P${digit}:mswAve:x:ErrorCC S${Sn1}A:P${digit}:mswAve:y:ErrorCC ]] != 0} {
        APSAlertBox .alert -errorMessage "$errorCode"
        exit
    }
    if {[pv umon BPxOffset($sector)] != 0} {
        APSAlertBox .alert -errorMessage "$errorCode"
        exit
    }
    if {[pv umon BPyOffset($sector)] != 0} {
        APSAlertBox .alert -errorMessage "$errorCode"
        exit
    }
    if {[pv umon APxOffset($sector)] != 0} {
        APSAlertBox .alert -errorMessage "$errorCode"
        exit
    }
    if {[pv umon APyOffset($sector)] != 0} {
        APSAlertBox .alert -errorMessage "$errorCode"
        exit
    }

    if {[pv umon BPxAdjusted($sector)] != 0} {
        APSAlertBox .alert -errorMessage "$errorCode"
        exit
    }
    if {[pv umon BPyAdjusted($sector)] != 0} {
        APSAlertBox .alert -errorMessage "$errorCode"
        exit
    }
    if {[pv umon APxAdjusted($sector)] != 0} {
        APSAlertBox .alert -errorMessage "$errorCode"
        exit
    }
    if {[pv umon APyAdjusted($sector)] != 0} {
        APSAlertBox .alert -errorMessage "$errorCode"
        exit
    }

    if {[pv umon BPxError($sector)] != 0} {
        APSAlertBox .alert -errorMessage "$errorCode"
        exit
    }
    if {[pv umon BPyError($sector)] != 0} {
        APSAlertBox .alert -errorMessage "$errorCode"
        exit
    }
    if {[pv umon APxError($sector)] != 0} {
        APSAlertBox .alert -errorMessage "$errorCode"
        exit
    }
    if {[pv umon APyError($sector)] != 0} {
        APSAlertBox .alert -errorMessage "$errorCode"
        exit
    }

    APSFrame .moreParameters -parent $dialogFrame -packOption "-side left"
    $dialogFrame.moreParameters.frame configure -relief flat
    APSLabeledOutputFrame .offset -parent $dialogFrame.moreParameters.frame \
      -label Offsets -packOption "-side left" \
      -variableList "BPxOffset($sector) BPyOffset($sector) APxOffset($sector) APyOffset($sector)"\
      -orientation vertical -width 10 \
      -contextHelp "Electrical offset of ID$sector bpms."
    $dialogFrame.moreParameters.frame.offset.frame configure -relief flat

    APSLabeledOutputFrame .adjusted -parent $dialogFrame.moreParameters.frame \
      -label "Adjusted" -packOption "-side left" \
      -variableList "BPxAdjusted($sector) BPyAdjusted($sector) APxAdjusted($sector) APyAdjusted($sector)"\
      -orientation vertical -width 10 \
      -contextHelp "Adjusted readbacks of ID$sector bpms: raw readback - offsets."
    $dialogFrame.moreParameters.frame.adjusted.frame configure -relief flat

    APSLabeledOutputFrame .error -parent $dialogFrame.moreParameters.frame \
      -label "Error" -packOption "-side left" \
      -variableList "BPxError($sector) BPyError($sector) APxError($sector) APyError($sector)"\
      -orientation vertical -width 10 \
      -contextHelp "Error readbacks of ID$sector bpms: adjusted readback - setpoint."
    $dialogFrame.moreParameters.frame.error.frame configure -relief flat

    set border 3
    foreach outputFrame {offset adjusted error} {
        foreach entry {1 2 3 4} {
            $dialogFrame.moreParameters.frame.${outputFrame}.frame.entry${entry} \
              configure -borderwidth $border
        }
    }

    AdjustSetpoints -sector $sector -parent $dialogFrame
    APSEnableButton .dialogID$sector.buttonRow.ok.button
    
    update
    if {$statusCallback!="" && $timeDebug} {
        $statusCallback "[clock format [clock seconds] -format %H:%M:%S]: Enabling averaging for S${Sn}B:P${digit} and S${Sn1}A:P${digit}."
    }
    
    if [catch {exec cavput -list=S${Sn}B:P${digit},S${Sn1}A:P${digit} \
                  -list=:msAve:AveEnbBO=Enable \
                  -pendIoTime=10 \
              } result ] {
        APSAlertBox .alert -errorMessage "$result\nSomething wrong with a cavput command. Averaging may not be enabled."
        return
    }
}

proc TransferValues {args} {
    global timeLimit tolerance statusCallback timeDebug P0Sector

    set sector ""
    APSParseArguments {sector}
    if ![string length $sector] {
        APSAlertBox [APSUniqueName .] -errorMessage \
          "No sector variable specified in IDSteeringDialog"
        return 1
    }
    if $P0Sector($sector) {
        set digit 0
    } else {
        set digit 1
    }
    global BPxAdjusted BPyAdjusted APxAdjusted APyAdjusted
    if ![APSYesNoPopUp "You are asking to transfer adjusted bpm values to setpoint values.  Are you sure?"] {
            SetIDSteeringStatus "Transfer cancelled."
            return 1
        }
    set Sn $sector
    set Sn1 [exec rpnl "$Sn 1 + 40 > pop ? 40 - : \$"]
    set cavputList S${Sn}B:P${digit}:ms:x:SetpointAO=$BPxAdjusted($sector),
    append cavputList S${Sn}B:P${digit}:ms:y:SetpointAO=$BPyAdjusted($sector),
    append cavputList S${Sn1}A:P${digit}:ms:x:SetpointAO=$APxAdjusted($sector),
    append cavputList S${Sn1}A:P${digit}:ms:y:SetpointAO=$APyAdjusted($sector)
    if [catch {exec cavput -list=$cavputList -pendIoTime=10 \
               } result ] {
        APSAlertBox .alert -errorMessage "$result\nSomething wrong with a cavput command. Setpoints may not be asserted."
        return 1
    }
    if {$statusCallback!=""& $timeDebug} {
        $statusCallback "[clock format [clock seconds] -format %H:%M:%S]: Refreshing datapool vector of bpm adjust PVs..."
    }
    global waveformFile
    set pvList [list S${Sn}B:P${digit} S${Sn1}A:P${digit}]
    if [catch {APSRefreshVectorAdjust -pvList $pvList \
                 -waveformFile $waveformFile($sector) -plane both} result] {
        APSAlertBox .alert -errorMessage "$result\nSomething wrong with resetting vector adjust values."
        return 1
    }
    
    if [catch {APSRefreshFFVectorAdjust -pvList $pvList \
                 -pvList $pvList -plane both -statusCallback $statusCallback } result] {
        APSAlertBox .alert -errorMessage "$result\nSomething wrong with resetting FF vector adjust values."
        return 1
    }
    
    if {$statusCallback!=""} {
        $statusCallback "[clock format [clock seconds] -format %H:%M:%S]: Done."
    }
    return 0
}

proc APSRefreshFFVectorAdjust {args} {
    set plane ""
    set pvList ""
    set statusCallback ""
    APSParseArguments {pvList plane statusCallback}

    set tmpRoot /tmp/[APSTmpString]
    set tmpList ""
    if {![llength $pvList]} {
        return -code error "APSRefreshFFVectorAdjust(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 "APSRefreshFFVectorAdjust(2): plane should be x or y or both !"
        }
    }
    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 $pvList
    foreach coord $planes {
        set waveformPV DP:[string toupper $coord]bpmSetPt:WF
        set waveformFile $tmpRoot.$coord.wf
        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"
        }
        set fid [open $waveformFile "w"]
        puts $fid SDDS1
        puts $fid "&column name=ControlName type=string &end"
        puts $fid "&column name=DeviceName type=string &end"
        puts $fid "&data mode=ascii no_row_counts=1 &end"
        puts $fid $result
        close $fid
        
        set adjustFile $tmpRoot.$coord.adjust
        if [catch {exec cavget -list=[join $pvList ,] -list=:ms:${coord}:OffsetAO -pend=30} Offsets] {
            return -code error "APSRefreshFFVectorAdjust(3 - cavget): $Offsets"
        }
        if [catch {exec cavget -list=[join $pvList ,] -list=:ms:${coord}:SetpointAO -pend=30} Setpoints] {
            return -code error "APSRefreshFFVectorAdjust(4 - cavget): $Setpoints"
        }
        set Adjust ""
        foreach setpoint $Setpoints offset $Offsets {
            lappend Adjust [expr $setpoint + $offset]
        }
        #puts $Adjust
        if [catch {exec sddsmakedataset $adjustFile -col=DeviceName,type=string -data=[join $pvList ,] \
                     -col=Waveform,type=double -data=[join $Adjust ,] } result] {
            return -code error "APSRefreshFFVectorAdjust(5 -- create current adjust file): $result"
        }
        if [catch {exec sddswget -pv=$waveformPV $tmpRoot.$coord.wfread } result] {
            return -code error "APSRefreshFFVectorAdjust(6 -- sddswget): $result"
        }
        #check if the adjust changing PVs are in the FF waveform first
        if [catch {exec sddsselect $adjustFile $tmpRoot.$coord.wf -match=DeviceName $tmpRoot.$coord.exist } result] {
            return -code error "APSRefreshFFVectorAdjust(7 -- sddsselect): $result"
        }
        set rows [exec sdds2stream -rows=bar $tmpRoot.$coord.exist]
        if !$rows {
            if [string length $statusCallback] {
                eval $statusCallback {"the setpoint changes are not in the FF waveform, no need to update FF setpoint waveform."}
            }
            continue
        }
        if [catch {exec sddsxref $tmpRoot.$coord.wfread $tmpRoot.$coord.wf $tmpRoot.$coord.wfvalue -take=DeviceName
            exec sddsselect $tmpRoot.$coord.wfvalue $adjustFile -match=DeviceName -invert $tmpRoot.$coord.wf.notexist
            exec sddsxref  $adjustFile $tmpRoot.$coord.wfvalue -match=DeviceName -take=Index  -nowarnings $tmpRoot.$coord.wf.exist
            exec sddscombine $tmpRoot.$coord.wf.notexist $tmpRoot.$coord.wf.exist -merge -pipe=out \
                     | sddssort -pipe=in -col=Index $tmpRoot.$coord.wfset } result] {
            return -code error "APSRefreshFFVectorAdjust(8 -- create new FF waveform data): $result"
        }
        if [catch {exec sddswput $tmpRoot.$coord.wfset -pend=30 } result] {
            return -code error "APSRefreshFFVectorAdjust(8 - error setting FF setpoint waveform): $result"
        }
    }
    set tmpFiles [glob ${tmpRoot}*]
    APSAddToTmpFileList -ID ff -fileList $tmpFiles
}

proc UpdateFFAdjust {args} {
    set plane ""
    set statusCallback ""
    APSParseArguments {pvList plane statusCallback}

    set tmpRoot /tmp/[APSTmpString]
  
    set plane [string tolower $plane]
    switch $plane {
        x -
        y {
            set planes $plane
        }
        both {
            set planes {x y}
        }
        default {
            return -code error "APSRefreshFFVectorAdjust(2): plane should be x or y or both !"
        }
    }
    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}
    }
    foreach coord $planes {
        set waveformPV DP:[string toupper $coord]bpmSetPt:WF
        set waveformFile $tmpRoot.$coord.wf
        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"
        }
        set fid [open $waveformFile "w"]
        puts $fid SDDS1
        puts $fid "&column name=ControlName type=string &end"
        puts $fid "&column name=DeviceName type=string &end"
        puts $fid "&data mode=ascii no_row_counts=1 &end"
        puts $fid $result
        close $fid
        set pvList [exec sdds2stream -col=DeviceName $waveformFile]
        set valueList ""
        set indexList ""
        set index 0
        foreach pv $pvList {
            if {$pv=="Illegal Value"} {
                lappend valueList 0
            } else {
                if [catch {exec cavget -list=${pv}:ms:${coord}:OffsetAO -pend=10 -printErrors} offset] {
                    return -code error "Error reading offst of $pv: $offset"
                }
                if [catch {exec cavget -list=${pv}:ms:${coord}:SetpointAO -pend=10 -printErrors} setpoint] {
                    return -code error "Error reading offst of $pv: $setpoint"
                }
                set adjust [expr $offset + $setpoint]
                lappend valueList $adjust
            }
            lappend indexList $index
            incr index
        }
        if [catch {exec sddsmakedataset -par=WaveformPV,type=string -data=$waveformPV \
                     -col=Index,type=long -data=[join $indexList ,] \
                     -col=DeviceName,type=string -data=[join $pvList ,] \
                     -col=Waveform,type=double -data=[join $valueList ,]  $tmpRoot.$coord.ffAdjust} result] {
            return -code error "Error generating FF adjust file: $result"
        }
        #puts $tmpRoot.$coord.ffAdjust
        if [catch {exec sddswput  $tmpRoot.$coord.ffAdjust  } result] {
            return -code error "Error setting FF waveform data: $result"
        }
    }
    
}

proc AdjustSetpoints {args} {
    global deltax deltaxp deltay deltayp P0Sector
    global deltaxAcc deltaxpAcc deltayAcc deltaypAcc

    set sector ""
    set parent ""
    APSParseArguments {sector parent}
    if ![string length $sector] {
        APSAlertBox [APSUniqueName .] -errorMessage \
          "No sector variable specified in AdjustSetpoints"
        return 1
    }
    if $P0Sector($sector) {
        set digit 0
    } else {
        set digit 1
    }
    global BPx BPy APx APy
    set adjustFrame $parent.adjust.frame
    APSFrame .adjust -parent $parent
    $adjustFrame configure -relief flat

# accumulators for steering changes.
    set deltaxAcc($sector) 0
    set deltaxpAcc($sector) 0
    set deltayAcc($sector) 0
    set deltaypAcc($sector) 0

    set Sn $sector
    set Sn1 [exec rpnl "$Sn 1 + 40 > pop ? 40 - : \$"]
    APSFrame .parameters -parent $adjustFrame -packOption "-side left"
    $adjustFrame.parameters.frame configure -relief flat
    APSFrame .deltas -parent $adjustFrame.parameters.frame -packOption "-side left" \
      -label "Coordinate deltas"  -width 10\
      -contextHelp "Enter deltas for the x, x', y, y' coordinates  in ID$sector"
    $adjustFrame.parameters.frame.deltas.frame configure -relief flat
    if ![info exists deltax($sector)] {
        set deltax($sector) 0.001
    }
    if ![info exists deltaxp($sector)] {
        set deltaxp($sector) 0.001
    }
    if ![info exists deltay($sector)] {
        set deltay($sector) 0.001
    }
    if ![info exists deltayp($sector)] {
        set deltayp($sector) 0.001
    }
    APSLabeledEntry .bpx \
      -parent $adjustFrame.parameters.frame.deltas.frame \
      -label "delta x (mm)"  -width 10\
      -textVariable deltax($sector) \
      -contextHelp "Enter value for delta x"
    APSLabeledEntry .bpy \
      -parent $adjustFrame.parameters.frame.deltas.frame \
      -label "delta xp (mrad)"  -width 10\
      -textVariable deltaxp($sector) \
      -contextHelp "Enter value for delta xp"
    APSLabeledEntry .apx \
      -parent $adjustFrame.parameters.frame.deltas.frame \
      -label "delta y (mm)"  -width 10\
      -textVariable deltay($sector) \
      -contextHelp "Enter value for delta y"
    APSLabeledEntry .apy \
      -parent $adjustFrame.parameters.frame.deltas.frame \
      -label "delta yp (mrad)"  -width 10\
      -textVariable deltayp($sector) \
      -contextHelp "Enter value for delta yp"

    APSLabeledOutputFrame .accumulation \
      -parent $adjustFrame.parameters.frame \
      -label "Accumulators" -packOption "-side left" \
      -variableList "deltaxAcc($sector) deltaxpAcc($sector) deltayAcc($sector) deltaypAcc($sector)" \
      -orientation vertical -width 10 \
      -contextHelp "Accumulated coordinate deltas. Values return to zero when dialog box is closed and reopened."
    $adjustFrame.parameters.frame.accumulation.frame configure -relief flat
    set border 3
    foreach entry {1 2 3 4} {
        $adjustFrame.parameters.frame.accumulation.frame.entry${entry} \
          configure -borderwidth $border
    }
    APSFrame .incrButtons -parent $adjustFrame.parameters.frame -packOption "-side left" \
      -label " "  -width 10\
      -contextHelp "Causes setpoints to increment by the corresponding coordinate"
    $adjustFrame.parameters.frame.incrButtons.frame configure -relief flat

    APSFrame .decrButtons -parent $adjustFrame.parameters.frame -packOption "-side left" \
      -label " "  -width 10\
      -contextHelp "Causes setpoints to decrement by the corresponding coordinate"
    $adjustFrame.parameters.frame.decrButtons.frame configure -relief flat

   
    APSButton .incrx -parent $adjustFrame.parameters.frame.incrButtons.frame  \
        -packOption "-side top" -text OUT -command "IncrX -sector $Sn" \
        -contextHelp  "Changes x setpoints using delta x." -fastClick 1
    APSButton .incrxp -parent $adjustFrame.parameters.frame.incrButtons.frame  \
        -packOption "-side top" -text OUT -command "IncrXp -sector $Sn" \
        -contextHelp  "Changes x setpoints using delta xp." -fastClick 1
   
    APSButton .incry -parent $adjustFrame.parameters.frame.incrButtons.frame  \
        -packOption "-side top" -text UP -command "IncrY -sector $Sn" \
        -contextHelp  "Changes y setpoints using delta y." -fastClick 1
    APSButton .incryp -parent $adjustFrame.parameters.frame.incrButtons.frame  \
        -packOption "-side top" -text UP -command "IncrYp -sector $Sn" \
        -contextHelp  "Changes y setpoints using delta yp." -fastClick 1
   
    APSButton .decrx -parent $adjustFrame.parameters.frame.decrButtons.frame  \
        -packOption "-side top" -text IN -command "IncrX -sector $Sn -sign -1" \
        -contextHelp  "Changes x setpoints using delta x." -fastClick 1
    APSButton .decrxp -parent $adjustFrame.parameters.frame.decrButtons.frame  \
        -packOption "-side top" -text IN -command "IncrXp -sector $Sn -sign -1" \
        -contextHelp  "Changes x setpoints using delta xp." -fastClick 1
   
    APSButton .decry -parent $adjustFrame.parameters.frame.decrButtons.frame  \
        -packOption "-side top" -text DOWN -command "IncrY -sector $Sn -sign -1" \
        -contextHelp  "Changes y setpoints using delta y." -fastClick 1
    APSButton .decryp -parent $adjustFrame.parameters.frame.decrButtons.frame  \
        -packOption "-side top" -text DOWN -command "IncrYp -sector $Sn -sign -1" \
        -contextHelp  "Changes y setpoints using delta yp." -fastClick 1
    set padding 2
    set width 4
    $adjustFrame.parameters.frame.incrButtons.frame.incrx.button configure -width $width -pady $padding
    $adjustFrame.parameters.frame.incrButtons.frame.incrxp.button configure -width $width -pady $padding
    $adjustFrame.parameters.frame.incrButtons.frame.incry.button configure -width $width -pady $padding
    $adjustFrame.parameters.frame.incrButtons.frame.incryp.button configure -width $width -pady $padding
    $adjustFrame.parameters.frame.decrButtons.frame.decrx.button configure -width $width -pady $padding
    $adjustFrame.parameters.frame.decrButtons.frame.decrxp.button configure -width $width -pady $padding
    $adjustFrame.parameters.frame.decrButtons.frame.decry.button configure -width $width -pady $padding
    $adjustFrame.parameters.frame.decrButtons.frame.decryp.button configure -width $width -pady $padding

    return 0
}

proc IncrX {args} {
    global deltax deltaxp deltay deltayp R11
    global deltaxAcc deltaxpAcc deltayAcc deltaypAcc P0Sector
    set sector ""
    set sign 1
    APSParseArguments {sector sign}
    if ![string length $sector] {
        APSAlertBox [APSUniqueName .] -errorMessage \
          "No sector variable specified in IDSteeringDialog"
        return 1
    }
    if $P0Sector($sector) {
        set digit 0
    } else {
        set digit 1
    }
    global BPx BPy APx APy
    set deltaxAcc($sector) [expr $deltaxAcc($sector) + $sign * $deltax($sector)]
    set BPx($sector) [expr $BPx($sector) + $sign * $R11($sector) * $deltax($sector)]
    set APx($sector) [expr $APx($sector) + $sign * $R11($sector) *$deltax($sector)]
    return 0
}

proc IncrXp {args} {
    global deltax deltaxp deltay deltayp R12
    global deltaxAcc deltaxpAcc deltayAcc deltaypAcc P0Sector
    set sector ""
    set sign 1
    APSParseArguments {sector sign}
    if ![string length $sector] {
        APSAlertBox [APSUniqueName .] -errorMessage \
          "No sector variable specified in IDSteeringDialog"
        return 1
    }
    if $P0Sector($sector) {
        set digit 0
    } else {
        set digit 1
    }
    global BPx BPy APx APy
    set deltaxpAcc($sector) [expr $deltaxpAcc($sector) + $sign * $deltaxp($sector)]
    set BPx($sector) [expr $BPx($sector) - $sign * $R12($sector) * $deltaxp($sector)]
    set APx($sector) [expr $APx($sector) + $sign * $R12($sector) * $deltaxp($sector)]
    return 0
}

proc IncrY {args} {
    global deltax deltaxp deltay deltayp R33
    global deltaxAcc deltaxpAcc deltayAcc deltaypAcc P0Sector
    set sector ""
    set sign 1
    APSParseArguments {sector sign}
    if ![string length $sector] {
        APSAlertBox [APSUniqueName .] -errorMessage \
          "No sector variable specified in IDSteeringDialog"
        return 1
    }
    if $P0Sector($sector) {
        set digit 0
    } else {
        set digit 1
    }
    global BPx BPy APx APy
    set deltayAcc($sector) [expr $deltayAcc($sector) + $sign * $deltay($sector)]
    set BPy($sector) [expr $BPy($sector) + $sign * $R33($sector) * $deltay($sector)]
    set APy($sector) [expr $APy($sector) + $sign * $R33($sector) * $deltay($sector)]
    return 0
}
proc IncrYp {args} {
    global deltax deltaxp deltay deltayp R34
    global deltaxAcc deltaxpAcc deltayAcc deltaypAcc P0Sector

    set sector ""
    set sign 1
    APSParseArguments {sector sign}
    if ![string length $sector] {
        APSAlertBox [APSUniqueName .] -errorMessage \
          "No sector variable specified in IDSteeringDialog"
        return 1
    }
    if $P0Sector($sector) {
        set digit 0
    } else {
        set digit 1
    }
    global BPx BPy APx APy
    set deltaypAcc($sector) [expr $deltaypAcc($sector) + $sign * $deltayp($sector)]
    set BPy($sector) [expr $BPy($sector) - $sign * $R34($sector) * $deltayp($sector)]
    set APy($sector) [expr $APy($sector) + $sign * $R34($sector) * $deltayp($sector)]
    return 0
}

proc ApplySetpoints {args} {
    global timeLimit tolerance statusCallback timeDebug P0Sector

    set sector ""
    set returnerror 0
    APSParseArguments {sector returnerror}
    if ![string length $sector] {
        APSAlertBox [APSUniqueName .] -errorMessage \
          "No sector variable specified in IDSteeringDialog"
        if {$returnerror} {
            return -code error "AppySetpoints: No sector variable specified in IDSteeringDialog"  
        } else {
            return 1
        }
    }
    if [catch {CompareSetpointsWithBPLDLimit -sector $sector} result] {
        set answer [APSMultipleChoice [APSUniqueName .] \
                      -question "$result. The steering will probably trip the BPLD system, go ahead or cancel the steering? " -returnList {Go Cancel} \
                      -labelList {Go-Ahead Cancel} ]
        if {$answer == "Cancel"} {
            if {$statusCallback!=""} {
                $statusCallback "[clock format [clock seconds] -format %H:%M:%S]: Steering was cancel due to the probability of BPLD tripping."
            }
            return 1
        }
    } else {
        if {$statusCallback!="" && $timeDebug} {
            $statusCallback "[clock format [clock seconds] -format %H:%M:%S]: The setpoints are within the BPLD trip limits."
        }
    }
    
    if $P0Sector($sector) {
        set digit 0
    } else {
        set digit 1
    }
    global BPx BPy APx APy

    set Sn $sector
    set Sn1 [exec rpnl "$Sn 1 + 40 > pop ? 40 - : \$"]
    set cavputList S${Sn}B:P${digit}:ms:x:SetpointAO=$BPx($sector),
    append cavputList S${Sn}B:P${digit}:ms:y:SetpointAO=$BPy($sector),
    append cavputList S${Sn1}A:P${digit}:ms:x:SetpointAO=$APx($sector),
    append cavputList S${Sn1}A:P${digit}:ms:y:SetpointAO=$APy($sector)
    if {$statusCallback!="" && $timeDebug} {
        $statusCallback "[clock format [clock seconds] -format %H:%M:%S]: Applying setpoints to sr bpm ioc PVs..."
    }
    if [catch {exec cavput -list=$cavputList -pendIoTime=10 \
               } result ] {
        APSAlertBox .alert -errorMessage "$result\nSomething wrong with a cavput command. Setpoints may not be asserted."
        if {$returnerror} {
            return -code error "[clock format [clock seconds] -format %H:%M:%S]: ApplySetpoints: $result"
        } else {
            return 1
        }
    }
    if {$statusCallback!="" && $timeDebug} {
        $statusCallback "[clock format [clock seconds] -format %H:%M:%S]: Applying setpoints to datapool vector bpm PVs..."
    }
    global waveformFile
    set pvList [list S${Sn}B:P${digit} S${Sn1}A:P${digit}]
    if [catch {APSRefreshVectorAdjust -pvList $pvList -waveformFile $waveformFile($sector) -plane both} result] {
        APSAlertBox .alert -errorMessage "$result\nSomething wrong with resetting vector adjust values."
        if $returnerror {
            return -code error "ApplySetpoints: $result"
        } else {
            return 1
        }
    }
    if [catch {APSRefreshFFVectorAdjust -pvList $pvList  -plane both} result] {
        APSAlertBox .alert -errorMessage "$result\nSomething wrong with resetting  FF vector adjust values."
        if $returnerror {
            return -code error "ApplySetpoints (FF): $result"
        } else {
            return 1
        }
    }
    if {$statusCallback!="" && $timeDebug} {
        $statusCallback "[clock format [clock seconds] -format %H:%M:%S]: Applying setpoints done."
    }
}

proc CompareSetpointsWithBPLDLimit {args} {
    global BPLDList IsDigitalList firstChannelBPM secondChannelBPM
    set sector ""
    APSParseArguments {sector}
    if ![string length $sector] {
        return -code error "No sector variable specified in IDSteeringDialog"
    }
    set index [lsearch -exact $BPLDList $sector]
    if {$index<0} {
        return -code error "Sector $sector is not include in BPLD sectors.sdds file."
    }
    set isDigital [lindex $IsDigitalList $index]
 
    if {!$isDigital} {
        # 4 values returned
        if [catch {exec cavget -pend=30 -list=S -list=${sector} -list=BPLD: \
                     -list=x,y -list=Up,Low -list=TripLimitAO -pend=30} tripLimits] {
            return -code error $tripLimits
        }
        set bxUp [lindex $tripLimits 0]
        set bxLow [lindex $tripLimits 1]
        set byUp [lindex $tripLimits 2]
        set byLow [lindex $tripLimits 3]
        set axUp $bxUp
       # set axLow $axLow
        # The above looks like a bug, replace by following
        set axLow $bxLow
        set ayUp $byUp
        set ayLow $byLow
    } else {
        if [catch {exec cavget -pend=30 -list=$firstChannelBPM($sector),$secondChannelBPM($sector) \
                     -list=:dbpld: \
                     -list=x,y -list=hi,lo -list=AO.VAL -pend=30} tripLimits] {
            return -code error $tripLimits
        }

        set bxUp [lindex $tripLimits 0]
        set bxLow [lindex $tripLimits 1]
        set byUp [lindex $tripLimits 2]
        set byLow [lindex $tripLimits 3]
        set axUp [lindex $tripLimits 4]
        set axLow [lindex $tripLimits 5]
        set ayUp [lindex $tripLimits 6]
        set ayLow [lindex $tripLimits 7]
    }
    global BPx BPy APx APy P0Sector BPxOffset BPyOffset APxOffset APyOffset
    set badList ""
    set Sn $sector
    set Sn1 [exec rpnl "$Sn 1 + 40 > pop ? 40 - : \$"]
    if $P0Sector($sector) {
        set digit 0
    } else {
        set digit 1
    }
    if [catch {exec cavget -list=S${Sn}B:,S${Sn1}A: -list=P${digit}:ms: \
                 -list=x,y -list=:GainAO -pend=30} gainList] {
        return -code error $gainList
    }
    if [lsearch $gainList "\\?"]>=0 {
        return -code error "Error in reading gain value of sector $sector pvs."
    }
    #changed to use setpoints plus offsets divided by gain, instead of only setpoints 11/20/2013, H. Shang
    #which is more meaningful because BPLDs only compare raw bpm values.
    set bx [expr ($BPx($sector) + $BPxOffset($sector)) / [lindex $gainList 0]]
    set by [expr ($BPy($sector) + $BPyOffset($sector)) / [lindex $gainList 1]]
    set ax [expr ($APx($sector) + $APxOffset($sector)) / [lindex $gainList 2]]
    set ay [expr ($APy($sector) + $APyOffset($sector)) / [lindex $gainList 3]]
    if {$bx<$bxLow || $bx>$bxUp} {
        append badList "The setpoint of S${Sn}B:P${digit}:x is out of BPLD trip limit.\n"
    }
    if {$by<$byLow || $by>$byUp} {
        append badList "The setpoint of S${Sn}B:P${digit}:y is out of BPLD trip limit.\n"
    }
    if {$ax<$axLow || $ax>$axUp} {
        append badList "The setpoint of S${Sn1}A:P${digit}:x is out of BPLD trip limit.\n"
    }
    if {$ay<$ayLow || $ay>$ayUp} {
        append badList "The setpoint of S${Sn1}A:P${digit}:y is out of BPLD trip limit."
    }
    if [llength $badList] {
        return -code error $badList
    }
}

proc StartControllaw {args} {
    global timeLimit tolerance statusCallback timeDebug
    global gain interval P0Sector

    set sector ""
    APSParseArguments {sector}
    if ![string length $sector] {
        APSAlertBox [APSUniqueName .] -errorMessage \
          "No sector variable specified in IDSteeringDialog"
        return 1
    }
    if [catch {APSSRCheckCorrectorMode -plane h} hCorrMode] {
        APSAlertBox .alert -errorMessage "$hCorrMode"
        return
    }
    if [catch {APSSRCheckCorrectorMode -plane v} vCorrMode] {
        APSAlertBox .alert -errorMessage "$vCorrMode"
        return
    }
    if [string compare $hCorrMode $vCorrMode]!=0 {
        APSAlertBox .alert -errorMessage "SRIDSteering: The corrector modes in x and y plane are not consistent!"
        return
    }
  
    if [string compare $hCorrMode vector]==0 {
        set runControlPV DP:S:SteeringSDDS
    } else {
        set runControlPV S:SteeringSDDS
    }
    if [catch {exec cavget -list=$runControlPV.RUN -pend=30} running] {
        return -code error $running
    }
    if $running {
        if [APSYesNoPopUp "Another steering is running, would you like to abort it and continue?"] {
            if [catch {exec cavput -list=$runControlPV.ABRT=1 -pend=30} result] {
                return -code error $result
            }
            after 2000
            if [catch {exec cavput -list=$runControlPV.CLR=1 \
                           -pend=30} result] {
                return -code error "Unable to abort steering: $result"
            }
        } else {
            APSAlertBox .alert -errorMessage "SRIDSteering: another steering is running!"
            return
        }
    }
    if $P0Sector($sector) {
        set digit 0
    } else {
        set digit 1
    }
    if {$statusCallback!="" && !$timeDebug} {
        $statusCallback "[clock format [clock seconds] -format %H:%M:%S]: Start of checks for local steering."
    } 
    if {$statusCallback!="" && $timeDebug} {
        $statusCallback "[clock format [clock seconds] -format %H:%M:%S]: Start of checks for local steering. Check setpoints with BPLD limits."
    } 
    if [catch {CompareSetpointsWithBPLDLimit -sector $sector} result] {
        set answer [APSMultipleChoice [APSUniqueName .] \
                        -question "$result. The steering will probably trip the BPLD system, go ahead or cancel the steering? " -returnList {Go Cancel} \
                        -labelList {Go-Ahead Cancel} ]
        if {$answer == "Cancel"} {
            if {$statusCallback!=""} {
                $statusCallback "[clock format [clock seconds] -format %H:%M:%S]: Steering was cancel due to the probability of BPLD tripping."
            }
            return 1
        }
    } else {
        if {$statusCallback!="" && $timeDebug} {
            $statusCallback "[clock format [clock seconds] -format %H:%M:%S]: The setpoints are within the BPLD trip limits."
        } 
    }
    if {$statusCallback!="" && $timeDebug} {
        $statusCallback "[clock format [clock seconds] -format %H:%M:%S]: Applying setpoints in general."
    }
    #  if [catch {ApplySetpoints -sector $sector -returnerror 1} result] {
    #     if {$statusCallback!=""} {
    #        $statusCallback "[clock format [clock seconds] -format %H:%M:%S]: $result."
    #   }
    #   return
    # }
    set controllawDir /home/helios/oagData/sr/localSteering/lattices/default/IDs/[format %02ld ${sector}]ID
    if {![file exist $controllawDir]} {
        APSAlertBox .alert -errorMessage "SRIDSteering: $controllawDir does not exist!"
        return
    }
    if {![file exist $controllawDir/irm] || ![file exist $controllawDir/tests] || \
            ![file exist $controllawDir/plain.defs] || ![file exist $controllawDir/dynamic.defs]} {
        APSAlertBox .alert -errorMessage "SRIDSteering: some controllaw files in $controllawDir do not exist!"
        return
    }
    if [catch {exec sdds2stream -par=ConditionNumber $controllawDir/irm} ConditionNumber] {
        APSAlertBox .alert -errorMessage "SRIDSteering: Problem with getting ConditionNumber from $controllawDir/irm!"
        return
    }
    if {$ConditionNumber>2.0e3} {
        APSAlertBox .alert -errorMessage "SRIDSteering: The ConditionNumber of $controllawDir/irm is greater than 2.0e3! Contact the SR manager or designee."
        return
    }
    
    # determine what mode the correctors are in to pick the correct 
    # definitions file to use.
    if {$statusCallback!="" && $timeDebug} {
        $statusCallback "[clock format [clock seconds] -format %H:%M:%S]: Checking corrector modes."
    }
    if [catch {exec sdds2stream -col=ControlName $controllawDir/irm} correctors] {
        APSAlertBox .alert -errorMessage "SRIDSteering: Problem with getting correctors from $controllawDir/irm"
        return
    }
    if [catch {exec cavget -pend=15 -list=[join $correctors ,] \
                   -list=:ControlSrcBO -label -embrace} tagValuePairs] {
        APSAlertBox .alert -errorMessage "SRIDSteering: Problem with getting source for correctors: $tagValuePairs"
        return
    }
    # Take first one corrector as giving the reference mode
    set mode [lindex [lindex $tagValuePairs 0] 1]
    set incorrectMode 0
    foreach tagValue $tagValuePairs {
        if {[string compare [lindex $tagValue 1] $mode]} {
            incr incorrectMode
        }
    }
    if $incorrectMode {
        APSAlertBox .alert -errorMessage "Found $incorrectMode correctors not in mode $mode. Correction can't proceed."
        return
    }
    switch $mode {
        Maintenance {
            set defs plain.defs
        }
        Operation {
            set defs dynamic.defs
        }
    }

    set oldDir [pwd]
    cd $controllawDir
    
    if {$statusCallback!="" && $timeDebug} {
        $statusCallback "[clock format [clock seconds] -format %H:%M:%S]: Sending info to logDaemon."
    }
    if [catch {exec logMessage  -sourceId=steeringAudit\
                   -tag=Instance ID${sector} -tag=Action Start \
               } result ] {
        APSAlertBox .alert -errorMessage "SRIDSteering: Error with logMessage: $result"
        return
    }
    
    if {$statusCallback!="" } {
        $statusCallback "[clock format [clock seconds] -format %H:%M:%S]: Starting sddscontrollaw on ID$sector."
    }

    if [string match $hCorrMode vector] {
        exec medm -attach -x -macro SDDSPV=$runControlPV \
          /usr/local/iocapps/adlsys/srbpm/datapool/sddsEpicsLauncher.adl &

	#global env
        #if {$env(HOST_ARCH)=="linux-x86_64"} {
	#    set option [exec /usr/local/iocapps/R3.14.11/base/3-14-11-asd1/bin/linux-x86_64/caget -S ${runControlPV}.OPTN]
	#    APSInfoWindow .infosteering -name "Steering Controllaw Options" -width 70 -infoMessage "$option"  \
        #		-packOption "-side top -fill both" -copyable 1
	#}
        if [catch {APSSRStartLocalSteeringInIOC -interval $interval \
                     -gain $gain -infiniteLoop 1 \
                     -definition $defs -controllawDir $controllawDir} result] {
            APSAlertBox .alert -errorMessage "$result!"
            return 
        }
    } else {
        exec medm -attach -x -macro RCPV=$runControlPV \
          /usr/local/iocapps/adlsys/sr/psApp/APSRunControlSingle.adl &
        # Steering lmit was reduced to 0.5 A to be consistent with 
        # a corrector limit of 149.5 or 99.5. It is important to
        # not exceed the actual limits of the corrector, which sproc ap
        # may be 150 A or 100 A.
        APSExecLog .controllaw -width 100 \
          -lineLimit 1024 \
          -name "$controllawDir local correction" \
          -unixCommand "sddscontrollaw irm \
              -test=tests \
              -runControlPV=string=$runControlPV,pingTimeout=30 \
              \"-runControlDescription=string=ID steering\" \
               -controlQuantityDefinition=$defs \
              -gain=$gain -interval=$interval -deltaLimit=value=0.5 \
              -verbose=1 -infiniteLoop"  \
          -callback "controllawCallback -sector $sector" \
          -abortCallback "abortControllawCallback  -sector $sector" \
          -cancelCallback "abortControllawCallback  -sector $sector"
    }
    cd $oldDir
    return 0
}

proc controllawCallback {args} {
    global statusCallback P0Sector

    APSParseArguments {sector}
    if $P0Sector($sector) {
        set digit 0
    } else {
        set digit 1
    }

    if {$statusCallback!=""} {
        $statusCallback "[clock format [clock seconds] -format %H:%M:%S]: sddscontrollaw on BP${digit} and AP${digit} bpms for ID$sector completed."
    }
    if [catch {exec logMessage  -sourceId=steeringAudit \
                  -tag=Instance ID${sector} -tag=Action Exit \
              } result ] {
        APSAlertBox .alert -errorMessage "error with logMessage: $result"
        return
    }
    return
}

proc abortControllawCallback {args} {
    global statusCallback P0Sector

    APSParseArguments {sector}
    if $P0Sector($sector) {
        set digit 0
    } else {
        set digit 1
    }

    if {$statusCallback!=""} {
        $statusCallback "[clock format [clock seconds] -format %H:%M:%S]: sddscontrollaw on BP${digit} and AP${digit} bpms for ID$sector aborted."
    }
    if [catch {exec logMessage  -sourceId=steeringAudit \
                  -tag=Instance ID${sector} -tag=Action Abort \
              } result ] {
        APSAlertBox .alert -errorMessage "error with logMessage: $result"
        return
    }
    return
}

set IDSectorList [APSGetSDDSColumn -column Sector \
                    -fileName /home/helios/oagData/sr/IDs/sectors.sdds]

MakeSectorsWidget .sectors -parent .userFrame
MakeOptionWidget .options -parent .userFrame
if $allowTransfer {
    MakeTransferWidget .actions -parent .userFrame
}

set tolerance 0.050
set timeLimit 20
set statusCallback SetIDSteeringStatus
set interval 1.5
set gain 0.3
set offsetReferenceFile /home/helios/oagData/SCR/snapshots/SR/SR-BPMOffsetReference.gz

# global variable P0Sector is used to set the label names
# of the bpms whose setpoints are changed.
set linkDir /home/helios/oagData/sr/localSteering/lattices/default/IDs
for {set sector 1} {$sector<41} {incr sector} {
    set id ${linkDir}/[format %02ld $sector]ID
    set idLink [file readlink $id] 
    switch -regexp $idLink  {
        P1 {
        # means that P1 bpms are used for steering
            set P0Sector($sector) 0
            set R11($sector) 1.09
            set R12($sector) 4.29
            set R33($sector) 0.91
            set R34($sector) 3.67
        }
        P0 {
        # means that P0 bpms are used for steering
            set P0Sector($sector) 1
            set R11($sector) 1.0
            set R12($sector) 2.48
            set R33($sector) 1.0
            set R34($sector) 2.48
	    #removed the special case for ID 34, since it is same as others now, before R12($sector) and R34($sector) were 1.24
        }
    }
}
set BPLDFile /home/helios/oagData/sr/BPLDs/sectors.sdds
sdds load $BPLDFile bpldConfig
set BPLDList [lindex $bpldConfig(Column.Sector) 0]
set IsDigitalList [lindex $bpldConfig(Column.IsDigital) 0]
set Channel2List [lindex $bpldConfig(Column.Channel2) 0]
set Channel1List [lindex $bpldConfig(Column.Channel1) 0]
foreach sector $BPLDList chan1 $Channel1List chan2 $Channel2List {
    set sector1 [expr $sector + 1]
    set firstChannelBPM($sector) S${sector}B:$chan1
    set secondChannelBPM($sector) S${sector1}A:$chan2
}
