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

#
# $Log: not supported by cvs2svn $
# Revision 1.28  2010/10/01 22:03:14  shang
# added "canted ds" and "canted us" button per Glenn's request.
#
# Revision 1.27  2010/10/01 21:42:13  shang
# now do not open other gaps when move the selected gaps to 20mm.
#
# Revision 1.26  2009/11/09 20:22:24  shang
# removed shang's personal library path.
#
# Revision 1.25  2009/11/09 20:21:43  shang
# made the "Preview" button work and set the ID16us for defaul selection, unselect the ID16ds by default.
#
# Revision 1.24  2008/09/25 16:40:07  shang
# added time interval selection for staggering closing gaps.
#
# Revision 1.23  2008/07/03 14:38:24  shang
# enabled the archive button.
#
# Revision 1.22  2008/06/19 21:20:58  shang
# removed the debugging print statement "puts"
#
# Revision 1.21  2008/06/17 22:03:03  shang
# added "default" "default a", and "default b" buttons to select default IDs, first half defaults, and second half default IDs.
#
# Revision 1.20  2008/03/11 17:08:27  shang
# added option of scan dual gaps together or with one gap fixed or scan independently, tested with Glenn Decker.
#
# Revision 1.19  2007/03/27 18:55:46  shang
# added checking ID device status and disable the check buttons for locked devices.
#
# Revision 1.18  2007/02/07 06:21:37  emery
# Clarified the loading message from procedure Load.
#
# Revision 1.17  2007/02/07 06:16:29  emery
# Changed some printout wording.
# Added missing -loadRate argument in APSSRGapBriefScanLoad
# in procedure Load.
#
# Revision 1.16  2006/12/20 18:22:05  shang
# modified to use updated library procedures.
#
# Revision 1.15  2006/06/13 21:59:14  shang
# added feature of scanning canted-undulator (sector 3, 26, 30) together, creating the monitoring files as needed during scan and fitting only ds data of sector 3, 26, 30 sectors.
#
# Revision 1.14  2006/05/29 15:32:25  emery
# Fixed typo in variable sector1 for dealing with CU.
#
# Revision 1.13  2006/01/26 19:53:56  shang
# removed :Corrected from the offset and rate PV name.
#
# Revision 1.12  2006/01/26 15:32:13  shang
# modified data fit so that it no longer hard-codes the blade type but obtains from
# the data file and added fit tolerance which removes the bad fits.
#
# Revision 1.11  2005/10/03 13:30:05  shang
# fixed a bug in ReviewGapScan that the P2 C and D blades for sector 5 were
# not plotted because of the blads name changes.
#
# Revision 1.10  2005/10/02 17:28:32  shang
# added all and none to the check buttons.
#
# Revision 1.9  2005/10/02 17:21:37  shang
# changed the P2 blades for sector 5 since the blades pv names were changed.
#
# Revision 1.8  2005/08/09 14:57:06  shang
# fixed bugs in data processing, set the tolerance of data fitting to 1.0e-14 which solved the problem of mis-fittng for some curves; added Review Scan Data, Review Fit Data and "Load Offset & Rate" buttons.
#
# Revision 1.7  2005/02/15 01:39:03  shang
# removed the plots after fitting each sector, instead, plots the whole data
# after all data are fitted.
#
# Revision 1.6  2005/02/15 01:14:34  shang
# enabled the plotting after fitting data
#
# Revision 1.5  2005/02/15 01:07:34  shang
# fixed the problem in FitData for adding devices (ID14, ID26, and ID30).
#
# Revision 1.4  2005/02/14 23:59:34  shang
# the IDs will be selected according to their Default value in devices.sdds
#
# Revision 1.3  2005/02/14 23:13:18  shang
# added checking the permission of writing to output directory before doing
# experiment
#
# Revision 1.2  2005/01/31 17:14:50  shang
# added alert dialog box to CallbackProc to remind user the problem with sddsexperiment
# if the output file does not exist.
#
# Revision 1.1  2004/11/04 16:22:47  shang
# first version, tested by Glenn Decker
#
# 

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)]
#set auto_path [linsert $auto_path 0 /home/oxygen/SHANG/oag/apps/src/mplib/sr]
APSDebugPath
APSStandardSetup

