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

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

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

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

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 generate 0
    set install 0
    set rootname ""
    set singularValues 320
    set permissions 0664
    APSStrictParseArguments {configFile outputRoot referenceMatrix generate \
                               install rootname singularValues 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 etayCorrConfigStatus "Creating file $configFile..."
    APSWriteSRConfig -rootname etayCorr -filename $configFile \
      -nameTypeList {Monitor Corrector} -interactive 0 \
      -suffixLists [list $BPMList $quadList ] \
      -description "Dispersion correction"
    catch {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"
    }
    catch {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"
        }
    }

    set inverseOptions "-largestSingularValues=$singularValues -sFile=$outputRoot/s -vMatrix=$outputRoot/v"

    if [lsearch -exact [APSGetSDDSNames -fileName $outputRoot/monitors -class column] Weight]!=-1 {
        lappend inverseOptions "-weights=$outputRoot/monitors,name=Name,value=Weight"
    }

    
    # conversion factor
    # From magnetic measurement directory /usr6/apsmmf/sr_mag/sqskew/sqs002
    # I found *log files with the calibration factor 0.0358508 [T/A].
    # elegant uses magnetic length of 0.127 m
    #                
    # kl [m^-1] =  (Calibration) I / 23.349 T-m              
    #     
    # so 1A gives KL = 0.00153 or K = 0.0121
    # and K=1 m^2  gives I = 82.7 A
    set length 0.128
    set rigidity 23.349
    set calibration 0.0358508
    set amplitude 0.1
    # matrix elements have units m^-2 of quad strength per m of dispersion.
    set deltaI [expr $length * $rigidity / $calibration * $amplitude]
    
    # these are calibration factor not taken into account in the referenceMatrix
    # it would be better to find the slope of the excitation curve.
    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 $deltaI * 1000 /,select=S*,units=mm/A,description=deltaK per mm dispersion error\" \
                 -print=parameter,ConfigurationFile,$configFile \
                 -print=parameter,ReferenceMatrix,$origRefMatrix \
             } result] {
        return -code error "APSSRGenerateOrbCorrFiles: $result"
    }
    if [catch {exec sddsxref $outputRoot/s $outputRoot/irm -noWarning \
                 -transfer=para,NumberOfSingularValuesUsed \
             } 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 etayCorrConfigStatus "Done.  Condition number is $condNumber ."
    update
}

proc PlotSVspectrum {args} {
    set outputRoot .
    APSStrictParseArguments {outputRoot}

    set usedSVs [exec sdds2stream -para=NumberOfSingularValuesUsed $outputRoot/s]

    if [catch {exec sddsplot \
                 -col=Index,SingularValues $outputRoot/s -grap=impulse,type=1 \
                 -filter=col,Index,0,[expr $usedSVs - 1] -leg=spec=Used  \
                 -col=Index,SingularValues $outputRoot/s -grap=impulse,type=0 \
                 -filter=col,Index,0,[expr $usedSVs - 1],!  "-leg=spec=Not used" & \
             }  results] {
        return -code error "PlotSVspectrum: $results"
    }
}

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=etay -match=Rootname=ElementName \
                 | sddsprocess -pipe \
                 "-def=col,dispDiff,yDispersion etay 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=9 -grap=sym,conn \
                 -col=Quadrupoles,quadDelta \
                 $burtOutputDir/$burtOutputFile & \
             } result] {
        return -code error "CorrectDispersion: $result"
    }

    APSSetVarAndUpdate etayCorrConfigStatus "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 etayCorrConfigStatus "Done."
}
    
# P0's are removed because the measurement never contains P0 bpms.
set BPMList {A:P0 A:P1 A:P2 A:P3 A:P4 B:P5 B:P4 B:P3 B:P2 B:P1 B:P0}
set quadList {A:QS B:QS}
#set BPMMissingList [APSGetMissingBPMList -plane V]
set BPMMissingList [APSSRGetBadOrbitDevices -plane v -device BPM]
set quadMissingList {S1B:QS} 
for {set sector 2} {$sector < 41} {incr sector 2} {
   lappend quadMissingList S${sector}A:QS S${sector}B:QS 
}
for {set sector 1} {$sector < 41} {incr sector 4} {
   lappend quadMissingList S${sector}A:QS
}
for {set sector 3} {$sector < 41} {incr sector 4} {
   lappend quadMissingList S${sector}B:QS
}

