#!/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 apsttk 1
set CVSRevisionAuthor "\$Author: shang $"

proc setStatus {text} {
    APSSetVarAndUpdate status $text
}

#Go to daily dir button
proc GoToDailyDirectory {} {
    global Rootname
    set DD [APSGoToDailyDirectory]
    cd $DD
    set Rootname [file join $DD $Rootname]
}

#Setup GUI
proc BuildApplication {} {

    APSApplication . -name MeasureLinacDispersion

    global status
    set status "This script measures the dispersion in the linac"
    APSScrolledStatus .status -parent .userFrame -textVariable status -height 10 -width 60 -packOption "-fill both -expand true"
    set status "downstream of L2 by modulating the PFN in L2."
    set status "The user needs:"
    set status "1. Disable the trajectory controllaw"
    set status "2. Enable the linac RF-Beam phase controllaw"
    set status "This script with suspend power controllaw for the selected sector."
    set status "Do not use less than 4 cycles or there will be an error in post processing."

    # output file 
    APSFrame .file -parent .userFrame -label "Output File"
    set w .userFrame.file.frame
    global Rootname DataFileIndex
    set Rootname linacDispBPM
    set DataFileIndex -1
    

    APSLabeledEntry .le1 -parent $w -label "Rootname: " -textVariable Rootname \
      -width 60 \
      -contextHelp "Enter the rootname, including the directory name, for output files"
    APSLabeledEntry .le2 -parent $w -label "Index: " -textVariable DataFileIndex \
      -contextHelp "The output file index for the next run is obtained by incrementing this value by 1."
    APSButton .gtdd -parent $w -text "Go to daily dir" -width "" -command \
      GoToDailyDirectory -contextHelp "Creates and moves to the daily directory."

    # measurement parameters 
    APSFrame .parameters -parent .userFrame -label "Measurement Parameters"
    set w .userFrame.parameters.frame
    global PFNModulation Cycles PostChangePause Averages InterMeasPause Sector
    set PFNModulation 0.5
    set Cycles 6
    set PostChangePause 30
    set Averages 20
    set InterMeasPause 0.5
    set Sector L2
    APSRadioButtonFrame .sector -parent $w -label "Sector: " \
      -valueList {L2 L4 L5} -buttonList {L2 L4 L5} -variable Sector \
      -contextHelp "Choose the sector you wish to modulate." -orientation horizontal
    APSLabeledEntry .pfnModLev -parent $w -label "PFN modulation level (+/-kV): " \
      -textVariable PFNModulation -contextHelp \
      "Enter the amount by which to modulate the PFN in each direction. The total modulation will be twice this amount."
    APSLabeledEntry .cycles -parent $w -label "Cycles: " -textVariable Cycles \
      -contextHelp "Enter the number of modulation cycles."
    APSLabeledEntry .pause -parent $w -label "Post-change Pause (s):" -textVariable \
      PostChangePause -contextHelp \
      "Enter the number of seconds to wait after changing the PFN before taking data. You should allow sufficient time for the phase feedback on L2 to converge."
    APSLabeledEntry .averages -parent $w -label "Readings to Average:" -textVariable \
      Averages -contextHelp \
      "Enter the number of readings to average at each step of each cycle. This is in addition to any averaging the happens in the IOC."
    APSLabeledEntry .interMeasPause -parent $w -label "Pause Between Readings (s):" \
      -textVariable InterMeasPause -contextHelp \
      "Enter the pause between successive readings that are being averaged.  Should match the IOC averaging time, if any, for BPMs."

    APSFrame .correct -parent .userFrame -label "Correction Parameters"
    set w .userFrame.correct.frame
    global CorrectionGain
    set CorrectionGain 0.5
    APSLabeledEntry .correctionGain -parent $w -label "Correction gain: " \
      -textVariable CorrectionGain -contextHelp "Enter gain for correction"
    
    # action buttons
    APSFrame .action -parent .userFrame 
    set w .userFrame.action.frame
    APSButton .run -parent $w -text Run -width "" -command \
      { incr DataFileIndex ; \
          APSPerformLinacDispersionMeasurement \
          -output [format $Rootname-%03ld.sdds $DataFileIndex] \
          -interactive 1 -sector $Sector \
          -pfnModulation $PFNModulation -cycles $Cycles -postChangePause $PostChangePause \
          -averages $Averages -interMeasPause $InterMeasPause -process 1 -display 1} \
      -contextHelp "Begin a measurement.  Data is automatically processed and displayed."
    APSButton .review -parent $w -text Review... -width "" -command \
      {ReviewMeasurement -directory [file dirname $Rootname]} \
      -contextHelp "Reviews results of a dispersion measurement."
    APSButton .correct -parent $w -text Correct... -width "" -command \
      {CorrectDispersion -gain $CorrectionGain -statusCallback setStatus \
         -directory [file dirname $Rootname]} \
      -contextHelp "Allows correcting the dispersion based on a previous measurement."
}

