#!/bin/sh
# \
  exec oagwish "$0" "$@"
set auto_path [linsert $auto_path 0 /usr/local/oag/apps/lib/$env(HOST_ARCH)]
set auto_path [linsert $auto_path 0 /usr/local/oag/lib_patch/$env(HOST_ARCH)]
APSDebugPath

set CVSRevisionAuthor "\$Revision: 1.25 $ \$Author: emery $"

APSApplication . -name "SRorbitResponse" -version $CVSRevisionAuthor \
  -overview "SRorbitResponse measures the orbit repsonse to a corrector."

set SROrbitStatus "Initializing ..."
APSScrolledStatus .status -parent .userFrame -width 96 \
  -textVariable SROrbitStatus 

proc SetStatus {text} {
    global SROrbitStatus
    set SROrbitStatus "[clock format [clock seconds] -format %T] $text"
    update
#    bell
}

#-------------------------------------------------------------------------------------------

proc DisableWidgets {args} {
    global sensitiveWidgets
    APSParseArguments {option}
    if {-1 < [lsearch [array names sensitiveWidgets] $option]} {
        foreach widget $sensitiveWidgets($option) {
            $widget configure -state disabled
        }
    }
    return
}
proc EnableWidgets {args} {
    global sensitiveWidgets
    APSParseArguments {option}
    if {-1 < [lsearch [array names sensitiveWidgets] $option]} {
        foreach widget $sensitiveWidgets($option) {
            $widget configure -state normal
        }
    }
    return
}
#
#-------------------------------------------------------------------------------------------

