#
# $Log: not supported by cvs2svn $
# Revision 1.58  2004/06/08 14:44:03  borland
# Added option for verbose output from parsecasw for CASW beacon group.
#
# Revision 1.57  2004/06/08 14:41:13  borland
# Added -v option to parsecasw for beacon event analysis.
#
# Revision 1.56  2004/06/08 13:52:57  borland
# For CASW data, now optionally runs K. Evan's parsecasw on the printout to
# give "event analysis".
#
# Revision 1.55  2004/06/04 14:36:28  borland
# Added interval analysis for CASW events.
#
# Revision 1.54  2004/05/18 21:15:46  soliday
# Added the Time column to the sort in addtion to TimeWS.
#
# Revision 1.53  2004/01/19 16:40:14  borland
# Removed hard-coded path for SR PS fault decoding file.  Now use OAGGlobal(AlarmLogDirectory).
#
# Revision 1.52  2004/01/19 16:32:47  borland
# Added review of CASW logger data, which contains fewer columns than alarm logger
# data.
# Added use of configuration file /home/helios/oagData/Alarms/ReviewAlarmLog.config.
# Added use of OAGGlobal(AlarmLogDirectory) resource.
#
# Revision 1.51  2003/11/06 22:55:17  borland
# For histograms vs time or hour of day, now computes and plots the statistical
# significance the data in each bin using Poisson statistics.
#
# Revision 1.50  2003/07/24 22:27:20  emery
# Indented some lines.
#
# Revision 1.49  2003/06/04 21:34:09  soliday
# Removed extra quote.
#
# Revision 1.48  2003/06/04 21:31:28  soliday
# Modified to work if the RelatedControlName is there or if it is not.
#
# Revision 1.47  2002/03/04 21:43:06  shang
# modified AlarmReviewSetup proc to make "Power Supply Trip" only valid for SR.
#
# Revision 1.46  2002/03/04 20:37:02  shang
# modifeid PrintAlarmLog proc and added DecodeErrorMessage proc
#
# Revision 1.45  2001/02/06 18:53:35  borland
# Turned off debugging output.
#
# Revision 1.44  2001/02/06 18:50:19  borland
# Added SRDCPS-100Hz group.
# Fixed problem with not recognizing that data file has changed.
#
# Revision 1.43  2000/08/07 01:44:22  borland
# Restore printout of WS time stamp.
#
# Revision 1.42  2000/06/28 16:28:30  borland
# Added top-up alarm system.  Also modified printout generation and dereferencing
# to deal with possibility of related values.
#
# Revision 1.41  2000/03/13 16:31:57  borland
# Severity selections now apply to overlap analysis.  Fixed bug in wildcard
# selection for histograms.
#
# Revision 1.40  1999/06/22 14:57:58  borland
# Reverted to using the workstation time stamp for all operations, as the
# IOC time stamps have proven unreliable.
#
# Revision 1.39  1999/06/22 14:00:00  borland
# Now use WS timestamp for data filtering and display this timestamp first.
# The IOC timestamps are unreliable for some records.
#
# Revision 1.38  1999/06/16 14:08:26  borland
# Now compute workstation time as well as IOC time data and make time
# text for both.
#
# Revision 1.37  1998/08/17 21:25:33  borland
# Made printouts wider and adjusted labels etc. to accomodate RelatedValueString
# data.
#
# Revision 1.36  1998/08/15 16:39:09  borland
# Now prints the RelatedValueString data.
#
# Revision 1.35  1998/05/29 15:54:13  borland
# Added runControl group.
#
# Revision 1.34  1998/04/21 16:49:02  borland
# Fixed syntax error in MakeTimeHistograms
#
# Revision 1.33  1997/07/15 22:03:50  borland
# Added srRTFB system.
#
# Revision 1.32  1997/06/27 18:17:19  borland
# Changed data directory to /home/helios/oagData/Alarms
#
# Revision 1.31  1997/05/20 20:12:05  borland
# Fixed problem in alarm count plots and printouts; wasn't breaking
# pages as I should.
#
# Revision 1.30  1997/05/08 14:11:12  borland
# Improved plot labelling.
#
# Revision 1.29  1997/05/07 23:01:54  borland
# Fixed two typos in last change.
#
# Revision 1.28  1997/05/07 22:57:43  borland
# Added APSMakeSafeQualiferString for -title and -topline options of
# *all* sddsplot calls.
#
# Revision 1.27  1997/05/01 15:10:40  borland
# Made some format and label changes in printouts.
#
# Revision 1.26  1997/04/23 20:51:19  borland
# In PrintAlarmLog, now do sddsselect before sddstimeconvert, which can really
# improve performance.
#
# Revision 1.25  1997/04/11 23:39:31  borland
# Added sddsconvert -recover=clip upstream of sddsprocess in dereference
# code to prevent errors when file is live.   Also fixed bug in deletion
# of out-of-date reference files.
#
# Revision 1.24  1997/04/03 19:45:19  borland
# Added ACIS alarm group.
#
# Revision 1.23  1997/03/06 23:21:53  borland
# Changed order of dereference and merge operations for raw files.
# This accomodates changes in control name lists during a single day
# or over multiple days.
#
# Revision 1.22  1997/02/28 23:33:39  borland
# Removed SCrowbar system.
#
# Revision 1.21  1997/02/26 18:23:18  borland
# Fixed filter on PreviousRow.  Was removing any negative value, but should
# have kept values of -1.
#
# Revision 1.20  1997/02/07 17:27:25  borland
# Added printouts of the number of occurrences of each alarm, in addition to
# the plots.
#
# Revision 1.19  1997/02/04 14:40:00  borland
# Added option to dereferencing to allow filtering by TimeOfDay, rather than
# Time.  This is needed to fix a bug introduced by recent modifications done
# to enhance ReviewAlarmLog.
#
# Revision 1.18  1997/01/31 23:15:17  borland
# Use APSMakeSafeQualifierString to prevent errors from sddsprintout when
# the title contains commas.
#
# Revision 1.17  1997/01/30 15:23:36  borland
# Added support for filter files.  Now eliminates non-transitional alarms
# (e.g., start of run alarm states) in dereferencing stage.
#
# Revision 1.16  1997/01/29 17:41:22  borland
# Added capability for multiple match and exclude terms, separated by
# commas in the entry box.
#
# Revision 1.15  1997/01/24 20:34:58  borland
# Removed statements that defeated time filtering prior to dereferencing.
#
# Revision 1.14  1997/01/24 14:54:22  borland
# Added better topline and title generation.
#
# Revision 1.13  1997/01/16 03:39:39  borland
# Fixed problem with sddsprintout command for printing Description.
#
# Revision 1.12  1997/01/16 03:33:58  borland
# Added SCrowbar group.  Also, dereferences description data if available and
# displays it.
#
# Revision 1.11  1996/11/07 14:49:30  borland
# Added AlarmReviewSetup procedure.  Changed error returns for
# DereferenceAlarmLoggerData.
#
# Revision 1.10  1996/11/04 18:29:25  borland
# Added AlarmReviewSetup procedure to set some variables that are needed
# by several apps.
#
# Revision 1.9  1996/10/11 14:00:53  borland
# Added dereferencing and printout of alarm status information.
#
# Revision 1.8  1996/09/27 01:19:52  borland
# Added time filtering with dereferencing for greater speed.  Added printout
# capability.  Added NO_ALARM choice.
#
# Revision 1.7  1996/03/11  19:59:35  borland
# Added -updateIffOld options to allow use of existing processed files if they
# are still younger than source files.  Removed -keepOld options.  Added
# facility for accumulating lists of temp files and deleting them.
#
# Revision 1.6  1996/03/11  03:41:00  borland
# Improved behavior for missing files. Added better filter composition
# code to histogram maker (from count routine).
#
# Revision 1.5  1996/03/11  02:47:43  borland
# Fixed type in MakeAlarmHistograms.
#
# Revision 1.4  1996/03/08  03:41:40  borland
# Fixed problem with catch statment.  Fixed quoting to avoid errors when
# selection/exclusion strings have square brackets.
#
# Revision 1.3  1996/02/14  19:26:05  borland
# Added more careful error checking and messages.  Now handles .gz files
# and system selection really does something.  Remove date entry temporarily.
#
# Revision 1.2  1996/02/10  00:56:07  borland
# Added features for better selection of what is plotted and how it is sorted.
#
# Revision 1.1  1996/01/16  21:49:05  saunders
# Added application specific tcl procedure libraries, which are installed
# as libraries, not applications.
#
#