set CVSRevisionAuthor "\$Revision: 1.29 $ \$Author: shang $"
APSApplication . -name "SRFitGapScan"  -version $CVSRevisionAuthor \
  -overview "Does undulator gap scans to determine required H1/V1 magnet setpoints for feedforward. Can be used for calculating ID first and second integrals. Orbit correction in both planes must be running externally. The gap scan values are from 20mm to 35mm, as shown in file /home/helios/oagData/sr/gapScans/feedforwardInputFiles/gapValuesBrief.sdds"

set status ""
set archiveDir /home/helios/oagData/sr/gapScans/feedforwardDataFiles
set archiveDataDir /home/helios/oagData/sr/gapScans/data/gapScanBrief
set inputFileDir /home/helios/oagData/sr/gapScans/feedforwardInputFiles
set orbitControlDir /home/helios/oagData/sr/orbitControllaw/lattices
set bpmOffsetRef /home/helios/oagData/SCR/snapshots/SR/SR-BPMOffsetReference.gz

APSScrolledStatus .status -parent .userFrame -width 80 -textVariable status

# make a widget to allow selecting individual IDs and specifying their
# minimum gap

 
proc MakeIDSelectionWidget {widget args} {
    set parent ""
    APSStrictParseArguments {parent}

    APSFrame $widget -parent $parent -label "ID Brief Scan Selection"
    set parent $parent$widget.frame
    global AllIDList IDUseFlag CUSectorList lockedIDList buttonNameList IDList

    if [catch {APSSRGetIDGapBriefInfo} result] {
        return -code error $result
    }
    set lockedIDList [APSSRGetLockedIDDevices]
    set variableList ""
    set commandList ""
    foreach id $AllIDList {
        lappend variableList IDUseFlag($id)
        lappend commandList "APSUpdateIDScanSelection $id"
    }

    set buttonNameList [APSCheckButtonFrame .id -parent $parent -buttonList $AllIDList -variableList $variableList -allNone 1   \
                          -limitPerRow 6 -label "" -commandList $commandList \
                          -contextHelp "check ID gaps to include in the scan. Use /home/helios/oagData/sr/gapScans/feedforwardInputFiles/gapValuesBrief.sdds file for gap scan values, i.e., gap scans from 20mm to 35mm as shown in the file"]
 
    set rows [expr [llength $AllIDList] /6 -1]
    APSFrameGrid .grid -parent $parent.id.frame.row$rows  -yList {y1 y2 y3 y4}
    set w1 $parent.id.frame.row$rows
    APSButton .d -parent $w1.grid.y3 -text "default" \
        -command "APSSRResetDefaultSelection  -selectType default"  -size small 
    APSButton .da -parent $w1.grid.y3 -text "default a" \
        -command "APSSRResetDefaultSelection -selectType defaultA"   -size small 
    APSButton .db -parent $w1.grid.y3 -text "default b" \
        -command "APSSRResetDefaultSelection -selectType defaultB"  -size small 
    APSButton .ds -parent $w1.grid.y4 -text "canted ds" -size small \
	-command "SelectCantedUndulator -type ds"
    APSButton .us -parent $w1.grid.y4 -text "canted us" -size small \
      -command "SelectCantedUndulator -type us"
    
    foreach ID $lockedIDList {
        set index [lsearch $AllIDList $ID]
        if {$index>=0} {
            [lindex $buttonNameList $index] configure -state disabled
        }
    }
}

