#!/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

if ![file exists /adata/daq] {
    wm withdraw .
    APSAlertBox .oops -name "Error" -modeless 0 -beep no \
      -errorMessage "Error: /adata/daq not found. You must be on orthrosa to use this application."
    exit
}


set CVSRevisionAuthor "\$Revision: 1.00 $ \$Author: borland $"

APSApplication . -name SrTbtDaqPomoBrowser -version $CVSRevisionAuthor \
  -overview {This interface provides the ability to browse TBT post-mortem data for the SR.}

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

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

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 -command ResetIdList -buttonSize small 
    
    APSDateTimeAdjEntry .endDate -parent $w -yearVariable EndYear -monthVariable EndMonth \
      -dayVariable EndDay -hourVariable EndHour \
      -label "Ending date/time (year, month, day, hour):   " \
      -defaultHour 24 -command ResetIdList -buttonSize small 
    
    SetDateTimeToToday -rootname Start -hour 0
    SetDateTimeToToday -rootname End  -hour 24
    
    global idList
    APSButton .find -parent $w -text "Find\nEvents" -command FindEvents
    APSScrolledList .list -parent $w -height 10 -listvar idList -callback EventChoiceCallback -selectMode single
}

proc ResetIdList {} {
    global idList eventList eventFileList lossTimeFound
    set idList [list]
    set eventList [list]
    set eventFileList [list]
    set lossTimeFound 0
}

proc FindEvents {} {
    global StartYear StartMonth StartDay StartHour
    global EndYear EndMonth EndDay EndHour
    global idList eventList

    set idList [list]
    set eventList [list]
    global yearIteration monthIteration dayIteration
    set yearIteration $StartYear
    set monthIteration $StartMonth
    set dayIteration $StartDay
    while {1} {
        set year $yearIteration
        set month $monthIteration
        set day $dayIteration
        set pattern [format /adata/daq/%4d/%02d/%02d/tbtPostMortemBurst.s??.*.sdds $year $month $day]
        set list1 [lsort [glob -nocomplain $pattern]]
        foreach item $list1 {
            if [string match *.eventNumber.sdds $item] continue
            regexp {(.*)\.s..\.(.*).sdds} $item all dummy id
            regexp {(....)(..)(..)(..)(..)(..)(...)} $id all YYYY MM DD hh mm ss seq
            set idString "$YYYY/$MM/$DD $hh:$mm:$ss $seq"
            if [lsearch -exact $idList $idString]==-1 {
                lappend eventList $YYYY/$MM/$DD/tbtPostMortemBurst.s??.$id.sdds
                lappend idList $idString
            }
        }
        APSIncrementDateVariables -yearVariable yearIteration -monthVariable monthIteration -dayVariable dayIteration -offset 1
        if $yearIteration>$EndYear {
            break
        }
        if $yearIteration==$EndYear {
            if $monthIteration>$EndMonth {
                break
            }
            if $monthIteration==$EndMonth {
                if $dayIteration>$EndDay {
                    break
                }
            }
        }
    }

    set combined [list]
    foreach event $eventList id $idList {
        lappend combined [list $id $event]
    }
    
    set sortedCombined [lsort -index 0 $combined]

    set idList [list]
    set eventList [list]
    foreach pair $sortedCombined {
        lappend eventList [lindex $pair 1]
        set count [llength [glob /adata/daq/[lindex $pair 1]]]
        lappend idList "[lindex $pair 0] $count files"
    }

    update
}