proc MakeInputFrame {widget args} {

    global sensitiveWidgets xyBothPlanes swapout launchLOCO OAGGlobal makeSCRSave bpmScaleFactor
    APSStrictParseArguments {parent}
    set w $parent$widget.frame
    APSFrame $widget -parent $parent -label "Input Parameters"

    APSRadioButtonFrame .bpmUnits -parent $w -label "Units used by BPM PVs" \
        -variable bpmScaleFactor -valueList "1 1e-3" -buttonList "um nm" \
        -orientation horizontal \
        -contextHelp "Specify the assumed units for the BPM data."
    
    APSRadioButtonFrame .makeSCRSave -parent $w -label "Make SCR Save?" \
        -variable makeSCRSave -valueList "0 1" -buttonList "No Yes" \
        -orientation horizontal \
        -contextHelp "Specify whether to make an SCR save before starting measurements."
    
    APSRadioButtonFrame .swapout -parent $w -label "Swap-out integration" \
      -variable swapout -valueList {0 1} \
      -buttonList {"Ignore" "Synchronize"} \
      -orientation horizontal \
      -contextHelp "Allows for measurements during swap-out. Program will wait for swapout shots when necessary."


    APSRadioButtonFrame .noiseMeasurement -parent $w -label "Type of measurement" \
      -variable noiseMeasurement -valueList {0 1} \
      -buttonList {"Response" "Noise"} \
      -orientation horizontal \
      -commandList {
          {set configuration h.default
              set amplitude 0.5
	      set xyBothPlanes 2
	      set doDisp 1
	      EnableWidgets -option dispersion
	      EnableWidgets -option yOnly
	      EnableWidgets -option xOnly
	      set configurationX h.LOCO.20
              set root Corrector-} 
          {set configuration h.noise
              set amplitude 0.5
	      set xyBothPlanes 2
	      set doDisp 0
	      DisableWidgets -option dispersion
	      EnableWidgets -option yOnly
	      EnableWidgets -option xOnly
	      set configurationX h.LOCO.Noise
	      set configurationY v.LOCO.Noise
              set root Noise}} \
	-contextHelp "Selects response or noise measurements."
    
    APSLabeledEntry .extraMon -parent $w -label "Extra monitor file: " -textVariable extraMonFile \
      -width 72 \
      -contextHelp "File with additional variables. It will be appended to default monitor file. It must have columns s and Rootname (should look similar to /home/helios/oagData/sr/sddsmonitorFiles/SROrbit-Rootnames.vmon). "
	 
    global lattice LatticeDir
    APSLabeledEntry .lattice -parent $w -textVariable lattice -label "Configuration lattice: " \
      -contextHelp "Enter the lattice for the orbit correction configuration for processing orbit" \
      -width 72
    APSButton .pick -parent $w.lattice -text P -size small \
      -command "set lattice \[APSPickFileFromDir [APSUniqueName .] -pathVariableList LatticeDir -type directory -default $lattice\]" \
      -contextHelp "Press to choose the lattice."

    APSFrame .response -parent $w -label "Orbit Response Measurement" -packOption "-fill x"
    set w1 $w.response.frame
    APSRadioButtonFrame .xyBoth -parent $w1 -label "" \
      -variable xyBothPlanes -valueList {0 1 2} \
      -buttonList {"X only" "Y only" "Both planes"} \
      -orientation horizontal \
      -commandList { { DisableWidgets -option yOnly; EnableWidgets -option xOnly}
          { DisableWidgets -option xOnly; EnableWidgets -option yOnly }
          { EnableWidgets -option xOnly; EnableWidgets -option yOnly } } \
      -contextHelp "Selects what planes to run."
    
    APSLabeledEntry .configX -parent $w1 \
	-label "Corrector configuration X:" -textVariable configurationX \
	-contextHelp "Enter the orbit correction configuration for the corrector list. For noise measurement, alternate between 2 correctors because when repeating one corrector many times, it goes bad." -width 30
    lappend sensitiveWidgets(xOnly) $w1.configX.label $w1.configX.entry
    APSLabeledEntry .configY -parent $w1 \
	-label "Corrector configuration Y:" -textVariable configurationY \
	-contextHelp "Enter the orbit correction configuration for the corrector list. For noise measurement, alternate between 2 correctors because when repeating one corrector many times, it goes bad." -width 30
    lappend sensitiveWidgets(yOnly) $w1.configY.label $w1.configY.entry

    APSLabeledEntry .deltaH -parent $w1 -label "Amplitude in H plane (A): " \
      -textVariable amplitudeH -width 6 \
      -contextHelp "Enter the desired value for corrector amplitude."
    APSLabeledEntry .deltaV -parent $w1 -label "Amplitude in V plane (A): " \
      -textVariable amplitudeV -width 6 \
      -contextHelp "Enter the desired value for corrector amplitude."
    APSLabeledEntry .bpmPause -parent $w1 -label "BPM average time (s): " \
      -textVariable bpmPause -width 6 \
      -contextHelp "Enter the time for the averaging of bpms."
    APSLabeledEntry .correctorPause -parent $w1 -label "Corrector pause time (s): " \
      -textVariable correctorPause -width 6 \
      -contextHelp "Enter the time for the correctors to settle."
    APSLabeledEntry .dataAcquisitionTime -parent $w1 -label "Time allowed for measurement (s): " \
      -textVariable dataAcquisitionTime -width 6 \
      -contextHelp "Enter the time for the data acquisition to take place."

    APSFrame .dispFrame -parent $w -label "Dispersion Measurement" -packOption "-fill x"
    set w1 $w.dispFrame.frame
    APSRadioButtonFrame .disp -parent $w1 -label "Include dispersion: " \
      -variable doDisp -valueList {0 1} \
      -buttonList {"No" "Yes"} \
      -orientation horizontal \
      -commandList {{DisableWidgets -option dispersion} {EnableWidgets -option dispersion}} \
      -contextHelp "Dispersion will be measured at the end of the RM measurement."
    APSLabeledEntry .rfFreq -parent $w1 \
	-label "RF frequency change: " -textVariable rfFreqChange \
	-contextHelp "RF freqeuncy will be changed from minus this number to plus this number." -width 10
    lappend sensitiveWidgets(dispersion) $w1.rfFreq.label $w1.rfFreq.entry
    APSLabeledEntry .rfSteps -parent $w1 \
	-label "RF frequency steps: " -textVariable rfFreqSteps \
	-contextHelp "Number of steps in RF frequency scan." -width 10
    lappend sensitiveWidgets(dispersion) $w1.rfSteps.label $w1.rfSteps.entry

    APSFrame .output -parent $w -label "Output File" -packOption "-fill x"
    set w1 $w.output.frame
    APSLabeledEntry .outputDir -parent $w1 \
      -label "Output directory: " -textVariable outputDir \
      -contextHelp "Enter a name for the output file directory." -width 72
    APSButton .daily -parent $w1.outputDir -packOption "-side top" \
      -text "daily" -size small \
      -command {set outputDir [APSGoToDailyDirectory -subdirectory response]}
    #APSLabeledEntry .root -parent $w1 -label "Root name of file: " \
    #  -textVariable root \
    #  -contextHelp "Enter the root of the raw data files and processed output <root><index>."
    APSLabeledEntry .index -parent $w1 -label "Index: " -textVariable index \
      -contextHelp "Enter the index to be part of the output file name. The index is incremented by 1 before execution."
    APSLabeledEntry .comment -parent $w1 -label "Comment: " -textVariable comment \
      -width 55 \
      -contextHelp "Enter a comment to be written as a parameter to the output file."

     APSRadioButtonFrame .launch -parent $w1 -label "Launch SRLOCOFitting: " \
      -variable launchLOCO -valueList {0 1} \
      -buttonList {"No" "Yes"} \
      -orientation horizontal \
      -commandList {{} {}} \
      -contextHelp "If yes, \"Take Data\" button will also launch SRLOCOFitting with proper inputs."

    APSFrameGrid .bgrid -parent $parent -packOption "-side left" -yList {y1 y2 y3} -relief flat

    # collect orbit before and after
    APSButton .collect -parent $parent.bgrid.y1 \
      -text "Take Data"  \
      -packOption "-side left" \
      -command {
	  #--- If you chose "ignore swapout" but swapout is running, warn user about it:
          set swapoutRunning 0
	  if {$swapout == 0 && $swapoutRunning == 1} {
	      set message "Error: you are going to run measurement with swapout running! Aborting measurement..."
	      APSInfoWindow .info -name "Swapout running" -infoMessage $message -modal 0
	      SetStatus $message
	      return
	  }

          if {$noiseMeasurement == 0} {
	      #--- Not noise measurement:
	      set rfAccess [exec cavget -num -list=SRF:accessControl]
	      if {$rfAccess == 0} {return -code error "Don't have RF access."}
              if $makeSCRSave {
                  #--- Save SR conditions:
                  if [catch {APSSaveMachine -machine SR -description "Automated save before RM measurement" -routine 0 \
                                 -pendIOTime 80 -pingTimeOut 80 -statusCallback SetStatus} result] {
                      SetStatus "Warning: Error saving SR: $result"
                  }
              }
          }
          
	  incr index; 
	  set indexf [format %02ld $index]
	  set measRoot $root${indexf}
	  set fullLatticeName $OAGGlobal(SRLatticesDirectory)/$lattice
	  if {[string compare link [file type $fullLatticeName]] == 0} {
	      set latticeSource [file tail [file link $fullLatticeName]]
          } else {
              set latticeSource $lattice
          }

          if {$noiseMeasurement == 0} {
	      #--- Not noise measurement:
	      SetStatus "You can start the following command on your computing workstation:"
	      SetStatus "\$LOCO_BINDIR/SRLOCOFitting -prepare4processing 1 -hrmConfig $configurationX -vrmConfig $configurationY -measRoot $measRoot -measDir $outputDir -latticeName $latticeSource -workDir /home/helios/SR/LOCO/workdir -removeRFQuads 1 &"
	      if $launchLOCO {
		  exec SRLOCOFitting -prepare4processing 1 -hrmConfig $configurationX \
		      -vrmConfig $configurationY -measRoot $measRoot -measDir $outputDir -latticeName $latticeSource \
		      -workDir /home/helios/SR/LOCO/workdir -removeRFQuads 1 &
                  puts "SRLOCOFitting -prepare4processing 1 -hrmConfig $configurationX \
		      -vrmConfig $configurationY -measRoot $measRoot -measDir $outputDir -latticeName $latticeSource \
		      -workDir /home/helios/SR/LOCO/workdir -removeRFQuads 1 &"
	      }
	  }
	  if [catch {CollectData -configurationX $configurationX -configurationY $configurationY \
			 -outputDir $outputDir \
			 -index $index \
			 -bpmPause $bpmPause \
			 -dataAcquisitionTime $dataAcquisitionTime \
			 -correctorPause $correctorPause \
			 -amplitudeH $amplitudeH \
			 -amplitudeV $amplitudeV \
			 -xyBothPlanes $xyBothPlanes \
			 -doDisp $doDisp \
			 -rfFreqChange $rfFreqChange \
			 -rfFreqSteps $rfFreqSteps \
			 -extraMonFile $extraMonFile \
			 -noiseMeasurement $noiseMeasurement \
			 -abortVariable abortRun} result] {
	      SetStatus "CollectData: $result"
	  }
      } \
      -contextHelp "Collects orbit data for two values of the corrector"

    APSButton .abort -parent $parent.bgrid.y1 \
      -text "Abort"  \
      -packOption "-side left" \
      -command {set abortRun 1; \
                  SetStatus "Abort pending..." } \
      -contextHelp "Aborts scan at next appropriate moment."

    APSButton .plot1 -parent $parent.bgrid.y2 \
      -text "Plot Meas. Response"  \
      -packOption "-side left" \
      -command {PlotResponseOrNoise -noiseMeasurement 0 -outputDir $outputDir -index $index } \
      -contextHelp "Plots response data."
    APSButton .plot2 -parent $parent.bgrid.y2 \
      -text "Compare Response w/Model"  \
      -packOption "-side left" \
      -command {PlotResponseOrNoise -noiseMeasurement 0 -outputDir $outputDir -index $index -includeModel 1 } \
      -contextHelp "Plots response data."
    APSButton .plot3 -parent $parent.bgrid.y2 \
      -text "Plot Meas. Dispersion"  \
      -packOption "-side left" \
      -command {PlotDispersionData -outputDir $outputDir -index $index } \
      -contextHelp "Plots response data."
    APSButton .plot4 -parent $parent.bgrid.y2 \
      -text "Compare Dispersion w/Model"  \
      -packOption "-side left" \
      -command {PlotDispersionData -outputDir $outputDir -index $index -includeModel 1 } \
      -contextHelp "Plots response data."
    APSButton .plot1 -parent $parent.bgrid.y3 \
      -text "Plot Noise Data"  \
      -packOption "-side left" \
      -command {PlotResponseOrNoise -noiseMeasurement 1 -outputDir $outputDir -index $index } \
      -contextHelp "Plots noise data."
    APSButton .process -parent $parent.bgrid.y3 \
      -text "Process Noise Data"  \
      -packOption "-side left" \
      -command {
          if {$noiseMeasurement == 0 } {
	      SetStatus "Processing only works for Noise measurement."
          } else {
              ProcessNoise -outputDir $outputDir -index $index \
                -configurationX $configurationX -configurationY $configurationY
          }
      } -contextHelp "Processes orbit data for noise measurement."
    return
}

