#!/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.15 $ \$Author: emery $"

APSApplication . -name BTSDispMeasurement -version "\$Revision: 1.15 $ \$Author: emery $" \
  -overview {BSTDispersionMeasurement: BTS bpm vs. turns -- slop=dx/dn}

set ControlStatus "Ready."
set args $argv


proc SetMainStatus {text} {
    global ControlStatus
    set ControlStatus $text
    update
}

proc MakeStatusWidget {args} {
    global ControlStatus
    set parent ""
    APSParseArguments {parent}
    APSScrolledStatus .status -parent $parent -textVariable\
      ControlStatus -width 50 -packOption "-side top -fill x"  -withButtons 1
    return 1
}
proc MakeOptionFrame {widget args} {
    global TurnsRange numToAve numSteps outputDir DataIndex referenceDir
    global postChangePause Comment Ef Ei deltaT twissFile
    set parent ""
    APSParseArguments {parent}

    APSFrame $widget -parent $parent -label "options" 
    set w0 $parent$widget.frame
    APSFrameGrid .fg -parent $w0 -xList {x1 x2}
    set w1 $w0.fg.x1
    APSLabeledEntry .step -parent $w1 -label \
      "steps: " -textVariable numSteps -width 10 \
      -contextHelp "The number of steps for running sddsexperiment"
    APSLabeledEntry .post -parent $w1 -label \
      "pause after change (s):" -textVariable postChangePause -width 10 \
      -contextHelp "the waiting time between setting the new turns and taking new measurements"
    APSLabeledEntry .ave -parent $w1 -label "num to average:" \
      -textVariable numToAve -width 10 \
      -contextHelp "The number of average for taking measurement"
    APSLabeledEntry .time -parent $w1 -label "Time between inj and ext (s):" \
      -textVariable deltaT -width 10 \
      -contextHelp "time between booster injection and extraction"
    set w2 $w0.fg.x2
    APSLabeledEntry .inj -parent $w2 -label \
      "Injection Energy (MeV):" -textVariable Ei -width 10 \
      -contextHelp "The injection energy of booster"
    APSLabeledEntry .ext -parent $w2 -label \
      "Extraction Energy (Mev):" -textVariable Ef -width 10 \
      -contextHelp "The booster extractiion Energy"
    APSLabeledEntry .hirange -parent $w2 -label \
      "Upper turns relative limit:    " -textVariable hiTurns -width 10 \
      -contextHelp "The upper relative range of booster turn pv -- Mt:BoosterRampTurnsAO.VAL"
    APSLabeledEntry .lorange -parent $w2 -label \
      "Lower turns relative limit:    " -textVariable loTurns -width 10 \
      -contextHelp "The lower relative range of booster turn pv -- Mt:BoosterRampTurnsAO.VAL"
    APSLabeledEntry .index -parent $w2 -label \
      "Data Index:    " -textVariable DataIndex -width 10 \
      -contextHelp "the index of output data for plotting (the directory is given by the output directory, the file name would be $outputDir/BTS{4-digit Index}"
    APSLabeledEntry .desc -parent $w0 -label "Comment: " -textVariable Comment \
      -width 80 -contextHelp "Please enter the comment for taking experiment."
    APSLabeledEntry .outdir -parent $w0 -label "Output Directory: " -textVariable outputDir \
      -width 80 -contextHelp "output directory."
    APSButton .select -parent $w0.outdir -text "P" -size small -packOption "-side left" \
      -command "SelectOutputDir" \
      -contextHelp "Press to select the old data directory."

    global apsFileSelect inputDir
    set apsFileSelect(path) $referenceDir
    set apsFileSelect(done) 1

    APSLabeledEntry .twiss -label "Refer. Twiss File:" -textVariable twissFile \
      -contextHelp "input the twiss file for comparing with the measured dispersion" -width 78 -parent $w0 -commandButton 1 -fileSelectPattern *.twi
    APSButton .select -parent $w0.twiss -text "P"  -size small -packOption "-side left" \
      -command "SelectTwissFile" \
      -contextHelp "Press to select a twiss file."
}

