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

#

# the interface is copied from quickExperiment, SHANG

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.0 $ \$Author: shang $"

APSApplication . -name ptbResponseMeasurement -version $CVSRevisionAuthor \
  -overview {This is the ptResponseMeasurement utility.  It provides a simple interface to the sddsexperiment program, which allows varying and measuring EPICS data, and logging the results and some statistical analysis to an SDDS file.  The data can be plotted after it is collected. You can also manipulate and display the data offline using the SDDS toolkit programs.}

set mainStatus ""
proc SetMainStatus {text} {
    global mainStatus
    set mainStatus "[exec date] $text"
    update 
}

proc ProcessBPMData {args} {
    global responseFile coeff measFile invResponseFile plane outputDir minimum comment
    set respDir [file dirname $responseFile]
    set rawDir $respDir/rawData
   
   
    SetMainStatus "process $plane response data..."
    foreach pl {X Y} {
        switch $plane {
            X {
                if {$pl=="X"} {
                    set ext hrm
                } else {
                    set ext hvrm
                }
            }
            Y {
                if {$pl=="X"} {
                    set ext vhrm
                } else {
                    set ext vrm
                }
            }
        }
                    
        set files [glob -nocomplain $outputDir/rawData/${plane}.resp_${pl}_*.resp]
        if ![llength $files] {
            continue
        }
        set readbacks [exec sddsprocess $measFile -pipe=out \
                         "-match=col,ControlName=*${pl}position,ControlName=*[string tolower $pl],|" \
                         | sdds2stream -pipe=in -col=SymbolicName]
        SetMainStatus "process $plane bpm data for $plane plane..."
        set fileList ""
        set optList ""
        foreach file $files {
            set corr [lindex [split [file root $file] _] 2]
            if [catch {exec sddsslopes $file [file root $file].slope \
                         -independentVariable=$corr -col=[join $readbacks ,] } result] {
                return -code error "Error getting slopes1: $result"
            }      
            if ![info exist coeff($corr)] {
                set coeff($corr) 1.0
            }
            if [catch {exec sddsconvert [file root $file].slope -delete=col,*Intercept -pipe=out \
                         | sddstranspose -pipe \
                         | sddsconvert -pipe -rename=col,OldColumnNames=BPMNames \
                         | sddsprocess -pipe=in -reedit=col,BPMNames,%/Slope//%/X//%/Y// \
                         "-redefine=col,$corr,$corr $coeff($corr) /,units=m/rad" \
                         [file root $file].bpm } result] {
                return -code error "Error processing bpm data: $result"
            }
            lappend optList -col=BPMNames,$corr [file root $file].bpm
            lappend fileList [file root $file].bpm
        }
        set len [llength $fileList]
        set n [expr int(($len+2)/3)]
        #puts $fileList
        set respFile $outputDir/
        eval exec sddsplot -split=page -sep  -layout=3,$n -gra=sym,conn,scale=2  $optList &
        if [catch {eval exec sddsxref $fileList $outputDir/$plane.$ext -pipe=out -match=BPMNames -leave=BPMNames -nowarnings} result] {
            return -code error "Error combining response file: $result"
        }
        if [catch {exec sddspseudoinverse $outputDir/$plane.$ext -pipe=out \
                     -minimumSingularValueRatio=$minimum -oldColumnNames=Actuators \
                     | sddsprocess -pipe=in $outputDir/$plane.$ext.inv "-print=par,comment,$comment" } result] {
            SetMainStatus "Error in processing data: $result; Inverse response matrix is not created"
            return
        }
        set condition [exec sdds2stream $outputDir/$plane.$ext.inv -par=ConditionNumber -page=1]
        SetMainStatus "Inverse Response matrix $outputDir/$plane.$ext.inv created, condition number is $condition." 
    }
    SetMainStatus "done."
}


proc ClearCorrs {args} {
    global plane
    global Hcorrectors Hcorrector Vcorrectors Vcorrector
    set corrs [set ${plane}correctors]
    for {set i 0} {$i<$corrs} {incr i} {
        set ${plane}corrector($i.checked) 0
    }
}

