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

set dataDir /home/helios/oagData/logging/SRDCPS-HVD
set dataRootname SRDCPS-HVD

APSApplication . -name SRCorrectorReview -version $CVSRevisionAuthor \
        -overview "Shows storage ring corrector data in graphical form as a function of position around the ring.  Allows computation of the orbit change that would result from action of the correctors alone."

cd $dataDir

set status "Ready."
APSScrolledStatus .status -parent .userFrame -textVariable status -width 60 \
    -height 3

proc MakeDateTimeFrame {widget args} {
    set parent .
    set rootname ""
    APSStrictParseArguments {parent rootname glitchMode}
    set label "Date/Time Range of Interest"

    APSFrame $widget -parent $parent -label $label
    set w $parent$widget.frame

    APSDateTimeAdjEntry .startDate -parent $w \
      -yearVariable ${rootname}StartYear \
      -monthVariable ${rootname}StartMonth \
      -dayVariable ${rootname}StartDay \
      -hourVariable ${rootname}StartHour \
      -label "Start (year, month, day, hour): " -defaultHour 0 \
      -command ResetDataFileList -buttonSize small
    APSDateTimeAdjEntry .endDate -parent $w \
      -yearVariable ${rootname}EndYear \
      -monthVariable ${rootname}EndMonth \
      -dayVariable ${rootname}EndDay \
      -hourVariable ${rootname}EndHour \
      -label "End (year, month, day, hour):   " -defaultHour 24 \
      -command ResetDataFileList -buttonSize small

    SetDateTimeToToday -rootname ${rootname}Start  -hour 0
    SetDateTimeToToday -rootname ${rootname}End  -hour 24
}

proc SetDateTimeToToday {args} {
    set rootname ""
    set hour 0
    ResetDataFileList
    APSStrictParseArguments {rootname hour}

    global ${rootname}Month ${rootname}Year ${rootname}Day ${rootname}Hour
    
    APSDateBreakDown -dayVariable ${rootname}Day -yearVariable ${rootname}Year \
      -monthVariable ${rootname}Month -twoDigitYear 0 -leadingZeros 0
    set ${rootname}Hour $hour
}

set dataFileList ""
set dataFileListIsOld 1
proc ResetDataFileList {} {
    global dataFileList  dataFileListIsOld
    set dataFileList ""
    set dataFileListIsOld 1
}

set doHPlots 1
set doVPlots 1
set HScalingMode fixedAuto
set VScalingMode fixedAuto
set HUserLowerScale -1
set HUserUpperScale 1
set VUserLowerScale -1
set VUserUpperScale 1
set abscissaColumn Sector
set sparsingFactor 1
set sectorLowerLimit 1
set sectorUpperLimit 40

proc APSSetLabeledEntryFrameState {widget args} {
    set state normal
    APSStrictParseArguments {state}
    set index 1
    while 1 {
        if [winfo exists $widget.frame.entry$index] {
            $widget.frame.entry$index configure -state $state
        } else {
            break
        }
        incr index
    }
}

proc MakePlotControls {widget args} {
    set parent ""
    APSStrictParseArguments {parent}

    APSFrame $widget -parent $parent -label "Plot controls"
    set w $parent$widget.frame
    
    APSCheckButtonFrame .planes -parent $w -label "Planes: " \
      -buttonList {x/H y/V} -variableList {doHPlots doVPlots} \
      -orientation horizontal \
      -contextHelp "Choose the planes for which you want corrector plots." \
      -allNone 1
    APSRadioButtonFrame .abscissa -parent $w -label "Plot vs.: " \
      -buttonList {s Sector} -valueList {s Sector} -variable abscissaColumn \
      -orientation horizontal -contextHelp \
      "Choose whether to plot vs s (in meters) or vs sector number."

    APSLabeledEntry .sparse -parent $w -label "Sparsing factor: " \
      -textVariable sparsingFactor -contextHelp \
      "Allows displaying only every Nth set, where N is the sparsing factor."

    APSLabeledEntryFrame .sectorLimits -parent $w -label "Sector range: " \
      -variableList {sectorLowerLimit sectorUpperLimit} -width 4 \
      -orientation horizontal -contextHelp \
      "Enter the first and last sector to be shown on the plots."

    foreach plane {H V} {
        set hv [string tolower $plane]
        APSFrame .${hv}scale -parent $w -label "$plane plane plotting" 
        set w1 $w.${hv}scale.frame
        APSRadioButtonFrame .mode -parent $w1 -label "Plot scaling mode: " \
          -buttonList {"Variable Autoscale" "Fixed Autoscale" "User-supplied"} \
          -valueList {variableAuto fixedAuto userSupplied} \
          -variable ${plane}ScalingMode -orientation vertical \
          -contextHelp \
          "Choose mode for vertical plot scale.  Variable autoscale finds appropriate scales for each page of the plot.  Fixed autoscale finds a single scale appropriate for all pages.  User-supplied does no autoscaling, but instead uses the values below, which you must enter."
        APSLabeledEntryFrame .ulimits -parent $w1 -label "Plot limits: " \
          -variableList "${plane}UserLowerScale ${plane}UserUpperScale" \
          -orientation horizontal -width 6
        APSEntryMultiplicationButtons .mbuttons -parent $w1 -label "" \
          -variableList "${plane}UserLowerScale ${plane}UserUpperScale" \
          -description "$plane plane scales"
    }

}

