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

#
# $Log: not supported by cvs2svn $
# Revision 1.6  2005/12/09 19:58:09  soliday
# Removed attempt to load the dp package which is not used.
#
# Revision 1.5  2001/10/29 20:45:34  soliday
# Fixed a problem with the log files.
#
#
#

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.7 $ \$Author: soliday $"

proc SetMainStatus {text} {
    APSSetVarAndUpdate mainStatus "[clock format [clock seconds] -format %H:%M:%S] $text"
}

proc addNewPV {} {
    global pvScroll PVentry PVvariable
    set okCommand {MakeNewMonitorLineForPV $pvScroll $PVentry $PVvariable}

    if {[winfo exists .pv]} {
	destroy .pv
    }

    APSDialogBox .pv \
	-name "Add PV Box" \
	-width 60 \
        -cancelCommand "" \
	-okCommand $okCommand \
        -contextHelp "Press OK button to add PV name."
    APSLabeledEntry .addVar \
	-parent .pv.userFrame \
        -label "PV name: " \
	-textVariable PVentry -width 60 \
        -contextHelp "Enter a PV name of your choice."
    APSLabeledEntry .nickPV \
	-parent .pv.userFrame \
        -label "Variable name:" \
	-textVariable PVvariable -width 60 \
        -contextHelp ""
}

proc addNewEQ {} {
    global pvScroll EQentry EQvariable EQunits
    set okCommand {MakeNewMonitorLineForEQ $pvScroll $EQentry $EQvariable $EQunits 1}

    if {[winfo exists .eq]} {
	destroy .eq
    }

    APSDialogBox .eq \
	-name "Add Equation Box" \
	-width 60 \
        -cancelCommand "" \
	-okCommand $okCommand \
        -contextHelp "Press OK button to add an equation."
    APSLabeledEntry .addEq \
	-parent .eq.userFrame \
        -label "Equation:" \
	-textVariable EQentry \
	-width 60 \
        -contextHelp "Assign an equation using PV's and EQ's variable names with TCL convention.\
         \nSome examples: \
         \n(\$PV0 + \$PV1) * 3;\
         \nabs(\$EQ2 * \$PV3);\
         \nint((\$EQ2 + \$EQ4) * \$PV1) % 2"
    APSLabeledEntry .nickEQ \
	-parent .eq.userFrame \
        -label "Variable name:" \
	-textVariable EQvariable \
	-width 60 \
        -contextHelp ""
    APSLabeledEntry .unitEQ \
	-parent .eq.userFrame \
        -label "Units:" \
	-textVariable EQunits \
	-width 60 \
        -contextHelp ""
}

proc addNewPAR {} {
    global pvScroll PARvalue PARvariable PARunits
    set okCommand {MakeNewMonitorLineForPAR $pvScroll $PARvariable $PARvalue $PARunits}

    if {[winfo exists .pv]} {
	destroy .par
    }

    APSDialogBox .par \
	-name "Add Parameter Box" \
	-width 60 \
        -cancelCommand "" \
	-okCommand $okCommand \
        -contextHelp "Press OK button to add Parameter name."
    APSLabeledEntry .nickPAR \
	-parent .par.userFrame \
        -label "Parameter name:" \
	-textVariable PARvariable \
	-width 60 \
        -contextHelp ""
    APSLabeledEntry .addVal \
	-parent .par.userFrame \
        -label "Parameter value: " \
	-textVariable PARvalue \
	-width 60 \
        -contextHelp "Enter a Parameter value."
    APSLabeledEntry .unitPAR \
	-parent .par.userFrame \
        -label "Parameter units: " \
	-textVariable PARunits \
	-width 60 \
        -contextHelp "Enter a Parameter value."
}

