#!/bin/sh  
# \
  exec oagwish "$0" "$@"

# $Log: not supported by cvs2svn $
# Revision 1.13  2007/12/20 20:36:11  borland
# Included SRrfFast logger in the search.
#
# Revision 1.12  2005/08/23 20:18:53  soliday
# Added APSRenameExecToAPSBGExec.
#
# Revision 1.11  2001/09/26 14:31:15  soliday
# Skips glitchloggers.
#
# Revision 1.10  2000/03/10 19:17:17  borland
# Added capability to search for control names listed in an SDDS file.
#
# Revision 1.9  1999/10/16 19:06:29  borland
# Now says how many channels are found.
#
# Revision 1.8  1998/10/26 22:19:24  soliday
# Modified packing order and simplified tcl code.
#
# Revision 1.7 1998/09/08 14:54:22  borland
# Fixed typo: outputFildID instead of outputFileID
#
# Revision 1.6  1998/07/08 14:00:30  borland
# New version per R. Soliday that allows comma-separated lists in the
# match and exclude entry boxes.
#
# Revision 1.5  1998/06/19 22:53:51  borland
# New version per R. Soliday that combines functions of searching for
# data logs entries, alarm log entries, and request file entries.
#
# Revision 1.4  1998/06/15 15:18:57  borland
# Per R. Soliday: added exclusion filter.
#
# Revision 1.3  1998/06/12 20:34:17  borland
# Improved version per R. Soliday.  Caches the combined monitor files for
# better performance.  Allows re-searching previous results.
#
# Revision 1.2  1998/06/09 18:37:36  borland
# Amplified the context help for two buttonss.
#
# Revision 1.1  1998/06/09 18:34:29  borland
# First version per R. Soliday.
#

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)]

set apsttk 1
APSRenameExecToAPSBGExec

set CVSRevisionAuthor "\$Revision: 1.14 $ \$Author: soliday $"

proc APSLabeledEntryGrid { widget args } {
    global apsContextHelp apsttk
    set parent ""
    set noPack 0
    set packOption "-side top -fill x"
    set labelList ""
    set variableList ""
    set contextHelp ""
    APSStrictParseArguments {parent noPack packOption labelList variableList contextHelp}
    if {[info exists apsttk] && ($apsttk != 0)} {
        set ttkPrefix ttk::
    } else {
        set ttkPrefix ""
    }
    
    if {$contextHelp != ""} {
        set apsContextHelp($parent$widget) $contextHelp
    }
    if {$parent == ""} {
        toplevel $widget -bd 1
        wm title $widget $label
    } else {
        ${ttkPrefix}frame $parent$widget -borderwidth 1
        if {!$noPack} {
            eval pack $parent$widget $packOption
        }
    }
    set row 0
    foreach label $labelList variable $variableList {
        ${ttkPrefix}label $parent$widget.label$row -text $label
        upvar \#0 $variable textVar
        if {![info exists textVar]} {
            set textVar ""
        }
        ${ttkPrefix}entry $parent$widget.entry$row -textvariable $variable
        grid $parent$widget.label$row -column 0 -row $row -sticky e
        grid $parent$widget.entry$row -column 1 -row $row -sticky ew
        incr row
    }
    grid columnconfigure $parent$widget 1 -weight 1
}