set existingDataFile ""
proc FindFiles {} {
    global StartMonth StartYear StartDay StartHour
    global EndMonth EndYear EndDay EndHour existingDataFile dataDir dataRootname
    global dataFileListIsOld StartTime EndTime

    if {!$dataFileListIsOld && [string length $existingDataFile]} {
        return $existingDataFile
    }
    set dataFileList  \
      [APSFindFilesBetweenDates -tailsOnly 1 \
         -rootname ${dataRootname}- \
         -directory $dataDir \
         -startDateList [APSFormatDate -year $StartYear -month $StartMonth \
                           -day $StartDay -dateFormat list] \
         -endDateList [APSFormatDate -year $EndYear -month $EndMonth \
                         -day $EndDay -dateFormat list] ]
    APSSetVarAndUpdate status "[llength $dataFileList] files found."
    set tmpFile /tmp/[APSTmpString]
    set existingDataFile $tmpFile
    switch [llength $dataFileList] {
        0 {
            return ""
        }
        1 {
            if [string compare [file extension $dataFileList] .gz]==0 {
                set tmpFile $tmpFile.gz
                set existingDataFile $tmpFile
            }
            APSAddToTempFileList $tmpFile
            if [catch {exec cp $dataFileList $tmpFile} result] {
                APSSetVarAndUpdate status "copy problem: $result"
                return ""
            }
        }
        default {
            APSAddToTempFileList $tmpFile
            APSSetVarAndUpdate status "Merging..."
            if [catch {eval exec sddscombine -merge $dataFileList -overwrite $tmpFile} result] {
                APSSetVarAndUpdate status "Processing problem: $result"
                return ""
            }
        }
    }

    set dataFileListIsOld 0
    return $tmpFile
}

proc PlotData {args} {
    set mode current
    APSStrictParseArguments {mode}

    global dataFileList
    global StartMonth StartYear StartDay StartHour
    global EndMonth EndYear EndDay EndHour 
    global sparsingFactor sectorLowerLimit sectorUpperLimit

    if [catch {APSConvertTimeToHours $StartHour} hour] {
        APSSetVarAndUpdate status "Invalid starting hour: $StartHour"
        return
    }
    set startTime \
      [exec timeconvert \
         -breakDown=year=$StartYear,day=$StartDay,month=$StartMonth,hour=$hour]

    if [catch {APSConvertTimeToHours $EndHour} hour] {
        APSSetVarAndUpdate status "Invalid ending hour: $EndHour"
        return
    }
    set endTime \
      [exec timeconvert \
         -breakDown=year=$EndYear,day=$EndDay,month=$EndMonth,hour=$hour]

    APSSetVarAndUpdate status "Working..."
    set datafile [FindFiles]
    if ![string length $datafile] {
        APSSetVarAndUpdate status "No data found."
        return
    }

    MakePlots -fileName $datafile -startTime $startTime -endTime $endTime \
      -mode $mode
}