proc EventChoiceCallback {item doubleClick} {
    global eventChoice idList eventList eventFileList eventLabel lossTimeFound
    puts stderr "EventChoiceCallback: $item"
    puts stderr [join $idList ,]
    set selectionIndex [lsearch -exact $idList $item]
    puts stderr "selectionIndex: $selectionIndex"
    if {$selectionIndex > -1} {
        set eventChoice [lindex $eventList $selectionIndex]
        set eventFileList [lsort [glob -nocomplain /adata/daq/${eventChoice}]]
        regexp {.*\.s..\.(....)(..)(..)(..)(..)(..)(...)\.sdds} $eventChoice all Y M D h m s id
        set eventLabel "$Y/$M/$D $h:$m:$s \#$id"
        SetMainStatus "$eventChoice: [llength $eventFileList] files"
        SetMainStatus "Be sure to find the loss time (Preparation tab) before proceeding."
        set lossTimeFound 0
    }
}

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

    global timeROI
    
    APSLabeledOutput .lossPoint -parent $w -label "Loss Time: " -textVariable timeROI(Center) -width 30
    APSLabeledOutput .lossPointText -parent $w -label "Loss Time Stamp: " -textVariable timeROI(TimeStamp) \
      -width 40
    APSButton .findLossPoint -parent $w -text "Find Loss Time" -command FindLossTime
}

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

    global timeROI sumLimit groupingMode orbitOffsetMode includeMagnetLayout plotVsSector 
    
    set timeROI(LL) -0.2
    set timeROI(UL) 0.06
    set timeROI(Center) 0
    # My humble homage to FORTRAN
    set timeROI(Min) -999.999
    set timeROI(Max) 999.999

    APSLabeledEntryFrame .timeROI -parent $w -label "Time ROI relative to loss time (ms): " \
                         -variableList [list timeROI(LL) timeROI(UL)] -width 10 -orientation horizontal  -type real \
                         -contextHelp "Enter the lower and upper time limits of the region of interest relative to the time of loss."

    set sumLimit 0
    APSLabeledEntry .sumLimit -parent $w -label "Lower sum limit for plots (counts): " -width 10 -type real \
      -textVariable sumLimit -contextHelp "Enter the lower limit for the first sum signal as a filter for plots."
    
    set groupingMode sector
    APSRadioButtonFrame .grouping -parent $w -label "Grouping mode for time plots: " -orientation horizontal \
      -variable groupingMode -buttonList "Sector Location" -valueList "sector location"

    set orbitOffsetMode 1
    APSRadioButtonFrame .offset -parent $w -label "Offset/normalize orbit data by first orbit: " -orientation horizontal \
      -variable orbitOffsetMode -buttonList "Yes No" -valueList "1 0"
    set includeMagnetLayout 0
    APSRadioButtonFrame .magnets -parent $w -label "Include magnet layout in orbit plots: " -orientation horizontal \
      -variable includeMagnetLayout -buttonList "Yes No" -valueList "1 0"
    set plotVsSector 0
    APSRadioButtonFrame .vsSector -parent $w -label "Plot orbits vs sector number: " -orientation horizontal \
      -variable plotVsSector -buttonList "Yes No" -valueList "1 0"
    
    APSButton .plotSum -parent $w -text "Plot Sum vs Time" -command "PlotSignals -signal sum"
    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 .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"
    APSButton .plotXYO -parent $w -text "Plot x/y Orbits" -command "PlotSignals -signal xy -orbit 1"
    APSButton .plotSO -parent $w -text "Plot sum Orbits" -command "PlotSignals -signal sum -orbit 1"
}

proc MakePreLossAnalysisFrame {args} {
    set parent ""
    if {[APSStrictParseArguments {parent}] || ![string length $parent]} {
        return -code error "MakePreLossAnalysisFrame: invalid arguments"
    }

    set w $parent
    
    global analysisTimeLimit groupingMode orbitSparsingInterval orbitPlotStartTime
    global freqAxisMode FFTAxisMode orbitOffsetMode
    set analysisTimeLimit -0.01
    set orbitSparsingInterval 120
    set orbitPlotStartTime -100
    set freqAxisMode x=log,x=special
    set FFTAxisMode y=log,y=special
    
    APSLabeledEntry .analysisTimeLimit2 -parent $w -label "Orbit plot start time relative to loss time (ms): " \
      -width 10 -type real -textVariable orbitPlotStartTime \
      -contextHelp "Enter the time at which orbit plots start relative to the loss time."
    APSLabeledEntry .orbitSparsing -parent $w -label "Orbit sparsing interval (turns): " \
      -width 10 -type integer -textVariable orbitSparsingInterval \
      -contextHelp "Enter the sparsing interval for orbit plots. If too small, there will be thousands of plot panels!"
    
    APSRadioButtonFrame .offset -parent $w -label "Offset orbit data by first orbit: " -orientation horizontal \
      -variable orbitOffsetMode -buttonList "Yes No" -valueList "1 0"

    APSLabeledEntry .analysisTimeLimit1 -parent $w -label "FFT end time relative to loss time (ms): " \
      -width 10 -type real -textVariable analysisTimeLimit \
      -contextHelp "Enter the time at which analysis ends relative to the loss time."
    APSRadioButtonFrame .grouping -parent $w -label "Grouping mode for FFT plots: " -orientation horizontal \
      -variable groupingMode -buttonList "Sector Location" -valueList "sector location"
    APSRadioButtonFrame .xloglin -parent $w -label "Mode for frequency axis: " -orientation horizontal \
      -variable freqAxisMode -buttonList "linear log auto-log" -valueList "x=lin x=log,x=special x=autolog"
    APSRadioButtonFrame .yloglin -parent $w -label "Mode for FFT/PSD axis: " -orientation horizontal \
      -variable FFTAxisMode -buttonList "linear log auto-log" -valueList "y=lin y=log,y=special y=autolog"
    
    APSButton .fftx -parent $w -text "FFT x" -command "PlotFFTSignals -signal x -mode fft"
    APSButton .ffty -parent $w -text "FFT y" -command "PlotFFTSignals -signal y -mode fft"
    APSButton .sipsdx -parent $w -text "PSD x" -command "PlotFFTSignals -signal x -mode sipsd"
    APSButton .sipsdy -parent $w -text "PSD y" -command "PlotFFTSignals -signal y -mode sipsd"
    APSButton .plotXO -parent $w -text "Plot x Orbits" -command "PlotSignals -signal x -orbit 1 -postLoss 0"
    APSButton .plotYO -parent $w -text "Plot y Orbits" -command "PlotSignals -signal y -orbit 1 -postLoss 0"
    APSButton .plotXYO -parent $w -text "Plot x/y Orbits" -command "PlotSignals -signal xy -orbit 1 -postLoss 0"
    APSButton .locate -parent $w -text "Locate motion source" -command "LocateMotionSource"
}