#package require rpn

set debugReviewAlarmLog 0

proc AlarmReviewSetup {args} {
    set customSystem 1
    APSStrictParseArguments {customSystem}
    global dataDir defaultDataDir Systems SystemRootnames SystemCommands systemChoice
    global IsAlarmLogger debugReviewAlarmLog

    global OAGGlobal
    set dataDir $OAGGlobal(AlarmLogDirectory)
    if $debugReviewAlarmLog {
        puts stderr "alarm log dir: $dataDir"
    }
    if {![file exists $dataDir] || ![file isdirectory $dataDir]} {
        return -code error "$dataDir doesn't exist or is not a directory"
    }
    set defaultDataDir $dataDir
    set configFile $dataDir/ReviewAlarmLog.config
    if [catch {sdds load $configFile config} result] {
        return -code error "problem reading configuration file $configFile: $result"
    }
    if $debugReviewAlarmLog {
        puts stderr "read $configFile"
    }
    set Systems [lindex $config(Column.System) 0]
    set SystemRootnames [lindex $config(Column.SystemRootname) 0]
    set IsAlarmLogger [lindex $config(Column.IsAlarmLogger) 0]
    foreach item $Systems {
        lappend SystemCommands "SetNoncustomMode"
    }
    if $customSystem {
        set systemChoice Custom
        lappend Systems Custom
        lappend SystemRootnames ""
        lappend SystemCommands SetCustomMode
    } else {
        if [lsearch -exact $Systems SR]!=-1 {
            set systemChoice SR
        } else {
            set systemChoice [lindex $Systems 0]
        }
        SetNoncustomMode
    }
}
    