proc LoadCorrector {args} {
    set plane ""
    APSParseArguments {plane}
    
    global Xcorrectors Ycorrectors Xcorrector Ycorrector
    set varFile /home/helios/oagData/ptb/responseMeasurement/PTB2B1C2-${plane}.vars
    set names  [exec sdds2stream -col=ControlName $varFile ]
    set symbols [exec sdds2stream -col=SymbolicName $varFile]
    set initialValues [exec sdds2stream -col=InitialValue $varFile]
    set finalValues [exec sdds2stream -col=FinalValue $varFile]
    set relativeValues [exec sdds2stream -col=Relative $varFile]
    set rows [llength $names]
    set ${plane}correctors $rows
    for {set i 0} {$i<$rows} {incr i} {
        set ${plane}corrector($i.PV) [lindex $names $i]
        set ${plane}corrector($i.symbol) [lindex $symbols $i]
        set ${plane}corrector($i.init) [lindex $initialValues $i]
        set ${plane}corrector($i.final) [lindex $finalValues $i]
        set ${plane}corrector($i.relative) [lindex $relativeValues $i]
        set ${plane}corrector($i.checked) 1
    }
    update
}

set Xorrectors 0
set Ycorrectors 0
proc CreateCorrectorWidget {args} {
    set parent ""
    set plane ""
    APSParseArguments {parent plane}

    global XCorrecgtor YCorrector Xcorrectors Ycorrectors
    set varScroll [APSScroll .varScroll -parent $parent]
    set varScrollFrame $parent.varScroll
    $varScroll configure -relief flat -bd 0
    #load input
    LoadCorrector -plane $plane
    set parent $varScroll
    set rows [set ${plane}correctors]
    for {set i 0} {$i<$rows} {incr i} {
        APSFrame .data$i  -parent $parent -height 1
        set w $parent.data$i.frame
        $w configure -relief flat -bd 0
        APSLabeledEntry .x -parent $w -label "PV:" -textVariable \
          ${plane}corrector($i.PV) -width 25 \
          -contextHelp "Enter the name of a variable process variable (PV) in this field." \
          -packOption "-side left"
        APSLabeledEntry .y -parent $w -label "Symbolic Name:" -textVariable \
          ${plane}corrector($i.symbol) -width 20 \
          -contextHelp "Enter the name of a variable process variable (PV) in this field." \
          -packOption "-side left"
        APSLabeledEntry .init -parent $w -label "Initial:" -textVariable \
          ${plane}corrector($i.init) -width 6 \
          -contextHelp "Enter the initial value for the variable process variable (PV) in this field." \
          -packOption "-side left"
        APSLabeledEntry .final -parent $w -label "Final:" -textVariable \
          ${plane}corrector($i.final) -width 6 \
          -contextHelp "Enter the initial value for the variable process variable (PV) in this field." \
          -packOption "-side left"
        APSCheckButtonFrame .rel -parent $w -label {} -buttonList {Relative} \
          -variableList ${plane}corrector($i.relative) -orientation horizontal \
          -contextHelp "If checked, then the variation range is defined relative to the PV's original value." \
          -packOption "-side left"
        APSCheckButtonFrame .scan -parent $w -label {} -buttonList {Scan} \
          -variableList ${plane}corrector($i.checked) -orientation horizontal \
          -contextHelp "If checked, then the variation range is defined relative to the PV's original value." \
          -packOption "-side left"
        tkwait visibility $w.scan
        APSScrollAdjust $varScrollFrame -numVisible 10
    }
}

set outputDir [APSGoToDailyDirectory -subdirectory ptbResponse]
set measFile /home/helios/oagData/ptb/responseMeasurement/PTB2B1C2.meas
set expFile /home/helios/oagData/ptb/responseMeasurement/ptbResponseMeasTemplate.exp
proc ChangePlane {args} {
    set plane0 ""
    APSParseArguments {plane0}
    global plane responseFile outputDir
    if [string length $plane0] {
        set plane $plane0
    }
    set responseFile $outputDir/${plane}.resp

}
proc SelectAll {args} {
    set value 1
    APSParseArguments {value}
    global plane Xcorrectors Xcorrector Ycorrectors Ycorrector
    set corrs [set ${plane}correctors]
    for {set i 0} {$i<$corrs} {incr i} {
        set ${plane}corrector($i.checked) $value
    }
}