proc LoadExistingInput {} {
    global pvScroll fileSelListDir
    global monitorLines PVname Significant Interval 
    set types ""
    set variables ""
    set units ""
    set filename [APSFileSelectDialog .chooseInputFile -listDir $fileSelListDir]
    if {$filename==""} { 
        return 
    }
    set fileSelListDir [file dirname $filename]
    if {![APSCheckSDDSFile -fileName $filename]} {
        SetMainStatus "$filename is not an SDDS file."
        return
    }
    if {[catch {sdds load $filename inputData} results]} {
        SetMainStatus "Unable to open $filename : $results"
        return	
    }
    if {[llength $inputData(ColumnNames)]<1} {
        SetMainStatus "$filename is not a valid input file."
        return 
    }
    if {[lsearch -exact $inputData(ColumnNames) "ControlName"]!=-1} {
        set columnName ControlName
    } elseif {[lsearch -exact $inputData(ColumnNames) "Device"]!=-1} {
        set columnName Device
    } else {
        SetMainStatus "$filename is not a valid input file."
        return
    }
    
    SetMainStatus "Loading configuration..."
    if {[lsearch -exact $inputData(ColumnNames) "ControlType"]!=-1} {
	set types [lindex $inputData(Column.ControlType) 0]
    }
    if {[lsearch -exact $inputData(ColumnNames) "ControlVariable"]!=-1} {
	set variables [lindex $inputData(Column.ControlVariable) 0]
    }
    if {[lsearch -exact $inputData(ColumnNames) "Unit"]!=-1} {
	set units [lindex $inputData(Column.Unit) 0]
    }

    set names [lindex $inputData(Column.$columnName) 0]
    if {[llength $names]==0} {
        SetMainStatus "$filename has no process variable names."
        return
    }
    if {$monitorLines==1 && [string length $PVname(0.PV)]==0} {
        incr monitorLines -1
    }

    if {[llength $types]!=0 && [llength $variables]!=0 && [llength $units]!=0} {
	foreach n $names t $types v $variables u $units {
            if {![string compare $t pv]} {
                MakeNewMonitorLineForPV $pvScroll $n $v
                update
            }
            if {![string compare $t eq]} {
                MakeNewMonitorLineForEQ $pvScroll $n $v $u 0
                update
            }
	}
    } elseif {[llength $types]!=0 && [llength $variables]!=0 && [llength $units]==0} {
	foreach n $names t $types v $variables u $units {
            if {![string compare $t pv]} {
                MakeNewMonitorLineForPV $pvScroll $n $v
                update
            }
            if {![string compare $t eq]} {
	        set u ""
                MakeNewMonitorLineForEQ $pvScroll $n $v $u 0
                update
            }
	}
    } elseif {[llength $types]!=0 && [llength $variables]==0} {
	set nCount 0
	foreach n $names t $types {
            if {![string compare $t pv]} {
                MakeNewMonitorLineForPV $pvScroll $n $n
                update
            }
            if {![string compare $t eq]} {
	        set u ""
                MakeNewMonitorLineForEQ $pvScroll $n EQ$nCount $u 0
                update
		incr nCount
            }
	}
    } else {
	foreach monitorName $names {
            MakeNewMonitorLineForPV $pvScroll $monitorName $monitorName
            update
	}
    }

    if {[lsearch -exact $inputData(ParameterNames) "Interval"]!=-1} {
	set Interval [lindex $inputData(Parameter.Interval) 0]
    }
    if {[lsearch -exact $inputData(ParameterNames) "Significant"]!=-1} {
	set Significant [lindex $inputData(Parameter.Significant) 0]
    }
    if {[llength $inputData(ParameterNames)]!=0} {
        set unitVal ""
        foreach p $inputData(ParameterNames) {
	    if {([string compare $p Interval]!=0) && 
		([string compare $p Significant]!=0)} {
		array set pInfo $inputData(ParameterInfo.$p)
		set v [format %.${Significant}g [lindex $inputData(Parameter.$p) 0]]
		puts "$pvScroll $p $v $pInfo(units)"
		MakeNewMonitorLineForPAR $pvScroll $p $v $pInfo(units)
	    }
        }
    }
    SetMainStatus "Configuration loaded from file $filename"
}

proc ClearAllSettings {widget0} {
    global monitorLines run pvScrollFrame changeWidgetStateList unitsEntryList
    global PVname PVnick PVunit EQname EQnick EQunit PARnick PARunit
    global pvSaveList eqSaveList parSaveList

    if {![APSYesNoPopUp "Really? Clear all settings?"]} { return }
    set run 0
    update
    APSScrollAdjust $pvScrollFrame -numVisible 2
    for {set i 0} {$i<$monitorLines} {incr i} {
        if {[winfo exists $widget0.m$i]} {
	    set k [lsearch $changeWidgetStateList $widget0.m$i.data.unit]
	    set changeWidgetStateList [lreplace $changeWidgetStateList $k $k]
	    set k [lsearch $unitsEntryList $widget0.m$i.data.unit]
	    set unitsEntryList [lreplace $unitsEntryList $k $k]
	    
	    set j [lsearch $changeWidgetStateList $widget0.m$i.op.delete.button]
	    set changeWidgetStateList [lreplace $changeWidgetStateList $j $j]   
	    set j [lsearch $unitsEntryList $widget0.m$i.op.delete.button]
	    set unitsEntryList [lreplace $unitsEntryList $j $j]   

	    destroy $widget0.m$i
        }
    }
    foreach ePV [array names PVnick] {
	global $PVnick($ePV)
	set $PVnick($ePV) 0
    }
    foreach eEQ [array names EQnick] {
	global $EQnick($eEQ)
	set $EQnick($eEQ) 0
    }
    foreach ePAR [array names PARnick] {
	global $PARnick($ePAR)
	set $PARnick($ePAR) 0
    }

    foreach inf [list PVname PVnick PVunit EQname EQnick EQunit PARnick PARunit] {
	if {[info exists $inf]} {
	    unset $inf
	}
    }
    set monitorLines 0

    foreach var [list outputFile logFile PVentry PVvariable EQentry \
		     EQvariable PARvalue PARvariable] {
        global $var
        set $var ""
    }
    set pvSaveList ""
    set eqSaveList ""
    set parSaveList ""

    SetMainStatus "Enter PV name for monitoring and press corresponding \"ADD\" button."
}