#
# Start datalogger search
#
proc startDL {} {
    global CVSRevisionAuthor
    APSApplication . -name "Search Data Loggers" -version $CVSRevisionAuthor \
      -overview "Search active data loggers for string match\n\n\
   This program opens the\n\
   /home/helios/oagData/dataLoggerConfig\n\
   /timeSeries.config file and locates\n\
   filenames of the data loggers with a\n\
   doRun value of 1.  It then searches\n\
   the data logger files for matches to\n\
   the given string in the ControlName\n\
   column and/or the ReadbackName column.\n\
   All data loggers that have a match are\n\
 then displayed."
    
    set w .userFrame
    
    set passed {-SearchString $searchString -ExcludeString $excludeString  \
                  -Caller DataLoggers -Normal "searchRN search" -SearchFile $searchFile}
    set wb ${w}.buttonFrame
    ttk::frame $wb
    pack $wb -fill x -side bottom
    APSButton .searchCN -parent $wb -text "Search ControlName" -width "" \
      -command "ScanData -Button ControlName $passed" \
      -contextHelp "Search for active data loggers with specified value for ControlName. \
   The ControlName is the process variable name used by EPICS."
    
    APSButton .searchRN -parent $wb -text "Search ReadbackName" -width "" \
      -command "ScanData -Button ReadbackName $passed" \
      -contextHelp "Search for active data loggers with specified value for ReadbackName. \
   The ReadbackName is the name used for a process variable in the data logger files."
    APSButton .search -parent $wb -text "Search both" -width "" \
      -command "ScanData -Button Both $passed" \
      -contextHelp "Search for active data loggers with specified value for ControlName and ReadbackName"
    APSButton .searchResults -parent $wb -text "Search Results" -width "" \
      -command "ScanData -Button Results $passed" \
      -contextHelp "Search previous Results"
    APSButton .abort -parent $wb -text "Abort" -width "" \
      -command Abort \
      -contextHelp "Abort a search"
    
    APSLabeledEntryGrid .entryFrame -parent $w -packOption "-fill x -side bottom" \
      -labelList {"Search for:" "Exclusion filter:" "From file:"} \
      -variableList "searchString excludeString searchFile" \
      -contextHelp "Enter string (wildcards accepted) to be searched for in active data loggers.  If the file option is used, the script matches the ControlName data from the file you supply with the same column in the data logger files."
    
    APSScrolledStatus .status -parent $w -textVariable status \
      -packOption "-fill both -expand true" -width 60
    pack configure ${w}.status.frame -fill both -expand true
    
    APSSetVarAndUpdate status "Caching files..."
    FindDataLoggers
}

#
# Start SCR request file search
#
proc startRF {} {
    global CVSRevisionAuthor
    APSApplication . -name "Search Request Files" -version $CVSRevisionAuthor \
      -overview "Search request files for string match\n\n\
   This program searches the request\n\
   files for matches to the given string\n\
   in the ControlName column.  The\n\
   ControlName, Category, Beamline, and\n\
   System Description columns are then\n\
   displayed."
    
    set w .userFrame
    set wb ${w}.buttonFrame
    ttk::frame $wb
    pack $wb -fill x -side bottom
    set passed {-SearchString $searchString -ExcludeString $excludeString -Caller RequestFiles -SearchFile $searchFile}
    
    APSButton .searchCN -parent $wb -text "Search ControlName" \
      -command  "ScanData $passed -Button ControlName" \
      -contextHelp "Search request files with specified value for ControlName. \
   The ControlName is the process variable name used by EPICS."
    
    APSButton .searchResults -parent $wb -text "Search Results" \
      -command  "ScanData $passed -Button Results" \
      -contextHelp "Search previous results"
    
    APSButton .abort -parent $wb -text "Abort" \
      -command Abort \
      -contextHelp "Abort a search"
    
    APSLabeledEntryGrid .entryFrame -parent $w -packOption "-fill x -side bottom" \
      -labelList {"Search for:" "Exclusion filter:"} \
      -variableList "searchString excludeString searchFile" \
      -contextHelp "Enter string (wildcards accepted) to be searched for in request files. If the file option is used, the script matches the ControlName data from the file you supply with the ControlName data in the request files."
    
    APSScrolledStatus .status -parent $w -textVariable status \
      -packOption "-fill both -expand true" -width 60
    pack configure ${w}.status.frame -fill both -expand true
    
    APSSCRDefineVariables
    APSSetVarAndUpdate status "Caching files..."
    FindRequestFiles
}