# Add explicit ControlName and AlarmSeverity strings to alarm logger output
proc DereferenceAlarmLoggerData {args} {
    global debugReviewAlarmLog
    if $debugReviewAlarmLog { puts stderr "In DereferenceAlarmLoggerData"}
    set fileName ""
    set newFile ""
    set timeMin 0
    set timeMax 0
    set timeName TimeWS
    set isAlarmData 1
    APSStrictParseArguments {fileName newFile timeMin timeMax timeName isAlarmData}
    if {[string length $fileName]==0 || ![file exists $fileName]} {
        return -code error "DereferenceAlarmLoggerData: Missing/nonexistent input file: $fileName"
    }
    if [string length $newFile]==0 {
        return -code error "DereferenceAlarmLoggerData: Missing output filename."
    }
    if $debugReviewAlarmLog { puts stderr "fileName = $fileName" }

    set timeNow [format %.0f [exec timeconvert -break=now]]
    set age [expr ($timeNow-[file mtime $fileName])/3600.0]
    switch -- [AlarmFileIsExplicit -fileName $fileName] {
        1 {
            if $timeMin<$timeMax {
                if $debugReviewAlarmLog { puts stderr "Filtering explicit file on time."}
                # filter data by time.
                if [catch {exec sddsconvert -pipe=out -recover=clip $fileName -nowarning \
                             | sddsprocess -pipe -nowarning \
                             "-define=column,Time,StartTime TimeOfDay StartHour - 3600 * +,units=s" \
                             "-define=column,TimeWS,StartTime TimeOfDayWS StartHour - 3600 * +,units=s" \
                             -filter=column,$timeName,$timeMin,$timeMax \
                             -filter=column,PreviousRow,-1,1e300 \
                             | sddscombine -pipe=in $newFile -merge \
                         } message] {
                    return -code error "DereferenceAlarmLoggerData: $message"
                }
                if $debugReviewAlarmLog { puts stderr "Done."}
            } else {
                if $age<1.0 {
                    if $debugReviewAlarmLog { puts stderr "Converting explicit young file."}
                    if [catch \
                          {exec sddsconvert -pipe=out -recover=clip $fileName -nowarning \
                             | sddsprocess -pipe -nowarning \
                             "-define=column,Time,StartTime TimeOfDay StartHour - 3600 * +,units=s" \
                             "-define=column,TimeWS,StartTime TimeOfDayWS StartHour - 3600 * +,units=s" \
                             -filter=column,PreviousRow,-1,1e300 \
                             | sddscombine -pipe=in $newFile -merge \
                         } message] {
                        return -code error "DereferenceAlarmLoggerData: $message"
                    }
                if $debugReviewAlarmLog { puts stderr "Done."}
                }
            }
            return -code ok 1
        }
        0 {
            if [catch {APSGetSDDSNames -fileName $fileName -class array} columnNames] {
                return -code error "DereferenceAlarmLoggerData: $result"
            }
            set derefOption ""
            if [lsearch -exact $columnNames DescriptionString]!=-1 {
                set derefOption -column=Description,array=DescriptionString,ControlNameIndex
            }
            if [lsearch -exact $columnNames RelatedControlNameString]!=-1 {
                lappend derefOption -column=RelatedControlName,array=RelatedControlNameString,ControlNameIndex
            }
            if $isAlarmData {
                lappend derefOption -column=AlarmSeverity,array=AlarmSeverityString,AlarmSeverityIndex 
                lappend derefOption -column=AlarmStatus,array=AlarmStatusString,AlarmStatusIndex 
            } 
            if $timeMin<$timeMax {
                if $debugReviewAlarmLog { puts stderr "Filtering and dereferencing."}
                if {[catch {eval exec sddsconvert $fileName -recover=clip -pipe=out -nowarning \
                              | sddsprocess -pipe -nowarning \
                              {"-define=column,Time,StartTime TimeOfDay StartHour - 3600 * +,units=s"} \
                              {"-define=column,TimeWS,StartTime TimeOfDayWS StartHour - 3600 * +,units=s"} \
                              -filter=column,$timeName,$timeMin,$timeMax \
                              -filter=column,PreviousRow,-1,1e300 \
                              | sddsderef -pipe -outOfBounds=delete \
                              -column=ControlName,array=ControlNameString,ControlNameIndex \
                              $derefOption \
                              | sddscombine -pipe=in -merge $newFile  \
                          } message]} {
                    return -code error "DereferenceAlarmLoggerData: $message"
                }
                if $debugReviewAlarmLog { puts stderr "Done."}
            } else {
                if $debugReviewAlarmLog { puts stderr "Converting and dereferencing."}
                if {[catch {eval exec sddsconvert $fileName -recover=clip -pipe=out -nowarning \
                              | sddsprocess -pipe -nowarning \
                              {"-define=column,Time,StartTime TimeOfDay StartHour - 3600 * +,units=s"} \
                              {"-define=column,TimeWS,StartTime TimeOfDayWS StartHour - 3600 * +,units=s"} \
                              -filter=column,PreviousRow,-1,1e300 \
                              | sddsderef -pipe -outOfBounds=delete \
                              -column=ControlName,array=ControlNameString,ControlNameIndex \
                              $derefOption \
                              | sddscombine -pipe=in $newFile -merge \
                          } message]} {
                    return -code error "DereferenceAlarmLoggerData: $message"
                }
                if $debugReviewAlarmLog { puts stderr "Done."}
            }
            return -code ok 
        }
        default {
            return -code error "DereferenceAlarmLoggerData: Bad return value from AlarmFileIsExplicit"
        }
    }
}

# determine whether alarm logger file has explicit ControlName
proc AlarmFileIsExplicit {args} {
    global debugReviewAlarmLog
    if $debugReviewAlarmLog { puts stderr "In AlarmFileIsExplicit"}
    set fileName ""
    APSStrictParseArguments {fileName}
    if {[string length $fileName]==0 || ![file exists $fileName]} {
        return -code error -errorinfo "Bad filename or missing file" 0
    }
    set names [APSGetSDDSNames -class column -fileName $fileName]
    if [lsearch -exact $names ControlName]!=-1 {
        return -code ok 1
    }
    return -code ok 0
}

# determine whether alarm logger file has ControlNameIndex
proc AlarmFileIsIndexed {args} {
    global debugReviewAlarmLog
    if $debugReviewAlarmLog { puts stderr "In AlarmFileIsIndexed"}
    set fileName ""
    APSStrictParseArguments {fileName}
    if {[string length $fileName]==0 || ![file exists $fileName]} {
        return -code error -errorinfo "Bad/nonexistent file $fileName (AlarmFileIsIndexed)" 0
    }
    if ![APSCheckSDDSFile -fileName $fileName] {
        return -code error -errorinfo "Non-SDDS file $fileName (AlarmFileIsIndexed)" 0
    }
    set names [APSGetSDDSNames -class column -fileName $fileName]
    if [lsearch -exact $names ControlNameIndex]!=-1 {
        return -code ok 1
    }
    return -code ok 0
}