proc MakeNewMonitorLineForPV {widget0 monitorName variable} {
    global PVname PVnick PVunit EQnick PARnick
    global pvScrollFrame monitorLines run changeWidgetStateList
    global apsContextHelp unitsEntryList pvSaveList


    if {![string length $monitorName] || ![string length $variable]} {
	SetMainStatus "No entry submited for monitoring."
	bell
	return
    }
    foreach name [array names PVname] {
	if {[string compare $monitorName $PVname($name)]==0} {
	    SetMainStatus "Entry \($monitorName\) already exists."
	    bell
	    return
	}
	if {[string compare $variable $PVnick($name)]==0} {
	    SetMainStatus "Variable name entry \($variable\) already exists."
	    bell
	    return
	}
    }
    foreach name [array names EQnick] {
	if {[string compare $variable $EQnick($name)]==0} {
	    SetMainStatus "Variable name entry \($variable\) already exists."
	    bell
	    return
	}
    }
    foreach name [array names PARnick] {
	if {[string compare $variable $PARnick($name)]==0} {
	    SetMainStatus "Variable name entry \($variable\) already exists."
	    bell
	    return
	}
    }

    SetMainStatus "Searching for ${monitorName}..."
    if {[catch {SearchPVName -useCA 1 -pvnameList $monitorName} result]} {
	SetMainStatus "$result\nDone."
	bell
	return
    }
    set run 0
    SetMainStatus "Done."

    set widget $widget0.m$monitorLines
    if {![winfo exists $widget]} {
        frame $widget -bd 1
        pack $widget -fill x
        frame $widget.op 
        frame $widget.data
        pack $widget.data -side left -fill x
        pack $widget.op -side right -fill x

        global $variable
	set $variable 0
	lappend pvSaveList $monitorLines.PV
        set PVname($monitorLines.PV) $monitorName
        set PVnick($monitorLines.PV) $variable 
        catch {eval exec cavget -list=${monitorName}.EGU} unit
	if {[string compare $unit ?] == 0} {
	    set PVunit($monitorLines.PV) ""
        } else {
	    set PVunit($monitorLines.PV) [string trim $unit \"]
        }

	pack [label $widget.data.label -text "$PVname($monitorLines.PV) : " -width 35 -anchor c]\
	    -side left
        set apsContextHelp($widget.data.label) "Original PV name."
        pack [entry $widget.data.unit -textvariable PVunit($monitorLines.PV) -width 7] -side right
        set apsContextHelp($widget.data.unit) "Units for corresponding PV."
        lappend unitsEntryList $widget.data.unit
	lappend changeWidgetStateList $widget.data.unit
        pack [entry $widget.data.entry -textvariable $PVnick($monitorLines.PV) -width 20 \
		  -state disabled] -side right
        set apsContextHelp($widget.data.entry) "Monitoring window for $PVnick($monitorLines.PV)."
        pack [entry $widget.data.ref -textvariable PVnick($monitorLines.PV) -width 20 \
		  -state disabled] -side right
        set apsContextHelp($widget.data.ref) "Nick name for corresponding PV."

        APSButton .delete -parent $widget.op -text "DELETE" \
	    -command "DeleteMonitorLine $widget0 $monitorLines" \
	    -contextHelp "Press to delete the corresponding PV line."
        lappend unitsEntryList $widget.op.delete.button
	lappend changeWidgetStateList $widget.op.delete.button
        $widget.op.delete configure -bd 0
        $widget.op.delete.button configure -font -adobe-courier-medium-r-normal-*-12-*-*-*-*-*-*-*
    }

    incr monitorLines
    tkwait visibility $widget.data.label
    APSScrollAdjust $pvScrollFrame -numVisible 10
}

proc MakeNewMonitorLineForEQ {widget0 monitorName variable units ioSwitch} {
    global EQname EQnick EQunit PVnick PARnick
    global pvScrollFrame run eqSaveList
    global monitorLines changeWidgetStateList apsContextHelp unitsEntryList

    if {![string length $monitorName] || ![string length $variable]} {
	SetMainStatus "No entry submited for monitoring."
	bell
	return
    }
    foreach name [array names EQnick] {
	if {[string compare $variable $EQnick($name)]==0} {
	    SetMainStatus "Variable name entry \($variable\) already exists."
	    bell
	    return
	}
    }
    foreach name [array names PVnick] {
	if {[string compare $variable $PVnick($name)]==0} {
	    SetMainStatus "Variable name entry \($variable\) already exists."
	    bell
	    return
	}
    }
    foreach name [array names PARnick] {
	if {[string compare $variable $PARnick($name)]==0} {
	    SetMainStatus "Variable name entry \($variable\) already exists."
	    bell
	    return
	}
    }
    # Evaluation is not going be launched when input is done from the file,
    # assuming that the file was created by the program itself and 
    # each equation was alredy evaluated.
    if {$ioSwitch} {
        if {[catch {EvaluateEquation $monitorName} result]} {
	    SetMainStatus "$result"
            bell
	    return
        }
    }
    set run 0
    update

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

	global $variable
	lappend eqSaveList $monitorLines.EQ
        set EQname($monitorLines.EQ) $monitorName
	set EQnick($monitorLines.EQ) $variable
	set $EQnick($monitorLines.EQ) 0
	if {[string length $units]} {
	    set EQunit($monitorLines.EQ) $units
        } else {
	    set EQunit($monitorLines.EQ) ""
        }

	pack [label $widget.data.label -text "" -width 0 -anchor c]\
	    -side left
        APSButton .button -parent $widget.data -text "Equation" \
	    -command "DisplayEquation $monitorLines" -packOption "-anchor c -side left"\
	    -contextHelp "Invokes a display of a corresponding EQ editor."
	$widget.data.button.button configure -width 33
        pack [entry $widget.data.unit -textvariable EQunit(${monitorLines}.EQ) -width 7] -side right
        set apsContextHelp($widget.data.unit) "Units for corresponding EQ."
        lappend unitsEntryList $widget.data.unit
	lappend changeWidgetStateList $widget.data.unit
        pack [entry $widget.data.entry -textvariable $EQnick($monitorLines.EQ) -width 20 -state disabled]\
	    -side right
	set apsContextHelp($widget.data.entry) "Monitoring window for $EQnick($monitorLines.EQ)." 
        pack [entry $widget.data.ref -textvariable EQnick($monitorLines.EQ) -width 20 \
		  -state disabled] -side right
        set apsContextHelp($widget.data.ref) "Nick name of the corresponding equation."

        APSButton .delete -parent $widget.op -text "DELETE" \
	    -command "DeleteMonitorLine $widget0 $monitorLines" \
	    -contextHelp "Press to delete the corresponding EQ line."
        lappend unitsEntryList $widget.op.delete.button
	lappend changeWidgetStateList $widget.op.delete.button 
        $widget.op.delete configure -bd 0
        $widget.op.delete.button configure -font -adobe-courier-medium-r-normal-*-12-*-*-*-*-*-*-*
    }

    incr monitorLines
    tkwait visibility $widget.data.ref
    APSScrollAdjust $pvScrollFrame -numVisible 10
}

