#Called by APSMakeIDGapFullScanSelection
proc APSSRGetIDGapFullInfo {args} {
    global APSMpSRGapFullScan AllIDList CUSectorList IDUseFlag 
    
    if ![info exist APSMpSRGapFullScan(maxGap)] {
        set APSMpSRGapFullScan(maxGap) 34
    }
    if ![info exist APSMpSRGapFullScan(maxStep)] {
        set APSMpSRGapFullScan(maxStep) 30
    }
    if ![info exist APSMpSRGapFullScan(minStep)] {
        set APSMpSRGapFullScan(minStep) 0.25
    }
    if ![info exist APSMpSRGapFullScan(stepInterval)] {
        set APSMpSRGapFullScan(stepInterval) 7
    }
    if ![info exist APSMpSRGapFullScan(factor)] {
        set APSMpSRGapFullScan(factor) 2
    }
    if ![info exist APSMpSRGapFullScan(points)] {
        set APSMpSRGapFullScan(points) 30
    }
    if ![info exist APSMpSRGapFullScan(numToAve)] {
        set APSMpSRGapFullScan(numToAve) 2
    }
    if ![info exist APSMpSRGapFullScan(deltaToMinGap)] {
        set APSMpSRGapFullScan(deltaToMinGap) 0.01
    }
    if ![info exist APSMpSRGapFullScan(pause)] {
        set APSMpSRGapFullScan(pause) 10
    }
    if ![info exist APSMpSRGapFulLScan(readingType)] {
        #FIX THIS - Not sure what readingType should be for the APSU
        set APSMpSRGapFullScan(readingType) LowPass1s
    }
    if ![info exist APSMpSRGapFulLScan(transferOrbit)] {
        set APSMpSRGapFullScan(transferOrbit) 0
    }
    
    set  CUdataDir /home/helios/oagData/feedForwardFiles/IDgap
    if [catch {exec sddsprocess $CUdataDir/cantingLogic.sdds -pipe=out \
                 -filter=col,TreatedAsCU,1,1 \
                 | sdds2stream -pipe -col=Sector} CUSectorList] {
        return -code error "APSSRGetIDGapInfo(1 - unable to get CU sectors): $CUSectorList"
    }
    global checkButtonVariableList entryVariableList checkButtonCommandList IDDeviceLimit
    if [catch {APSSRGetDeviceLimits -default 1 -initial 1 -gapVar APSMpSRGapFullScan} result] {
        return -code error "APSSRGetIDGapInfo(2 - unable to get device limits): $result"
    }
    APSSRGapSetThresholds -IDList $AllIDList -thresholdVar APSMpSRGapFullScan
}

#Called by APSSRGapFullScan
proc APSSRGetSteeringConfigNames {args} {
    set orbitControlDir /home/helios/oagData/sr/orbitControllaw/lattices 
    set pem 0
    set hConfig ""
    set vConfig ""
    APSStrictParseArguments {pem hConfig vConfig}
    
    global hSteeringConfig vSteeringConfig
    if !$pem { 
        set lattice [file tail [file readlink /home/helios/oagData/sr/orbitControllaw/lattices/default]]
        if ![info exist hSteeringConfig] {
            set hSteeringConfig $lattice/h.default
        }
        if ![info exist vSteeringConfig] {
            set vSteeringConfig $lattice/v.default
        }
        
        set w [APSUniqueName .]
        global okPressed cancelPressed 
        set okPressed 0
        set cancelPressed 0
        APSDialogBox $w -name "Configuration dialog" \
          -okCommand "set okPressed 1" \
          -cancelCommand "set cancelPressed 1" -width 50
        APSLabeledEntry .le1 -parent $w.userFrame -label "H. steering config." \
          -textVariable hSteeringConfig -width 50 -contextHelp \
          "Enter the name of the horizontal steering configuration file (e.g., etax10cm/h.2001-0313.04)"
        APSLabeledEntry .le2 -parent $w.userFrame -label "V. steering config." \
          -textVariable vSteeringConfig -width 50 -contextHelp \
          "Enter the name of the vertical steering configuration file (e.g., etax10cm/h.2001-0313.04)"
        update
        tkwait window $w
        if $cancelPressed {
            return -code error "No configuration chosen"
        }
    } else {
        set hSteeringConfig h.default
        set vSteeringConfig v.default
    }
    set oldDir [pwd]
    set hOk 1
    set vOk 1
    # initial value of variable hSteeringConfig is actually
    # a lattice directory plus a configuration directory
    # i.e. nux36nuy19/h.default
    # If there was confusion and hSteeringConfig 
    # has only the configuration part, then the statements below
    # will prepend the lattice part.
    if ![file exists $orbitControlDir/$hSteeringConfig/config] {
        if [file exists $orbitControlDir/default/$hSteeringConfig/config] {
            cd $orbitControlDir/default
            set latticeName [file tail [pwd]]
            cd $oldDir
            set hSteeringConfig $latticeName/$hSteeringConfig 
        } else {
            return -code error "Not found: $orbitControlDir/$hSteeringConfig/config or $orbitControlDir/default/$hSteeringConfig/config"
        }
    }
    if ![file exists $orbitControlDir/$vSteeringConfig/config] {
        if [file exists $orbitControlDir/default/$vSteeringConfig/config] {
            cd $orbitControlDir/default
            set latticeName [file tail [pwd]]
            cd $oldDir 
            set vSteeringConfig $latticeName/$vSteeringConfig
        } else {
            return -code error "Not found: $orbitControlDir/$vSteeringConfig/config"
        }
    }
    cd $oldDir
}

#Called by APSMakeIDGapFullScanSelection
proc APSSRSetAllMinGaps {args} {
    set var APSMpSRGapFullScan
    APSStrictParseArguments {var}
    global $var AllIDList
    
    foreach ID $AllIDList {
        set ${var}($ID.minGap) [set ${var}(minGap)]
    }
}

#Called by APSMakeIDGapFullScanSelection
proc APSSRUpdateMinGap {args} {
    set var APSMpSRGapFullScan
    APSStrictParseArguments {var}
    global $var AllIDList
    if [catch {exec cavget -list=[join $AllIDList ,] -list=:DeviceLimitM -pend=30} valList] {
        return -code error $valList
    }
    foreach ID $AllIDList val $valList {
        if {$val=="?"} {
            APSSetVarAndUpdate status "Unable to get the device limit of $id"
        } else {
            if {$ID=="S14ID:USID"} {
                #FIX THIS - I don't know the name of the PV for the APSU
                if [catch {exec cavget -list=ID14us:GapPowerLimit -pend=10 -printErrors} val] {
                    return -code error "error in reading I4us gap power limit: $val"
                }
            }
            set ${var}($ID.minGap) [format %.1f [expr $val + [set ${var}(deltaToMinGap)]]]
        }
    }
}