set widgetList [APSTabFrame .tf1 -parent .userFrame -labelList {Date/Time/Event "BPM Selection"} \
                  -width 1000 -height 320]

MakeDateTimeFrame .dt -parent [lindex $widgetList 0]

set BPMList {A:P0 A:P1 A:P2 A:P3 A:P4 A:P5 A:P6 B:P6 B:P5 B:P4 B:P3 B:P2 B:P1 B:P0}
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]

APSSRSectorButtons .bpmButtons -parent [lindex $widgetList 1] -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

set widgetList [APSTabFrame .tf2 -parent .userFrame -labelList {"Preparation" "Plots Near Loss Time" "Analysis Before Loss"} \
                  -width 140 -height 210]

MakePreparationFrame -parent [lindex $widgetList 0]
MakeLossTimePlotsFrame -parent [lindex $widgetList 1]
MakePreLossAnalysisFrame -parent [lindex $widgetList 2]

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 FindFilesForSelectedBPMs {args} {
    if [APSStrictParseArguments {bpmList}] {
        return -code error "FindFilesForSelectedBPMs: invalid arguments"
    }
    global eventFileList
    if ![llength $eventFileList] {
        SetMainStatus "Select an event first"
        return [list]
    }
    if ![llength $bpmList] {
        SetMainStatus "Select some BPMs"
        return [list]
    }
    SetMainStatus [join $bpmList ,]
    set neededFileList [list]
    foreach eventFile $eventFileList {
        set needed 0
        set columnList [APSGetSDDSNames -fileName $eventFile -class column]
        foreach bpm $bpmList {
            if {[lsearch -glob $columnList ${bpm}:*] > -1} {
                set needed 1
                break
            }
        }
        if $needed {
            lappend neededFileList $eventFile
        }
    }

    set offsetList [concat [APSReplicateItem -item -1 -number 9] [APSReplicateItem -item 0 -number 14] [APSReplicateItem -item 1 -number 5]]
    set minusList [list A:P5 A:P6 B:P6 B:P5 B:P4 B:P3 B:P2 B:P1 B:P0]
    set zeroList [list A:P0 A:P1 A:P2 A:P3 A:P4 A:P5 A:P6 B:P6 B:P5 B:P4 B:P3 B:P2 B:P1 B:P0]
    set plusList [list A:P0 A:P1 A:P2 A:P3 A:P4 ]
    
    # Check for missing files
    set noFileList [list]
    foreach bpm $bpmList {
        regexp {S(..)(.:..)} $bpm all nn suffix
        scan $nn "%02d" sector
        if [expr $sector/2==0] {
            if [lsearch -exact $minusList $suffix]!=-1 {
                set fileSector [expr $sector+1]
            } elseif [lsearch -exact $plusList $suffix]!=-1 {
                set fileSector [expr $sector-1]
            } else {
                SetMainStatus "Problem figuring out which file $bpm should be in"
            }
        } else {
            set fileSector $sector
        }
        if [lsearch -glob $neededFileList [format *.s%02d.* $fileSector]]==-1 {
            lappend noFileList $bpm
        }
    }
    if [llength $noFileList] {
        SetMainStatus "No file found for $noFileList"
    }
    
    return $neededFileList
}