proc MakeNewMonitorLineForPAR {widget0 variable value units} {
    global PARnick PVnick PARunit EQnick
    global pvScrollFrame monitorLines run changeWidgetStateList
    global apsContextHelp unitsEntryList parSaveList


    if {![string length $variable] || ![string length $value]} {
	SetMainStatus "Not all entry submited for Parameter."
	bell
	return
    }
    foreach name [array names PVnick] {
	if {[string compare $variable $PVnick($name)]==0} {
	    SetMainStatus "Variable name entry \($variable\) already exists."
	    bell
	    return
	}
    }
    foreach name [array names EQnick] {
	if {[string compare $variable $EQnick($name)]==0} {
	    SetMainStatus "Variable name entry \($variable\) already exists."
	    bell
	    return
	}
    }
    foreach name [array names PARnick] {
	if {[string compare $variable $PARnick($name)]==0} {
	    SetMainStatus "Variable name entry \($variable\) already exists."
	    bell
	    return
	}
    }

    set widget $widget0.m$monitorLines
    if {![winfo exists $widget]} {
        frame $widget -bd 1
        pack $widget -fill x
        frame $widget.op 
        frame $widget.data
        pack $widget.data -side left -fill x
        pack $widget.op -side right -fill x

        global $variable
	set $variable $value
	lappend parSaveList $monitorLines.PAR
        set PARnick($monitorLines.PAR) $variable 
	if {[string length $units]} {
	    set PARunit($monitorLines.PAR) $units
	} else {
	    set PARunit($monitorLines.PAR) ""
        }

	pack [label $widget.data.label -text "" -width 0 -anchor c]\
	    -side left
        APSButton .button -parent $widget.data -text "Parameter" \
	    -command "DisplayParameter $monitorLines" -packOption "-anchor c -side left"\
	    -contextHelp "Invokes a display of a corresponding parameter editor."
	$widget.data.button.button configure -width 33

        pack [entry $widget.data.unit -textvariable PARunit($monitorLines.PAR) -width 7 \
		  -state normal] -side right
        set apsContextHelp($widget.data.unit) "Units for corresponding Parameter."
        lappend unitsEntryList $widget.data.unit
	lappend changeWidgetStateList $widget.data.unit
        pack [entry $widget.data.entry -textvariable $PARnick($monitorLines.PAR) -width 20 \
		  -state disabled] -side right
        set apsContextHelp($widget.data.entry) "Monitoring window for $PARnick($monitorLines.PAR)."
        pack [entry $widget.data.ref -textvariable PARnick($monitorLines.PAR) -width 20 \
		  -state disabled] -side right
        set apsContextHelp($widget.data.ref) "Parameter name."

        APSButton .delete -parent $widget.op -text "DELETE" \
	    -command "DeleteMonitorLine $widget0 $monitorLines" \
	    -contextHelp "Press to delete the corresponding Parameter line."
        lappend unitsEntryList $widget.op.delete.button
	lappend changeWidgetStateList $widget.op.delete.button
        $widget.op.delete configure -bd 0
        $widget.op.delete.button configure -font -adobe-courier-medium-r-normal-*-12-*-*-*-*-*-*-*
    }

    incr monitorLines
    tkwait visibility $widget.data.label
    APSScrollAdjust $pvScrollFrame -numVisible 10
}

