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

# $Log: not supported by cvs2svn $
# Revision 1.18  2011/05/30 16:38:29  lemery
# Added emacs tcl-mode local variables at end of file.
#
# Revision 1.17  2011/01/30 08:48:47  xiaoam
# Fix the bug. There is no value variable, change it to setpoint
#
# Revision 1.16  2010/09/28 21:15:50  xiaoam
# Fix a mistake.
#
# Revision 1.14  2010/02/12 20:15:28  xiaoam
# Modify maximum current of CPU, fix a bug due to pv changes.
#
# Revision 1.13  2010/01/27 18:26:54  soliday
# Updated because of PV name changes related to the CPU.
#
# Revision 1.12  2009/05/23 05:50:30  xiaoam
# Fixed a small bug inside the script
#
# Revision 1.11  2009/03/17 21:21:12  emery
# Added choice of AC mode, but no functionality was given.
#
# Revision 1.10  2009/01/28 04:40:18  emery
# Change the order of modes in selection widget.
#
# Revision 1.9  2008/10/29 11:45:17  emery
# Passed CPUmode variable in StartScan arguments.
#
# Revision 1.8  2008/10/01 10:32:20  emery
# The +/- delta for the cavput on the ID04b:set_corr_?
# PV didn't seem to work, so I add a time delay between the two cavput.
#
# Revision 1.7  2008/09/30 10:01:54  emery
# Replace controllaw abort with suspend.
# Removed explicitly-named skipOrbit variable from
# WaitForOrbitConvergence in order to use a
# string variable that has that name as value.
# Fixed the logic of exiting the wait loop of
# orbit convergenace.
# Added button for TransferCPUCorrectors,
# whic is useful when setpoints (e.g. ID04b:set_corr_1)
# of CPU correctors are stale.
#
# Revision 1.6  2008/09/30 05:33:52  emery
# Added a "." to the name of the 1SV matrix file since the
# rootname often ends with a number, and the completed name would
# run the numbers together.
#
# Revision 1.5  2008/09/29 22:29:30  emery
# Added many more choices of correction matrix.
#
# Revision 1.4  2008/09/28 23:34:06  emery
# Added possibility of choosing a difference H-matrix.
# Set teh RTFB HP filters to higher values to speed-up
# convergence of CPU correctors.
# Increased gain of sddscontrollaws from 0.125 to 0.5.
#
# Revision 1.3  2007/10/10 11:57:41  emery
# Added option for H plane correction with weaker 2nd integral correction.
#
# Revision 1.2  2006/10/11 09:10:08  emery
# Made the skip orbit convergence button functional.
#
# Revision 1.1  2004/05/23 15:17:02  emery
# First installation under new name. Previous name was SRMeasureCPUperturbation.
#

# Log of application changes under the old name of
# SRMeasureCPUperturbation.
# Revision 1.2  2004/03/23 03:37:53  emery
# Added argument "comment" to scan procedure.
# Added runcontrol abort command before starting orbit correction.
# Added tests file to orbit correction.
# Added more bpms in list of orbit tolerance.
# Made variable vMAtrix global in MakeInputFrame procedure.
# Changed v-matrix entry box to a selection widget.
# Cahnged default v-matrix file name.
#
# Revision 1.1  2001/06/29 05:16:07  emery
# First installation.
#
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 CVSRevisionAuthor "\$Revision: 1.19 $ \$Author: lemery $"

APSApplication . -name "CPUmeasureDCperturbation" -version \
  $CVSRevisionAuthor -overview "Measures the CPU perturbation by scanning the main coil setpoints and using the dipole corrector coils as correctors. The dipole corrector coil values can be written to a feedforward corrector file suitable for loading into the CPU ioc."

set status "Ready."
APSScrolledStatus .status -parent .userFrame -width 80 -height 10 \
        -textVariable status