# make index for ControlName's for alarm logger file
proc MakeIndexedControlNameList {args} {
    global debugReviewAlarmLog
    if $debugReviewAlarmLog { puts stderr "In MakeIndexedControlNameList"}
    set fileName ""
    set listFile ""
    set updateIffOld 1
    APSStrictParseArguments {fileName listFile updateIffOld}
    if {[string length $fileName]==0 || ![file exists $fileName]} {
        return -code error -errorinfo "Bad input filename or missing file"
    }
    if {[string length $listFile]==0} {
        return -code error -errorinfo "Bad list filename"
    } 
    if [catch {AlarmFileIsIndexed -fileName $fileName} indexed] {
        return -code error "Alarm file indexing problem"
    }

    set generate 0
    if ![file exists $listFile] {
        set generate 1
    } else {
        if $updateIffOld {
            if [file mtime $fileName]>[file mtime $listFile] {
                set generate 1
            }
        } else {
            set generate 1
        }
    }
    if !$generate {
        return -code ok 
    }

    switch -- $indexed {
        1 {
            if [catch {exec sddsconvert $fileName -pipe=out "-dele=array,*" "-dele=col,*" \
                         "-retain=col,ControlName,ControlNameIndex,Description" \
                         | sddsbreak -rowlimit=2500 -pipe \
                         | sddssort -pipe -unique -column=ControlNameIndex \
                         | sddscombine -merge -pipe \
                         | sddssort -pipe=in -unique -column=ControlNameIndex $listFile} message] {
                return -code error -errorinfo $message 
            } 
            return -code ok
        }
        0 {
            if [catch {exec sddsconvert $fileName -pipe=out "-dele=array,*" "-dele=col,*" \
                         "-retain=col,ControlName,Description" \
                         | sddsbreak -rowlimit=2500 -pipe \
                         | sddssort -pipe -unique -column=ControlName \
                         | sddscombine -merge -pipe \
                         | sddssort -pipe -unique -column=ControlName \
                         | sddsprocess -pipe=in $listFile "-rpnexpr=0 sto i" \
                         "-define=column,ControlNameIndex,i 1 + sto i,type=long"} message] {
                return -code error -errorinfo $message 
            } 
            return -code ok
        }
        default {
            return -code error -errorinfo "Problem with file $fileName" 
        }
    }
}        

# Makes time histograms of alarm logger output for different severities.
# Assumes data has been separated in files for each severity.
#
proc MakeAlarmHistograms {args} {
    global debugReviewAlarmLog
    if $debugReviewAlarmLog { puts stderr "In MakeAlarmHistograms"}
    set rootName ""
    set major 0
    set minor 0
    set invalid 0
    set noAlarm 0
    set timeBin 0
    set pvMatch *
    set pvExclude ""
    set extension his
    set versus TimeOfDayWS
    set filterFiles ""
    set isAlarmData 1
    APSStrictParseArguments {rootName major minor invalid timeBin pvMatch pvExclude extension \
                        noAlarm versus filterFiles isAlarmData}
    if {$timeBin<=0 || [string length $rootName]==0} {
        return -code error -errorinfo "Invalid arguments" 0
    }
    if {!$minor && !$major && !$invalid && !$noAlarm} {
        return -code error -errorinfo "No severities selected" 0
    }

    if [llength $filterFiles] {
        set filterFile /tmp/[APSTmpString]
        APSAddToTempFileList $filterFile
        if [catch {eval exec sddscombine -merge $filterFiles -pipe=out \
                     | sddssort -pipe=in -column=ControlName -unique $filterFile} result] {
            return -code error "MakeAlarmHistograms: $result"
        }
        set selectCommand "| sddsselect -pipe $filterFile -match=ControlName -reuse=page,row"
    } else {
        set selectCommand ""
    }

    # make time histograms of various severity alarms
    set exceptions 0
    set no_alarm $noAlarm
    if $isAlarmData {
        set severityList [list major minor invalid no_alarm]
    } else {
        set severityList all
        set all 1
    }
    foreach severity $severityList {
        if [subst \$$severity] {
            if ![file exists $rootName.$severity] {
                return -code error -errorinfo "File not found: $rootName.$severity" 0
            }
            if [catch {eval exec sddsprocess $rootName.$severity -pipe=out \
                         -process=$versus,max,%sMax \
                         -process=$versus,min,%sMin \
                         | sddsprintout -pipe -parameter=${versus}*} result] {
            }
            if {[string compare "$pvMatch" "*"]!=0 || \
                  [string length "$pvExclude"] || \
                  [string length $selectCommand]} {
                set timeArg ""
                set filterList {}
                if [string compare "$pvMatch" "*"] {
                    lappend filterList [MakeMatchOption -pvMatch $pvMatch]
                }
                if [string length "$pvExclude"] {
                    lappend filterList  [MakeMatchOption -pvExclude $pvExclude]
                }
                if [catch { eval exec sddsprocess $rootName.$severity -pipe=out \
                              $filterList -nowarning \
                              $selectCommand \
                              | sddshist -pipe -data=$versus -size=$timeBin $timeArg \
                              | sddsprocess -pipe=in $rootName.$severity.$extension \
                              -process=frequency,ave,frequencyMean \
                              {"-define=column,PoissonSignificance,frequency frequencyMean poiSL"} } message] {
                    incr exceptions
                }
            } else {
                if [catch { eval exec sddshist -data=$versus -size=$timeBin  \
                              $rootName.$severity -pipe=out \
                              | sddsprocess -pipe=in $rootName.$severity.$extension \
                              -process=frequency,ave,frequencyMean \
                              {"-define=column,PoissonSignificance,frequency frequencyMean poiSL"} } message] {
                    incr exceptions
                }
            }
        }
    }
    return -code ok $exceptions
}