proc PlotResponseOrNoise {args} {
    set outputDir ""
    set index 0
    set noiseMeasurement 0
    set includeModel 0
    if {[APSStrictParseArguments {outputDir index noiseMeasurement includeModel}] || ![string length $outputDir]} {
        return -code error "PlotResponse: bad arguments"
    }

    if $noiseMeasurement {
        set root Noise-
    } else {
        set root Corrector-
    }
    set indexf [format %02d $index]
    foreach corPlane {h v} CorPlane {H V} {
        foreach bpmPlane {h v} {
            if $noiseMeasurement {
                set title "Noise in $bpmPlane orbit from $corPlane correctors"
            } else {
                set title "Response of $bpmPlane orbit to $corPlane correctors"
            }
            if [file exists $outputDir/${corPlane}$root${bpmPlane}BPM-$indexf.rm] {
                if !$includeModel {
                    exec sddsplot -separate -same "-title=$title" -column=s,*${CorPlane}* $outputDir/${corPlane}$root${bpmPlane}BPM-$indexf.rm &
                } elseif [string compare $corPlane $bpmPlane]==0 {
                    set modelDir [exec sdds2stream -parameter=Configuration $outputDir/${corPlane}$root${bpmPlane}BPM-$indexf.rm]
                    if ![file exists $modelDir/rm] {
                        return -code error "not found: $modelDir/rm"
                    }
                    set tmpRoot /tmp/[APSTmpString]
                    APSAddToTempFileList $tmpRoot.rm
                    exec sddsxref $modelDir/rm $outputDir/${corPlane}$root${bpmPlane}BPM-$indexf.rm $tmpRoot.rm \
                        -match=BPMName -take=s
                    exec sddsplot -separate -same "-title=$title" -groupby=names -separate=names -graph=line,vary \
                        -column=s,*${CorPlane}* $outputDir/${corPlane}$root${bpmPlane}BPM-$indexf.rm -legend=spec=Meas. \
                        -column=s,*${CorPlane}* $tmpRoot.rm -legend=spec=Model &
                }
            } else {
                SetStatus "no match: $outputDir/${corPlane}$root${bpmPlane}BPM-$indexf.rm"
            }
        }
    }
}