proc StartScan {args} {
    set coilSetPV ""
    set coilReadPV ""
    set currentTolerance 2
    set orbitTolerance 2
    set outputDir ""
    set root ""
    set index 0
    set comment ""
    set CPUmode ""
    set skipVariable ""
    set abortVariable ""
    set timeLimit 60
    set waitAtLeast 0
    set hMatrix ""
    set vMatrix ""
    APSParseArguments {coilSetPV coilReadPV currentTolerance orbitTolerance outputDir root index comment CPUmode skipVariable abortVariable timeLimit waitAtLeast hMatrix vMatrix}
    global mainPVSettings

    if {![string length $coilSetPV] || ![string length $coilReadPV] \
          || $currentTolerance <= 0 || $orbitTolerance <= 0} {
        return -code error "StartScan: Problem with arguments"
    }
    if [string length $abortVariable] {
        global $abortVariable
    }
    if [string length $skipVariable] {
        global $skipVariable
    }
    
    set output $outputDir/$root-[format %03ld $index]

    if [file exists $outputDir/$output] {
        return -code error "StartScan: file $output exists."
    }

    set tmpfile /tmp/[APSTmpString]

    set fileList ""
    foreach setpoint $mainPVSettings {
        #0)make sure no orbit correction is running.
        if [catch {exec cavput -list=S:RC:OrbitControlLawXC,S:RC:OrbitControlLawYC,S:rfFreqControlLawRC -list=.SUSP=1} result ] {
            return -code error "StartScan: $result"
        }
        if [catch {exec cavput -list=DP:S: \
                     -list=OrbitControlLawX,OrbitControlLawY \
                     -list=SDDS.SUSP=1 \
                 } result ] {
            return -code error "StartScan: $result"
        }

        # set the RTFB high-pass filter to high values in order to
        # steady the orbit while not causing frequency overlap problems.
        exec cavput -list=SRFB:fhSlAO=5,SRFB:fhAO=1
        exec cavput -list=SRFB:GBL:loadBO=1

        # wait appropriate amount of time for RC to clear
        after [expr 2 * 1000]
        if {[string length $abortVariable] && [subst \$$abortVariable]} {
            set $abortVariable 0
            break
        }
        #1)send new value to main coil of CPU.
        if [catch {exec cavput -list=$coilSetPV=$setpoint \
                 } result ] {
            return -code error "StartScan: $result"
        }
        if {[string length $abortVariable] && [subst \$$abortVariable]} {
            set $abortVariable 0
            break
        }
        #2)wait for ramping to end. This procedure can handle
        #       a setpoint and a readback of opposite sign.
        APSSetVarAndUpdate status "Ramping to $setpoint ..."
        after 1000
        exec cawait -interval=1 -timelimit=60 -waitfor=ID04b:busy,equal=0
        if {[string length $abortVariable] && [subst \$$abortVariable]} {
            set $abortVariable 0
            break
        }
        # after ramping ends the actual corrector current differ from the
        # set points PVs because the CPU updates the AFG independently of the
        # set point PVs.

        # Re-apply the set points from last iteration after the ramp
        # ended, thus undoing any change the CPU AFG applied to the
        # correctors from the stored tables.  This will make the
        # values continue from one scan to the next, avoiding steps
        # that are seen on 09/30/2008 data.  Also this will make the
        # orbit convergence faster. Use cavput twice to jog the CPU
        # control system to actually process the PVs and update the
        # values from last iteration.
        APSSetVarAndUpdate status "Reapply the corrector current PVs by jogging..."
        exec cavput -list=ID04b:=0.01 -list=US,DS -list=_HorCorrSetpt -delta 
        exec cavput -list=ID04b:=0.01 -list=US,DS -list=_VertCorrSetpt -delta 
        after 1000
        exec cavput -list=ID04b:=0.01 -list=US,DS -list=_HorCorrSetpt -delta=factor=-1 
        exec cavput -list=ID04b:=0.01 -list=US,DS -list=_VertCorrSetpt -delta=factor=-1 
        # wait some time for orbit to settle.
        after 2000
        
        #3)run sddscontrollaw for the pairs of CPU corrector coils in each plane.
        #   use limits on the correctors. If limits occur then user must
        # select the single-variable controllaw.
        set CPUdataDir /home/helios/oagData/sr/IDs/CPU/DCmodes
        set slowOCinterval 3
        APSExecLog .srXOrbitControllaw \
          -lineLimit 2048  -width 100 \
          -name "SR X-orbit Correction for CPU" \
          -unixCommand "sddscontrollaw $CPUdataDir/${hMatrix}.inv \
                     -test=$CPUdataDir/${hMatrix}.tests \
                     -runControlPV=string=S:RC:OrbitControlLawXC \
                     \"-runControlDescription=string=special for CPU\" \
                     -steps=1000 -gain=0.5 -verb -interval=$slowOCinterval"

        
        APSExecLog .srYOrbitControllaw \
          -lineLimit 2048 -width 100 \
          -name "SR Y-orbit Correction for CPU" \
          -unixCommand "sddscontrollaw $CPUdataDir/$vMatrix.inv \
                     -test=$CPUdataDir/$vMatrix.tests \
                     -runControlPV=string=S:RC:OrbitControlLawYC \
                     \"-runControlDescription=string=special for CPU\" \
                     -steps=1000 -gain=0.5 -verb -interval=$slowOCinterval"

        APSSetVarAndUpdate status "sddscontrollaws started..."
        update
        if {[string length $abortVariable] && [subst \$$abortVariable]} {
            set $abortVariable 0
            break
        }
        
        #5)wait for orbit convergence.
        APSSetVarAndUpdate status "Wait for orbit convergence..."
        if [catch {WaitForOrbitConvergence -tolerance $orbitTolerance \
                     -waitAtLeast $waitAtLeast \
                     -timeLimit $timeLimit \
                     -skipVariable $skipVariable} result] {
            if [string match $result timeout] {
                APSSetVarAndUpdate status "Orbit convergence timed out. Proceeding with next point..."
            } else {
                APSSetVarAndUpdate status "StartScan: $result"
                return -code error "StartScan: $result"
            }
        }
        
        #6)stop sddscontrollaws 
        after 1000
        if [catch {exec cavput -list=S:RC:OrbitControlLawXC,S:RC:OrbitControlLawYC,S:rfFreqControlLawRC -list=.ABRT=1} result ] {
            return -code error "StartScan: $result"
        }
        # wait appropriate amount of time for RC to clear
        after [expr 2 * 1000]
        if {[string length $abortVariable] && [subst \$$abortVariable]} {
            set $abortVariable 0
            break
        }
        update

        #7)record the *[DU]S_Hor/Vert* PVs for CPU current setpoint.
        APSSetVarAndUpdate status "Recording corrector values..."
        if [catch {exec sddsstatmon $CPUdataDir/CPU.mon $tmpfile.$setpoint \
                     -interval=0.5 -includeStatistics=mean \
                     -samplesPerStatistic=10 -updateInterval=10 -steps=1 \
                     "-comment=Comment,[APSMakeSafeQualifierString $comment]" \
                 } result] {
            return -code error "StartScan: $result"
        }
        update
        lappend fileList $tmpfile.$setpoint
    }
    # process results
    if [catch {eval exec sddscombine $fileList -pipe=out -merge \
                 | sddsprocess -pipe \
                 -print=para,Mode,$CPUmode \
                 | sddsconvert -pipe=in $output \
                 -dele=col,Step,CAerrors,Time,TimeOfDay,DayOfMonth \
                 -editnames=col,*Mean,%/Mean// \
             } result] {
        return -code error "StartScan: $result"
    }
    APSSetVarAndUpdate status "Done."
    return
}

