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



set auto_path [linsert $auto_path 0  /usr/local/oag/apps/lib/$env(HOST_ARCH)]
set auto_path [linsert $auto_path 0 /usr/local/oag/lib_patch/$env(HOST_ARCH)]
APSDebugPath

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

APSApplication . -name quickWaveformMonitor -version $CVSRevisionAuthor \
  -overview {This is the quickWaveformMonitor utility.  It provides a simple interface to the sddswmonitor program, which allows logging EPICS waveform and scalar data to an SDDS file.}

set monitors(monitor) 0
set monitorLines(monitor) 0
set monitors(wmonitor) 0
set monitorLines(wmonitor) 0

set mainStatus "Press ADD to enter more PV names for monitoring"

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

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

proc CreatePVFrame {widget args} {
    set parent ""
    set type ""
    set label ""
    APSStrictParseArguments {parent type label}

    APSFrame $widget -parent $parent  -relief raised -label "$label"
    set pw $parent$widget.frame

    global pvScroll pvScrollFrame
    set pvScroll($type) [APSScroll .sw -parent $pw -name "$label PV List"]
    set pvScrollFrame($type) $pw.sw

    APSButton .pvScrollAdd -parent $pw -text ADD -command \
      "MakeNewMonitorLine $pvScroll($type) -type $type -scrollAdjust 1" \
      -contextHelp "Press to add PV name entry slots."
    APSButton .clear -parent $pw -text "CLEAR ALL" -command "ClearAllSettings $pvScroll($type) -type $type" \
      -contextHelp "Clears all of the settings for quickMonitor, including PV names and filenames."
    APSButton .save -parent $pw -text "SAVE CONFIG..." -command "SaveMonitorList -type $type" \
      -contextHelp "Allows you to save the configuration, which includes the list of PVs, the interval, and the number of steps"
    APSButton .load -parent $pw -text "LOAD CONFIG..." -command "LoadExistingInput -type $type" \
      -contextHelp "Loads PV names and run parameters from a previously saved configuration file." 
}

CreatePVFrame .pvScrollWf -parent .userFrame -type wmonitor -label "Waveforms"
CreatePVFrame .pvScroll -parent .userFrame -type monitor -label "Scalars"
 
frame .userFrame.settings -bd 4 -relief raised
pack .userFrame.settings -side top -fill x
set Interval 1
set Steps 100
set outputFile ""
APSLabeledEntry .settings.interval -parent .userFrame -label "Interval (s): " \
  -textVariable Interval -width 80 \
  -contextHelp "Enter the time between samples in seconds in this field."
APSLabeledEntry .settings.steps -parent .userFrame -label "Steps: " \
  -textVariable Steps -width 80 \
  -contextHelp "Enter the number of steps (or samples) in this field.  The total time to run is approximately the \
number of steps times the interval."
APSLabeledEntry .settings.output -parent .userFrame -label "Output file: " \
  -textVariable outputFile -width 80 -commandButton 1 \
  -contextHelp "Enter a name for the output file to hold the data.  This file is not deleted by quickMonitor, so you can use the data for other purposes.  For example, more complicated plots can be done with quickSDDSplot."

frame .userFrame.ops -bd 4 -relief raised
pack .userFrame.ops -side top -fill x
APSButton .ops.run -parent .userFrame -text RUN -command RunMonitor \
  -contextHelp "Launches a monitoring subprocess to collect the data."
APSButton .ops.namecap -parent .userFrame -text "NAME CAPTURE..." -command \
  "catch {exec namecapture &}" -contextHelp \
  "Launches an instance of the name capture utility, which is useful in making lists of process variable names for use with quickMonitor.  Use the center mouse button on any MEDM screen to get the process variable name of whatever you point at.  Hold the mouse button down and drag the name into the name capture window.  You can drop the name from there into a quickMonitor entry box, or have name capture put names in a file that you can load into quickMonitor using LOAD INPUT..."

frame .userFrame.review -bd 4 -relief raised
pack .userFrame.review -side top -fill x

set fileSelListDir [pwd]