proc ProcessNoise {args} {
    set outputDir ""
    set index 0
    set configurationX ""
    set configurationY ""
    APSParseArguments {outputDir index configurationX configurationY}

    set root Noise-
    set indexf [format %02ld $index]
    set outputRoot [file join $outputDir $root]${indexf}
    set bpmStatusFile /home/helios/oagData/sr/BPMStatus/config.sdds 
    foreach plane [list h v] {
	set filename $outputDir/$plane$root${plane}BPM-$indexf.rm
        if ![file exists $filename] {
            return -code error "not found: $filename"
        }
        set columnList [exec sddsquery -column $filename]
        set correctorCollectionList [list ]
        foreach column $columnList  {
            if [string match *:* $column] {
                lappend correctorCollectionList -collect=prefix=$column
            }
        }
        eval exec sddsprocess $filename -pipe=out \
             -process=*:*,rms,%sRms-P0,match=BPMName,value=*:P0 \
             -process=*:*,rms,%sRms-P1,match=BPMName,value=*:P1 \
             -process=*:*,rms,%sRms-P2,match=BPMName,value=*:P2 \
             -process=*:*,rms,%sRms-P3,match=BPMName,value=*:P3 \
             -process=*:*,rms,%sRms-P4,match=BPMName,value=*:P4 \
             -process=*:*,rms,%sRms-P5,match=BPMName,value=*:P5 \
             -process=*:*,rms,%sRms-P6,match=BPMName,value=*:P6 \
            | sddscollapse -pipe \
            | sddsconvert -pipe -retain=col,*Rms-P? \
            | sddscollect -pipe=in $filename.${plane}proc $correctorCollectionList
	exec sddsplot -graph=sym,vary,sub=type -legend -col=Rootname,*:* "-ylabel=Rms difference (nm)" $filename.${plane}proc \
            -enumerate=edit=Z- &
    }
}

proc getDirection {directory} {
    set direction ""
    if [regexp {h\.} $directory] {
	set direction h
    } elseif [regexp {v\.} $directory] {
	set direction v
    } else {
	return -code error "getDirection: Warning. Can't determine direction from directory file name."
    }
    return $direction
}