proc PlotAlarmHistograms {args} {
    global debugReviewAlarmLog
    if $debugReviewAlarmLog { puts stderr "In PlotAlarmHistograms"}
    set rootName ""
    set minor 0
    set major 0
    set invalid 0
    set noAlarm 0
    set separate 0
    set clipHead 0
    set title ""
    set topline ""
    set extension his
    set versus TimeOfDayWS
    set isAlarmData 1
    APSStrictParseArguments {rootName minor major invalid separate clipHead title extension noAlarm \
                     versus topline isAlarmData}
    if {[string length $rootName]==0} {
        return -code error -errorinfo "Empty file rootname"
    }
    set sddsplotArgs {}
    set missing {}
    set dataPresent 0
    if $separate { lappend sddsplotArgs -separate }
    if $clipHead { lappend sddsplotArgs "-clip=1,0" }
    set no_alarm $noAlarm
    if $isAlarmData {
        set severityList [list major minor invalid no_alarm]
    } else {
        set severityList all
        set all 1
    }
    foreach severity $severityList {
        if [subst \$$severity] {
            if [file exists $rootName.$severity.$extension] {
                if [lsearch -exact [list Time TimeWS TimeIOC] $versus]!=-1 {
                    lappend sddsplotArgs -ticks=xtime
                }
                set topline1 "[string toupper $severity] alarms: [APSMakeSafeQualifierString $topline]"
                lappend sddsplotArgs "-column=$versus,frequency" "$rootName.$severity.$extension" "-topline=$topline1" -ylabel=NumberOfAlarms 
                lappend sddsplotArgs "-column=$versus,PoissonSignificance" "$rootName.$severity.$extension" "-topline=$topline1" -ylabel=PoissonSignificance
                set dataPresent 1
            } else {
                lappend missing $severity
            }
        }
    }
    if $dataPresent {
        eval exec sddsplot -same=x,global {"-title=[APSMakeSafeQualifierString $title]"} \
          -graph=line,vary $sddsplotArgs &
    }
    if [llength $missing] {
        return -code error -errorinfo "Missing file(s) for: $severity" 
    }
    return -code ok
}