#
# Start alarm loggers search
#
proc startAL {} {
    global CVSRevisionAuthor
    APSApplication . -name "Search Alarm Loggers" -version $CVSRevisionAuthor \
      -overview "Search alarm logger files for string match\n\n\
   This program searches the alarm logger\n\
   files for matches to the given string\n\
   in the ControlName column.  The\n\
   ControlName, Category, System Description\n\
   columns are then displayed."
    
    set w .userFrame
    set wb ${w}.buttonFrame
    ttk::frame $wb
    pack $wb -fill x -side bottom
    set passed {-SearchString $searchString -ExcludeString $excludeString -Caller AlarmLoggers -SearchFile $searchFile}
    
    APSButton .searchCN -parent $wb -text "Search ControlName" \
      -command  "ScanData $passed -Button ControlName" \
      -contextHelp "Search request files with specified value for ControlName. \
   The ControlName is the process variable name used by EPICS."
    
    APSButton .searchResults -parent $wb -text "Search Results" \
      -command  "ScanData $passed -Button Results" \
      -contextHelp "Search previous results"
    
    APSButton .abort -parent $wb -text "Abort" \
      -command Abort \
      -contextHelp "Abort a search"
    
    APSLabeledEntryGrid .entryFrame -parent $w -packOption "-side bottom -fill x" \
      -labelList {"Search for:" "Exclusion filter:"} \
      -variableList "searchString excludeString searchFile" \
      -contextHelp "Enter string (wildcards accepted) to be searched for in alarm files.  If the filename option is used, the script matches the ControlName data from the file you supply with the same column from the alarm logger files."
    
    APSScrolledStatus .status -parent $w -textVariable status \
      -packOption "-fill both -expand true" -width 60
    pack configure ${w}.status.frame -fill both -expand true
    
    APSSetVarAndUpdate status "Caching files..."
    
    FindAlarmLoggers
}


#
# Collect file names to be cached for dataloggers
#
proc FindDataLoggers {} {
    global fileName Result time
    ButtonStates -ButtonsDisabled "searchCN searchRN search searchResults abort"
    RemoveVar -ClearVar "Result time"
    set fileName [APSTmpDir]/[APSTmpString]
    APSAddToTempFileList $fileName.1 $fileName.0 $fileName.2
    exec sddsmakedataset $fileName.2 \
      -column=monitorProgram,type=string -data=sddsmonitor \
      -column=GroupName,type=string "-data=SR rf Fast" \
      -column=subDirectory,type=string -data=monitoring/SRrfFast \
      -column=rootname,type=string -data=SRrfFast \
      -column=workstation,type=string -data=ravel \
      -column=sampleInterval,type=double -data=0.007 \
      -column=doRun,type=short -data=1 \
      -column=doOnePvPerFileRun,type=short -data=0 \
      -column=extraArguments,type=string "-data="

    APSExecModified -unixCommand \
      "sddscombine /home/helios/oagData/dataLoggerConfig/timeSeries.config $fileName.2 -merge -pipe=out \
            | sddsprocess -pipe \
	    -print=column,MonitorFile,/home/helios/oagData/%s/%s.mon,subDirectory,rootname \
	    -print=column,LoggingConditions,%s,extraArguments \
	    -match=column,GroupName=* \"-match=column,monitorProgram=sddsglitchlogger,!\" \"-match=column,monitorProgram=sddspvaglitchlogger,!\" \
	    | tee  $fileName.0 \
	    | sddsprocess -pipe=in $fileName.1 -filter=column,doRun,1,1,doOnePvPerFileRun,1,1,|" \
      -outputVariable Result \
      -outputFileId fID
    after 1000
    while {[file exists $fileName.1] == 0} {
        after 1000
    }
    sdds load $fileName.1 data
    set onePVList [lindex $data(Column.doOnePvPerFileRun) 0]
    set sampleInterval [lindex $data(Column.sampleInterval) 0]
    set newSampleInterval ""
    set loggingConditions [lindex $data(Column.LoggingConditions) 0]
    set newLoggingConditions ""
    foreach a $onePVList b $sampleInterval ea $loggingConditions {
        if {$a == 0} {
            lappend newSampleInterval $b
        } else {
            set b [expr {round($b)}]
            if {$b < 2} {
                set c 1
            } elseif {$b < 4} {
                set c 2
            } elseif {$b < 8} {
                set c 4
            } elseif {$b < 16} {
                set c 8
            } elseif {$b < 29.9} {
                set c 16
            } elseif {$b < 59.9} {
                set c 32
            } elseif {$b < 119.9} {
                set c 64
            } elseif {$b < 256} {
                set c 128
            } else {
                set c 256
            }
            lappend newSampleInterval $c
        }
        set i [string first "-con" $ea]
        if {$i >= 0} {
            set i [string first "=" $ea $i]
            if {$i >= 0} {
                set ea [lindex [string range $ea [expr $i + 1] end] 0]
                set i [string first "helios" $ea]
                if {$i >= 0} {
                    lappend newLoggingConditions "$ea"
                } else {
                    lappend newLoggingConditions "/home/helios/oagData/logging/inputFiles/$ea"
                }
            } else {
                lappend newLoggingConditions ""
            }
        } else {
            lappend newLoggingConditions ""
        }
    }

    set data(Column.sampleInterval) [list $newSampleInterval]
    set data(Column.LoggingConditions) [list $newLoggingConditions]
    sdds save $fileName.1 data

    tkwait variable Result
    if {$Result != 1} {
	APSSetVarAndUpdate status "Error: No active dataloggers: $Result"
	ButtonStates -ButtonsNormal "searchCN searchRN search" -ButtonsDisabled "searchResults abort"
	APSSetVarAndUpdate status Done
	return
    }
    if [catch {exec sdds2stream -column=MonitorFile $fileName.0} allfiles] {
        APSSetVarAndUpdate status "Unable to manipulate data in timeSeries.config file"
    }
    foreach file $allfiles {
	set time($file) [file mtime $file]
    }
    if [catch {exec sdds2stream -column=MonitorFile $fileName.1} files] {
        APSSetVarAndUpdate status "Unable to manipulate data in timeSeries.config file"
    }
    Combinefiles -Retain "ControlName,ReadbackName" -Files $files -Normal "searchRN search"
}