proc SelectCantedUndulator {args} {
    set type ""
    APSParseArguments {type}
    global IDUseFlag CUSectorList AllIDList
    
    switch $type {
	ds {
	    set type1 us
	}
	us {
	    set type1 ds
	}
    }
    foreach sector $CUSectorList {
	set IDUseFlag(ID[format %02d $sector]$type) 1
	set IDUseFlag(ID[format %02d $sector]$type1) 0
    }
    foreach id $AllIDList {
	set sector [scan $id ID%ld]
	if [lsearch -exact $CUSectorList $sector]<0 {
	    set IDUseFlag($id) 0
	}
    }
}
proc CheckIDStatus {args} {
    global buttonNameList AllIDList IDUseFlag lockedIDList
    set lockedIDList [APSSRGetLockedIDDevices]
    
    if [llength $lockedIDList] {
        foreach ID $AllIDList {
            if [lsearch $lockedIDList $ID]>=0 {
                set IDUseFlag($ID) 0
            }
        }
    }
    APSSRDisableLockedIDDeviceSelect -buttonNameList $buttonNameList \
        -IDList $AllIDList -enable 1 -lockedIDList $lockedIDList
}

# global values for entry boxes
set end 35
set points 30
set numToAve 2
set readingType msAve
set pause 10
set minStep 0.25
set maxStep 30.0
set stepInterval 7
set factor 2
set stepCounter 0
set rootname IDscan
set outputDir .
set outputFile ""
set transferOrbit 0
set gainSetpointFF ""
set DeltaToMinimumGap 0.0

APSAddToTmpFileList -ID FF -fileList $gainSetpointFF
# make widgets for entering common values for experiments

proc MakeEntryWidgets {widget args} {
    set parent ""
    APSStrictParseArguments {parent}
    APSFrame .option -parent $parent
    set parent $parent.option.frame
    #APSFrameGrid .grid -parent $parent -xList {x1 x2}
    APSLabeledEntry .rootname -parent $parent -width 55 \
      -label "Output rootname: " -textVariable rootname \
      -contextHelp \
      "Enter a rootname for generation of the output filenames."
    APSLabeledEntry .dir -parent $parent -width 55 \
      -label "Output dir: " -textVariable outputDir \
      -contextHelp \
      "Enter a directory for saving the output files to."
    APSButton .dialy -parent $parent.dir -text "daily" -size small \
      -command {set outputDir [APSGoToDailyDirectory  -subdirectory gapScanBrief]} \
      -packOption "-side right" \
      -contextHelp "set the output dir to APS daily directory"
    APSButton .archive -parent $parent.dir -text "archive" -size small \
      -command {set outputDir $archiveDataDir} \
      -packOption "-side right" \
      -contextHelp "set the output directory to ASP archive dir"
   # APSDisableButton  $parent.dir.archive.button
    APSLabeledOutput .output -parent $parent -width 55 \
      -label "Output file: " -textVariable outputFile 
    APSLabeledEntry .pause -parent $parent -width 10 \
      -label "Pause for orbit correction after gap change (s): " \
      -textVariable pause -contextHelp \
      "Length of pause after gap change is completed to allow orbit correction to converge."
    
    global fitPar
    set fitPar(startValue) 0.1
    set fitPar(factor) 340
    set fitPar(rate) -0.2
    APSLabeledEntry .ptToAve -parent $parent -width 10 \
      -label "Readings to average: " -textVariable numToAve \
      -contextHelp "Number of readings to average at each gap value."
    APSLabeledEntryFrame .fit -parent $parent -width 10 \
      -label "Fitting parameters(start value, factor and rate):" -orientation horizontal \
      -variableList "fitPar(startValue) fitPar(factor) fitPar(rate)" \
      -contextHelp "Enter the fit parameters in start value, factor and rate order for sddsexpfit to process the data."
    global sameScale fitTolerance closeInterval
    set sameScale 0
    set fitTolerance 1.0
    set closeInterval 0.1
    APSLabeledEntry .fittolerance -parent $parent -width 32 \
	-label "Tolerance of fit residual:" -textVariable fitTolerance \
	-contextHelp "If the residual of fitting is greater than this tolerance, it will be considered as bad fit"
    APSLabeledEntry .interval -parent $parent -width 32 \
      -label "Interval between closing two gaps (seconds): " -textVariable closeInterval \
      -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."

    APSRadioButtonFrame .scale -parent $parent -label "Same Scale:" -buttonList {yes no} \
      -variable sameScale -valueList {1 0} -orientation horizontal \
      -contextHelp "choose yes to plot all plots with same scale."
   #  APSRadioButtonFrame .rbf -parent $parent -label "Reading type: " \
   #   -orientation horizontal -variable 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 $parent \
   #   -label "Transfer orbit at each iteration:" \
    #  -variable transferOrbit -buttonList {Yes No} -valueList {1 0} \
     # -packOption "-side left" -orientation horizontal -contextHelp \
      #"Transfer orbit at each iteration to remove systematic dispersion orbit."
    
}