#Run button
proc APSPerformLinacDispersionMeasurement {args} {
    global apsPerformLinacDispersionMeasurementControl
    set apsPerformLinacDispersionMeasurementControl 0

    set output ""
    set interactive 1
    set pfnModulation 0.1
    set cycles 12
    set postChangePause 6
    set averages 1
    set interMeasPause 1
    set process 0
    set display 0
    set sector L2
    if {[APSStrictParseArguments {output interactive pfnModulation cycles sector \
                                    postChangePause averages interMeasPause process display}] || \
          ![string length [set output [string trim $output]]] || \
          $pfnModulation<0 || $cycles<1 || $postChangePause<0 || $averages<0 || \
          $interMeasPause<0} {
        return -code error "APSPerformLinacDispersionMeasurement: parameter error"
    }
    if [string match L\[12345\] $sector]==0 {
        return -code error "APSPerformLinacDispersionMeasurement: sector is invalid: $sector"
    }

    # check for prior existence of the output file
    if {[file exists $output]} {
        if {$interactive && \
              [APSMultipleChoice [APSUniqueName .] -question \
                 "$output exists already.  What do you want to do?" \
                 -returnList {1 0} -labelList {Abort Overwrite}]} {
            return
        }
        file delete -force $output
    }

    # enable phase control loop on ${sector}
    #/net/helios/iocapps/adlsys/linac/liPhaseCtl.adl
    ##if [catch {exec cavput -list=${sector}:AUTO:PH:holdAtMeasdBO=1} result] {
    ##    return -code error "APSPerformLinacDispersionMeasurement: $result"
    ##}
    # save state of power control loop on ${sector}, plus trajectory controllaws
    #    if [catch {exec cavget -list=L:${sector}StabilizerRC.SUSP -cavputform} ControllawState] {
    #        return -code error "APSPerformLinacDispersionMeasurement: $ControllawState"
    #    }
    if [catch {exec cavget -list=L:${sector}Stabilizer:SDDS.SUSP -cavputform} ControllawState] {
        return -code error "APSPerformLinacDispersionMeasurement: $ControllawState"
    }
    # suspend power control loop on ${sector} and trajectory controllaws
    #    if [catch {exec cavput -list=L:${sector}StabilizerRC -list=.SUSP=1} result] {
    #        return -code error "APSPerformLinacDispersionMeasurement: $result"
    #    }
    if [catch {exec cavput -list=L:${sector}Stabilizer:SDDS -list=.SUSP=1} result] {
        return -code error "APSPerformLinacDispersionMeasurement: $result"
    }

    # create the input file for sddsexperiment
    set tmpFile /tmp/[APSTmpString]
    APSAddToTmpFileList -ID PerformLinacDispersionMeasurement \
      -fileList $tmpFile
    exec replaceText /home/helios/oagData/linac/dispersionMeasurement/dispersionTemplate.exp \
      $tmpFile \
      -original=<points>,<pfnModulation>,<averages>,<postChangePause>,<interMeasPause>,<sector> \
      -replace=[expr 2*$cycles],$pfnModulation,$averages,$postChangePause,$interMeasPause,$sector 

    # run the experiment 
    if $interactive {
        set w [APSUniqueName .]
        set apsPerformLinacDispersionMeasurementControl 0
        APSExecLog $w -width 100 \
          -unixCommand "sddsexperiment $tmpFile $output -verbose -summarize " \
          -callback {set apsPerformLinacDispersionMeasurementControl done} \
          -abortCallback {set apsPerformLinacDispersionMeasurementControl abort} \
          -cancelCallback {set apsPerformLinacDispersionMeasurementControl cancel}
        tkwait variable apsPerformLinacDispersionMeasurementControl
        switch $apsPerformLinacDispersionMeasurementControl {
            abort -
            cancel {
                file delete -force $output
                catch {exec cavput -list=$ControllawState}
                ##catch {exec cavput -list=$sector:AUTO:PH:modeMO=Disabled -pend=20}
                #return -code error "Experiment aborted."
                catch {destroy $w}
            }
            default {
                destroy $w
            }
        }
    } else {
        if [catch {exec sddsexperiment $tmpFile $output} result] {
            catch {exec cavput -list=$ControllawState}
            ##catch {exec cavput -list=$sector:AUTO:PH:modeMO=Disabled -pend=20}
            return -code error "APSPerformLinacDispersionMeasurement: $result"
        }
    }

    # should probably do something if there is an error here
    catch {exec cavput -list=$ControllawState}
    ##catch {exec cavput -list=$sector:AUTO:PH:modeMO=Disabled -pend=20}
    if {$process && \
          [catch {APSProcessLinacDispersionMeasurement -data $output -output $output.proc \
                    -display $display -sector $sector} result]} {
        return -code error "APSPerformLinacDispersionMeasurement: $result"
    }
}

