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

APSApplication . -name SrBeamMotionBandPlots -version $CVSRevisionAuthor \
  -overview {This interface provides the ability to plot storage ring beam motion frequency-band data in orbit movie or time-series mode.}

set mainStatus ""
proc SetMainStatus {text} {
    global mainStatus
    set mainStatus $text
    update
}

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

set dataDir /home/helios/oagData/logging/8/

set availableBandList [list 1Hz 2Hz 3Hz 30Hz 60Hz 120Hz 180Hz 240Hz 300Hz 360Hz 480Hz 540Hz 600Hz To10Hz To30Hz To60Hz To100Hz To600Hz To1kHz]

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

    if [APSStrictParseArguments {rootname hour}] {
        return -code error "SetDateTimeToToday: invalid arguments"
    }

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

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

    if [APSStrictParseArguments {parent}] {
        return -code error "MakeDateTimeFrame: invalid arguments"
    }

    APSFrame $widget -parent $parent -label $label 
    
    set w $parent$widget.frame
    
    APSDateTimeAdjEntry .startDate -parent $w -yearVariable StartYear -monthVariable StartMonth \
      -dayVariable StartDay -hourVariable StartHour \
      -label "Starting date/time (year, month, day, hour): " \
      -defaultHour 0 -buttonSize small 
    
    APSDateTimeAdjEntry .endDate -parent $w -yearVariable EndYear -monthVariable EndMonth \
      -dayVariable EndDay -hourVariable EndHour \
      -label "Ending date/time (year, month, day, hour):   " \
      -defaultHour 24 -buttonSize small 
    
    SetDateTimeToToday -rootname Start -hour 0
    SetDateTimeToToday -rootname End  -hour 24
}


proc MakePlotsFrame {args} {
    set parent .userFrame
    if [APSStrictParseArguments {parent}] {
        return -code error "MakeActionFrame: invalid arguments"
    }
    
    set w $parent

    APSButton .plotX -parent $w -text "Plot x vs Time" -command "PlotSignals -signal x"
    APSButton .plotY -parent $w -text "Plot y vs Time" -command "PlotSignals -signal y"
    APSButton .plotXStat -parent $w -text "Plot x stats vs Time" -command "PlotSignals -signal x -plotStats 1"
    APSButton .plotYStat -parent $w -text "Plot y stats vs Time" -command "PlotSignals -signal y -plotStats 1"
    APSButton .plotXO -parent $w -text "Plot x Orbits" -command "PlotSignals -signal x -orbit 1"
    APSButton .plotYO -parent $w -text "Plot y Orbits" -command "PlotSignals -signal y -orbit 1"
}

set BPMList {A:P0 A:P3 B:P6 B:P5 B:P3 B:P0 ID:Pos ID:Ang}
set BPMDir /home/helios/oagData/sr/BPMStatus
set BPMMissingList [exec sddsprocess $BPMDir/config.sdds -pipe=out \
                      -filter=col,NonexistentH,1,1,NonexistentV,1,1,| -noWarning \
                      | sdds2stream -pipe -column=DeviceName]
eval lappend BPMMissingList S36ID:Ang S37ID:Ang S38ID:Ang S39ID:Ang S40ID:Ang
eval lappend BPMMissingList S36ID:Pos S37ID:Pos S38ID:Pos S39ID:Pos S40ID:Pos

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

APSSetSRSectorButtons -mode all-off -rootname bpmOffset -itemList $BPMList -missingList $BPMMissingList

MakeDateTimeFrame .dt -parent .userFrame 

set bandSelection [lindex $availableBandList end]
APSRadioButtonFrame .band -parent .userFrame -label "Band: " -variable bandSelection -buttonList $availableBandList \
    -valueList $availableBandList -orientation horizontal -limitPerRow 13

set xMax 10
set yMax 5
APSLabeledEntryFrame .maxPos -parent .userFrame -label "Limiting x, y value (um): " \
                     -variableList {xMax yMax} -orientation horizontal

set xpMax [expr $xMax/5.2]
set ypMax [expr $yMax/2.4]

APSLabeledEntryFrame .maxSlope -parent .userFrame -label "Limiting xp, yp value (urad): " \
                     -variableList {xpMax ypMax} -orientation horizontal

set userModeOnly 1
APSRadioButtonFrame .userMode -parent .userFrame -label "User beam mode only: " \
                    -buttonList "Yes No" -valueList {1 0} -orientation horizontal -variable userModeOnly

set statList [list Mean Median Min Max]
foreach stat $statList {
    lappend statVarList includeStat($stat)
    set includeStat($stat) 0
}
APSCheckButtonFrame .cbstat -parent .userFrame -label "Statistics (over location): " \
    -buttonList $statList -variableList $statVarList -orientation horizontal -allNone 1 -toggle 1