# make widgets for buttons to initiate actions

proc MakeActionButtons {widget args} {
    set parent ""
    APSStrictParseArguments {parent}

    global gainSetpointFF
    APSFrame .br1 -parent $parent -label "" -relief flat
    set w $parent.br1.frame
   # APSButton .default -parent $w -text "Default selection" \
    #  -command APSSRResetDefaultSelection
    
    APSButton .preview -parent $w -text "Preview" \
      -command {RunScan -preview 1}
    APSButton .run -parent $w -text "Run" \
      -command {RunScan -preview 0}
    APSButton .review -parent $w -text "Review scan data..." \
      -command ReviewGapScanData -contextHelp "Review results of gap scan experiment."
    APSButton .review1 -parent $w -text "Review fit data..." \
      -command ReviewFitData -contextHelp "Review fitting results of gap scan experiment."
    APSButton .minimum -parent $w -text "Goto 20mm Gap" \
      -command Goto20mmGap -contextHelp "Move the gaps to the value of minimum gap + the delta to the minimum gap."
    
    APSFrame .br2 -parent $parent -label "" -relief flat
    set w $parent.br2.frame

    APSButton .prepFF -parent $w -text "Fit Scan Data..." \
      -command FitScanData \
      -contextHelp "Process gap scan experiment to create and view data for running feedforward."
    APSButton .load1 -parent $w -text "Load Offsets..." \
	-command Load -contextHelp "Download the fitted offsets to process variables, fit data has to be done before loading offsets."
    APSButton .load2 -parent $w -text "Load Offsets & Rates..." \
	-command "Load -loadRate 1" \
	-contextHelp "Download the fitted offsets and rates to process variables, fit data has to be done before loading offsets."
  #  APSButton .prepFFgainsetpoint -parent $w -text "Gen. Gain-Setpoint FF Data" \
  #    -command "GenerateGainAndSetpointActuatorFile -filename $gainSetpointFF" \
   #   -contextHelp "generate the FF file of ID gain and setpoint pvs, required whenever the UBOP file changes"
  #  APSButton .runFF -parent $w -text "Run FF..." \
  #    -command RunFeedForward \
  #    -contextHelp "Run gap feedforward from local or archived data."
  #  APSButton .install -parent $w -text "Install ..." \
  #    -command InstallFeedForward \
  #    -contextHelp "install the chosed FF file to /home/helios/oagData/feedForwardFiles/IDgap/FF"
    APSButton .info -parent $w -text "IDFeedforwardStatus" \
      -contextHelp "display gap feedforward run control information." \
      -command "exec medm -x -attach -macro RCPV=S:ID:GapFeedForwardRC \
      ./sr/psApp/APSRunControlSingle.adl &"
    APSButton .check -parent $w -text "Check ID Status" -command "CheckIDStatus" \
	-contextHelp "check the ID device status to see if the IDs are ready to move, if not, disable the corresponding check button."
}