proc PrintAlarmLog {args} {
    global debugReviewAlarmLog
    if $debugReviewAlarmLog { puts stderr "In PrintAlarmLog"}
    set dataFile ""
    set outputFile ""
    set pvMatch *
    set pvExclude ""
    set title ""
    set topline ""
    set invalid 0
    set major 0
    set minor 0
    set noAlarm 0
    set sortBy ControlName
    set filterFiles ""
    set decode 0
    set isAlarmData 1
    set intervalAnalysis 0
    set eventAnalysis 0
    APSStrictParseArguments {dataFile outputFile decode pvMatch pvExclude major minor invalid noAlarm sortBy title topline filterFiles isAlarmData intervalAnalysis eventAnalysis}
    if {[string length $dataFile]==0 || [string length $outputFile]==0} {
        return -code error "PrintAlarmLog: No data file or output file"
    }
    if $debugReviewAlarmLog {puts stderr "constructing filters"}
    set filterList {}
    set severityLabel ""
    if [string compare "$pvMatch" "*"] {
        lappend filterList [MakeMatchOption -pvMatch $pvMatch]
    }
    if [string length "$pvExclude"] {
        lappend filterList [MakeMatchOption -pvExclude $pvExclude]
    }
    if !$isAlarmData {
        set severityList all
        set all 1
        set severityLabel ""
    } else {
        set severityList [list minor major invalid no_alarm]
        set severityLabel "with severities"
    }
    if {$major || $minor || $invalid || $noAlarm || $all} {
        set no_alarm $noAlarm
        set qualifierList ""
        foreach severity $severityList {
            if {[string compare $severity all]!=0 && [subst \$$severity]} {
                lappend qualifierList AlarmSeverity=[string toupper $severity]
                set severityLabel "$severityLabel $severity"
            }
        }
        switch [llength $qualifierList] {
            0 {
            }
            1 {
                lappend filterList "-match=column,[lindex $qualifierList 0]"
            }
            default {
                lappend filterList \
                  "-match=column,[lindex $qualifierList 0],[join [lrange $qualifierList 1 end] ,|,],|"
            }
        }
    }

    if {$intervalAnalysis || $eventAnalysis} {
        set sortBy ControlName
    }
    switch $sortBy {
        ControlName {
            set sortArg "-column=ControlName -column=TimeWS -column=Time"
        }
        TimeOfDay {
            set sortArg "-column=TimeWS -column=Time -column=ControlName"
        }
    }

    if [llength $filterFiles] {
        set filterFile /tmp/[APSTmpString]
        APSAddToTempFileList $filterFile
        if [catch {eval exec sddscombine -merge $filterFiles -pipe=out \
                     | sddssort -pipe=in -column=ControlName -unique $filterFile} result] {
            return -code error "PrintAlarmLog (1): $result"
        }
        set selectCommand "sddsselect $dataFile -pipe=out $filterFile -match=ControlName -reuse=page,row"
    } else {
        set selectCommand "sddsconvert $dataFile -pipe=out -binary"
    }

    set t ""
    if {[catch {exec sdds2stream $dataFile -array=ControlNameString} allPVs]} {
    } else {
        foreach match [split $pvMatch ','] {
            if {[lsearch -glob $allPVs $pvMatch] == -1} {
                lappend t "This alarm logger is not logging any PVs that match $match"
            }
        }
    }
    if {$t != ""} {
        set title "[join $t \n]\n$title"
    }

    if $debugReviewAlarmLog {puts stderr "doing sdds pipeline"}
    set title "$title $severityLabel\n$topline"
    set tmpFile /tmp/[APSTmpString]
    APSAddToTmpFileList -ID PrintAlarmLog -fileList $tmpFile
    if {[catch {eval exec $selectCommand \
                  | sddsprocess -pipe -nowarning $filterList \
                  | sddstimeconvert -pipe \
                  -breakdown=column,TimeWS,text=TimeStringWS \
                  -breakdown=column,Time,text=TimeString \
                  | sddssort -pipe=in $sortArg $tmpFile} result]} {
        APSDeleteTmpFileList -ID PrintAlarmLog
        return -code error "PrintAlarmLog (2): $result"
    }
    if [catch {APSGetSDDSNames -fileName $tmpFile -class column} result] {
        APSDeleteTmpFileList -ID PrintAlarmLog
        return -code error "PrintAlarmLog (3): $result"
    }
    if $decode {
        if [catch {DecodeErrorMessage -file $tmpFile} result] {
            APSDeleteTmpFileList -ID PrintAlarmLog
            return -code error "PrintAlarmLog (4): $result"
        }
	if {$result != "skip"} {
	    if {[catch {eval exec sddsprintout $tmpFile \
			    $outputFile -column=ControlName,format=%34s \
			    {"-title=[APSMakeSafeQualifierString $title]"} \
			    -column=TimeString,format=%21s,label=TimeStringIOC \
			    -column=TimeStringWS,format=%21s,label=TimeStringWS \
			    -column=ErrorMessage,format=%65s \
			    -width=160 } result]} {
		if [string first error [string tolower $result]]!=-1 {
		    APSDeleteTmpFileList -ID PrintAlarmLog
		    return -code error "PrintAlarmLog (5): $result"
		}
	    }
	    return -code ok
	}
    } elseif $intervalAnalysis {
        if [catch {exec sddssort $tmpFile -pipe=out -col=ControlName -col=Time,incr \
                     | sddsbreak -pipe -change=ControlName \
                     | sddsbreak -pipe "-gap=Time,amount=[expr 5*60]" \
                     | sddsprocess -nowarning -pipe -process=Time,first,Time0 \
                     "-test=parameter,n_rows 3 >" \
                     "-rpnexpr=0 sto t0,repeat" \
                     "-define=col,dTime,Time t0 - Time sto t0 pop,units=s" \
                     -define=param,Count,n_rows -clip=1,0 \
                     -process=dTime,ave,%sAve -process=dTime,stand,dTimeStDev \
                     -process=dTime,min,%sMin -process=dTime,max,%sMax -process=ControlName,first,%s \
                     | sddscollapse -pipe \
                     | sddssort -pipe -column=dTimeStDev,decre -column=ControlName \
                     | sddstimeconvert -pipe -break=column,Time0,text=TimeStamp0 \
                     | sddsprintout -pipe=in $outputFile \
                     -column=ControlName,format=%20s -col=TimeStamp0,format=%25s \
                     -column=dTime*,format=%6.3f} result] {
            APSDeleteTmpFileList -ID PrintAlarmLog
            return -code error "PrintAlarmLog (6): $result"
        }
        return -code ok
    }
    if {([lsearch -glob $result RelatedControlName] != -1) && ([lsearch -glob $result RelatedValueString] != -1)} {
        set precommand "sddsprocess $tmpFile -pipe=out \"-print=column,RelatedInfo,%s %s,RelatedControlName,RelatedValueString\""
    } else {
        set precommand "cat $tmpFile"
    }
    set printFile $outputFile
    if $eventAnalysis {
        set printFile $tmpFile.1
    }
    if {[catch {eval exec $precommand | sddsprintout -pipe=in \
                  $printFile -column=ControlName,format=%34s \
                  {"-title=[APSMakeSafeQualifierString $title]"} \
                  -column=AlarmSeverity,label=Severity,format=%10s \
                  -column=AlarmStatus,label=Status,format=%10s \
                  -column=TimeString,format=%21s,label=TimeStringIOC \
                  -column=TimeStringWS,format=%21s,label=TimeStringWS \
                  -column=Description,format=%30s \
                  -column=RelatedInfo,format=%16s \
                  -width=160 } result]} {
        if [string first error [string tolower $result]]!=-1 {
            APSDeleteTmpFileList -ID PrintAlarmLog
            return -code error "PrintAlarmLog (7): $result"
        }
    }
    if $eventAnalysis==1 {
        if [catch {exec parsecasw -oag $tmpFile.1 > $outputFile} result] {
            APSDeleteTmpFileList -ID PrintAlarmLog
            return -code error "PrintAlarmLog (8): $result"
        }
    }
    if $eventAnalysis==2 {
        if [catch {exec parsecasw -v -oag $tmpFile.1 > $outputFile} result] {
            APSDeleteTmpFileList -ID PrintAlarmLog
            return -code error "PrintAlarmLog (8): $result"
        }
    }
    APSDeleteTmpFileList -ID PrintAlarmLog
    return -code ok
}