proc DeleteMonitorLine {widget0 number} {
    global monitorLines changeWidgetStateList unitsEntryList
    global PVname PVnick PVunit EQname EQnick EQunit PARnick PARunit
    global pvSaveList eqSaveList parSaveList
    destroy $widget0.m$number
    

    foreach var [list PVnick EQnick PARnick] {
        foreach pos [array names $var] {
	    if {[string compare $pos $number.PV]==0} {
		global $PVnick($number.PV)
		set index [lsearch $pvSaveList $number.PV] 
		set pvSaveList [lreplace $pvSaveList $index $index]
		unset $PVnick($number.PV)
		foreach ar [list PVname($number.PV) PVnick($number.PV) PVunit($number.PV)] {
		    if {[catch {unset $ar} result]} {
			SetMainStatus "$result"
		    }
		}
	    }
	    if {[string compare $pos $number.EQ]==0} {
		global $EQnick($number.EQ)
		set index [lsearch $eqSaveList $number.EQ] 
		set eqSaveList [lreplace $eqSaveList $index $index]
		unset $EQnick($number.EQ)
		foreach ar [list EQname($number.EQ) EQnick($number.EQ) EQunit($number.EQ)] {
		    if {[catch {unset $ar} result]} {
			SetMainStatus "$result"
		    }
		}
	    }
	    if {[string compare $pos $number.PAR]==0} {
		global $PARnick($number.PAR)
		set index [lsearch $parSaveList $number.PAR]
		set parSaveList [lreplace $parSaveList $index $index]
		unset $PARnick($number.PAR)
		foreach ar [list PARnick($number.PAR) PARunit($number.PAR)] {
		    if {[catch {unset $ar} result]} {
			SetMainStatus "$result"
		    }
		}
	    }
	}
    }
    set i [lsearch $changeWidgetStateList $widget0.m$number.data.unit]
    set changeWidgetStateList [lreplace $changeWidgetStateList $i $i]
    set i [lsearch $unitsEntryList $widget0.m$number.data.unit]
    set unitsEntryList [lreplace $unitsEntryList $i $i]
    
    set j [lsearch $changeWidgetStateList $widget0.m$number.op.delete.button]
    set changeWidgetStateList [lreplace $changeWidgetStateList $j $j]   
    set j [lsearch $unitsEntryList $widget0.m$number.op.delete.button]
    set unitsEntryList [lreplace $unitsEntryList $j $j]   
}

proc SaveMonitorList {} {
    global monitorLines PVname PVnick PVunit EQname EQnick EQunit PARnick PARunit
    global Interval Significant dataArray
    global pvSaveList eqSaveList parSaveList

    set count 0
    for {set i 0} {$i<$monitorLines} {incr i} {
        if {[info exists PVname($i.PV)]} {
            if {[string length $PVname($i.PV)]} {
		incr count
            }
        }
        if {[info exists EQname($i.EQ)]} {
            if {[string length $EQname($i.EQ)]} {
		incr count
            }
        }
    }
    if {!$count} {
        SetMainStatus "No PV names given."
        bell
        return	
    }
    set outputFile \
	[APSInfoDialog [APSUniqueName .] \
	     -name "Configuration Output File" \
             -width 60 \
	     -infoMessage "Output filename: " \
             -contextHelp "Enter the name of the file to which to write the configuration."]

    if {[string length $outputFile]==0} {
        SetMainStatus "Saving Configuration is canceled."
        bell
        return
    }
    if {[string first " " $outputFile]!=-1} {
        SetMainStatus "Spaces are not allowed in the filename"
        bell
        return
    }

    if {[file exists $outputFile]} {
        bell
        set ok [APSYesNoPopUp "Delete existing $outputFile?"]
        if {!$ok} {
            SetMainStatus "Supply a new output filename"
            return
        }
    }

    SetMainStatus "Saving Configuration..."
    set nameList ""
    set typeList ""
    set variableList ""
    set parameterList ""
    set unitList ""
    foreach pv $pvSaveList {
	lappend nameList $PVname($pv)
	lappend variableList $PVnick($pv)
	lappend typeList pv
	lappend unitList "$PVunit($pv)"
    }
    foreach eq $eqSaveList {
	lappend nameList $EQname($eq)
	lappend variableList $EQnick($eq)
	lappend typeList eq
	lappend unitList "$EQunit($eq)"
    }
    foreach par $parSaveList {
	global $PARnick($par)
	if {[string length $PARunit($par)]} {
	    set dataArray(ParameterInfo.$PARnick($par)) "type SDDS_DOUBLE units \"$PARunit($par)\""
	} else {
	    set dataArray(ParameterInfo.$PARnick($par)) "type SDDS_DOUBLE"
	}
	set dataArray(Parameter.$PARnick($par)) [set $PARnick($par)] 
	lappend parameterList $PARnick($par)
    }
    lappend parameterList Interval
    lappend parameterList Significant
    set dataArray(ParameterNames) "$parameterList"
    set dataArray(ParameterInfo.Interval) "type SDDS_DOUBLE units s"
    set dataArray(ParameterInfo.Significant) "type SDDS_LONG"
    set dataArray(Parameter.Interval) $Interval
    set dataArray(Parameter.Significant) $Significant
    set dataArray(ColumnNames) "ControlName ControlVariable ControlType Unit"
    set dataArray(ColumnInfo.ControlName) "type SDDS_STRING"
    set dataArray(ColumnInfo.ControlVariable) "type SDDS_STRING"
    set dataArray(ColumnInfo.ControlType) "type SDDS_STRING"
    set dataArray(ColumnInfo.Unit) "type SDDS_STRING"

    set dataArray(Column.ControlName) [list $nameList]
    set dataArray(Column.ControlVariable) [list $variableList]
    set dataArray(Column.ControlType) [list $typeList]
    set dataArray(Column.Unit) [list $unitList]

    if {[catch {sdds save $outputFile dataArray} result]} {
        SetMainStatus "Unable to save $outputFile: $result"
	bell
	return
    }    
    SetMainStatus "Configuration is saved into $outputFile."
}

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/helios4/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 PVname PVnick EQname EQnick PARnick
    global monitorLines switch Interval Significant
    global Interval logFileSwitch run logFile pvSaveList eqSaveList

    if {!$monitorLines} {
        SetMainStatus "No entries supplied."
        bell
        return
    }
    if {$Interval < 1.0} {
        SetMainStatus "This application does not support Interval lower than 1.0."
        set Interval 1.0
        bell
    }
    set interval [expr {int($Interval * 1000)}]

    SetMainStatus "Running..."
    set run 1
    set i 1

    changeWidgetState 0
    set pvList ""
    set newpvList ""
    foreach name $pvSaveList {
	global $PVname($name) $PVnick($name)
	lappend pvList $PVname($name)
	lappend newpvList $PVnick($name)
    }
    foreach eq $eqSaveList {
	global $EQname($eq) $EQnick($eq)
    }
    set switch "Running..."
    if {[pv linkw $newpvList $pvList]} {
	SetMainStatus "Error from link."
    }
    .userFrame.ops.stop.button configure -state normal
    set fileID [open $logFile a+]
    while {$run} {
        if {$i==1} {set switch "Running...|...|...|."}
	if {$i==2} {set switch "Running.../.../.../."}
	if {$i==3} {set switch "Running...-...-...-."}
	if {$i==4} {
	    set switch "Running...\\...\\...\\."
	    set i 0
        }
	set newlist ""
        if {[pv getw $newpvList]} {
	    SetMainStatus "Error from pv."
        }
        if {$logFileSwitch} {
	    lappend newlist [clock seconds]
        }

        foreach el $newpvList {
            set $el [format %.${Significant}g [set $el]]
	    if {$logFileSwitch} {
		lappend newlist [set $el]
            }
	    update
        }

	foreach eq [array names EQname] {
	    if {[catch {set $EQnick($eq) [format %.${Significant}g [expr $EQname($eq)]]} result]} {
                SetMainStatus "Problem in expression \"$EQname($eq)\" :\n $result"
                bell
                set run 0
                .userFrame.ops.stop.button configure -state disabled
		changeWidgetState 1
		break
            }
	    if {$logFileSwitch} {
		lappend newlist [expr $EQname($eq)]
            }
            update
 	}
        if {$logFileSwitch} {
	    puts $fileID $newlist
            flush $fileID
        }
        incr i
        set wait 1
	after $interval {set wait 0}
        tkwait variable wait
    }
    close $fileID
    if {$logFileSwitch} {
        set logFileSwitch 0
    }
    SetMainStatus "Stopped." 
    set switch "Stopped... ... ... ."
    changeWidgetState 1
}