proc MakePlots {args} {
    global plotDevice printerName labelSizeOption 
    global env plotTitle userPlotOptions
    global doHPlots doVPlots HScalingMode VScalingMode
    global HUserLowerScale HUserUpperScale VUserLowerScale VUserUpperScale
    global abscissaColumn
    global sparsingFactor sectorLowerLimit sectorUpperLimit

    set fileName ""
    set startTime 0
    set endTime 0
    set mode current
    APSStrictParseArguments {fileName startTime endTime mode}
    if ![file exists $fileName] {
        return
    }

    set collectedData /tmp/[APSTmpString]
    APSAddToTempFileList $collectedData
    APSSetVarAndUpdate status "Collecting data..."
    if [catch {APSSRCollectCorrectorData -input $fileName -output $collectedData \
                 -startTime $startTime -endTime $endTime \
                 -sparsingFactor $sparsingFactor} result] {
        APSSetVarAndUpdate status $result
        return
    }
    set dataFile $collectedData

    set quantityPrefix ""
    if [string compare $mode currentChanges]==0 {
        APSSetVarAndUpdate status "Computing changes..."
        if [catch {exec sddschanges $dataFile $dataFile.ch \
                     -copy=s,Sector -changesIn=*CurrentAI 
            exec cp $dataFile.ch $dataFile} result] {
            return -code error $result
        }
        set quantityPrefix ChangeIn
        set quantitySuffix CurrentAI
    } elseif [string compare $mode current]==0 {
        set quantitySuffix CurrentAI
    } else {
        APSSetVarAndUpdate status "Computing implied orbits..."
        if [catch {ComputeImpliedOrbits -input $dataFile -output $dataFile.orb} result] {
            APSSetVarAndUpdate status $result
            return
        }
        set dataFile $dataFile.orb
        set quantitySuffix Orbit
    }


    if [string compare $plotDevice motif] {
        set commandTail "| lpr -P$printerName"
    } else {
        set commandTail ""
    }

    foreach plane {H V} {
        if [subst \$do[string toupper $plane]Plots] {
            switch [subst \$${plane}ScalingMode] {
                fixedAuto {
                    set scaleOption -samescales=y
                }
                variableAuto {
                    set scaleOption ""
                }
                userSupplied {
                    set scaleOption -scale=0,0,[subst \$${plane}UserLowerScale],[subst \$${plane}UserUpperScale]
                }
            }
            set yLabelOption ""
            eval exec sddsplot -title=@TimeText $yLabelOption \
              -sep=page -split=page $scaleOption \
              -filter=column,Sector,$sectorLowerLimit,[expr $sectorUpperLimit+1] \
              "-topline=[APSMakeSafeQualifierString $plotTitle]" \
              $labelSizeOption \
              -device=$plotDevice $dataFile $userPlotOptions \
              -column=${abscissaColumn},${quantityPrefix}${plane}${quantitySuffix} \
              $commandTail &
        }
        set xy y
    }

    APSSetVarAndUpdate status "Plots launched."
}

proc APSSRCollectCorrectorData {args} {
    global OAGGlobal
    set dataDir /home/helios/oagData/logging/SRDCPS-HVD
    set input ""
    set output ""
    set startTime 0
    set endTime 0
    set sparsingFactor 1
    set sectorLimits [list 1 40]
    APSStrictParseArguments {input output startTime endTime \
                           sparsingFactor sectorLimits}
    if {![string length $input] || ![file exists $input]} {
        return -code error "APSSRCollectCorrectorData: not found: $input"
    }
    if ![string length $output] {
        return -code error "APSSRCollectCorrectorData: no output file named."
    }
    set timeFilter ""
    if {$startTime<$endTime} {
        set timeFilter "| sddsprocess -pipe -filter=column,Time,$startTime,$endTime"
    }
    set sparseFilter ""
    if {$sparsingFactor>1} {
        set sparseFilter "| sddsprocess -pipe -define=parameter,SparsingFactor,$sparsingFactor {-test=column,i_row SparsingFactor / = int - 0 ==}"
    }
    # make a xref file with combine magnet names HV
    if [catch {exec sddsprocess $OAGGlobal(SRLatticesDirectory)/scripts/SRCorrPosition.xref /tmp/SRCorrPosition.xref \
                   -match=col,ElementName=*H* \
                   -reedit=col,ElementName,%/H/HV/ \
               } result] {
        return -code error "APSSRCollectCorrectorData: $result"
    }
        
    # there is not necessarily 8 correctors per sector, so I replace with
    # a rough division by 27.60 m.
    if [catch {eval exec sddsconvert -pipe=out $input -recover \
                   -retain=column,Time,*\\\[HV\\\]*CurrentAI \
                   -editNames=column,*H*CurrentAI,%/H//%/Current/HCurrent/ \
                   -editNames=column,*V*CurrentAI,%/V//%/Current/VCurrent/ \
                   -editNames=column,*CurrentAI,s/:/i/HV/ \
                   $timeFilter $sparseFilter \
                   | sddscollect -pipe -collect=suffix=:HCurrentAI,col=HCurrentAI -collect=suffix=:VCurrentAI,col=VCurrentAI \
                   | sddssort -column=Rootname -numericHigh -pipe \
                   | sddsxref -pipe  /tmp/SRCorrPosition.xref -take=s,Sector -reuse=page \
                   -match=Rootname=ElementName -noWarn \
                   | sddssort -pipe -column=s \
                   | sddstimeconvert -pipe=in $output -breakdown=parameter,Time,text=TimeText \
               } result] {
        return -code error "APSSRCollectCorrectorData: $result"
    }
    APSSetVarAndUpdate status "corrector data in file $output"
}