proc CollectData {args} {
    global simulateRun swapout BPMSuffix bpmScaleFactor
    set configurationX ""
    set configurationY ""
    set amplitudeH 1
    set amplitudeV 1
    set bpmPause 10
    set correctorPause 3
    set outputDir ""
    set xyBothPlanes ""
    set extraMonFile ""
    set abortVariable ""
    set index 0
    set noiseMeasurement 0
    set resetAveraging 0
    #SetStatus "CollectData: $args"
    APSStrictParseArguments {configurationX configurationY outputDir index \
				 bpmPause correctorPause amplitudeH amplitudeV xyBothPlanes abortVariable extraMonFile \
				 doDisp rfFreqChange rfFreqSteps dataAcquisitionTime noiseMeasurement}
    if {![string length $outputDir] } {
        return -code error "SRorbitResponse: Invalid parameters"
    }
    if {![string length $configurationX] && ![string length $configurationY]} {
        return -code error "SRorbitResponse: Both corrector configutations cannot be empty."
    }
    if ![file exists $outputDir] {
        exec mkdir -p $outputDir
    }
    if ![file isdir $outputDir] {
        return -code error "not a directory: $outputDir"
    }

    if {$noiseMeasurement == 0} {
        #--- Real measurement
        set root Corrector-
    } else {
        #--- Noise measurement
        set root Noise-
    }
    set indexf [format %02d $index]
    if {$xyBothPlanes==0  || $xyBothPlanes==2} {
        if [llength [glob -nocomplain $outputDir/h${root}?BPM-${indexf}*]] {
            return -code error "files present: $outputDir/h${root}?Response${indexf}*"
        }
    }
    if {$xyBothPlanes==1  || $xyBothPlanes==2} {
        if [llength [glob -nocomplain $outputDir/v${root}?BPM-${indexf}*]] {
            return -code error "files present: $outputDir/v${root}?Response${indexf}*"
        }
    }
    
    if [string length $abortVariable] {
        global $abortVariable
        set $abortVariable 0
    }

    set beamCurrentPV S-DCCT:CurrentM
    set beamCurrent [exec cavget -list=$beamCurrentPV -pend=5]
    if {$beamCurrent > 11} {
	set choice [APSMultipleChoice .bplds -question \
			"Beam current is above 11 mA. Measurement may cause beam dump. Do you want to continue?" \
			-labelList [list "No, abort (recommended)." "Yes, continue. I know what I am doing."] \
			-returnList [list 1 0]]
	if $choice {
	    return -code error "Measurement aborted due to armed BPLDs."
	}
    }
       
    if 0 {
        #------ Checking BPLDs:
        set bpldList [list 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 26 30 31 32 33 34 35]
        if [catch {exec cavget -numerical -list=S -list=[join $bpldList ,] -list=BPLD:CH1,BPLD:CH2 -list=:IDgapBI.VAL} bplds] {
            return -code error "Error reading BPLDs: $bplds"
        }
        
        set bpldAlert 0
        foreach bpld $bplds { 
            if $bpld {set bpldAlert 1} 
        }
        if $bpldAlert {
            set choice [APSMultipleChoice .bplds -question \
                            "Some BPLDs are armed. Measurement may cause beam dump. Do you want to continue?" \
                            -labelList [list "No, abort (recommended)." "Yes, continue. I know what I am doing."] \
                            -returnList [list 1 0]]
            if $choice {
                return -code error "Measurement aborted due to armed BPLDs."
            }
        }
    }
    #SetStatus "CollectData: 1"

    set bpmList [list ]
    for {set sector 1} {$sector<40} {incr sector} {
        foreach side {A B} {
            for {set p 0} {$p<7} {incr p} {
                set name [format S%02d%s:P%d $sector $side $p]
                if [string compare $name S39B:P1]==0 continue
                lappend bpmList $name
            }
        }
    }
        
    set indexf [format %02ld $index]
    set output [file join $outputDir $root]${indexf}
    set monitorDir /home/helios/oagData/sr/sddsmonitorFiles
    set dataDir /home/helios/oagData/sr/orbitControllaw/lattices/default
    set mainDir /home/helios/oagData/sr/orbitControllaw/lattices
    set vmonRootnameFile $output.vmon
    set bpmStatusFile /home/helios/oagData/sr/BPMStatus/config.sdds
    if [string length $extraMonFile] {
        if [catch {exec sddscombine $monitorDir/SROrbit-Rootnames.vmon $extraMonFile -pipe=out -merge \
		       | sddssort -pipe=in $vmonRootnameFile -col=s} result] {
	    return -code error $result
	}
    } else {
	file copy -force $monitorDir/SROrbit-Rootnames.vmon $vmonRootnameFile
    }
    exec sddsprocess $monitorDir/SROrbit-${BPMSuffix}.vmon $outputDir/SROrbit-${BPMSuffix}.vmon -match=col,Suffix=:x:*,Suffix=:y:*,|

    #SetStatus "CollectData: 2"

    set link [file readlink $dataDir]
    # check for second level of link of type
    # public/default -> ../etax10cm
    if {[file type [file join $mainDir $link]] == "link" } {
        set link [file tail [file readlink [file join $mainDir $link]]]
    }
    set seconds2InjectPV S-INJ:InjectionPeriodCounterM
    set seconds2InjectMin [expr (1 + $bpmPause + $correctorPause + $dataAcquisitionTime) * 2 + 3]

    #SetStatus "CollectData: 3"

    switch -exact -- $xyBothPlanes {
        0 {
            set directionList [list h]
            set configList [list $configurationX]
	    set kickList [list $amplitudeH]
        }
        1 {
            set directionList [list v]
            set configList [list $configurationY]
	    set kickList [list $amplitudeV]
        }
        2 {
            set directionList [list h v]
            set configList [list $configurationX $configurationY]
	    set kickList [list $amplitudeH $amplitudeV]
        }
    }
    #--- Checking presence of configuration files
    foreach configuration $configList {
        if ![file exists $dataDir/$configuration/config] {
            SetStatus "Can't find file $dataDir/$configuration/config"
            return
        }
    }
    set interval $bpmPause
    if !$simulateRun {
        #--- turn off orbit correction
        if [catch {turnOffOrbitCorrection} result] {
            SetStatus "Error in turning off orbit correction: $result"
            return
        }
        #--- TODO: disable pulsed magnets
    }

    #SetStatus "CollectData: 5"

    foreach direction $directionList configuration $configList delta $kickList {
        # Direction refers to the corrector plane
        if [string length $configuration] {
            # The output files for this corrector plane will be of the form <correctorPlane><rootname>...
            set outputRoot ${direction}${root}
            set output [file join $outputDir $outputRoot]${indexf}
            # Response of h BPM readings to $direction plane corrector
            set hBPMResponseOutputFile [file join $outputDir $outputRoot]hBPM-${indexf}.rm
            # Response of v BPM readings to $direction plane corrector
            set vBPMResponseOutputFile [file join $outputDir $outputRoot]vBPM-${indexf}.rm
            set config $mainDir/$link/$configuration/config
            switch $direction {
                h {set plane x}
                v {set plane y}
            }
            set corrFileList ""
            set configFile $dataDir/$configuration/config
            if ![file exists $dataDir/$configuration/config] {
                SetStatus "Can't find file $dataDir/$configuration/config"
                return
            }

            set corrList [exec sddsprocess $configFile -pipe=out -match=para,NameType=CorrectorNames \
                              -filter=col,Flag,1,1 \
                              | sdds2stream -pipe -col=Name]
            SetStatus "Corrector names in $configFile: [join $corrList ,]"
            
            #--- get the beam current before measurement 
            if [catch {exec cavget -list=$beamCurrentPV -pend=5} beamCurrentBefore] {
                return -code error $beamCurrentBefore
            }
            foreach corr $corrList {
                if $swapout {
                    #--- Making sure that there is enough time for one corrector before the swapout shot.
                    set counter 0
                    set abortHere 0
                    SetStatus "Need $seconds2InjectMin s between swap-out shots"
                    while {[APScavget -list=$seconds2InjectPV] < $seconds2InjectMin} {
                        if {$counter == 0} { SetStatus "Waiting for swapout shot..." }
                        after 1000
                        incr counter
                        if {$counter > 120} {
                            SetStatus "Error: Waiting for swapout shot takes too long."
                            set abortHere 1
                            break
                        }
                        update
                        if {[string length $abortVariable] && [info exists $abortVariable] \
                              && [subst \$$abortVariable]} {
                            set $abortVariable 0
                            SetStatus "Measurement aborted."
                            set abortHere 1
                            break
                        }
                    }
                    if $abortHere { break }
                }

                catch {file delete -force -- $outputDir/corr.mon}
                set fid [open $outputDir/corr.mon w]
                puts $fid "SDDS1"
                puts $fid "&column name=ControlName type=string &end"
                puts $fid "&column name=ReadbackName type=string &end"
                puts $fid "&data mode=ascii no_row_counts=1 &end"
                puts $fid "$corr:PS:SetCurrentC $corr:SetCurrentC"
                puts $fid "$beamCurrentPV $beamCurrentPV"
                close $fid

                file rename -force $outputDir/corr.mon $outputDir/scalars.mon 

                #################################
                # +delta tweek
                #################################
                if [catch {SetCorrector -simulateRun $simulateRun -corr $corr -delta $delta} result] {
                    return -code error "SetCorrector: $result"
                }
                SetStatus "Wait for $correctorPause s for correctors and $bpmPause s for bpms"
                APSWaitWithUpdate -waitSeconds [expr (1 + $correctorPause)] -updateInterval 1
		if $resetAveraging {
		    if [catch {exec cavput -list=[join $bpmList ,] -list=:BPM:APS-SA:FilterResetC=1} result] {
			return -code error "Reset BPM filters: $result"
		    }
		}
                APSWaitWithUpdate -waitSeconds [expr (1 + $bpmPause)] -updateInterval 1
                # wait for some time for corrector and bpms to settle
                set monID [open "|sddsvmonitor -erase -rootname=$vmonRootnameFile \
                   -suffixes=$outputDir/SROrbit-${BPMSuffix}.vmon ${output}.${corr}.raw \
                   -scalars=$outputDir/scalars.mon -singleShot=noprompt -steps=2" w]
                puts $monID "y"
                flush $monID
                APSWaitWithUpdate -waitSeconds $dataAcquisitionTime -updateInterval 1
                if $noiseMeasurement==0 {
                    #################################
                    # -delta tweek
                    # We only do this for non-noise measurments. For noise measurements, all data is collected at
                    # one setting.
                    #################################
                    if [catch {SetCorrector -simulateRun $simulateRun -corr $corr -delta [expr $delta * -2.0]} result] {
                        return -code error "SetCorrector: $result"
                    }
                    SetStatus "Wait for $correctorPause s for correctors and $bpmPause s for bpms"
                    APSWaitWithUpdate -waitSeconds [expr (1 + $correctorPause)] -updateInterval 1
		    if $resetAveraging {
			if [catch {exec cavput -list=[join $bpmList ,] -list=:BPM:APS-SA:FilterResetC=1} result] {
			    return -code error "Reset BPM filters: $result"
			}
		    }
                    APSWaitWithUpdate -waitSeconds [expr (1 + $bpmPause)] -updateInterval 1
                } else {
		    if $resetAveraging {
			if [catch {exec cavput -list=[join $bpmList ,] -list=:BPM:APS-SA:FilterResetC=1} result] {
			    return -code error "Reset BPM filters: $result"
			}
		    }
                    SetStatus "Wait for $bpmPause s for bpms"
                    APSWaitWithUpdate -waitSeconds [expr (1 + $bpmPause)] -updateInterval 1
                }
                puts $monID "y"
                flush $monID
                APSWaitWithUpdate -waitSeconds $dataAcquisitionTime -updateInterval 1
                catch {close $monID}
                #################################
                # restore corrector
                #################################
                if {$noiseMeasurement==0} {
                    if [catch {SetCorrector -simulateRun $simulateRun -corr $corr -delta $delta -dontWait 1} result] {
                        return -code error "SetCorrector: $result"
                    }
                } else {
                    if [catch {SetCorrector -simulateRun $simulateRun -corr $corr -delta [expr -1*$delta] -dontWait 1} result] {
                        return -code error "SetCorrector: $result"
                    }
                }

                # Do some processing for each corrector during loop
                set outputFile ${output}.${corr}.proc
		if [catch {ProcessRawFile -rawFile ${output}.${corr}.raw -outputFile $outputFile -bpmScaleFactor $bpmScaleFactor \
			       -bpmPause $bpmPause -corr $corr -delta $delta -noiseMeasurement $noiseMeasurement} result] {
		    return -code error "ProcessRawFile: $result"
		}
                lappend rawFileList ${output}.${corr}.raw
                lappend corrFileList $outputFile
                update
                if {[string length $abortVariable] && [info exists $abortVariable] \
                      && [subst \$$abortVariable]} {
                    #--- This break will take you out of corrector loop only, next one will break plane loop.
                    break
                }
            }
            if [catch {exec cavget -list=$beamCurrentPV -pend=5} beamCurrentAfter] {
                return -code error $beamCurrentAfter
            }
            
            SetStatus "Creating final response files ..."
            if [catch { eval exec sddscombine $corrFileList -pipe=out -merge \
			    | sddsprocess -pipe=in $output.tmp \
			    "-print=par,Configuration,$dataDir/$configuration" \
			    "-print=par,ConfigurationFile,$config" \
			    "-redefine=par,CurrentBeforeMeas,$beamCurrentBefore,units=mA" \
			    "-redefine=par,CurrentAfterMeas,$beamCurrentAfter,units=mA" 
                exec sddsprocess $output.tmp -pipe=out -match=col,Plane=x \
		    | sddsconvert -pipe -del=col,Plane \
                    | sddstranspose -pipe -oldColumnNames=BPMName \
                    | sddsxref -pipe /home/helios/oagData/sr/lattices/default/aps.twi \
                               -match=BPMName=ElementName -take=s \
                    | sddssort -pipe=in -column=s $hBPMResponseOutputFile
                exec sddsprocess $output.tmp -pipe=out -match=col,Plane=y \
		    | sddsconvert -pipe -del=col,Plane \
                    | sddstranspose -pipe -oldColumnNames=BPMName \
                    | sddsxref -pipe /home/helios/oagData/sr/lattices/default/aps.twi \
                               -match=BPMName=ElementName -take=s \
                    | sddssort -pipe=in -column=s $vBPMResponseOutputFile
                file delete $output.tmp 
	    } result] {
                return -code error $result
            }
            eval file delete $corrFileList $rawFileList
            SetStatus "Created files $hBPMResponseOutputFile and $vBPMResponseOutputFile"
	    if {[string length $abortVariable] && [info exists $abortVariable] && [subst \$$abortVariable]} {
		#--- This break will take you out of plane loop
		break
	    }
        }
    }

    #SetStatus "CollectData: 6"

    if {[string length $abortVariable] && [info exists $abortVariable] \
	    && [subst \$$abortVariable]} {
	set $abortVariable 0
	return -code error "Measurement aborted."
    }

    #------ Do dispersion measurement:
    if $doDisp {
        SetStatus "Measuring dispersion:"
	set rfFreqReadPV A014-IETS:BTC:SRSetFreqM
	set rfFreqSetPV A014-IETS:BTC:SROffsetFreqC
        set rateLimit 5.0
	set returnRfFreqLine [exec cavget -list=$rfFreqSetPV -float=%21.15e -cavputForm -pend=5]
	set rfFreqStep [expr $rfFreqChange * 2.0 / ($rfFreqSteps - 1)]
	exec sddsmakedataset $outputDir/rfFreq.mon -col=ControlName,type=string -data=$rfFreqReadPV,$rfFreqSetPV \
	    -col=ReadbackName,type=string -data=rfFrequency,FrequencyOffset
	#------ Wait for swapout shot:
	if $swapout {
	    set seconds2InjectMin [expr (1 + $bpmPause + $correctorPause + $dataAcquisitionTime) * $rfFreqSteps + 3]
	    set counter 0
	    set abortHere 0
            SetStatus "Need $seconds2InjectMin s between swap-out shots"
	    while {[APScavget -list=$seconds2InjectPV] < $seconds2InjectMin} {
		if {$counter == 0} { SetStatus "Waiting for swapout shot..." }
		after 1000
		incr counter
		if {$counter > 120} {
		    SetStatus "Error: Waiting for swapout shot takes too long."
		    set abortHere 1
		    break
		}
		update
		if {[string length $abortVariable] && [info exists $abortVariable] \
			&& [subst \$$abortVariable]} {
		    set $abortVariable 0
		    SetStatus "Measurement aborted."
		    set abortHere 1
		    break
		}
	    }
	    if $abortHere { break }
	}
	#------ get the beam current before measurement 
	if [catch {exec cavget -list=$beamCurrentPV -pend=5} beamCurrent] {
	    return -code error $beamCurrent
	}
      
	#------ Initial frequency change:
	SetStatus "Waiting for $correctorPause sec for rf frequency and $bpmPause sec for bpms..."
        exec cavput -list=$rfFreqSetPV=$rfFreqChange -delta=factor=-1 -ramp=step=[expr int($rfFreqChange/$rateLimit+2)],pause=1 -pend=5
	APSWaitWithUpdate -waitSeconds [expr (1 + $bpmPause + $correctorPause)] -updateInterval 1
	set freqRawFile $outputDir/rf$root$indexf.rfFreq.raw
	set monID [open "|sddsvmonitor -erase -rootname=$vmonRootnameFile \
                   -suffixes=$outputDir/SROrbit-${BPMSuffix}.vmon $freqRawFile \
                   -scalars=$outputDir/rfFreq.mon \
                   -singleShot=noprompt -steps=$rfFreqSteps -precision=double " w]
        
	puts $monID "y"
	flush $monID
	APSWaitWithUpdate -waitSeconds $dataAcquisitionTime -updateInterval 1
	#------ Loop over frequency:
	for {set i 1} {$i < $rfFreqSteps} {incr i} {
	    exec cavput -list=$rfFreqSetPV=$rfFreqStep -delta=factor=1 -ramp=step=[expr int($rfFreqStep/$rateLimit+2)],pause=1 -pend=5
	    SetStatus "Waiting for $correctorPause sec for rf frequency and $bpmPause sec for bpms..."
	    APSWaitWithUpdate -waitSeconds [expr (1 + $correctorPause)] -updateInterval 1
	    if $resetAveraging {
		if [catch {exec cavput -list=[join $bpmList ,] -list=:BPM:APS-SA:FilterResetC=1} result] {
		    return -code error "Reset BPM filters: $result"
		}
	    }
	    APSWaitWithUpdate -waitSeconds [expr (1 + $bpmPause)] -updateInterval 1
	    puts $monID "y"
	    flush $monID
	    APSWaitWithUpdate -waitSeconds $dataAcquisitionTime -updateInterval 1
	}
	catch {close $monID}
	SetStatus "Restoring frequency to starting value."
	exec cavput -list=$returnRfFreqLine -ramp=step=[expr $rfFreqSteps*int($rfFreqChange/$rateLimit+2)],pause=1 -pend=5
	SetStatus "Processing data..."
	if [catch {ProcessDispersionData -input $freqRawFile -output $outputDir/rf$root$indexf.disp -vmonFile $vmonRootnameFile -bpmScaleFactor $bpmScaleFactor} result] {
	    return -code error "ProcessDispersionData (${output}.rfFreq.raw): $result"
	}
    }

    SetStatus "Ready."
    return
}