set plane X

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

APSFrame .variable -parent .userFrame  -label Actuators -packOption \
  "-side top -fill x" -contextHelp "Enter data for variable PVs in this frame."


set Steps 3

APSFrameGrid .grid -parent .userFrame.variable.frame -xList {x1 x2 x3 x4}
APSLabeledEntry .steps -parent .userFrame.variable.frame.grid.x1 -label \
  "Steps:  " -textVariable Steps -width 10 -contextHelp "Enter the number of steps (or samples) in this field." -packOption "-side left"
  

set w1 .userFrame.variable.frame
$w1 configure -relief flat -bd 0
set wList [APSTabFrame .planes -parent $w1 -label "" \
             -labelList "Horizontal Vertical" -width 950 -height 200 -commandList {"ChangePlane -plane0 X" "ChangePlane -plane0 Y"}]
set wH [lindex $wList 0]
set wV [lindex $wList 1]
CreateCorrectorWidget -parent $wH -plane X
$w1.planes.frame.tn select 1
CreateCorrectorWidget -parent $wV -plane Y
$w1.planes.frame.tn select 0
APSButton .all -parent $w1 -text "Select All" -command {SelectAll}
APSButton .none -parent $w1 -text "Select None" -command {SelectAll -value 0}


APSFrame .execution -parent .userFrame -contextHelp "Enter execution parameters here"

set interval 1
set PostChangePause 5
set rampSteps 1

set definitionFile ""
set responseFile ""
set invResponseFile ""
set comment ""
set numToAverage 10
set doStd 1
set doSigma 0
set minimum 0
set saveRawData 1

APSFrameGrid .grid -parent .userFrame.execution.frame -xList {x1 x2}
set w .userFrame.execution.frame.grid.x1
APSLabeledEntry .average -parent $w -label "No. to average:" -width 10 -textVariable numToAverage \
  -contextHelp "The number of averages of the readback values."
APSLabeledEntry .interval -parent $w -label "Interval (s): " \
  -textVariable interval -width 10 \
  -contextHelp "Enter the time in seconds between measurements."
APSLabeledEntry .minimum -parent $w -label "Minimum singular value ratio:" -width 10 \
  -textVariable minimum \
  -contextHelp "minimumSingularValueRatio used in sddspseudoinverse:\nReject singular values less than the\nlargest singular value times this ratio."
APSLabeledEntry .ramp -parent $w -label "Ramp steps:" -width 10 \
  -textVariable rampSteps

set w .userFrame.execution.frame.grid.x2
APSLabeledEntry .postChangePause -parent $w -label "Post change pause (s): " \
  -textVariable PostChangePause -width 10 \
  -contextHelp "Enter the time in seconds to wait after a variable change and before measurements are taken."

APSCheckButtonFrame .std -parent $w -buttonList {stddev sigma} -label {} \
      -variableList "doStd doSigma" -orientation horizontal -allNone 0 \
      -contextHelp "Press these buttons to enable standard deviation or sigma calculation by sddsexperiment."
APSRadioButtonFrame .saverawdata -parent $w -label "Save raw data:" -buttonList {yes no} \
  -valueList {1 0}  -variable saveRawData  -packOption "-side left" -orientation horizontal \
  -contextHelp "if save raw data is chosen, then raw data will save under sub-directory rawData."

APSLabeledEntry .execution.frame.dir -parent .userFrame -label "Output dir:" \
  -textVariable outputDir -width 80
bind .userFrame.execution.frame.dir <Leave> "ChangePlane"
APSButton .daily -parent .userFrame.execution.frame.dir -size small -text "daily" \
  -command "set outputDir [APSGoToDailyDirectory -subdirectory ptbResponse]" -packOption "-side right"
APSLabeledEntry .execution.frame.output -parent .userFrame -label "Response file: " \
  -textVariable responseFile -width 80 \
  -contextHelp "Enter a name for the output file to hold the response matrix."
bind .userFrame.execution.frame.output.entry <Leave> SetInvResponseFile
#APSLabeledEntry .execution.frame.inv -parent .userFrame -label "Inverse file: " \
#  -textVariable invResponseFile -width 80 \
 # -contextHelp "Enter a name for inverse response matrix file"