#Called by APSPerformLinacDispersionMeasurement (Run button)
#Called by ReviewMeasurement (Review button)
proc APSProcessLinacDispersionMeasurement {args} {
    set data ""
    set output ""
    set display 0
    set sector L2
    if {[APSStrictParseArguments {data output display sector}] || \
          ![string length [set data [string trim $data]]] || \
          ![string length [set output [string trim $output]]]} {
        return -code error "APSProcessLinacDispersionMeasurement: parameter error"
    }
    if [string match L\[12345\] $sector]==0 {
        return -code error "APSProcessLinacDispersionMeasurement: sector is invalid: $sector"
    }
    set tmpRoot /tmp/[APSTmpString]
    if [catch \
          {exec \
             sddsconvert $data -pipe=out \
             -retain=column,${sector}PFN*,L\[12345\]:P?:*,*VD1*,Time,F:PM*:*,PB:PM?:*,BB:PM?:* \
             | sddsconvert -pipe \
             -delete=column,*Delay* \
             | sddsbreak -pipe -rowlimit=2 \
             | sddsprocess -pipe \
             -process=${sector}PFN*,first,%sFirst \
             -process=L\[2345\]:P?:*,first,%sFirst \
             -process=PB:PM*:*,first,%sFirst \
             -process=BB:PM*:*,first,%sFirst \
             -process=*VD1*,first,%sFirst \
             -process=${sector}PFN*,last,%sLast \
             -process=L\[2345\]:P?:*,last,%sLast \
             -process=PB:PM*:*,last,%sLast \
             -process=BB:PM*:*,last,%sLast \
             -process=*VD1*,last,%sLast \
             | tee $tmpRoot.2 \
             | sddscollapse -pipe \
             | sddsprocess -pipe \
             "-define=column,%sDelta,%sLast %sFirst -,select=*Last,edit=%/Last//" \
             | sddsprocess -pipe -retain=column,*Delta \
             "-define=column,%sResponse,%sDelta ${sector}PFNVoltageDelta /,units=mm/kV,select=L?:P?:*\[xy\]*Delta,edit=%/Delta//" \
             "-define=column,%sResponse,%sDelta ${sector}PFNVoltageDelta /,units=V/kV,select=L?:P?:*sum*Delta,edit=%/Delta//" \
             | sddsprocess -pipe \
             -process=*Response,ave,%sAve \
             -process=*Response,median,%sMedian \
             -process=*Response,perc,%sQ25,perc=25 \
             -process=*Response,perc,%sQ75,perc=75 \
             | sddscollapse -pipe \
             | sddsprocess -pipe \
             "-define=column,%sErrorBar,%sQ75 %sQ25 - 2 /,units=mm/kV,select=*\[xy\]*ResponseQ75,edit=%/Q75//" \
             "-define=column,%sErrorBar,%sQ75 %sQ25 - 2 /,units=V/kV,select=*sum*ResponseQ75,edit=%/Q75//" \
             | tee $tmpRoot.3  \
             | sddscollect -pipe \
             -collect=suffix=xResponseAve \
             -collect=suffix=yResponseAve  \
             -collect=suffix=sumResponseAve \
             -collect=suffix=xResponseMedian \
             -collect=suffix=yResponseMedian  \
             -collect=suffix=sumResponseMedian \
             -collect=suffix=xResponseQ75 \
             -collect=suffix=yResponseQ75  \
             -collect=suffix=sumResponseQ75 \
             -collect=suffix=xResponseQ25 \
             -collect=suffix=yResponseQ25  \
             -collect=suffix=sumResponseQ25 \
             -collect=suffix=xResponseErrorBar \
             -collect=suffix=yResponseErrorBar  \
             -collect=suffix=sumResponseErrorBar \
             | sddsprocess -pipe -reedit=col,Rootname,ebd \
             -filter=column,sumResponseErrorBar,0,0,! \
             -filter=column,sumResponseErrorBar,0,1 \
             | sddsxref /home/helios/oagData/logging/linacDiag/linacDiag.bpmOrder -pipe \
             -nowarning -match=Rootname=BPMName -take=Index \
             | sddssort -pipe=in $output -column=Index } result] {
        return -code error "APSProcessLinacDispersionMeasurement: $result"
    }
    
    if $display {
        exec sddsplot $output -graph=sym,scale=2,vary \
          -column=Rootname,xResponseAve -legend=spec=average "-ylabel=edit=%/ResponseAve//" \
          -column=Rootname,xResponseMedian -legend=spec=median \
          -column=Rootname,xResponseMedian,xResponseErrorBar -graph=error -end \
          -column=Rootname,yResponseAve -legend=spec=average "-ylabel=edit=%/ResponseAve//" \
          -column=Rootname,yResponseMedian -legend=spec=median \
          -column=Rootname,yResponseMedian,yResponseErrorBar -graph=error -end \
          -column=Rootname,sumResponseAve -legend=spec=average "-ylabel=edit=%/ResponseAve//" \
          -column=Rootname,sumResponseMedian -legend=spec=median \
          -column=Rootname,sumResponseMedian,sumResponseErrorBar -graph=error -end \
          &
    }
}