proc SelectTwissFile {args} {
    global twissFile referenceDir
    set twissFile [APSFileSelectDialog .chooseInputFile -listDir $referenceDir -pattern *.twi]
}

proc SelectOutputDir {args} {
    global outputDir dirSelection
    set dir /home/helios/oagData/bts/dispersionMeasurement/data
    set oldDir [pwd]
    cd $dir
    set dirList [lsort -decreasing [glob -nocomplain *]]
    APSScrolledListWindow .dirselect \
      -name "Directory selection" \
      -label "Select a directory" \
      -itemList $dirList \
      -selectionVar dirSelection
    tkwait variable dirSelection
    set outputDir $dir/$dirSelection
    cd $oldDir

}
proc GetBTSBPMListFromFile {args} {
    global BTSMatrix 
    
    set beamline BTS
    set BTSMatrix(Actuators) "Mt:BoosterRampTurnsAO"
    if [catch {sdds load /home/helios/oagData/bts/measurements/response/measurement/bts.bpm.mon bFile} result] {
        return -code error $result
    }
    
    foreach PV [lindex $bFile(Column.ControlName) 0] units [lindex $bFile(Column.Units) 0] {
        if [regexp {(BTS.*):PositionM} $PV match bpm] {
            append BTSMatrix(bpmPVList)   " $PV"
            append BTSMatrix(bpmList)     " $bpm"
            append BTSMatrix(bpmUnitsList) " $units"
        }
    }
}

proc BTSBPMCheckButtons {widget args} {
    set parent ""
    APSStrictParseArguments {parent}
    global BTSMatrix
    pack [frame $parent$widget]
    set w $parent$widget
    set index 0
    foreach parameter [set BTSMatrix(bpmList)] {
        global bpmButton$index
        set bpmButton$index 1
        lappend bpmButtons bpmButton$index
        incr index
    }
    destroy $w.bpms
    APSCheckButtonFrame .bpms -parent $w -label "BTS BPMs" \
      -buttonList [set BTSMatrix(bpmList)] \
      -variableList $bpmButtons \
      -limitPerRow 7  -allNone 1
}

proc GetSelectedBPMList {args} {
    set bpmList ""
    global BTSMatrix
    set index 0
    foreach parameter [set BTSMatrix(bpmList)] pv [set BTSMatrix(bpmPVList)] {
        set name bpmButton$index
        global $name
        if [set $name] {
            lappend bpmList $pv
        }
        incr index
    }
    return $bpmList
}