proc ComputeImpliedOrbits {args} {
    global OAGGlobal
    set input ""
    set output ""
    APSStrictParseArguments {input output}
    
    set matrixFile(H) $OAGGlobal(SRLatticesDirectory)/default/aps.hrm
    set matrixFile(V) $OAGGlobal(SRLatticesDirectory)/default/aps.vrm
    set tmpMatrix /tmp/[APSTmpString]
    APSAddToTempFileList $tmpMatrix
    foreach plane {H V} {
        if [catch {exec sddsconvert $matrixFile($plane) -retain=column,*:$plane* $tmpMatrix
            exec sddsconvert $input -pipe=out -retain=column,${plane}CurrentAI \
                     | sddschanges -pipe -change=${plane}CurrentAI \
                     | sddsmatrixmult -commute $tmpMatrix -pipe -reuse \
                     | sddsconvert -pipe -rename=column,ChangeIn${plane}CurrentAI=${plane}Orbit \
                     | sddsprocess -pipe \
                     "-redefine=column,${plane}Orbit,${plane}Orbit,symbol=,description=,units=mm" \
                     | sddsxref $matrixFile($plane) -pipe -reuse=page -take=BPMName,s \
                     | sddssort -pipe=in $output.$plane -column=s } result] {
            return -code error $result
        }
    }
    if [catch {exec sddsxref $output.H $output.V -take=* -pipe=out \
                 | sddsxref -pipe $input -leave=* -transfer=param,* \
                 | sddsprocess -pipe=in $output \
                 "-define=column,Sector,i_row 9 / 1 +"} result] {
        return -code error $result
    }
    catch {eval exec rm $output.H $output.V}
}

proc MakeGenericPlotControls {widget args} {
    global plotDevice 
    global printerName labelSizeOption env groupMode plotTitle userPlotOptions
    set plotDevice motif
    set sameScaleY 0
    set layoutChoice -layout=1,1
    set labelSizeOption -labelsize=0.02
    set groupMode sector
    set plotTitle ""
    set userPlotOptions "-axe=x -grap=impulse"
    if [info exists env(PRINTER)] {
        set printerName $env(PRINTER)
    } else {
        set printerName mcr1
    }

    set parent "" 
    APSStrictParseArguments {parent}

    APSFrame $widget -parent $parent -label "Plot controls"
    set parent $parent$widget.frame

    APSFrameGrid .grid -parent $parent \
      -yList {y1 y2} -width 10 \
      -xList {x1 x2 x3 x4}
        
    APSRadioButtonFrame .device  -parent ${parent}.grid.x4.y1 \
      -label Device -orientation vertical \
      -variable plotDevice \
      -buttonList {X-windows "B&W Postscript" "Color Postscript"} \
      -valueList {motif postscript cpostscript} \
      -contextHelp "Choose the plotting device.  Postscript is for delivery to a printer only."
    APSLabeledEntry .printer -parent ${parent}.grid.x4.y2 \
        -label "Printer: " -textVariable printerName -width 10
    APSRadioButtonFrame .labelsize -parent ${parent}.grid.x2.y2 \
        -label "Label size" -orientation vertical -variable labelSizeOption \
        -buttonList {normal +15% +30% +45% +60%} \
        -valueList {-labelsize=0.02 -labelsize=0.023 -labelsize=0.026 -labelsize=0.029 -labelsize=0.032} \
        -contextHelp "For layouts with many panels, the labels may be hard to read.  You can increase the size of the labels using these options."
            
    APSLabeledEntry .title -parent $parent \
      -label "Title: " -textVariable plotTitle -width 45 \
      -contextHelp "Enter a text string to be used as a plot title."
    APSLabeledEntry .extra -parent $parent \
      -label "Extra options: " -textVariable userPlotOptions -width 45 \
      -contextHelp "Enter sddsplot options to be used for plotting."
}

APSFrameGrid .fg -parent .userFrame -xList {left right}
update
MakeGenericPlotControls .plotcontrol -parent .userFrame.fg.right
update
MakeDateTimeFrame .date -parent .userFrame.fg.right
update
MakePlotControls .ops -parent .userFrame.fg.left
update

APSButton .plotCurrent -parent .userFrame \
  -text "Plot current" -command {PlotData -mode current} \
  -contextHelp "Plots the chosen readback data."
APSButton .plotCurrentChanges -parent .userFrame \
  -text "Plot current changes" -command {PlotData -mode currentChanges} \
  -contextHelp "Plots the chosen readback data relative to the first set."
APSButton .plotOrbit -parent .userFrame \
  -text "Plot implied orbit changes" -command {PlotData -mode orbit} \
  -contextHelp "Plots the orbit changes corresponding to the chosen readback data."