# add 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 skewQuadDelta01
# temporary defaults
if {0} {
set measDir /home/helios/SR/daily/9905/24/2/dispChrom
set burtOutputDir [APSGoToDailyDirectory -subdirectory dispersionCorrection]
GenerateCorrectionDir
}

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

#APSRescanBadOrbitDevices -rootname etayCorr -plane v -application DCOrbitCorrection

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

proc MakeActionFrames {parent args} {
    global measDir measFile refMatrixDir refMatrixFile corrMatrixDir singularValues
    global dispersionReferenceFile burtOutputDir burtOutputFile gain saveDescription saveConfig

    APSFrame .tab -parent $parent
    
    set widgetList {"Correction Matrix" "Dispersion Correction" "Save SR Configuration"}
    set tabFrameWidgetList [APSTabFrame .buttons -parent $parent.tab.frame -label "" \
                              -labelList $widgetList -width 1000 -height 250 -packOption "-expand true"]
    set width 75
    set index 0
    set w [lindex $tabFrameWidgetList $index]
    incr index
    APSLabeledEntry .dirname -parent $w \
      -gridPack "-row 0 -column 0 -sticky nsew" \
      -label "Measurement directory: " \
      -textVariable measDir -width $width -contextHelp \
      "Enter the name of the directory from which to get dispersion measurements."
    APSButton .daily  -parent $w.dirname \
      -text "daily" -size small \
      -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 $width
    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 $width
    APSButton .genCorrDir  -parent $w.corrMatrixDir \
      -text "daily" -size small \
      -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."

    APSButton .plotSV  -parent $w \
      -gridPack "-row 6 -column 0 " \
      -text "Plot SV spectrum"\
      -command {PlotSVspectrum -outputRoot $corrMatrixDir} \
      -contextHelp "Plot spectrum of singular values, including those that are not used."

    
    set w [lindex $tabFrameWidgetList $index]
    incr index
    APSLabeledEntry .dispersionReference -parent $w \
      -gridPack "-row 7 -column 0  -sticky nsew" \
      -label "Dispersion reference file:" \
      -textVariable dispersionReferenceFile -width $width
    
    APSLabeledEntry .burtOutputDir -parent $w \
      -gridPack "-row 8 -column 0  -sticky nsew" \
      -label "Burt output directory:" \
      -textVariable burtOutputDir -width $width
    APSLabeledEntry .burtOutputFile -parent $w \
      -gridPack "-row 9 -column 0  -sticky nsew" \
      -label "Burt output file:" \
      -textVariable burtOutputFile 
    APSButton .resultsDaily  -parent $w.burtOutputDir \
      -text "daily" -size small \
      -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 20
    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."

    set w [lindex $tabFrameWidgetList $index]
    incr index
    APSLabeledEntry .saveDescription -parent $w \
      -gridPack "-row 13 -column 0 -sticky nsew" \
      -label "Save description:" \
      -textVariable saveDescription -width $width
    set saveConfig ""
    APSButton .saveMachine -parent $w \
      -gridPack "-row 14 -column 0 -sticky nsw" \
      -text "Save SR configuration" \
      -command {set saveConfig [SaveConfig -description $saveDescription -statusCallback "APSSetVarAndUpdate etayCorrConfigStatus"]} \
      -contextHelp "Saves SR configuration, just like SaveCompareRestore."
    APSLabeledEntry .saveConfig -parent $w \
      -gridPack "-row 15 -column 0 -sticky nsew" \
      -label "Save configuration:" \
      -textVariable saveConfig -width $width \
      -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."
    
}

# to add next:
# make save of SR
# make standardization file from save
# option to install standardization file.
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 etayCorrConfigStatus "Save to $saveConfig Done."
    return -code ok $saveConfig
}
proc MakeStandardizeFile {args} {
    set root ""
    set directory ""
    APSParseArguments {root directory}
    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
    exec standardize -configure $directory/$root.std
    APSSetVarAndUpdate etayCorrConfigStatus "Configured to $directory/$root.std"
}

MakeActionFrames .userFrame
APSSetVarAndUpdate etayCorrConfigStatus "Ready."