proc FindLossTime {} {
    global BPMList eventChoice eventLabel timeROI lossTimeFound
    set bpmList [CreateList -rootname bpmSelection -suffixList $BPMList]
    if ![llength [set fileList [FindFilesForSelectedBPMs -bpmList $bpmList]]] {
        return
    }
    if {$timeROI(LL) > $timeROI(UL)} {
        SetMainStatus "Invalid ROI Relative Time range: Min=$timeROI(LL) Max=$timeROI(UL)\nCheck the entries in tab \"Plots Near Loss Time\""
        return
    }
    if [llength $fileList]>2 {
        if {[set choice [APSMultipleChoice .choose -question "This may take a long time with [llength $fileList] files." \
                           -labelList [list Proceed Abort "Use 1st File"] \
                           -returnList [list 1 0 2]]]==0} {
            return
        }
        if $choice==2 {
            set fileList [lindex $fileList 0]
        }
    }
    SetMainStatus "[llength $fileList] files being used to determine loss time"
    set sumList [list]
    foreach bpm $bpmList {
        lappend sumList $bpm:sum
    }
    set tmpRoot /tmp/[APSTmpString]
    APSAddToTempFileList $tmpRoot.1
    if {[llength $fileList]>1} {
        if [catch {eval exec sddsxref $fileList -pipe=out \
                     | sddsconvert -pipe=in $tmpRoot.1  -retain=col,time,[join $sumList ,]} result] {
            return -code error "FindLossTime (1): $result"
        }
    } else {
        if [catch {exec sddsconvert $fileList $tmpRoot.1 -retain=col,time,[join $sumList ,]} result] {
            return -code error "FindLossTime (2): $result"
        }
    }
    if [catch {exec sddsprocess $tmpRoot.1 -pipe=out -process=*:sum,min,%sMin -process=*:sum,spread,%sSpread \
                 "-redefine=col,%s,%s %sMin - %sSpread /,select=*:sum" \
                 | sddsprocess -pipe -process=*:sum,zerocrossing,%sZeroCrossing,offset=-0.5,functionOf=time \
                 | sddscollapse -pipe \
                 | sddscollect -pipe -collect=suffix=ZeroCrossing \
                 | sddsprocess -pipe -process=ZeroCrossing,median,%sMedian \
                 | sdds2stream -pipe -parameter=ZeroCrossingMedian} result] {
        return -code error "FindLossTime (3): $result"
    }
    SetMainStatus "Time reference of loss $result s"
    set timeROI(Center) $result
    set timeROI(Min) [expr $result+$timeROI(LL)/1e3]
    set timeROI(Max) [expr $result+$timeROI(UL)/1e3]
    set ts1 [clock format [expr int($result)] -format "%Y/%m/%d %H:%M:%S"]
    regexp {0(..........).*} [expr $result-int($result)] all ts2
    set timeROI(TimeStamp) $ts1$ts2
    set lossTimeFound 1
    
    eval exec sddsplot -separate=20 -filter=col,time,$timeROI(Min),$timeROI(Max) -column=time,([join $sumList ,]) $tmpRoot.1 \
      -offset=xchange=-$timeROI(Center) -graph=line,vary -order=spect -legend "{-title=time offset by $timeROI(Center) s}" \
      "{-topline=$eventLabel}" &
}


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

    global lossTimeFound
    if [expr $lossTimeFound==0] {
        SetMainStatus "Please find the loss time before proceeding."
        return
    }
    
    switch $signal {
        x -
        y -
        sum {
            set signalPattern $signal
        }
        xy {
            set signalPattern \[xy\]
        }
        default {
            return -code error "PlotSignals: invalid signal $signal"
        }
    }
    set offsetList [concat [APSReplicateItem -number 9 -item -1] [APSReplicateItem -number 14 -item 0] \
                      [APSReplicateItem -number 5 -item 1] ]
    set suffixList [list A:P5 A:P6 B:P0 B:P1 B:P2 B:P3 B:P4 B:P5 B:P6 \
                      A:P0 A:P1 A:P2 A:P3 A:P4 A:P5 A:P6 B:P0 B:P1 B:P2 B:P3 B:P4 B:P5 B:P6 A:P0 A:P1 A:P2 A:P3 A:P4]
    set missingList [list S34A:P5 S36A:P5 S38B:P3 S38B:P0 S39A:P0 S39A:P2 S39A:P3]

    if [llength $offsetList]!=[llength $suffixList] {
        return -code error "offsetList and suffixList have different lengths"
    }
    
    set yLabel(sum) "Sum (counts)"
    set yLabel(x) "x (\$gm\$rm)"
    set yLabel(y) "y (\$gm\$rm)"
    
    global BPMList eventChoice eventLabel timeROI sumLimit groupingMode analysisTimeLimit orbitSparsingInterval
    global orbitPlotStartTime orbitOffsetMode includeMagnetLayout plotVsSector
    
    set bpmList [CreateList -rootname bpmSelection -suffixList $BPMList]
    if ![llength [set fileList [FindFilesForSelectedBPMs -bpmList $bpmList]]] {
        return
    }
    SetMainStatus "[llength $fileList] files being plotted"
    
    set timeROI(Min) [expr $timeROI(Center)+$timeROI(LL)/1e3]
    set timeROI(Max) [expr $timeROI(Center)+$timeROI(UL)/1e3]
    if $postLoss {
        set tLimit1 $timeROI(Min)
        set tLimit2 $timeROI(Max)
        set sparsing 1
    } else {
        set tLimit1 [expr $timeROI(Center)+$orbitPlotStartTime/1e3]
        set tLimit2 [expr $timeROI(Center)+$analysisTimeLimit/1e3]
        set sparsing $orbitSparsingInterval
    }
    
    if $orbit {
        if [llength $bpmList]<2 {
            SetMainStatus "Orbit plotting doesn't make sense with less than 2 BPMs"
            return
        }
        # Use the first sum signal for filtering
        set sumSignal [lindex $bpmList 0]:sum
        set signalList $sumSignal
        foreach bpm $bpmList {
            lappend signalList $bpm:$signalPattern
        }
        set tmpRoot /tmp/[APSTmpString]
        APSAddToTempFileList $tmpRoot.1 $tmpRoot.2
        SetMainStatus "Applying sparsing and time filter"
        set tmpFileList [list]
        foreach filename $fileList {
            set tmpFile $tmpRoot.[file tail $filename]
            if {$orbitOffsetMode} {
                if {[string compare sum $signalPattern]} {
                    if [catch {exec sddsprocess -sparse=$sparsing $filename $tmpFile -filter=column,time,$tLimit1,$tLimit2 \
                                 -define=col,index,i_row,type=long -process=*:$signalPattern,first,%s0 \
                                 "-redefine=col,%s,%s %s0 -,units=um,select=*:$signalPattern" \
                             } result] {
                        return -code error "PlotSignals (0): $result"
                    }
                } else {
                    if [catch {exec sddsprocess -sparse=$sparsing $filename $tmpFile -filter=column,time,$tLimit1,$tLimit2 \
                                 -define=col,index,i_row,type=long -process=*:$signalPattern,first,%s0 \
                                 "-redefine=col,%s,%s %s0 /,units=,select=*:$signalPattern" \
                             } result] {
                        return -code error "PlotSignals (0): $result"
                    }
                }
            } else {
                if [catch {exec sddsprocess -sparse=$sparsing $filename $tmpFile -filter=column,time,$tLimit1,$tLimit2 \
                             -define=col,index,i_row,type=long \
                         } result] {
                    return -code error "PlotSignals (0): $result"
                }
            }
            SetMainStatus "$filename: [exec sdds2stream -rows $tmpFile]"
            lappend tmpFileList $tmpFile
            APSAddToTempFileList $tmpFile
        }
        SetMainStatus "Gathering files together"
        if [llength $fileList]==1 {
            if [catch {exec sddsconvert $tmpFileList $tmpRoot.1 "-retain=col,time,[join $signalList ,]" } result] {
                return -code error "PlotSignals (1): $result"
            }
        } else {
            if [catch {eval exec sddsxref $tmpFileList -pipe=out -take=* -equate=index -nowarning \
                         | sddsconvert -pipe=in $tmpRoot.1 "{-retain=col,time,[join $signalList ,]}" } result] {
                return -code error "PlotSignals (2): $result"
            }
        }
        eval file delete $tmpFileList
        SetMainStatus "Organizing data into orbits, filtered on $sumSignal"
        if [string compare $signal xy]==0 {
            set collectOptList [list -collect=suffix=:x,column=x -collect=suffix=:y,column=y]
        } else {
            set collectOptList -collect=suffix=:$signal,column=$signal
        }
        if [catch {eval exec sddsrowstats $tmpRoot.1 -pipe=out -mean=AverageSum,*:sum \
                     | sddsprocess -pipe -filter=col,$sumSignal,$sumLimit,1e100 \
                     "{-define=column,dt,time $timeROI(Center) - 1e6 *,units=\$gm\$rs}" \
                     "{-print=column,Label1,\$gD\$rt: %.3f\$gm\$rs  Ave. Sum: %.3g,dt,AverageSum}" \
                     | sddscollect -pipe $collectOptList \
                     | sddsxref -pipe=in /home/helios/oagData/sr/lattices/default/aps.twi $tmpRoot.2 -reuse=page \
                     -match=Rootname=ElementName -take=s} result] {
            return -code error "PlotSignals (3): $result"
        }
        SetMainStatus "Plotting orbits"
        set extraRequestList [list]
        if $includeMagnetLayout {
            lappend extraRequestList -column=s,Profile /home/helios/oagData/sr/lattices/default/aps.mag -omnipresent \
              -overlay=xmode=norm,yfact=0.04 -graph=line,type=1
        }
        set optionList [list]
        if $plotVsSector {
            lappend optionList -convert=col,s,Sector,m,[expr 40/1.103608783783009e+03]
        }
        switch $signal {
            x -
            y -
            sum {
                eval exec sddsplot $optionList -column=s,$signal $tmpRoot.2 -split=page -separate -same -graph=sym,scale=0.5 \
                  "{-topline=$eventLabel}" -title=@Label1 $extraRequestList &
            }
            xy {
                eval exec sddsplot $optionList -split=page -groupby=page,request -separate=request -same \
                  -graph=sym,scale=0.5 -layout=1,2 -join=x "{-topline=$eventLabel}" \
                  -column=s,x $tmpRoot.2 -column=s,y $tmpRoot.2 -title=@Label1 \
                  $extraRequestList &
            }
        }
    } else {
        # plot vs time
        switch $groupingMode {
            sector {
                for {set sector 1} {$sector<41} {incr sector} {
                    set optionList($sector) [list]
                }
                set optionList(all) [list]
                foreach filename $fileList {
                    regexp {(.*)\.s(..)\.(.*).sdds} $filename all dummy nn dummy
                    scan $nn %ld sector
                    foreach offset $offsetList suffix $suffixList {
                        set bpmSector [expr ($sector-1+$offset)%40+1]
                        set bpm [format S%02d%s $bpmSector $suffix]
                        if {[lsearch $missingList $bpm]==-1 && [lsearch $bpmList $bpm]!=-1} {
                            lappend optionList($bpmSector) -column=time,$bpm:$signal \
                              -filter=column,$bpm:sum,$sumLimit,1e10 $filename
                        }
                    }
                }
                for {set sector 1} {$sector<41} {incr sector} {
                    if [llength $optionList($sector)] {
                        eval lappend optionList(all) $optionList($sector) -end
                    }
                }
                eval exec sddsplot -filter=col,time,$tLimit1,$tLimit2 -offset=xchange=-$timeROI(Center) \
                  {"-ylabel=$yLabel($signal)"} "{-xlabel=time (s)}" -graph=line,vary -legend=edit=%/:$signal// \
                  "{-topline=$eventLabel}" "{-title=time offset by $timeROI(Center) s}" $optionList(all) &
            }
            location {
                global BPMList
                foreach type $BPMList {
                    for {set msector 0} {$msector<40} {incr msector} {
                        set optionList($type,$msector) [list]
                    }
                }
                set optionList(all) [list]
                foreach filename $fileList {
                    regexp {(.*)\.s(..)\.(.*).sdds} $filename all dummy nn dummy
                    scan $nn %ld sector
                    set msector [expr $sector%40]
                    foreach offset $offsetList suffix $suffixList {
                        set bpmSector [expr ($sector-1+$offset)%40+1]
                        set bpm [format S%02d%s $bpmSector $suffix]
                        if {[lsearch $missingList $bpm]==-1 && [lsearch $bpmList $bpm]!=-1} {
                            lappend optionList($suffix,$msector) -column=time,$bpm:$signal \
                              -filter=column,$bpm:sum,$sumLimit,1e10 $filename
                        }
                    }
                }
                foreach type $BPMList {
                    set optionList($type) [list]
                    for {set msector 0} {$msector<40} {incr msector} {
                        if [llength $optionList($type,$msector)] {
                            #SetMainStatus "optionList($type,$msector): $optionList($type,$msector)"
                            eval lappend optionList($type) $optionList($type,$msector)
                        }
                    }
                    if [llength $optionList($type)] {
                        #SetMainStatus "optionList($type): $optionList($type)"
                        eval lappend optionList(all) $optionList($type) -end
                    }
                }
                eval exec sddsplot -filter=col,time,$tLimit1,$tLimit2 -offset=xchange=-$timeROI(Center) \
                  {"-ylabel=$yLabel($signal)"} "{-xlabel=time (s)}" -graph=line,vary -legend=edit=%/:$signal// \
                  "{-topline=$eventLabel}" "{-title=time offset by $timeROI(Center) s}" $optionList(all) &
            }
        }
    }
}