proc SplitAlarmLoggerData {args} {
    global debugReviewAlarmLog
    if $debugReviewAlarmLog { puts stderr "In SplitAlarmLoggerData"}
    set fileName ""
    set rootName ""
    set minor 0
    set major 0
    set invalid 0
    set noAlarm 0
    set isAlarmData 1
    set updateIffOld 1
    APSStrictParseArguments {fileName rootName minor major invalid updateIffOld noAlarm isAlarmData}
    if {[string length $fileName]==0 || ![file exists $fileName]} {
        return -code error -errorinfo "Bad filename or nonexistent file" 0
    }
    set no_alarm $noAlarm
    if !$isAlarmData {
        if ![file exists $rootName.all] {
            exec ln -s $fileName $rootName.all
        }
        return 1
    } else {
        set severityList [list minor major invalid no_alarm]
    }
    foreach severity $severityList {
        if [subst \$$severity] {
            set generate 0
            if ![file exists $rootName.$severity] {
                set generate 1
            } else {
                if {$updateIffOld} {
                    set mtime1 [file mtime $fileName]
                    set mtime2 [file mtime $rootName.$severity]
                    if {$mtime1>$mtime2} {
                        set generate 1
                    }
                } else {
                    set generate 1
                }
            }
            if $generate {
                if [catch {exec sddsprocess -nowarning $fileName $rootName.$severity "-match=column,AlarmSeverity=[string toupper $severity]"} message] {
                    return -code error -errorinfo $message 0
                }
            }
        }
    }
    return -code ok 1
}

proc CountAlarmOccurences {args} {
    global debugReviewAlarmLog
    if $debugReviewAlarmLog { puts stderr "In CountAlarmOccurences"}
    set rootName ""
    set minor 0
    set major 0
    set invalid 0
    set noAlarm 0
    set indexFile ""
    set pvMatch *
    set pvExclude ""
    set extension counts
    set countMin 0
    set sortBy Name
    set filterFiles ""
    set isAlarmData 1
    APSStrictParseArguments {rootName minor major invalid indexFile pvMatch pvExclude extension countMin sortBy noAlarm filterFiles isAlarmData}
    if {[string length $rootName]==0 || [string length $indexFile]==0} {
        return 0
    }
    switch $sortBy {
        Counts {
            set sortCommand "sddssort -pipe -column=frequency,decr"
        }
        Name -
        default  {
            set sortCommand "sddssort -pipe -column=ControlName,incr"
        }
    }
    if [llength $filterFiles] {
        set filterFile /tmp/[APSTmpString]
        APSAddToTempFileList $filterFile
        if [catch {eval exec sddscombine -merge $filterFiles -pipe=out \
                     | sddssort -pipe=in -column=ControlName -unique $filterFile} result] {
            return -code error "CountAlarmOccurences: $result"
        }
        set selectCommand "| sddsselect -pipe $filterFile -match=ControlName -reuse=page,row"
    } else {
        set selectCommand ""
    }

    set no_alarm $noAlarm
    if $isAlarmData {
        set severityList [list major minor invalid no_alarm]
    } else {
        set severityList all
        set all 1
    }
    foreach severity $severityList {
        if ![subst \$$severity] continue
        if $debugReviewAlarmLog {puts stderr "working on severity $severity"}
        if {[string compare "$pvMatch" "*"] || [string length "$pvExclude"] || [string length $selectCommand]} {
            if $debugReviewAlarmLog {puts stderr "constructing filters for severity $severity"}
            set filterList {}
            if [string compare "$pvMatch" "*"] {
                lappend filterList [MakeMatchOption -pvMatch $pvMatch]
            }
            if [string length "$pvExclude"] {
                lappend filterList [MakeMatchOption -pvExclude $pvExclude]
            }

            if $debugReviewAlarmLog {puts stderr "doing sdds pipeline"}
#file copy $rootName.$severity /tmp/bobo.$severity
            if [catch {eval exec sddsprocess $rootName.$severity -pipe=out \
                         -nowarning $filterList \
                         -print=param,Severity,$severity \
                         $selectCommand \
                         | sddshist -size=1 -pipe -data=ControlNameIndex \
                         | sddsxref -pipe $indexFile -equate=ControlNameIndex \
                         -take=* -nowarning \
                         | sddsprocess "-filter=col,frequency,0,$countMin,!" \
                         -pipe -nowarning \
                         | $sortCommand \
                         | sddsbreak -pipe=in -rowlimit=20 $rootName.$severity.$extension } message] {
                puts "$message"
                return 0
            }
        } else {
            catch {eval exec sddshist -size=1 $rootName.$severity -pipe=out -data=ControlNameIndex \
                     | sddsxref -pipe $indexFile -equate=ControlNameIndex -take=* \
                     -nowarning \
                     | sddsprocess "-filter=col,frequency,0,$countMin,!" -pipe -nowarning \
                     -print=param,Severity,$severity \
                     | $sortCommand \
                     | sddsbreak -pipe=in -rowlimit=20 $rootName.$severity.$extension } message
        }
        if [string length $message] {
            puts "$message"
            return 0
        }
    }
    return 1
}

proc PlotAlarmOccurences {args} {
    global debugReviewAlarmLog
    if $debugReviewAlarmLog { puts stderr "In PlotAlarmOccurences"}
    set rootName ""
    set minor 0
    set major 0
    set invalid 0
    set noAlarm 0
    set title ""
    set topline ""
    set extension counts
    set fixedScale 1
    set isAlarmData 1
    APSStrictParseArguments {rootName minor major invalid title counts extension fixedScale noAlarm topline isAlarmData}
    if {[string length $rootName]==0} {
        return -code error -errorinfo "Empty file rootname" 
    }
    set numberToPlot 0
    set sddsplotArgs {}
    set no_alarm $noAlarm
    if $isAlarmData {
        set severityList [list major minor invalid no_alarm]
    } else {
        set severityList all
        set all 1
    }
    foreach severity $severityList {
        if {[subst \$$severity] && [file exists $rootName.$severity.$extension]} {
            set topline1 "[string toupper $severity] alarms: [APSMakeSafeQualifierString $topline]"
            lappend sddsplotArgs "-column=ControlName,frequency" $rootName.$severity.$extension "-topline=$topline1" -end 
            incr numberToPlot 
        }
    }
    if !$numberToPlot {
        return -code error -errorinfo "No files to plot were found" 
    }
    if $fixedScale {
        set extraArg -same=x
    } else {
        set extraArg ""
    }

    eval exec sddsplot  $extraArg -split=page -separate=page "-pspace=0.3,0.90,0,0" \
      {"-title=[APSMakeSafeQualifierString $title]"} -swap -unsuppress=x -graph=yimpulse \
      $sddsplotArgs &
    return -code ok
}