#APSButton .daily -parent .userFrame.execution.frame.output -size small -text "daily" \
#  -command "set responseFile [APSGoToDailyDirectory]" -packOption "-side right"
APSLabeledEntry .execution.frame.comment -parent .userFrame -label "Comment: " \
  -textVariable comment -width 80 \
  -contextHelp "Enter the comment what you want to be saved as a parameter in the output file."

APSFrame .ops -parent .userFrame \
  -contextHelp "Actions are started in this frame."
APSButton .ops.frame.run -parent .userFrame -text RUN -command "RunExperiment" \
  -contextHelp "Launches a sddsexperiment subprocess to collect the data."


APSButton .ops.frame.process1 -parent .userFrame -text ProcessData -command ProcessBPMData \
  -contextHelp "process the bpm reponse data."



APSButton .ops.frame.adt1 -parent .userFrame -text "Booster Single Turn BPM ADT" -command "exec  adt -geometry +30+0 -f /home/helios/oagData/ADTFiles/booster/B4C8-B1C3-singleTurn.bpm.pv &"

proc RecalculateInv {args} {
    global responseFile invResponseFile
    if ![file exist $responseFile] {
        return -code error "The response file does not exist!"
    }
    SetMainStatus "InvRespMatrix does not work now, checking it..."
  #  exec InvRespMatrix -inputFile $responseFile -outputFile $invResponseFile &
    if [catch {exec sddspseudoinverse $responseFile -pipe=out \
                 -minimumSingularValueRatio=$minimum -oldColumnNames=Actuators \
                 | sddsprocess -pipe=in $invResponseFile "-print=par,comment,$comment" } result] {
        SetMainStatus "Error in processing data: $result; Inverse response matrix is not created"
        return
    }
    set condition [exec sdds2stream $invResponseFile -par=ConditionNumber -page=1]
    SetMainStatus "Inverse Response matrix $invResponseFile created, condition number is $condition." 
}