proc LoadExistingInput {args} {
    set filename ""
    set type ""
    if {[APSStrictParseArguments {filename type}] || ![string length $type]} {
        return -code error "LoadExistingInput: invalid arguments"
    }
    SetMainStatus "Loading $filename ..."
    global pvScroll fileSelListDir pvScrollFrame
    global monitorLines PVname Steps Interval
    if ![string length $filename] {
        set filename [APSFileSelectDialog .chooseInputFile \
                        -listDir $fileSelListDir]
        if {$filename==""} { 
            return 
        }
    }
    if {![APSCheckSDDSFile -fileName $filename]} {
        SetMainStatus "$filename is not an SDDS file."
        return
    }
    set fileSelListDir [file dirname $filename]
    set names [APSGetSDDSNames -fileName $filename -class column]
    if {[llength $names]<1} {
        SetMainStatus "$filename is not a valid input file."
        return 
    }
    switch $type {
        monitor {
            if {[lsearch -exact $names "ControlName"]!=-1} {
                set columnName ControlName
            } else {
                SetMainStatus "$filename is not a valid sddsmonitor input file."
                return
            }
            if {[lsearch -exact $names "ReadbackName"]!=-1} {
                set columnNameRB ReadbackName
            } else {
                set columnNameRB $columnName
            }
            set names [APSGetSDDSColumn -fileName $filename -column $columnName -page 0]
            set namesRB [APSGetSDDSColumn -fileName $filename -column $columnNameRB -page 0]
            if {[llength $names]==0} {
                SetMainStatus "$filename has no process variable names."
                return
            }
            if {$monitorLines($type)==1 && [string length $PVname($type,0.PV)]==0} {
                set monitorLines($type) 0
            }
            foreach namePV $names nameRB $namesRB {
                MakeNewMonitorLine $pvScroll($type) -pvNamePV $namePV -pvNameRB $nameRB -type $type
            }
            after 1000
            update
            APSScrollAdjust $pvScrollFrame($type) -numVisible 5
            if ![catch {APSGetSDDSParameter -fileName $filename -parameter Interval -page 1} result] {
                set Interval $result
            }
            if ![catch {APSGetSDDSParameter -fileName $filename -parameter Steps -page 1} result] {
                set Steps $result
            }
        }
        wmonitor {
            if {[lsearch -exact $names "WaveformPV"]!=-1} {
                set columnName WaveformPV
            } else {
                SetMainStatus "$filename is not a valid sddswmonitor input file."
                return
            }
            if {[lsearch -exact $names "WaveformName"]!=-1} {
                set columnNameRB WaveformName
            }
            set names [APSGetSDDSColumn -fileName $filename -column $columnName -page 0]
            set namesRB [APSGetSDDSColumn -fileName $filename -column $columnNameRB -page 0]
            if {[llength $names]==0} {
                SetMainStatus "$filename has no process variable names."
                return
            }
            if {$monitorLines($type)==1 && [string length $PVname($type,0.PV)]==0} {
                set monitorLines($type) 0
            }
            foreach namePV $names nameRB $namesRB {
                MakeNewMonitorLine $pvScroll($type) -pvNamePV $namePV -pvNameRB $nameRB -type $type
            }
            update
            after 1000
            APSScrollAdjust $pvScrollFrame($type) -numVisible 5
            if ![catch {APSGetSDDSParameter -fileName $filename -parameter Interval -page 1} result] {
                set Interval $result
            }
            if ![catch {APSGetSDDSParameter -fileName $filename -parameter Steps -page 1} result] {
                set Steps $result
            }
        }
        default {
            return -code error "invalid type: $type"
        }
    }

    SetMainStatus "Loaded configuration data from file $filename"
}


proc ClearAllSettings {widget0 args} {
    set type ""
    APSStrictParseArguments {type}
    global monitorLines PVname outputFile logplot
    if {![APSYesNoPopUp "Really? Clear all settings?"]} { return }
    for {set i 0} {$i<$monitorLines($type)} {incr i} {
        if {[winfo exists $widget0.m$i]} { destroy $widget0.m$i}
        set PVname($type,$i.PV) ""
        set PVname($type,$i.RB) ""
    }
    set monitorLines($type) 0
    set outputFile ""
    set logplot 0
    SetMainStatus "Press ADD to enter PV names for monitoring"
}