#
# Collect file names to be cached for 
# SCR Request files
#
proc FindRequestFiles {} {
    global apsSCRSystemDescription fileName time
    ButtonStates -ButtonsDisabled "searchCN searchResults abort"
    RemoveVar -ClearVar "time"
    set fileName [APSTmpDir]/[APSTmpString]
    APSAddToTempFileList $fileName.1
    set fileId [open $fileName.1 w 0600]
    puts $fileId "SDDS1\n&column name=MonitorFile, type=string &end\n&column name=Description, type=string &end"
    puts $fileId "&data mode=ascii &end\n[array size apsSCRSystemDescription]"
    foreach rootname "[array names apsSCRSystemDescription]" {
	set file /home/helios/oagData/SCR/requestFiles/$rootname.req
        append files "$file "
	set time($file) [file mtime $file]
        set description $apsSCRSystemDescription($rootname)
        set linebreak [string first "\n" $description]
        if {$linebreak != -1} {
            set description [string range $description 0 [expr $linebreak - 1]]
        }
        puts $fileId "/home/helios/oagData/SCR/requestFiles/$rootname.req \t \"$description\""
    }
    close $fileId
    Combinefiles -Retain "ControlName,Beamline,Category" -Files $files
}

#
# Collect file names for alarmloggers
#
proc FindAlarmLoggers {} {
    global time fileName
    ButtonStates -ButtonsDisabled "searchCN searchResults abort"
    RemoveVar -ClearVar "time"
    set files [glob /home/helios/oagData/Alarms/*.alog]
    foreach file $files {
	set time($file) [file mtime $file]
    }
    set fileName [APSTmpDir]/[APSTmpString]
    Combinefiles -Retain "ControlName" -Files $files
}

#
# Combine all the files that need to be searched
#
proc Combinefiles {args} {
    global Result fileName
    set Retain "*"
    set Normal ""
    APSStrictParseArguments {Retain Files Normal}
    RemoveVar -ClearVar "Result"
    APSAddToTempFileList $fileName
    APSExecModified -unixCommand "sddscombine $Files -pipe=out -retain=column,$Retain \
                 | sddsprocess -pipe -print=column,FileName,%s,Filename \
                 | sddscombine -pipe=in $fileName -merge" \
      -outputVariable Result \
      -outputFileId fID
    tkwait variable Result
    if {$Result != 1} {
	APSSetVarAndUpdate status "Error: $Result"
	ButtonStates -ButtonsDisabled "searchResults abort"
    }
    ButtonStates -ButtonsNormal "searchCN $Normal"
    APSSetVarAndUpdate status "Ready. [exec sdds2stream -rows $fileName]"
}

#
# Check to make sure that the data logger files have not changed.
# Search for match to string.
#
proc ScanData {args} {
    global time fileName lastfile fID Result lastbutton 
    set SearchString ""
    set ExcludeString ""
    set Normal ""
    set SearchFile ""
    APSStrictParseArguments {Button SearchString ExcludeString Caller Normal SearchFile}

    if {[string length $SearchString]==0 && [string length $SearchFile]==0} {
        return
    }  
    set current 0
    foreach file "[array names time]" {
        if {[string compare $time($file) [file mtime $file]] != 0 } {	
            set current 1
        }
    }
    # If a new file is detected then rerun recache the files
    if {$current == 1} {
        set Button ControlName
        APSSetVarAndUpdate status "$Caller have changed.  Recaching files..."
        Find$Caller
    }
    ButtonStates -ButtonsDisabled "searchCN searchResults $Normal" -ButtonsNormal "abort"
    switch -exact -- $Caller {
        DataLoggers {    
            set widthoutput 182
            set param4 "ReadbackName -column=FileName"
            set param5 "-column=ControlName,format=%-28s \
       -column=ReadbackName,format=%-28s \
       -column=GroupName,format=%-38s \
       -column=sampleInterval,format=%5.5g \
       -column=LoggingConditions,format=%-38s"
            set param6 "GroupName,workstation,sampleInterval,sampleInterval,LoggingConditions"
        } AlarmLoggers {
            set param4 "FileName"
            set param5 "-column=ControlName,format=%-30s \
       -column=FileName,format=%-40s"
            set widthoutput 80
        } default {
            set param4 "Category -column=Beamline"
            set param5 "-column=ControlName,format=%-30s \
       -column=Category,format=%-20s \
       -column=Beamline,format=%-10s \
       -column=Description,format=%-38s"
            set param6 "Description"
            set widthoutput 110
        }
    }
    APSSetVarAndUpdate status "Searching.  Please wait..."
    set file $fileName
    if [string length $SearchFile] {
        if {![file exists $SearchFile] || \
              [string compare [exec sddscheck $SearchFile] ok]!=0} {
            APSSetVarAndUpdate status "Not found or not SDDS file: $SearchFile"
            return
        }
        set columnsPresent [exec sddsquery $SearchFile]
        if [lsearch -exact $columnsPresent ControlName]==-1 {
            APSSetVarAndUpdate status "ControlName column not found in $SearchFile"
            return
        }
        set tmpSearch [APSTmpDir]/[APSTmpString]
        APSAddToTempFileList $tmpSearch $tmpSearch.sdds
        if [info exists param6] {
            set param7 "| sddsxref -pipe $fileName.1 -match=FileName=MonitorFile -take=$param6 -reuse=rows"
        } else {
            set param7 ""
        }
        if [catch {eval exec sddsselect $fileName $SearchFile -match=ControlName -pipe=out \
                     $param7 \
                     | tee $tmpSearch.sdds \
                     | sddsprintout -pipe=in $tmpSearch $param5} result] {
            APSSetVarAndUpdate status "$result"
            return
        }
        APSFileDisplayWindow [APSUniqueName .] \
          -fileName $tmpSearch -deleteOnClose 1 -comment "Searched for match to file $SearchFile" \
          -width $widthoutput -sddsExportableFile $tmpSearch.sdds
        ButtonStates -ButtonsNormal "searchCN searchRN search" -ButtonsDisabled "searchResults abort"
        return
    } 

    if {[string compare $Button Results] == 0} {
        set file $lastfile
        set Button $lastbutton
    }
    if [string length $SearchFile] {
        set Button ControlName
    } else {
        # Check which button was clicked
        set commaSearch [string first , $SearchString]
        set commaExclude [string first , $ExcludeString]
        set param -match=column
        # APSSetVarAndUpdate status $commaSearch
    }
    switch -exact -- $Button {
        ControlName {    
            foreach s [split $SearchString ,] {
                append param ",$Button=$s,|"
            }
            foreach s [split $ExcludeString ,] {
                append param ",$Button=$s,!,&"
            }
            set param2 "-column=ControlName -column=$param4"
        } ReadbackName {
            foreach s [split $SearchString ,] {
                append param ",$Button=$s,|"
            }
            foreach s [split $ExcludeString ,] {
                append param ",$Button=$s,!,&"
            }
            set param2 "-column=ReadbackName -column=ControlName -column=FileName"
        } default {
            foreach s [split $SearchString ,] {
                append param ",ReadbackName=$s,|,ControlName=$s,|"
            }
            foreach s [split $ExcludeString ,] {
                append param ",ReadbackName=$s,!,&,ControlName=$s,!,&"
            }
            set param2 "-column=ControlName -column=ReadbackName -column=FileName"
        }
    }
    # APSSetVarAndUpdate status $param
    set param "[string range $param 0 [expr [string first | $param] - 2]][string range $param [expr [string first | $param] + 1] end]"
    # APSSetVarAndUpdate status $param
    RemoveVar -ClearVar Result
    if [info exists param6] {
        set param7 "| sddsxref -pipe $fileName.1 -match=FileName=MonitorFile -take=$param6 -reuse=rows"
    } else {
        set param7 ""
    }
    APSAddToTempFileList $file.2 $file.3
    APSExecModified -unixCommand "sddsprocess $file -pipe=out $param \
   | sddssort -pipe $param2 \
   $param7 \
   | tee $file.2 \
   | sddsprintout -pipe=in $file.3 $param5 -width=0" \
      -outputVariable Result \
      -outputFileId fID
    tkwait variable Result
    if {$Result != 1} {
        APSSetVarAndUpdate status "No matches: $Result"
        ButtonStates -ButtonsNormal "searchCN $Normal" -ButtonsDisabled "searchResults abort"
        return
    } else {
        APSFileDisplayWindow [APSUniqueName .] \
          -fileName $file.3 -sddsExportableFile $file.2 \
          -width $widthoutput -deleteOnClose 1 -comment "Searched for $SearchString"
        set lastfile $file.2
        set lastbutton $Button
        ButtonStates -ButtonsNormal "searchCN searchResults $Normal" -ButtonsDisabled "abort"	
        APSSetVarAndUpdate status "Done"
    }
}

#
# Abort process 
#
proc Abort {} {
    global fID Result
    set Result Aborted
    foreach id [pid $fID] {
        catch {exec kill -9 $id} result
    }
}

#
# Execute unix commands
#
proc APSExecModified { args } {
    set unixCommand ""
    set callback ""
    set outputVariable ""
    set outputFileId ""
    APSStrictParseArguments {unixCommand callback outputVariable outputFileId}
    if {$outputVariable != ""} {
	set saveOutput 1
	global $outputVariable
    } else {
	set saveOutput 0
    }
    global $outputFileId
    set cmd1 "$unixCommand |& cat -u"
    if [catch {APSSetVarAndUpdate $outputFileId [open |$cmd1 r]} results] {
	if {$saveOutput} {
	    set $outputVariable "APSExecModified: $results"
	}
    } else {
	fileevent [set $outputFileId] readable  [list APSExecCallback [set $outputFileId] $callback $outputVariable $saveOutput]
	set $outputVariable 1	
    }
}


#
# unset variables
#
proc RemoveVar {args} {
    set ClearVar ""
    APSStrictParseArguments {ClearVar}
    foreach var $ClearVar {
	global $var
	if [info exists $var] {
	    unset $var
	}
    }
}

#
# alter button states
#
proc ButtonStates {args} {
    set ButtonsDisabled ""
    set ButtonsNormal ""
    APSStrictParseArguments {ButtonsDisabled ButtonsNormal}
    foreach button $ButtonsDisabled {
	APSDisableButton .userFrame.buttonFrame.$button.button
    }
    foreach button $ButtonsNormal {
	APSEnableButton .userFrame.buttonFrame.$button.button
    }
    update
}


set status "Working..."
#check the command line arguments
set args $argv
set type ""
APSStrictParseArguments {type}
if {[string compare $type data] == 0} {
    startDL
} elseif {[string compare $type alarm] == 0} {
    startAL
} elseif {[string compare $type request] == 0} {
    startRF
} else {
    startDL
}