#Review button
proc ReviewMeasurement {args} {
    set directory ""
    if {[APSStrictParseArguments {directory}]} {
        return -code error "ReviewMeasurement: parameter error"
    }
    if {![string length $directory] || ![file exists $directory] || \
          ![file isdirectory $directory]} {
        set directory [pwd]
    }
    set dataFile [APSFileSelectDialog [APSUniqueName .] \
                    -title "Select Data File" \
                    -width 40 -path $directory -pattern *.sdds \
                    -contextHelp "Select a data file to be processed and displayed"]
    if ![string length $dataFile] return
    if [catch {APSGetSDDSNames -fileName $dataFile -class column} nameList] {
        return -code error "ReviewMeasurement: $nameList"
    }
    if [set index [lsearch -glob $nameList L?PFNVoltage]]==-1 {
        return -code "ReviewMeasurement: this doesn't appear to be a dispersion measurement file."
    }
    set sector [os editstring %/PFNVoltage// [lindex $nameList $index]]
    APSProcessLinacDispersionMeasurement -data $dataFile \
      -output $dataFile.proc -display 1 -sector $sector
}

#Correct button
proc CorrectDispersion {args} {
    set directory ""
    set gain 0.5
    set beamEnergy 150
    set statusCallback APSNoOp
    if {[APSStrictParseArguments {directory beamEnergy gain statusCallback}]} {
        return -code error "CorrectDispersion: parameter error"
    }
    if ![string length [set rootname [ChooseMeasurement -directory $directory]]] {
        return -code error "No file chosen"
    }
    if ![file exists $rootname.proc] {
        return -code error "Not found: $rootname.proc"
    }

    if [llength [set refLatticeList [ChooseRefLattice]]]!=2 {
        return
    }
    set latticeParentDir [file dirname [lindex $refLatticeList end]]

    setStatus "Correcting dispersion based on measurement $rootname"
    
    set bpmList [APSGetSDDSColumn -column Rootname -fileName $rootname.proc]
    set etaList  [APSGetSDDSColumn -column xResponseAve -fileName $rootname.proc]

    set referenceEta 0
    foreach bpm $bpmList eta $etaList {
        if [string compare $bpm L3:P1]==0 {
            set referenceEta $eta
            break
        }
    }
    if $referenceEta==0 {
        setStatus "Reference eta not found!"
        return
    }
    foreach bpm $bpmList eta $etaList {
        if [string compare $bpm L3:P1]==0 {
            continue
        }
        set ratio [expr $eta/$referenceEta]
        lappend etaValueList ${bpm}Ratio=$ratio
    }

    set tmpRoot /tmp/[APSTmpString]
    global apsCorrectDispersionElegantDone
    set apsCorrectDispersionElegantDone 0
    APSExecLog [APSUniqueName .] \
      -unixCommand "elegant $latticeParentDir/matcheta.ele \
-macro=latticeDir=$latticeParentDir,startingFile1=[lindex $refLatticeList 0]/transverse.param \
-macro=startingFile2=[lindex $refLatticeList 1]/transverse.param \
-macro=twissReference=[lindex $refLatticeList 0]/transverse.twi,rootname=[file root $rootname] \
-macro=[join $etaValueList ,]" \
      -callback "set apsCorrectDispersionElegantDone 1" \
      -abortCallback "set apsCorrectDispersionElegantDone 2" \
      -cancelCallback "set apsCorrectDispersionElegantDone 2" \
      -width 100 -height 20
    tkwait variable apsCorrectDispersionElegantDone
    if $apsCorrectDispersionElegantDone!=1 {
        return 
    }

    exec sddsplot -parameter=Step,optimizationFunction \
      [file root $rootname].finOpt -graph=line,type=1 -mode=y=log,y=spec &
    exec plotTwiss -fileRoot [file root $rootname] &

    if [catch {exec sddsprocess [lindex $refLatticeList 0]/transverse.param -pipe=out \
                 -match=col,ElementName=L3:QM\[12\] -match=col,ElementParameter=K1 \
                 | sddssort -pipe -column=ElementName \
                 | sdds2stream -pipe -column=ParameterValue} nominalK1List] {
        return -code error "CorrectDispersion: $nominalK1List"
    }
    if [catch {exec sddsprocess [file root $rootname].param -pipe=out \
                 -match=col,ElementName=L3:QM\[12\] -match=col,ElementParameter=K1 \
                 | sddssort -pipe -column=ElementName \
                 | sdds2stream -pipe -column=ParameterValue} newK1List] {
        return -code error "CorrectDispersion: $newK1List"
    }
    # setStatus "nominal K1: $nominalK1List"
    # setStatus "new K1    : $newK1List"

    # calibration of quad in 1/m^2/A 
    set calFactor [expr $beamEnergy/299.79*7.730000e-02/0.0420458]
    set deltaQM1 [expr ([lindex $nominalK1List 0]-[lindex $newK1List 0])*$calFactor*$gain]
    set deltaQM2 [expr ([lindex $nominalK1List 1]-[lindex $newK1List 1])*$calFactor*$gain]
    set choice [ APSQueryToProceed -message \
                   "Predicted required delta current is $deltaQM1 for L3:QM1 and $deltaQM2 for L3:QM2.  Do you want to install/condition to this?" ]
    if {$choice} {
        if [catch {exec cavget -list=L3:QM -list=1,2 -list=:CurrentAO} startList] {
            return -code error "CorrectDispersion: $startList"
        }
        if [catch {exec cavput -list=L3:QM \
                     -list=1=[expr [lindex $startList 0]+$deltaQM1],2=[expr [lindex $startList 1]+$deltaQM2] \
                     -list=:StandardizeSUB.G
            after 1000
            exec cavput -list=L3:QM -list=1,2 -list=:StandardizeSUB.F=0
            after 1000
            exec cavput -list=L3:QM -list=1,2 -list=:StandardizeSUB.PROC=1} result] {
            return -code error "$result"
        }
        eval $statusCallback "{Conditioning L3:QM1 and L3:QM2}"
    }
}

#Called by CorrectDispersion (Correct button)
proc ChooseMeasurement {args} {
    set directory ""
    if {[APSStrictParseArguments {directory}]} {
        return -code error "ReviewMeasurement: parameter error"
    }
    if {![string length $directory] || ![file exists $directory] || \
          ![file isdirectory $directory]} {
        set directory [pwd]
    }
    set dataFile [APSFileSelectDialog [APSUniqueName .] \
                    -title "Select Data File" \
                    -width 40 -path $directory -pattern *.sdds \
                    -contextHelp "Select a data file to be processed and displayed"]
    if ![string length $dataFile] return
    return $dataFile
}

#Called by CorrectDispersion (Correct button)
proc ChooseRefLattice {} {
    global OAGGlobal
    set latticeList [lsort [glob -nocomplain \
                              $OAGGlobal(LinacLatticesDirectory)/RG2_L3FS5/2001-1219/*-dZ*]]
    set chicaneList [eval os editstring 100Z/ $latticeList]
    if ![llength $latticeList] {
        return -code error "No lattice files found!"
    }
    set latticeDir1 [APSChooseItemFromList \
                       -name "Choose first reference lattice" \
                       -itemList $chicaneList \
                       -returnList $latticeList -returnIndices 0]


if {0} {
    set latticeList [lsort [glob -nocomplain \
                              $OAGGlobal(LinacLatticesDirectory)/L1O_L3FS5/2001-1219/*-dZ*]]
    set chicaneList [eval os editstring 100Z/ $latticeList]
    if ![llength $latticeList] {
        return -code error "No lattice files found!"
    }
    set latticeDir1 [APSChooseItemFromList \
                       -name "Choose first reference lattice" \
                       -itemList $chicaneList \
                       -returnList $latticeList -returnIndices 0]
}

    set latticeList [lsort [glob -nocomplain \
                              $OAGGlobal(LinacLatticesDirectory)/L3FS5_PBC1/2001-1219/*MeV*]]
    set chicaneList [eval os editstring 100Z/ $latticeList]
    if ![llength $latticeList] {
        return -code error "No lattice files found!"
    }
    set latticeDir2 [APSChooseItemFromList \
                       -name "Choose second reference lattice" \
                       -itemList $chicaneList \
                       -returnList $latticeList -returnIndices 0]

    return [list $latticeDir1 $latticeDir2]
    
}

BuildApplication