proc MakeNewMonitorLine {widget0 args} {
    global pvScrollFrame
    global monitorLines PVname
    set pvNamePV ""
    set pvNameRB ""
    set type ""
    set scrollAdjust 0
    APSParseArguments {pvNamePV pvNameRB type scrollAdjust}

    set widget $widget0.m$monitorLines($type)

    if ![winfo exists $widget] {
        frame $widget -bd 1
        pack $widget -fill x
        frame $widget.op 
        frame $widget.data
        pack $widget.op $widget.data -side left

        APSButton .delete -parent $widget.op -text "DELETE" \
          -command "DeleteMonitorLine $widget0 -number $monitorLines($type) -type $type" \
          -contextHelp "Press to delete the corresponding PV name entry line."
        $widget.op.delete configure -bd 0
        $widget.op.delete.button configure -font -adobe-courier-medium-r-normal-*-12-*-*-*-*-*-*-*
        APSButton .clear -parent $widget.op -text "CLEAR" \
          -command "ClearMonitorLine $widget0 -number $monitorLines($type) -type $type" \
          -contextHelp "Press to clear the corresponding PV name entry line."
        $widget.op.clear configure -bd 0
        $widget.op.clear.button configure -font -adobe-courier-medium-r-normal-*-12-*-*-*-*-*-*-*
        APSLabeledEntry .x -parent $widget.data -label " PV name:" \
          -textVariable PVname($type,$monitorLines($type).PV) -width 30 -packOption "-side left" \
          -contextHelp "Enter the name of a process variable (PV) to be monitored in this field."
        APSLabeledEntry .y -parent $widget.data -label " PV readback:" \
          -textVariable PVname($type,$monitorLines($type).RB) -width 30 -packOption "-side right" \
          -contextHelp "Enter the name of the column or parameter in the output file for this PV.  Defaults to the PV name."
    }

    set PVname($type,$monitorLines($type).PV) $pvNamePV
    if [string length $pvNameRB] {
        set PVname($type,$monitorLines($type).RB) $pvNameRB
    } else { 
        set PVname($type,$monitorLines($type).RB) $pvNamePV
    }
    if $scrollAdjust {
        tkwait visibility $widget.data.x
        APSScrollAdjust $pvScrollFrame($type) -numVisible 5
    }
    incr monitorLines($type)
}

proc DeleteMonitorLine {widget0 args} {
    global PVname 
    set type ""
    set number ""
    APSStrictParseArguments {type number}
    destroy $widget0.m$number
    set PVname($type,$number.PV) ""
    set PVname($type,$number.RB) ""
}

proc ClearMonitorLine {widget0 args} {
    global monitorLines PVname 
    set type ""
    set number ""
    APSStrictParseArguments {type number}
    set PVname($type,$number.PV) ""
    set PVname($type,$number.RB) ""
}