proc DisplayEquation {number} {
    global EQname EQnick run display n
    set n $number
    set display $EQname($number.EQ)
    set okCommand "update"

    if {[winfo exists .display$number]} {
	destroy .display$number
    }
    APSDialogBox .display$number -name "Editing equation \"$EQnick($number.EQ)\"" -width 120 \
        -okCommand $okCommand -contextHelp "Equation $EQnick($number.EQ) editor."
    APSLabeledEntry .eq -parent .display${number}.userFrame -label "$EQnick($number.EQ): "\
        -textVariable EQname($number.EQ) -width 120 -contextHelp "Original equation setup."
    set eq .display${number}.userFrame
    $eq.eq.entry configure -state disabled
    APSDialogBoxAddButton .edit -parent .display${number} -text "Enable edit" \
        -command "set run 0; $eq.eq.entry configure -state normal -bg grey; bell" \
        -contextHelp "Click this button to enable a text box for equation $EQnick($number.EQ) modifications"
    if {$run} {
        .display${number}.buttonRow.edit.button configure -state disabled
    }
}

proc DisplayParameter {number} {
    global PARnick PARunit run display n 
    set n $number
    set okCommand "update"
    global $PARnick($number.PAR)

    if {[winfo exists .displayPAR$number]} {
	destroy .displayPAR$number
    }
    APSDialogBox .displayPAR$number -name "Editing parameter \"$PARnick($number.PAR)\"" -width 120 \
        -okCommand $okCommand -contextHelp "Parameter $PARnick($number.PAR) editor."
    APSLabeledEntry .parValue -parent .displayPAR${number}.userFrame -label "$PARnick($number.PAR) value: "\
        -textVariable $PARnick($number.PAR) -width 40 -contextHelp "Parameter $PARnick($number.PAR) value."
    set par .displayPAR${number}.userFrame
    $par.parValue.entry configure -state disabled
    set command "$par.parValue.entry configure -state normal -bg grey"
    APSDialogBoxAddButton .edit -parent .displayPAR${number} -text "Enable edit" \
        -command "set run 0; $command; bell" \
        -contextHelp "Click this button to enable a text box for parameter $PARnick($number.PAR) modifications"
    if {$run} {
        .displayPAR${number}.buttonRow.edit.button configure -state disabled
    }
}

proc GenerateLogFileName {} {
    global logFile
    set userDirectory [pwd]
    set user [exec whoami]
    set logDir ""

    if {[string length $logFile]} {
	if {[file isdirectory $logFile]} {
	    set logDir $logFile
	} else { 
	    set logDir [file dirname $logFile]
	}
    }
    if {![string length $logDir]} {
	set logDir $userDirectory
    }
    set file "${user}-[clock format [clock seconds]\
              -format %y%m%d-%H%M%S]mon.sdds" 
    set logFile $logDir/$file
    update  
}