APSFrame .ppframe -parent .userFrame -label "Processing over time samples: "
set w .userFrame.ppframe.frame
set sparsingPoints 1
set sparsingType mean
APSLabeledEntry .sparsing -parent $w -label "Points: " -textVariable sparsingPoints -width 4 \
  -type integer -contextHelp "Interval with which to sparse (uniformly sample) the data."
APSRadioButtonFrame  .sparsingType -parent $w -label "Sparsing type: " -variable sparsingType -orientation horizontal \
  -buttonList "Sample Mean Median Min Max" -valueList {"" mean median minimum maximum} \
  -contextHelp "Select the method of sparsing."

MakePlotsFrame -parent .userFrame 

proc CreateList {args} {
    set rootname ""
    set suffixLists ""
    if [APSStrictParseArguments {rootname suffixList}] {
        return -code error "CreateList: invalid arguments"
    }
    
    if { ![string length $rootname] || ![string length $suffixList]} {
        return -code error "CreateList: Bad arguments" 
    }
    set names ""
    for {set sector 1} {$sector < 41} {incr sector} {
        set Sector S[format %02d $sector]
        foreach suffix $suffixList {
            set nameFlag ${rootname}${Sector}$suffix
            global $nameFlag
            if [set $nameFlag] {
                lappend names ${Sector}${suffix}
            }
        }
    }
    return $names
}

proc FindFiles {args} {
    set plane x
    if [APSStrictParseArguments {plane}] {
        return -code error "FindFiles: invalid arguments"
    }
    
    global bpmList bandSelection dataDir StartMonth StartYear EndMonth EndYear bpmFileList
    set minNumberOfFiles 10000
    set maxNumberOfFiles -1

    foreach bpm [concat $bpmList S-DCCT:CurrentM S:DesiredMode] {
        set bpmFileList($bpm) [list]
        switch -glob $bpm {
            *:P? {
                set directory [file join $dataDir $bpm:$plane:$bandSelection]
            }
            S??ID:Pos {
                regexp {S(..)ID:Pos} $bpm all nn
                set directory [file join $dataDir ID${nn}:$plane:$bandSelection]
            }
            S??ID:Ang {
                regexp {S(..)ID:Ang} $bpm all nn
                set directory [file join $dataDir ID${nn}:${plane}p:$bandSelection]
            }
            *Desired* -
            *DCCT* {
                set directory [file join $dataDir $bpm]
            }
        }
        for {set year $StartYear} {$year<=$EndYear} {incr year} {
            if {$year == $StartYear} {
                set m1 $StartMonth
            } else {
                set m1 1 
            }
            if {$year == $EndYear} {
                set m2 $EndMonth
            } else {
                set m2 12
            }
            for {set m $m1} {$m<=$m2} {incr m} {
                set fn $directory/log-$year-[format %02d $m].xz
                if [file exists $fn] {
                    lappend bpmFileList($bpm) $fn
                } else {
                    set pattern $directory/log-$year-[format %02d $m].????
                    if [llength [set matchList [lsort [glob -nocomplain $pattern]]]] {
                        eval lappend bpmFileList($bpm) $matchList
                    }
                }
            }
        }
        set nFiles [llength $bpmFileList($bpm)] 
        SetMainStatus "$nFiles files for $bpm"
        if {[string match *:P* $bpm] || [string match *ID* $bpm]} {
            if $nFiles<$minNumberOfFiles {
                set minNumberOfFiles $nFiles
            }
            if $nFiles>$maxNumberOfFiles {
                set maxNumberOfFiles $nFiles
            }
        }
    }
    if { $minNumberOfFiles != $maxNumberOfFiles } {
        SetMainStatus "Error: inconsistent number of files found for BPMs ($minNumberOfFiles to $maxNumberOfFiles)"
        return 0
    } else {
        SetMainStatus "$minNumberOfFiles files found for each of [llength $bpmList] BPMs"
        return 1
    }
}