set outputListLength 0
proc SaveMonitorList {args} {
    set filename ""
    set type ""
    APSStrictParseArguments {filename type}
    global monitorLines PVname 
    global Interval Steps 
    global outputList outputListLength

    set count 0
    set PVNameList ""
    set ReadbackNameList ""
    for {set i 0} {$i<$monitorLines($type)} {incr i} {
        if {[string length $PVname($type,$i.PV)]} {
            incr count
            lappend PVNameList $PVname($type,$i.PV)
            if ![string length $PVname($type,$i.RB)] {
                lappend ReadbackNameList $PVname($type,$i.PV)
            } else {
                lappend ReadbackNameList $PVname($type,$i.RB)
            }
        }
    }
    if {!$count} {
        switch $type {
            monitor {
                return 0
            }
            wmonitor {
                return -code error "No waveform PV names given."
            }
            default {
                return -code error "unknown type"
            }
        }
    }
  
    set userMode 0
    global fileSelListDir 
    while {![string length $filename]} {
        set userMode 1
        set filename [APSFileSelectDialog .chooseOutputFile -listDir $fileSelListDir -checkValidity 0]
        set filename [string trim $filename]
        if ![string length $filename] {
            return -code error "No filename given."
        }
        set fileSelListDir [file dirname $filename]
        if {[file exists $filename] && \
              ![APSMultipleChoice [APSUniqueName .] -question "$filename exists.  Overwrite it?" \
                  -labelList {Yes No} -returnList {1 0}]} {
            set filename ""
        }
    }


    switch $type {
        monitor {
            set tmpfile /tmp/[APSTmpString]
            set dataArray(ParameterNames) "Interval Steps"
            set dataArray(ParameterInfo.Interval) "type SDDS_DOUBLE units s"
            set dataArray(ParameterInfo.Steps) "type SDDS_LONG"
            set dataArray(Parameter.Interval) $Interval
            set dataArray(Parameter.Steps) $Steps
            set dataArray(ColumnNames) "ControlName ReadbackName"
            set dataArray(ColumnInfo.ControlName) "type SDDS_STRING"
            set dataArray(ColumnInfo.ReadbackName) "type SDDS_STRING"
            set origNameList ""
            set readbackNameList ""

            global plotList
            set plotList ""
            for {set i 0} {$i<$monitorLines($type)} {incr i} {
                if {[string length $PVname($type,$i.PV)]} {
                    set origName [string trim $PVname($type,$i.PV)]
                    if {[string length $PVname($type,$i.RB)]} {
                        set readbackName [string trim $PVname($type,$i.RB)]
                    } else {
                        set readbackName $origName
                    }
                    foreach var [list origName readbackName] {
                        if {[llength [set $var]]==0} {
                            return -code error "No name specified in PVname [set $var]."
                        }
                        if {[llength [set $var]]>1} {
                            return -code error "White space detected in PVname [set $var]."
                        }
                    }
                    
                    lappend origNameList $origName
                    lappend readbackNameList $readbackName
                    if {[string length $plotList]==0} {
                        set plotList $readbackName
                    } else {
                        set plotList "$plotList,$readbackName"
                    }
                }
            }
            SetMainStatus "Searching..."
            if [catch {SearchPVName -useCA 1 -pvnameList $origNameList} result] {
                if ![APSQueryToProceed \
                       -message "$result\nThis may indicate an IOC malfunction, an input error, or a database problem."] {
                           return -code error "$result"
                       }
            }
            set dataArray(Column.ControlName) [list $origNameList]
            set dataArray(Column.ReadbackName) [list $readbackNameList]
            
            if [catch {sdds save $tmpfile dataArray} result] {
                return -code error "Unable to save $tmpfile: $result"
            }    
            if [catch {exec sddssort $tmpfile $filename -column=ControlName -unique} result] {
                return -code error "Problem in sddssort: $result"
            }
            file delete -force $tmpfile
            if $userMode {
                SetMainStatus "Configuration saved to $filename"
            }
        }
        wmonitor {
            if [catch {exec sddsmakedataset -pipe=out \
                         -parameter=Interval,units=s,type=double -data=$Interval \
                         -parameter=Steps,type=long -data=$Steps \
                         -column=WaveformPV,type=string \
                         -data=[join $PVNameList ,] \
                         -column=WaveformName,type=string \
                         -data=[join $ReadbackNameList ,] \
                         | sddssort -pipe=in $filename -column=WaveformName -unique} result] {
                return -code error "problem saving file: $result"
            }
            SetMainStatus "Configuration saved to $filename"
        }
        default {
            return -code error "unknown monitoring type"
        }
    }
    return -code ok $count
}

