#!/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)]

set CVSRevisionAuthor "\$Revision: 1.19 $ \$Author: lemery $"

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

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

proc StartScan {args} {
    set coilSetPV ""
    set coilReadPV ""
    set currentTolerance 0.1
    set orbitTolerance 0.1
    set outputDir ""
    set root ""
    set index 0
    set comment ""
    set skipVariable ""
    set abortVariable ""
    set timeLimit 60
    set waitAtLeast 0
    set hMatrix ""
    APSParseArguments {coilSetPV coilReadPV currentTolerance orbitTolerance outputDir root index comment  skipVariable abortVariable timeLimit waitAtLeast hMatrix  SCUdataDir}
    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 $output] {
        return -code error "StartScan: file $output exists."
    }

    set tmpfile /tmp/[APSTmpString]

    set fileList ""
    # make corrector monitor file
    regexp {(.*):MainCurrentSet} $coilSetPV match prefix
    exec sddsmakedataset $outputDir/SCUcorrector.mon \
              -column=ControlName,type=string -data=$coilSetPV,${prefix}:PSCorr1Set,${prefix}:PSCorr2Set,${prefix}:PSCorr3Set
    set oldSetpoint [APScavget -list=$coilSetPV]
    foreach setpoint $mainPVSettings {
        #0)make sure no orbit correction is running.
        if [catch {APScavput -list=S:RC:OrbitControlLawXC,S:RC:OrbitControlLawYC,S:rfFreqControlLawRC -list=.SUSP=1} result ] {
            return -code error "StartScan: $result"
        }
        if [catch {APScavput -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 SCU. First go with the look-up table.
        if [catch {APScavput -list=${prefix}:IndCorr=0 \
                 } result ] {
            return -code error "StartScan: $result"
        }
        if [catch {APScavput -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
        # make a good guess when the SCU ramps to setpoint
        APSWaitWithUpdate -waitSeconds [expr 5 + abs($setpoint - $oldSetpoint)/2.0] -updateInterval 1
#        if [catch {exec cawait -interval=1 -timelimit=60 -waitfor=$coilReadPV,lowerLimit=[expr $setpoint - $currentTolerance],upperlimit=[expr $setpoint + $currentTolerance] \
#                 } result ] {
#            return -code error "StartScan (cawait): $result"

#        }
        #  go to independent mode
        if [catch {APScavput -list=${prefix}:IndCorr=1 \
                 } result ] {
            return -code error "StartScan: $result"
        }
        if {[string length $abortVariable] && [subst \$$abortVariable]} {
            set $abortVariable 0
            break
        }

        # wait some time for orbit to settle.
        after 2000
        
        #3)run sddscontrollaw for the pairs of SCU corrector coils in each plane.
        # for now the matrices are located here
        set slowOCinterval 3
        APSExecLog .srXOrbitControllaw \
          -lineLimit 2048  -width 100 \
          -name "SR X-orbit Correction for SCU" \
          -unixCommand "sddscontrollaw $SCUdataDir/${hMatrix}.inv \
                     -test=$SCUdataDir/${hMatrix}.tests \
                     -runControlPV=string=S:RC:OrbitControlLawXC \
                     \"-runControlDescription=string=special for SCU\" \
                     -steps=1000 -gain=0.25 -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. Oh well. 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: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 corrector PVs for SCU current setpoint.
        APSSetVarAndUpdate status "Recording corrector values in $tmpfile.$setpoint..."
        if [catch {exec sddsstatmon $SCUdataDir/SCUcorrector.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
        set oldSetpoint $setpoint
    }
    # process results
    APSSetVarAndUpdate status "You have successfully made a scan of the SCU1."
    if [catch {eval exec sddscombine $fileList -pipe=out -merge \
                   | sddsprocess -pipe \
                   \"-print=para,SetpointList,$mainPVSettings\" \
                 | 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
}

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 {S1B:P1 S2A:P1 S5B:P1 S15B:P1 S25B:P1 S35B:P1 S10B:P1 S20B:P1 S30B:P1}
    foreach bpm $bpmList {
        lappend PVList $bpm:msAve:x:ErrorCC
# on y-plane for SCU correction
#        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 because 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 SCU1]}
    APSLabeledEntry .scuDataDir -parent $w \
      -label "SCU data directory:" -textVariable SCUdataDir \
      -contextHelp "Enter a name for the location of the SCU response matrix." -width 50
    APSButton .dailySCU -parent $w.scuDataDir -packOption "-anchor e" \
      -text "daily" -size small \
      -command {set SCUdataDir [APSGoToDailyDirectory -subdirectory SCU1]}
    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."

    APSLabeledOutput .setPV -parent $w -label "Set PV: "  -textVariable coilSetPV -width 28
    APSLabeledOutput .readPV -parent $w -label "Read PV: "  -textVariable coilReadPV -width 28
    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 "SCUMainCoilSettings" \
      -contextHelp "SCU 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" "First corr. only" "Second corr. only"} \
      -valueList {hResponse hResponse.1SV  hResponse#1 hResponse#2} -contextHelp \
      "Use one of the matrices prepared for orbit correction in H plane."

}

proc startControllaw {args} {
    global vMatrix hMatrix SCUdataDir
    APSParseArguments {plane}

    # 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 SCU 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 SCU" \
              -unixCommand "sddscontrollaw $SCUdataDir/${hMatrix}.inv \
                -test=$SCUdataDir/${hMatrix}.tests \
                -runControlPV=string=S:RC:OrbitControlLawXC \
               \"-runControlDescription=string=special for SCU\" \
                 -steps=1000 -gain=0.5 -verb -interval=$slowOCinterval"
        }
    }
    return
}

set prefix ID06ds
set args $argv
APSParseArguments {prefix}

set outputDir .
set index -1
set timeLimit 60
set waitAtLeast 20
# readback is only about 0.7 A, so set to 1.0 for ability to go on.
set currentTolerance 1.0
set orbitTolerance 0.001
set mainPVSettings "50 100 150 200 250 300 350 400 450"
set coilSetPV  ${prefix}:MainCurrentSet
set coilReadPV ${prefix}:MainCurrentRdbk
set abortScan 0
set skipOrbit 0
set comment test
set root SCU1
set hMatrix hResponse
set vMatrix vResponse

MakeInputFrame .input -parent .userFrame

APSButton .run -parent .userFrame -text "SCAN SCU" -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 \
               -orbitTolerance $orbitTolerance \
               -comment $comment \
               -skipVariable skipOrbit \
               -abortVariable abortScan \
               -hMatrix $hMatrix -SCUdataDir $SCUdataDir \
           } 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 .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."

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

set SCUdataDir /home/helios/SR/daily/2015/06/10/1/SCU1
set SCUdataDir .

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