proc PlotFFTSignals {args} {
    set signal ""
    set mode fft
    if [APSStrictParseArguments {signal mode}] {
        return -code error "PlotFFTSignals: invalid arguments"
    }

    global lossTimeFound
    if [expr $lossTimeFound==0] {
        SetMainStatus "Please find the loss time before proceeding."
        return
    }
    
    set offsetList [concat [APSReplicateItem -number 9 -item -1] [APSReplicateItem -number 14 -item 0] \
                      [APSReplicateItem -number 5 -item 1] ]
    set suffixList [list A:P5 A:P6 B:P0 B:P1 B:P2 B:P3 B:P4 B:P5 B:P6 \
                      A:P0 A:P1 A:P2 A:P3 A:P4 A:P5 A:P6 B:P0 B:P1 B:P2 B:P3 B:P4 B:P5 B:P6 A:P0 A:P1 A:P2 A:P3 A:P4]
    set missingList [list S34A:P5 S36A:P5 S38B:P3 S38B:P0 S39A:P0 S39A:P2 S39A:P3]

    if [llength $offsetList]!=[llength $suffixList] {
        return -code error "offsetList and suffixList have different lengths"
    }
    
    set yLabel(x,fft) "FFT x (\$gm\$rm)"
    set yLabel(y,fft) "FFT y (\$gm\$rm)"
    set yLabel(x,sipsd) "Sqrt Integ. PSD x (\$gm\$rm)"
    set yLabel(y,sipsd) "Sqrt Integ. PSD y (\$gm\$rm)"
    set prefix(fft) FFT
    set prefix(sipsd) SqrtIntegPSD
    set legendPart(fft) "FFT "
    set legendPart(sipsd) "Sqrt Integ PSD "
    
    global BPMList eventChoice eventLabel analysisTimeLimit groupingMode timeROI freqAxisMode FFTAxisMode
    set bpmList [CreateList -rootname bpmSelection -suffixList $BPMList]
    if ![llength [set fileList [FindFilesForSelectedBPMs -bpmList $bpmList]]] {
        return
    }
    SetMainStatus "[llength $fileList] files being analyzed"

    set tLimit [expr $timeROI(Center)+$analysisTimeLimit]
    set axisModes -mode=$freqAxisMode,$FFTAxisMode
    
    set tmpRoot /tmp/[APSTmpString]
    foreach filename $fileList {
        SetMainStatus "Taking FFTs for $filename"
        set FFTList [list]
        regexp {(.*)\.s(..)\.(.*).sdds} $filename all dummy nn dummy
        scan $nn %ld sector
        foreach offset $offsetList suffix $suffixList {
            set bpmSector [expr ($sector-1+$offset)%40+1]
            set bpm [format S%02d%s $bpmSector $suffix]
            if {[lsearch $missingList $bpm]==-1 && [lsearch $bpmList $bpm]!=-1} {
                lappend FFTList $bpm:$signal
            }
        }
        if [catch {exec sddsprocess $filename -pipe=out -filter=col,time,0,$tLimit \
                     | sddsfft -pipe=in $tmpRoot.[file tail $filename] -column=time,[join $FFTList ,] \
                     -nowarnings -truncate -suppress -psd=integ} result] {
            return -code error "PlotFFTSignals (0): $result"
        }
        set FFTResult($filename) $tmpRoot.[file tail $filename]
        APSAddToTempFileList $tmpRoot.[file tail $filename]
    }

    switch $groupingMode {
        sector {
            for {set sector 1} {$sector<41} {incr sector} {
                set optionList($sector) [list]
            }
            set optionList(all) [list]
            foreach filename $fileList {
                regexp {(.*)\.s(..)\.(.*).sdds} $filename all dummy nn dummy
                scan $nn %ld sector
                foreach offset $offsetList suffix $suffixList {
                    set bpmSector [expr ($sector-1+$offset)%40+1]
                    set bpm [format S%02d%s $bpmSector $suffix]
                    if {[lsearch $missingList $bpm]==-1 && [lsearch $bpmList $bpm]!=-1} {
                        lappend optionList($bpmSector) -column=f,$prefix($mode)$bpm:$signal $FFTResult($filename) \
                          "-title=S$nn BPMs"
                    }
                }
            }
            for {set sector 1} {$sector<41} {incr sector} {
                if [llength $optionList($sector)] {
                    eval lappend optionList(all) $optionList($sector) -end
                }
            }
            eval exec sddsplot $axisModes "{-ylabel=$yLabel($signal,$mode)}" -graph=line,vary \
              "{-legend=edit=%/$legendPart($mode)//%/:$signal//}" \
              "{-topline=$eventLabel}" $optionList(all) &
        }
        location {
            global BPMList
            foreach type $BPMList {
                for {set msector 0} {$msector<40} {incr msector} {
                    set optionList($type,$msector) [list]
                }
            }
            set optionList(all) [list]
            foreach filename $fileList {
                regexp {(.*)\.s(..)\.(.*).sdds} $filename all dummy nn dummy
                scan $nn %ld sector
                set msector [expr $sector%40]
                foreach offset $offsetList suffix $suffixList {
                    set bpmSector [expr ($sector-1+$offset)%40+1]
                    set bpm [format S%02d%s $bpmSector $suffix]
                    if {[lsearch $missingList $bpm]==-1 && [lsearch $bpmList $bpm]!=-1} {
                        lappend optionList($suffix,$msector) -column=f,$prefix($mode)$bpm:$signal $FFTResult($filename) \
                          "-title=$suffix BPMs"
                    }
                }
            }
            foreach type $BPMList {
                set optionList($type) [list]
                for {set msector 0} {$msector<40} {incr msector} {
                    if [llength $optionList($type,$msector)] {
                        #SetMainStatus "optionList($type,$msector): $optionList($type,$msector)"
                        eval lappend optionList($type) $optionList($type,$msector)
                    }
                }
                if [llength $optionList($type)] {
                    #SetMainStatus "optionList($type): $optionList($type)"
                    eval lappend optionList(all) $optionList($type) -end
                }
            }
            eval exec sddsplot $axisModes {"-ylabel=$yLabel($signal,$mode)"} -graph=line,vary \
              "{-legend=edit=%/$legendPart($mode)//%/:$signal//}" \
              "{-topline=$eventLabel}" $optionList(all) &
        }
    }
}