#------------------------------------------------------------------------------------------------------
proc ProcessRawFile {args} {
    global BPMSuffix
    SetStatus "ProcessRawFile: $args"
    APSParseArguments {rawFile outputFile bpmPause corr delta noiseMeasurement bpmScaleFactor}
    set bpmStatusFile /home/helios/oagData/sr/BPMStatus/config.sdds
    if {$noiseMeasurement == 1} {
        # noise measurement---Setting delta=0.5 means we are just taking the difference between two states
        # (note the -2.0 in the -redef command below).
        set delta 0.5
    }
    if [catch {exec sddschanges $rawFile -pipe=out -change=:?:${BPMSuffix} -cop=Index,Rootname \
                 | sddsprocess -pipe \
                 "-define=param,BPMScaleFactor,$bpmScaleFactor" \
                 "-redef=col,%s,ChangeIn%s BPMScaleFactor * $delta / -2.0 /,select=ChangeIn:?:${BPMSuffix},edit=%/ChangeIn//,units=um/A" \
                 |  sddsconvert -pipe \
                 -retain=col,Rootname,:?:${BPMSuffix} -edit=column,*${BPMSuffix},%/://%/:${BPMSuffix}// \
                 | sddstranspose -pipe -newcolumnnames=Rootname -oldColumnNames=Plane \
                 | sddsprocess -pipe=in  $outputFile -print=col,Corrector,${corr} } result] {
        return -code error $result
    }
}
#------------------------------------------------------------------------------------------------------