proc RunExperiment {args} {
    global BTSMatrix outputDir inputDir BTSRunButton numToAve numSteps postChangePause
    global loTurns hiTurns comment controllawMode DataIndex Comment
    

    if ![file exist $outputDir] {
        if [catch {exec mkdir -p $outputDir} result] {
            puts stderr "Can not create directory $outputDir: $result"
            exit
        }
        #catch {exec setfacl -m mask:rwx $outputDir}
    }
    incr DataIndex
    set genName BTS[format %.4d $DataIndex]
    if [file exist ${outputDir}/${genName}] {
        set genName [APSNextGenerationedName -directory $outputDir -name $genName -separator S -newFile 1]
        if ![regexp {BTS(.+)} $genName a index] {
            return -code error "Invalid name generated: $genName"
        }
        set DataIndex [format %.0f $index]
    }
    set tmpMon ${outputDir}/${genName}.mon
    set output ${outputDir}/${genName}
    #if ![regexp {\S(.+)} $genName a DataIndex] {
    #    return -code error "Invalide Name created!"
    #}
    set bpmList [GetSelectedBPMList]
    if ![llength $bpmList] {
        return -code error "no bpm was selected for measurement!"
    }
    set fd [open $tmpMon w]
    puts $fd "SDDS1"
    puts $fd "&parameter name=Comment, type=string,  &end"
    puts $fd "&column name=ControlName, type=string,  &end"
    puts $fd "&column name=ReadbackName, type=string,  &end"
    puts $fd "&data mode=ascii, no_row_counts=1 &end"
    puts $fd "$Comment"
    foreach bpm $bpmList {
        puts $fd "$bpm $bpm"
    }
    close $fd
    
    if {![file exists ${inputDir}/BTSDisp.exp]} {
        return -code error "experiment file ${inputDir}/BTSDisp.exp does not exist"
    }
    
    global experimentDone 
    set experimentDone 0
    set date [clock format [clock seconds] -format %Y-%m%d]
    if ![string length $comment] {
        set comment "Experiment took on $date"
    }
    set comm [APSMakeSafeQualifierString $comment]

# start BTS extraction
    if [catch {TogglePulsedMagnetEnables -location GuntoBoosterExt \
             } result] {
        SetMainStatus "RunExperiment: $result"
        return -code error "RunExperiment: $result"
    }
    set rcList {BTS:ControlLawXRC BTS:ControlLawYRC}
    if [catch {exec cavget -list=[join $rcList ,] \
                 -list=.SUSP -cavputForm} \
          controllawMode] {
        return -code error "RunExperiment: Problem getting BTS controllaw status: $controllawMode"
        return
    }
    if [catch {exec cavput -list=[join $rcList ,] \
                 -list=.SUSP=1} result] {
        return -code error "RunExperiment: Problem suspending BTS controllaws: $result"
    }
    
    after 2000
    set command "sddsexperiment  \
                ${inputDir}/BTSDisp.exp $output -verbose \
                -macro=monitorFile=$tmpMon,numToAve=$numToAve,lo=$loTurns,hi=$hiTurns,points=$numSteps,pause=$postChangePause "
    APSDisableButton $BTSRunButton
    APSExecLog .jon -width 80 -unixCommand "$command" \
      -callback "set experimentDone done" \
      -abortCallback "set experimentDone abort" \
      -cancelCallback "set experimentDone cancel"
    tkwait variable experimentDone
    APSEnableButton $BTSRunButton
    switch $experimentDone {
        abort {
            SetMainStatus "Scan Aborted!"
        }
        cancel {
            SetMainStatus "Scan Canceled!"
        }
        done {
            SetMainStatus "Scan done, processing..."
            PostProcessScan -file $output
        }
    }
}

proc PostProcessScan {args} {
    set file ""
    APSParseArguments {file}
    global BTSMatrix controllawMode
    
    if ![file exist $file] {
        return -code error "PostProcessScan: The measurement file was not created!"
        return
    }
    if {[string length $controllawMode] && \
          [catch {exec cavput -list=$controllawMode} result]} {
        SetMainStatus "Warning: Problem setting BTS controllaw mode: $result"
    } else {
        SetMainStatus "BTS controllaws restored."
    }
    if [catch {sdds load $file bData} result] {
        SetMainStatus "Error in loading $file."
        return
    }
    set requests ""
    set fileList ""
    set options ""
    set slopeList ""
    set bpmList ""
    lappend requests -layout=4,2 -sep -legend
    foreach bpm $BTSMatrix(bpmList) pv $BTSMatrix(bpmPVList) {
        set index [lsearch -exact $bData(ColumnNames) $pv]
        if { $index >= 0 } {
            set fitfile ${file}.$bpm.fit
            if [catch {exec sddspfit $file $fitfile -generate \
                         -columns=Mt:BoosterRampTurnsAO,$pv } result] {
                return -code error $result
            }
            set val [exec sdds2stream -par=Slope $fitfile]
            set sigma [exec sdds2stream -par=SlopeSigma $fitfile]
            lappend bpmList $bpm
            lappend slopeList $val
            lappend slopeSigmaList $sigma
            append options " -define=par,${pv}Slope,$val,type=double -define=par,${pv}SlopeSigma,$sigma"
            lappend requests "-col=Mt:BoosterRampTurnsAO,${pv} -graphic=symbol $file"
            lappend requests "-col=Mt:BoosterRampTurnsAO,${pv}Fit -graphic=line $fitfile"
            lappend fileList $fitfile
        }
    }
    APSAddToTempFileList -ID BTSDispersionMeas -fileList $fileList
    if [catch {eval exec sddsprocess $file $file.proc $options} result] {
        SetMainStatus "Postprocessscan: $result"
        return -code error "Postprocessscan: $result"
    }
    if [catch {eval exec sddsplot $requests &} result] {
        return -code error  "PostProcessScan: $result"
    }
    # Calculate the dispersion, Ef=7000Mev, extraction energy, Ei=325Mev, inject energy, deltaT=0.225 s
    # T0 -- revolution frequency
    # set Ef 7000 
    # set Ei 325
    # set deltaT 0.225 
    global Ef Ei deltaT resultFile
    if [catch {exec cavget -list=BRF:S:Hp53181_Ch2FreqAI -pend=30} rf] {
        return -code error $rf
    }
    #calculate rf revolution time
    set T0 [expr 432.0/$rf]
    # extra factor of 3 since Mt:BoosterRampTurnsAO is in units
    # of SR turns.
    set factor [expr 1.0*$Ef/($Ef-$Ei)*($deltaT/$T0) / 3]
    set resultFile ${file}.disp
    set fd [open $resultFile w]
    puts $fd "SDDS1"
    puts $fd "&column name=ElementName, type=string,  &end"
    puts $fd "&column name=etax, type=double, &end"
    puts $fd "&column name=etaxSigma,type=double, &end"
    puts $fd "&data mode=ascii, no_row_counts=1 &end"
    foreach bpm $bpmList slope $slopeList sigma $slopeSigmaList {
        global ${bpm}Display
        set disp [expr $factor * $slope]
        set error [expr $factor * $sigma]
        set ${bpm}Display "[format %.3f $disp] +- [format %.3f $error]"
        puts $fd "$bpm $disp $error"
    }
    close $fd
    
}

proc MakeDispersionFrame {widget args} {
    global BTSMatrix
    set parent ""
    
    APSParseArguments {parent}
    
    APSFrame $widget -parent $parent -label "Result: dispersion" 
    set w $parent$widget.frame
    APSFrameGrid .fg -parent $w -xList {x1 x2} 
    set w1 $w.fg.x1
    set w2 $w.fg.x2
    foreach bpm $BTSMatrix(bpmList) {
        global ${bpm}Display
        if [string match *H* $bpm] {
            APSLabeledOutput .s$bpm -parent $w1 -label $bpm -textVariable ${bpm}Display -width 20
        } else {
            APSLabeledOutput .s$bpm -parent $w2 -label $bpm -textVariable ${bpm}Display -width 20
        }
    }
}

proc PlotData {args} {
    global outputDir twissFile DataIndex baseDir
    if ![string length $DataIndex] {
        return -code error "Please provide the file index for plotting"
    }
    if {[string length $DataIndex]<4} {
        set inputFile $outputDir/BTS[format %.4d $DataIndex]
    } else {
        set inputFile $outputDir/BTS$DataIndex
    }
    if ![file exist $inputFile] {
        SetMainStatus "$inputFile does not exist!"
        return
    }
    set fileTitle [os editstring %+$baseDir/data/++ $inputFile]
    if [catch {exec sddsplot $inputFile -grap=sym,conn=sub,vary=sub,sca=2 \
                 -sep=request "-topline=Raw data for $fileTitle" -file \
                 -leg=edit=%/:ErrorCC// \
                 -col=Mt:BoosterRampTurnsAO,BTS:?PH?:ErrorCC \
                 "-ylabel=H bpms (mm)" \
                 -col=Mt:BoosterRampTurnsAO,BTS:?PV?:ErrorCC \
                 "-ylabel=V bpms (mm)" \
                 & \
             } result] {
        return -code error "PlotData: $result"
    }
    if [catch {exec sddsplot $inputFile -grap=sym,conn=sub,vary=sub,sca=2 \
                 -sep=request "-topline=Offset data for $fileTitle" \
                 -mode=x=center,y=offset -title= \
                 -leg=edit=%/:ErrorCC// \
                 -col=Mt:BoosterRampTurnsAO,BTS:?PH?:ErrorCC \
                 "-ylabel=H bpms (mm)" \
                 -col=Mt:BoosterRampTurnsAO,BTS:?PV?:ErrorCC \
                 "-ylabel=V bpms (mm)" \
                 & \
             } result] {
        return -code error "PlotData: $result"
    }
}