# This procedure should not be used willy-nilly because of the large
# offset between readback and set point.
proc TransferCPUCorrectors {} {
    set argList ""

    set value [exec caget ID04b:DS_VertCorrRdbk]
    lappend argList ID04b:DS_VertCorrSetpt=$value
    set value [exec caget ID04b:US_VertCorrRdbk]
    lappend argList ID04b:US_VertCorrSetpt=$value
    set value [exec caget ID04b:DS_HorCorrRdbk]
    lappend argList ID04b:DS_HorCorrSetpt=$value
    set value [exec caget ID04b:US_HorCorrRdbk]
    lappend argList ID04b:US_HorCorrSetpt=$value

    exec cavput -list=[join $argList ,]
    return
}

proc WaitForOrbitConvergence {args} {
    set orbitTolerance 0.010
    set timeLimit 60
    set skipVariable ""
    set waitAtLeast 0
    APSParseArguments {orbitTolerance timeLimit skipVariable waitAtLeast}
    
    if [string length $skipVariable] {
        global $skipVariable
    }
    APSWaitWithUpdate -waitSeconds $waitAtLeast -updateInterval 1
    set orbitTolerance [expr abs($orbitTolerance)]
    set bpmList {S4B:P1 S5A:P1 S5B:P1 S15B:P1 S25B:P1 S35B:P1 S10B:P1 S20B:P1 S30B:P1}
    foreach bpm $bpmList {
        lappend PVList $bpm:msAve:x:ErrorCC
        lappend PVList $bpm:msAve:y:ErrorCC
    }
    set first 1
    foreach PV $PVList {
        lappend waitList -waitfor=$PV,lowerLimit=[expr $orbitTolerance * -1],upperLimit=$orbitTolerance
        if {!$first} {
            lappend waitList -and
        }
        set first 0
    }
    set waitTime 2
    set steps [expr int($timeLimit / $waitTime) + 1]
    APSSetVarAndUpdate status "Waiting $waitTime seconds $steps times...."
    update
    while {$steps} {
        APSSetVarAndUpdate status "Waiting $steps more steps..."
        update
        if {[string length $skipVariable] && [subst \$$skipVariable]} {
            APSSetVarAndUpdate status "Skipping orbit convergence. Exiting WaitForOrbitConvergence..."
            set $skipVariable 0
            return 
        }
        update
        if [catch {eval exec cawait -interval=1 -timelimit=$waitTime $waitList \
                 } result] {
        } else {
            APSSetVarAndUpdate status "Returned from WaitForOrbitConvergence because of convergence."
            update
            return
        }
        incr steps -1
        update
    }
    APSSetVarAndUpdate status "Returned from WaitForOrbitConvergence beacsue of timeout."
    return -code error "timeout"
}