#Called by APSMpGapFullScan and SRCollectGapFFData
proc APSSRGapFullScan {args} {
    set outputDir /home/helios/oagData/sr/gapScans/data/gapScanFull
    set rootname IDscan
    set maxGap 180.0
    set points 30
    set statusCallback "APSSetVarAndUpdate status"
    set preview 0
    set transferOrbit 1
    set readingType LowPass1s
    set numToAve 10
    set IDList ""
    set minStep 0.25
    set maxStep 30
    set stepInterval 7
    set factor 2
    set minGapList ""
    set deltaToMinGap 0.01
    set pem 0
    set fixedIDList ""
    set fixedGapList ""
    set togetherList ""
    set keepFFrunning 0
    set takeTunes 0
    set readingType LowPass1s
    set ignoreXbpms 0
    APSStrictParseArguments {outputDir rootname start maxGap statusCallback keepFFrunning \
                               pause preview IDList \
                               transferOrbit readingType numToAve pem\
                               minStep maxStep stepInterval factor minGapList deltaToMinGap \
                               fixedIDList fixedGapList togetherList takeTunes readingType ignoreXbpms}

    set outputFile ""
    if ![llength $IDList] {
        return -code error "No IDs provided for scan!"
    }
    if {[llength $IDList]!=[llength $minGapList]} {
        return -code error "The number of minum gaps does not equal to the number of IDs."
    }
    
    if !$preview {
        if ![file exist $outputDir] {
            return -code error "Error: directory $outputDir does not exist!"
        }
        if ![file isdirectory $outputDir] {
            return -code error "$outputDir (in the Output dir entry) is not a directory."
        }
        set tmpstring [APSTmpString]
        if [catch {open $outputDir/$tmpstring "w"} fd] {
            return -code error "You do not have permission to write in $outputDir directory"
        } else {
            close $fd
            file delete -force $outputDir/$tmpstring
        } 
        if [llength $fixedIDList] {
            foreach ID $fixedIDList {
                set index [lsearch $IDList $ID]
                if $index>=0 {
                    set IDList [lreplace $IDList $index $index]
                    set minGapList [lreplace $minGapList $index $index]
                }
            }
        }
    }
    set inputFileDir /home/helios/oagData/sr/gapScans/feedforwardInputFiles
    
    set expFileList ""
    set IDtagList ""
    set IDgapValues /tmp/[APSTmpString].gapValues
    set IDgapTests /tmp/[APSTmpString].gapTests
    APSAddToTempFileList $IDgapValues $IDgapTests
    
    if [catch {exec sddsmakedataset -col=DeviceName,type=string -data=[join $IDList ,] -pipe=out \
                 | sddsprocess -pipe=in $IDgapTests -edit=col,ControlName,DeviceName,ei/:GlobalBusyM/ } result] {
        return -code error "APSSRGapFullScan: Error generating tests file: $result"
    }
    
    if [catch {APSSRCreateGapScanValuesFile -minStep $minStep -maxStep $maxStep \
                 -IDList $IDList -minGapList $minGapList -gapValuesFile $IDgapValues \
                 -stepInterval $stepInterval -maxGap $maxGap -factor $factor} result] {
        return -code error $result
    }
    global stepCounter
    set stepCounter $result
    foreach ID $IDList { 
        #FIX THIS - Pre APSU the tag would look like 12ds, I think the APSU version would be 12ID:DSID
        #scan $ID ID%s tag
        scan $ID S%s tag
        lappend IDtagList $tag
        if !$preview {
            set expFile /tmp/[APSTmpString]
            APSAddToTmpFileList -ID scanfullgap -fileList $expFile
            if [catch {exec replaceText $inputFileDir/IDscan.template1.exp $expFile \
                         -original=<ID>,<values_file> \
                         -replace=$ID,$IDgapValues} result] {
                set status $result
                ReturnProc
                return
            }
            lappend expFileList $expFile
        }
    }
    
    if $preview {
        exec sddsplot $IDgapValues -column=Step,S*ID* -separate -graph=symbol &
        return
    }
    if !$pem {
        if ![APSYesNoPopUp "The experiment will scan with $stepCounter steps? Would you like to continue?"] {
            return -code error "APSSRGapFullScan: scan experiment was cancelled."
        }
       # if [catch {APSCheckGapScanConditions} result] {
        #    return -code error $result
      #  }
    }
    global hSteeringConfig vSteeringConfig
    if [catch {APSSRGetSteeringConfigNames -pem $pem} result] {
        return -code error "APSSRGapFullScan: $result"
    }
    
    set expFile /tmp/[APSTmpString].exp
    APSAddToTempFileList $expFile
    if $transferOrbit {
        set template $inputFileDir/IDscan.template2.exp
    } else {
        set template $inputFileDir/IDscan.template3.exp
    }
    if $takeTunes {
        set template $inputFileDir/IDscan.template3.addTunes.exp
    }
    if [catch {exec replaceText $template $expFile \
                 -original=<IDlist>,<pause>,<numToAve>,<readingType>,<testFile>,<readingType> \
                 "-replace=$IDList,$pause,$numToAve,$readingType,$IDgapTests,$readingType" } result] {
        return -code error $result
    }
    lappend expFileList $expFile
    
    set expFile /tmp/[APSTmpString]
    APSAddToTempFileList $expFile
    if [catch {eval exec cat $expFileList > $expFile} result] {
        return -code error $result
    } 
    catch {eval file delete $expFileList}
    #check if sddsfeedforward is running
    if !$keepFFrunning {
        if [catch {exec cavget -list=S:ID:GapFeedForwardRC.RUN -pend=10} running] {
            return -code error $running
        }
        if $running {
            set answer [APSMultipleChoice [APSUniqueName .] -modal 1 \
                          -question "ID gap feedforward is running, abort it or cancel experiment" \
                          -returnList {1 0} \
                          -labelList {Abort_And_Run_Experiment Cancel_Experiment}]
            if !$answer { 
                return -code error "Experiment was cancelled."
            }
            if [catch {APSMpIDOffsetFeedforward -action stop} result] {
                return -code error $result
            }
        }
    }
    
    set snapshot [lindex [APSSCRMakeSnapshotFilename -machine SR] 0]
    regsub {SR} $snapshot ${rootname}- snapshot0
    set outputFile $outputDir/${snapshot0}.sdds
    
    exec sddssequence -pipe=out \
      -define=transferOrbit,type=long -sequence=begin=$transferOrbit,end=$transferOrbit,number=1 \
      | sddsprocess -pipe=in $outputFile.rec \
      -define=column,end,$maxGap,units=mm \
      -define=column,points,$points,type=long \
      -define=column,numberOfSameSizeSteps,$stepInterval,type=long \
      -define=column,pause,$pause,units=s \
      -define=column,minStep,$minStep,units=mm \
      -define=column,maxStep,$maxStep,units=mm \
      -define=column,factor,$factor \
      -define=column,numToAve,$numToAve,type=long \
      -define=column,deltaToMinimumGap,$deltaToMinGap,units=mm \
      "-print=column,hSteeringConfig,$hSteeringConfig" \
      "-print=column,vSteeringConfig,$vSteeringConfig" 
    global fullScan
    set fullScan 0
    APSExecLog [APSUniqueName .] -name "ID scan" -width 100 \
      -unixCommand "sddsexperiment $expFile $outputFile -verbose" \
      -callback "APSSRGapScanCallbackProc -ok 1 -outputFile $outputFile -IDtagList \"$IDtagList\"; set fullScan done" \
      -cancelCallback "APSSRGapScanCallbackProc -ok 0 -outputFile $outputFile -IDtagList \"$IDtagList\"; set fullScan canceled" \
      -abortCallback "APSSRGapScanCallbackProc -ok 0 -outputFile $outputFile -IDtagList \"$IDtagList\"; set fullScan aborted"
    tkwait variable fullScan
    if [string compare $fullScan "done"]!=0 {
        return -code error "APSSRGapFullScan was $fullScan"
    }
    if [catch {exec sddsprocess $outputFile $outputFile.1 "-print=par,FixedGaps,[join $fixedIDList]" \
                 "-print=par,FixedGapValues,[join $fixedGapList]" "-print=par,ScanTogetherSectors,[join $togetherList]" } result] {
        return -coded error "APSSRGapFullScan(1 -- sddsprocess): $result"
    }
    exec mv $outputFile.1 $outputFile
    return $outputFile
}

#Called by APSSRGapFullScan
proc APSSRCreateGapScanValuesFile {args} {
    set minStep 0.25
    set maxStep 30
    set IDList ""
    set minGapList ""
    set gapValuesFile ""
    set stepInterval 7
    set maxGap 34
    set factor 2
    APSStrictParseArguments {minStep maxStep IDList gapValuesFile stepInterval minGapList maxGap factor}

    if ![string length $gapValuesFile] {
        return -code error "gapValuesFile not provided."
    }
    if {$minStep > $maxStep} {
        return -code error "Minimum step size is larger than maximum step\
                     value=$maxStep (mm).\nRun is terminated."
    }
    
    set stepList ""
    set minIndex 0
    set minGap 100
    set index 0
    foreach gap $minGapList {
        if {$gap<$minGap} {
            set minGap $gap
            set minIndex $index
        }
        incr index
    }
    #found the maximum steps needed for scan
    set stepCount 1
    set count 1
    set stepList $stepCount
    set gap $minGap
    set step $minStep
    while {1} {
        incr stepCount
        lappend stepList $stepCount
        if $count<=$stepInterval {
            set gap [expr ($gap + $step)]
            if {$gap > $maxGap} {
                break
            }
        }
        if $count==$stepInterval {
            set step [expr ($factor * $step)]
            set count 0
        }
        if {$step > $maxStep} {
            set step $maxStep
        }
        incr count
    }
    
    foreach ID $IDList gap0 $minGapList {
        set count 1
        set gapList $gap0
        set step $minStep
        set gap $gap0
        for {set i 1} {$i < $stepCount} {incr i} { 
            if $count<=$stepInterval {
                set gap [expr ($gap + $step)]
                if {$gap > $maxGap} {
                    lappend gapList $maxGap
                } else {
                    lappend gapList $gap
                }
            }
            if $count==$stepInterval {
                set step [expr ($factor * $step)]
                set count 0
            }
            if {$step > $maxStep} {
                set step $maxStep
            }
            incr count
        }
        lappend optList -col=${ID}gap,type=double
        lappend optList -data=[join $gapList ,]
        incr index
    }
    lappend optList -col=Step,type=long 
    lappend optList -data=[join $stepList ,]
    if [catch {eval exec sddsmakedataset $optList $gapValuesFile} result] {
        return -code error $result
    }
    return $stepCount
}