proc ReviewGapScanData {} {
    global rootname outputDir sameScale
    
    set pattern ${rootname}*.sdds
    set input [APSFileSelectDialog [APSUniqueName .] -width 70 -reverseSort 1\
                   -path $outputDir -pattern $pattern ]
    if [catch {APSSRReviewBriefScanData -scanData $input -sameScale $sameScale} result] {
        APSSetVarAndUpdate status "$result"
    }
}

proc ReviewFitData {} {
    global rootname outputDir sameScale CUSectorList

    set pattern ${rootname}*.sdds.fit
    set input [APSFileSelectDialog [APSUniqueName .] -width 70 -reverseSort 1 \
                   -path $outputDir -pattern $pattern ]
    if ![string length $input] {
        return
    }
    if [catch {APSSRReviewBriefScanFitData -fitData $input \
                    -sameScale $sameScale -CUSectorList $CUSectorList} result] {
        APSSetVarAndUpdate status "$result"
        return
    }
}

proc GetSelectedIDList {args} {
    global IDUseFlag
    set IDList ""
    foreach ID [lsort [array names IDUseFlag]] {
        if $IDUseFlag($ID) {
            lappend IDList $ID
        }
    }
    return $IDList
}

set fixedIDList ""
set fixedGapList ""
set togetherList ""
proc RunScan {args} {
    set preview 0
    APSStrictParseArguments {preview}

    global outputDir rootname pause numToAve outputFile IDUseFlag FixedIDList FixedGapList TogetherList
  
    set IDList [GetSelectedIDList]
    if [catch {APSSRCheckUndulatorScanType -IDList $IDList} result] {
            APSSetVarAndUpdate status "$result."
        return
    }
   
    if !$preview {
        if [llength $FixedIDList] {
            foreach ID $FixedIDList {
                set index [lsearch $IDList $ID]
                if $index>=0 {
                    set IDList [lreplace $IDList $index $index]
                }
            }
            foreach ID $FixedIDList val $FixedGapList {
                lappend putcom ${ID}.GapSet=$val
                lappend putcom1 ${ID}.Start=1
                append pos "$ID = $val\n"
            }
            
            set answer [APSMultipleChoice [APSUniqueName .] \
                          -question "Please check if following gaps are in their desired positions\n\n$pos\nClick \"Continue\" if the above gaps are in the desired position;\n  \"MoveGaps\" to move the above gaps to the desired position;\n \"Abort\" to abort experiment.\n" \
                          -returnList {Continue Move Abort} \
                          -labelList {Continue MoveGap Abort}]
            switch $answer {
                Abort {
                    APSSetVarAndUpdate status "SRFitGapScan was aborted."
                    return
                }
                Move {
                    APSSetVarAndUpdate status "Move the fixed gaps to their desired values..."
                    if [catch {exec cavput -list=[join $putcom ,] } result] {
                        return -code eorr "Unable to set the gap setpoints of fixed gaps: $result"
                    }
                    if [catch {exec cavput -list=[join $putcom1 ,] } result] {
                        return -code error "Unable to mvoe the fixed gaps: $result"
                    } 
                    if [catch {WaitForTaskToFinish \
                                   -cavget "APScavget -list=[join $FixedIDList ,] -list=:Busy -pend=30 -label" \
                                   -waitLimit 500 \
                                   -updateInterval 1 \
                                   -checkExpression {($value)=="Moving"}} results] {
                        APSSetVarAndUpdate status  "Problem occurred while waiting for setting $FixedIDList gaps: $results" 
                        return
                    }
                }   
            }
        }
    }
    set ID01OffsetList ""
    if [lsearch -exact $IDList ID01ds]>=0 {
        APSSetVarAndUpdate status "Opening ID01 ds gap to 180mm  and wait..."
        update
        if [catch {exec cavput -list=ID01ds:GapSet=180 -pend=20
            exec cawait -waitFor=ID01ds:Gap,lowerLimit=175,upperLimit=185 -timeLimit=600 } result] {
            return -code error "Error opening ID01ds gap: $result"
        }
        APSSetVarAndUpdate status "Reading ID01 blades voltage as ID01ds offset ..."
        update
        if [catch {exec cavget -list=S1ID: -list=P1,P2 -list=: -list=A,B,C,D -list=:CorrectedM -pend=30} offsetList] {
            return -code error "Error opening ID01ds blades offset: $offsetList"
        }
        set ID01OffsetList [join $offsetList]
        APSSetVarAndUpdate status "moving ID01ds to 14 mm before scanning ..."
        if [catch {exec cavput -list=ID01ds:GapSet=14  -pend=20
            exec cawait -waitFor=ID01ds:Gap,lowerLimit=13.8.8,upperLimit=14.2 -timeLimit=600 } result] {
            return -code error "Error opening ID01ds gap: $result"
        }
    }
    #puts $ID01OffsetList
    if [catch {APSSRGapBriefScan -outputDir $outputDir -rootname $rootname -pause $pause   \
                 -ID01OffsetList $ID01OffsetList \
                 -fixedIDList $FixedIDList -fixedGapList $FixedGapList -togetherList $TogetherList \
                 -numToAve $numToAve -IDList $IDList -preview $preview} result] {
        APSSetVarAndUpdate status "RunScan failed: $result"
        return
    }
    set outputFile $result
}