proc MakeInputFrame {widget args} {
    global vMatrix
    set parent ""
    
    APSStrictParseArguments {parent}
    
    set outputDir .
    APSFrame $widget -parent $parent -label "Input parameters"
    set w $parent$widget.frame

    APSLabeledEntry .outputDir -parent $w \
      -label "Output directory:" -textVariable outputDir \
      -contextHelp "Enter a name for the output file directory." -width 50
    APSButton .daily -parent $w.outputDir -packOption "-anchor e" \
      -text "daily" -size small \
      -command {set outputDir [APSGoToDailyDirectory -subdirectory CPU]}
    APSLabeledEntry .root -parent $w -label "Root name of file: " \
      -textVariable root \
      -contextHelp "Enter the root name of the file for the processed output <root>-nnn."
    APSLabeledEntry .index -parent $w -label "Index: " -textVariable index \
      -contextHelp "Enter the index (minus 1) to be part of the name of the output file. The index is incremented by 1 before execution."

    APSRadioButtonFrame .cpuMode -parent $w -orientation horizontal \
      -label "CPU mode: " -variable CPUmode -buttonList {CW CCW V H} \
      -valueList {CW CCW V H} \
      -commandList {setStartEnd setStartEnd setStartEnd setStartEnd} \
      -contextHelp \
      "Choose mode of CPU."

    APSLabeledOutput .setPV -parent $w -label "Set PV: "  -textVariable coilSetPV
    APSLabeledOutput .readPV -parent $w -label "Read PV: "  -textVariable coilReadPV
    APSLabeledEntry .comment -parent $w -label "Comment: " -textVariable comment \
      -width 55 \
      -contextHelp "Enter a comment to be written as a parameter to the output file."

    APSLabeledEntry .current -parent $w -width 80 \
      -textVariable mainPVSettings \
      -label "CPUMainCoilSettings" \
      -contextHelp "CPU main coil settings for doing measurement. The list is from current correction file and you can delete or insert value to it"

    APSLabeledEntry .timeLimit -parent $w -label "Time limit (s): " \
      -textVariable timeLimit \
      -contextHelp "Enter the time limit for each steps."
    APSLabeledEntry .waitAtLeast -parent $w -label "Minimum time at each step (s): " \
      -textVariable waitAtLeast \
      -contextHelp "Enter the minimum time at each steps."
    APSLabeledEntry .orbitTol -parent $w -label "Orbit tolerance (mm): " \
      -textVariable orbitTolerance \
      -contextHelp "Enter the orbit tolerance, the minimum orbit before delcaring the orbit converged, and going to the next step."
    APSRadioButtonFrame .hMatrix -parent $w -orientation horizontal \
      -label "H-matrix :" -variable hMatrix -buttonList {Normal "1SV" "Reduced-gain 2nd integral" "Upstream only" "Downstream only"} \
      -valueList {hResponse hResponse.1SV hResponseMod hResponse#1 hResponse#2} -contextHelp \
      "Use one of the matrices prepared for orbit correction in H plane."
    APSRadioButtonFrame .vMatrix -parent $w -orientation horizontal \
      -label "V-matrix :" -variable vMatrix -buttonList {Normal "1SV" "Reduced-gain 2nd integral"  "Upstream only" "Downstream only"} \
      -valueList {vResponse vResponse.1SV vResponseMod vResponse#1 vResponse#2} -contextHelp \
      "Use one of the matrices prepared for orbit correction in V plane. The matrix with only one corrector is used when the other corrector has reached its limit of 7.5 A."

}

proc startControllaw {args} {
    global vMatrix hMatrix
    APSParseArguments {plane}
    set CPUdataDir /home/helios/oagData/sr/IDs/CPU/DCmodes

    # set the RTFB high-pass filter to high values in order to
    # steady the orbit while not causing frequency overlap problems.
    exec cavput -list=SRFB:fhSlAO=5,SRFB:fhAO=1
    exec cavput -list=SRFB:GBL:loadBO=1

    # the interval has to be this slow because of the time
    # it take to update the CPU correctors.
    set slowOCinterval 3
    switch $plane {
        H {
            if [catch {exec cavput -list=S:RC:OrbitControlLawXC.CLR=1} result ] {
                return -code error "StartScan: $result"
            }
            APSExecLog .srXOrbitControllaw \
              -lineLimit 2048 -width 100 \
              -name "SR X-orbit Correction for CPU" \
              -unixCommand "sddscontrollaw $CPUdataDir/${hMatrix}.inv \
                -test=$CPUdataDir/${hMatrix}.tests \
                -runControlPV=string=S:RC:OrbitControlLawXC \
               \"-runControlDescription=string=special for CPU\" \
                 -steps=1000 -gain=0.5 -verb -interval=$slowOCinterval"
        }
        V {
            if [catch {exec cavput -list=S:RC:OrbitControlLawYC.CLR=1} result ] {
                return -code error "StartScan: $result"
            }
            APSExecLog .srYOrbitControllaw \
              -lineLimit 2048 -width 100 \
              -name "SR Y-orbit Correction for CPU" \
              -unixCommand "sddscontrollaw $CPUdataDir/$vMatrix.inv \
                -test=$CPUdataDir/$vMatrix.tests \
                -runControlPV=string=S:RC:OrbitControlLawYC \
               \"-runControlDescription=string=special for CPU\" \
                 -steps=1000 -gain=0.5 -verb -interval=$slowOCinterval"
        }
    }
    return
}

proc setStartEnd {} {
    global CPUmode coilSetPV coilReadPV mainPVSettings
    switch $CPUmode {
        H {
            set coilSetPV ID04b:set_V_coil
            set coilReadPV ID04b:V_coil
        }
        default {
            set coilSetPV ID04b:set_H_coil
            set coilReadPV ID04b:H_coil
        }
    }
    SelectCPUSettings $CPUmode
    return
}

proc SelectCPUSettings {mode} {
    global mainPVSettings

    set root "/home/helios/oagData/"
    switch $mode {
        AC {
            set fileroot $root
            lappend fileroot [lindex [exec caget ID04b:corrFile_7.VAL] 1]
            set filename [join $fileroot ""]
            set setpointList [exec sddsprocess $filename -pipe=out -print=para,value,%4.0f,MainCurrent \
                | sdds2stream -pipe=in -para=value]
        }
        default {
            set fileroot $root
            lappend fileroot [lindex [exec caget ID04b:corrFile.VAL] 1]
            set filename [join $fileroot ""]
            set setpointList [exec sddsprocess $filename -pipe=out \
                -match=para,Mode=$mode \
                "-redef=col,I,I abs" -print=col,value,%.0f,I \
                | sddssort -pipe -col=I,increasing \
                | sdds2stream -pipe=in -col=value]
        }
    }
    set mainPVSettings [join $setpointList " "]
}

set outputDir .
set root ""
set index -1
set timeLimit 60
set waitAtLeast 20
#set currentTolerance 5.0
set orbitTolerance 0.001
set CPUmode CW
setStartEnd
set abortScan 0
set skipOrbit 0
set comment test
set root up
set vMatrix vResponse
set hMatrix hResponse

MakeInputFrame .input -parent .userFrame

APSButton .run -parent .userFrame -text "SCAN CPU" -command \
  { \
      APSDisableButton .userFrame.run.button 
      APSEnableButton .userFrame.stop.button
      APSEnableButton .userFrame.skipOrbit.button
      incr index
      catch {StartScan -outputDir $outputDir \
               -root $root -index $index \
               -timeLimit $timeLimit -waitAtLeast $waitAtLeast\
               -coilSetPV $coilSetPV -coilReadPV $coilReadPV \
               -CPUmode $CPUmode \
               -orbitTolerance $orbitTolerance \
               -comment $comment \
               -skipVariable skipOrbit \
               -abortVariable abortScan \
               -hMatrix $hMatrix \
               -vMatrix $vMatrix \
           } status
      update idletasks
      APSDisableButton .userFrame.stop.button 
      APSDisableButton .userFrame.skipOrbit.button 
      APSEnableButton .userFrame.run.button }

APSButton .stop -parent .userFrame -text STOP -command \
  { \
      set abortScan 1
      APSDisableButton .userFrame.stop.button
      APSDisableButton .userFrame.skipOrbit.button 
      APSEnableButton .userFrame.run.button } \
  -contextHelp "Aborts scan."

APSButton .skipOrbit -parent .userFrame -text "SKIP ORBIT" -command \
  { \
      set skipOrbit 1 } \
  -contextHelp "Skips waiting for orbit convergence."

APSButton .transfer -parent .userFrame -text "Transfer CPU corrector set points" -command \
  { \
      TransferCPUCorrectors } \
  -contextHelp "This procedure should not be used willy-nilly because of the large offset between readback and set point. This is useful to help sddscontrollaw start with close to the right values."

APSButton .startHcontrollaw -parent .userFrame -text "Start H controllaw" -command \
  { \
      startControllaw -plane H} \
  -contextHelp "Start H controllaw, usually because the controllaw timed out for some reason. Do this only when orbit correction is supposed to run."
APSButton .startVcontrollaw -parent .userFrame -text "Start V controllaw" -command \
  { \
      startControllaw -plane V} \
  -contextHelp "Start V controllaw, usually because the controllaw timed out for some reason. Do this only when orbit correction is supposed to run."

APSDisableButton .userFrame.stop.button 
APSDisableButton .userFrame.skipOrbit.button 


# Local Variables:
# mode: tcl
# indent-tabs-mode: nil
# End:
