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

APSApplication . -name SRdispersionCorrection -version 1 \
    -overview {This application allows correction of the horizontal dispersion using a selection of quadrupoles as correctors. The horizontal dispersion must be measured at the bpms using other applications.}

set etaxCorrConfigStatus ""
APSScrolledStatus .status -parent .userFrame \
  -textVariable etaxCorrConfigStatus  \
  -width 90

set rootDir /home/helios/oagData/sr/latticeFunctionCorrection/etax
set refMatrixDir $rootDir/lattices/default
set refMatrixFile default.etax


proc GenerateCorrectionDir {} {
    global corrMatrixDir rootDir
    if ![string length $corrMatrixDir] {
        set index 0
        set foundAvailableIndex 0
        while {!$foundAvailableIndex} {
            set corrMatrixDir $rootDir/lattices/default/[clock format [clock seconds] -format %Y-%m%d].[format %02ld $index]
            if [file exists $corrMatrixDir] {
                incr index
            } else {
                set foundAvailableIndex 1
            }
        }        
    }
    if [file exists $corrMatrixDir] {
        APSSetVarAndUpdate etaxCorrConfigStatus "Directory $corrMatrixDir already exists."
    } 
    if [catch {exec mkdir -p $corrMatrixDir} result] {
        return -code error "GenerateCorrectionDir: $result"
    }
    if [catch {file attributes $corrMatrixDir -permissions 00775} result] {
        return -code error "GenerateCorrectionDir: $result"
    }
    return -code ok
}