proc PlotSignals {args} {
    set signal ""
    set orbit 0
    set plotStats 0
    if [APSStrictParseArguments {orbit signal plotStats}] {
        return -code error "PlotSignals: invalid arguments"
    }

    if $plotStats {
        global includeStat statList BPMList
        set statCount 0
        foreach stat $statList {
            if $includeStat($stat) {
                incr statCount
            }
        }
        if $statCount==0 {
            return -code error "PlotSignals: no statistics selected"
        }
    }

    switch $signal {
        x -
        y {
        }
        default {
            return -code error "PlotSignals: invalid signal $signal"
        }
    }
    
    global BPMList fileList bpmList bpmFileList
    
    set bpmList [CreateList -rootname bpmSelection -suffixList $BPMList]
    SetMainStatus "[llength $bpmList] BPMs selected"
    if [llength $bpmList]==0 {
        return
    }

    if ![FindFiles -plane $signal] {
        return
    }

    set tmpRoot /tmp/[APSTmpString]

    global StartYear StartMonth StartDay StartHour
    global EndYear EndMonth EndDay EndHour
    global bandSelection xMax yMax xpMax ypMax userModeOnly
    global sparsingPoints sparsingType

    set pLimit(x) $xMax
    set pLimit(y) $yMax
    set pLimit(xp) $xpMax
    set pLimit(yp) $ypMax
    
    set startTime [exec timeconvert -break=year=$StartYear,month=$StartMonth,day=$StartDay,hour=[APSConvertTimeToHours $StartHour]]
    set endTime [exec timeconvert -break=year=$EndYear,month=$EndMonth,day=$EndDay,hour=[APSConvertTimeToHours $EndHour]]

    if $userModeOnly {
        set filterMode -filter=col,S:DesiredMode,.9,1.1
    } else {
        set filterMode -filter=col,S:DesiredMode,-1,1000
    }

    if $orbit {
        if {[expr ($endTime-$startTime)>3600.0] && \
              ![APSQueryToProceed -message "A movie with more than 1 hour of data may take a long time. Proceed?"]} {
            return
        }
        if [llength $bpmList]<2 {
            SetMainStatus "Orbit plotting doesn't make sense with less than 2 BPMs"
            return
        }
        set fileList [list]
        foreach bpm [concat $bpmList S-DCCT:CurrentM S:DesiredMode] {
            if [string match *ID* $bpm] {
                continue
            }
            SetMainStatus "Combining files for $bpm"
            APSAddToTmpFileList -ID signals -fileList $tmpRoot.$bpm
            if [catch {eval exec sddscombine $bpmFileList($bpm) -merge -pipe=out -retain=col,Time,*:* \
                         | sddsprocess -pipe=in $tmpRoot.$bpm -filter=col,Time,$startTime,$endTime } result] {
                APSDeleteTmpFileList -ID signals
                return -code error "$result"
            }
            lappend fileList $tmpRoot.$bpm
        }
        SetMainStatus "Combining all files"
        APSAddToTmpFileList -ID signals -fileList [list $tmpRoot.all $tmpRoot.twi]
        exec sddsprocess /home/helios/oagData/sr/lattices/default/aps.twi $tmpRoot.twi -match=col,ElementType=MONI
        if [catch {eval exec sddsxref $fileList -pipe=out -equate=Time -nowarning -take=*:* \
                     | sddsprocess -pipe -filter=col,S-DCCT:CurrentM,10,205 "$filterMode" \
                     | sddsbreak -pipe -rowlimit=1 \
                     | sddsprocess -pipe -process=Time,first,Time -print=column,DataName,$signal:$bandSelection \
                     | sddsconvert -pipe -delete=column,Time,S-DCCT:CurrentM,S:DesiredMode \
                     | sddstranspose -pipe -oldColumnNames=SignalName -newColumnName=DataName \
                     | sddsprocess -pipe -edit=col,BPMName,SignalName,2S/:/100d \
                     | sddstimeconvert -pipe -breakdown=parameter,Time,text=TimeStamp \
                     | sddsxref -pipe=in -match=BPMName=ElementName $tmpRoot.twi -take=s -reuse=page $tmpRoot.all } result] {
            APSDeleteTmpFileList -ID signals
            return -code error "$result"
        }
        
        SetMainStatus "Plotting"
        eval exec sddsplot -column=s,*:* $tmpRoot.all -split=page -separate -same -graph=sym,scale=0.5 "{-topline=@TimeStamp}" \
             -limit=ymax=$pLimit($signal),auto &
    } else { 
        # plot vs time
        set fileList [list]
        set bpmTypeList [list]
        set patternList [list]
        foreach bpm [concat $bpmList S-DCCT:CurrentM S:DesiredMode] {
            SetMainStatus "Combining files for $bpm"
            # List of BPM types is needed for statistics computation and plotting
            if [string match S*ID* $bpm] {
                regexp {S..ID:(.*)} $bpm all bpmType
                set bpmType ID:$signal
                if [lsearch -exact $bpmTypeList $bpmType]==-1 {
                    lappend bpmTypeList $bpmType
                    if [string match *Pos* $bpm] {
                        lappend patternList ID??:$signal
                    } else {
                        lappend patternList ID??:${signal}p
                    }
                }
            } elseif [string match S*:P? $bpm] {
                regexp {S...:(.*)} $bpm all bpmType
                set bpmType ${bpmType}:$signal
                if [lsearch -exact $bpmTypeList $bpmType]==-1 {
                    lappend bpmTypeList $bpmType
                    lappend patternList *$bpmType
                }
            } 
            APSAddToTmpFileList -ID signals -fileList $tmpRoot.$bpm
            if [catch {eval exec sddscombine $bpmFileList($bpm) -merge -pipe=out -retain=col,Time,*:* \
                         | sddsprocess -pipe=in $tmpRoot.$bpm -filter=col,Time,$startTime,$endTime } result] {
                APSDeleteTmpFileList -ID signals
                return -code error "$result"
            }
            lappend fileList $tmpRoot.$bpm
        }
        if $plotStats {
            SetMainStatus "Computing stats for $bpmTypeList"
            set statsOptList [list]
            set statsPlotQuantityList [list]
            foreach bpmType $bpmTypeList pattern $patternList {
                foreach stat $statList {
                    if $includeStat($stat) {
                        lappend statsOptList -$stat=${stat}${bpmType}:$bandSelection,${pattern}:$bandSelection
                        lappend statsPlotQuantityList ${stat}${bpmType}:$bandSelection
                    }
                }
            }
            if [llength $statsOptList]==0 {
                SetMainStatus "No statistics selected"
                return
            }
            SetMainStatus "Combining all files and computing statistics into $tmpRoot.all"
            APSAddToTmpFileList -ID signals -fileList [list $tmpRoot.all $tmpRoot.twi]
            exec sddsprocess /home/helios/oagData/sr/lattices/default/aps.twi $tmpRoot.twi -match=col,ElementType=MONI
            #SetMainStatus "$statsOptList"
            set runstatCmd ""
            set runstatLabel ""
            if $sparsingPoints>1 {
                set runstatCmd "| sddsrunstats -pipe -$sparsingType=* -points=$sparsingPoints -nooverlap | sddsconvert -pipe -edit=col,*,%/Mean//%/Minimum//%/Maximum//%/Median// "
                set runstatLabel "$sparsingType over $sparsingPoints consecutive samples"
            }
            if [catch {eval exec sddsxref $fileList -pipe=out -equate=Time -nowarning -take=*:* \
                     $runstatCmd \
                     | sddsrowstats -pipe=in $tmpRoot.all $statsOptList \
                     } result] {
                APSDeleteTmpFileList -ID signals
                return -code error "$result"
            }
            SetMainStatus "Plotting"
            set optList [list]
            foreach statsQuantity $statsPlotQuantityList {
                eval lappend optList -column=Time,$statsQuantity -limit=ymax=$pLimit($signal) -end
            }
            eval exec sddsplot $tmpRoot.all -ticks=xtime -separate=1 -sever=xgap=[expr 16*$sparsingPoints] "$filterMode" \
              "{-title=$runstatLabel}" -filter=col,Time,$startTime,$endTime -filter=col,S-DCCT:CurrentM,10,205 $optList &
        } else {
            SetMainStatus "Combining all files"
            APSAddToTmpFileList -ID signals -fileList [list $tmpRoot.all $tmpRoot.twi]
            exec sddsprocess /home/helios/oagData/sr/lattices/default/aps.twi $tmpRoot.twi -match=col,ElementType=MONI
            if $sparsingPoints>1 {
                if [catch {eval exec sddsxref $fileList -equate=Time -pipe=out -nowarning -take=*:* \
                         | sddsrunstats -pipe -$sparsingType=* -points=$sparsingPoints -nooverlap \
                         | sddsconvert -pipe=in $tmpRoot.all -edit=col,*,%/Mean//%/Minimum//%/Maximum//%/Median// } result] {
                    APSDeleteTmpFileList -ID signals
                    return -code error "$result"
                }
                set runstatLabel "$sparsingType over $sparsingPoints consecutive samples"
            } else {
                if [catch {eval exec sddsxref $fileList -equate=Time $tmpRoot.all -nowarning -take=*:*} result] {
                    APSDeleteTmpFileList -ID signals
                    return -code error "$result"
                }
                set runstatLabel ""
            }
            eval file delete $fileList 
            
            SetMainStatus "Plotting"
            set optList [list]
            foreach bpm $bpmList {
                switch -glob $bpm {
                    *:P? {
                        eval lappend optList -column=Time,$bpm:$signal:$bandSelection -limit=ymax=$pLimit($signal) -end
                    }
                    S??ID:Pos {
                        regexp {S(..)ID:Pos} $bpm all nn
                        eval lappend optList -column=Time,ID${nn}:$signal:$bandSelection -limit=ymax=$pLimit($signal) -end
                    }
                    S??ID:Ang {
                        regexp {S(..)ID:Ang} $bpm all nn
                        eval lappend optList -column=Time,ID${nn}:${signal}p:$bandSelection -limit=ymax=$pLimit(${signal}p) -end
                    }
                }
            }
            eval exec sddsplot $tmpRoot.all -ticks=xtime -separate=1 -sever=xgap=[expr 16*$sparsingPoints] "$filterMode" \
              "{-title=$runstatLabel}" -filter=col,Time,$startTime,$endTime -filter=col,S-DCCT:CurrentM,10,205 $optList &
        }
    }
}