proc LocateMotionSource {} {
    global lossTimeFound
    if [expr $lossTimeFound==0] {
        SetMainStatus "Please find the loss time before proceeding."
        return
    }
    
    set offsetList [concat [APSReplicateItem -number 9 -item -1] [APSReplicateItem -number 14 -item 0] \
                      [APSReplicateItem -number 5 -item 1] ]
    set suffixList [list A:P5 A:P6 B:P0 B:P1 B:P2 B:P3 B:P4 B:P5 B:P6 \
                      A:P0 A:P1 A:P2 A:P3 A:P4 A:P5 A:P6 B:P0 B:P1 B:P2 B:P3 B:P4 B:P5 B:P6 A:P0 A:P1 A:P2 A:P3 A:P4]
    set missingList [list S34A:P5 S36A:P5 S38B:P3 S38B:P0 S39A:P0 S39A:P2 S39A:P3]

    if [llength $offsetList]!=[llength $suffixList] {
        return -code error "offsetList and suffixList have different lengths"
    }
    
    global BPMList eventChoice eventLabel timeROI sumLimit groupingMode analysisTimeLimit orbitSparsingInterval
    global orbitPlotStartTime orbitOffsetMode
    
    set bpmList [CreateList -rootname bpmSelection -suffixList $BPMList]
    if ![llength [set fileList [FindFilesForSelectedBPMs -bpmList $bpmList]]] {
        return
    }
    SetMainStatus "[llength $fileList] files being plotted"
    
    set tLimit1 [expr $timeROI(Center)+$orbitPlotStartTime/1e3]
    set tLimit2 [expr $timeROI(Center)+$analysisTimeLimit/1e3]
    set sparsing $orbitSparsingInterval
    
    if [llength $bpmList]<400 {
        SetMainStatus "Orbit analysis doesn't make sense with less than 400 BPMs"
        return
    }
    # Use the first sum signal for filtering
    set sumSignal [lindex $bpmList 0]:sum
    set tmpRoot /tmp/[APSTmpString]
    APSAddToTempFileList $tmpRoot.1 $tmpRoot.2
    SetMainStatus "Applying sparsing and time filter"
    set tmpFileList [list]
    foreach filename $fileList {
        set tmpFile $tmpRoot.[file tail $filename]
        if [catch {exec sddsprocess -sparse=$sparsing $filename $tmpFile -filter=column,time,$tLimit1,$tLimit2 \
                     -define=col,index,i_row,type=long -process=*:\[xy\],first,%s0 \
                     "-redefine=col,%s,%s %s0 -,units=um,select=*:\[xy\]" } result] {
            return -code error "LocateMotionSource (0): $result"
        }
        SetMainStatus "$filename: [exec sdds2stream -rows $tmpFile]"
        lappend tmpFileList $tmpFile
        APSAddToTempFileList $tmpFile
    }
    SetMainStatus "Gathering files together"
    if [catch {eval exec sddsxref $tmpFileList -pipe=out -take=* -equate=index -nowarning \
                 | sddsconvert -pipe=in $tmpRoot.1 "{-retain=col,time,$sumSignal,*:x,*:y}" } result] {
        return -code error "LocateMotionSource (2): $result"
    }
    eval file delete $tmpFileList
    SetMainStatus "Organizing data into orbits and filtering with $sumSignal"
    if [catch {eval exec sddsprocess $tmpRoot.1 -pipe=out -filter=col,$sumSignal,$sumLimit,1e100 \
                 | sddscollect -pipe=in $tmpRoot.2 -collect=suffix=:x,column=x -collect=suffix=:y,column=y} result] {
        return -code error "LocateMotionSource (3): $result"
    }
    foreach xy {x y} hv {h v} HV {H V} {
        SetMainStatus "Performing response matrix overlap analysis for $xy plane"
        if {![file exists [set rm /home/helios/oagData/sr/lattices/default/aps.fl${hv}rm]]} {
            return -code error "LocateMotionSource (4): response matrix $rm not found"
        }
        if [catch {exec sddsxref $tmpRoot.2 $rm -pipe=out -reuse=page -match=Rootname=BPMName -take=s,*${HV}* \
                     | sddsprocess -pipe "-define=col,${xy}.%s,$xy %s *,select=*${HV}*" \
                     | sddsprocess -pipe -process=${xy}.*,stand,%sStDev \
                     | sddsconvert -pipe -delete=parameter,OriginalPage \
                     | sddscollapse -pipe \
                     | sddscollect -pipe -collect=suffix=StDev \
                     | sddssort -pipe -column=StDev,decr \
                     | sddsprocess -pipe -define=col,Rank,i_row,type=short \
                     | sddssort -pipe -column=Rootname \
                     | sddsenvelope -pipe -copy=Rootname -median=Rank -mean=Rank -median=StDev -mean=StDev\
                     | sddssort -pipe -column=RankMedian \
                     | sddsprintout -pipe=in "-title=$xy-plane overlap analysis for $eventLabel" $tmpRoot.$xy \
                     -column=Rootname,label=Product \
                     -column=RankMedian,format=%10.2f -column=RankMean,format=%10.2f \
                     -column=StDevMedian,format=%10.4f -column=StDevMean,format=%10.4f} result] {
            return -code error "LocateMotionSource (5): $result"
        }
        APSFileDisplayWindow .${xy}Overlap -fileName $tmpRoot.$xy -modal 0 -deleteOnClose 1 -width 80
    }
}

ResetIdList

SetMainStatus "Use the \"Find Events\" button to find events in the selected time range."
SetMainStatus "Use the \"Find Loss Time\" button to reduce the time range."