proc Goto20mmGap {args} {
    global inputFileDir IDUseFlag status FixedIDList FixedGapList TogetherList closeInterval
    set IDList [GetSelectedIDList]
    if [catch {APSSRCheckUndulatorScanType -IDList $IDList} result] {
        APSSetVarAndUpdate status "$result."
        return
    }
  
    if [catch {APSSRMoveIDGaps -pem 0 \
                 -moveTo 20 -IDList $IDList -open 0 -closeInterval $closeInterval \
                 -fixedIDList $FixedIDList -fixedGapList $FixedGapList } result] {
        set status "MoveIDGaps failed: $result"
        return
    }
    set status "Gaps are moving, please wait until all gaps stop moving."
}

proc FitScanData {args} {
    global outputDir status  fitPar rootname CUSectorList
    
    set pattern ${rootname}*.sdds
    set input [APSFileSelectDialog [APSUniqueName .] -width 70 -reverseSort 1 \
                   -path $outputDir -pattern $pattern ]
    if [catch {APSSRGapBriefScanDataFit -inputFile $input \
                   -fitStart $fitPar(startValue)  \
                   -fitFactor $fitPar(factor) \
                   -fitRate $fitPar(rate) \
                   -CUSectorList $CUSectorList } result] {
        set status "Fit data failed: $result"
        return
    }
    set status "Data fit done."
}

proc Load {args} {
    set loadRate 0
    APSStrictParseArguments {loadRate}
    global outputDir rootname fitTolerance CUSectorList status

    if !$loadRate {
        set status "Loading offsets only..."
    } else {
        set status "Loading offsets and rates..."
    }
    if [catch {APSSRGapBriefScanLoad -dataDir $outputDir -rootname $rootname -fitTolerance $fitTolerance \
                 -loadRate $loadRate \
                 -CUSectorList $CUSectorList -statusCallback "APSSetVarAndUpdate status" } result] {
        set status "Load offset/rate failed: $result"
        return
    }
    set status "Loading done."
}

if [catch {exec sddsprocess /home/helios/oagData/feedForwardFiles/IDgap/cantingLogic.sdds \
             -filter=col,TreatedAsCU,1,1 -pipe=out \
             | sdds2stream -pipe -col=Sector} CUSectorList] {
    puts stderr "$CUSectorList"
    exit
}
set status "Working..."
update
MakeIDSelectionWidget .idsel -parent .userFrame
MakeEntryWidgets .entries -parent .userFrame
MakeActionButtons .action -parent .userFrame
set status "Ready."