proc SearchPVName {args} {
     set pvnameList ""
     set useCA 0
     if [APSStrictParseArguments {pvnameList useCA}] {
         return -code error "SearchPVName: bad arguments"
     }
     if $useCA {
         if [catch {exec cavget -list=[join $pvnameList ,]} dataList] {
             return -code error "SearchPVName: $result"
         }
         set pvNotFoundList ""
         foreach pv $pvnameList value $dataList {
             if [string compare $value ?]==0 {
                 lappend pvNotFoundList $pv
             }
         }
         if [llength $pvNotFoundList] {
             return -code error "PVs not found: [join $pvNotFoundList]"
         }
     } else {
         set pvdata_path "/home/helios/iocinfo/pvdata"
         set origDir [pwd]
         if {[file isdirectory $pvdata_path] != 1} {
             return -code error "Cannot open or locate directory $pvdata_path"
         }

         cd $pvdata_path
         set pvdata_file_list [glob -nocomplain *]
         set findFlag 0
         foreach variable $pvnameList {
             set findFlag 0
             set variable [string trim $variable]
             set cmd "grep \"$variable\" $pvdata_file_list /dev/null"
             set catchlist [list open |$cmd r]
             catch $catchlist fp
             while {[gets $fp line] >= 1} {
                 set breakpoint [string first : $line]
                 set ematch [string range $line [expr $breakpoint+1] end]
                 
                 if {[string compare $ematch $variable] == 0} {
                     set findFlag 1
                     break
                 }
             }
             if !$findFlag {
                 cd $origDir
                 return -code error "Unable to find PV $variable."
             }
         }
         cd $origDir
     }
}

proc RunMonitor {} {
    global monitorLines outputFile
    global Interval Steps logplot
    global outputList plotComList plotLogScale outputListLength
    global plotList

    if {[string length $outputFile]==0} {
        SetMainStatus "Supply an output filename."
        bell
        return
    }
    if {[string first " " $outputFile]!=-1} {
        SetMainStatus "Spaces are not allowed in the filename"
        bell
        return
    }
    if [array size outputList] {
        for {set index 0} {$index < $outputListLength} {incr index} {
	     if ![string compare $outputList($index) $outputFile] {
		SetMainStatus "Output file name $outputFile\n is already used for run# [expr $index + 1]."
		bell
		return
	     }
	 }
    }
    if {[file exists $outputFile]} {
        bell
        set ok [APSYesNoPopUp "Delete existing $outputFile?"]
        if {!$ok} {
            SetMainStatus "Supply a new output filename"
            return
        }
    }

    set inputFile(monitor) /tmp/[APSTmpString]
    if [catch {SaveMonitorList -type monitor -filename $inputFile(monitor)} scalarCount] {
	bell
        APSAlertBox [APSUniqueName .] -errorMessage "$scalarCount"
        return
    }
    set inputFile(wmonitor) /tmp/[APSTmpString]
    if [catch {SaveMonitorList -type wmonitor -filename $inputFile(wmonitor)} waveformCount] {
	bell
        APSAlertBox [APSUniqueName .] -errorMessage "$waveformCount"
        return
    }
    
    set date [exec date]
    SetMainStatus "Running.... (started $date)."
    set outputList($outputListLength) $outputFile
    if $scalarCount {
        APSExecLog .job$outputListLength -unixCommand \
          "sddswmonitor $inputFile(wmonitor) $outputFile -scalars=$inputFile(monitor) \
                -erase -interval=$Interval,s -steps=$Steps -verbose -logOnChange=waveformOnly " \
          -contextHelp "This window is running a quickMonitor subprocess."  
    } else {
        APSExecLog .job$outputListLength -unixCommand \
          "sddswmonitor $inputFile(wmonitor) $outputFile -erase -interval=$Interval,s -steps=$Steps -verbose \
            -logOnChange=waveformOnly " \
          -contextHelp "This window is running a quickMonitor subprocess."  
    }
    incr outputListLength
}


MakeNewMonitorLine $pvScroll(monitor) -type monitor -scrollAdjust 1
MakeNewMonitorLine $pvScroll(wmonitor) -type wmonitor -scrollAdjust 1

if [llength $argv] {
    set args $argv
    set waveformFileName ""
    set scalarFileName ""
    if {[APSStrictParseArguments {waveformFileName scalarFileName}] || \
        ![string length $waveformFileName]} {
        return -code error {usage: quickWaveformMonitor [-waveformFileName <string> [-scalarFileName <string>]]}
    }
    if ![file exists $waveformFileName] {
        return -code error "not found: $waveformFileName"
    }
    if [string length $scalarFileName] {
        LoadExistingInput -filename $scalarFileName -type monitor
    }
    if [string length $waveformFileName] {
        LoadExistingInput -filename $waveformFileName -type wmonitor
    }
}