proc TurnLoggerOn {} {
    global logFile logFileSwitch PVname unitsEntryList

    if {![array size PVname]} {
	SetMainStatus "No PV name to monitor."
	set logFileSwitch 0
	update
	bell
	return
    }
    if {![string length $logFile]} {
	SetMainStatus "Enter log file name."
	set logFileSwitch 0
	update
	bell
	return
    }
    if {[file exists $logFile]} {
        bell
        set ok [APSYesNoPopUp "Delete existing $logFile?"]
        if {!$ok} {
            SetMainStatus "Supply a new log filename"
            set logFileSwitch 0
            update
            bell
            return
        }
    }
    WriteLogHeader
    .userFrame.settings.log.file.entry configure -state disabled
    .userFrame.settings.log.gener.button configure -state disabled
    foreach un $unitsEntryList {
        $un configure -state disabled
    }
    SetMainStatus "Logger is on. File $logFile"
    bell
}

proc TurnLoggerOff {} {
    global logFileSwitch unitsEntryList
    .userFrame.settings.log.file.entry configure -state normal
    .userFrame.settings.log.gener.button configure -state normal
    foreach un $unitsEntryList {
        $un configure -state normal
    }
    if {$logFileSwitch} {
	set logTextSwitch 0
	SetMainStatus "Logger is off."
	bell
    }
}

proc WriteLogHeader {} {
    global Interval Significant
    global PVname PVnick PVunit EQname EQnick EQunit PARnick PARunit logFile
    global pvSaveList eqSaveList parSaveList Parameter
    set columnList ""
    set parameterList ""
    lappend parameterList Interval
    lappend parameterList Significant

    set ParameterInfo(Interval) "units=s, type=double"
    set ParameterInfo(Significant) "type=long"
    set Parameter(Interval) $Interval
    set Parameter(Significant) $Significant


    foreach par $parSaveList {
        set v $PARnick($par)
        lappend parameterList $v
        if {[string length $PARunit($par)]} {
            set ParameterInfo($v) "units=\"$PARunit($par)\", type=double"
        } else {
	    set ParameterInfo($v) "type=double"
        }
        global $PARnick($par)
	set Parameter($v) [set $PARnick($par)]
    }
    
    lappend columnList Time
    set ColumnInfo(Time) "units=s, type=long"
    foreach p $pvSaveList {
        set z $PVnick($p)
        lappend columnList $z
        if {[string length $PVunit($p)]} {
            set ColumnInfo($z) "description=\"$PVname($p)\", units=\"$PVunit($p)\" type=double"
        } else {
	    set ColumnInfo($z) "type=double"
        }
    }
    foreach e $eqSaveList {
        set z $EQnick($e)
        lappend columnList $z
        if {[string length $EQunit($e)]} {
            set ColumnInfo($z) "description=\"$EQname($e)\", units=\"$EQunit($e)\", type=double"
        } else {
            set ColumnInfo($z) "description=\"$EQname($e)\", type=double"
        }
    }

    set fileID [open $logFile w+]
    puts $fileID "SDDS1"
    foreach p $parameterList {
        puts $fileID "&parameter name=$p, $ParameterInfo($p), &end"
    }
    foreach col $columnList {
        puts $fileID "&column name=$col, $ColumnInfo($col), &end"
    }
    puts $fileID "&data mode=ascii, no_row_counts=1, &end"
    puts $fileID "! page number 1"
    foreach p $parameterList {
	puts $fileID "$Parameter($p)"
    }
    flush $fileID
    close $fileID
}

proc StopRun {} {
    global run PVname PVnick EQname EQnick logFile logFileSwitch
    set run 0
    .userFrame.ops.stop.button configure -state disabled
    update
    TurnLoggerOff
}

proc EvaluateEquation {equation} {
    global PVnick EQnick PARnick

    foreach pv [array names PVnick] {
	set $PVnick($pv) [Random -seed 5 -range 10]
    }
    foreach eq [array names EQnick] {
	set $EQnick($eq) 1
    }
    foreach par [array names PARnick] {
	global $PARnick($par)
    }
    
    if {[catch {expr $equation} result]} {
	return -code error "$result"
    } 
}

proc Random {args} {
    set seed 1
    set range 1
    APSStrictParseArguments {seed range}
    
    set timeV [expr {([clock seconds] - 941100000)}]
    set random [expr {(((($seed*$timeV)+49297) % 233280) / double(233280))*$range}]

    return $random
}

proc changeWidgetState {state} {
    global changeWidgetStateList 
    
    if {$state} {
	set form normal
    } else {
	set form disabled
    }
    foreach w $changeWidgetStateList {
        $w configure -state $form
    }
}

proc PlotLogFile {} {
    global logFile

    if {![string length $logFile]} {
	SetMainStatus "Log file name is not provided."
	bell
	return
    }
    if {![file exists $logFile]} {
	SetMainStatus "Log file $logFile does not exists."
	bell
	return
    }
    eval exec quickSDDSplot -dataFileList $logFile &
}

##############

APSApplication . -name UserCustomMonitor -version $CVSRevisionAuthor \
    -overview {This is the UserCustomMonitor utility.  It provides the user with an interface to create his/her own custom made monitoring tool. The user may create a display for any equation which includes previously chosen PVs. The equation is updated automatically. The user has possibility to save data from each run in the log file. The user has possibility to save a current configuration in the output file or load a configuration from the specified file.}

.menu.file.menu insert 1 separator
.menu.file.menu insert 1 command -label "Save Config..." -underline 0 \
    -command SaveMonitorList
.menu.file.menu insert 1 command -label "Load Config..." -underline 0 \
    -command LoadExistingInput


set monitors 0
set monitorLines 0
set changeWidgetStateList ""
set unitsEntryList ""
set PVentry ""
set PVvariable ""
set pvSaveList ""
set EQentry ""
set EQvariable ""
set eqSaveList ""
set PARvalue ""
set PARvariable ""
set parSaveList ""