proc CompareDispersion {args} {
    global outputDir twissFile DataIndex
    if ![string length $DataIndex] {
        return -code error "Please provide the file index for plotting"
    }
    if {[string length $DataIndex]<4} {
        set inputFile $outputDir/BTS[format %.4d $DataIndex].disp
    } else {
        set inputFile $outputDir/BTS$DataIndex.disp
    }
    if ![file exist $inputFile] {
        SetMainStatus "$inputFile does not exist!"
        return
    }
    if ![file exist $twissFile] {
        SetMainStatus "Twiss file $twissFile does not exist!"
        return
    }
    set tmpfile /tmp/[APSTmpString]
    if [catch {exec sddsconvert $inputFile -pipe=out \
                 -rename=col,etax=etaMeas \
                 | sddsxref -pipe=in $twissFile $tmpfile \
                 -match=ElementName -take=s,etax,etay } result] {
        return -code error "CompareDispersion: $result"
    }
    if [catch {exec sddsplot $tmpfile -axes=x -unsup=y -yscale=id=eta  \
                 -col=s,etax -legend=spec=calculated \
                 -graphic=symbol,scale=3,sub=1,type=1,conn=sub \
                 -factor=yMult=1000 \
                 -match=col,ElementName=BTS*PH* \
                 -col=s,etaMeas -legend=spec=Measured \
                 -graphic=symbol,scale=3,sub=2,type=1,conn=sub \
                 -match=col,ElementName=BTS*PH* \
                 -end \
                 -col=s,etay -legend=spec=calculated \
                 -graphic=symbol,scale=3,sub=1,type=1,conn=sub \
                 -factor=yMult=1000 \
                 -match=col,ElementName=BTS*PV* \
                 -col=s,etaMeas -legend=spec=Measured \
                 -graphic=symbol,scale=3,sub=2,type=1,conn=sub \
                 -match=col,ElementName=BTS*PV* \
                 & \
             } result] {
        return -code error "CompareDispersion: $result"
    }
}
proc MakeRunFrame {widget args} {
    global BTSRunButton
    set parent ""
    APSParseArguments {parent}
    pack [frame $parent$widget]
    set w $parent$widget

    APSButton .run -parent $w -text "Run Experiment" -command "RunExperiment"
    APSButton .plot -parent $w -text "PLOT Data" -command "PlotData" \
      -contextHelp "Plots raw data and offset data. Two windows will pop-up, each with two frames for x and y."
    APSButton .comp -parent $w -text "PLOT Comparison" -command "CompareDispersion" \
      -contextHelp "Plots comparision of measured dispersion with model dispersion from reference twiss file."
    set BTSRunButton $w.run.button
}

set date [clock format [clock seconds] -format "%Y-%m%d"]
set baseDir /home/helios/oagData/bts/dispersionMeasurement
set outputDir $baseDir/data/$date
set inputDir $baseDir/inputData
# upper turns range is set small because the booster rf usually
# can't go so high
set loTurns -150
set hiTurns 50
set numToAve 10
set numSteps 5
set postChangePause 2
set comment ""
set Ef 6000 
set Ei 425
set deltaT 0.187
set referenceDir /home/helios/oagData/bts/lattices/default
set twissFile /home/helios/oagData/bts/lattices/default/bts.twi
set resultFile ""
set DataIndex -1

GetBTSBPMListFromFile
MakeStatusWidget -parent .userFrame
MakeOptionFrame .option -parent .userFrame
BTSBPMCheckButtons .bpm -parent .userFrame
MakeRunFrame .runOpt -parent .userFrame
MakeDispersionFrame .slope -parent .userFrame