proc PlotDispersionData {args} {
    global lattice OAGGlobal
    set outputDir ""
    set index -1
    set includeModel 0
    APSStrictParseArguments {outputDir index includeModel}
    set indexf [format %02d $index]
    set dispFile $outputDir/rfCorrector-$indexf.disp
    if ![file exists $dispFile] {
        SetStatus "not found: $dispFile"
        return
    }
    if $includeModel {
        set modelFile /home/helios/oagData/sr/lattices/default/aps.twi
        exec sddsplot -convert=col,eta?,mm,m,1e3 \
            -column=s,xDispersion,xDispersionSigma $dispFile -graph=error \
            -column=s,etax $modelFile -match=col,ElementType=*MON* -graph=sym,subtype=1 -end \
            -column=s,yDispersion,yDispersionSigma $dispFile -graph=error \
            -column=s,etay $modelFile -match=col,ElementType=*MON* -graph=sym,subtype=1 -end &
    } else {
        exec sddsplot \
            -column=s,xDispersion,xDispersionSigma $dispFile -graph=error \
            -column=s,xDispersion $dispFile -graph=sym,subtype=1,scale=0.5,fill \
            -column=s,xDispersion $dispFile -graph=line,type=1 -end \
            -column=s,yDispersion,yDispersionSigma $dispFile -graph=error \
            -column=s,yDispersion $dispFile -graph=sym,subtype=1,scale=0.5,fill \
            -column=s,yDispersion $dispFile -graph=line,type=1 -end  &
    }
}