proc RunExperiment {} {
    global doStd doSigma Average comment plane
    global responseFile inputFile experimentDone minimum invResponseFile
    global PostChangePause interval Steps  measBTS measSRH measSRV
    global numToAverage doStd doSigma VarSymName saveRawData rampSteps VarPVname SRInjMode
    global measFile expFile
    global Xcorrectors Xcorrector Ycorrectors Ycorrector coeff 
    #search for controlname
    
    if [catch {SaveDefinitionFile -plane $plane} result] {
        SetMainStatus "$result"
        return
    }
        
    if {[string length $responseFile]==0 || [file isdirector $responseFile]} {
        SetMainStatus "Supply the response filename."
        bell
        return
    }
    
    if ![string match "*.resp" $responseFile] {
        set responseFile ${responseFile}.resp
    }
    if {[file exists $responseFile]} {
        set ok [APSYesNoPopUp "$responseFile already exists, overwrite it?"]
        if {!$ok} {
            SetMainStatus "Supply a new response matrix filename"
            return
        } else {
            catch {exec rm $responseFile}
        }
    }
    if {[file exists $invResponseFile]} {
        set ok [APSYesNoPopUp "$invResponseFile already exist, overwrite it?"]
        if {!$ok} {
            SetMainStatus "Supply a new inverse response filename"
            return
        } else {
            catch {exec rm $invResponseFile}
        }
    }
    
    #save measurements into a file
    if $saveRawData {
        set outDir [file dirname $responseFile]/rawData
        set rootname [file tail $responseFile]
        if ![file exist $outDir] {
            if [catch {exec mkdir -p $outDir} result] {
                SetMainStatus "Can not make directory $outDir: $result."
                return
            }
        }
        set tmpRoot0 ${outDir}/${rootname}
    } else {
        set tmpRoot0 /tmp/[APSTmpString]
    }
    
    SetMainStatus "starting experiment..."
    #for each plane, need do measurement at different conditions with the same correctors
    #1, meas bts bpms
    #2, meas H plane SR bpms
    #3, meas V plane SR bpms
    
    set respFileList ""
    set corrs [set ${plane}correctors]
    #set readbacks [exec sdds2stream $measFile -col=SymbolicName]
    set bpmFileList ""
    set optList ""
    #booster single turn bpm
    SetMainStatus "working on $plane correctors..."
    foreach bPlane {X Y} bl {h v} {
        set bpmFileList ""
        set optList ""
        SetMainStatus "setup $bPlane for booster single turn bpm..."
        set RAMfile /home/helios/oagData/SCR/snapshots/BBPMWaveform/BBPMWaveform-singleTurn${bPlane}.gz
        SetMainStatus "loading RAM $RAMfile ..."
        set filename [file readlink $RAMfile]
        set dir /home/helios/oagData/SCR/snapshots/BBPMWaveform
        if [catch {exec sddscasr $filename -restore -pendIOTime=60 \
                     -waveform=directory=$dir,onefile,rootname=[file rootname [file tail $filename]],extension=.waveform.gz } result] {
            SetMainStatus "Error loading ram file: $result"
            return
        }
        set tmpRoot ${tmpRoot0}_${bPlane}_
        set respFileList ""
        set bpmFileList ""
        set optList ""
        SetMainStatus "Starting exeperiment..."
        set readbacks [exec sddsprocess $measFile -pipe=out \
                          "-match=col,ControlName=*${bPlane}position,ControlName=*[string tolower $bPlane],|" \
                         | sdds2stream -pipe=in -col=SymbolicName]
        if ![APSYesNoPopUp "starting scan $bPlane booster bpms now?"] {
                SetMainStatus "experiment cancelled."
                return
        }
        for {set i 0} {$i<$corrs} {incr i} {
            if ![set ${plane}corrector($i.checked)] {
                continue
            }
            set PV [set ${plane}corrector($i.PV)]
            set symbol [set ${plane}corrector($i.symbol)]
            set relative  [set ${plane}corrector($i.relative)]
            set init [set ${plane}corrector($i.init)]
            set final [set ${plane}corrector($i.final)]
            if ![string length $PV] {
            #in case the pv name is not provided
                continue
            }
            SetMainStatus "Scan $PV ..."
            set experimentDone 0
            if !$saveRawData {
                APSAddToTmpFileList -ID response -fileList ${tmpRoot}_$symbol.resp
            } else {
                set file ${tmpRoot}_$symbol.resp
                if [file exist $file] {
                    if ![APSYesNoPopUp "$file already exist, overwrite it?"] {
                        SetMainStatus "$file already exist."
                        return
                    } else {
                        file delete -force $file
                    }
                }
            }
            lappend processList ${tmpRoot}_$symbol.resp
            
            set macro -macro=monitorFile=$measFile,numToAve=$numToAverage,stddev=$doStd,sigma=$doSigma,actuatorName=$PV,columnName=$symbol,nPoints=$Steps,initialValue=$init,finalValue=$final,relative=$relative,postChangePause=$PostChangePause,interval=$interval,rampSteps=$rampSteps
            APSExecLog .runExperiment -width 80 -unixCommand \
              "sddsexperiment $expFile ${tmpRoot}$symbol.resp $macro \"-comment=[APSMakeSafeQualifierStringForEval $comment]\" -verbose" \
              -cancelCallback "set experimentDone cancelled" \
              -abortCallback "set experimentDone aborted" \
              -callback "set experimentDone done"
            
            if !$experimentDone {
                tkwait variable experimentDone
            }
            if [catch {exec  sddsslopes ${tmpRoot}$symbol.resp -pipe=out  \
                         -independentVariable=$symbol -col=[join $readbacks ,] \
                         | tee ${tmpRoot}$symbol.slope \
                         | sddsconvert -pipe -del=col,*Intercept \
                         | sddstranspose -pipe \
                         | sddsconvert -pipe -rename=col,OldColumnNames=BPMNames \
                         | sddsprocess -pipe=in -reedit=col,BPMNames,%/Slope//%/X//%/Y// \
                         "-redefine=col,$symbol,$symbol $coeff($symbol) /,units=m/rad" \
                         ${tmpRoot}$symbol.bpm  \
                     } result] {
                return -code error "Error1: $result"
            }
            set col ""
            set color 0
            foreach meas $readbacks {
                append col " -col=$symbol,$meas -graph=sym,scale=2,connected,type=$color,sub=$color"
                incr color
            }
            eval exec sddsplot -mode=y=offset ${tmpRoot}$symbol.resp -legend $col 

            lappend respFileList ${tmpRoot}$symbol.slope 
            lappend bpmFileList ${tmpRoot}$symbol.bpm
            lappend optList -col=BPMNames,$symbol  ${tmpRoot}$symbol.bpm
        }
        set len [llength $bpmFileList]
        set n [expr int(($len+2)/3)]
        eval exec  sddsplot -split=page -sep -layout=3,$n -gra=sym,connect,scale=2  $optList &
        switch $plane {
            X {
                if {$bl=="h"} {
                    set root hrm
                } else {
                    set root hvrm
                }
            }
            Y {
                if {$bl=="h"} {
                    set root vhrm
                } else {
                    set root vrm
                }
            }
        }
        set rootname [file root $responseFile]
        if [llength $bpmFileList]>1 {
            if [catch {eval exec sddsxref $bpmFileList -leave=BPMNames $rootname.$root } result] {
                return -code error "Error combining response file: $result"
            }
        } else {
            exec cp $bpmFileLisst $rootname.$root
        }
        SetMainStatus "done for $plane plane."
        if [catch {exec sddspseudoinverse $rootname.$root -pipe=out \
                     -minimumSingularValueRatio=$minimum -oldColumnNames=Actuators \
                     | sddsprocess -pipe=in $rootname.$root.inv "-print=par,comment,$comment" } result] {
            SetMainStatus "Error in processing data: $result; Inverse response matrix is not created"
            return
        }
        set condition [exec sdds2stream $rootname.$root.inv -par=ConditionNumber -page=1]
        SetMainStatus "Inverse Response matrix $rootname.$root.inv created, condition number is $condition." 
    }
    SetMainStatus "done for $plane plane." 
}