set mainStatus "Use one of \"Add ...\" buttons to provide input."

APSScrolledStatus .status -parent .userFrame -textVariable mainStatus -width 90\
    -withButtons 1
pack [entry .userFrame.entry1 -textvariable switch -width 19 -state disabled]\
    -side top

pack [frame .userFrame.title -bd 1 -relief flat] -side top -fill x
pack [label .userFrame.title.label1 -text "PV / Equation / Parameter" -width 35 -anchor c]\
    -side left
pack [label .userFrame.title.label2 -text "  Variable" -width 20 -anchor c]\
    -side left
pack [label .userFrame.title.label3 -text "Value" -width 20 -anchor c]\
    -side left
pack [label .userFrame.title.label4 -text "  Units" -width 12 -anchor w]\
    -side left 

frame .userFrame.lines  -bd 4  -relief raised
pack .userFrame.lines -side top -fill x
set pvScroll [APSScroll .sw -parent .userFrame.lines -name "PV List"]
set pvScrollFrame .userFrame.lines.sw

pack [frame .userFrame.lines.add -bd 4  -relief ridge] -side top -fill x
APSButton .addPV -parent .userFrame.lines.add -text "Add Process Variable..." \
    -command addNewPV -packOption "-side left -fill x" -contextHelp "Press for PV entry box."
.userFrame.lines.add.addPV.button configure -width 29
lappend changeWidgetStateList .userFrame.lines.add.addPV.button

APSButton .addEQ -parent .userFrame.lines.add -text "Add Equation..." \
    -command addNewEQ -packOption "-side left -fill x" -contextHelp "Press for EQ entry box."
.userFrame.lines.add.addEQ.button configure -width 30
lappend changeWidgetStateList .userFrame.lines.add.addEQ.button

APSButton .addPAR -parent .userFrame.lines.add -text "Add New Parameter..." \
    -command addNewPAR -packOption "-side right -fill x" -contextHelp "Press for Parameter entry box."
.userFrame.lines.add.addPAR.button configure -width 29
lappend changeWidgetStateList .userFrame.lines.add.addPAR.button

frame .userFrame.settings -bd 4 -relief raised
pack .userFrame.settings -side top -fill x
set Interval 1
set logFile ""

pack [frame .userFrame.settings.add -bd 1 -relief flat] -side top -fill x
APSLabeledEntry .settings.add.interval -parent .userFrame -label "Interval (s):   " \
    -textVariable Interval -width 10 -packOption "-side left" \
    -contextHelp "Enter the time between samples in seconds in this field."
set Significant 9
APSLabeledEntry .settings.add.significant -parent .userFrame -label \
    "   Display NumbOfDigits : " \
    -textVariable Significant -width 10 -packOption "-side left" \
    -contextHelp "Enter the number of significant digits to be on display."
set logFileSwitch 0
APSRadioButtonFrame .settings.add.radio -parent .userFrame -packOption "-side right" \
    -label "Turn logger :" -variable logFileSwitch -buttonList {On Off} \
    -valueList {1 0} -orientation horizontal -commandList {TurnLoggerOn TurnLoggerOff}\
    -contextHelp "Logger on prepares logging procedure. A log file name must be supplied or\
    generated prior to this action. Run button invokes actual data logger."
lappend changeWidgetStateList .userFrame.settings.add.radio.frame.button1
lappend changeWidgetStateList .userFrame.settings.add.radio.frame.button2


pack [frame .userFrame.settings.log -bd 1 -relief flat]  -side top -fill x
APSLabeledEntry .file -parent .userFrame.settings.log -label "Log File:       " \
    -textVariable logFile -width 62 -packOption "-side left"\
    -contextHelp "Enter a log file name or generate by clicking next button."
APSButton .gener -parent .userFrame.settings.log -text "  Generate Name  "\
    -packOption "-side right" -size small -command GenerateLogFileName\
    -contextHelp "Generates a log file name with the user name and time stamp."
lappend changeWidgetStateList .userFrame.settings.log.gener.button

frame .userFrame.ops -bd 4 -relief raised
pack .userFrame.ops -side top -fill x
set w .userFrame.ops
APSButton .run -parent $w -text RUN -command RunMonitor \
    -contextHelp "Launches a monitoring subprocess and a logger to collect the data."
APSButton .stop -parent $w -text STOP -command StopRun \
    -contextHelp "Interrupting a monitoring subprocess."
APSButton .new -parent $w -text "NEW..." -command "exec UserCustomMonitor &" \
    -contextHelp "Launches a new instance of the UserCustomMonitor screen"
APSButton .clear -parent $w -text "CLEAR ALL" -command "ClearAllSettings $pvScroll" \
    -contextHelp "Clears all of the settings for UserCustomMonitor, including PV names and filenames."
APSButton .namecap -parent $w -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 UserCustomMonitor.  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 UserCustomMonitor entry box, or have name capture put names in a file that you can load into UserCustomMonitor using LOAD INPUT..."
APSButton .plot -parent $w -text "PLOT LOG..." -command PlotLogFile \
    -contextHelp "Launches a quickSDDSplot application to plot data from the log file."

foreach butt [list .run.button .clear.button .namecap.button] {
    lappend changeWidgetStateList ${w}$butt
}

.userFrame.ops.stop.button configure -state disabled

set fileSelListDir $env(HOME)