proc ProcessDispersionData {args} {
    global lattice OAGGlobal
    APSParseArguments {input output vmonFile bpmScaleFactor}
    set tmpRoot /tmp/[APSTmpString]
    set twiFile $OAGGlobal(SRLatticesDirectory)/$lattice/aps.twi
    set circumference [exec sddsprocess $twiFile -pipe=out -process=s,max,%sMax | sdds2stream -pipe -parameter=sMax]
    set rfFreq0 [expr 299792458.0 / $circumference * 1296]
    set pars [exec sddsquery -par $input]
    foreach plane [list x y] {
	if [catch {exec sddsconvert $input -pipe=out -retain=col,Rootname,:$plane:LowPass1sAdjustedM \
                     -rename=column,:$plane:LowPass1sAdjustedM=$plane \
                     | sddsvslopes -pipe -indep=FrequencyOffset -sigma \
                     | sddsxref -pipe $twiFile -transfer=para,alphac -leave=* \
                     | sddsxref -pipe $vmonFile -take=s -match=Rootname -nowarning \
                     | sddssort -pipe -col=s \
                     | sddsprocess -pipe=in $tmpRoot.$plane \
                     "-define=param,BPMScaleFactor,$bpmScaleFactor" \
                     "-redef=col,${plane}Dispersion,${plane}Slope alphac * $rfFreq0 * chs 1e3 / BPMScaleFactor *,symbol=\$gc\$r\$a(1)\$b$plane\$n,units=mm" \
                     "-redef=col,${plane}DispersionSigma,${plane}SlopeSigma alphac * $rfFreq0 * 1e3 / BPMScaleFactor *,units=mm"} result] {
	    return -code error "Error processing dispersion: $result"
	}
            
    }
    exec sddsxref $tmpRoot.x $tmpRoot.y $output -take=yDispersion,yDispersionSigma 
}

proc SetCorrector {args} {
    #SetStatus "SetCorrector: $args"
    set dontWait 0
    APSParseArguments {simulateRun corr delta dontWait}
    set tolerance 0.2
    set pvAO ${corr}:PS:SetCurrentC
    set pvAI1 ${corr}:PS:MeasCurrentM
    set pvAI2 ${corr}:DCCT:CurrentM
    if [catch {APScavget -list=$pvAO,$pvAI1,$pvAI2} origValueList] {
        return -code error "Error reading $pvAO and $pvAI1 and $pvAI2: $origValueList"
    }
    set origValueAO [lindex $origValueList 0]
    set origValueAI1 [lindex $origValueList 1]
    set origValueAI2 [lindex $origValueList 2]
    set valueAI1 [expr $origValueAI1 + $delta]
    set valueAI2 [expr $origValueAI2 + $delta]
    #SetStatus "cavput -delta=factor=1 -list=$pvAO=[format %3.1f $delta]"
    if !$simulateRun {
        if [catch {exec cavput -delta=factor=1 -list=$pvAO=$delta -pend=5} result] {
            return -code error "Error setting $pvAO to $value amp: $result"
        }
        if !$dontWait {
            if [catch {exec cawait -waitfor=$pvAI1,lower=[expr $valueAI1-$tolerance],upper=[expr $valueAI1+$tolerance] -timeLimit=10} result] {
		if [catch {exec cawait -waitfor=$pvAI2,lower=[expr $valueAI2-$tolerance],upper=[expr $valueAI2+$tolerance] -timeLimit=10} result] {
		    catch {exec cavput -list=$pvAO=$origValueAO -pend=15}
		    return -code error "Problem with $pvAI settling to value of $valueAI with tolerance of $tolerance: $result"
		}
		SetStatus "======> It seems $pvAI1 has problems. Continue without errors based on $pvAI2."
            }
        }
    }
}

proc turnOffOrbitCorrection {args} {
    global orbitCorrectionRunning controllawMode dpControllawMode
    set orbitCorrectionRunning 0
    foreach coord {X Y} {
        if [catch {exec cavget -list=S:RC:OrbitControlLaw${coord}C -pend=10} running1] {
            return -code error $running1
        }
        if {$running1=="?"} {
            return -code error "Unable to get value of S:RC:OrbitControlLaw${coord}C"
        }
        if $running1 {
            set orbitCorrectionRunning 1
        }
    }
    if $orbitCorrectionRunning {
        if [catch {exec cavget -list=S:RC:OrbitControlLawXC,S:RC:OrbitControlLawYC,S:rfFreqControlLawRC -list=.SUSP -cavputForm} controllawMode] {
            return -code error "Problem getting orbit controllaw status: $controllawMode"
        }
        if [catch {exec cavput -list=S:RC:OrbitControlLawXC,S:RC:OrbitControlLawYC,S:rfFreqControlLawRC -list=.SUSP=1} result] {
            return -code error "Problem suspending controllaw: $result"
        }
    }
}

proc RestoreControllaw {args} {
    global orbitCorrectionRunning controllawMode
    if {$orbitCorrectionRunning && [string length $controllawMode]} {
        if [catch {exec cavput -list=$controllawMode} result] {
            return -code error "Can not restore orbit correction: $result"
        }
    }
}

SetStatus "Ready."
set orbitCorrectionRunning 0
set dpOrbitCorrectionRunning 0
set controllawMode ""
set dpControllawMode ""
set abortRun 0
set lattice default
set LatticeDir /home/helios/oagData/sr/orbitControllaw/lattices
set configurationX h.LOCO.20
set configurationY v.LOCO.20
set outputDir .
# No longer allow this to change
set root Corrector-
set index 0
set amplitudeH 2
set amplitudeV 1
set bpmPause 4
set correctorPause 1
set noiseMeasurement 0
set xyBothPlanes 2
set simulateRun 0
set swapout 0
set doDisp 1
set rfFreqChange 50
set rfFreqSteps 5
set dataAcquisitionTime 1
set launchLOCO 0
set makeSCRSave 1
set bpmScaleFactor 1
# I'd rather use LowPass1sM, but it doesn't work on weed
set BPMSuffix LowPass1sAdjustedM
MakeInputFrame .input -parent .userFrame
update