proc SearchPVName {args} {
    set pvName ""
    APSParseArguments {pvName}
    if ![string length $pvName] {
        return
    }
    if [catch {exec cavget -list=$pvName -pend=10} result] {
       return -code error "$pvName not found: $result"
    }
    if {$result=="?"} {
        return -code error "$pvName not found"
    }
}

proc SetInvResponseFile {} {
    global responseFile invResponseFile definitionFile
    if [regexp {(.*)\.} $responseFile a name] {
        set invResponseFile $name.inv
        set definitionFile $name.def
    } else {
        set invResponseFile $responseFile.inv
        set definitionFile $responseFile.def
    }
}

proc SaveDefinitionFile {args} {
    set plane ""
    APSParseArguments {plane}
    
    global outputDir Xcorrectors Xcorrector Ycorrectors Ycorrector measFile
    set definitionFile $outputDir/$plane.def
    
    set controlNameList [join [exec sdds2stream -col=ControlName $measFile]]
    set symbolNameList [join [exec sdds2stream -col=SymbolicName $measFile]]
    
   
    for {set i 0} {$i<[set ${plane}correctors]} {incr i} {
        lappend controlNameList [string trim [set ${plane}corrector($i.PV)]]
        lappend symbolNameList  [string trim [set ${plane}corrector($i.symbol)]]
    }
    if [catch {exec sddsmakedataset -defaultType=string -col=ControlName \
                 -data=[join $controlNameList ,] \
                 -col=SymbolicName -data=[join $symbolNameList ,] \
                 $definitionFile } result] {
        return -code error "makedataset(1): $result"
    }
    SetMainStatus "Definitions are save to $definitionFile!"
}

proc SetInvResponseFile {} {
    global responseFile invResponseFile definitionFile
    if [regexp {(.*)\.} $responseFile a name] {
        set invResponseFile $name.inv
        set definitionFile $name.def
    } else {
        set invResponseFile $responseFile.inv
        set definitionFile $responseFile.def
    }
}

set kickFile /home/helios/oagData/ptb/responseMeasurement/correctorKickScaling.kick
set names [exec sdds2stream -col=SymbolicName $kickFile]
set mradPerAmps [exec sdds2stream -col=mradPerAmp $kickFile]
foreach name $names val $mradPerAmps {
    set coeff($name) $val
}