proc GenerateCorrectionMatrix {args} {
    global BPMList quadList BPMMissingList
    set configFile ""
    set outputRoot ""
    set referenceMatrix ""
    set removeVectors 0
    set generate 0
    set install 0
    set rootname ""
    set singularValues 320
    set removeVectors 0
    set permissions 0664
    APSStrictParseArguments {configFile outputRoot referenceMatrix generate \
                               install rootname singularValues removeVectors permissions}
    if ![file exists $outputRoot] {
        if [catch {exec mkdir -p $outputRoot } result] {
            return -code error "GenerateCorrectionMatrix: $result"
        }
        if [catch {file attributes $outputRoot -permissions $permissions} result] {
            return -code error "GenerateCorrectionMatrix: $result"
        }
    }
    # create a config file
    set configFile $outputRoot/config
    catch {file delete -force -- $ConfigFile}
    APSSetVarAndUpdate etaxCorrConfigStatus "Creating file $configFile..."
    APSWriteSRConfig -rootname etaxCorr -filename $configFile \
      -nameTypeList {Monitor Corrector} -interactive 1 \
      -suffixLists [list $BPMList $quadList ] \
      -description "Dispersion correction"
    file attributes $configFile -permissions $permissions

    set referenceMatrix [APSResolveLink $referenceMatrix]
    set origRefMatrix $referenceMatrix

    if ![string length $outputRoot] {
        return -code error "GenerateCorrectionMatrix: supply an output rootname."
    }

    if [catch {exec sddsprocess $configFile -pipe=out \
                 -match=parameter,NameType=CorrectorNames \
                 -filter=column,Flag,1,1 \
                 | sddscombine -merge -pipe \
                 | sdds2stream -pipe -column=Name} correctorNames] {
        return -code error "GenerateCorrectionMatrix: $correctorNames"
    }
    file delete -force -- $outputRoot/monitors
    if [catch {exec sddsprocess $configFile -pipe=out \
                 -match=parameter,NameType=MonitorNames \
                 -filter=column,Flag,1,1 \
                 | sddscombine -pipe -merge \
                 | tee $outputRoot/monitors \
                 | sdds2stream -pipe -column=Name} monitorNames] {
        return -code error "APSSRGenerateOrbCorrFiles: $monitorNames"
    }
    file attributes $outputRoot/monitors  -permissions $permissions
    set monitorNameMatchOption ""
    foreach item $monitorNames {
        if [string length $monitorNameMatchOption] {
            set monitorNameMatchOption "$monitorNameMatchOption,ElementName=$item,|"
        } else {
            set monitorNameMatchOption "-match=column,ElementName=$item"
        }
    }
    if $removeVectors {
        set inverseOptions "-largestSingularValues=$singularValues -removeDCVectors"
    } else {
        set inverseOptions "-largestSingularValues=$singularValues"
    }
    if [lsearch -exact [APSGetSDDSNames -fileName $outputRoot/monitors -class column] Weight]!=-1 {
        lappend inverseOptions "-weights=$outputRoot/monitors,name=Name,value=Weight"
    }
    if [catch {file delete -force -- $outputRoot/irm
        eval exec sddsconvert $referenceMatrix -pipe=out \
                 -retain=column,ElementName,[join $correctorNames ,] \
                 | sddsprocess -pipe $monitorNameMatchOption \
                 | sddspseudoinverse -pipe $inverseOptions \
                 | sddsconvert -pipe -rename=column,OldColumnNames=Quadrupoles \
                 | sddsprocess -pipe=in $outputRoot/irm \
                 \"-redefine=col,%s,%s 1000 /,select=S*,units=A/mm,description=A per mm dispersion error\" \
                 -print=parameter,ConfigurationFile,$configFile \
                 -print=parameter,ReferenceMatrix,$origRefMatrix} result] {
        return -code error "APSSRGenerateOrbCorrFiles: $result"
    }
    if {[string length $permissions] && [catch {file attributes $outputRoot/irm -permissions $permissions} result]} {
        return -code error "APSSRGenerateOrbCorrFiles: error setting permissions $permissions on $outputRoot/irm"
    }
    if [catch {exec sdds2stream -param=ConditionNumber $outputRoot/irm} condNumber] {
        return -code error "APSSRGenerateOrbCorrFiles: error getting condition number"
    }
    APSSetVarAndUpdate etaxCorrConfigStatus "Done.  Condition number is $condNumber ."
    update
}

proc CorrectDispersion {args} {
    set measFile ""
    set dispersionReferenceFile ""
    set corrMatrixDir ""
    set gain ""
    set burtOutputDir ""
    set burtOutputFile ""
    APSParseArguments {measFile dispersionReferenceFile \
                         corrMatrixDir gain burtOutputDir burtOutputFile}
    if {![string length $measFile] || \
          ![string length $dispersionReferenceFile] || \
          ![string length $corrMatrixDir] || \
          ![string length $gain] || ![string length $burtOutputDir] || \
          ![string length $burtOutputFile] } {
        return -code error "CorrectDispersion: Not all argument specified."
    }
    if ![file exists $corrMatrixDir] {
        return -code error "CorrectDispersion: Can't find directory $corrMatrixDir"
    }
    if ![file exists $measFile] {
        return -code error "CorrectDispersion: Can't find file $measFile"
    }
    if ![file exists $burtOutputDir] {
        return -code error "CorrectDispersion: Can't find directory $burtOutputDir"
    }
    if ![file exists $dispersionReferenceFile] {
        return -code error "CorrectDispersion: Can't find file $dispersionReferenceFile."
    }
    set tempRoot /tmp/[APSTmpString]
    set dispDiff $tempRoot.dispDiff
# filter dispersion measurement to match correction matrix columns.
    if [catch {exec sddsxref $measFile \
                 $dispersionReferenceFile -pipe=out \
                 -take=etax -match=Rootname=ElementName \
                 | sddsprocess -pipe \
                 "-def=col,dispDiff,xDispersion etax 1000 * -,units=mm" \
                 | sddsselect -pipe $corrMatrixDir/monitors -match=Rootname=Name \
                 | sddsconvert -pipe=in $dispDiff \
                 -retain=col,Rootname,dispDiff \
             } result] {
        return -code error "CorrectDispersion: $result"
    }

    if [catch {exec sddsmatrixmult $corrMatrixDir/irm $dispDiff -pipe=out \
                 | sddsxref -pipe $corrMatrixDir/irm \
                 -take=Quadrupoles \
                 | sddsprocess -pipe \
                 "-def=col,quadDelta,dispDiff chs $gain *" \
                 | sddsprocess -pipe=in $burtOutputDir/$burtOutputFile \
                 -print=col,ControlName,%s:CurrentAO,Quadrupoles \
                 -print=col,ValueString,%lf,quadDelta \
                 -print=col,ControlType,pv \
                 -print=col,Lineage,- \
                 -define=col,Count,1,type=long \
                 -print=para,SnapType,Relative \
             } result] {
        return -code error "CorrectDispersion: $result"
    }
    if [catch {exec sddsplot -sep \
                 -col=Rootname,dispDiff $dispDiff \
                 -axes=x -enum=int=18 \
                 -col=Quadrupoles,quadDelta \
                 $burtOutputDir/$burtOutputFile & \
             } result] {
        return -code error "CorrectDispersion: $result"
    }

    APSSetVarAndUpdate etaxCorrConfigStatus "Done."
}

proc ApplyCorrection {args} {
    APSParseArguments {burtFile}
    if ![file exists $burtFile] {
        return -code error "Can't find file $burtFile"
    }
    exec burtwb -f $burtFile
    #exec sddscasr -restore $burtFile
    APSSetVarAndUpdate etaxCorrConfigStatus "Done."
}
    
# P0's are removed because the measurement never contains P0 bpms.
set BPMList {A:P1 A:P2 A:P3 A:P4 B:P5 B:P4 B:P3 B:P2 B:P1}
set quadList {A:Q1 A:Q2 A:Q3 A:Q4 A:Q5 B:Q5 B:Q4 B:Q3 B:Q2 B:Q1}
set BPMMissingList [APSGetMissingBPMList -plane H]
# always bad bpms
lappend BPMMissingList S35A:P4 S35B:P4 S37B:P4 
set gain 1
set singularValues 40
set dispersionReferenceFile $OAGGlobal(SRLatticesDirectory)/default/aps.twi
set measFile dispChrom01.slopes
set burtOutputFile quadDelta01

APSSRSectorButtons .bpmButtons -parent .userFrame -rootname etaxCorr -orientation horizontal \
  -label "BPM selections" -description "BPM selections" \
  -itemList $BPMList -packOption "-side top" \
  -itemLabelList $BPMList \
  -missingList $BPMMissingList

APSSRSectorButtons .quadButtons -parent .userFrame -rootname etaxCorr -orientation horizontal \
  -label "Quad selections" -description "Quad selections" \
  -itemList $quadList -packOption "-side top" \
  -itemLabelList $quadList

set w [APSScroll .scroll -parent .userFrame \
              -contextHelp "View controls"]
APSLabeledEntry .dirname -parent $w \
  -gridPack "-row 0 -column 0 -sticky nsew" \
  -label "Measurement directory: " \
  -textVariable measDir -width 55 -contextHelp \
  "Enter the name of the directory from which to get dispersion measurements."
APSButton .daily  -parent $w \
  -gridPack "-row 0 -column 1 -sticky nsw" \
  -text "Go to daily directory" \
  -command {set measDir [APSGoToDailyDirectory -subdirectory dispChrom]} \
  -contextHelp "Setting daily directory from which to get dispersion measurements."

APSLabeledEntry .measurement -parent $w \
  -gridPack "-row 1 -column 0 -sticky nsew" \
  -label "Dispersion measurement file:" \
  -textVariable measFile 

APSLabeledEntry .refMatrixDir -parent $w \
  -gridPack "-row 2 -column 0 -sticky nsew" \
  -label "Reference matrix directory:" \
  -textVariable refMatrixDir -width 55
APSLabeledEntry .refMatrixFile -parent $w \
  -gridPack "-row 3 -column 0 -sticky nsew" \
  -label "Reference matrix file:" \
  -textVariable refMatrixFile

APSLabeledEntry .corrMatrixDir -parent $w \
  -gridPack "-row 4 -column 0  -sticky nsew" \
  -label "Correction matrix directory:" \
  -textVariable corrMatrixDir -width 55
APSButton .genCorrDir  -parent $w \
  -gridPack "-row 4 -column 1 -sticky nsw" \
  -text "Generate Directory" \
  -command {GenerateCorrectionDir} \
  -contextHelp "Generate correction directory into which to put dispersion correction matrix."
APSLabeledEntry .singularValues -parent $w \
  -gridPack "-row 5 -column 0 -sticky nsew" \
  -label "Number of singular values:" \
  -textVariable singularValues
APSButton .genCorrMatrix  -parent $w \
  -gridPack "-row 6 -column 0 -sticky nsw" \
  -text "Generate correction matrix" \
  -command {GenerateCorrectionMatrix -outputRoot $corrMatrixDir \
              -singularValues $singularValues \
              -referenceMatrix $refMatrixDir/$refMatrixFile} \
  -contextHelp "Uses the selected bpms and quads to filter out columns and rows of the reference matrix. The resulting matrix is inverted and written into a subdirectory of /home/helios/oagData/sr/latticeFunctionCorrection/lattices/default."

APSLabeledEntry .dispersionReference -parent $w \
  -gridPack "-row 7 -column 0  -sticky nsew" \
  -label "Dispersion reference file:" \
  -textVariable dispersionReferenceFile -width 55

APSLabeledEntry .burtOutputDir -parent $w \
  -gridPack "-row 8 -column 0  -sticky nsew" \
  -label "Burt output directory:" \
  -textVariable burtOutputDir -width 55
APSLabeledEntry .burtOutputFile -parent $w \
  -gridPack "-row 9 -column 0  -sticky nsew" \
  -label "Burt output file:" \
  -textVariable burtOutputFile 
APSButton .resultsDaily  -parent $w \
  -gridPack "-row 8 -column 1 -sticky nsw" \
  -text "Go to daily directory" \
  -command {set burtOutputDir [APSGoToDailyDirectory -subdirectory dispersionCorrection]} \
  -contextHelp "Setting daily directory in which to put dispersion correction."

APSLabeledEntry .gain -parent $w \
  -gridPack "-row 10 -column 0 -sticky nsew" \
  -label "Gain:" \
  -textVariable gain -width 10
APSButton .genBurtOutput  -parent $w \
  -gridPack "-row 11 -column 0 -sticky nsw" \
  -packOption "-side top -anchor w" \
  -text "Calculate dispersion correction" \
  -command {CorrectDispersion -measFile $measDir/$measFile -gain $gain \
  -corrMatrixDir $corrMatrixDir \
  -dispersionReferenceFile $dispersionReferenceFile \
  -burtOutputDir $burtOutputDir -burtOutputFile $burtOutputFile} \
  -contextHelp "Generate a burt output file with quadrupole correction."
APSButton .applyCorrection -parent $w \
  -gridPack "-row 12 -column 0 -sticky nsw" \
  -packOption "-side top -anchor w" \
  -text "Apply dispersion correction" \
  -command {ApplyCorrection -burtFile $burtOutputDir/$burtOutputFile} \
  -contextHelp "Sends calculated quadrupole correction to control system."

proc SaveConfig {args} {
    set description ""
    APSParseArguments {description}
    if ![string length $description] {
        return -code error "MakeStandardizeFile: description has null value."
    }
    set saveConfig [APSSaveMachine -machine SR -description $description]
    APSSetVarAndUpdate etaxCorrConfigStatus "Save to $saveConfig Done."
    return -code ok $saveConfig
}

proc MakeStandardizeFile {args} {
    set root ""
    set directory ""
    set makeOnly 0
    APSParseArguments {root directory makeOnly}
    if ![string length $root] {
        return -code error "MakeStandardizeFile: root has null value."
    }
    if ![string length $directory] {
        return -code error "MakeStandardizeFile: directory has null value."
    }
    exec snap2standardize -snapshot \
      /home/helios/oagData/SCR/snapshots/SR/$root.gz \
      -output $directory/$root.std  \
      -template /home/helios/oagData/sr/magnetConditioning/template.std
    if !$makeOnly {
        exec standardize -configure $directory/$root.std
    }
    APSSetVarAndUpdate etaxCorrConfigStatus "Configured to $directory/$root.std"
}
    

APSLabeledEntry .saveDescription -parent $w \
  -gridPack "-row 13 -column 0 -sticky nsew" \
  -label "Save description:" \
  -textVariable saveDescription -width 55
set saveConfig ""
APSButton .saveMachine -parent $w \
  -gridPack "-row 14 -column 0 -sticky nsw" \
  -text "Save SR configuration" \
  -command {set saveConfig [lindex [SaveConfig -description $saveDescription -statusCallback "APSSetVarAndUpdate etaxCorrConfigStatus"] 0]} \
  -contextHelp "Saves SR configuration, just like SaveCompareRestore."
APSLabeledEntry .saveConfig -parent $w \
  -gridPack "-row 15 -column 0 -sticky nsew" \
  -label "Save configuration:" \
  -textVariable saveConfig -width 30 \
  -contextHelp "Name of the SR configuration from which the standardize file is created. This entry box gets a value when the Save SR configuration button button is pressed and the action completes. User may type in a valid file name."
APSButton .makeOnlyStandardizeFile -parent $w \
  -gridPack "-row 16 -column 0 -sticky nsw" \
  -text "Make standardize file" \
  -command {MakeStandardizeFile -directory $burtOutputDir -makeOnly 1\
              -root [file root $saveConfig]} \
  -contextHelp "Makes a standardization file from a saved SR configuration."
APSButton .makeStandardizeFile -parent $w \
  -gridPack "-row 17 -column 0 -sticky nsw" \
  -text "Make/Apply standardize file" \
  -command {MakeStandardizeFile -directory $burtOutputDir \
              -root [file root $saveConfig]} \
  -contextHelp "Makes a standardization file from a saved SR configuration and applies the values to the control system. One may proceed to pem stanardization."

tkwait visibility $w.makeStandardizeFile
# the APSScrollAdjust somehow has problems with
# indentifying the children for height and width measurements.
# So I explicitly configure the scrolling here.
.userFrame.scroll.frame.canvas config -scrollregion "0 0 [winfo width $w] [winfo height $w]"
.userFrame.scroll.frame.canvas config -yscrollincrement [winfo height $w.saveConfig]
.userFrame.scroll.frame.canvas config -height 200
APSSetVarAndUpdate etaxCorrConfigStatus "Ready."