proc PrintAlarmOccurences {args} {
    global debugReviewAlarmLog
    if $debugReviewAlarmLog { puts stderr "In PlotAlarmOccurences"}
    set rootName ""
    set minor 0
    set major 0
    set invalid 0
    set noAlarm 0
    set title ""
    set topline ""
    set extension counts
    set isAlarmData 1
    APSStrictParseArguments {rootName minor major invalid title counts extension noAlarm topline isAlarmData}
    if {[string length $rootName]==0} {
        return -code error "Empty file rootname" 
    }
    set no_alarm $noAlarm
    if $isAlarmData {
        set severityList [list major minor invalid no_alarm]
    } else {
        set severityList all
        set all 1
    }
    foreach severity $severityList {
        set title1 "$title: $severity"
        if {[subst \$$severity] && [file exists $rootName.$severity.$extension]} {
            set tmpFile /tmp/[APSTmpString]
            if {[catch {exec sddscombine $rootName.$severity.$extension -merge -pipe=out \
                          | sddsprintout -column=ControlName,format=%34s \
                          -column=frequency,label=Count,format=%.0f \
                          "-title=[APSMakeSafeQualifierString $title1]\n[APSMakeSafeQualifierString $topline]" \
                          -column=Description,format=%34s -pipe=in $tmpFile} result] && \
                  [string first error [string tolower $result]]!=-1} {
                return -code error "PrintAlarmOccurences: $result"
            }
            APSFileDisplayWindow [APSUniqueName .] -fileName $tmpFile -deleteOnClose 1 \
              -width 132 -height 45
        }
    }
    return -code ok
}

proc MakeMatchOption {args} {
    set pvExclude ""
    set pvMatch ""
    APSStrictParseArguments {pvExclude pvMatch}

    if {[llength $pvExclude] && [llength $pvMatch]} {
        return -code error "MakeMatchOption: -pvMatch and -pvExclude can't be given together"
    }
    if [llength $pvExclude] {
        set nameList [split $pvExclude ,]
    } else {
        set nameList [split $pvMatch ,]
    }
    set optionList -match=column
    set first 1
    foreach elem $nameList {
        if $first {
            set first 0
            lappend optionList ControlName=[string trim $elem]
        } else {
            lappend optionList ControlName=[string trim $elem],|
        }
    }
    if [llength $pvExclude] {
        return "[join $optionList ,],!"
    } 
    return [join $optionList ,]
}

proc DecodeErrorMessage {args} {
    global OAGGlobal
    set decodefile "$OAGGlobal(AlarmLogDirectory)/sr/ErrorCode.sdds"
    set valueList ""
    set file ""
    APSStrictParseArguments {file} 

    if ![string length $file] {
        return -code error "filename for decoding is not given.!"
    }
   
    if [catch {sdds load $file data} result] {
        return -code error "$result"
    }
    if {![info exists data(Column.RelatedControlName)]} {
	return "skip"
    }
    if {![info exists data(Column.RelatedValueString)]} {
	return "skip"
    }
    set nameList [lindex $data(Column.RelatedControlName) 0]
    set valueList [lindex $data(Column.RelatedValueString) 0]
 
    set type [APSGetSDDSParameter -fileName $decodefile -parameter Type]
    set errorList [APSGetSDDSColumn -fileName $decodefile -column ErrorMessage]
    if [string compare [lindex $type 0] "H,V,QS"]==0 {
        set HVQsError [APSGetSDDSColumn -fileName $decodefile -column ErrorMessage -page 1]
        set MtQSError [APSGetSDDSColumn -fileName $decodefile -column ErrorMessage -page 2]
    } else {
        set HVQsError [APSGetSDDSColumn -fileName $decodefile -column ErrorMessage -page 2]
        set MtQSError [APSGetSDDSColumn -fileName $decodefile -column ErrorMessage -page 1]
    }
    #HVQsError for H,V,QS  MtQSError for MT, Q, S
    
    set errorCol ""
    foreach name $nameList value $valueList {
        if {[string match S*:H* $name] || [string match S*:V* $name] 
            || [string match S*:QS:* $name] } {
            set error $HVQsError
        } elseif {[string match S*:MT:* $name] ||
                  [string match S*:Q* $name] ||
                  [string match S*:S* $name] } {
            
            set error $MtQSError
        } else {
            lappend errorCol "unknown control type for decoding"
            continue
        }
        if !$value {
            lappend errorCol "No Indication"
            continue
        }
        set divider $value
        set er ""
        for {set i 0} {$i<10} {incr i} {
            set remainder [expr int(fmod($divider,2))]
            set divider [expr int(floor($divider/2))]
            if $remainder {
                append er "[lindex $error $i],"
            }
            if !$divider {
                break
            }
        }
        set er [string replace $er end end]
        lappend errorCol $er
    }
    
    lappend data(ColumnNames) ErrorMessage
    set data(ColumnInfo.ErrorMessage) "type SDDS_STRING"
    set data(Column.ErrorMessage) [list $errorCol]
    if [catch {sdds save $file data} result] {
        return -code error "$result"
    }
    return -code ok
}