#Called by APSSRGapFullScan
proc APSSRPrepareFFData {args} {
    set orbitControlDir /home/helios/oagData/sr/orbitControllaw/lattices
    set input "" 
    set CUSectorList ""
    set gapThreSholdVar ""
    set statusCallback "" 
    set pem 0
    set fixedGapTolerance 0.1
    set gapDiffTolerance 0.5
    APSStrictParseArguments {orbitControlDir input CUSectorList gapThresholdVar statusCallback pem fixedGapTolerance gapDiffTolerance}

    set IDList ""
    set gapSPList ""
    set sectorList "" 
    set tmpRoot /tmp/[APSTmpString]
    APSAddToTempFileList $tmpRoot.sectors
    global $gapThresholdVar
    
    if [catch {exec sdds2stream -col=DeviceName /home/helios/oagData/sr/IDs/devices.sdds} AllIDList] {
        return -code error "APSSRPrepareFFData1: Problem getting list of IDs: $AllIDList"
    }
    if [catch {exec sddsmakedataset $tmpRoot.exist -col=Rootname,type=string -data=[join $AllIDList ,]} result] {
        return -code error "APSSRPrepareFFData2: $result"
    }
    set parNames [exec sddsquery -par $input]
    set fixedIDList ""
    set fixedGapList ""
    #set togetherList {3 26 29 30}
    #FIX THIS - This file needs to be updated for the APSU
    set togetherList [join [exec sddsprocess /home/helios/oagData/sr/IDs/dualGapConfig.sdds -pipe=out \
                              -match=col,ScanType=together -nowarn | sdds2stream -pipe -col=Sector]]
    # puts $parNames
    if [lsearch $parNames FixedGaps]>=0 {
        set fixedIDList [join [exec sdds2stream -par=FixedGaps $input]]
        set fixedGapList [join [exec sdds2stream -par=FixedGapValues $input]]
        set togetherList [join [exec sdds2stream -par=ScanTogetherSectors $input]]
    }
    # puts $fixedIDList
    set fixedSectorList ""
    foreach id $fixedIDList {
        #set sector [scan $id ID%ld]
        scan $id S%02dID sector
        lappend fixedSectorList $sector
    }
    # puts $fixedSectorList 
    APSAddToTempFileList $tmpRoot.exist
    # These sectors have ID*GapSP present in the file.
    #FIX THIS - I am note sure if GapSP is correct for the APSU
    if [catch {exec sddsquery $input -column -sddsOutput \
                 | sddssort -pipe -col=Name \
                 | sddsprocess -pipe  "-match=column,Name=*GapSP" \
                 | sddsprocess -pipe  \
                 -edit=column,Rootname,Name,%/GapSP// \
                 -scan=column,Sector,Name,S%ld,type=long \
                 | sddsselect -pipe=in $tmpRoot.exist $tmpRoot.sectors -match=Rootname \
             } result] {
        return -code error "APSSRPrepareFFData3: $result"
    }
    
    if [catch {exec sdds2stream $tmpRoot.sectors -column=Sector} sectorList] {
        return -code error "$sectorList" 
    }
    if [catch {exec sdds2stream $tmpRoot.sectors -column=Name} gapSPList] {
        return -code error "$gapSetpointList"
    }
    if [catch {exec sdds2stream $tmpRoot.sectors -column=Rootname} IDList] {
        return -code error "$IDList"
    } 
    if ![llength $IDList] {
        return -code error "APSSRPrepareFFData3a: no IDs varied in $input" 
    }
    set hSteeringConfig [exec sdds2stream $input.rec -column=hSteeringConfig]
    set vSteeringConfig [exec sdds2stream $input.rec -column=vSteeringConfig] 
    set hSteeringConfig $orbitControlDir/$hSteeringConfig/config
    set vSteeringConfig $orbitControlDir/$vSteeringConfig/config
    if ![file exists $hSteeringConfig] {
        return -code error "APSSRPrepareFFData4: Couldn't find  $hSteeringConfig"
    }
    if ![file exists $vSteeringConfig] {
        return -code error "APSSRPrepareFFData5: Couldn't find  $vSteeringConfig"
    }
    global dialogBoxResponse doXRayBPMFF doCorrectorFF doInterpolate APSMpSRGapFullScan
    global keepDialogResponse newOffsetValue
    set doXRayBPMFF 1
    set doCorrectorFF 1
    set doInterpolate 1
    global smoothLowerPts smoothHigherPts smoothOrder
    set smoothLowerPts 3
    set smoothHigherPts 3
    set smoothOrder 1
    
    if {!$pem} {
        set w [APSUniqueName .] 
        set dialogBoxResponse ""
        APSDialogBox $w -cancelCommand "set dialogBoxResponse cancel" \
          -okCommand "set dialogBoxResponse ok"  -name "FF signal choice"
        
        APSCheckButtonFrame .cb1 -parent $w.userFrame -label "Choose signals to FF: " \
          -orientation horizontal -variableList "doCorrectorFF doXRayBPMFF" \
          -buttonList {correctors "x-ray bpms"} \
          -contextHelp "Select doCorrectorFF if you have scanned from the minimum to 180 mm, and you don't care about xray bpms.\n\nOtherwise select doXRayBPMFF.
"
        APSFrame .sgf -parent $w.userFrame -label "Smoothing parameters (Savitzky-Golay)" \
          -contextHelp "Set smoothing parameters for Savitzky-Golay algorithm.  If desired, the data is first interpolated on a uniform grid with the spacing of the low-gap points.  It is then smoothed by fitting a polynomial of user-selected order to a user-specified number of points at lower and higher gap values.  Each point is replaced by the interpolated value of this polynomial at its gap position."
        
        APSRadioButtonFrame .rb1 -parent $w.userFrame.sgf.frame -label "Interpolate prior to smoothing? " \
          -orientation horizontal -variable doInterpolate \
          -buttonList {Yes No} -valueList {1 0} 
        APSLabeledEntry .le0 -parent $w.userFrame.sgf.frame -label "Lower points: " \
          -textVariable smoothLowerPts -type integer
        APSLabeledEntry .le1 -parent $w.userFrame.sgf.frame -label "Higher points: " \
          -textVariable smoothHigherPts -type integer
        APSLabeledEntry .le2 -parent $w.userFrame.sgf.frame -label "Order: " \
          -textVariable smoothOrder -type integer 
        APSFrame .min -parent $w.userFrame -label "Gap Threshold (mm)"
        set count 5
        set parent $w.userFrame.min.frame 
        foreach ID $IDList {
            if $count==5 {
                set widget [APSUniqueName .]
                APSFrame $widget -parent $parent -label "" -relief flat -packOption "-side left -anchor n"
                set w1 $parent$widget.frame
            }
            incr count -1
            if !$count {
                set count 5
            }
            
            set widget [APSUniqueName .]
	    set ${gapThresholdVar}($ID.gapThreshold) 140.0
            APSLabeledEntry $widget -parent $w1 -textVariable ${gapThresholdVar}($ID.gapThreshold) \
              -label [APSMenuFramePadLength "$ID" 6] -width 6 -packOption  "-side top" \
              -contextHelp "The minimum gap for non-zero offsets and gains for $ID"
        }
        update
        tkwait window $w
        update
        if [string match $dialogBoxResponse cancel] return
        if {!$doXRayBPMFF && !$doCorrectorFF} return
    }
    if $smoothLowerPts<1 {
        set smoothLowerPts 1
    }
    if $smoothHigherPts<1 {
        set smoothHigherPts 1
    }
    if $smoothOrder<1 {
        set smoothOrder 1
    }
    
    set insertRow /tmp/[APSTmpString].insert
    foreach id $IDList {
        APSAddToTmpFileList -ID gapscan -fileList $insertRow.$id
        if [catch {exec sddsmakedataset $insertRow.$id -defaultType=double \
                     -col=ReadbackValue -data=[expr [set ${gapThresholdVar}($id.gapThreshold)] + 0.02] \
                     -col=ActuatorValue -data=0 } result] {
            return -code error "APSSRPrepareFFData6: $result"
        }
    }
    if $doInterpolate {
        set interpCommand "sddsinterp -pipe -fillIn -column=ReadbackValue,ActuatorValue"
    } else {
        set interpCommand cat
    }
    set shutterPVList ""
    foreach sector $sectorList {
        lappend shutterPVList S[format %.2d $sector]ID-FEEPS:PS1:OpenedM
    }
    
    if [catch {exec cavget -list=[join $shutterPVList ,] -list=.ONAM -pend=30} shutterOneNameList] {
        return -code error "APSSRPrepareFFData7: $shutterOneNameList"
    }
    set columns [exec sddsquery -col $input]
    if [string match "*DCCT:CurrentM*" $columns] {
        set corrType :DCCT:CurrentM
    } else {
        set corrType :PS:SetCurrentC
    }
    set smoothFileList(x) ""
    set smoothFileList(y) ""
    set rawFileList(x) ""
    set rawFileList(y) ""
    #this is no longer needed
    #foreach name {GapName TestPVName LowerLimit UpperLimit} {
     #   set test${name}List [exec sdds2stream -col=$name /home/helios/oagData/sr/IDs/IDFeedforwardTest.sdds ]
    #}
    foreach xy {x y} plane {h v} Plane {H V} config [list $hSteeringConfig $vSteeringConfig] {
        foreach sector $sectorList gapSP $gapSPList id $IDList {
            set shutterPV S[format %.2d $sector]ID-FEEPS:PS1:OpenedM
            set shutterIndex [lsearch $shutterPVList $shutterPV]
            set shutterOneName [lindex $shutterOneNameList $shutterIndex]
            set lowerLimit -0.5
            set upperLimit 0.5
            set gapPV [regsub {GapSP} $gapSP {:GapM}]
            set gapReadback $gapPV
           
            APSSetVarAndUpdate status "Processing $xy plane $id..."
            set gapThreshold [set ${gapThresholdVar}($id.gapThreshold)]
            if $doCorrectorFF {
                set PVSuffix PS:SetCurrentC
                set sector1 [expr int($sector+1)]
                # Find the correctors in the configuration that bracket this ID.
                if [string length $statusCallback] {
                    eval $statusCallback {"$config"}
                }
                if [catch {exec sddsprocess $config -pipe=out \
                             -nowarning -match=parameter,NameType=CorrectorNames \
			       -match=column,Name=S$[format %02d $sector]B:*$Plane*,Name=S[format %02d $sector1]A:*$Plane*,| \
                             | sdds2stream -column=Name -pipe} corrList] {
                    return -code error "APSSRPrepareFFData9: $corrList"
                }
                if [string length $statusCallback] {
                    eval $statusCallback {"Working on sector $sector $gapReadback: $corrList"}
                }
                foreach plainCorrector $corrList {
                    # set corrector [os editstring i/SFB:/ $plainCorrector]
                    set corrector $plainCorrector
		    
                    if [string length $statusCallback] {
                        eval $statusCallback {"Working on $corrector"}
                    }
                    # Process data for each corrector for this ID
                    set newFile $tmpRoot.$corrector
                    APSAddToTempFileList $newFile $newFile.raw
                    #shang 3/3/2016 make the actuator values of last two rows to be zero
		    
                    if [catch \
                          {eval exec sddsconvert $input -pipe=out \
                             -retain=column,$gapReadback,${corrector}$corrType,Time,Sigma${corrector}$corrType \
                             -rename=column,$gapReadback=ReadbackValue \
                             -rename=column,${corrector}$corrType=ActuatorValue \
                             -rename=column,Sigma${corrector}$corrType=ActuatorSigma \
                             | sddsprocess -pipe \
                             -filter=col,ReadbackValue,0,180 \
                             "-print=parameter,ReadbackName,$gapPV" \
                             "-print=parameter,ActuatorName,${plainCorrector}:$PVSuffix" \
			       | sddssort -pipe -column=ReadbackValue -column=Time \
                             | sddsprocess -pipe \
                             -process=ActuatorValue,last,ActuatorValue0 \
                             {"-redefine=column,ActuatorValue,ActuatorValue ActuatorValue0 -,units=A"} \
                             | sddsbreak -pipe -gapin=ReadbackValue,amount=0.01 \
                             | sddsprocess -pipe -clip=0,1,invert \
                             | sddscombine -pipe -merge \
                             | tee $newFile.raw \
                             | $interpCommand \
                             | sddssmooth -pipe -newColumns \
                             -savitzky=$smoothLowerPts,$smoothHigherPts,$smoothOrder \
                             -column=ActuatorValue \
                             | sddsconvert -pipe \
                             -rename=column,ActuatorValue=ActuatorValueRaw,ActuatorValueSmoothed=ActuatorValue \
                             | sddsprocess -pipe \"-redefine=par,lastRow,n_rows 1 -,type=long\"  \
                             | sddsprocess -pipe=in $newFile \
                             \"-redefine=col,ActuatorValue,lastRow i_row - 2 < ? 0 : ActuatorValue $ \" \
                         } result] { 
                        APSSetVarAndUpdate status "APSSRPrepareFFData11: $result" 
                        bell
                        continue
                    }
                    lappend smoothFileList($plane) $newFile
                    lappend rawFileList($plane) $newFile.raw
                }
            }
            if $doXRayBPMFF {
		#no xray bpm yet...
            }
        }
    }
    foreach plane {h v} {
        # combine all the results
        if [catch {eval exec sddscombine $smoothFileList($plane) -pipe=out \
                     | sddsprocess -pipe=in $input.FF.$plane \
                     -reprint=par,plane,$plane \
                     {"-print=parameter,Label,%s  %s,ReadbackName,ActuatorName"}} result] {
            return -code error "APSSRPrepareFFData12: $result"
        }
        if [catch {eval exec sddscombine $rawFileList($plane) -pipe=out \
                     | sddsprocess -pipe=in $input.raw.$plane \
                     {"-print=parameter,Label,%s  %s,ReadbackName,ActuatorName"}} result] {
            return -code error "APSSRPrepareFFData13: $result"
        }
        if {!$pem} {
            # don't plot ht epages that are just SFB: copies of the normal correctors.
            exec sddsplot -split=page -sep=page -groupby=page "-title=$input plane $plane" \
              -ylabel=@ActuatorName -xlabel=@ReadbackName -layout=2,2 \
              -match=param,ActuatorName=SFB:*,! \
              -column=ReadbackValue,ActuatorValue $input.FF.$plane -graph=line,type=1 -legend=spec=Fit \
              -column=ReadbackValue,ActuatorValue $input.raw.$plane  -graph=sym -legend=spec=Data &
        }
    }
    
    if [catch {exec sddscombine $input.FF.h $input.FF.v $input.FF  -overwrite} result] {
        return -code error "APSSRPrepareFFData14: $result"
    }
   
    exec cp $input.FF $input.FF.1
    if [catch {exec sddssort $input.FF.1 -pipe=out -par=ReadbackName \
		   | sddssort -pipe=in $input.FF -col=ReadbackValue -unique } result] {
	return -code error "APSSRPrepareFFData15: $result"
    }
    file delete -force $input.FF.1
    APSSetVarAndUpdate status "Prepare FF data Done."
}

# When the gap is opened beyond the minimum gap value 
# (given by user in "Prepared FF data ..." dialog)
# the OffsetAO, GainAO for all four xbpm readbacks (P1x,P1y,P2x,P2y)
# associated with that device are to be forced to zero at minimum gap + 0.1 mm.
# Similarly, if the gap is then closed to less than the minimum gap, 
# the feedforward should "wake up"
# and get the OffsetAO's from the FF lookup table and get the GainAO's from SCR SBPMs operator
# preferred file.
#Called by APSSRPrepareFFData
proc APSSRGenerateGainActuatorFile {args} {
    set input ""
    set shutterPVList ""
    set shutterOneNameList ""
    set gapThresholdVar ""
    set statusCallback ""
    set CUSectorList ""
    set fixedIDList ""
    set fixedGapList ""
    set togetherList ""
    set gapDiffTolerance 0.25
    set fixedGapTolerance 0.1
    APSStrictParseArguments {input shutterPVList shutterOneNameList gapThresholdVar statusCallback CUSectorList fixedIDList fixedGapList togetherList gapDiffTolerance fixedGapTolerance}

    if ![string length $input] {return -code error "input file is not provided."} 
    set filename $input.gain.FF
    set inputfile $input.FF
    set IDList [exec sdds2stream $inputfile -par=ReadbackName]
    APSSetVarAndUpdate status "Generating $filename for gain FF..."
    
    global $gapThresholdVar
    set tmpfile /tmp/[APSTmpString]
    set fileList ""
    #replace the UBOP file by "id xbpm position gain reference" file
    if [catch {exec sddsprocess /home/helios/oagData/SCR/snapshots/SBPMs/SBPMs-IDXrayGainReference.gz \
                 -match=col,ControlName=*ID*:GainAO $tmpfile.gain } result] {
        return -code error $result
    }
    APSAddToTmpFileList -ID 1 -fileList "$tmpfile.gain"
    set newIDs ""
    
    APSSetVarAndUpdate status "Preparing gain FF data..."
    foreach name {GapName TestPVName LowerLimit UpperLimit} {
        set test${name}List [join [exec sdds2stream -col=$name /home/helios/oagData/sr/IDs/IDFeedforwardTest.sdds]]
    }
    foreach ID $IDList {
        if [lsearch -exact $newIDs $ID]>=0 {
            continue
        }
        lappend newIDs $ID
        set id [regsub {:Gap} $ID {}]
        set gapThreshold [set ${gapThresholdVar}($id.gapThreshold)]
        if [string length $statusCallback] {
            eval $statusCallback {"ID $ID threshold: $gapThreshold"}
        }
        set sector [scan $ID ID%2d]
        set shutterPV EPS:[format %.2d $sector]:ID:PS1:POS:CL
        set shutterIndex [lsearch $shutterPVList $shutterPV]
        set shutterOneName [lindex $shutterOneNameList $shutterIndex]
        set lowerLimit -0.5
        set upperLimit 0.5
        set sector [format %.0f $sector]
        if {$sector==26 || $sector==30} {
            set shutterPV ""
        }
        if [lsearch $togetherList $sector]>=0 {
            set enable2PV ID[format %02d $sector]:DualGapDiff
            set enable2UpperLimit $gapDiffTolerance
            set enable2LowerLimit -$gapDiffTolerance
        } else {
            set id ID[format %02d [scan $ID ID%ld]]
            set index [lsearch -regexp $fixedIDList $id]
            if $index<0 {
                set enable2PV ""
                set enable2UpperLimit 0
                set enable2LowerLimit 0
            } else {
                set enable2PV [lindex $fixedIDList $index]:Gap
                set gap [lindex $fixedGapList $index]
                set enable2UpperLimit  [expr $gap + $fixedGapTolerance]
                set enable2LowerLimit  [expr $gap - $fixedGapTolerance]
            }
        }
        if {$ID=="ID07us"} {
            #ID07us gap FF valid only when ID07ds (HSCU) is off
            set enable2PV ID07ds:MainOn
            set enable2UpperLimit -0.5
            set enable2UpperLimit 0.5
        }
        set idIndex [lsearch $testGapNameList [regsub {:Gap} $ID {}]]
        if {$idIndex>=0} {
            set testPV [lindex $testTestPVNameList $idIndex]
            set testLower [lindex $testLowerLimitList $idIndex]
            set testUpper [lindex $testUpperLimitList $idIndex]
        } else {
            set testPV ""
            set testLower 0
            set testUpper 0
        }
        foreach plane {x y} {
            set bpmList {P1 P2}
            set shutterList [list "" $shutterPV]
            #for canted undulator 12, 21, 23, 24, P1 is associated with us only, P2 is associated with ds only
            if [lsearch $CUSectorList $sector]>=0 {
                if [regexp {us} $ID] {
                    set bpmList P1
                    set shutterList [list ""]
                } else {
                    set bpmList P2
                    set shutterList [list $shutterPV]
                }
            }
            foreach pv $bpmList shutterPV1 $shutterList {
                set gain S${sector}ID:$pv:ms:$plane:GainAO
                if [catch  {exec sddsprocess $tmpfile.gain -pipe=out -match=col,ControlName=$gain \
                              | sdds2stream -pipe -col=ValueString} value] {
                    APSSetVarAndUpdate status "$gain pv does not exist in gain reference file: $result!"
                } else {
                    if [catch {expr $value * 2.0} result] {
                        APSSetVarAndUpdate status "the value for $gain ($value) in the gain reference file is not a number"
                        bell
                        continue
                    }
                    if [catch {exec sddsmakedataset $tmpfile.$gain \
                                 -defaultType=double -col=ReadbackValue \
                                 -data=$gapThreshold,[expr $gapThreshold +0.02] \
                                 -col=ActuatorValue -data=$value,0 \
                                 -par=ScanTogetherSectors,type=string "-data=$togetherList" \
                                 -par=ReadbackName,type=string -data=${ID} \
                                 -par=ActuatorName,type=string -data=$gain \
                                 -par=FeedforwardEnablePV,type=string -data=$shutterPV1 \
                                 -par=FeedforwardEnableLowerLimit,type=double -data=$lowerLimit \
                                 -par=FeedforwardEnableUpperLimit,type=double -data=$upperLimit \
                                 -par=FeedforwardEnable2PV,type=string -data=$enable2PV \
                                 -par=FeedforwardEnable2LowerLimit,type=double -data=$enable2LowerLimit \
                                 -par=FeedforwardEnable2UpperLimit,type=double -data=$enable2UpperLimit \
                                 -par=FeedforwardDisabledOutputValue,type=double -data=0.0 \
                                 -par=FeedforwardTestPV,type=string -data=$testPV  \
                                 -par=FeedforwardTestLowerLimit,type=double -data=$testLower \
                                 -par=FeedforwardTestUpperLimit,type=double -data=$testUpper \
                             } result] {
                        return -code error $result
                    }
                    lappend fileList $tmpfile.$gain
                }
            }
        }
    }
    lappend fileList $tmpfile.enable2
    APSAddToTmpFileList -ID gainFF -fileList $fileList
    if [catch {eval exec sddscombine $fileList $filename -overwrite} result] {
        return -code error $result
    }
    APSSetVarAndUpdate status "Done preparing gain FF data."
}

#Called by APSMpSRGapFullScan
proc APSMakeIDGapFullScanSelection {args} {
    global IDUseFlag AllIDList CUSectorList APSMpSRGapFullScan
    global checkButtonVariableList entryVariableList checkButtonCommandList
    
    if [catch {APSSRGetIDGapFullInfo} result] {
        return -code error $result
    }
    set APSMpSRGapFullScan(closeInterval) 3
    APSDialogBox .idscan -name "ID Full Gap Scan Selection" -cancelButton 0
    set parent .idscan.userFrame
    APSCheckButtonEntryFrame .id -parent .idscan.userFrame -buttonList \
      $AllIDList -variableList $checkButtonVariableList -allNone 1 \
      -limitPerRow 7 -entryVariableList $entryVariableList \
      -label "ID selection and Minimum gaps specification" -commandList $checkButtonCommandList \
      -contextHelp "Check ID gaps to include in the scan."
    
    APSFrameGrid .grid -parent $parent.id.frame.frame5 -yList {y1 y2}
    APSButton .default -parent $parent.id.frame.frame5.grid.y1 -text "default" -command "APSSRResetDefaultSelection -selectType default" -size small 
    APSButton .defaulta -parent $parent.id.frame.frame5.grid.y2 -text "default a" -command "APSSRResetDefaultSelection -selectType defaultA" -size small
    APSButton .defaultb -parent $parent.id.frame.frame5.grid.y2 -text "default b" -command "APSSRResetDefaultSelection -selectType defaultB"  -size small

    APSFrame .par -parent $parent -label "Gap Scan Parameters"
    APSFrameGrid .grid -parent $parent.par.frame -xList {x1 x2} -label "Minimum Gap Value"
    set w1 $parent.par.frame.grid.x1
    set w2 $parent.par.frame.grid.x2
    APSLabeledEntry .max -parent $w1 -label "Maximum gap value (mm):" -width 10 \
      -textVariable APSMpSRGapFullScan(maxGap) -contextHelp "Enter the maximum gap for the scan."
    APSLabeledEntry .min -parent $w2 -label "Minimum gap value (mm):" -width 10 \
      -textVariable APSMpSRGapFullScan(minGap) \
      -contextHelp "Enter the minimum gap for the scan.  Overrides individual gaps."
    bind $w2.min.entry <Return> "APSSRSetAllMinGaps"
    APSLabeledEntry .maxStep -parent $w1 -width 10  -label "Maximum step value (mm):" \
      -textVariable APSMpSRGapFullScan(maxStep) \
      -contextHelp "Enter the maximum step for the scan."
    APSLabeledEntry .minStep -parent $w2 -width 10 -label "Minimum step value (mm):" \
      -textVariable APSMpSRGapFullScan(minStep) \
      -contextHelp "Enter the minimum step for the scan.  Overrides individual gaps."
    
    APSLabeledEntry .stepInt -parent $w1 -width 10 \
      -label "Number of the same size steps:" -textVariable APSMpSRGapFullScan(stepInterval) \
      -contextHelp "Enter the number of the same size steps."
    APSLabeledEntry .factor -parent $w2 -width 10 \
      -label "Factor for a step increase: " \
      -textVariable APSMpSRGapFullScan(factor) \
      -contextHelp  "Enter a factor for a step increase expression."
    
    APSLabeledEntry .ptToAve -parent $w2 -width 10 \
      -label "Readings to average: " -textVariable APSMpSRGapFullScan(numToAve) \
      -contextHelp "Number of readings to average at each gap value." 
    
    APSLabeledEntry .delta -parent $w1 -width 10 \
      -label "Delta to Minimum Gap (mm):" -textVariable APSMpSRGapFullScan(deltaToMinGap) \
      -contextHelp "The different value between moving the gap to (close to minimum) and the device limit (minimum gap). This delta value is to prevent from moving the gap to the minimum value which may cause problems"
    bind $w1.delta.entry <Return> "APSSRUpdateMinGap"
    APSLabeledEntry .pause -parent $w2 -width 10 \
      -label "Pause for orbit correction after gap change (s): " \
      -textVariable APSMpSRGapFullScan(pause) -contextHelp \
      "Length of pause after gap change is completed to allow orbit correction to converge."
    
    
    APSRadioButtonFrame .rbf -parent $w1 -label "Reading type: " \
      -orientation horizontal -variable APSMpSRGapFullScan(readingType) \
      -buttonList "ms msAve mswAve" \
      -valueList "ms msAve mswAve" -contextHelp \
      "Choose the type of BPM reading you want to use.  The names of the BPM data will be the same no matter what you choose!"
    APSRadioButtonFrame .transfer -parent $w1 \
      -label "Transfer orbit at each iteration:" \
      -variable APSMpSRGapFullScan(transferOrbit) -buttonList {Yes No} -valueList {1 0} \
      -packOption "-side left" -orientation horizontal -contextHelp \
      "Transfer orbit at each iteration to remove systematic dispersion orbit."
    APSLabeledEntry .interval -parent $w2  -textVariable APSMpSRGapFullScan(closeInterval) \
      -label "Interval of closing gaps (seconds):" -width 10 \
      -contextHelp "Due to on-going restrictions on the SR rf systems, we need to tagger the closing of the gaps by a selectable time interval, i.e. issuing the next gap close command after T seconds of the previous one."
    tkwait window .idscan
    
    set selectList ""
    foreach id $AllIDList {
        if $IDUseFlag($id) {
            lappend selectList $id
        }
    }
    return $selectList
}

#Called by SR PEM
proc APSMpSRGapFullScan {args} {
    global execMode AllIDList CUSectorList
    set IDList ""
    set minGapList ""
    set numToAve 2
    set maxGap 34
    set maxStep 30
    set minStep 0.25
    set stepInterval 7
    set factor 2
    set transferOrbit 0
    set readingType LowPass1s
    set deltaToMinGap 0.01
    set pause 10
    set gapVar ""
    set rootname IDscan
    set fullScan 0
    set fixedIDList ""
    set fixedGapList ""
    set togetherList ""
    set closeInterval 3
    set scanOnly 0
    set keepFFrunning 0
    APSStrictParseArguments {IDList  minGapList numToAve pause maxGap gapVar rootname fixedIDList fixedGapList togetherList keepFFrunning \
                               transferOrbit maxStep minStep stepInterval deltaToMinGap factor fullScan closeInterval scanOnly}
    set oldMode $execMode
    APSMpStep "Enter full scan gap pem..."
    if ![llength $IDList] {
        global APSMpSRGapFullScan
        
        APSMpStep "Select IDs..."
        if [catch {APSMakeIDGapFullScanSelection} IDList] {
            APSMpReturn error "APSMpSRGapFullScan: $IDList"
        }
        
        if ![llength $IDList] {
            APSMpReturn error "APSMpSRGapFullScan: No IDs selected."
        } 
        
        set nameList {numToAve pause maxGap \
                        transferOrbit maxStep minStep stepInterval deltaToMinGap factor}
        foreach nm $nameList {
            set $nm $APSMpSRGapFullScan($nm)
        }
        set gapVar APSMpSRGapFullScan
        global FixedIDList FixedGapList TogetherList
        APSMpStep "Check Undulator gaps..."
        if [catch {APSSRCheckUndulatorScanType -IDList $IDList} result] {
            APSMpReturn error "APSMpSRGapFullScan(1 - APSSRCheckUndulatorScanType): $result"
        }
        set minGapList ""
        foreach ID $IDList {
            lappend minGapList $APSMpSRGapFullScan($ID.minGap)
        }
        set fixedIDList $FixedIDList
        set fixedGapList $FixedGapList
        set togetherList $TogetherList
        set closeInterval $APSMpSRGapFullScan(closeInterval)
    }
    
    foreach ID $fixedIDList {
        set index [lsearch $IDList $ID]
        if $index>=0 {
            set IDList [lreplace $IDList $index $index]
            set minGapList [lreplace $minGapList $index $index]
        }
    }
    if [lsearch -exact $$IDList ID35ds]>=0 {
        if [catch {APSCloseID35Shutter} result] {
            APSMpReturn error "APSMpSRGapFullScan: Error in closing ID35 shutter: $result."
        }
    }
    APSSetVarAndUpdate status "Move $IDList to minimum gaps..."
    APSMpStep "Move $IDList to minimum gaps..."
    APSMpStep "FixedID: $fixedIDList  $fixedGapList"
    APSMpStep "togetherID: $togetherList"
    if [catch {APSSRMoveIDGaps -IDList $IDList -minGapList $minGapList -closeInterval $closeInterval  \
                 -fixedIDList $fixedIDList -fixedGapList $fixedGapList -togetherList $togetherList \
                 -moveTo MinimumGap -open 0} result] {
        APSMpReturn error "APSMpSRGapFullScan: $result"
    }
    APSSetVarAndUpdate status "Wait for gaps stop moving..."
    APSMpStep "Wait for gaps stop moving ..."
    set waitList [concat $IDList $fixedIDList]
    if [catch {exec cavget -list=[join $waitList ,] -list=:MotorDriveStatusM -num -pend=30} valList] {
        APSMpReturn error "APSMpSRGapFullScan(unable to read the motor status): $valList"
    }
    #APSMpStep "motor status: $valList"
    set len [llength $waitList]
    for {set i 0} {$i<$len} {incr i} {
        set val [lindex $valList $i]
        if !$val {
            set waitList [lreplace $waitList $i $i]
        }
    }
    
    if [catch {APSMpCheckAndWaitForIDs -deviceList $waitList} results] {
        #if {[APSPEMTKDialog .warningDialog -text "APSMpSRGapFullScan: Problem occurred while waiting ID gaps\n$results"] == 1} {
        #    APSMpReturn error "APSMpSRGapBriefScan: $results"
        #}
        set answer [APSMultipleChoice [APSUniqueName .] -modal 1 \
                      -question "Move ID gaps for full scan failed - $result, would you like continue or abort?" \
                      -returnList {1 0} \
                      -labelList {Continue Abort}]
        if !$answer {
            APSMpReturn error "APSMpSRGapFullScan was aborted due to - $result."
        }
    }
    APSSetVarAndUpdate status "Start full scan ..."
    APSMpStep "Scan gaps ..."
    while {1} {
        while {1} {
            if [catch {APSSRGapFullScan -pem 1 -pause $pause -rootname $rootname \
                         -numToAve $numToAve -keepFFrunning $keepFFrunning \
                         -maxGap $maxGap \
                         -IDList $IDList -minGapList $minGapList \
                         -fixedGapList $fixedGapList \
                         -fixedIDList $fixedIDList \
                         -togetherList $togetherList \
                         -transferOrbit $transferOrbit \
                         -readingType $readingType \
                         -maxStep $maxStep \
                         -minStep $minStep \
                         -stepInterval $stepInterval \
                         -deltaToMinGap $deltaToMinGap \
                         -factor $factor } result] {
                set answer [APSMultipleChoice [APSUniqueName .] -modal 1 \
                              -question "Gap Scan failed - $result, would you like scan again or abort" \
                              -returnList {1 0} \
                              -labelList {Scan_Again Abort}]
                if !$answer { 
                    APSMpReturn error "APSMpSRGapFullScan was aborted due to - $result."
                }
            } else {
                set outputFile $result
                APSSetVarAndUpdate status "Gap full scan stored in file: $outputFile"
                break
            }
        }
        if ![file exist $outputFile] {
            APSMpReturn error "APSMpSRGapFullScan: gap scan output file -- $outputFile does not exist!"
        }
        if !$scanOnly {
            APSMpStep "Prepare gap FF data for $outputFile..."
            if [catch {APSSRPrepareFFData -input $outputFile -CUSectorList $CUSectorList \
                         -gapThresholdVar $gapVar -pem 1} result] {
                APSMpReturn error "APSMpSRGapFullScan: error in preparing FF data: $result"
            }
            if [catch {APSSRReviewGapFFData -gapScanFile $outputFile -pem 1} result] {
                APSMpReturn error "APSMpSRGapFullScan: $result"
            }
            set answer [APSMultipleChoice [APSUniqueName .] -modal 1 \
                          -question "review plots and\n\ni) repeat scan, or\nii) install,  or\niii) continue without install or\niv) abort" \
                          -returnList {repeat install continue abort} \
                          -labelList {Repeat-scan Install Continue-without-install Abort}]
            if {$answer=="install"} {
                APSMpStep "Install $outputFile to FF..."
                if [catch {APSSRInstallFeedForward -filename $outputFile -fullScan $fullScan -pem 1} result] {
                    APSMpReturn error "APSMpSRGapFullScan: $result"
                }
                break
            } elseif {$answer=="continue"} {
                break
            } elseif {$answer=="abort"} {
                APSMpReturn error "APSMpSRGapFullScan was aborted."
            }
        } else {
            break
        }
    }
    APSMpReturn ok
}

#Called by APSMpSRGapFullScan
proc APSSRInstallFeedForward {args} {
    set outputDir /home/helios/oagData/sr/gapScans/data/gapScanFull
    set rootname IDscan
    set filename ""
    set fullScan 0
    set pem 0
    set ignoreXbpms 0
    APSStrictParseArguments {rootname outputDir filename fullScan pem ignoreXbpms}
    
    if !$ignoreXbpms {
        # set outputDir  /home/helios/oagData/sr/gapScans/data/gapScanFull
        set ffDir /home/helios/oagData/feedForwardFiles/IDgap
        set extraFF /home/helios/oagData/feedForwardFiles/IDgap/ID01.FF.extra
    } else {
        # set outputDir /home/helios/oagData/sr/gapScans/data/corretor
        set ffDir /home/helios/oagData/sr/gapScans/data/corrector
        set extraFF /home/helios/oagData/sr/gapScans/data/corrector/FF.extra
    }
    if ![string length $filename] {
        global fileSelection
        set fileSelection ""
        set files [lsort -decreasing [glob -nocomplain $outputDir/${rootname}*.sdds]]
        if ![llength $files] {
            return -code error "No scan data found in $outputDir."
        }
        regsub -all $outputDir/ $files {} files0
        APSScrolledListWindow [APSUniqueName .] -name "Select a data file" \
          -label "Select a full scan data file for installing FF"  -selectMode single \
          -itemList $files0 -selectionVar fileSelection \
          -directory $outputDir -appendDescription 1 \
          -rootnameOnly 1 -descriptionName Description 
        tkwait variable fileSelection
        if ![string length $fileSelection] {
            return -code error "APSSRInstallFeedForward: no file was chosen for installing offsets and rates."
        }
        set filename $outputDir/[lindex [lindex $fileSelection 0] 0]
    }
    if ![file exist $filename.FF] {
        return -code error "$filename.FF does not exist, you need prepare the FF for $filename first."
    }
    set descList [split [exec sdds2stream -par=Description $filename -page=1] ,]
    if {[llength $descList]<10 && $fullScan} {
        set fullScan 0
    }
    #set filename $filename.FF
    #set ffDir /home/helios/oagData/feedForwardFiles/IDgap
    set name [APSNextGenerationedName -directory $ffDir -name FF.0001 -separator . -newFile 1]
    set IDList [lsort -unique [exec sdds2stream -par=ReadbackName $filename.FF]]
    #regsub {.FF} $filename "" gapScanFile
    # if !$pem {
    #	if [catch {APSSRReviewGapFFData -gapScanFile $filename} result] {
    #    return -code error "APSSRInstallFeedForward: $result" 
    #	}
    #    }
    set oldDir [pwd]
    cd $ffDir
    if $fullScan {
        #if it is full scan then link the FF to $filename.FF
        #  exec cp $filename.FF $name
        if [catch {exec sddscombine $filename.FF $extraFF \
                     -pipe=out \
                     | sddssort -pipe=in -par=ReadbackName -par=ActuatorName $name} result] {
            cd $oldDir
            return -code error "APSSRInstallFeedForward: $result -- FF not installed."
        }
    } else { 
        #if it is not full scan, then combine the $filename.FF into FF
        set matchOpt ""
        foreach ID $IDList {
            lappend newList $ID
            if ![string length $matchOpt] {
                set matchOpt -match=par,ReadbackName=$ID,!
            } else {
                append matchOpt ,ReadbackName=$ID,!,&
            }
        }
        set tmpfile /tmp/[APSTmpString]
        APSAddToTmpFileList -ID gapscan -fileList $tmpfile.1
        lappend fileList $tmpfile.1 
        lappend fileList $filename.FF
        # if {!$ignoreXbpms && [regexp {ID01ds} $IDList]} {
        #ID01ds.FF.extra was removed in $tmpfile.1 for ID01ds, and it is not included in $filename.FF either, need to add it back.
        #    lappend fileList /home/helios/oagData/feedForwardFiles/IDgap/ID01.FF.extra
        #} 
        if [catch {exec sddsprocess FF $matchOpt $tmpfile.1 -nowarnings
            eval exec sddscombine $fileList -pipe=out \
                     | sddsprocess -pipe -reprint=par,Filename,$filename.FF \
                     | sddssort -pipe=in -par=ReadbackName -par=ActuatorName $name} result] {
            cd $oldDir
            return -code error "APSSRInstallFeedForward: $result -- FF not installed."
        }
    }
    if !$pem {
        if ![APSYesNoPopUp "Please review the scan data in the plots, and then answer \"Yes\" or \"No\" for installing $name to FF or not ?"] {
            return -code error "APSSRInstallFeedForward: FF is not installed." 
        }
    }
    
    file delete -force FF
    exec ln -s $name FF
    #set the setpoint of scaned IDs to zero
    if {!$ignoreXbpms} {
        if [catch {APSZeroXrayBPMSetpoint -filename $filename} result] {
            return -code error "APSSRInstallFeedForward(Error zero scanned xbpm setpoint): $result"
        }
    }
    cd $oldDir
}

#Called by APSSRInstallFeedForward
proc APSZeroXrayBPMSetpoint {args} {
    set filename ""
    APSStrictParseArguments {filename}
    if {![string length $filename] || ![file exist $filename]} {
        return -code error "APSZeroXrayBPMSetpoint1: scan data file $filename does not exist!"
    }
    set desc [exec sdds2stream -par=Description $filename -page=1]
    set IDList [split [exec editstring $desc "-edit=%/\"IDs: //%/\"//"] ,]
    set sectors ""
    foreach ID $IDList {
        lappend sectors [scan $ID %ld]
    }
    set sectors [lsort -unique -integer $sectors]
    if [catch {exec cavput -list=S -list=[join $sectors ,] -list=ID: -list=P1,P2 -list=:ms: -list=x,y -list=:SetpointAO=0 -pend=30} result] {
        return -code error "APSZeroXrayBPMSetpoint1: error in setting xbpm setpoint to zero: $result"
    }
}

#Called by APSMpSRGapFullScan
proc APSSRReviewGapFFData {args} {
    set gapScanFile ""
    set pem 0
    set ignoreXbpms 0
    APSStrictParseArguments {gapScanFile pem ignoreXbpms}
    
    if ![string length $gapScanFile] {
        return
    }
    if ![file exist $gapScanFile] {
        return -code error "APSSRReviewGapFFData: $gapScanFile does not exist!"
    }
    
    set tmpfile /tmp/[APSTmpString]
    APSAddToTempFileList $tmpfile
    if [catch {exec sddsquery $gapScanFile -column -sddsOutput \
                 | sddssort -pipe -col=Name \
                 | sddsprocess -pipe=in $tmpfile.sectors \
                 -match=column,Name=*GapSP \
                 -scan=column,Sector,Name,S%ld,type=short  } result] {
        return -code error "APSSRReviewGapFFData: $result"
    }
    if [catch {exec sdds2stream $tmpfile.sectors -column=Sector} sectorList] {
        return -code error "APSSRReviewGapFFData: $sectorList" 
    }
    if [catch {exec sdds2stream $tmpfile.sectors -column=Name} gapSPList] {
        return -code error "APSSRReviewGapFFData: $gapSPList"
    }
    if ![llength $gapSPList] {
        return -code error "APSSRReviewGapFFData: no IDs varied in that file"
    }
    if !$pem {
        set optionList ""
        set columns [join [exec sddsquery -col $gapScanFile]]
        if {[regexp "SetCurrentC" $columns] || [regexp "DCCT:CurrentM" $columns]} {
            foreach sector $sectorList gapSP $gapSPList {
		set sectorf [format %02d $sector]
                foreach magnet {FH1 H1 FH2 FV1 V1 FV2} {
                    foreach nm {A B} {
			set PV S${sectorf}${nm}:${magnet}:DCCT:CurrentM
			set PV1 S${sectorf}${nm}:${magnet}:SetCurrentC
			if [lsearch $columns $PV]>=0 {
			    lappend optionList -column=$gapSP,$PV
			} elseif [lsearch $columns $PV1]>=0 {
			    
			    lappend optionList -column=$gapSP,$PV1
			}
		    }
                }
            }
	}
	if [llength $optionList] {
	    eval exec sddsplot $gapScanFile -graph=sym,connect -separate -layout=2,2 -filename $optionList &
	}
    }
    #no Xbpm data is collected for APSU yet.
    return
    if $ignoreXbpms {
        return
    }
    set  CUdataDir /home/helios/oagData/feedForwardFiles/IDgap
    if [catch {exec sddsprocess $CUdataDir/cantingLogic.sdds -pipe=out \
                 -filter=col,TreatedAsCU,1,1 \
                 | sdds2stream -pipe -col=Sector} CUSectorList] {
        return -code error "APSSRGetIDGapInfo: $CUSectorList"
    }
    set optionList ""
    foreach sector $sectorList gapSP $gapSPList {
        if [lsearch $CUSectorList $sector]>=0 {
	    lappend optionList -column=$gapSP,S${sector}ID*:x* 
            lappend optionList -column=$gapSP,S${sector}ID*:y*
            lappend optionList -column=$gapSP,S${sector}ID*:x*
            lappend optionList -column=$gapSP,S${sector}ID*:y*
        } else {
	    lappend optionList -column=$gapSP,S${sector}ID*:x*
	    lappend optionList -column=$gapSP,S${sector}ID*:y* -endp
        }
        
    }
    eval exec sddsplot $gapScanFile -graph=sym,connect -separate -layout=2,2 -filename $optionList &
}

#Called by APSSRGetIDGapFullInfo and APSUpdateFFTable
proc APSSRGapSetThresholds {args} {
    set thresholdVar ""
    APSStrictParseArguments {threshold lowThreshold lowThresholdSectorList thresholdVar}
    global $thresholdVar
    set idList [exec sdds2stream /home/helios/oagData/sr/IDs/devices.sdds -col=DeviceName]
    set thresholdList [exec sdds2stream -col=GapThreshold /home/helios/oagData/sr/IDs/devices.sdds]
    foreach ID $idList threshold $thresholdList {
        set ${thresholdVar}($ID.gapThreshold) [format %.1f $threshold]
    }
}

#Called by APSSRGetIDGapFullInfo
proc APSSRGetDeviceLimits {args} {
    set default 0
    set initial 0
    set gapVar ""
    set deltaToMinGap 0.01
    APSStrictParseArguments {default initial gapVar deltaToMinGap}
    
    if ![string length $gapVar] {
        return -code error "gapVar not provided."
    }
    global AllIDList checkButtonVariableList entryVariableList checkButtonCommandList IDDeviceLimit IDUseFlag AllgapList
    global $gapVar
    
    if [catch {exec sdds2stream -col=DeviceName /home/helios/oagData/sr/IDs/devices.sdds} AllIDList] {
        return -code error $AllIDList
    }
    if [catch {exec sdds2stream -col=GapName /home/helios/oagData/sr/IDs/devices.sdds} AllgapList] {
        return -code error $AllIDList
    }
    if [catch {exec sdds2stream -col=Default /home/helios/oagData/sr/IDs/devices.sdds} defaultList] {
        set defaultList ""
    }
   
    if [catch {exec cavget -list=[join $AllIDList ,] -list=:DeviceLimitM -pend=10 \
                 -label "-delimiter= "} IDDataList] {
        return -code error "Problem getting ID data: $IDDataList"
    }
    
    catch {regsub -all {:DeviceLimitM} $IDDataList {}} IDDataList
    
    array set IDDeviceLimit $IDDataList
    if $initial {
        set checkButtonVariableList ""
        set entryVariableList ""
        set checkButtonCommandList ""
    }
    set lockedIDList [APSIDGetBadDeviceList]
    set index 0
    if [catch {exec cavget -list=[join $AllIDList ,] -list=:MotorDriveStatusM } readyList] {
        return -code error "APSSRGetDeviceLimits(2a - cavget -list=[join $AllIDList ,] -list=:MotorDriveStatus): $readyList"
    }
    foreach ID  $AllIDList {
        set ${gapVar}($ID.minGap) [format %.2f [expr $IDDeviceLimit($ID) + $deltaToMinGap]]
        if {[llength $defaultList] && $default} {
            set IDUseFlag($ID) [lindex $defaultList $index]
            set ready [lindex $readyList $index]
            if !$ready {
                set IDUseFlag($ID) 0
            }
            if [lsearch -exact $lockedIDList $ID]>=0 {
                set IDUseFlag($ID) 0
            }
        }
        if $initial {
            lappend checkButtonVariableList IDUseFlag($ID)
            lappend entryVariableList ${gapVar}($ID.minGap)
            lappend checkButtonCommandList "APSUpdateIDScanSelection $ID"
        }
        incr index
    }
}

#Called by SRCollectGapFFData
proc APSSRSetGlobalMinimumGaps {args} {
    set minGap ""
    set gapVar ""
    set statusCallback ""

    APSStrictParseArguments {minGap gapVar statusCallback}
    global IDDeviceLimit $gapVar
    foreach ID [lsort [array names IDDeviceLimit]] {
        if {$minGap<$IDDeviceLimit($ID)} {
            if [string length $statusCallback] {
                eval $statusCallback {"The global min gap is less than the device limit of $ID, skip setting global min gap to $ID"}
            }
        } else {
            set ${gapVar}($ID.minGap) $minGap 
        }
    }
}

#Called by APSMakeIDGapFullScanSelection
proc APSSRResetDefaultSelection {args} {
    set selectType default
    set closeGap 0
    APSStrictParseArguments {selectType closeGap}
    set config /home/helios/oagData/sr/IDs/devices.sdds
    set selectType [regsub {default} $selectType {Default}]
    set selectType [regsub {group} $selectType {Group}]
    #set lockedIDList  [APSSRGetLockedIDDevices]
    set lockedIDList [APSIDGetBadDeviceList -closeGap $closeGap]
    switch $selectType {
        Default -
        DefaultA -
        DefaultB -
        Group1 -
        Group2 -
        Group3 -
        Group4 {
            #ok
        }
        default {
            return -code error "APSSRResetDeafultSelection: invalid selection type $selectType provided."
        }
    }
    global IDUseFlag
    if [catch {exec sdds2stream -col=DeviceName /home/helios/oagData/sr/IDs/devices.sdds} IDList] {
        return -code error "APSSRResetDeafultSelection1: $IDList"
    }
    if [catch {exec sdds2stream -col=$selectType /home/helios/oagData/sr/IDs/devices.sdds} valList] {
        return -code error "APSSRResetDeafultSelection2: $valList"
    }
    foreach ID $IDList val $valList {
        if {[llength $lockedIDList]>0 && [lsearch -exact $lockedIDList $ID]>=0} {
            #do not select locked ID (can not scan)
            set val 0
        }
        set IDUseFlag($ID) $val
    }
}

#Called by various procedures
proc APSSRGetLockedIDDevices {args} {
    #sdds processes and sddscasr takes 1 second to get locked list (this method not easy to understand)
    #while use tcl loop for get the status values for each id method, takes 3 seconds. (but this method
    #  is more direct and easier to understand.)
    return [APSIDGetBadDeviceList]
    
    global AllIDList
    set tmpRoot /tmp/[APSTmpString]
    set badList [ APSIDGetBadDeviceList]
    if [llength $badList] {
        if [APSYesNoPopUp "Following IDs [join $badList ,] can not be moved, do you want to skip these IDs and continue or abort?"] {
            foreach ID $badList {
                set index [lsearch -exact $AllIDList $ID]
                if $index>=0 {
                    set AllIDList [lreplace $AllIDList $index $index]
                }
            }
        } else {
            return -code error "[join $systemIDs ,] are in System Manager mode, you cannot move them."
        }
    }
    APSAddToTmpFileList -ID gapscan -fileList $tmpRoot
    return $badList 
}

#Called by SRCollectGapFFData and SRFitGapScan
proc APSSRDisableLockedIDDeviceSelect {args} {
    set buttonNameList ""
    set IDList "" 
    set enable 0
    set lockedIDList ""
    APSStrictParseArguments {buttonNameList IDList enable lockedIDList}
    if ![llength $lockedIDList] {
        #set lockedIDList [APSSRGetLockedIDDevices]
	set lockedIDList [APSIDGetBadDeviceList]
    }
    if !$enable {
        if ![llength $lockedIDList] {
            return
        }
        foreach ID $lockedIDList {
            set index [lsearch $IDList $ID]
            if {$index>=0} {
                [lindex $buttonNameList $index] configure -state disabled
            }
        }
    } else {
        foreach ID $IDList button $buttonNameList {
            if [lsearch $lockedIDList $ID]>=0 {
                $button configure -state disabled 
            } else {
                $button configure -state normal 
            }
        }
    }
}

#Called by APSSRCheckUndulatorScanType
proc APSSRSetupUndulatorGap {args} {
    global IDFixGap IDScanType doubleGapSectorList
    set configFile /home/helios/oagData/sr/IDs/dualGapConfig.sdds
    
    set doubleGapSectorList [exec sdds2stream -col=Sector $configFile]
    set fixedValueList [exec sdds2stream -col=FixedGap $configFile]
    set scanTypeList [exec sdds2stream -col=ScanType $configFile]
    set index 0
    foreach sector $doubleGapSectorList {
        set sectorf [format %02d $sector]
        set IDFixGap(S${sectorf}) [lindex $fixedValueList $index]
        if [catch {expr $IDFixGap(S${sectorf}) /2} result] {
        } else {
            set IDFixGap(S${sectorf}) [format %.3f $IDFixGap(S${sectorf})]
        }
        set IDScanType(S${sectorf}) [lindex $scanTypeList $index]
        incr index
    }
}

#Called by APSSRCheckUndulatorScanType
proc APSSRMakeIDUndulatorScanSelectionWidget {args} {
    set sectorList ""
    APSStrictParseArguments {sectorList}
    
    global IDFixGap IDScanType IDScanEnergy  undialogBoxChoice
    set undialogBoxChoice 0
    APSDialogBox .un -name "Undulator Gap Scan Option" -okCommand "set undialogBoxChoice ok" \
      -cancelCommand "set undialogBoxChoice cancel"
    
    set parent .un.userFrame
    APSFrame .f -parent $parent -label "Choose Scan Mode for Selected Double-gap Sectors" 
    set parent $parent.f.frame 

    APSFrame .t -parent $parent -label "Sector    fixed Gap(mm)" -relief flat -packOption "-side left -fill y" 
    foreach sector $sectorList {
	set sectorf [format %02d $sector]
        APSLabeledEntry .s$sector -parent $parent.t.frame -label "$sector     " -width 10 \
          -textVariable IDFixGap(S${sectorf})  \
          -contextHelp "the value of fixed ds gap when its us gap is being scanned." 
    }
    
    APSFrame .type -parent $parent -label "Scan Type" -relief flat -packOption "-side left -fill y" \
      -contextHelp "select whether to scan the ds and us gap together or scan one with another gap fixed or scan independently."
    foreach sector $sectorList {
        set var IDScanType(ID${sector})
        APSFrame .s$sector -parent $parent.type.frame -relief flat  
        set w $parent.type.frame.s$sector.frame
        $w configure -bd 0
        radiobutton $w.b1 -text "fix_ds" -value fix_ds -variable $var \
          -anchor w 
        pack $w.b1 -side left -fill x -in $w
        radiobutton $w.b2 -text "fix_us" -value fix_us -variable $var \
          -anchor w 
        pack $w.b2 -side left -fill x -in $w
        radiobutton $w.b3 -text "independent" -value independent -variable $var \
          -anchor w 
        pack $w.b3 -side left -fill x -in $w
        radiobutton $w.b4 -text "together" -value together -variable $var \
          -anchor w
        pack $w.b4 -side left -fill x -in $w
    }
    tkwait variable undialogBoxChoice
    if {$undialogBoxChoice=="cancel"} {
        return -code error "APSSRMakeIDUndulatorScanSelectionWidget (1): Undulator gap scan selection was cancelled."
    }
}

#Called by APSMpSRGapFullScan
proc APSSRCheckUndulatorScanType {args} {
    set IDList ""
    APSStrictParseArguments {IDList}
    global IDFixGap IDScanType  FixedIDList FixedGapList doubleGapSectorList TogetherList
    global IDMinGap APSMpSRCollectGapFFTable APSMpSRGapFullScan APSMpSRSingleGapScan
    
    if ![info exist doubleGapSectorList] {
        APSSRSetupUndulatorGap
    }
    set sectorList ""
    set FixedIDList ""
    set FixedGapList ""
    set TogetherList ""
    foreach ID $IDList {
	set sector [format %02d [scan $ID S%ld]]
	if {[lsearch $doubleGapSectorList $sector]>=0 && [lsearch $sectorList $sector]<0} {
	    lappend sectorList $sector
	}
    }
    if ![llength $sectorList] {
        return
    }
    if [llength $sectorList] {
        foreach sector $sectorList {
	    set sectorf [format %02d $sector]
            if {[lsearch S${sectorf}ID:DSID $IDList]>=0 && [lsearch S${sectorf}ID:USID $IDList]>=0} {
                set IDScanType(S$sectorf) together
            }
        }
        if [catch {APSSRMakeIDUndulatorScanSelectionWidget  -sectorList $sectorList} result] {
            return -code error "APSCheckUndulatorScanType(1 - APSSRMakeIDUndulatorScanSelectionWidget): $result"
        }
        foreach sector $sectorList {
	    set sector [format %02d $sector]
            set ID ""
            set ID1 "" 
            if {[set IDScanType(S$sectorf)]=="fix_ds"} {
		set ID S${sectorf}ID:DSID
		set ID1 S${sectorf}ID:USID
            } elseif {[set IDScanType(ID$sector)]=="fix_us"} {
		set ID S${sectorf}ID:DSID
		set ID1 S${sectorf}ID:USID
	    }  elseif {[set IDScanType(ID$sector)]=="together"} {
                lappend TogetherList [scan $sector %ld]
            }
            if [string length $ID] {
                set index [lsearch $IDList $ID]
                set index1 [lsearch $IDList $ID1]
                if {$index>=0} {
                    set IDList [lreplace $IDList $index $index]
                }
                if {$index1>=0} {
                    lappend FixedIDList $ID
                    lappend FixedGapList $IDFixGap(S$sectorf)
                }
            }
        }
    }
    
    foreach sector $TogetherList {
	set sectorf [format %02d $sector]
        set ds S${sectorf}ID:DSID
        set us S${sectorf}ID:USID 
        foreach nm {IDMinGap APSMpSRCollectGapFFTable APSMpSRGapFullScan APSMpSRSingleGapScan} {
            if [info exist $nm] {
                if {[set ${nm}($ds.minGap)] < [set ${nm}($us.minGap)]} {
                    set ${nm}($ds.minGap) [set ${nm}($us.minGap)]
                } elseif {[set ${nm}($ds.minGap)] > [set ${nm}($us.minGap)]} {
                    set ${nm}($us.minGap) [set ${nm}($ds.minGap)]
                }
            }
        }
    }
    APSSetVarAndUpdate status  "$FixedIDList"
    APSSetVarAndUpdate status  "$TogetherList"
    APSSetVarAndUpdate status  "$FixedGapList"
    return
}


#this procedure update gap feedforward with new gap thresholds
#Called by SRCollectGapFFData
proc APSUpdateFFTable {args} {
    set oldTable ""
    set newTable ""
    set statusCallback ""
    APSStrictParseArguments {oldTable newTable statusCallback}

    if ![string length $oldTable] {
        set oldTable /home/helios/oagData/feedForwardFiles/IDgap/FF
    }
    if ![string length $newTable] {
        set lastFile [file readlink $oldTable]
        set newTable [file dirname $oldTable]/[APSNextGenerationedName -name $lastFile -separator . -newFile 1 -directory [file dirname $oldTable]]
    }
    global gapThreshold
    APSSRGapSetThresholds -thresholdVar gapThreshold
    set tmpRoot /tmp/[APSTmpString]
    if [catch {exec sddssplit $oldTable -digits=3 -rootname=$tmpRoot} result] {
        return -code error "APSUpdateFFTable: $result"
    }
    set fileList [lsort [glob ${tmpRoot}???.sdds]]
    set files ""
    set update 0
    foreach file $fileList {
        set gap [exec sdds2stream -par=ReadbackName $file]
        set actuator [exec sdds2stream -par=ActuatorName $file]
        set id [regsub {\:Gap} $gap ""]
        if [catch {exec sddsprocess $file -clip=0,1,invert $file.last } result] {
            return -code error "APSUpdateFFTable2: $result"
        }
        set threshold [format %.0f [exec sdds2stream -col=ReadbackValue $file.last]]
        APSAddToTmpFileList -ID updateFF -fileList $file.last
        if {$threshold>$gapThreshold($id.gapThreshold)} {
            #only update the table when the new threshold is reduced
            set update 1
            set rows [exec sdds2stream -rows=bar $file]
            if {$rows==2} {
                #for gain data who has only two rows
                if [catch {exec sddsprocess $file -clip=1,0,invert $file.1 -redefine=col,ReadbackValue,$gapThreshold($id.gapThreshold)} result] {
                    return -code error  "APSUpdateFFTable3: $result"
                }
            } elseif {$rows>2} {
                if [catch {exec sddsprocess $file -filter=col,ReadbackValue,0,$gapThreshold($id.gapThreshold) $file.1} result] {
                    return -code error  "APSUpdateFFTable4: $result"
                }
            }
            if [catch {exec sddsprocess $file.last -redefine=col,ReadbackValue,[expr $gapThreshold($id.gapThreshold) + 0.02] $file.2
                exec sddscombine $file.1 $file.2 -merge $file.update} result] {
                return -code error  "APSUpdateFFTable5: $result"
            }
            APSAddToTmpFileList -ID updateFF -fileList "$file.1 $file.2"
            if [string length $statusCallback] {
                eval $statusCallback {"$actuator ($id) threhsold changed, old=$threshold, new=$gapThreshold($id.gapThreshold)"}
            }
            lappend files $file.update
        } else {
            lappend files $file
        }
    }
    if !$update {
        APSAlertBox .alert -errorMessage "No gap threshold has been reduced, the FF table ooes not need to be updated"
        return ""
    }
    APSAddToTmpFileList -ID udpateFF -fileList $fileList
    APSAddToTmpFileList -ID udpateFF -fileList $files
    set files [lsort $files]
    if [catch {eval exec sddscombine $files $newTable} result] {
        return -code error  "APSUpdateFFTable6: $result"
    }
    return $newTable
}

proc APSCheckGapScanConditions {args} {
    set keepFFrunning 0
    APSStrictParseArguments {keepFFrunning}
    
    if [catch {exec cavget -list=S:ID:GapFeedForwardRC.RUN -pend=30} running] {
        return -code error "APSSRCheckGapScanConditions(3 - cavget -list=S:ID:GapFeedForwardRC.RUN ): $description"
    }
    if $keepFFrunning {
        if !$running {
            if [catch {APSMpIDOffsetFeedforward -action start} result ] {
                return -code error "APSSRCheckGapScanConditions(5 - abort ID FF): $result"
            }
        }
    } else {
        if $running {
            if [catch {APSMpIDOffsetFeedforward -action stop} result ] {
                return -code error "APSSRCheckGapScanConditions(5 - abort ID FF): $result"
            }
        }
    }
    
    if [catch {exec cavget -list=S:OrbitControlLaw -list=X,Y -list=SDDS.RUN -pend=30} runList] {
        return -code error "APSSRCheckGapScanConditions(6): problem in reading controllaw pvs."
    }
    set xrun [lindex $runList 0]
    set yrun [lindex $runList 0]
    if {!$xrun && !$yrun} {
        return -code error "APSSRCheckGapScanConditions(7): orbitcorrection is not running."
    } elseif {!$xrun} {
        return -code error "APSSRCheckGapScanConditions(7): x plane orbitcorrection is not running."
    } elseif {!$yrun} {
        return -code error "APSSRCheckGapScanConditions(7): y plane orbitcorrection is not running."
    }
}


proc APSUpdateIDScanSelection {ID} {
    global IDUseFlag  TogetherList
    set sector [scan $ID ID%ld] 
    if ![info exist TogetherList] {
	set TogetherList [join [exec sddsprocess /home/helios/oagData/sr/IDs/dualGapConfig.sdds -pipe=out \
				    -match=col,ScanType=together -nowar | sdds2stream -pipe -col=Sector]]
    }
    if {[lsearch $TogetherList $sector]>0} {
        if [regexp {ds} $ID] {
            set id ID[format %02d $sector]us
        } elseif [regexp {us} $ID] {
            set id ID[format %02d $sector]ds
        }
        set IDUseFlag($id) $IDUseFlag($ID) 
    }
}

#Called by APSSRGapBriefScan and APSSRGapFullScan
proc APSSRCheckGapScanConditions {args} {
    set keepFFrunning 0
    APSStrictParseArguments {keepFFrunning}
    #not sure if it is needed for APSU, skip for now
    return
    #check if offset transfer is running
    
    if [catch {exec cavget -list=SR:CorrectorFFTransferRC.RUN -pend=30} result] {
        return -code error "APSSRCheckGapScanConditions(1 - cavget  -list=SR:CorrectorFFTransferRC.RUN): $result"
    }
    if $result {
        if [catch {exec cavget -list=SR:CorrectorFFTransferRC.DESC -pend=30} description] {
            return -code error "APSSRCheckGapScanConditions(2 - cavget -list=SR:CorrectorFFTransferRC.DESC): $description"
        }
        set desc "transferScalarstoWaveform"
        if [string compare $description $desc]!=0 {
            set result 0
        }
    }
    if !$result {
        return -code error "DP offset transfer is not running, please run it first!"
    }
    
    if [catch {exec cavget -list=S:ID:GapFeedForwardRC.RUN -pend=30} running] {
        return -code error "APSSRCheckGapScanConditions(3 - cavget -list=S:ID:GapFeedForwardRC.RUN ): $description"
    }
    if $keepFFrunning {
        if !$running {
            if [catch {APSMpIDOffsetFeedforward -action start} result ] {
                return -code error "APSSRCheckGapScanConditions(5 - abort ID FF): $result"
            }
        }
    } else {
        if $running {
            if [catch {APSMpIDOffsetFeedforward -action stop} result ] {
                return -code error "APSSRCheckGapScanConditions(5 - abort ID FF): $result"
            }
        }
    }
    
    if [catch {exec cavget -list=S:OrbitControlLaw -list=X,Y -list=SDDS.RUN -pend=30} runList] {
        return -code error "APSSRCheckGapScanConditions(6): problem in reading controllaw pvs."
    }
    set xrun [lindex $runList 0]
    set yrun [lindex $runList 0]
    if {!$xrun && !$yrun} {
        return -code error "APSSRCheckGapScanConditions(7): orbitcorrection is not running."
    } elseif {!$xrun} {
        return -code error "APSSRCheckGapScanConditions(7): x plane orbitcorrection is not running."
    } elseif {!$yrun} {
        return -code error "APSSRCheckGapScanConditions(7): y plane orbitcorrection is not running."
    }
}
