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

# $Log: not supported by cvs2svn $
# Revision ??? 2013/11/11 shang
# made extensive changes per Louis Emery's suggestion 1)made the entry widget stretchable so that we can see the hiding text
# with stretching the GUI. 2) made "Delete Knob Line" button work and added "View" button for displaying knob file contains.
# 3) added debug option for commandline execution so that user can turn off the debug statements when running from commandline.
# 4) fixed the problem of re-viewing an execution command which complained that the widget already exists. 5) fixed the bug that
# the non-existent PV was added to the monitor name list.
#
# Revision 1.53  2011/03/14 14:27:44  shang
# added ExecLine to run customer program.
#
# Revision 1.52  2011/03/11 17:48:17  shang
# added ExecLine global varaible so that execution line can be passed to script.
#
# Revision 1.51  2011/03/10 21:43:23  shang
# changed the floatFormat of cavget from %f to %.15g because %f can not display very small numbers, for example, 4.5e-7.
#
# Revision 1.50  2011/03/08 20:31:18  shang
# added "-floatFormat=%f" to cavget statements so that it won't lose digits for big numbers.
#
# Revision 1.49  2011/03/08 19:03:43  shang
# added "TotalSteps" parameter to statistics log files.
#
# Revision 1.48  2011/03/08 18:57:17  shang
# added TotalSteps parameter to log file.
#
# Revision 1.47  2011/02/16 19:34:49  shang
# to avoid round-up for integers, used float type for computing control PV values at each step.
#
# Revision 1.46  2010/06/04 15:54:37  shang
# fixed a bug in saving configs that the config files should be deleted when nothing to be saved for that config.
#
# Revision 1.45  2009/07/24 20:09:53  shang
# fixed problem in running commandline
#
# Revision 1.44  2009/07/24 20:00:26  shang
# changed "puts stderr" to "puts" in SetMainStatus proc so that the message will be printed to standard output instead of standar error (which will be caught as errors)XC
#
# Revision 1.43  2009/07/22 16:03:43  shang
# fixed an error that "Step" variable does not exist when TERMINATE button is pressed.
#
# Revision 1.42  2009/07/21 17:56:50  soliday
# Updated to fix 'global' commands that tried to globalize an array element
# instead of the array. This was causing error messages with the new Tcl/Tk version.
#
# Revision 1.41  2009/03/30 21:10:08  shang
# fixed a bug in "EDIT" button of process variables
#
# Revision 1.40  2008/12/03 15:23:51  shang
# fixed problem in postprocessing that ExpSteps could not be read.
#
# Revision 1.39  2007/09/19 18:45:59  shang
# added StepString variable with format of %06d of Step
#
# Revision 1.38  2007/04/25 15:35:14  shang
# fixed the problem of saving postprocess configuration when "" is needed for the options.
#
# Revision 1.37  2007/04/25 15:17:56  shang
# fixed a typo
#
# Revision 1.36  2007/04/25 14:55:28  shang
# added knob files feature and fixed a bug in saveing postprocessing config.
#
# Revision 1.35  2006/04/06 21:59:34  shang
# checked if the sdds program options contains $ character before use "eval set".
#
# Revision 1.34  2006/04/06 21:04:05  shang
# made it is able to pass the values of global variables into runSDDSprogram execution also.
#
# Revision 1.33  2006/04/06 20:27:50  shang
# fixed a bug in sdds2stream statement for extracting change-control PV values from
# change-control file.
#
# Revision 1.32  2006/03/11 01:27:57  shang
# set the default relative for control pv to "No"; added "Add Script" to process variable panel to pass the value of a script to tcl variable; made it possible to pass the values of variables in the process panel to custom program and scripts.
#
# Revision 1.31  2006/02/23 15:47:15  soliday
# Updated because some string trimimng statements were not trimming the strings
# exactly as the author intended.
#
# Revision 1.30  2006/02/22 22:26:41  shang
# added editButton to postproces option entry
#
# Revision 1.29  2006/02/21 00:18:01  shang
# fixed more bugs with testing.
#
# Revision 1.28  2006/02/20 22:42:44  emery
# Corrected a context help explaining the setpoint PV.
#
# Revision 1.27  2006/02/15 05:05:51  emery
# Changed OutputfileSuffix fgrom "program" to "sdds" so that it is
# consistent with the default sddsProgramOutputSuffix upon loading
# a saved configuration.
#
# Revision 1.26  2006/02/15 04:15:10  emery
# Changed some wording in messages and context help.
#
# Revision 1.25  2005/12/12 20:33:42  shang
# fixed spelling errors and bugs in load/save configuration, added applicationName command
# line option to create applications with given name.
#
# Revision 1.24  2005/11/02 20:46:44  shang
# replaced "pv putw" by "pv putq" since putw waiting for processing complete
# sometimes it returns timeout errors since some processing takes a long time to
# complete.
#
# Revision 1.23  2005/11/01 23:14:23  emery
# Added global errorCode errorInfo where needed.
#
# Revision 1.22  2005/10/21 18:49:59  shang
# rewrote some procedures to have better performance and removed the reduanant codes,
# added more new features.
#
# rewrote ExperimentDesinger to make it easier to modify
#
# Revision 1.21  2005/03/04 20:32:47  shang
# removed the setfacl statement
#

set auto_path [linsert $auto_path 0 /usr/local/oag/apps/lib/$env(HOST_ARCH)]
set auto_path [linsert $auto_path 0 /usr/local/oag/lib_patch/$env(HOST_ARCH)]
#set auto_path [linsert $auto_path 0 /home/oxygen/SOLIDAY/oag/apps/src/tcltklib]


set apsttk 0
if {$tcl_platform(os) == "Darwin"} {
    #This could be set for all operating systems if desired
    set apsttk 1
}
APSStandardSetup

set CVSRevisionAuthor "\$Revision: 1.54 $ \$Author: shang $"

proc SetMainStatus {text} {     
    global mainStatus popupInterface debug
    if $popupInterface {
        set mainStatus "[clock format [clock seconds] -format %H:%M:%S] $text"
    } else {
       if $debug {
           puts "[clock format [clock seconds] -format %H:%M:%S] $text"
       }
    }
    update      
}   

proc closeExperiment {args} {
    global logFileID Stats pvVarList controlPVVarList StartStep logFile
    global restoreControl globalList controlPVList
    eval global $globalList
    global errorInfo errorCode
    
    if $restoreControl {
	set putList ""
        foreach var $controlPVVarList pv $controlPVList {
            global orig$var $var
            set $var [set orig$var] 
	    lappend putList $pv=[set orig$var]
            update
        }
        update
        set errorInfo ""
        set errorCode "" 
       # if [pv putw $controlPVVarList 30] {
       #     return -code error "Unable to set control PVs: $errorInfo, $errorCode"
       # }
	if [catch {exec cavput -list=[join $putList ,] -pend=30 } result] {
	    return -code error "Error restore control pv values: $result"
	}
        after 2000
    }
    if [llength $pvVarList] {
        pv unlink $pvVarList
    }
    if [llength $controlPVVarList] {
        pv unlink $controlPVVarList
    }
    if [string length $logFileID] {
        close $logFileID
        set logFileID ""
        if $logCombine {
            set tmpfile /tmp/[APSTmpString]
            if [catch {exec sddscombine $logFile $logFile.old $tmpfile} result] {
                return -code error $result
            }
            exec mv $tmpfile $logFile
        }
    }
    if {[info exists Stats(fileID)] && [string length $Stats(fileID)]} {
        close $Stats(fileID)
        set Stats(fileID) ""
        if $Stats(combine) {
            set tmpfile /tmp/[APSTmpString]
            if [catch {exec sddscombine $Stats(file) $Stats(file).old $tmpfile} result] {
                return -code error $result
            }
            exec mv $tmpfile $Stats(file)
        }
    }
    foreach nm $globalList {
        set $nm 0
    }
    set PauseStep $StartStep
}

if {$tcl_platform(os) == "Darwin"} {
    ttk::style configure Fixed.TLabel -font "Courier 14"
} else {
    eval font create largeFixedFont [font actual TkFixedFont]
    font configure largeFixedFont -size -13
    ttk::style configure Fixed.TLabel -font largeFixedFont
}

proc CreatePVWidget {parent} {
    global monitorScroll monitorScrollFrame changeWidgetStateList brief
    APSFrame .pvframe -name "PV Information" -parent $parent -packOption "-side top -fill x" 
    set w $parent.pvframe.frame
    $w configure -relief flat -borderwidth 0
    APSFrame .title -parent $w -packOption "-side top -fill x"
    set w1 $w.title.frame
    $w1 configure -relief flat -borderwidth 0
    APSLabel .pv -parent $w1 -text "      PV / Equation    " -width 25 -packOption "-side left" \
      -contextHelp "PV name or equation" -style "Fixed.TLabel"
    APSLabel .var -parent $w1 -text "      Variable     " -width 11 -packOption "-side left" \
      -contextHelp "The variable name (is also the column name in the output file)" -style "Fixed.TLabel"
    APSLabel .val -parent $w1 -text "   Value     " -width 16 -packOption "-side left" \
      -contextHelp "display the current value of PV or equation." -style "Fixed.TLabel"
    APSLabel .unit -parent $w1 -text " Units   " -width 7 -packOption "-side left" \
      -contextHelp "unit of the PV." -style "Fixed.TLabel"
    APSLabel .min -parent $w1 -text "Minimum" -width 15 -packOption "-side left" \
      -contextHelp "The minimum value allowed for the PV, used for testing." -style "Fixed.TLabel"
    APSLabel .max -parent $w1 -text "      Maximum" -width 15 -packOption "-side left" \
      -contextHelp "The maximum value allowed for the PV, used for testing." -style "Fixed.TLabel"
    APSLabel .type -parent $w1 -text "      PVtype" -width 12 -packOption "-side left" \
      -contextHelp "the type of PV: readback, control or output." -style "Fixed.TLabel"
    APSFrame .scroll -parent $w -packOption "-side top -fill x"
    set w2 $w.scroll.frame
    $w2 configure -relief flat -borderwidth 0
    set monitorScroll [APSScroll .pvScroll -parent $w2]
    set monitorScrollFrame $w2.pvScroll

    if !$brief {
        APSFrame .but -parent $w -packOption "-side top -fill x"
        set w3 $w.but.frame
        $w3 configure -relief flat -borderwidth 0
        APSButton .addPV -parent $w3 -text "Add Process Variable ..." \
          -command addNewPV -contextHelp "Press to add another PV name
            entry line." -width ""
        APSButton .addEQ -parent $w3 -text "Add Equation ..." \
          -command addNewEQ -contextHelp "Press to add another equation line." -width ""
        APSButton .addPar -parent $w3 -text "Add Parameter ..." \
          -command addNewPAR -contextHelp "Press to add another parameter line." -width ""
        APSButton .addScript -parent $w3 -text "Add Script ..." \
            -command addNewScriptMon -width ""
        APSButton .clear -parent $w3 -text "Clear" \
          -command "ClearPVSettings $monitorScroll" -contextHelp "Press to clear all pv lines." -width ""
        APSButton .read -parent $w3 -text "Read Value" \
          -command "ReadValue 0 -savefile 0" -contextHelp "Press to read the pv values." -width ""
        lappend changeWidgetStateList $w3.addPV.button
        lappend changeWidgetStateList $w3.addEQ.button
        lappend changeWidgetStateList $w3.addPar.button
        lappend changeWidgetStateList $w3.addScript.button
        lappend changeWidgetStateList $w3.clear.button
    }
}

proc CreateInitWidget {parent} {
    global initScroll initScrollFrame  changeWidgetStateList
    
    APSFrame .initframe -name "Initialization" -parent $parent -packOption "-side top -fill x"
    set w $parent.initframe.frame
    $w configure -relief flat -borderwidth 0
    APSFrame .steps -parent $w -packOption "-side top -fill x"
    set w1 $w.steps.frame
    $w1 configure -relief flat -borderwidth 0
    APSLabel .desc -parent $w1 -text "  Press \"Add Init Entry\" to add the initialization steps."

    APSFrame .title -parent $w -packOption "-side top -fill x"
    set w1 $w.title.frame
    $w1 configure -relief flat -borderwidth 0
    APSLabel .pv -parent $w1 -text "        PV name       " -width 25 -packOption "-side left" \
      -contextHelp "PV name" -style "Fixed.TLabel"
    APSLabel .var -parent $w1 -text "        Readback name " -width 25 -packOption "-side left" \
      -contextHelp "The name of the readback" -style "Fixed.TLabel"
    APSLabel .val -parent $w1 -text "             set_value" -width 25 -packOption "-side left" \
      -contextHelp "The value for setting the PV to." -style "Fixed.TLabel"
    APSLabel .unit -parent $w1 -text "   orig_value" -width 25 -packOption "-side left" \
      -contextHelp "The initial value of the PV" -style "Fixed.TLabel"
    APSLabel .min -parent $w1 -text "  tolerance" -width 25 -packOption "-side left" \
      -contextHelp "The tolerance for setting the PV." -style "Fixed.TLabel"

    APSFrame .scroll -parent $w -packOption "-side top -fill x -expand true"
    set w2 $w.scroll.frame
    $w2 configure -relief flat -borderwidth 0
    set initScroll [APSScroll .initScroll -parent $w2 -packOption "-side top -fill x -expand true"]
    set initScrollFrame $w2.initScroll

    APSFrame .but -parent $w -packOption "-side top -fill x"
    set w3 $w.but.frame
    $w3 configure -relief flat -borderwidth 0
    APSButton .addPV -parent $w3 -text "Add Init Entry" \
      -command "AddInitFinalizeLine -makeType init" \
      -contextHelp "Press to add the initializing pvs in order" -width ""
    APSButton .clear -parent $w3 -text "Clear" \
      -command {ClearInitFinalizeSettings $initScroll -makeType init} -contextHelp "Press to clear all initialization lines" -width ""
    lappend changeWidgetStateList $w3.addPV.button
    lappend changeWidgetStateList $w3.clear.button
}

proc UpdatePauseStep {args} {
    global StartStep PauseStep
    set PauseStep $StartStep
}

proc SetDailyOutputDir {args} {
    global daily outputDir
    if $daily {
        set outputDir [APSGoToDailyDirectory]
    }
}

set autoIncr 0
set daily 0
proc CreateExecuteWidget {parent} {
    global execScroll execScrollFrame changeWidgetStateList ExpSteps Interval StartStep autoIncr daily
    global outputDir outputRootname Description Timeout RunPostprocess runStatsWithTest
   
    APSFrame .execframe -name "Execution Design" -parent $parent -packOption "-side top -fill x"
    set w $parent.execframe.frame
    $w configure -relief flat -borderwidth 0
    APSFrame .settings -parent $w -packOption "-side top -fill x"
    set widget $w.settings.frame
    APSLabeledEntry .step -parent $widget -label "Steps" -textVariable ExpSteps \
      -width 20 -packOption "-side left" -contextHelp \
      "Pleaset enter the number of total execution steps, variable name is Steps."
    APSLabeledEntry .startstep -parent $widget -label "Start Step: " -textVariable StartStep \
      -width 10 -packOption "-side left" -contextHelp \
      "The start step of the experiment, the variable name is StartStep."
    bind $widget.startstep.entry <Leave> "UpdatePauseStep"
    APSLabeledEntry .interval -parent $widget -label "Interval (s)" -textVariable Interval \
      -width 10 -packOption "-side left" -contextHelp "Please enter interval (pause time) \
                in seconds between two steps."
    APSLabeledEntry .timeout -parent $widget -label "Timeout (s)" -textVariable Timeout \
      -width 10 -packOption "-side left" -contextHelp "Please enter the maximum allowed waiting time when pv is out of range"
    APSFrame .f2 -parent $w -packOption "-side top -fill x"
    set w2 $w.f2.frame
    APSRadioButtonFrame .post -parent $w2 -label "  Run Postprocess?" \
      -variable RunPostprocess -buttonList {Yes "No  "} -valueList {1 0} -orientation horizontal \
      -contextHelp "postprocessing defined in Postprocess widget will be run after the
experiment is done if answer is yes!" -packOption "-side left"
    APSRadioButtonFrame .stattest -parent $w2 -label "  Run statistics with test?" \
      -variable runStatsWithTest -buttonList {Yes "No  "} -valueList {1 0} -orientation horizontal \
      -contextHelp "run statistics with test if it is yes!"  -packOption "-side left"
   
    APSFrame .logfile -parent $w -packOption "-side top -fill x"
    set widget $w.logfile.frame
    APSLabeledEntry .outputdir -parent $widget -label "Output Directory:" \
      -textVariable outputDir -width 80 -packOption "-fill x -expand true" \
      -contextHelp "The directory for all output files in the executions, variable name is outputDir."
    APSButton .p -parent $widget.outputdir -text p -packOption "-side right" \
      -contextHelp "click to pick a directory for output files." \
      -command {set outputDir [APSFileSelectDialog .choosedirectory -listDir $outputDir \
                                 -title "Choose a directory for output files" -selectDir 1]}
   # APSButton .daily -parent $widget.outputdir -text daily -packOption "-side right" \
    #  -command "set outputDir [APSGoToDailyDirectory]"
    APSCheckButtonFrame .dc -parent $widget.outputdir -label "" -buttonList {use_daily_dir} -packOption "-side right" \
      -variableList {daily} -orientation horizontal -contextHelp "if checked, the output directory will be current daily directory." \
      -commandList {SetDailyOutputDir}
    set outputRootname exp[format %03d [APSUniqueNumber]]
    APSLabeledEntry .outputroot -parent $widget -label "Output rootname:" \
      -textVariable outputRootname -width 80 -packOption "-fill x -expand true" \
      -contextHelp "The rootname for all output files, variable name is outputRootname"
    #APSButton .gen -parent $widget.outputroot -packOption "-side right" \
     # -text "gen" -command "set outputRootname exp[format %03d [APSUniqueNumber]]"
    APSCheckButtonFrame .check -parent $widget.outputroot -packOption "-side right" -label "" \
      -variableList autoIncr -buttonList AutoIncr -orientation horizontal \
      -contextHelp "if auto increment is checked, the rootname will be added with an automatically increased number for each run."
    bind $widget.outputdir.entry <Leave> UpdateOutputFiles
    bind $widget.outputroot.entry <Leave> UpdateOutputFiles
    APSLabeledEntry .desp -parent $widget -label "Experiment Description:" -textVariable Description \
      -contextHelp "Enter the description for experiment, the variable name is Description." \
      -width 80 -packOption "-fill x -expand true"

    APSFrame .display -parent $w -packOption "-side top -fill x"
    set w1 $w.display.frame
    $w1 configure -relief flat -borderwidth 0
    APSLabel .desc -parent $w1 -text "Press \"Add Exec Entry\" button to add the execution steps in order" \
      -packOption "-side left"

    APSFrame .scroll -parent $w -packOption "-side top -fill x"
    set w2 $w.scroll.frame
    $w2 configure -relief flat -borderwidth 0
    set execScroll [APSScroll .execScroll -parent $w2]
    set execScrollFrame $w2.execScroll

    APSFrame .but -parent $w -packOption "-side top -fill x"
    set w3 $w.but.frame
    $w3 configure -relief flat -borderwidth 0
    APSButton .addExec -parent $w3 -text "Add Exec Entry" \
      -command addExecEntry -contextHelp "Press to add the execution steps in order" -width ""
    APSButton .clear -parent $w3 -text "Clear" \
      -command {ClearExecSettings $execScroll} -contextHelp "Press to clear all execution lines" -width ""
    lappend changeWidgetStateList $w3.addExec.button
    lappend changeWidgetStateList $w3.clear.button
}


proc CreatePostprocessWidget {parent} {
    global postProcessScroll postProcessScrollFrame outputDir outputRootname
    global postProcessLines postProcess changeWidgetStateList

    APSFrame .post -parent $parent -name "PostProcess" -packOption "-side top -fill x"
    set w $parent.post.frame
    $w configure -relief flat -borderwidth 0
    APSFrame .desc -parent $w -packOption "-side top -fill x"
    set w1 $w.desc.frame
    $w1 configure -relief flat -borderwidth 0
    APSLabel .txt -parent $w1 -text "Press Add Postprocess button to add post-processing command" 
    APSFrame .scroll -parent $w -packOption "-side top -fill x"
    set w2 $w.scroll.frame
    $w2 configure -relief flat -borderwidth 0
    set postProcessScroll [APSScroll .pvScroll -parent $w2]
    set postProcessScrollFrame $w2.pvScroll

    APSFrame .but -parent $w -packOption "-side top -fill x"
    set w3 $w.but.frame
    $w3 configure -relief flat -borderwidth 0
    APSButton .add -parent $w3 -text "Add Postprocess ..." \
      -command "MakeNewPostprocessLine $postProcessScroll -mode add" -contextHelp "Press to add another postprocess
   entry line." -width ""
    APSButton .clear -parent $w3 -text "Clear All" \
      -command "ClearPostprocessSettings $postProcessScroll" \
      -contextHelp "Press to clear all arguments lines." -width ""
    lappend changeWidgetStateList $w3.add.button
    lappend chagneWidgetStateList $w3.clear.button
}

proc IncrRootname {args} {
    global outputRootname
    if ![string length outputRootname] {
        set outputRootname exp001
        return
    }
    set ln [string length $outputRootname]
    set nIndex -1
    for {set i 0} {$i<$ln} {incr i} {
        set a [string index $outputRootname $i]
        if [catch {expr $a /2 } result] {
            set nIndex -1
            continue
        } else {
            if {$nIndex<0} {
                set nIndex $i
            }
        }
    }
    if {$nIndex<0} {
        set outputRootname ${outputRootname}0001
    } else {
        set root [string range $outputRootname 0 [expr $nIndex -1]]
        set num [scan $outputRootname ${root}%ld]
        set outputRootname ${root}[format %04d [expr $num+1]]
    }
}

proc CreateNewPostprocessLine {widget0 line} {
    global postProcess  postProcessScrollFrame  changeWidgetStateList brief
    if [winfo exist $widget0.m$line] {
        destroy $widget0.m$line
    }
    if [winfo exist $widget0.opt$line] {
	destroy $widget0.opt$line
    }
    if !$postProcess($line.valid) {
        return
    }
    APSFrame .m$line -parent $widget0
    set w $widget0.m$line.frame
    $w configure -relief flat -borderwidth 0
    APSLabeledEntry .com -parent $w -label "Command:" -textVariable postProcess($line.command) \
      -width 70 -packOption "-side left"
    APSButton .insert -parent $w -text "INSERT" -command "MakeNewPostprocessLine $widget0 -line $line -mode insert" -width ""
    APSButton .delete -parent $w -text "DELETE" -command "DeletePostprocessLine $widget0 $line" -width ""
    APSButton .run -parent $w -text "RUN" -command "TestPostprocess $line" \
	-contextHelp "click to run this post-process command" -width ""
    APSFrame .opt$line -parent $widget0 -packOption "-side top"
    $widget0.opt$line.frame configure -relief flat -borderwidth 0
    APSLabeledEntry .opt -parent $widget0.opt$line.frame \
        -label "    Option:" -textVariable postProcess($line.option) \
        -width 90 -packOption "-side top"  -editButton 1
 #   lappend changeWidgetStateList $w.delete.button
 #   lappend changeWidgetStateList $w.insert.button
    if $brief {
        .userFrame.main.frame.tn select 2
    } else {
        .userFrame.main.frame.tn select 6
    }
    tkwait visibility $w.com
    APSScrollAdjust $postProcessScrollFrame -numVisible 9
}

proc ClearPostprocessSettings {widget0 args} {
    set popup 1
    APSParseArguments {popup}
    
    global postProcess postProcessLines changeWidgetStateList
    if !$postProcessLines {
        return
    }
    if $popup {
        if {![APSYesNoPopUp "Really? Clear all post-process settings?"]} { return }
    }
    for {set i 0} {$i<$postProcessLines} {incr i} {
        if [winfo exists $widget0.m$i] {
            destroy $widget0.m$i
        }
        if [winfo exist $widget0.opt$i] {
            destroy $widget0.opt$i
        }
       # set j [lsearch $changeWidgetStateList  $widget0.m$i.frame.insert.button]
       # set changeWidgetStateList [lreplace $changeWidgetStateList $j $j]
       # set j [lsearch $changeWidgetStateList  $widget0.m$i.frame.delete.button]
        #set changeWidgetStateList [lreplace $changeWidgetStateList $j $j]
    }
    set postProcessLines 0
}

proc MakeNewPostprocessLine {widget0 args} {
    global postProcess postProcessLines postProcessScrollFrame  changeWidgetStateList popupInterface
    set mode add 
    set line ""
    set PostprocessCommand ""
    set postProcessOption ""
    APSParseArguments {mode line PostprocessCommand postProcessOption}
    
    if {$mode=="insert"} {
        for {set i $postProcessLines} {$i>$line} {incr i -1} {
            set j [expr $i -1]
            set postProcess($i.command) $postProcess($j.command)
	    set postProcess($i.option) $postProcess($j.option)
            set postProcess($i.valid) $postProcess($j.valid)
        }
        set postProcess($line.command) $PostprocessCommand
	set postProcess($line.option) $postProcessOption
        set posProcess($line.valid) 1
        incr postProcessLines
        if $popupInterface {
            for {set i $line} {$i<$postProcessLines} {incr i} {
                if [winfo exist $widget0.m$i] {
                    destroy $widget0.m$i
                }
                if $postProcess($i.valid) {
                    CreateNewPostprocessLine $widget0 $i 
                }
            }
        }
    } else {
        set postProcess($postProcessLines.command) $PostprocessCommand
	set postProcess($postProcessLines.option) $postProcessOption
        set postProcess($postProcessLines.valid) 1
        if $popupInterface {
            CreateNewPostprocessLine $widget0 $postProcessLines
        }
        incr postProcessLines
    }
}

proc CheckPostprocessCommand {line} {
    global postProcess popupInterface
    if $popupInterface {
        if {![string match "exec *" $postProcess($line.command)] || ![string match "eval exec *" $postProcess($line.command)]} {
            if [APSYesNoPopUp "exec command is missing for post-process, add it?"] {
                set postProcess($line.command) [concat "exec" $postProcess($line.command)]
            }
        }
    }
}

proc TestPostprocess {line} {
    global postProcess outputDir outputRootname Description Step Steps ExpSteps popupInterface StepString logFileSuffix
    
    if {!$postProcess($line.valid) || ![string length $postProcess($line.command)]} {
        return -code error "invalid post-process command!"
    }
    set  command "$postProcess($line.command) $postProcess($line.option)"
    if [regexp {sddsprintout} $command ] {
        if [string match "exec *" $command] {
            set command [regsub {exec} $command ""]
        }
        APSExecLog .exp-postprocess -name "" -width 100 \
          -unixCommand "[subst $command]"
    } else {
        if ![string match "exec *" $command] {
            set command "exec $command"
        }
        set command [APSMakeSafeQualifierString $command]
        if [catch {eval $command} result] {
            return -code error $result
        }
    }
    SetMainStatus "$postProcess($line.command) completed."
}

proc DeletePostprocessLine {widget0 line} {
    global postProcess changeWidgetStateList

    if [winfo exist $widget0.m$line] {
	destroy $widget0.m$line
    }
    if [winfo exist $widget0.opt$line] {
	destroy $widget0.opt$line
    }
    set postProcess($line.valid) 0
 #   set j [lsearch $changeWidgetStateList $widget0.m$line.frame.delete.button]
 #   set changeWidgetStateList [lreplace $changeWidgetStateList  $j $j]
 #   set j [lsearch $changeWidgetStateList $widget0.m$line.frame.insert.button]
  #  set changeWidgetStateList [lreplace $changeWidgetStateList  $j $j]
}

proc LoadPostprocessInput {args} {
    global postProcessScroll popupInterface postProcess postProcessLines popupInterface
    set directory ""
    APSParseArguments {directory} 
    set file $directory/postprocess
    if ![file exist $file] {
        return
    }
    SetMainStatus "loading post-process..."
    set columnNames [exec sddsquery -col $file]
    set parameterNames [exec sddsquery -par $file]
    if [lsearch $columnNames ArgumentName]>=0 {
        if [lsearch $parameterNames PostprocessCommand]>=0 {
            set PostprocessCommand [exec sdds2stream $file -par=PostprocessCommand]
        }
        set names [exec sdds2stream -col=ArgumentName $file]
        set values [exec sdds2stream -col=ArgumentValue $file]
        set postprocessOption ""
        set postprocessCommand ""
        foreach name $names value $values {
            if {$name=="Directory"} {
                append postprocessOption " -directory $value"
            } elseif {$name=="Rootname"} {
                append postprocessOption " -rootname $value"
            } elseif {$name=="Command" || $name=="command"} {
                set PostprocessCommand $value
            } else {
                append postprocessOption " $name $value"
            }
        }
        set postprocessOption [regsub -all {\$Output} $postprocessOption \$output]
        
        if $popupInterface {
            MakeNewPostprocessLine $postProcessScroll -mode add -PostprocessCommand "$PostprocessCommand" \
              -postProcessOption $postprocessOption
        } else {
            set postProcess(0.command) $command
            set postProcess(0.option) $postprocessOption
            set postProcess(0.valid) 1
            set postProcessLines 1
        }
    } elseif {[lsearch $columnNames PostprocessCommand]>=0} {
        set commandList [exec sdds2stream $file -col=PostprocessCommand]
        if [catch {exec sdds2stream $file -col=PostprocessOption} optionList] {
            set optionList ""
        }
        foreach comm $commandList option $optionList {
            if ![string length $option] {
                set command [join [lrange $comm 0 1]]
                set option [join [lrange $comm 2 end]]
            } else {
                set command $comm
            } 
            set option [regsub -all {\$Output} $option \$output] 
            if $popupInterface {
                MakeNewPostprocessLine $postProcessScroll -mode add \
                  -PostprocessCommand "$command" -postProcessOption $option
            } else {
                set postProcess($postProcessLines.command) $command
                set postProcess($postProcessLines.option) $option
                set postProcess($postProcessLines.valid) 1
                incr postProcessLines
            }
        }
    }
}

proc CreateFinalizationWidget {parent} {
    global finalizeScroll finalizeScrollFrame  changeWidgetStateList
    
    APSFrame .finalframe -name "Finalization" -parent $parent -packOption "-side top -fill x"
    set w $parent.finalframe.frame
    $w configure -relief flat -borderwidth 0
    APSFrame .steps -parent $w -packOption "-side top -fill x"
    set w1 $w.steps.frame
    $w1 configure -relief flat -borderwidth 0
    APSLabel .desc -parent $w1 -text "  Press \"Add Finalization Entry\" to add the finalization steps."

    APSFrame .title -parent $w -packOption "-side top -fill x"
    set w1 $w.title.frame
    $w1 configure -relief flat -borderwidth 0
    APSLabel .pv -parent $w1 -text "         PV name       " -width 25 -packOption "-side left" \
      -contextHelp "PV name" -style "Fixed.TLabel"
    APSLabel .var -parent $w1 -text "        Readback name " -width 25 -packOption "-side left" \
      -contextHelp "The name of the readback" -style "Fixed.TLabel"
    APSLabel .val -parent $w1 -text "            set_value" -width 25 -packOption "-side left" \
      -contextHelp "The value for setting the PV to." -style "Fixed.TLabel"
    APSLabel .unit -parent $w1 -text "  orig_value  " -width 25 -packOption "-side left" \
      -contextHelp "The initial value of the PV" -style "Fixed.TLabel"
    APSLabel .min -parent $w1 -text "tolerance" -width 25 -packOption "-side left" \
      -contextHelp "The tolerance for setting the PV." -style "Fixed.TLabel"

    APSFrame .scroll -parent $w -packOption "-side top -fill x"
    set w2 $w.scroll.frame
    $w2 configure -relief flat -borderwidth 0
    set finalizeScroll [APSScroll .finalScroll -parent $w2]
    set finalizeScrollFrame $w2.finalScroll

    APSFrame .but -parent $w -packOption "-side top -fill x"
    set w3 $w.but.frame
    $w3 configure -relief flat -borderwidth 0
    APSButton .addPV -parent $w3 -text "Add Finalization Entry" \
      -command "AddInitFinalizeLine -makeType finalize" \
      -contextHelp "Press to add the finalization PVs in order" -width ""
    APSButton .clear -parent $w3 -text "Clear" \
      -command {ClearInitFinalizeSettings $finalizeScroll -makeType finalize} \
      -contextHelp "Press to clear all finalization lines" -width ""
    lappend changeWidgetStateList $w3.addPV.button
    lappend changeWidgetStateList $w3.clear.button
}

proc CreateHelpWidget {parent} {
    global globalVarList globalVarDescList
    set index 1
    APSLabel .h$index -parent $parent \
      -text "You may pass the values of following global variables into your script or command with format \$VariableName."
    incr index
    APSFrameGrid .grid -parent $parent -xList {x1 x2}
    set w1 $parent.grid.x1
    set w2 $parent.grid.x2
    APSLabel .help$index -parent $w1 -text "Variable Name"
    APSLabel .help$index -parent $w2 -text "Descript of variable"
    incr index
    foreach var $globalVarList desc $globalVarDescList {
        APSLabel .help$index -parent $w1 -fgColor red -text $var
        APSLabel .help$index -parent $w2 -text "$desc"
        incr index
    }
    
    APSLabel .help$index -parent $parent -text "    "
    incr index
    APSLabel .help$index -parent $parent -text "For example, following is a calling syntax of a tcl script"
    incr index
    label $parent.help$index -fg blue -text "<script_name> -index \$Step -rootname \$outputRootname -directory \$outputDir"
    pack $parent.help$index -side top
    incr index
    APSLabel .help$index -parent $parent -text "and following is a calling syntax of an SDDS command"
    incr index
    label $parent.help$index -fg blue -text "<sdds-program name> -index=\$Step -rootname=\$outputRootname -directory=\$outputDir"
    pack $parent.help$index -side top
    incr index
    
}

#procedures for adding PV lines
proc SearchPVName {args} {
    set pvnameList ""
    set useCA 0
    if [APSStrictParseArguments {pvnameList useCA}] {
        return -code error "SearchPVName: bad arguments"
    }
   
    if [catch {exec cavget -numerical -list=[join $pvnameList ,] } dataList] {
        return -code error "SearchPVName: $dataList"
    }
    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]"
    }
}

set NoCheck 0
proc addNewPV {} {
    global monitorVarList 
    eval global $monitorVarList
    set typeCommands  [list "set NoCheck 0;AddControlWidget -parent .pv.userFrame -type Readback" \
                           "set NoCheck 0;AddControlWidget -parent .pv.userFrame -type Control" \
                           "set NoCheck 0;AddControlWidget -parent .pv.userFrame -type Setpoint" \
                           "set NoCheck 1;AddControlWidget -parent .pv.userFrame -type Knob"]
    set okCommand {MakeNewMonitorLineForPV $monitorScroll -monitorName $PVname \
                       -relative $ControlRelative -noCheck $NoCheck \
                       -variable $PVvariable -minimum $PVmin -maximum $PVmax -pvtype $PVtype \
                       -setValue $controlSetValue -changeValue $controlChangeValue -action $controlAction \
                     -listofvalues $controlListValue \
                     -controlInitial $controlInitial -controlFinal $controlFinal -mode create}
    APSDialogBox .pv -name "Add PV Box" -width 30 \
      -cancelCommand "" -okCommand $okCommand
   # APSDialogBoxAddButton .help -parent .pv -text "Help" -command "HelpDialog -box PVBox"
    APSDialogBoxAddButton .clear -parent .pv -text "Clear" \
      -contextHelp "clear the existing settings in this dialog." \
      -command {set PVname "";set PVvariable "";set PVmax "";set PVmin "";set PVtype ""}
    APSLabeledEntry .addVar -parent .pv.userFrame \
      -label "PV name: " -textVariable PVname -width 30 \
      -contextHelp "Enter a PV name of your choice."
    APSLabeledEntry .nickPV -parent .pv.userFrame \
      -label "Variable name:" -textVariable PVvariable -width 30 \
      -contextHelp "Enter a tcl variable name for the above PV, which has to be different from existing variables"
    
    APSLabeledEntry .min -parent .pv.userFrame \
      -label "Mimimum Value:" -textVariable PVmin -width 30 \
      -contextHelp "Enter the maximum allowed value of the above PV. The experiment may halt if the PV goes outside the range."
    APSLabeledEntry .max -parent .pv.userFrame \
      -label "Maximum Value:" -textVariable PVmax -width 30  \
      -contextHelp "Enter the maximum allowed value of the above PV. The experiment may halt if the PV goes outside the range."
    APSRadioButtonFrame .check -parent .pv.userFrame -label "Check if PV exists?" \
        -buttonList {Yes No} -valueList {0 1} -variable NoCheck -orientation horizontal
    APSRadioButtonFrame .type -parent .pv.userFrame -label "PV type:" -buttonList \
      {readback control setpoint knob} -valueList {Readback Control Setpoint Knob} -variable PVtype \
      -commandList $typeCommands -orientation horizontal \
      -contextHelp "A readback PV is simply a readback. The PV will will be logged in a special at every step of the experiment. A bpm is an example of a readback PV.\n\nControl PV is a PV that will change something physically. The way this PV will change over the course of the experiment is specified here. One still has to enter the PV name again in the ExecutionDesign tab, otherwise the PV value will not be changed. An example of a control PV is a corrector magnet.\n\nA PV name designated as \"setpoint\" is a more general way assign a value to a PV. It could be said that it is a complicated version of a change control PV. The setpiont PV gets its value from the associated tcl variable listed. The tcl variable name should be of the form S-<name>, e.g. S-corr1. This tcl variable is given a value during the experiment, either from a change control or from an equation."
   # if {$PVtype=="Control"} {
   #     AddControlWidget -parent .pv.userFrame -type Control
   # }
}

proc AddControlWidget {args} {
    set parent ""
    set type ""
    set line ""
    APSStrictParseArguments {parent type line}
    global controlFrame Monitor
    global PVvariable controlAction Monitor controlvarList ControlRelative

    if [winfo exist $parent.con] {
        destroy $parent.con
    }
   
    set relativeVar ControlRelative
    if {$type=="Control" || $type=="Knob"} {
        if [string length $line] {
            set actionvar Monitor($line.controlAction)
            foreach var $controlvarList {
                if ![info exist Monitor($line.$var)] {
                    set Monitor($line.$var) ""
                }
            }
            set relativeVar Monitor($line.relative)
        } else {
            set actionvar controlAction
        }  
        APSFrame .con -parent $parent
        if {$type=="Control"} {
            APSRadioButtonFrame .relative -parent $parent.con.frame -orientation horizontal \
                -label "Change relative to current value?" -buttonList {Yes No} \
                -valueList {1 0} -variable $relativeVar
        }
        set comList [list "SetControlMode -parent $parent.con.frame -line \"$line\" -action increment" \
                       "SetControlMode -parent $parent.con.frame -line \"$line\" -action specify_range" \
                       "SetControlMode -parent $parent.con.frame -line \"$line\" -action set_fixed_value" \
                       "SetControlMode -parent $parent.con.frame -line \"$line\" -action list_of_values" \
                       "SetControlMode -parent $parent.con.frame -line \"$line\" -action set_from_file" ]
        APSRadioButtonFrame .act -parent $parent.con.frame \
          -label "Action:" \
          -buttonList {increment specify_range specifiy_fixed_value list_of_values set_from_file} \
          -valueList {increment specify_range set_fixed_value list_of_values set_from_file} \
          -variable $actionvar \
          -contextHelp "Please choose the method for changing the control values in experiment" \
          -commandList $comList
        if [string length [set $actionvar]] {
            SetControlMode -parent $parent.con.frame -line $line -action [set $actionvar]
        }
    }
}

proc SetControlMode {args} {
    global monitorVarList Monitor changeControlFile
    eval global $monitorVarList
    set parent ""
    set line ""
    set action ""
    APSStrictParseArguments {parent line action}
    set frameList [list $parent.change $parent.init $parent.final \
                    $parent.set1 $parent.set2 $parent.set3]
    foreach w $frameList {
        if [winfo exist $w] {
            destroy $w
        }
    }
    
    if [string length $line] {
        set changevar Monitor($line.controlIncrement)
        set initvar Monitor($line.controlInitial)
        set finalvar Monitor($line.controlFinal)
        set setvar Monitor($line.controlFixedValues)
        set listvar Monitor($line.controlListOfValues)
    } else {
        set changevar controlChangeValue
        set initvar controlInitial
        set finalvar controlFinal
        set setvar controlSetValue
        set listvar controlListValue
    }
    switch $action {
        increment {
            APSLabeledEntry .change -parent $parent -label "Increment Value:" \
              -textVariable $changevar \
              -contextHelp "Enter the value that is to increase or decrease the control by at each step"
        }
        specify_range {
            APSLabeledEntry .init -parent $parent -label "Initial Value:" \
              -textVariable $initvar \
              -contextHelp "Enter the initial setting value for the control of the experiment."
            APSLabeledEntry .final -parent $parent -label "Final Value:" \
              -textVariable $finalvar \
              -contextHelp "Enter the final setting value for the control in the experiment." 
        }
        set_fixed_value {
            APSLabeledEntry .set1 -parent $parent -label "Fixed Value:" \
              -textVariable $setvar -width 35 \
              -contextHelp "Enter the values separated by space or comma that is going to set the control\
at each step of the experiment."
        }
        list_of_values {
            APSLabeledEntry .set3 -parent $parent -label "List of Values:" \
              -textVariable $listvar -width 35 \
              -contextHelp "Enter the values separated by space or comma that is going to set the control\
at each step of the experiment, the length of list_values is equal to the number of steps."
        }
        set_from_file {
            APSLabeledEntry .set2 -parent $parent -label "FileName:" \
              -textVariable changeControlFile -width 80 -packOption "-fill x -expand true"   \
              -contextHelp "Enter the name of the file that is going to set the control value from"
        }
    }
}

set firstControl 1
proc CheckControlArguments {args} {
    global Monitor monitorLines firstControl controlvarList changeControlFile
    
    foreach var $controlvarList {
        set $var ""
    }
    set line ""
    APSStrictParseArguments {controlAction controlIncrement controlInitial \
                               controlFinal controlFixedValues controlListOfValues line}

    switch $controlAction {
        increment {
            if ![string length $controlIncrement] {
                return -code error "The change value for the control was not given."
            }
        }
        specify_range {
            if {![string length $controlInitial] || ![string length $controlFinal]} {
                return -code error "The initial and final value for the control was not given."
            }
        }
        set_fixed_value {
            if ![string length $controlFixedValues] {
                return -code error "The set value for the control was not given."
            }
        }
        list_of_values {
            global ExpSteps
            if [regexp {,} $controlListOfValues] {
                set setvalue [split $controlListOfValues ,]
            } else {
                set setvalue $controlListOfValues
            }
            set size [llength $setvalue]
            if {!$size} {
                return -code error "The list of values is not given!"
            }
            if $firstControl {
                set ExpSteps $size
            } else {
                if {$size!=$ExpSteps} {
                    SetMainStatus "warning: the size of list_values of $ControlPV($index.PV) is different from the number of steps, some values may not be used or will be reused."
                }
            }
            set firstControl 0
        }
        set_from_file {
            if ![string length $changeControlFile] {
                return -code error "the file name for setting control value is not given."
            }
            if [catch {CheckControlFile -filename $changeControlFile -pvName $Monitor($line.name) } result] {
                return -code error "$result"
            }
        }
        default {
            return -code error "Unknown control action given."
        }
    }
    foreach var $controlvarList {
        set Monitor($line.$var) "[set $var]"
    }
    return 0
}

proc SaveRestoreMonitorPV {args} {
    set line ""
    set action save
    set popup 1
    APSStrictParseArguments {line action popup}
    global Monitor SavedMonitor monitorNameList
    foreach name $monitorNameList {
        switch $action {
            save {
                if [info exist Monitor($line.$name)] {
                    set SavedMonitor($line.$name) $Monitor($line.$name)
                }
            }
            restore {
                if [info exist SavedMonitor($line.$name)] {
                    set Monitor($line.$name) $SavedMonitor($line.$name)
                }
            }
        }
    }
}

proc CheckEditPVValues {args} {
    set line ""
    APSStrictParseArguments {line}
    global Monitor monitorLines monitorVarList pvSavedList
    eval global $monitorVarList
    set pv $Monitor($line.name)
    set var $Monitor($line.var)
    if ![string length $Monitor($line.knobFile)] {
        if [catch {SearchPVName -useCA 1 -pvnameList $pv} result] {
            SetMainStatus "$result, edit pv failed, restore previous setting"
            SaveRestoreMonitorPV -line $line -action restore
            return
        }
    }
    set nameList ""
    set varList ""
    for {set i 0} {$i<$monitorLines} {incr i} {
        if {$i==$line} {
            continue
        }
        if [info exist Monitor($i.name)] {
            lappend nameList $Monitor($i.name)
            lappend varList $Monitor($i.var)
        }
    }
    set index1 [lsearch -exact $nameList $pv]
    set index2 [lsearch -exact $varList $var] 
    if {$index1>=0 || $index2>=0} {
        SetMainStatus "duplicate $pv exists, editting pv failed, restore previous settings."
        SaveRestoreMonitorPV -line $line -action restore
        return
    }
    switch $Monitor($line.type) {
        Setpoint {
            if [catch {CheckSetpointVar -variable $var} setpointvar] {
                SetMainStatus "Edit pv failed: $setpointvar"
                SaveRestoreMonitorPV -line $line -action restore
                return
            }
            set Monitor($line.setpointChecked) 1
            set Monitor($line.setpointVar) $setpointvar
        }
        Control -
        Knob {
            set Monitor($line.controlChecked) 1
            if [catch {CheckControlArguments -controlAction $Monitor($line.controlAction) \
                           -controlInitial $Monitor($line.controlInitial) \
                           -controlFinal $Monitor($line.controlFinal) \
                           -controlIncrement $Monitor($line.controlIncrement) \
                           -controlFixedValues $Monitor($line.controlFixedValues) \
                           -controlListOfValues $Monitor($line.controlListOfValues) \
                           -line $line} result] {
                SetMainStatus "Edit pv failed: $result; restore pv settings."
                SaveRestoreMonitorPV -line $line -action restore
                return
            }
        }
    } 
    UpdateMonitorLists 
}

proc CheckSetpointVar {args} {
    set variable ""
    set returnError 0
    APSStrictParseArguments {variable resturnError}
    global Monitor monitorLines
    set varList ""
    for {set i 0} {$i<$monitorLines} {incr i} {
        if [info exist Monitor($i.name)] {
            lappend varList $Monitor($i.var)
        }
    }
    set i1 [string first - $variable]
    if {$i1<0} {
        return -code error "invalid variable name for setpoint given, should be S-<existing_variable>"
    }
    set var [string range $variable [expr $i1+1] end]
    if [lsearch -exact $varList $var]<0 {
        if !$returnError {
            bell
            SetMainStatus "Warning: variable $var does not exist yet, please remember to add it for setting the setpoint with its value."
        } else {
            return -code error "variable $var does not exist, can not used for setting setpoint"
        }
    }
    return $var
}

proc CheckControlFile {args} {
    set filename ""
    set pvName ""
    APSStrictParseArguments {filename pvName}
    if {![string length $pvName] || ![string length $filename] } {
        return -code error "filename or PV name is not given!"
    }
    if ![file exist $filename] {
        return -code error "$filename does not exist."
    }
    set colNames [exec sddsquery -col $filename]
    if {[lsearch -exact $colNames $pvName]==-1} {
        return -code error "$filename does not contain $pvName"
    }
}

proc ClearOnePVSettings {line} {
    global Monitor
    
    set Monitor($line.name) ""
    set Monitor($line.var) ""
    set Monitor($line.max) ""
    set Montior($line.min) ""
    set Monitor($line.type) ""
}

proc EditMonitorPVLine {line args} {
    global Monitor monitorVarList SavedMonitor MonitorNameList pvLinksSetup
    eval global $monitorVarList 
    set typeCommands [list "AddControlWidget -parent .editpv.userFrame -type Readback -line $line" \
                          "AddControlWidget -parent .editpv.userFrame -type Control -line $line" \
                          "AddControlWidget -parent .editpv.userFrame -type Setpoint -line $line " \
                          "AddControlWidget -parent .editpv.userFrame -type Knob -line $line"]
    set clearCommand "ClearOnePVSettings $line"
    SaveRestoreMonitorPV -line $line -action save
    APSDialogBox .editpv -name "Edit PV Box for line $line" -width 30 \
      -cancelCommand "SaveRestoreMonitorPV -line $line -action restore" \
      -okCommand "set pvLinksSetup 0;CheckEditPVValues -line $line"
    #APSDialogBoxAddButton .help -parent .editpv -text "Help" -command "HelpDialog -box PVBox"
    APSDialogBoxAddButton .clear -parent .editpv -text "Clear" \
      -contextHelp "clear the existing settings in this dialog." \
      -command "$clearCommand"
    APSLabeledEntry .name -parent .editpv.userFrame \
      -label "PV name: " -textVariable Monitor($line.name) -width 30 \
      -contextHelp "Enter a PV name of your choice."
    APSLabeledEntry .var -parent .editpv.userFrame \
      -label "Variable name:" -textVariable Monitor($line.var) -width 30 \
      -contextHelp "Enter a tcl variable name for the above PV, which is distinguish from existing variables"
   
    APSLabeledEntry .min -parent .editpv.userFrame \
      -label "Minimum Value:" -textVariable Monitor($line.min) -width 30 \
      -contextHelp "Enter the maximum allowed value of the above PV."
    APSLabeledEntry .max -parent .editpv.userFrame \
      -label "Maximum Value:" -textVariable Monitor($line.max) -width 30  \
      -contextHelp "Enter the maximum allowed value of the above PV." 
    APSRadioButtonFrame .check -parent .editpv.userFrame -label "Check if PV exists?" \
        -buttonList {Yes No} -valueList {0 1} -orientation horizontal \
        -variable Monitor($line.noCheck)
    APSRadioButtonFrame .type -parent .editpv.userFrame -label "PV type:" -buttonList \
        {readback control setpoint knob} -valueList {Readback Control Setpoint Knob} -variable Monitor($line.type) \
        -commandList $typeCommands -orientation horizontal \
        -contextHelp "output PV is the PV to write the value to. Readback PV is bpm. The meaning of corrector is obvious."
    if {$Monitor($line.type)=="Control"} {
        AddControlWidget -parent .editpv.userFrame -type Control -line $line
    }
    if {$Monitor($line.type)=="Knob"} {
        AddControlWidget -parent .editpv.userFrame -type Knob -line $line
    }
}

proc UpdateMonitorLists {args} {
    global Monitor monitorLines saveListNames
    eval global $saveListNames
    foreach name  $saveListNames {
        set $name ""
    }
    for {set i 0} {$i<$monitorLines} {incr i} {
        if {[info exist Monitor($i.name)] && [string length $Monitor($i.name)]} {
            set name [string tolower $Monitor($i.type)]
            if [string match "par*" $name] {
                lappend parSavedList $i
            } else {
                lappend ${name}SavedList $i
            }
        }
    }
    set pvSavedList [lsort -integer [concat $readbackSavedList $controlSavedList $setpointSavedList]]
}

proc DeleteMonitorLine {widget0 line} {
    global Monitor monitorLines changeWidgetStateList monitorNameList
    global saveListNames
    eval global $saveListNames
    
    destroy $widget0.m$line
    set type $Monitor($line.type)
    foreach name $monitorNameList {
        if [info exist Monitor($line.$name)] {
            unset Monitor($line.$name)
        }
    }
    set index1 [lsearch $changeWidgetStateList $widget0.m$line.frame.delete.button]
    set changeWidgetStateList [lreplace $changeWidgetStateList $index1 $index1]
    set index2 [lsearch $changeWidgetStateList $widget0.m$line.frame.edit.button]
    set changeWidgetStateList [lreplace $changeWidgetStateList $index2 $index2]
    foreach name $saveListNames {
        set index [lsearch [set $name] $line]
        set $name [lreplace [set $name] $index $index]
    }
    UpdateMonitorLists
}

proc DeleteKnobLine {widget line} {
    global  KnobFile  KnobScrollFrame  changeWidgetStateList brief
    if [winfo exist $widget.m$line] {
        destroy $widget.m$line
    }
    if [winfo exist $widget.opt$line] {
        destroy $widget.opt$line
    }
    if !$KnobFile($line.valid) {
        return
    }
    set $KnobFile($line.valid) 0
}


proc CheckMonitorNameAndVariable {args} {
    global Monitor monitorLines
    set monitorName ""
    set variable ""
    APSParseArguments {monitorName variable}
    set nameList ""
    set varList ""
    for {set i 0} {$i<$monitorLines} {incr i} {
        if [info exist Monitor($i.name)] {
            if [string compare $Monitor($i.name) $monitorName]==0 {
                return -code error "Monitor name $monitorName already exists"
            }
            if [string compare $Monitor($i.var) $variable]==0 {
                return -code error "Variable name $variable already exists"
            }
        }
    }
}

proc MakeNewMonitorLineForPV {widget0 args} {
    global monitorLineVariables
    global monitorScrollFrame amonitorLines run changeWidgetStateList popupInterface Monitor monitorLines
    global controlSavedList knobSavedList setpointSavedList readbackSavedList pvSavedList monitorNameList brief
    set monitorName ""
    set variable ""
    set pvtype ""
    set minimum -10
    set maximum 10
    set unit "?"
    set setValue ""
    set listofvalues ""
    set changeValue ""
    set action ""
    set controlInitial ""
    set controlFinal ""
    set mode load
    set active 1
    set relative 0
    set noCheck 0
    APSStrictParseArguments {monitorName variable minimum maximum pvtype unit action setValue \
                                 controlInitial controlFinal changeValue  mode listofvalues \
                                 active relative noCheck}

    if {![string length $monitorName] || ![string length $variable]} {
        SetMainStatus "MakeNewMonitorLineForPV: No name submited for experiment."
        bell 
        return 
    }
    if {![string length $variable]} {
        SetMainStatus "MakeNewMonitorLineForPV: variable name is not given."
        bell 
        return 
    }
    if {![string length $pvtype]} {
        SetMainStatus "Entry type is not provided"
        bell
        return 
    }
    set run 0 
    SetMainStatus "Check monitor name and variable for $monitorName ."
    if [catch {CheckMonitorNameAndVariable -monitorName $monitorName -variable $variable} result] {
        SetMainStatus $result
        return
    }
    SetMainStatus "Done."
    if {$pvtype=="Knob"} {
        set noCheck 1
    }
    if !$noCheck {
        if [catch {exec cavget -list=$monitorName -printErrors -pend=30} value] {
            SetMainStatus "Unable to read value of $monitorName: $value"
            return
        }
        if [catch {expr $value /2.0} result] {
            set Monitor($monitorLines.columnType) string
        } else {
            set Monitor($monitorLines.columnType) double
        }
    }
    foreach name $monitorNameList {
        set Monitor($monitorLines.$name) ""
    }
    set widget $widget0.m$monitorLines
    set Monitor($monitorLines.name) $monitorName
    set Monitor($monitorLines.noCheck) $noCheck
    set Monitor($monitorLines.knobFile) ""
    set Monitor($monitorLines.PARtype) ""
    lappend monitorLineVariables $variable
    
    if ![winfo exists $widget] {
        global $variable
        set $variable 0
        switch $pvtype {
            Setpoint {
                if [catch {CheckSetpointVar -variable $variable} var] {
                    SetMainStatus "$var"
                    return
                }
                set Monitor($monitorLines.setpointChecked) 1
                set Monitor($monitorLines.setpointVar) $var
                lappend setpointSavedList $monitorLines
            }
            Control {
                if {$mode=="create"} {
                    set Monitor($monitorLines.controlChecked) $active
                    if [catch {CheckControlArguments -controlAction $action -controlInitial $controlInitial \
                                   -controlFinal $controlFinal -controlIncrement $changeValue \
                                   -controlFixedValues $setValue \
                                   -controlListOfValues $listofvalues \
                                   -line $monitorLines} result] {
                        SetMainStatus "$result"
                        bell
                        return
                    }
                }
                lappend controlSavedList $monitorLines
            }
            Knob {
                if {$mode=="create"} {
                    set Monitor($monitorLines.controlChecked) $active
                    if [catch {CheckControlArguments -controlAction $action -controlInitial $controlInitial \
                                   -controlFinal $controlFinal -controlIncrement $changeValue \
                                   -controlFixedValues $setValue \
                                   -controlListOfValues $listofvalues \
                                   -line $monitorLines} result] {
                        SetMainStatus "$result"
                        bell
                        return
                    }
                }
                lappend knobSavedList $monitorLines
            }
            Readback {
                lappend readbackSavedList $monitorLines
            }
        }
        if {$pvtype!="Knob"} {
            lappend pvSavedList $monitorLines
        }
        set Monitor($monitorLines.relative) $relative
        set Monitor($monitorLines.type) $pvtype
        set Monitor($monitorLines.var) $variable 
        set Monitor($monitorLines.max) [format %.3f $maximum]
        set Monitor($monitorLines.min) [format %.3f $minimum]
        if [catch {exec cavget -list=[regsub ".VAL" ${monitorName} ""].EGU -pend=30} unit] {
            set unit ""
        }
        
        if {[string compare $unit ?] == 0} {
            set Monitor($monitorLines.unit) ""
        } else {
            #set unit [regsub -all " " $unit ""]
            #set Monitor($monitorLines.unit) [string trim $unit \"]
            set Monitor($monitorLines.unit) $unit
        }
        if !$popupInterface {
            incr monitorLines
            return
        }
        APSFrame .m$monitorLines -parent $widget0 -packOption "-side top -fill x"
        $widget.frame configure -borderwidth 0
        APSLabeledEntry .name -parent $widget.frame -textVariable Monitor($monitorLines.name) \
            -packOption "-side left" -label "" -width 25
        APSLabeledOutput .var -parent $widget.frame -textVariable Monitor($monitorLines.var) \
            -packOption "-side left" -width 11 -label ""
        APSLabeledOutput .value -parent $widget.frame -textVariable $Monitor($monitorLines.var) \
            -packOption "-side left" -width 16 -label ""
        APSLabeledEntry .unit -parent $widget.frame -textVariable Monitor($monitorLines.unit) \
            -packOption "-side left" -width 7 -label ""
        APSLabeledEntry .min -parent $widget.frame -textVariable Monitor($monitorLines.min) \
            -packOption "-side left" -width 11 -label ""
        APSLabeledEntry .max -parent $widget.frame -textVariable Monitor($monitorLines.max) \
            -packOption "-side left" -width 11 -label ""
        APSLabeledOutput .type -parent $widget.frame -textVariable Monitor($monitorLines.type) -width 9 \
            -packOption "-side left" -label ""
        if !$brief {
            APSButton .delete -parent $widget.frame -text "DELETE" -size small \
                -command "DeleteMonitorLine $widget0 $monitorLines" \
                -contextHelp "Press to delete the corresponding PV line."
            APSButton .edit -parent $widget.frame -text "EDIT" -size small \
                -command "EditMonitorPVLine $monitorLines" \
                -contextHelp "Press to delete the corresponding PV line."
            lappend changeWidgetStateList $widget.frame.delete.button
            lappend changeWidgetStateList $widget.frame.edit.button
        }
        .userFrame.main.frame.tn select 0
        incr monitorLines
        tkwait visibility $widget.frame.type
        APSScrollAdjust $monitorScrollFrame -numVisible 9
    }
}

proc addNewPAR {} {
    global monitorScroll PARvalue PARvariable PARunits PARtypes
    set okCommand {MakeNewMonitorLineForPAR $monitorScroll -variable $PARvariable -value $PARvalue -units $PARunits -types $PARtypes}
    if {[winfo exists .par]} {
	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."
    APSRadioButtonFrame .typePar -parent .par.userFrame -label "Process Variable?" \
	-buttonList {PV comments} -valueList {pv comments} -variable PARtypes -orientation horizontal \
	-contextHelp "Choose Yes if the parameter name is a process variable, otherwise, choose NO."
}

proc HelpDialog {args} {
    set box ""
    APSParseArguments {box}
    switch $box {
        PVBox {
            set title "Add Process Variables"
            set help "\
PV name:       the Process variable name.             \n\
Variable name: the tcl variable name for the above PV,\n\
               which is distinguish from existing     \n\
               variable names.                        \n\
Maximum Value: the upper limit of the PV during       \n\
               experiment, for testing purpose.       \n\
Minimum Value: the lower limit of the PV during       \n\
               experiment, for testing purpose.       \n\
PV type: choose a  PV type:                           \n\
         readback  for bpm                            \n\
         control   for the PV whose value is going to \n\
                   be changed during experiment.      \n\
         output    for PV to write to.                 "
        }
        EQBox {
            set title "Add Equation"
            set help "\
Equation: Assign an equation using PV's and EQ's variable names \
         \n with TCL convention.\
         \nSome examples: \
         \n(\$PV0 + \$PV1) * 3;\
         \n\${S10A:P1:ms:x}/\${S10A:H1:CurrentAO};  (use PV name as variable) \
         \nabs(\$EQ2 * \$PV3);\
         \nint((\$EQ2 + \$EQ4) * \$PV1) % 2\n\
Variable name: the tcl variable name for the above equation,   \n\
          which is distinguish from existing variable names. "
        }
        InitBox {
            set title "Add Initializaiton PV"
            set help "\
PV name:        the Process variable name for initializaion.    \n\
Value to set:   the value for setting the the initialization PV.\n\
Setting order:  the order for initializing the PV.              \n\
Original Value: the original value of the initialization PV.    "

        }
    }
    APSDialogBox .help -name "Help for $title" -width 40
    APSLabel .message -parent .help.userFrame -text "$help" -width 40 -packOption "-side left"
}

proc addNewEQ {} {
    global monitorScroll EQentry EQvariable EQunits
    set okCommand {MakeNewMonitorLineForEQ $monitorScroll -monitorName $EQentry -variable $EQvariable \
                     -units $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."
    APSDialogBoxAddButton .help -parent .eq -text "Help" -command "HelpDialog -box EQBox"
    
    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 "The tcl variable name for the above equation"
    APSLabeledEntry .unitEQ -parent .eq.userFrame \
      -label "Units:" -textVariable EQunits -width 60 \
      -contextHelp ""
}

proc MakeNewMonitorLineForEQ {widget0 args} {
    set monitorName ""
    set variable ""
    set units ""
    APSParseArguments {monitorName variable units}
    
    global popupInterface monitorScrollFrame run eqSavedList monitorLineVariables
    global monitorLines changeWidgetStateList Monitor brief

    if {![string length $monitorName] || ![string length $variable]} {
        SetMainStatus "No entry submited for monitoring."
        bell
        return 
    }
    if [catch {CheckMonitorNameAndVariable -monitorName $monitorName -variable $variable} result] {
        SetMainStatus $result
        return
    }
    set run 0
    update
    global $variable
    set $variable 0
    lappend eqSavedList $monitorLines
    set Monitor($monitorLines.type) eq
    set Monitor($monitorLines.name) $monitorName
    set Monitor($monitorLines.var) $variable
    set Monitor($monitorLines.PARtype) ""
    lappend monitorLineVariables $variable
    if [string length $units] {
        set Monitor($monitorLines.unit) $units
    } else {
        set Monitor($monitorLines.unit) ""
    }
    if !$popupInterface {
        incr monitorLines
        return
    }
    set widget $widget0.m$monitorLines
    if ![winfo exists $widget] {
        APSFrame .m$monitorLines -parent $widget0 -packOption "-side top -fill x"
        set w $widget0.m$monitorLines.frame
        $w configure -borderwidth 0
        APSButton .button -parent $w -text "Equation"  \
          -command "DisplayEquation $monitorLines" -packOption " -side left"\
          -contextHelp "Invokes a display of a corresponding EQ editor."
        $w.button.button configure -width 25
        APSLabeledOutput .entry -parent $w -textVariable Monitor($monitorLines.var) \
          -width 11 -packOption "-side left" -label ""  
        APSLabeledOutput .ref -parent $w -textVariable $Monitor($monitorLines.var) \
          -width 16 -packOption "-side left" -label ""  
        APSLabeledOutput .unit -parent $w -label "" \
          -textVariable Monitor($monitorLines.unit) -width 7 -packOption "-side left"
        if !$brief {
            APSButton .delete -parent $w -text "DELETE" -size small \
              -command "DeleteMonitorLine $widget0 $monitorLines" \
              -contextHelp "Press to delete the corresponding EQ line."
            lappend changeWidgetStateList $w.delete.button 
        }
        incr monitorLines
        .userFrame.main.frame.tn select 0
        tkwait visibility $w.ref
        APSScrollAdjust $monitorScrollFrame -numVisible 9  
    }
}

proc DisplayEquation {number} {
     global Monitor run display n
     set n $number
     set display $Monitor($number.name)
     set okCommand "update"

     if [winfo exists .display$number] {
          destroy .display$number
     }
     APSDialogBox .display$number -name "Editing equation \"$Monitor($number.var)\"" -width 120 \
        -okCommand $okCommand -contextHelp "Equation $Monitor($number.var) editor."
     APSLabeledEntry .eq -parent .display${number}.userFrame -label "$Monitor($number.var): "\
        -textVariable Monitor($number.name) -width 120 -contextHelp "Original equation setup for equation $Monitor($number.name)."
     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 $Monitor($number.var) modifications"
     if $run {
        .display${number}.buttonRow.edit.button configure -state disabled
     }
}

proc DisplayParameter {number} {
    global Monitor n run
    set n $number
    set okCommand "update"
    global $Monitor($number.var)

    if {[winfo exists .displayPAR$number]} {
	destroy .displayPAR$number
    }
    APSDialogBox .displayPAR$number -name "Editing parameter \"$Monitor($number.var)\"" -width 120 \
        -okCommand $okCommand -contextHelp "Parameter $Monitor($number.var) editor."
    APSLabeledEntry .parValue -parent .displayPAR${number}.userFrame -label "$Monitor($number.var) value: "\
        -textVariable $Monitor($number.var) -width 40 -contextHelp "Parameter $Monitor($number.var) 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 $Monitor($number.name) modifications"
    if {$run} {
        .displayPAR${number}.buttonRow.edit.button configure -state disabled
    }
}

proc MakeNewMonitorLineForPAR {widget0 args} {
    set variable ""
    set value ""
    set units ""
    set types ""
    set noCheck 0
    APSParseArguments {variable value units types noCheck}

    global monitorLines Monitor popupInterface brief monitorLineVariables
    global monitorScrollFrame run changeWidgetStateList parSavedList

    if {![string length $variable]} {
        SetMainStatus "The Parameter name is not given."
        bell
        return
    }
    set types [string tolower $types]
    if !$noCheck {
        if [string compare $types "pv"]==0 {
            if [catch {exec cavget -numerical -list=$variable -pend=30 -floatFormat=%.15g} value] {
                SetMainStatus "Can not get value for $variable: $value"
                bell
                return  
            }
        }
    }
    if {![string length $value]} {
        SetMainStatus "The Parameter value is not given."
        bell
        return
    }
    if [catch {CheckMonitorNameAndVariable -variable $variable} result] {
        SetMainStatus $result
        return
    }
    
    global $variable
    set $variable $value
    lappend parSavedList $monitorLines
    set Monitor($monitorLines.type) par_$types
    set Monitor($monitorLines.name) $variable
    set Monitor($monitorLines.var) $variable 
    set Monitor($monitorLines.PARtype) $types
    lappend monitorLineVariables $variable
    if {[string length $units]} {
        set Monitor($monitorLines.unit) $units
    } else {
        set Monitor($monitorLines.unit) ""
    }
    if !$popupInterface {
        incr monitorLines
        return
    }
    set widget $widget0.m$monitorLines
    if {![winfo exists $widget]} {
        APSFrame .m$monitorLines -parent $widget0 -packOption "-side top -fill x"
        set w $widget0.m$monitorLines.frame
        $w configure -borderwidth 0
        APSButton .button -parent $w -text "Parameter"  \
          -command "DisplayParameter $monitorLines" -packOption "-anchor c -side left"\
          -contextHelp "Invokes a display of a corresponding parameter editor."
        $w.button.button configure -width 25
        APSLabeledOutput .entry -parent $w -textVariable Monitor($monitorLines.var) \
          -width 11 -packOption "-side left" -label ""
        APSLabeledOutput .ref -parent $w -textVariable $Monitor($monitorLines.var) \
          -width 16 -packOption "-side left" -label ""
        APSLabeledOutput .unit -parent $w -textVariable Monitor($monitorLines.unit) \
          -width 7 -packOption "-side left" -label ""
        if !$brief {
            APSButton .delete -parent $w -text "DELETE" \
              -command "DeleteMonitorLine $widget0 $monitorLines" -size small \
              -contextHelp "Press to delete the corresponding Parameter line."
            lappend changeWidgetStateList $w.delete.button
        }
        incr monitorLines
        .userFrame.main.frame.tn select 0
        tkwait visibility $w.ref
        APSScrollAdjust $monitorScrollFrame -numVisible 10
    }
}

set ScriptName ""
set ScriptVariable ""
set ScriptUnits "" 
proc addNewScriptMon {} {
    global monitorScroll ScriptName ScriptVariable ScriptUnits
    set okCommand {MakeNewMonitorLineForScript $monitorScroll -name $ScriptName \
                       -variable $ScriptVariable  \
                       -units $ScriptUnits}
    if [winfo exists .scr] {
        destroy .scr
    }
    APSDialogBox .scr -name "Add Script Box" -width 60 \
      -cancelCommand "" -okCommand $okCommand \
      -contextHelp "Press OK button to add an script."
    APSDialogBoxAddButton .help -parent .scr -text "Help" -command "HelpDialog -box ScriptBox"
    
    APSLabeledEntry .addScript -parent .scr.userFrame \
      -label "Script:" -textVariable ScriptName -width 60 \
      -contextHelp "Assign the output of a script to a TCL variable, the execution of the script should output a value to stdout"
    APSLabeledEntry .nickscript -parent .scr.userFrame \
        -label "Variable name:" -textVariable ScriptVariable -width 60 \
        -contextHelp "The tcl variable name for storing the output of the execution of the above script"
    APSLabeledEntry .unitscript -parent .scr.userFrame \
      -label "Units:" -textVariable ScriptUnits -width 60 \
      -contextHelp ""
}

proc EditMonitorScript {line} {
    global Monitor 
    if [winfo exist .mon$line] {
        destroy .mon$line
    }
    APSDialogBox .mon$line -name "View/Edit Script for $Monitor($line.var)" -width 120 \
        -okCommand update -contextHelp "Script $Monitor($line.var) editor."
    APSLabeledEntry .script -parent .mon$line.userFrame -label "Script for $Monitor($line.var):" -editButton 1 -width 100 \
        -textVariable Monitor($line.name)
}
proc MakeNewMonitorLineForScript {widget0 args} {
    set name ""
    set variable ""
    set units ""
    APSParseArguments {name variable units}

    global popupInterface monitorScrollFrame run scriptSavedList
    global monitorLines changeWidgetStateList Monitor brief
    if {![string length $name] || ![string length $variable]} {
        SetMainStatus "No entry submited for monitoring."
        bell
        return 
    }
    if [catch {CheckMonitorNameAndVariable -monitorName $name -variable $variable} result] {
        SetMainStatus $result
        return
    }
    set run 0
    update
    global $variable
    set $variable 0
    lappend scriptSavedList $monitorLines
    set Monitor($monitorLines.type) script
    if {![string match "exec *" $name] && $popupInterface} {
        if [APSYesNoPopUp "exec command is missing, add it?"] {
            set name [concat "exec" $name]
        }
    }
    set Monitor($monitorLines.name) $name
    set Monitor($monitorLines.PARtype) ""
    set Monitor($monitorLines.var) $variable
    if [string length $units] {
        set Monitor($monitorLines.unit) $units
    } else {
        set Monitor($monitorLines.unit) ""
    }
    if !$popupInterface {
        incr monitorLines
        return
    }
    set widget $widget0.m$monitorLines
    if ![winfo exists $widget] {
        APSFrame .m$monitorLines -parent $widget0 -packOption "-side top -fill x"
        set w $widget0.m$monitorLines.frame
        $w configure -borderwidth 0
        
        APSButton .button -parent $w -text "Script"  \
          -command "EditMonitorScript $monitorLines" -packOption " -side left"\
          -contextHelp "display and edit script command"
        $w.button.button configure -width 25
        APSLabeledOutput .entry -parent $w -textVariable Monitor($monitorLines.var) \
          -width 11 -packOption "-side left" -label ""  
        APSLabeledOutput .ref -parent $w -textVariable $Monitor($monitorLines.var) \
          -width 16 -packOption "-side left" -label ""  
        APSLabeledOutput .unit -parent $w -label "" \
          -textVariable Monitor($monitorLines.unit) -width 7 -packOption "-side left"
        if !$brief {
            APSButton .delete -parent $w -text "DELETE" -size small \
              -command "DeleteMonitorLine $widget0 $monitorLines" \
              -contextHelp "Press to delete the corresponding  script line."
            lappend changeWidgetStateList $w.delete.button 
        } 
        APSButton .run -parent $w -text "RUN" -size small \
            -command "RunMonitorScript $monitorLines"
        incr monitorLines
        .userFrame.main.frame.tn select 0
        tkwait visibility $w.ref
        APSScrollAdjust $monitorScrollFrame -numVisible 9  
    }
}

proc RunMonitorScript {line} { 
    global globalParseVariables Monitor monitorLineVariables
    eval global $globalParseVariables
    if [llength $monitorLineVariables] {
	eval global $monitorLineVariables
    }
    if [catch {eval $Monitor($line.name)} result] {
        return -code error $result
    }
    global $Monitor($line.var)
    set $Monitor($line.var) $result
}
proc LoadMonitorLines {args} {
    global monitorScroll fileSelListDir Significant home
    global monitorLines PVname Significant Interval PVmax PVmin PVtype ExpSteps Interval
    
    set types ""
    set variables ""
    set units ""
    set minimum ""
    set maximum ""
   
    set directory $home
    APSParseArguments {directory}
    set filename $directory/monitorLines
    
    if ![file exist $filename] {
        return
    }
    SetMainStatus "loading monitor lines..."
    if [catch {sdds load $filename monitorData} result] {
        return -code error $result
    }
    if [lsearch $monitorData(ColumnNames) ControlName] {
        return -code error "ControlName column not found in $filename"
    }
    if [info exist monitorData(Parameter.Steps)] {
        set ExpSteps [lindex $monitorData(Parameter.Steps) 0]
    }
    if [info exist monitorData(Parameter.Interval)] {
        set Interval $monitorData(Parameter.Interval)
    }
    if [info exist monitorData(Parameter.Significant)] {
        set Significant [lindex $monitorData(Parameter.Significant) 0]
    }
    set ControlNames [lindex $monitorData(Column.ControlName) 0]
    set variables [llength $ControlNames]
    if !$variables {
        return -code error "No data in $filename"
    }
    foreach col $monitorData(ColumnNames) {
        set $col [lindex $monitorData(Column.$col) 0]
    }
    set nmList {NoCheckExistence MinimumValue MaximumValue ControlType Unit \
                    ValueString relative}
    set valList {0 -100 100 Readback "" "" 0}
    if ![info exist ControlVariable] {
        set ControlVariable $ControlNames
    }
    foreach nm $nmList val $valList {
        if ![info exist $nm] {
            set $nm [APSReplicateItem -item $val -number $variables]
        }
    }
    foreach name $ControlNames type $ControlType var $ControlVariable unit $Unit min $MinimumValue max $MaximumValue val $ValueString noCheck $NoCheckExistence {
        set min [format %.4f $min]
        set max [format %.4f $max]
        set noCheck [format %.0f $noCheck]
        switch $type {
            eq {
                MakeNewMonitorLineForEQ $monitorScroll -monitorName $name -variable $var -units $unit
            }
            par_pv -
            par_PV {
                MakeNewMonitorLineForPAR $monitorScroll -variable $var -value $val -units $unit -types pv -noCheck $noCheck
            }
            par_comments {
                MakeNewMonitorLineForPAR $monitorScroll -variable $var -value $val -units $unit -types comments
            }
            script {
                MakeNewMonitorLineForScript $monitorScroll -variable $var -name $name -units $unit
            }
            default {
                MakeNewMonitorLineForPV $monitorScroll -monitorName $name -noCheck $noCheck -relative $relative \
                    -variable $var -unit $unit -minimum $min -maximum $max -pvtype $type 
            }
        }
        update
    }
    SetMainStatus "Configuration loaded from file $filename"
}

proc LoadControlPVs {args} {
    global controlSavedList pvSavedList monitorLines monitorScroll changeControlFile Monitor knobSavedList
    set directory ""
    APSParseArguments {directory}
    set filename $directory/ChangeControl
    if ![file exist $filename] {
        SetMainStatus "$filename does not exist!"
        return
    }
    SetMainStatus "loading control pv settings..."
    if [catch {sdds load $filename controlData} result] {
        SetMainStatus "LoadChangeControlInput: $result"
        return
    }
    set controlName [lindex $controlData(Column.ControlName) 0]
    if [lsearch $controlData(ColumnNames) "IsKnob"]>=0 {
        set isKnobList [lindex $controlData(Column.IsKnob) 0]
    } else {
        set isKnobList [APSReplicateItem -item 0 -number [llength $controlName]]
    }
    set changeValue [lindex $controlData(Column.Increment) 0]
    set setValue [lindex $controlData(Column.FixedValue) 0]
    if [info exist controlData(Column.ListOfValues)] {
        set listofvalues [lindex $controlData(Column.ListOfValues) 0]
    } else {
        set listofvalues $setValue
    }
    set activeList [lindex $controlData(Column.Active) 0]
    set actionList [lindex $controlData(Column.Action) 0]
    if [info exist controlData(Parameter.ControlFile)] {
        set changeControlFile $controlData(Parameter.ControlFile)
    }
    if [lsearch $controlData(ColumnNames) InitialValue]==-1 {
        set initialList ""
    } else {
        set initialList [lindex $controlData(Column.InitialValue) 0]
    }
    if [lsearch $controlData(ColumnNames) FinalValue]==-1 {
        set finalList ""
    } else {
        set finalList [lindex $controlData(Column.FinalValue) 0]
    }
    set pvList ""
    for {set i 0} {$i<$monitorLines} {incr i} {
        switch $Monitor($i.type) {
            Readback -
            Setpoint -
            Control -
            Knob {
                lappend pvList $Monitor($i.name)
            }
        }
    }
    if [info exist controlData(Column.Relative)] {
        set relativeList [lindex $controlData(Column.Relative) 0]
    } else {
        set relativeList [APSReplicateItem -item 0 -number [llength $controlName]]
    }
    
    foreach name $controlName change $changeValue set $setValue active $activeList listvalues $listofvalues act $actionList init $initialList final $finalList relative $relativeList isKnob $isKnobList {
        if {$act=="setFromFile"} {
            set act set_from_file
        }
        if $isKnob {
            set type Knob
        } else {
            set type Control
        }
        if {$type!="Knob" && [string length $name]} {
            if [catch {SearchPVName -useCA 1 -pvnameList $name} result] {
                SetMainStatus "$result\nDone."
                bell
                continue
            }
        }
        set j [lsearch -exact $pvList $name]
        if {$j<0} {
            MakeNewMonitorLineForPV $monitorScroll -monitorName $name \
                -variable control$monitorLines -minimum -100 -maximum 100 -pvtype $type \
                -setValue $set -listofvalues $listvalues -relative $relative \
                -changeValue $change -action $act -active $active \
                -controlInitial $init -controlFinal $final -mode create
            lappend pvList $name
        } else {
            for {set i 0} {$i<$monitorLines} {incr i} {
                if [info exist Monitor($i.name)] {
                    if [string compare $name $Monitor($i.name)]==0 {
                        break
                    }
                }
            }
            set Monitor($i.controlIncrement) $change
            set Monitor($i.controlAction) $act
            set Monitor($i.controlFixedValues) $set
            set Monitor($i.controlChecked) $active
            set Monitor($i.controlInitial) $init
            set Monitor($i.controlFinal) $final
            set Monitor($i.controlListOfValues) $listvalues
            set Monitor($i.relative) $relative
            set Monitor($i.type) $type
        }   
    }
    SetMainStatus "Loaded control pv settings from $filename." 
}

proc ClearInitFinalizeSettings {widget0 args} {
    set makeType ""
    set popup 1
    APSParseArguments {makeType popup}
    global initScrollFrame changeWidgetStateList initLines init finalize finalizeLines
    if $popup {
        if {![APSYesNoPopUp "Really? Clear all $makeType settings?"]} { return }
    }
    set lines [set ${makeType}Lines] 
    for {set i 0} {$i<$lines} {incr i} {
        if {[winfo exists $widget0.m$i]} {
          #  set j [lsearch $changeWidgetStateList $widget0.m$i.frame.delete.button]
          #  set changeWidgetStateList [lreplace $changeWidgetStateList $j $j]   
          #  set j [lsearch $changeWidgetStateList $widget0.m$i.frame.insert.button]
          #  set changeWidgetStateList [lreplace $changeWidgetStateList $j $j]
            destroy $widget0.m$i
        }
        foreach info {name readback type script orig value tolerance}  {
            if [info exists ${makeType}($i.$info)] {
                unset ${makeType}($i.$info)
            }
        }
    }
    set ${makeType}Lines 0
}

proc ClearPVSettings {widget0} {
    global monitorLines run monitorScrollFrame changeWidgetStateList Monitor monitorNameList
    global saveListNames
    eval global $saveListNames
    if !$monitorLines {
        return
    }
    if {![APSYesNoPopUp "Really? Clear all PV settings?"]} { return }
    set run 0
    update
   # APSScrollAdjust $monitorScrollFrame -numVisible 2
    for {set i 0} {$i<$monitorLines} {incr i} {
        if {[winfo exists $widget0.m$i]} {
            set j [lsearch $changeWidgetStateList $widget0.m$i.frame.delete.button]
            set changeWidgetStateList [lreplace $changeWidgetStateList $j $j]
            set j [lsearch $changeWidgetStateList $widget0.m$i.frame.edit.button]
            set changeWidgetStateList [lreplace $changeWidgetStateList $j $j]
            destroy $widget0.m$i
        }
    }
    for {set i 0} {$i<$monitorLines} {incr i} {
        foreach name $monitorNameList {
            if [info exist Monitor($i.$name)] {
                unset Monitor($i.$name)
            }
        }
    }
    foreach name $saveListNames {
        set $name ""
    }
    set monitorLines 0
}

proc LoadConfiguration {args} {
    global saveDir readDir home tabFrameWidgetList monitorScroll initScroll execScroll finalizeScroll
    global monitorLines initLines execLines popupInterface finalizeLines postProcessLines
    global outputDir outputRootname postProcessScroll outputFileWidget brief outputFileChecked
    global KnobLines KnobScroll popupInterface

    set configuration ""
    APSParseArguments {configuration}
    if ![string length $configuration] {
        if [string length $saveDir] {
            set defaultDir $saveDir
        } elseif [string length $readDir] {
            set defaultDir $readDir
        } else {
            set defaultDir $home
        }
        set readDir ""
        set readDir [APSFileSelectDialog .choosedir -selectDir 1 -listDir $defaultDir -labelname configuration]
        if ![string length $readDir] {
            SetMainStatus "Load configuration was cancelled."
            return
        }
    } else {
        set readDir $configuration
    }
    if ![file isdirectory $readDir] {
        set readDir [file dirname $readDir]
    }
    set saveDir $readDir
    SetMainStatus "loading configurations from $readDir"
    if $monitorLines {
        ClearPVSettings $monitorScroll
    }
    if !$brief {
        if $initLines {
	    ClearInitFinalizeSettings $initScroll -makeType init -popup 0
        }
        if $finalizeLines {
            ClearInitFinalizeSettings $finalizeScroll -makeType finalize -popup 0
        }
        if $execLines {
            ClearExecSettings $execScroll -popup 0
        }
    }
    if $KnobLines {
        ClearKnobSettings $KnobScroll -popup 0
    }
    if $postProcessLines {
        ClearPostprocessSettings $postProcessScroll -popup 0
    }
    
    if [catch {LoadMonitorLines -directory $readDir} result] {
        return -code error "LoadMonitorLines: $result"
    }
    if [catch {LoadControlPVs -directory $readDir} result] {
        return -code error "LoadControlPVs: $result"
    }
    if [catch {LoadKnobInput -directory $readDir} result] {
        return -code error "LoadKnobInput: $result"
    }
    
    if [catch {LoadInitFinalizeInput -directory $readDir} result] {
        return -code error "LoadInitFinalizeInput: $result"
    }
    if [catch {LoadExecInput -directory $readDir} result] {
        return -code error "LoadExecInput: $result"
    }
    if [catch {LoadPostprocessInput -directory $readDir} result] {
        return -code error "LoadPostProcessInput: $result"
    }
    set outputFileChecked 0
    SetMainStatus "Ready."
}
#procedures for adding initialize/finalize lines
proc AddInitFinalizeLine {args} {
    global initFinalVarList  initLines finalizeLines initScroll finalizeScroll
    eval global $initFinalVarList
    set makeType ""
    set line 0
    set mode add
    APSParseArguments {line makeType mode}
    if [winfo exists .init] {
        destroy .init
    }
    set initFinalMode $mode
    switch $mode {
        add {
            set initLine [set ${makeType}Lines]
        } 
        insert {
            set initLine $line
        }
    }
    set initFinalName ""
    set initFinalMakeType $makeType
    set initFinalScroll [set ${makeType}Scroll]
    APSDialogBox .init -name "Add Init/Finalize line Box" -width 30 \
      -cancelCommand "" -contextHelp "Press OK button to add PV name." \
      -okCommand {
          if [catch {MakeNewInitFinalizeLine $initFinalScroll -name $initFinalName -value $initFinalValue \
                       -script $initFinalScript \
                       -orig $initFinalOrig -type $initFinalType -line $initFinalLine -makeType $initFinalMakeType \
                       -mode $initFinalMode -readback $initFinalReadback -tolerance $initFinalTolerance} result] {
              SetMainStatus "$result"
              return
          }
      }
    APSDialogBoxAddButton .clear -parent .init -text "Clear" \
      -command {set initFinalName "";set initFinalValue "";set initFinalOrig "";set initFinalReadback ""}
    APSRadioButtonFrame .choice -parent .init.userFrame -buttonList {"Run Script" "Set PV"} \
      -valueList {RunScript SetPV} -variable initFinalType -orientation horizontal -label ""\
      -commandList {"SetInitFinalArguments .init.userFrame" \
                      "SetInitFinalArguments .init.userFrame"} \
      -contextHelp "Choose \"Run Script\" to run a script for initialization or \
\"Set PV\" to set a PV to a given value."
    if [string length $initFinalType] {
        SetInitFinalArguments .init.userFrame
    }
}

proc SetInitFinalArguments {parent} {
    global initFinalVarList
    eval global $initFinalVarList
    
    destroy $parent.arg
    APSFrame .arg -parent $parent -packOption "-fill both -expand true"
    set w $parent.arg.frame
    switch $initFinalType {
        RunScript {
            APSLabeledEntry .script -parent $w -textVariable initFinalScript -label "Script command:" \
              -width 70 -contextHelp "enter the script command here." -packOption "-fill x -expand true"
        }
        SetPV {
            APSLabeledEntry .pv -parent $w -textVariable initFinalName -label "PV name:" -width 25
            APSLabeledEntry .re -parent $w -textVariable initFinalReadback -label "Readback PV name:" -width 25
            APSLabeledEntry .val -parent $w -textVariable initFinalValue -label "Set value:" -width 25
            APSLabeledEntry .orig -parent $w -textVariable initFinalOrig -label "Original Value:" -width 25 \
              -contextHelp "The PV's value will be restored to this value after experiment."
            APSLabeledEntry .tole -parent $w -textVariable initFinalTolerance -label "Tolerance:" -width 25 \
              -contextHelp "The acceptance between readback and setpoint values."
        }
    }
}


proc CreateInitFinalizeLine {widget0 args} {
    global init finalize initScroll initScrollFrame finalizeScroll finalizeScrollFrame
    global changeWidgetStateList popupInterface
    
    set line ""
    set makeType init
    set type ""
    set mode add
    APSParseArguments {line makeType type mode} 
    
    set widget $widget0.m$line
    
    if [winfo exist $widget] {
        destroy $widget
    }
    APSFrame .m$line -parent $widget0 -packOption "-side top -fill both -expand true"
    set w $widget0.m$line.frame
    $w configure -borderwidth 0 -relief flat
    switch $makeType {
        init {
            set select 2
        }
            finalize {
                set select 4
            }
    }
    switch $type {
        SetPV {
            APSLabeledEntry .pv -parent $w -label "" -textVariable ${makeType}($line.name) \
              -width 27 -packOption "-side left"
            APSLabeledEntry .readback -parent $w -label "" \
              -textVariable ${makeType}($line.readback) -width 27 -packOption "-side left"
            APSLabeledEntry .orig -parent $w -label "" -textVariable ${makeType}($line.value) \
              -width 11 -packOption "-side left"
            APSLabeledEntry .set -parent $w -label "" -textVariable ${makeType}($line.orig) \
                  -width 11 -packOption "-side left"
            APSLabeledEntry .tol -parent $w -label "" -textVariable ${makeType}($line.tolerance) \
              -width 7 -packOption "-side left"
            APSButton .insert -parent $w -text "INSERT" -size small \
              -command "AddInitFinalizeLine -mode insert -line $line -makeType $makeType" \
              -packOption "-side left" -width ""
            APSButton .delete -parent $w -text "DELETE" -size small \
                  -command "DeleteInitFinalizeLine $widget0 $line -makeType $makeType" -width ""
            .userFrame.main.frame.tn select $select
            tkwait  visibility $w.set
        }
        RunScript {
            set script [set ${makeType}($line.script)]
            if {$popupInterface && [string compare $mode "load"]} {
                if ![string match "exec *" $script] {
                    if [APSYesNoPopUp "exec command is missing for RunScript $script, add it?"] {
                            set ${makeType}($line.script) [linsert $script 0 exec]
                    }
                }
            }
            set ${makeType}($line.name) [set ${makeType}($line.script)]
            APSLabeledEntry .script -parent $w -label "Script:" -width 90 \
              -textVariable ${makeType}($line.name) -packOption "-fill x -expand true" -editButton 1
            APSButton .insert -parent $w.script.entry -text "INSERT" -size small \
              -command "AddInitFinalizeLine -mode insert -makeType $makeType -line $line" \
              -packOption "-side right" -width ""
            APSButton .delete -parent $w.script.entry -text "DELETE" -size small \
              -command "DeleteInitFinalizeLine $widget0 $line -makeType $makeType" \
              -packOption "-side right" -width ""
            APSButton .test -parent $w.script.entry -text "RUN" -size small \
              -contextHelp "Tests the script command." -packOption "-side right" \
              -command "RunInitFinalScript -type $makeType -line $line" -width ""
            .userFrame.main.frame.tn select $select
            tkwait visibility $w.script
        }
        # lappend changeWidgetStateList $w.delete.button
        # lappend changeWidgetStateList $w.insert.button
    }
    APSScrollAdjust [set ${makeType}ScrollFrame] -numVisible 9
}

proc DeleteInitFinalizeLine {widget0 line args} {
    set makeType ""
    APSParseArguments {makeType}
    global $makeType ${makeType}Lines changeWidgetStateList
    
   # set j [lsearch $changeWidgetStateList $widget0.m$line.frame.delete.button]
   # set changeWidgetStateList [lreplace $changeWidgetStateList $j $j]
   # set j [lsearch $changeWidgetStateList $widget0.m$line.frame.insert.button]
   # set changeWidgetStateList [lreplace $changeWidgetStateList $j $j]
    set lines [set ${makeType}Lines]
    if {$line>$lines} {
        return
    }
    #if [winfo exist $widget0.m$line] {
        destroy $widget0.m$line
    #}
    set ${makeType}($line.valid) 0
    foreach info {name readback type script orig value tolerance}  {
        if [info exists ${makeType}($line.$info)] {
           # unset ${makeType}($line.$info)
        }
    }
}

proc RunInitFinalScript {args} {
    set type ""
    set line ""
    APSParseArguments {type line}
    global $type globalParseVariables monitorLineVariables
    eval global $globalParseVariables
    if [llength $monitorLineVariables] {
	eval global $monitorLineVariables
    }
    if {[set ${type}($line.type)]=="RunScript"} {
        set command [set ${type}($line.name)]
        if [string length $command] {
	    if [catch {eval $command} result] {
		SetMainStatus "Running \"$command\" failed: $result"
	    } else {
		SetMainStatus "Running \"$command\" completed."
	    }
        }
    }
}

proc MakeNewInitFinalizeLine {widget0 args} {
    global initScrollFrame finalizeScrollFrame init finalize initLines finalizeLines 
    global popupInterface brief
    
    set mode add
    set type ""
    set orig ""
    set name ""
    set script ""
    set value ""
    set makeType init
    set line 0
    set readback ""
    set tolerance 0.01
    APSParseArguments {mode type orig name value makeType line readback tolerance script}

    if ![string length $type] {
        return -code error "MakeNewInitFinalizeLine: please choose the type: SetPV or RunScript."
    }
    if {$type=="SetPV"} {
        if [catch {SearchPVName -useCA 1 -pvnameList $name} result] {
            return -code error "MakeNewInitLine: $result"
        }
        if [string length $readback] {
            if [catch {SearchPVName -useCA 1 -pvnameList $readback} result] {
                return -code error "MakeNewInitLine: $result"
            }
        } else {
            set readback $name
        }
    }
    set varList {type name value script orig valid readback tolerance}
    switch $mode {
        add -
	load {
            #the initialization type could be SetPV or RunScript
            if {$type=="SetPV" && ![string length $orig]} {
                if [catch {exec cavget -numerical -list=$name -floatFormat=%.15g -pend=30} orig] {
                    return -code error "$orig"
                }
            }
            set line [set ${makeType}Lines]
            set valid 1
            foreach var $varList {
                set ${makeType}($line.$var) [set $var]
            }
            if {$popupInterface && !$brief} {
                if [catch {CreateInitFinalizeLine $widget0 -line $line \
                             -makeType $makeType -type $type -mode $mode} result] {
                    SetMainStatus "$result"
                    return
                }
            }
            incr ${makeType}Lines
        }
        insert {
            set lines [set ${makeType}Lines]
            for {set i $lines} {$i>$line} {incr i -1} {
                set j [expr $i -1]
                foreach var $varList {
                    set ${makeType}($i.$var) [set ${makeType}($j.$var)]
                }
            }
            if {$type=="SetPV" && ![string length $orig]} {
                if [catch {exec cavget -numerical -list=$name -floatFormat=%.15g -pend=30} orig] {
                    return -code error "$orig"
                }
            }
            set valid 1
            foreach var $varList {
                set ${makeType}($i.$var) [set $var]
            }
            incr ${makeType}Lines
            if {$popupInterface && !$brief} {
                for {set i $line} {$i<[set ${makeType}Lines]} {incr i} {
                    set widget $widget0.m$i
                    if [winfo exist $widget] {
                        destroy $widget
                    }
                    if [set ${makeType}($i.valid)] {
                        if [catch {CreateInitFinalizeLine $widget0 \
                                     -line $i -makeType $makeType -mode $mode \
                                     -type [set ${makeType}($i.type)]} result] {
                            SetMainStatus "$result"
                            return
                        }
                    }
                }
            }
        }
    }
}

proc LoadInitFinalizeInput {args} {
    global home initScroll finalizeScroll
    set directory $home
    APSParseArguments {directory}
    foreach file {initialize finalize}  makeType {init finalize} {
        if [file exist $directory/$file] {
            SetMainStatus "loading $makeType settings..."
            if [catch {sdds load $directory/$file data} result] {
                SetMainStatus "LoadInitFinalizeInput: $result"
                return
            }
            set name [lindex $data(Column.ControlName) 0]
            set value [lindex $data(Column.SetValue) 0]
            set origValue [lindex $data(Column.RestoreValue) 0]
            set types [lindex $data(Column.Type) 0]
            set readbacks [lindex $data(Column.ReadbackName) 0]
            if [info exists data(Column.Tolerance)] {
                set tolerances [lindex $data(Column.Tolerance) 0]
            } else {
                set size [llength $name]
                for {set i 0} {$i<$size} {incr i} {
                    lappend tolerances 0.01
                }
            }
            if ![llength $name] {
                return
            }
            set scroll [set ${makeType}Scroll]
            foreach nm $name val $value orig $origValue type $types readback $readbacks tol $tolerances {
                if [catch {MakeNewInitFinalizeLine $scroll -name "$nm" -value $val -tolerance $tol \
                             -orig $orig -type $type -mode load -script "$nm" \
                             -readback $readback -makeType $makeType} result] {
                    SetMainStatus "$result"
                }
            }
        }
    }
}

#procedures for adding execution lines
proc DeleteExecLine {widget0 number} {
    global exec execLines changeWidgetStateList outputFileWidget

    destroy $widget0.m$number
    #set j [lsearch $changeWidgetStateList $widget0.m$number.frame.delete.button]
    #set changeWidgetStateList [lreplace $changeWidgetStateList $j $j]
    #set j [lsearch $changeWidgetStateList $widget0.m$number.frame.insert.button]
    #set changeWidgetStateList [lreplace $changeWidgetStateList $j $j]

    set exec($number.valid) 0
    if [winfo exist $outputFileWidget.file$number] { 
        destroy $outputFileWidget.file$number
    } 
    return
}

proc addExecEntry {args} {
    global execVarList
    eval global $execVarList

    set mode add
    set line 0
    APSParseArguments {mode line}
    set execMode $mode
    set execLine $line
    if [winfo exists .exec] {
        destroy .exec
    }
    APSDialogBox .exec -name "Add Execution Steps" -width 50 \
      -cancelCommand "" -contextHelp "Press OK button to add Execution in order" \
      -okCommand {
          if [catch {MakeNewExecLine $execScroll -name $execName -WaitMessage $WaitMessage \
                       -mode $execMode -insertLine $execLine \
                       -setPV $setPV -setPVValue $setPVValue \
                       -waitTime $waitTime -sdds_append $sddsProgramAppend \
                       -sdds_appendToPage $sddsProgramAppendToPage \
                       -sdds_inputfile $sddsProgramInput -sdds_outputSuffix $sddsProgramOutputSuffix  \
                       -sdds_steps $sddsProgramSteps \
                       -sdds_interval $sddsProgramInterval -sdds_userOption $sddsProgramUserOption \
                       -runscript $runScript -sdds_program $sddsProgramName -customProgramName $customProgramName \
                       -query_columnName $queryColumnName -query_dataType $queryColumnDataType \
                       -query_units $queryColumnUnits} result] {
              SetMainStatus "$result"
              return
          }
      }
    for {set i 0} {$i<12} {incr i} {
        lappend commList SetExecArguments 
    }
    APSRadioButtonFrame .choice -parent .exec.userFrame \
      -label "Execution Name:" -variable execName \
      -contextHelp "Choose the execution name and Set the arguments for it" -limitPerRow 2 \
      -buttonList {ChangeControl RunScript RunSDDSProgram RunCustomProgram ReadValue SetValue WaitForUser \
                     WaitTime SetPV RunStatistics QueryUser TestPV} \
      -valueList {ChangeControl RunScript RunSDDSProgram RunCustomProgram ReadValue SetValue \
                    WaitForUser WaitTime SetPV RunStatistics QueryUser TestPV} \
      -orientation vertical -contextHelp "Choose one of the executions for the above step order." \
      -commandList $commList
    if [string length $execName] {
        SetExecArguments
    }
}

proc SetExecArguments {args} {
    global execVarList readbackSavedList Timeout
    eval global $execVarList
    set parent .exec.userFrame

    destroy $parent.arg
    
    APSFrame .arg -parent $parent -packOption "-anchor w -fill x" -name "Set Arguments"
    set w $parent.arg.frame
    switch $execName {
        WaitTime {
            label $parent.arg.frame.help  -bg "\#DDDD22" -fg red \
              -text "Wait for given time in seconds before continue."
            pack $parent.arg.frame.help -side top
            APSLabeledEntry .time -parent $parent.arg.frame -label "Wait time(s):" -width 15\
              -textVariable waitTime \
              -contextHelp "Please enter the time in seconds you wish to wait"
        }
        TestPV {
           label $parent.arg.frame.help  -bg "\#DDDD22" -fg red \
              -text "Wait at most the given time in seconds when the pv tests failed."
            pack $parent.arg.frame.help -side top
            APSLabeledEntry .time -parent $parent.arg.frame -label "Timeout(s):" -width 15\
              -textVariable Timeout \
              -contextHelp "Please enter the maximum allowed waiting time when the pv tests failed." 
        }
        SetPV {
            label $parent.arg.frame.help  -bg "\#DDDD22" -fg red \
              -text "SetPV: set the following given pv to the given value at each experiment step."
            pack $parent.arg.frame.help -side top
            APSLabeledEntry .setpv -parent $parent.arg.frame -label "Set PV name:" -width 25 \
              -textVariable setPV -packOption "-side left" \
              -contextHelp "Please enter the PV name you want to set in the experiment" 
            APSLabeledEntry .setValue -parent $parent.arg.frame -label "Set value:" -width 25 \
              -textVariable setPVValue -packOption "-side left" \
              -contextHelp "Please enter the value to set the PV."  
        }
        QueryUser {
            label $parent.arg.frame.help  -bg "\#DDDD22" -fg red \
              -text "QueryUser: produce a pop-up dialog that asks user to input a value at each experiment step\n and logged into the ReadValue's log file."
            pack $parent.arg.frame.help -side top
            #this action would produce a pop-up dialog taht asks the user to input a value
            APSLabeledEntry .query1 -parent $parent.arg.frame -label "Column Name:" -width 30 \
              -textVariable queryColumnName \
              -contextHelp "the column name for query user whose value will be written to the output file"
            APSLabeledEntry .query2 -parent $parent.arg.frame -label "Units:" -width 30 \
              -textVariable queryColumnUnits -contextHelp "the units of the query user column"
            APSRadioButtonFrame .query3 -parent $parent.arg.frame -label "Data Type:" \
              -buttonList {double float long short string} \
              -valueList {double float long short string} \
              -variable queryColumnDataType -contextHelp "The data type of the query user column" \
              -orientation horizontal
        }
        ChangeControl {
            label $parent.arg.frame.desc -bg "\#DDDD22" -fg red \
              -text "ChangeControl are control PVs that are changed during the experiment.\nYou must add the control PVs to the \"ProcessVariable\" panel\n in order to use them in ChangeControl execution steps.\n You may add control PVs later using add button in PV panel."
            pack $parent.arg.frame.desc -side left
        }
        SetValue {
            label $parent.arg.frame.desc -bg "\#DDDD22" -fg red \
              -text "SetValue is for setting the output pvs to the values specified in process variable panel.\nYou must output pvs set up in the process variable planel\n to be able to use SetValue execution.\n You may add output pvs later using add button in PV panel \nor SET/VIEW Arguments button of SetValue execution."
            pack $parent.arg.frame.desc -side left
        }
        WaitForUser {
            label $parent.arg.frame.help -bg "\#DDDD22" -fg red \
              -text "WaitForUser: popup message and wait for user's response."
            pack $parent.arg.frame.help -side top
            APSLabeledEntry .message -parent $parent.arg.frame -label "Message:" -textVariable WaitMessage \
              -width 80 -contextHelp "Please enter message for waiting for your response." -packOption "-fill x -expand true"
        }
        RunScript {
            APSLabeledEntry .script -parent $parent.arg.frame -textVariable runScript \
              -label "Script:" -width 80 -packOption "-fill x -expand true" \
              -contextHelp "Enter a script to run at this stage of the experiment. The calling syntax is user-defined.\n."
            label $parent.arg.frame.help -bg "\#DDDD22" -fg red -text "Run a script. For an external script, the script must be preceded by an exec.\nWithout an exec, it is assumed that the script is actually a tcl command or a library call.\n\nYou may pass the values of global variables to user script, see \"ArgumentsPassHelp\" for help"
            pack $parent.arg.frame.help -side left
        }
        RunCustomProgram {
            APSLabeledEntry .comm -parent $parent.arg.frame -textVariable customProgramName \
              -label "Command:" -width 80  -packOption "-fill x -expand true" \
              -contextHelp "Enter the custom command  to run at this stage of the experiment. The calling syntax is user-defined.\n."
            label $parent.arg.frame.help -bg "\#DDDD22" -fg red -text "Run a command. For an external script, the script must be preceded by an exec.\nWithout an exec, it is assumed that the script is actually a tcl command or a library call.\n\nYou may pass the values of global variables to user script, see \"ArgumentsPassHelp\" for help"
        }
        RunSDDSProgram {
            APSFrame .name -parent $w
            set w1 $w.name.frame
            $w1 configure -relief flat -borderwidth 0
            APSLabeledEntry .entry -parent $w1 -label "Program: " -textVariable sddsProgramName \
              -contextHelp "Enter the program name to execute." \
              -width 30 -packOption "-side left"
            APSRadioButtonFrame .choice -parent $w1 -label "" -variable sddsProgramName \
              -buttonList {sddsmonitor sddslogger sddswmonitor sddsstatmon} \
              -valueList {sddsmonitor sddslogger sddswmonitor sddsstatmon} \
              -orientation horizontal
            #pack [frame $w.f1 -relief flat] -fill x -expand true
            APSFrame .f1 -parent $w -packOption "-fill x -expand true"
            set w1a $w.f1.frame
            $w1a configure -relief flat -borderwidth 0
            APSLabeledEntry .input -parent $w1a -width 80 -packOption "-fill x -expand true" \
              -textVariable sddsProgramInput -contextHelp "Enter the input file here." \
              -label "Input file:        "
            pack [ttk::frame $w.f2 -relief flat] -fill x -expand true
            set w2 $w.f2
            APSLabeledEntry .out -parent $w2 -label "Output File Suffix:" \
              -textVariable sddsProgramOutputSuffix -width 80 -packOption "-side left"\
              -contextHelp "Enter the suffix name of the output file"
            APSFrame .ops2 -parent $w -label "" -packOption "-side top -fill x"
            set w3 $w.ops2.frame
            $w3  configure -relief flat -borderwidth 0
            APSLabeledEntry .step -parent $w3 -width 5\
              -packOption "-side left" -label "steps: " -textVariable sddsProgramSteps \
              -contextHelp "Enter the total number of running steps" 
            APSLabeledEntry .interval -parent $w3 -width 5\
              -packOption "-side left" -label "interval (s): " -textVariable sddsProgramInterval \
              -contextHelp "Enter the interval between two data collecting steps" 
            APSCheckButtonFrame .app -parent $w3 -label ""\
              -packOption "-side left" -buttonList {append} -variableList sddsProgramAppend \
              -contextHelp "the data will be appended to the existing output file if it is selected."
            APSCheckButtonFrame .apppage -parent $w3 -label ""\
              -packOption "-side left" -buttonList {appendToPage} -variableList sddsProgramApendToPage \
              -contextHelp "The data will be appended to the last page of the output file if it is selected."
            
            APSFrame .ops1 -parent $w -label "" -packOption "-side top -fill x"
            set widget $w.ops1.frame
            $widget  configure -relief flat -borderwidth 0
            APSLabeledEntry .app -parent $widget -label "User custom options:"\
              -textVariable sddsProgramUserOption -width 70 -packOption "-fill x -expand true" \
              -contextHelp "Enter the other options here."
        }
        ReadValue {
            global outputDir outputRootname logFile logFileSuffix
            set logFile $outputDir/${outputRootname}-$logFileSuffix
            label $w.help -bg "\#DDDD22" -fg red -text "ReadValue reads the values of all PVs listed in the ProcessVariable panel and\n log in file \$outputDir/\${outputRootname}-\$logFileSuffix."
            pack $w.help -side top
            APSLabeledEntry .file -parent $w -label "Log File suffix name:" \
              -textVariable logFileSuffix -width 45 \
              -contextHelp "Enter a log file name or generate by clicking next button."
            bind $w.file.entry <Leave> "UpdateLogFile"
            APSLabeledOutput .name -parent $w -label "Log file name: " -textVariable logFile \
              -width 70
        }
        RunStatistics {
            global Stats outputDir outputRootname
            set Stats(file) $outputDir/$outputRootname-$Stats(fileSuffix)
            if ![llength $readbackSavedList] {
                label $parent.arg.frame.help -bg "\#DDDD22" -fg red -text "No readback PVs found in PRocessVariable panel for running statistics!"
                pack $parent.arg.frame.help
                return
            }
            global Stats
            APSFrame .stats -parent $w
            set w1 $w.stats.frame
            pack [ttk::frame $w1.pm -relief flat] -fill x -expand true
            $w1.pm configure -relief flat -borderwidth 0
            set w2 $w1.pm
            APSLabeledEntry .entry -parent $w2 -label "Statistics output file suffix: " -textVariable Stats(fileSuffix) \
              -contextHelp "Enter the suffix of file name to log the statistics." \
              -width 60 
            bind $w2.entry.entry <Leave> "UpdateLogFile"
            APSLabeledOutput .name -parent $w2 -label "Statistics output file:" \
              -textVariable Stats(file) -width 60
            pack [ttk::frame $w1.pm1 -relief flat] -fill x -expand true
            $w1.pm1 configure -relief flat -borderwidth 0
            set w2 $w1.pm1
            APSLabeledEntry .entry -parent $w2 -label "no. to average: " -textVariable Stats(average) \
              -contextHelp "Enter the number of average to run statistics." \
              -width 30 -packOption "-side left"
            APSLabeledEntry .entry1 -parent $w2 -label "pause time: " -textVariable Stats(pause) \
              -contextHelp "Enter pause time between two readings." \
              -width 30 -packOption "-side left"
            
            set buttonList {mean sigma stDev min max spread}
            set variableList [list Stats(mean) Stats(sigma) Stats(stDev) Stats(min) \
                                Stats(max) Stats(spread)]
            pack [ttk::frame $w1.ck -relief flat] -fill x -expand true
            $w1.ck configure -relief flat -borderwidth 0
            set w2 $w1.ck
            APSCheckButtonFrame .group -parent $w2 -label "statistics:  " \
              -packOption "-side top" \
              -variableList $variableList -buttonList $buttonList -orientation horizontal \
              -allNone 1 -contextHelp "Choose the statistics"
        }
    }
}

proc UpdateLogFile {} {
    global logFileSuffix logFile outputDir outputRootname Stats
    set logFile $outputDir/${outputRootname}-$logFileSuffix
    set Stats(file) $outputDir/$outputRootname-$Stats(fileSuffix)
}


ttk::style configure Bold.TButton -font -adobe-courier-medium-r-normal-*-12-*-*-*-*-*-*-*

proc CreateExecLine {widget0 line args} {
    global exec execScroll execLines execScrollFrame changeWidgetStateList brief apsttk
    set mode add
    APSParseArguments {mode}
    if [winfo exist $widget0.m$line] {
        destroy $widget0.m$line
    }
    APSFrame .m$line -parent $widget0
    set w $widget0.m$line.frame
    $w configure -borderwidth 1
    APSLabeledOutput .label -parent $w -label "Type:" -textVariable exec($line.name) \
      -width 25 -packOption "-side left"
    APSButton .view -parent $w -text "SET/VIEW Arguments" -packOption "-side left"  \
      -command "$exec($line.name)Box $line"\
      -contextHelp "Click to view or set the execution arguments."
    APSButton .insert -parent $w -text "INSERT" -packOption "-side left"\
      -command "addExecEntry -line $line -mode insert" \
      -contextHelp "Press to insert an execution line before current line."
    APSButton .delete -parent $w -text "DELETE" -packOption "-side left"\
      -command "DeleteExecLine $widget0 $line" \
      -contextHelp "Press to delete the corresponding execution line."
    if {$apsttk} {
        $w.view.button configure -style Bold.TButton
        $w.insert.button configure -style Bold.TButton
        $w.delete.button configure -style Bold.TButton
    } else {
        $w.view.button configure -font -adobe-courier-medium-r-normal-*-12-*-*-*-*-*-*-*
        $w.insert.button configure -font -adobe-courier-medium-r-normal-*-12-*-*-*-*-*-*-*
        $w.delete.button configure -font -adobe-courier-medium-r-normal-*-12-*-*-*-*-*-*-*
    }
    .userFrame.main.frame.tn select 3 
    tkwait visibility $w.label
    APSScrollAdjust $execScrollFrame -numVisible 7
  #  lappend changeWidgetStateList $w.delete.button 
  #  lappend chagneWidgetStateLsit $w.insert.button
   
}

proc ClearExecSettings {widget0 args} {
    set popup 1
    APSParseArguments {popup}
    global execLines exec execScrollFrame changeWidgetStateList execParseArgumentsList 

    if !$execLines {
        return
    }
    if $popup {
        if {![APSYesNoPopUp "Really? Clear all Execution settings?"]} { return }
    }
    #APSScrollAdjust $execScrollFrame -numVisible 2
    for {set i 0} {$i<$execLines} {incr i} {
        if {[winfo exists $widget0.m$i]} {
          #  set j [lsearch $changeWidgetStateList $widget0.m$i.frame.delete.button]
          #  set changeWidgetStateList [lreplace $changeWidgetStateList $j $j]   
          #  set j [lsearch $changeWidgetStateList $widget0.m$i.frame.insert.button]
          #  set changeWidgetStateList [lreplace $changeWidgetStateList $j $j] 
            destroy $widget0.m$i
        }
        foreach var $execParseArgumentsList {
            if [info exist exec($i.$var)] {
                unset exec($i.$var)
            }
        }
        set exec(i.valid) 0
    }
    set execLines 0
}
proc MakeNewExecLine {widget0 args} {
    global execScrollFrame execScroll exec execLines changeWidgetStateList popupInterface brief
    global readbackSavedList QueryColumnNames execParseArgumentsList
    global outputDir outputRootname brief outputFileWidget

    foreach arg $execParseArgumentsList {
        set arg ""
    }
    set mode add
    set insertLine 0
    set sdds_append 0
    set sdds_appendToPage 0
    set sdds_steps 5
    set sdds_interval 1
    APSStrictParseArguments {mode insertLine name setPV setPVValue waitTime sdds_append \
                               sdds_appendToPage sdds_inputfile \
                               sdds_program sdds_outputSuffix sdds_steps \
                               sdds_interval sdds_userOption runscript \
                               WaitMessage query_columnName query_dataType query_units customProgramName}
    if {![string length $name]} {
        return -code error "MakeNewExecLine: No name submited for execution."
    }
    set nameList ""
    for {set i 0} {$i<$execLines} {incr i} {
        if {[info exist exec($i.name)] && $exec($i.valid)} {
            lappend nameList $exec($i.name)
        }
    }
    switch $name {
        RunSDDSProgram {
            if {![string length $sdds_program]} {
                return -code error "The program name is not given for adding RunSDDSProgram.!"
            }
            if {![string length $sdds_inputfile]} {
                return -code error "The input file name for $sdds_program is not given."
            }
        }
        RunCustomProgram {
            if ![string length $customProgramName] {
                return -code error "The custom program name is not given for adding CustomProgram.!"
            }
            if {$popupInterface && $mode=="add"} {
                if ![string match "exec *" $customProgramName] {
                    if [APSYesNoPopUp "exec command is missing for RunCustomProgram, add it?"] {
                        set customProgramName [linsert $runscript 0 exec]
                    }
                }
            }
        }
        WaitTime {
            if ![string length $waitTime] {
                return -code error "The wait time is not given."
            }
            if $waitTime<0 {
                set waitTime 0.001
            }
        }
        SetPV {
            if {![string length $setPV]} {
                return -code error "The PV name is not given.!"
            }
            if ![string length $setPVValue] {
                return -code error "The PV value is not given!"
            }
        }
        RunScript {
            if ![string length $runscript] {
                return -code error "The script is not provided."
            }
            if {$popupInterface && $mode=="add"} {
                if ![string match "exec *" $runscript] {
                    if [APSYesNoPopUp "exec command is missing for RunScript $runscript, add it?"] {
                        set runscript [linsert $runscript 0 exec]
                    }
                }
            }
        }
        RunStatistics {
            set StatsIncluded 1
            if {[lsearch $nameList RunStatistics] >=0} {
                bell
                return -code error "RunStatistics already exists."
            }
            #check if there is readbacklist
            if ![llength $readbackSavedList] {
                return -code error "No readbacks for running statistics!"
            }
        }
        QueryUser {
            if ![string length $query_columnName] {
                return -code error "column name is not given for QueryUser entry"
            }
            if ![string length $query_dataType] {
                return -code error "data type is not given for QueryUser entry"
            }
            if [lsearch $QueryColumnNames $query_columnName]>=0 {
                return -code error "$query_columnName for QueryUser already exists!"
            }
        }
    }
    
    switch $mode {
        add -
        load {
            set exec($execLines.name) $name
            set exec($execLines.valid) 1
            if {$popupInterface && !$brief} {
                CreateExecLine $widget0 $execLines
            }
            set line $execLines
            incr execLines
        }
        insert {
            for {set i $execLines} {$i>$insertLine} {incr i -1} {
                set j [expr $i-1]
                set widget $widget0.m$j
                set exec($i.name) $exec($j.name)
                set exec($i.valid) $exec($j.valid)
                if $exec($j.valid) {
                    if [winfo exist $outputFileWidget.file$j] {
                        destroy $outputFileWidget.file$j
                    }
                    switch $exec($j.name) {
                        RunSDDSProgram {
                            foreach nm {inputfile program steps interval append \
                                            appendToPage userOption outputSuffix} {
                                set exec($i.sdds_$nm) $exec($j.sdds_$nm)
                            }
                            CreateOutputFileLine RunSDDSProgram $i
                        }
                        QueryUser {
                            foreach nm {columnName dataType units} {
                                set exec($i.query_$nm) $exec($j.query_$nm)
                            }
                        }
                        WaitTime {
                            set exec($i.waitTime) $exec($j.waitTime)
                        }
                        SetPV {
                            set exec($i.setPV) $exec($j.setPV)
                            set exec($i.setPVValue) $exec($j.setPVValue)
                        }
                        RunScript {
                            set exec($i.runscript) $exec($j.runscript)
                        }
                        RunCustomProgram {
                            set exec($i.customProgramName) $exec($j.customProgramName)
                        }
                        WaitForUser {
                            set exec($i.WaitMessage) $exec($j.WaitMessage)
                        }
                        ReadValue -
                        RunStatistics {
                            CreateOutputFileLine $exec($j.name) $i
                        }
                        default {
                        }
                    }
                }
            }
            incr execLines
            set exec($insertLine.name) $name
            set exec($insertLine.valid) 1
            set line $insertLine
            if {$popupInterface && !$brief} {
                for {set i $insertLine} {$i<$execLines} {incr i} {
                    set widget $widget0.m$i
                    if [winfo exist $widget] {
                        destroy $widget
                        set widget "" 
                    }
                    if $exec($i.valid) {
                        CreateExecLine $widget0 $i 
                    }
                }
            }
        }
    }
    switch $name {
        RunSDDSProgram {
            foreach name {inputfile program steps interval append \
                            appendToPage userOption outputSuffix} {
                set exec($line.sdds_$name) [set sdds_$name]
            }
            set exec($line.sdds_outputfile) $outputDir/$outputRootname-$exec($line.sdds_outputSuffix)
        }
        QueryUser {
            set exec($line.query_columnName) $query_columnName 
            set exec($line.query_dataType) $query_dataType 
            set exec($line.query_units) $query_units
        }
        WaitTime {
            set exec($line.waitTime) $waitTime
        }
        SetPV {
            set exec($line.setPV) $setPV
            set exec($line.setPVValue) $setPVValue
        }
        RunScript {
            set exec($line.runscript) $runscript
           # set RunScript($line.capture) $capture
        }
        RunCustomProgram {
            set exec($line.customProgramName) $customProgramName
        }
        WaitForUser {
            set exec($line.WaitMessage) $WaitMessage
        }
        default {
        }
    }
    if {$popupInterface} {
        CreateOutputFileLine $exec($line.name) $line
    }
    return "done"
}

proc LoadExecInput {args} {
    global home execScroll Stats logFileSuffix outputDir ExpSteps Steps Interval Description daily autoIncr
    global Description popupInterface outputRootname RunPostprocess StartStep Timeout
    
    set WaitMessage ""
    set directory $home
    APSParseArguments {directory} 
    if ![file exist $directory/execution] {
        return
    }
    SetMainStatus "loading executions..."
    if [catch {sdds load $directory/execution execData} result] {
        SetMainStatus "LoadExecInput: $result"
        return
    }
    set execName ""
    set pages [llength $execData(Parameter.ExecutionName)]
    if !$pages {
        return
    }
    set argList {Script Capture name Inputfile outputfileSuffix Append AppendToPage UserOptions\
                   logFileSuffix WaitTime PVname PVvalue customProgramName customProgramOption}
    foreach var $argList {
        set $var ""
    }
    set logFileSuffix ReadValue
    set OutputfileSuffix sdds
    foreach par $execData(ParameterNames) {
        if $popupInterface {
            set $par [lindex $execData(Parameter.$par) 0]
            #if {$par=="OutputDirectory"} {
            #    set outputDir [set $par]
            #}
            if {$par=="OutputRootname"} {
                set outputRootname [set $par]
            }
        } elseif {$par!="OutputDirectory" && $par!="OutputRootname"} {
            set $par [lindex $execData(Parameter.$par) 0]
        }
    }
    if [lsearch $execData(ParameterNames) Steps]>=0 {
        set ExpSteps $Steps
    }
    set statsList {mean min max spread sigma }
    for {set i 0} {$i<$pages} {incr i} {
        set j [expr $i+1]
        set execution [lindex $execData(Parameter.ExecutionName) $i]
        if {$execution=="RunProgram"} {
            set execution RunSDDSProgram
        } elseif {$execution=="CustomProgram"} {
            set execution RunCustomProgram
        }
        set name [lindex $execData(Column.TclVariableName) $i]
        set value [lindex $execData(Column.TclVariableValue) $i]
        
        set columnName ""
        set dataType ""
        set units ""
        switch $execution {
            ChangeControl -
            SetValue {
            }
            WaitForUser {
                foreach nm $name val $value {
                    set $nm $val
                }
            }
            default {
                foreach nm $name val $value {
                    set $nm $val
		    if {$nm=="outputfileSuffix"} {
			set OutputfileSuffix $val
		    }
                }
                if {$execution=="RunStatistics"} {
                    foreach nm $name val $value {
                        set nm1 [string tolower $nm]
                        if {$nm=="stdev"} {set nm stDev}
                        if [lsearch $statsList $nm1]>=0 {
                            set nm $nm1
                        }
                        set Stats($nm) $val
                    }
                }
            }
        }
        if [lsearch -exact $Script Step]>=0 {
            set Script [regsub {Step} $Script \$Step]
        }
        if [lsearch -exact $Script OutputRootname]>=0 {
            set Script [regsub {OutputRootname} $Script \$outputRootname]
        }
        if [lsearch -exact $Script outputRootname]>=0 {
            set Script [regsub {outputRootname} $Script \$outputRootname]
        }
        if [lsearch -exact $Script OutputDir]>=0 {
            set Script [regsub {OutputDir} $Script \$outputDir]
        }
        if [lsearch -exact $Script outputDir]>=0 {
            set Script [regsub {outputDir} $Script \$outputDir]
        }
       # set Script [APSMakeSafeQualifierString $Script]
        set customProgramOption [APSMakeSafeQualifierString $customProgramOption]
        append customProgramName " $customProgramOption"
        MakeNewExecLine $execScroll -name $execution -WaitMessage $WaitMessage \
          -mode load -setPV $PVname -setPVValue $PVvalue \
          -waitTime $WaitTime -sdds_append $Append -sdds_appendToPage $AppendToPage \
          -sdds_inputfile $Inputfile -sdds_outputSuffix $OutputfileSuffix  -sdds_steps $Steps \
          -sdds_interval $Interval -sdds_userOption "$UserOptions" \
          -runscript "$Script" -sdds_program $name \
          -query_columnName $columnName -query_dataType $dataType -query_units $units \
          -customProgramName "$customProgramName"
    }
    if $daily {
        set outputDir [APSGoToDailyDirectory]
    }
    UpdateOutputFiles
    SetMainStatus "Completely loaded from $directory/execution"
}
proc WaitForUserBox {number} {
    global exec
    if [winfo exist .waitforuser$number] {
        destroy .waitforuser$number 
    }
    APSDialogBox .waitforuser$number -name "WaitForUser Box$number" -width 80 \
      -cancelCommand "" -okCommand ""
    APSLabeledEntry .message -parent .waitforuser$number.userFrame -label "Message:" -width 80\
      -textVariable exec($number.WaitMessage) -contextHelp "Please enter message for waiting for your response."
    return
}

proc QueryUserBox {number} {
    global exec

    if [winfo exist .query$number] {
        destroy .query$number
    }
    APSDialogBox .query$number -name "QueryUser Box$number" -width 80 \
      -cancelCommand "" -okCommand ""
    
    APSLabeledEntry .query1 -parent .query$number.userFrame -label "Column Name:" -width 30 \
      -textVariable exec($number.query_columnName) \
      -contextHelp "the column name for query user whose value will be written to the output file"
    APSLabeledEntry .query2 -parent .query$number.userFrame -label "Units:" -width 30 \
      -textVariable exec($number.query_units) -contextHelp "the units of the query user column"
    APSRadioButtonFrame .query3 -parent .query$number.userFrame -label "Data Type:" \
      -buttonList {double float long short string} \
      -valueList {double float long short string} \
      -variable exec($number.query_dataType) \
      -contextHelp "The data type of the query user column" \
      -orientation horizontal
    return
}
proc WaitTimeBox {number args} {
    global exec
    if [winfo exist .set$number] {
        destroy .set$number
    }
    APSDialogBox .set$number -name "Wate Time Box$number" -width 20 \
      -cancelCommand "" -okCommand ""
    APSLabeledEntry .time -parent .set$number.userFrame -label "Wait time(s):" -width 15\
      -textVariable exec($number.waitTime) -contextHelp "Please enter the time in seconds you wish to wait in\
each experiment step."
}

proc TestPVBox {number args} {
    global Timeout
    if [winfo exist .set$number] {
        destroy .set$number
    }
    APSDialogBox .set$number -name "Timeout Box$number" -width 20 \
      -cancelCommand "" -okCommand ""
    APSLabeledEntry .time -parent .set$number.userFrame -label "Timeout(s):" -width 15\
      -textVariable Timeout -contextHelp "Wait at most the given time in seconds when pv tests failed."
}


proc CreateOutputFileLine {name line} {
    global outputFileWidget outputDir outputRootname exec execLines  
    
    set widget $outputFileWidget.file$line
    switch $name {
        RunStatistics {
            global Stats 
            set Stats(file) $outputDir/$outputRootname-$Stats(fileSuffix) 
            if ![winfo exist $widget] {
                APSLabeledEntry .file$line -parent $outputFileWidget \
                    -label "Statisics Output File:" -width 80 -packOption "-fill x -expand true" \
                    -textVariable Stats(file) -commandButton 1
            }
        }
        RunSDDSProgram {
            if [string length $exec($line.sdds_outputSuffix)] {
                set exec($line.sdd_outputfile) $outputDir/$outputRootname-$exec($line.sdds_outputSuffix)
                if ![winfo exist $widget] {
                    APSLabeledEntry .file$line -parent $outputFileWidget \
                      -label "RunSDDSProgram($exec($line.sdds_program)) Output File:" -width 80 \
                      -textVariable exec($line.sdds_outputfile) -commandButton 1  -packOption "-fill x -expand true"
                }
            }
        }
        ReadValue {
            global logFileSuffix logFile
            set logFile $outputDir/$outputRootname-$logFileSuffix
            if ![winfo exist $widget] {
                APSLabeledEntry .file$line -parent $outputFileWidget \
                  -label "ReadValue Output File:" -width 80  -packOption "-fill x -expand true" \
                  -textVariable logFile -commandButton 1
            }
        }
    }
}
proc UpdateOutputFile {name line} {
    global outputDir outputRootname logFile logFileSuffix exec Stats
    
    switch $name {
        RunSDDSProgram {
            if [string length $exec($line.sdds_outputSuffix)] {
                set exec($line.sdds_outputfile) $outputDir/$outputRootname-$exec($line.sdds_outputSuffix)
            } else {
                set exec($line.sdds_outputfile) ""
            }
        }
        RunStatistics {
            set Stats(file) $outputDir/$outputRootname-$Stats(fileSuffix)
        }
        ReadValue {
            set logFile $outputDir/$outputRootname-$logFileSuffix
        }
    }
}
proc ReadValueBox {Number} {
    global logFile logFileSuffix saveSuffix
    
    set saveSuffix $logFileSuffix
    if [winfo exist .read] {
        destroy .read
    }
    APSDialogBox .read -name "Read Value Box" -width 60 \
      -cancelCommand "set logFileSuffix \"$saveSuffix\""  -okCommand ""
    set w .read.userFrame 
    APSLabeledEntry .file -parent $w -label "Log File suffix:" \
      -textVariable logFileSuffix -width 62 \
      -contextHelp "Enter the suffix of the log file name."
    APSLabeledOutput .name -parent $w -label "Log file name:" -textVariable logFile -width 70
    bind $w.file.entry <Leave> "UpdateOutputFile ReadValue $Number"
}

proc SetValueBox {number} {
    global Monitor monitorLines setpointSavedList 
    
    if ![llength $setpointSavedList] {
        return -code error "ChangeSetpointBox: No setpoint PVs supplied in ProcessVariable panel."
    }
    if [winfo exist .selsetpoint] {
        destroy .selsetpoint
    }
    APSDialogBox .selsetpoint  -name "Select Setpoint Box" -width 50 \
      -cancelCommand "" -okCommand  "" 
    APSDialogBoxAddButton .all -parent .selsetpoint -text "Select All" \
      -command \
      {
          foreach index $setpointSavedList {
              set Monitor($index.setpointChecked) 1
          }    
      } \
      -contextHelp "Press to select all"
    APSDialogBoxAddButton .none -parent .selsetpoint -text "Select None" \
      -command \
      {
          foreach index $setpointSavedList {
              set Monitor($index.setpointChecked) 0
          }
      } \
      -contextHelp "Press to unselect all"
    set setpointScroll [APSScroll .conscrol -parent .selsetpoint.userFrame -name "setpoint selection list" ]
    set setpointScrollFrame .selsetpoint.userFrame.conscrol
    
    foreach i $setpointSavedList {
        if {[info exist Monitor($i.name)]} {
            set widget $setpointScroll.m$i
            ttk::frame $widget -borderwidth 1 
            pack $widget -fill x
            pack [checkbutton $widget.check -text $Monitor($i.name) -variable Monitor($i.setpointChecked) \
                    -width 30] -side left
            tkwait visibility $widget.check
        }
    }   
    APSScrollAdjust $setpointScrollFrame -numVisible 7
}
proc RunScriptBox {number} {
    global exec runscript
    set runscript [APSMakeSafeQualifierString $exec($number.runscript)]
    if [winfo exist .scr] {
        destroy .scr
    }
    APSDialogBox .scr -name "Script Input Box" -width 80 \
      -cancelCommand ""  -okCommand "" \
      -contextHelp "Press OK button to set the script arguments"
    APSLabeledEntry .script -parent .scr.userFrame -label "Run script: " \
      -textVariable exec($number.runscript) -width 80 -contextHelp "The syntax of script is \n<script name> \[-rootname outputRootname\] \[-index Step\] <other user options>\n." -packOption "-fill x -expand true"
    label .scr.userFrame.help -bg "\#DDDD22" -fg red -text "to pass the values to script, see \"ArgumentsParseHelp\" tab for help"
    pack .scr.userFrame.help -side left
}

proc RunSDDSProgramBox {number} {
    global exec  changeWidgetStateList 
    if [winfo exist .prog$number] {
        destroy .prog$number
    }
    APSDialogBox .prog$number -name "Run SDDS Program" -cancelCommand "" \
      -okCommand ""
    pack [ttk::frame .prog$number.userFrame.pm -relief flat] -fill x -expand true
    .prog$number.userFrame.pm configure -relief flat -borderwidth 0
    set w .prog$number.userFrame.pm
    APSLabeledEntry .entry -parent $w -label "Program: " -textVariable exec($number.sdds_program) \
      -contextHelp "Enter the program name to execute." \
      -width 30 -packOption "-side left"
    APSRadioButtonFrame .choice -parent $w -label "" -variable exec($number.sdds_program) \
      -buttonList {sddsmonitor sddslogger sddswmonitor sddsstatmon} \
      -valueList {sddsmonitor sddslogger  sddswmonitor sddsstatmon} \
      -orientation horizontal
    pack [ttk::frame .prog$number.userFrame.f1 -relief flat] -fill x -expand true
    APSLabeledEntry .input -parent .prog$number.userFrame.f1 -width 80 -packOption "-fill x -expand true" \
      -textVariable exec($number.sdds_inputfile) -contextHelp "Enter the input file here." \
      -label "Input file:        "
    pack [ttk::frame .prog$number.userFrame.f2 -relief flat] -fill x -expand true
    set w2 .prog$number.userFrame.f2
    APSLabeledEntry .out -parent $w2 -label "Output File suffix:" \
      -textVariable exec($number.sdds_outputSuffix) -width 80 -packOption "-side left"\
      -contextHelp "Enter a log file name or generate by clicking next button."
    bind $w2.out.entry <Leave> "UpdateOutputFile RunSDDSProgram $number"
  
    APSFrame .ops2 -parent .prog$number.userFrame -label "" -packOption "-side top -fill x"
    set w3 .prog$number.userFrame.ops2.frame
    $w3  configure -relief flat -borderwidth 0
  
    APSLabeledEntry .step -parent $w3 -width 5\
      -packOption "-side left" -label "steps: " -textVariable exec($number.sdds_steps) \
      -contextHelp "Enter the total number of running steps" 
    APSLabeledEntry .interval -parent $w3 -width 5\
      -packOption "-side left" -label "interval (s): " -textVariable exec($number.sdds_interval) \
      -contextHelp "Enter the interval between two data collecting steps" 
    APSCheckButtonFrame .app -parent $w3 -label ""\
      -packOption "-side left" -buttonList {append} -variableList exec($number.sdds_append) \
      -contextHelp "Generates a log file name with the user name and time stamp."
    
    APSCheckButtonFrame .apppage -parent $w3 -label ""\
      -packOption "-side left" -buttonList {appendToPage} -variableList exec($number.sdds_appendToPage) \
      -contextHelp "Generates a log file name with the user name and time stamp."
    
    APSFrame .ops1 -parent .prog$number.userFrame -label "" -packOption "-side top -fill x -expand true"
    set widget .prog$number.userFrame.ops1.frame
    $widget  configure -relief flat -borderwidth 0
    APSLabeledEntry .app -parent $widget -label "User custom options:"\
      -textVariable exec($number.sdds_userOption) -width 70 \
      -contextHelp "Enter the other options here." -packOption "-fill x -expand true"
}

proc RunCustomProgramBox {number} {
    global exec changeWidgetStateList 
    if [winfo exist .cust] {
        destroy .cust
    }
    APSDialogBox .cust -name "Run Program" -cancelCommand "" \
      -okCommand ""
    pack [ttk::frame .cust.userFrame.pm -relief flat] -fill x -expand true
    .cust.userFrame.pm configure -relief flat -borderwidth 0
    set w .cust.userFrame.pm
    APSFrameGrid .grid -parent $w -yList {x1 x2}
    set x1 $w.grid.x1
    set x2 $w.grid.x2
    APSLabeledEntry .entry -parent $x1 -label "Custom Program: " -textVariable exec($number.customProgramName) \
      -contextHelp "Enter the custom program name to execute." \
      -width 70 -packOption "-side left" -packOption "-fill x -expand true"
    label $x2.com -bg "\#DDDD22" -fg red -text "To pass the values of the variables, see \"ArgumentsPassHelp\" tab for help"
    pack $x2.com -side left 
}

proc RunStatisticsBox {Number} {
    global exec Stats changeWidgetStateList readbackSavedList

    if ![llength $readbackSavedList] {
        SetMainStatus "No readbacks for running statistics!"
        return
    }
    if [winfo exist .stats] {
        destroy .stats
    }
    APSDialogBox .stats -name "Run Statistics" -cancelCommand "" \
      -okCommand ""
    pack [ttk::frame .stats.userFrame.pm -relief flat] -fill x -expand true
    .stats.userFrame.pm configure -relief flat -borderwidth 0
    set w .stats.userFrame.pm
    APSLabeledEntry .suffix -parent $w -label "Statistics output file suffix: " -textVariable Stats(fileSuffix) \
      -contextHelp "Enter the suffix of the file name to log the statistics." \
      -width 60 -packOption "-side left"
    bind $w.suffix.entry <Leave> "UpdateOutputFile RunStatistics $Number"
    pack [ttk::frame .stats.userFrame.pm1 -relief flat] -fill x -expand true
    .stats.userFrame.pm configure -relief flat -borderwidth 0
    set w .stats.userFrame.pm1
    APSLabeledEntry .entry -parent $w -label "no. to average: " -textVariable Stats(average) \
      -contextHelp "Enter the number of average to run statistics." \
      -width 30 -packOption "-side left"
    APSLabeledEntry .entry1 -parent $w -label "pause time: " -textVariable Stats(pause) \
      -contextHelp "Enter pause time between two readings." \
      -width 30 -packOption "-side left"
    
    set buttonList {mean sigma stDev min max spread}
    set variableList [list Stats(mean) Stats(sigma) Stats(stDev) Stats(min) \
                        Stats(max) Stats(spread)]
    pack [ttk::frame .stats.userFrame.ck -relief flat] -fill x -expand true
    .stats.userFrame.ck configure -relief flat -borderwidth 0
    set w .stats.userFrame.ck
    APSCheckButtonFrame .group -parent $w -label "statistics:  " \
      -packOption "-side top" \
      -variableList $variableList -buttonList $buttonList -orientation horizontal \
      -allNone 1 -contextHelp "Choose the statistics"
}

proc SetPVBox {number} {
    global exec
    if [winfo exist .setPV$number] {
        destroy .setPV$number
    }
    APSDialogBox .setPV$number -name "Set PV Box" -width 30 \
      -cancelCommand "" -okCommand  ""
    APSLabeledEntry .pv -parent .setPV$number.userFrame -label "PV name:" -width 25\
      -textVariable exec($number.setPV) -contextHelp "Please enter the PV name to be in\
each experiment step."
    APSLabeledEntry .value -parent .setPV$number.userFrame -label "value:" -width 25\
      -textVariable exec($number.setPVValue) -contextHelp "Please enter the value to set the PV in\
each experiment step."
}

proc ChangeControlBox {number} {
    global Monitor monitorLines controlSavedList changeControlFile knobSavedList
    
    if {![llength $controlSavedList] && ![llength $knobSavedList]} {
        return -code error "ChangeControlBox: No control or knob PVs supplied in ProcessVariable panel."
    }
    if [winfo exist .sel] {
        destroy .sel
    }
    APSDialogBox .sel  -name "Select Control Box" -width 50 \
      -cancelCommand "" -okCommand  "" 
    APSDialogBoxAddButton .all -parent .sel -text "Select All" \
      -command \
      {
          foreach index $controlSavedList {
              set Monitor($index.controlChecked) 1
          }
          foreach index $knobSavedList {
              set Monitor($index.controlChecked) 1
          }
      } \
        -contextHelp "Press to select all"
    APSDialogBoxAddButton .none -parent .sel -text "Select None" \
      -command \
        {
            foreach index $controlSavedList {
                set Monitor($index.controlChecked) 0
            }
            foreach index $knobSavedList {
                set Monitor($index.controlChecked) 0
            }
      } \
      -contextHelp "Press to unselect all"
    APSLabeledEntry .file -parent .sel.userFrame -textVariable changeControlFile -label "Change Control file name:" \
      -contextHelp "the name of file where the values of the controls are set from." -width 70
    set controlScroll [APSScroll .conscrol -parent .sel.userFrame -name "control selection list" ]
    set controlScrollFrame .sel.userFrame.conscrol 
    foreach i $controlSavedList {
        if {[info exist Monitor($i.name)]} {
            set widget $controlScroll.m$i
            ttk::frame $widget -borderwidth 1 
            pack $widget -fill x
            pack [checkbutton $widget.check -text $Monitor($i.name) -variable Monitor($i.controlChecked) \
                    -width 30] -side left
            APSButton .view -parent $widget -text "Set/View Arguments" \
                -command "SetViewControl -index $i" 
            tkwait visibility $widget.check
        }
    }
    foreach i $knobSavedList {
        if {[info exist Monitor($i.name)]} {
            set widget $controlScroll.m$i
            ttk::frame $widget -borderwidth 1 
            pack $widget -fill x
            pack [checkbutton $widget.check -text $Monitor($i.name) -variable Monitor($i.controlChecked) \
                      -width 30] -side left
            APSButton .view -parent $widget -text "Set/View Arguments" \
                -command "SetViewControl -index $i" 
            tkwait visibility $widget.check
        }
    }
    APSScrollAdjust $controlScrollFrame -numVisible 7
}

proc SetViewControl {args} {
    global Monitor controlSavedList Index knobSavedList
    set index ""
    APSParseArguments {index} 
    APSDialogBox .view$index -name "$Monitor($index.name)" 
    set parent .view$index.userFrame 
    set comdList [list "SetControlMode -line $index -parent $parent -action increment" \
                      "SetControlMode -line $index -parent $parent -action specify_range" \
                      "SetControlMode -line $index -parent $parent -action set_fixed_value" \
                      "SetControlMode -line $index -parent $parent -action list_of_values" \
                      "SetControlMode -line $index -parent $parent -action set_from_file" ]
    if {$Monitor($index.type)=="Control"} {
        APSRadioButtonFrame .relative -parent $parent -orientation horizontal \
            -label "Change relative to current value?" -buttonList {Yes No} \
            -valueList {1 0} -variable Monitor($index.relative)
    }
    APSRadioButtonFrame .act -parent $parent  \
        -label "Action:" -buttonList {increment specify_range set_fixed_value list_of_values set_from_file} \
        -valueList {increment specify_range set_fixed_value list_of_values set_from_file} \
        -variable Monitor($index.controlAction) -commandList $comdList
    SetControlMode -line $index -parent $parent -action $Monitor($index.controlAction)
}

proc UpdateOutputFiles {} {
    global outputDir outputRootname logFile logFileSuffix exec execLines Stats
    
    for {set i 0} {$i<$execLines} {incr i} {
        switch $exec($i.name) {
            RunSDDSProgram {
                set exec($i.sdds_outputfile) $outputDir/$outputRootname-$exec($i.sdds_outputSuffix)
            }
            RunStatistics {
                set Stats(file) $outputDir/$outputRootname-$Stats(fileSuffix)
            }
            ReadValue {
                set logFile $outputDir/$outputRootname-$logFileSuffix
            }
        }
    }
}

#procedures for save configuration
proc SaveMonitorList {args} {
    global monitorLines Monitor
    
    set directory [pwd]
    set popup 1
    APSParseArguments {directory popup}
    
    set outputFile $directory/monitorLines
    if !$monitorLines {
        SetMainStatus "No monitor pvs to be saved."
        catch {exec rm $outputFile}
        return
    }
    #set outputFile $directory/monitorLines
    if {[file exists $outputFile] && $popup && ![APSYesNoPopUp "Overwritting existing $outputFile?"]} {
        return -code error "$outputFile already exist."
    }
    set saved 0
    SetMainStatus "Saving Monitor lines..."
    set fd [open $outputFile "w"]
    puts $fd "SDDS1"
    foreach name {ControlName ControlVariable ControlType Unit ValueString} {
        puts $fd "&column name=$name type=string &end"
    }
    puts $fd "&column name=MaximumValue type=double &end"
    puts $fd "&column name=MinimumValue type=double &end"
    puts $fd "&data mode=ascii no_row_counts=1 &end"
    set nameList {name var type unit val max min}
    for {set i 0} {$i<$monitorLines} {incr i} {
        set valueList ""
        if [info exist Monitor($i.name)] {
            foreach nm $nameList {
                if [info exist Monitor($i.$nm)] {
                    set $nm $Monitor($i.$nm)
                } else {
                    set $nm ""
                }
            }
            if [string match "par_*" $Monitor($i.type)] {
                global Monitor
                set val $Monitor($i.var)
            }
            if ![string length $min] {
                set min 0
            } 
            if ![string length $max] {
                set max 0
            }
            set unit [string trim $unit '"']
            puts $fd "\"$name\" \"$var\" \"$type\" \"$unit\" \"$val\" \"$max\" \"$min\""
            set saved 1
        }
    }
    puts $fd " "
    close $fd
    if !$saved {
        catch {exec rm $outputFile}
    }
    SetMainStatus "done"
}

proc SaveChangeControlList {args} {
    global changeControlFile monitorLines Monitor controlSavedList knobSavedList
    
    set directory ""
    set popup 1
    APSParseArguments {directory popup}
    set filename $directory/ChangeControl
    if {![llength $controlSavedList] && ![llength $knobSavedList]} {
        catch {exec rm $filename}
        return
    }
    if {[file exist $filename] && $popup && ![APSYesNoPopUp "Delete existing $filename?"]} {
        return -code error "$filename already exists."
    }
    set saved 0
    SetMainStatus "Save change control..."
    set fid [open $filename w] 
    puts $fid "SDDS1"
    if [string length $changeControlFile] {
        puts $fid "&parameter name=ControlFile type=string &end"
    }
    puts $fid "&column name=ControlName type=string &end"
    puts $fid "&column name=Active type=short &end"
    puts $fid "&column name=Action type=string &end"
    puts $fid "&column name=Increment type=double &end"
    puts $fid "&column name=InitialValue type=double &end"
    puts $fid "&column name=FinalValue type=double &end"
    puts $fid "&column name=FixedValue type=string &end"
    puts $fid "&column name=ListOfValues type=string &end"
    puts $fid "&column name=Relative type=long &end"
    puts $fid "&column name=IsKnob type=short &end"
    puts $fid "&data mode=ascii no_row_counts=1 &end"
    if [string length $changeControlFile] {
        puts $fid "$changeControlFile"
    }
    set nameList {name controlChecked controlAction controlIncrement controlInitial controlFinal \
                    controlFixedValues controlListOfValues relative}
    foreach type {control knob} isKnob {0 1} {
        set savedList [set ${type}SavedList]
        foreach index $savedList {
            if [info exists Monitor($index.name)] {
                foreach nm $nameList {
                    if [info exists Monitor($index.$nm)] {
                        set $nm $Monitor($index.$nm)
                    } else {
                        set $nm ""
                    }
                }
                if ![string length $controlChecked] {
                    set controlChecked 0
                } 
                if ![string length $controlIncrement] {
                    set controlIncrement 0
                } 
                if ![string length $controlInitial] {
                    set controlInitial 0
                }
                if ![string length $controlFinal] {
                    set controlFinal 0
                }
                set saved 1
                puts $fid "\"$name\" \"$controlChecked\" \"$controlAction\" \"$controlIncrement\" \"$controlInitial\" \"$controlFinal\" \"$controlFixedValues\" \"$controlListOfValues\" $relative $isKnob"
            }
        }
    }
    puts $fid " "
    close $fid
    if !$saved {
        catch {exec rm $filename}
    }
    SetMainStatus "done"
}

proc SaveInitFinalizeList {args} {
    global init initLines finalize finalizeLines
    set directory .
    set type init
    set popup 1
    APSParseArguments {directory type popup}
    switch $type {
        init {
            set filename $directory/initialize
        }
        finalize {
            set filename $directory/finalize
        }
    }
    set lines [set ${type}Lines]
    if !$lines {
        catch {exec rm $filename}
        return
    }
    set nameList ""
    set valueList ""
    set origList ""
    set typeList ""
    set readbackList ""
    set toleranceList ""
    set nmList {name type value orig readback tolerance}
    for {set i 0} {$i<$lines} {incr i} {
        if [set ${type}($i.valid)] {
            if [string length [set ${type}($i.name)]] {
                foreach nm $nmList {
                    lappend ${nm}List [set ${type}($i.$nm)]
                }
            }
        }
    }
    if ![llength $nameList] {
        catch {exec rm $filename}
        SetMainStatus "No $type lines for saving."
        return
    }
   
    if {[file exist $filename] && $popup && ![APSYesNoPopUp "Delete existing $filename?"]} {
        return -code error "$filename already exists."
    }
    
    SetMainStatus "Save $type lines..."
    set fid [open $filename w] 
    puts $fid "SDDS1"
    puts $fid "&column name=ControlName type=string &end"
    puts $fid "&column name=ReadbackName type=string &end"
    puts $fid "&column name=SetValue type=string &end"
    puts $fid "&column name=RestoreValue type=string &end"
    puts $fid "&column name=Type type=string &end"
    puts $fid "&column name=Tolerance type=double &end"
    puts $fid "&data mode=ascii no_row_counts=1 &end"
    foreach name $nameList readback $readbackList val $valueList orig $origList type $typeList tole $toleranceList {
        puts $fid "\"$name\" \"$readback\" \"$val\" \"$orig\" \"$type\" \"$tole\""
    }
    puts $fid " "
    close $fid
    SetMainStatus "done"
    return 1
}

proc SaveExecutionList {args} {
    global exec execLines outputDir outputRootname Description ExpSteps RunPostprocess daily autoIncr
    global logFileSuffix Stats home StartStep Timeout Interval
    set directory $home
    set popup 1
    APSParseArguments {directory popup}
    
    set filename $directory/execution
    if !$execLines {
        catch {exec rm $filename}
        return
    }
    
    if {[file exist $filename] && $popup && ![APSYesNoPopUp "Delete existing $filename?"]} {
        return -code error "$filename already exist."
    }
    set saved 0
    SetMainStatus "Save executions ...."
    set fd [open $filename w]
    puts $fd "SDDS1"
    puts $fd "&parameter name=ExecutionName type=string &end"
   # puts $fd "&parameter name=OutputDirectory type=string &end"
    puts $fd "&parameter name=OutputRootname type=string &end"
    puts $fd "&parameter name=RunPostprocess type=short &end"
    puts $fd "&parameter name=Description type=string &end"
    puts $fd "&parameter name=Steps type=long &end"
    puts $fd "&parameter name=StartStep type=long &end"
    puts $fd "&parameter name=Interval type=double &end"
    puts $fd "&parameter name=Timeout type=double &end"
    puts $fd "&parameter name=daily type=long &end"
    puts $fd "&parameter name=autoIncr type=long &end"
    puts $fd "&column name=TclVariableName type=string &end"
    puts $fd "&column name=TclVariableValue type=string &end"
    puts $fd "&data mode=ascii no_row_counts=1 &end"
    for {set i 0} {$i<$execLines} {incr i} {
        if $exec($i.valid) {
            set saved 1
            puts $fd "$exec($i.name)"
           # puts $fd "$outputDir"
            puts $fd "$outputRootname"
            puts $fd "$RunPostprocess"
            puts $fd "$Description"
            puts $fd "$ExpSteps"
            puts $fd "$StartStep"
	    puts $fd "$Interval"
	    puts $fd "$Timeout"
            puts $fd "$daily"
            puts $fd "$autoIncr"
            switch $exec($i.name) {
                ChangeControl -
                SetValue {
                }
                WaitForUser {
                    puts $fd "\"WaitMessage\" \"$exec($i.WaitMessage)\""
                }
                SetPV {
                    puts $fd "\"PVname\" \"$exec($i.setPV)\""
                    puts $fd "\"PVvalue\" \"$exec($i.setPVValue)\""
                }
                RunScript {
                    puts $fd "\"Script\" \"[regsub -all {"}  $exec($i.runscript) {\\"}]\""
                }
                RunCustomProgram {
                    puts $fd "\"customProgramName\" \"[regsub -all {"} $exec($i.customProgramName) {\\"}]\""
                }
                QueryUser {
                    puts $fd "\"columnName\" \"$exec($i.query_columnName)\""
                    puts $fd "\"dataType\" \"$exec($i.query_dataType)\""
                    puts $fd "\"units\" \"$exec($i.query_units)\""
                }
                RunSDDSProgram {
                    puts $fd "\"name\" \"$exec($i.sdds_program)\""
                    puts $fd "\"Inputfile\" \"$exec($i.sdds_inputfile)\""
                    puts $fd "\"outputfileSuffix\" \"$exec($i.sdds_outputSuffix)\""
                    puts $fd "\"Append\" \"$exec($i.sdds_append)\""
                    puts $fd "\"AppendToPage\" \"$exec($i.sdds_appendToPage)\""
                    puts $fd "\"Steps\" \"$exec($i.sdds_steps)\""
                    puts $fd "\"Interval\" \"$exec($i.sdds_interval)\""
                    puts $fd "\"UserOptions\" \"$exec($i.sdds_userOption)\""
                }
                ReadValue {
                    puts $fd "\"logFileSuffix\" \"$logFileSuffix\""
                }
                WaitTime {
                    puts $fd "\"WaitTime\" \"$exec($i.waitTime)\""
                }
                RunStatistics {
                    global Stats
                    puts $fd "\"average\" \"$Stats(average)\""
                    puts $fd "\"pause\" \"$Stats(pause)\""
                    puts $fd "\"mean\" \"$Stats(mean)\""
                    puts $fd "\"sigma\" \"$Stats(sigma)\""
                    puts $fd "\"min\" \"$Stats(min)\""
                    puts $fd "\"max\" \"$Stats(max)\""
                    puts $fd "\"spread\" \"$Stats(spread)\""
                    puts $fd "\"stDev\" \"$Stats(stDev)\""
                    puts $fd "\"fileSuffix\" \"$Stats(fileSuffix)\""
                }
            }
            puts $fd " "
        }
    }
    SetMainStatus "done"
    close $fd
    if !$saved {
        catch {exec rm $filename}
    }
}

proc SavePostprocessConfig {args} {
    global postProcess postProcessLines
    set directory ""
    set popup 1
    APSParseArguments {directory popup}
    
    
    if !$postProcessLines {
        catch {exec rm $directory/postprocess}
        return
    }
    if {[file exist $directory/postprocess] && $popup && \
          ![APSYesNoPopUp "$directory/postprocess already exists, overwrite it?"]} {
        return -code error "$directory/postprocess already exists."
    }
    SetMainStatus "Save post-process ..."
    set saved 0
    set fd [open $directory/postprocess "w"]
    puts $fd "SDDS1"
    puts $fd "&column name=PostprocessCommand type=string &end"
    puts $fd "&column name=PostprocessOption type=string &end"
    puts $fd "&data mode=ascii no_row_counts=1 &end"
    for {set i 0} {$i<$postProcessLines} {incr i} {
        if $postProcess($i.valid) {
            puts $fd "\"$postProcess($i.command)\" \"[regsub -all {"} $postProcess($i.option) \\"]\""
            set saved 1
        }
    }
    puts $fd ""
    close $fd
    if !$saved {
        catch {exec rm $directory/postprocess}
    }
    SetMainStatus "done"
}

proc LoadKnobInput {args} {
    global KnobScroll popupInterface KnobFile KnobLines popupInterface
    set directory ""
    APSParseArguments {directory} 
    set file $directory/knobFiles
    if ![file exist $file] {
        return
    }
    SetMainStatus "loading knob files..."
    set fileList [exec sdds2stream -col=KnobFile $file]
    set rows [llength $fileList]
    for {set i 0} {$i<$rows} {incr i} {
        set file [lindex $fileList $i]
        if [file exist $file] {
            if $popupInterface {
                MakeNewKnobLine $KnobScroll -file $file
            } else {
                set KnobFile($KnobLines.file) $file
                set KnobFile($KnobLines.valid) 1
                incr KnobLines
            }
        }
    } 
}

proc SaveKnobConfig {args} {
    global KnobFile KnobLines
    set directory ""
    set popup 1
    APSParseArguments {directory popup}
    
    if !$KnobLines {
        catch {exec rm $directory/knobFiles}
        return
    }
   
    set fileList ""
    for {set i 0} {$i<$KnobLines} {incr i} {
        if {$KnobFile($i.valid) && [string length $KnobFile($i.file)] && [file exist $KnobFile($i.file)]} {
            lappend fileList $KnobFile($i.file)
        }
    }
    if [llength $fileList] {
        SetMainStatus "Save knob files..."
        if {[file exist $directory/knobFiles] &&  $popup && \
                ![APSYesNoPopUp "$directory/knobFiles already exists, overwrite it?"]} {
            return -code error "$directory/knobFiles already exists"
        }
        if [catch {exec sddsmakedataset $directory/knobFiles -col=KnobFile,type=string \
                       -data=[join $fileList ,] } result] {
            return -code error $result
        }
        SetMainStatus "done"
    } else {
        catch {exec rm $directory/knobFiles}
    }
}

proc SaveConfigure {} {
    global saveDir readDir home Postcommand
    if [string length $saveDir] {
        set defaultDir $saveDir
    } elseif [string length $readDir] {
        set defaultDir $readDir
    } else {
        set defaultDir $home
    }
    set saveDir ""
    set saveDir [APSFileSelectDialog .choosedir -listDir $defaultDir -selectDir 1 -checkValidity 0]
    if ![string length $saveDir] {
        SetMainStatus "Save configuration was canceled."
        return
    }
    if [file isfile $saveDir] {
	set saveDir [file dirname $saveDir]
    }
    set readDir $saveDir
    if ![string length $saveDir] {return}
    if ![file isdirectory $saveDir] {
        SetMainStatus "A new directory $saveDir is being created"
        if [catch {exec mkdir $saveDir} result] {
            SetMainStatus "$result"
            return
        }
    }
    SetMainStatus "saving ....."
    if [catch {SaveMonitorList -directory $saveDir -popup 1} result] {
        SetMainStatus "$result. Configuration not saved."
        return
    }
    SaveKnobConfig -directory $saveDir -popup 0
    SaveChangeControlList -directory $saveDir -popup 0
    SaveInitFinalizeList -directory $saveDir -type init -popup 0
    SaveInitFinalizeList -directory $saveDir -type finalize -popup 0
    SaveExecutionList -directory $saveDir -popup 0
    SavePostprocessConfig -directory $saveDir -popup 0
}
#########################################################################
#execution procedures
proc RunScript {line} {
    global exec globalParseVariables monitorLineVariables Step StepString ExecLine
    eval global $globalParseVariables
    if [llength $monitorLineVariables] {
        eval global $monitorLineVariables
    } 
    if [catch {eval $exec($line.runscript)} result] {
        return -code error "Step $Step: error in running script $exec($line.runscript) : $result"
    }
}

proc RunCustomProgram {line} {
    global exec globalParseVariables monitorLineVariables Step StepString ExecLine
    eval global $globalParseVariables
    if [llength $monitorLineVariables] {
        eval global $monitorLineVariables
    }
 
    if [catch {eval $exec($line.customProgramName)} result] {
        return -code error "Step $Step: error in running custom program $exec($line.customProgramName) : $result"
    }
}

proc SetupPVLinks {args} {
    global Monitor pvSavedList readbackSavedList controlSavedList setpointSavedList monitorLines
    global pvList pvVarList readbackList controlPVList controlPVVarList pvMinList pvMaxList
    global ExpSteps changeControlFile errorInfo errorCode pvLinksSetup knobSavedList

    if $pvLinksSetup {
        if [llength $pvVarList] {
            pv unlink $pvVarList
        }
        if [llength $controlPVVarList] {
            pv unlink $controlPVVarList
        }
    }
    
    foreach name {pv readback controlPV setpoint} {
        set ${name}List ""
        set ${name}VarList ""
    }
    set pvMinList ""
    set pvMaxList ""
    SetMainStatus "Set up pv links..."
    if [llength $pvSavedList] {
        foreach nm $pvSavedList {
            lappend pvList $Monitor($nm.name)
            lappend pvVarList $Monitor($nm.var)
            lappend pvMinList $Monitor($nm.min)
            lappend pvMaxList $Monitor($nm.max)
            if [catch {exec cavget -list=$Monitor($nm.name) -pend=30 -floatFormat=%.15g -printErrors -pend=30} value] {
                return -code error "Unable to read value of $Monitor($nm.name): $value" 
            }
            if [catch {expr $value /2.0} result] {
                set Monitor($nm.columnType) string
            } else {
                set Monitor($nm.columnType) double
            }
        }
    }
    if [llength $readbackSavedList] {
        foreach nm $readbackSavedList {
            lappend readbackList $Monitor($nm.name) 
        }
    }
    foreach nm $knobSavedList {
        set Monitor($nm.columnType) double
    }
    foreach type {control knob} {
        set nmList [set ${type}SavedList]
        if [llength $nmList] {
            foreach nm $nmList {
                if  {$Monitor($nm.controlChecked)} {
                    global $Monitor($nm.var).set $Monitor($nm.var)
                    set orig 0
                    if {$type=="control"} {
                        lappend controlPVList $Monitor($nm.name)
                        lappend controlPVVarList $Monitor($nm.var).set
                        if [catch {exec cavget -list=$Monitor($nm.name) -pend=30 -floatFormat=%.15g -printErrors -pend=30} orig] {
                            return -code error "Can not read $Monitor($nm.name) value: $orig"
                        }
                    }
                    switch $Monitor($nm.controlAction) {
                        increment {
                            for {set i_step 0} {$i_step<$ExpSteps} {incr i_step} {
                                set Monitor($nm.step$i_step) [expr $orig + $i_step * $Monitor($nm.controlIncrement)]
                            }
                        }
                        specify_range {
                            set diff [expr 1.0*$Monitor($nm.controlFinal) - 1.0*$Monitor($nm.controlInitial)] 
                            if {$ExpSteps>1} {
                                set Monitor($nm.controlIncrement) [expr $diff/($ExpSteps-1.0)]
                            } else {
                                set Monitor($nm.controlIncrement) $diff 
                            } 
                            if $Monitor($nm.relative) {
                                set conInitial [expr $orig + $Monitor($nm.controlInitial)]
                            } else {
                                set conInitial $Monitor($nm.controlInitial)
                            }
                            for {set i_step 0} {$i_step<$ExpSteps} {incr i_step} {
                                set Monitor($nm.step$i_step) [expr $conInitial * 1.0 + $i_step * $Monitor($nm.controlIncrement)*1.0]
                            }
                        }
                        set_fixed_value {
                            if [regexp {,} $Monitor($nm.controlFixedValues)] {
                                set Monitor($nm.controlFixedValues) [split $Monitor($nm.controlFixedValues) ,]
                            } 
                            set size [expr [llength $Monitor($nm.controlFixedValues)] -1]
                            for {set i_step 0} {$i_step<$ExpSteps} {incr i_step} {
                                if {$i_step>=$size} { 
                                    set Monitor($nm.step$i_step) [lindex $Monitor($nm.controlFixedValues) $size]
                                } else {
                                    set Monitor($nm.step$i_step) [lindex $Monitor($nm.controlFixedValues) $i_step]
                                }
                            }
                        }
                        list_of_values {
                            if [regexp {,} $Monitor($nm.controlListOfValues)] {
                                set Monitor($nm.controlListOfValues) [split $Monitor($nm.controlListOfValues) ,]
                            }
                            set size [expr [llength $Monitor($nm.controlListOfValues)] -1]
                            for {set i_step 0} {$i_step<$ExpSteps} {incr i_step} {
                                if {$i_step>=$size} {
                                    set Monitor($nm.step$i_step) [lindex $Monitor($nm.controlListOfValues) $size]
                                } else {
                                    set Monitor($nm.step$i_step) [lindex $Monitor($nm.controlListOfValues) $i_step]
                                }
                            }
                        }
                        set_from_file {
                            set setvalue [exec sdds2stream $changeControlFile -column=$Monitor($nm.name) -page=1]
                            set size [expr [llength $setvalue] -1]
                            for {set i_step 0} {$i_step<$ExpSteps} {incr i_step} {
                                if {$i_step>$size} {
                                    set Monitor($nm.step$i_step) [lindex $setvalue $size]
                                } else {
                                    set Monitor($nm.step$i_step) [lindex $setvalue $i_step]
                                }
                            }
                        }
                        default {
                            return -code error "Invalid control action - $Monitor($nm.controlAction)."
                        }
                    }
                }
            }
        }
    }
    #set up link
    set errorInfo ""
    set errorCode ""
    for {set i 0} {$i<$monitorLines} {incr i} {
        if {$Monitor($i.PARtype)=="pv"} {
            lappend pvList $Monitor($i.name)
            lappend pvVarList  $Monitor($i.var)
        }
    }
    if [llength $pvVarList] {
        eval global $pvVarList
        if [pv linkw $pvVarList $pvList 30] {
            return -code error "Unable to link PVs: $errorInfo, $errorCode"
        }
        #monitor PVs
	pv getw $pvVarList
	update
        #foreach var $pvVarList {
            #if {[pv umon $var] !=0} {
            #    return -code error "pv umon error: $errorInfo, $errorCode"
            #}
        #}
    }
    if [llength $controlPVVarList] {
        eval global $controlPVVarList
        if [pv linkw $controlPVVarList $controlPVList 30] {
            return -code error "Unable to link control PVs: $errorCode"
        }
        if [pv getw $controlPVVarList 30] {
            return -code error "Unable to read control PVs: $errorCode"
        }
        update
        foreach var $controlPVVarList {
            global orig$var
            set orig$var [set $var]
            update
        }
    }
    SetupKnobPVs
    set pvLinksSetup 1
    SetMainStatus "done"
}

proc SetupKnobPVs {args} {
    global KnobFile KnobLines knobSavedList Monitor 

    if ![llength $knobSavedList] {
        return
    }
    set knobList ""
    foreach nm $knobSavedList {
        if {$Monitor($nm.controlChecked)} {
            lappend knobList $Monitor($nm.name)
        }
    }
    if ![llength $knobList] {
        return
    }
    set knobFiles ""
    for {set i 0} {$i<$KnobLines} {incr i} {
        if $KnobFile($i.valid) {
            if [file exist $KnobFile($i.file)] {
                lappend knobFiles $KnobFile($i.file)
            } else {
                SetMainStatus "SetupKnobPVs Warning: $KnobFile($i.file) does not exist!"
            }
        }
    }
    if ![llength $knobFiles] {
        return -code error "SetupKnobPVs No existing knob files found.!"
    }
    set tmpfile /tmp/[APSTmpString]
    foreach nm $knobSavedList {
        if {$Monitor($nm.controlChecked)} {
            set knobPV $Monitor($nm.name)
            if [catch {eval exec sddscombine $knobFiles -pipe=out \
                           | sddsprocess -pipe=in $tmpfile.$knobPV -nowarnings \
                           -match=par,ControlName=$knobPV} result] {
                return -code error "SetupKnobPVS1: $result"
            }
            set pages [exec sdds2stream -npages=bare $tmpfile.$knobPV]
            if !$pages {
                return -code error "SetupKnobPVs2: Knob $knobPV does not found in the knob files."
            }
            set Monitor($knobPV.knobFile) $tmpfile.$knobPV
            APSAddToTmpFileList -ID ED -fileList $tmpfile
        }
    }
}
proc SetValue {line} {
    global Monitor monitorLines setpointValueList
    
    if ![llength $setpointSavedList] {
        return
    }
    if [catch {ReadValue 0 -savefile 0} result] {
        return -code error $result
    }
    foreach nm $setpointSavedList {
        if $Monitor($nm.setpointChecked) {
            set var $Monitor($nm.setpointVar)
            global $var
            if [catch {APScavput -list=$Monitor($nm.name)=[set $var] -pend=30} result] {
                return -code error "SetValue: $result"
            }
        }
    }
}

proc ChangeControlPV {args} {
    set line ""
    APSParseArguments {line}
    global Step Monitor controlSavedList controlPVVarList errorCode
    global errorInfo errorCode
    if ![llength $controlPVVarList] {
        return
    }
    eval global $controlPVVarList
    foreach nm $controlSavedList {
        if {$Monitor($nm.controlChecked)} { 
            set $Monitor($nm.var).set $Monitor($nm.step$Step) 
            if $Monitor($nm.relative) {
                if {$Monitor($nm.controlAction)!="increment" && $Monitor($nm.controlAction)!="specify_range"} {
                    if [catch {exec cavget -list=$Monitor($nm.name -floatFormat=%.15g) -pend=30} curVal] {
                        return -code error "Can not read $Monitor($nm.name) value: $curVal"
                    }
                    if {$curVal=="?"} {
                        return -code error "Unable to read $Monitor($nm.name) value."
                    }
                    set $Monitor($nm.var).set [expr $curVal + $Monitor($nm.step$Step)]
                }
            }
        }
    }
    set errorInfo ""
    set errorCode ""
    if [pv putq $controlPVVarList 30] {
        return -code error "Unable to set control PVs: $errorInfo, $errorCode"
    }
}

proc ChangeKnobPV {args} {
    set line 0
    APSParseArguments {line}
    global Step Monitor knobSavedList

    if ![llength $knobSavedList] {
        return
    }
    set tmpRoot /tmp/[APSTmpString]
    SetMainStatus "change knob PV $knobSavedList"
    foreach nm $knobSavedList {
        if {$Monitor($nm.controlChecked)} { 
            global $Monitor($nm.var).set $Monitor($nm.var)
            set $Monitor($nm.var).set $Monitor($nm.step$Step) 
            set $Monitor($nm.var) $Monitor($nm.step$Step)
            set knobValue $Monitor($nm.step$Step)
            if {$knobValue<$Monitor($nm.min) || $knobValue>$Monitor($nm.max)} {
                SetMainStatus "Knob pv $Monitor($nm.name) setting value - $knobValue out of range, skipping it."
            } else {
                #    puts $Monitor($nm.name)
                set knobPV $Monitor($nm.name)
                #puts $Monitor($knobPV.knobFile)
                SetMainStatus "change knob PV $Monitor($nm.name)"
                if [catch {exec sddsprocess $Monitor($knobPV.knobFile) \
                               -pipe=out "-define=par,KnobValue,$knobValue Gain *" \
                               | sddsprocess -pipe "-redefine=col,Value,KnobValue Weight *" \
                               | sddscombine -pipe -merge \
                               | sddscasr -pipe -save \
                               | sddsprocess -pipe -scan=col,CurrentValue,ValueString,%lf \
                               | sddsprocess -pipe=in $tmpRoot.$Step.$Monitor($nm.name) \
                               "-redefine=col,Value1,CurrentValue Value +" \
                               -reprint=col,ValueString,%lf,Value1} result] {
                    return -code error $result
                }
                #    puts $knobValue
                # puts $tmpRoot.$Step.$Monitor($nm.name)
                
                SetMainStatus "Loading $tmpRoot.$Step.$Monitor($nm.name) ..."
               # if {0} {
                if [catch {exec sddscasr $tmpRoot.$Step.$Monitor($nm.name)  -restore} result] {
                    return -code error $result
                }
              #  }
                APSAddToTmpFileList -ID ED -fileList $tmpRoot.$Step.$Monitor($nm.name)
            }
        }
    }
}

proc ChangeControl {line} {
    global Step Monitor controlSavedList controlPVVarList errorCode knobSavedList
    global errorInfo errorCode
    
    if {![llength $controlSavedList] && ![llength $knobSavedList]} {
        return
    }
    if [catch {ChangeControlPV -line $line} result] {
        return -code error $result
    }
    if [catch {ChangeKnobPV -line $line} result] {
        return -code error $result
    }
}

proc RunStatistics {line} {
    global Step ExpSteps Stats Monitor errorCode pvVarList runStatsWithTest
    global readbackSavedList pvSavedList statsFileSetup appendToStatsFile statsTypeList StepString
    global pvLinksSetup
    global errorInfo errorCode
   
    
    if !$pvLinksSetup {
        SetupPVLinks
    }
    eval global $pvVarList
    if {$Stats(average)<2} {
        SetMainStatus "It is meaningless to do statistics with average number = $Stats(average)"
        return
    }
    if !$statsFileSetup {
        SetupStatisticsFile -append $appendToStatsFile 
        set statsFileSetup 1
    }
    set valueList ""
    lappend valueList "$Step"
    lappend valueList "[clock seconds]"
    set errorInfo ""
    set errorCode ""
    #if [pv getw $pvVarList 30] {
    #    return -code error "Unable to read pv values: $errorInfo, $errorCode"
    #}
    foreach nm $pvSavedList {
        set Monitor($nm.varValueList) ""
    }
    for {set i 0} {$i<$Stats(average)} {incr i} {
        if $runStatsWithTest {
            if [catch {TestPV $line} result] {
                return -code error "PV out of range: $result"
            }
        } else {
            if [pv getw $pvVarList 30] {
                return -code error "Unable to read pv values: $errorInfo, $errorCode"
            }
        }
        update
        foreach nm $readbackSavedList {
            lappend Monitor($nm.varValueList) [set $Monitor($nm.var)]
        }
        after [format %.0f [expr $Stats(pause)*1000]]
    }
    foreach nm $pvSavedList {
        lappend valueList [set $Monitor($nm.var)]
    }
    foreach nm $readbackSavedList {
        set statsList [APSComputeMoments -valueList $Monitor($nm.varValueList)]
        if [catch {array set temparray $statsList} result] {
            return -code error "RunStatistics: $result"
        }
        foreach type $statsTypeList {
            if $Stats($type) {
                lappend valueList $temparray($type)
            }
        }
    }
    puts $Stats(fileID) "[join $valueList]"
    flush $Stats(fileID)
}

proc SetupStatisticsFile {args} {
    global Stats Monitor readbackSavedList pvSavedList statsTypeList Description ExpSteps
    
    set append 0
    APSStrictParseArguments {append}
    
    if ![llength $readbackSavedList] {
        return -code error "No readback PVs for running statistics!"
    }
    if {[file exist $Stats(file)] && $append} {
        exec mv $Stats(file) $Stats(file).old
        APSAddToTmpFileList -ID ED -fileList $Stats(file).old
        set Stats(combine) 1
    }
    set Stats(fileID) [open $Stats(file) w+]
    puts $Stats(fileID) "SDDS1"
    puts $Stats(fileID) "&parameter name=TimeStamp type=string &end"
    puts $Stats(fileID) "&parameter name=Description type=string &end"
    puts $Stats(fileID) "&parameter name=TotalSteps type=long &end"
    puts $Stats(fileID) "&column name=Step type=long &end"
    puts $Stats(fileID) "&column name=Time type=double units=s &end"
    foreach nm $pvSavedList {
        if [string length $Monitor($nm.unit)] {
            puts $Stats(fileID) "&column name=$Monitor($nm.name) type=$Monitor($nm.columnType) units=$Monitor($nm.unit) &end"
        } else {
            puts $Stats(fileID) "&column name=$Monitor($nm.name) type=$Monitor($nm.columnType) &end"
        }
    }
    foreach nm $readbackSavedList {
        foreach type $statsTypeList {
            if $Stats($type) {
                puts $Stats(fileID) "&column name=$Monitor($nm.name)[string toupper $type 0 0] type=double &end"
            }
        }
    }
    puts $Stats(fileID) "&data mode=ascii no_row_counts=1 &end"
    puts $Stats(fileID) "[exec date]"
    puts $Stats(fileID) "[APSMakeSafeQualifierString [APSMakeSafeQualifierString $Description]]"
    puts $Stats(fileID) "$ExpSteps"
    flush $Stats(fileID)
}

proc SetupLogFile {args} {
    set filename ""
    set append 0
    APSStrictParseArguments {filename append}

    global Monitor exec monitorLines execLines logFileSetup logFileID logCombine knobSavedList ExpSteps
    global parSavedList eqSavedList pvSavedList QueryUserColumnList hasQueryUser scriptSavedList
    
    if $logFileSetup {
        return
    }
   
    if {[info exist logFileID] && [string length $logFileID]} {
        close $logFileID
    }
    if {[file exist $filename] && $append} {
        exec mv $filename $filename.old
        APSAddToTmpFileList -ID ED -fileList $filename.old
        set logCombine 1
    }
    set logFileID [open $filename w+]
    puts $logFileID "SDDS1"
    set parList ""
    foreach nm $parSavedList {
        puts $logFileID "&parameter name=$Monitor($nm.name) type=string description=$Monitor($nm.PARtype) &end"
        lappend parList $Monitor($nm.name)
    }
    lappend parList Description
    puts $logFileID "&parameter name=Description type=string &end"
    puts $logFileID "&parameter name=TimeStamp type=string &end"
    puts $logFileID "&parameter name=TotalSteps type=long &end"
    puts $logFileID "&column name=Step type=long &end"
    puts $logFileID "&column name=Time type=double units=s &end"
    puts $logFileID "&column name=ExecutionLine type=long &end"
    foreach pv $pvSavedList {
        if [string length $Monitor($pv.unit)] {
            puts $logFileID "&column name=$Monitor($pv.var) type=$Monitor($pv.columnType) description=$Monitor($pv.name) units=$Monitor($pv.unit) &end"
        } else {
            puts $logFileID "&column name=$Monitor($pv.var) type=$Monitor($pv.columnType) description=$Monitor($pv.name) &end"
        }
    }
    
    foreach nm $scriptSavedList {
        if [string length $Monitor($nm.unit)] {
            puts $logFileID "&column name=$Monitor($nm.var) type=double units=$Monitor($nm.unit) &end"
        } else {
            puts $logFileID "&column name=$Monitor($nm.var) type=double &end"
        }
    }
    foreach eq $eqSavedList {
        if [string length $Monitor($eq.unit)] {
            puts $logFileID "&column name=$Monitor($eq.var) type=double description=$Monitor($eq.name) units=$Monitor($eq.unit) &end"
        } else {
            puts $logFileID "&column name=$Monitor($eq.var) type=double description=$Monitor($eq.name) &end"
        }
    }
    foreach knob $knobSavedList {
        if {$Monitor($knob.controlChecked)} {
            if [string length $Monitor($knob.unit)] {
                puts $logFileID "&column name=$Monitor($knob.var) type=double description=$Monitor($knob.name) units=$Monitor($knob.unit) &end"
            } else {
                puts $logFileID "&column name=$Monitor($knob.var) type=double description=$Monitor($knob.name) &end"
            }
        }
    }
    set QueryUserColumnList ""
    for {set i 0} {$i<$execLines} {incr i} {
        if {$exec($i.valid) && $exec($i.name)=="QueryUser"} {
            lappend QueryUserColumnList $exec($i.query_columnName)
            if [string length $exec($i.query_units)] {
                puts $logFileID "&column name=$exec($i.query_columnName) type=$exec($i.query_dataType) units=$exec($i.query_units) &end"
            } else {
                puts $logFileID "&column name=$exec($i.query_columnName) type=$exec($i.query_dataType) &end"
            }
        }
    }
    puts $logFileID "&data mode=ascii no_row_counts=1 &end"
    foreach par $parList {
        global $par
        puts $logFileID [set $par]
    }
    puts $logFileID "[exec date]"
    puts $logFileID "$ExpSteps"
    flush $logFileID
    set logFileSetup 1
}

proc ReadValue {line args} {
    global pvLinksSetup monitorLines Monitor eqSavedList readValueFileSetup appendToLogFile
    global logFile logFileSetup logFileID pvVarList QueryUserColumnList pvList scriptSavedList
    global globalParseVariables knobSavedList
    eval global $globalParseVariables

    set savefile 1
    APSParseArguments {savefile}
    
    if !$pvLinksSetup {
	catch {pv unlink $pvVarList}
	catch {pv unlink $controlPVVarList}
        SetupPVLinks
    }
    if [llength $pvVarList] {
        eval global $pvVarList
        if [pv getw $pvVarList 30] {
            return -code error "ReadValue: Can not read pv values: $errorCode"
        }
    }
    foreach nm $eqSavedList {
        global $Monitor($nm.var)
        if [catch {set $Monitor($nm.var) [format %.4g [expr $Monitor($nm.name)]]} result] {
            return -code error "ReadValue: Problem in expression \"$Monitor($nm.name)\": $result"
        }
    }
    foreach nm $scriptSavedList {
        global $Monitor($nm.var)
        if [catch {eval $Monitor($nm.name)} result] {
            return -code error "ReadValue: Error in executing $Monitor($nm.name): $result"
        }
        set $Monitor($nm.var) $result
    }
    if !$savefile {
        return
    }
    if !$logFileSetup {
        SetupLogFile -filename $logFile -append $appendToLogFile
    }
    SetMainStatus "Saving to log file ..."
    set valueList ""
    lappend valueList $Step
    lappend valueList [clock seconds]
    lappend valueList $line
    if [llength $pvVarList] {
        foreach var $pvVarList {
            lappend valueList "[set $var]"
        }
    }
    foreach nm $eqSavedList {
        global $Monitor($nm.var)
        if [catch {set $Monitor($nm.var) [format %.4g [expr $Monitor($nm.name)]]} result] {
            return -code error "ReadValue: problem in expression \"$Monitor($nm.name)\": $result"
        }
        lappend valueList [set $Monitor($nm.var)]
    }
    foreach nm $scriptSavedList {
        lappend valueList [set $Monitor($nm.var)]
    }
    foreach nm $knobSavedList {
        if $Monitor($nm.controlChecked) {
            global $Monitor($nm.var)
            lappend valueList [set $Monitor($nm.var)]
        }
    }
    foreach col $QueryUserColumnList {
        global $col
        lappend valueList [set ${col}($Step)]
    }
    
    puts $logFileID "[join $valueList]"
    flush $logFileID
}

proc TestPV {line args} {
    global pvVarList pvList errorCode Timeout ABORT pvMinList pvMaxList PAUSE
    global PauseLine PauseStep Step
    eval global $pvVarList
    global errorInfo errorCode

    set stoptime [expr [clock seconds]+$Timeout]
    while {[clock seconds]<$stoptime} {
        set outofRange 0
        if {$ABORT || $PAUSE} {
            return 0
        }
        if [pv getw $pvVarList 30] {
            return -code error "Unable to read pv values: $errorInfo, $errorCode"
        }
        update
        foreach pv $pvList var $pvVarList min $pvMinList max $pvMaxList {
            set val [set $var]
            if {$val<$min || $val>$max} {
                SetMainStatus "out of range,  $pv,  $val"
                set outofRange 1
            }
        }
        if $outofRange {
            SetMainStatus "Test failed, waiting 1 seconds ..."
            after 1000
        } else {
          #  SetMainStatus "Test passes."
            break
        }
    }
    if $outofRange {
        return -code error "ReadValue: Testing failed after $Timeout seconds"
    }
}

proc RunSDDSProgram {line} {
    global Step exec execLines popupInterface Description  outputDir outputRootname Description ExpSteps Timeout Interval 
    global monitorLineVariables StepString
    if [llength $monitorLineVariables] {
        eval global $monitorLineVariables
    }
  
    if {![info exists exec($line.sdds_options)] || ![string length $exec($line.sdds_options)]} {
        set options ""
        if {![string length $exec($line.sdds_program)] || ![string length $exec($line.sdds_inputfile)]} {
            return -code error "RunSDDSProgram: program or input file name is not provided."
        }
      #  lappend options $exec($line.sdds_inputfile)
        if $exec($line.sdds_appendToPage) {
            set append 1
            set exec($line.sdds_append) 1
        }
        if $exec($line.sdds_append) {
            if $exec($line.sdds_appendToPage) {
                lappend options -append=toPage
            } else {
                lappend options -append
            }
        }
        if {[string compare $exec($line.sdds_program) sddslogger]==0} {
            lappend options -sampleInterval=$exec($line.sdds_interval)
        } else {
            lappend options -interval=$exec($line.sdds_interval)
        }
        if {[string compare $exec($line.sdds_program) sddsstatmon]==0} {
            lappend options -samplesPerStatistic=$exec($line.sdds_steps)
            lappend options -steps=1
        } else {
            lappend options -steps=$exec($line.sdds_steps)
        }
        # lappend options [eval set $exec($line.sdds_outputfile)]
        # set exec($line.sdds_options) [concat $options [eval set $exec($line.sdds_userOption)]
        set exec($line.sdds_options) $options
    }
    if [regexp {\$} $exec($line.sdds_inputfile)] {
        eval set infile $exec($line.sdds_inputfile)
    } else {
        set infile $exec($line.sdds_inputfile)
    }
    if [regexp {\$} $exec($line.sdds_outputfile)] {
        eval set outfile $exec($line.sdds_outputfile)
    } else {
        set outfile $exec($line.sdds_outputfile)
    }
    if [regexp {\$} $exec($line.sdds_userOption)] {
        eval set useropt $exec($line.sdds_userOption)
    } else {
        set useropt $exec($line.sdds_userOption)
    }
    global wait
    set wait 0
    if $popupInterface {
        APSExecLog .runProg -width 80 -unixCommand "$exec($line.sdds_program) $exec($line.sdds_options) $infile $outfile $useropt -verbose" \
          -callback "set wait Done" -abortCallback "set wait Aborted" \
          -cancelCallback "set wait Cancelled"
    } else {
        eval exec $exec($line.sdds_program) $exec($line.sdds_options) -verbose
        set wait Done
    }
    if {$wait==0} {
        tkwait variable wait
    }
    switch $wait {
        Done {
            if [file exist $exec($line.sdds_outputfile)] {
                if [catch {exec sddsprocess $exec($line.sdds_outputfile) -nowarnings \
                             "-reprint=par,Description,[APSMakeSafeQualifierString $Description]" } result] {
                    return -code error $result
                }
            }
        }
        default {
            return -code error "RunSDDSProgram: $exec($line.sdds_program) is aborted or cancelled."
        }
    }
}

proc QueryUser {line} {
    global exec Step popupInterface
    set queryColumn $exec($line.query_columnName)
    global $queryColumn

    set prevStep [expr $Step -1]
    if $popupInterface {
        APSDialogBox .queryuser$Step \
          -name "Enter the value for $Step row of $queryColumn" \
          -width 60 -cancelCommand "set ${queryColumn}($Step) \"\""
        if {$Step>0 && [info exist ${queryColumn}($prevStep)]} {
            APSLabel .prev -parent .queryuser$Step.userFrame \
              -text "The previous value is [set ${queryColumn}($prevStep)]"
        }
        APSLabeledEntry .val -parent .queryuser$Step.userFrame -width 60 \
          -textVariable ${queryColumn}($Step) -label "Enter a $exec($line.query_dataType) value for $queryColumn at step $Step:"
        bind .queryuser$Step.userFrame.val.entry <Return> "destroy .queryuser$Step"
        tkwait window .queryuser$Step
    } else {
        if {$Step>0 && [info exist ${queryColumn}($prevStep)]} {
            puts "The previous value of $queryColumn is [set ${queryColumn}($prevStep)]"
        }
        puts "Please enter a $exec($line.query_dataType) value for $queryColumn at step $Step."
        set ${queryColumn}($Step) [gets stdin]
    }
    if {$exec($line.query_dataType) !="string"} {
        set val [set ${queryColumn}($Step)]
        if [catch {expr $val*1.0} result] {
            bell
            SetMainStatus "Invalid value entered.!"
            set ${queryColumn}($Step) NAN
        }
    }
}

proc SetPV {line} {
    global Step exec
    if [catch {APScavput -list=$exec($line.setPV)=$exec($line.setPVValue) -pend=30} result] {
        return -code error "SetPV: $result"
    }
}

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

proc WaitForUser {line} {
    global Step exec popupInterface ABORT
    if $popupInterface {
        if ![APSYesNoPopUp "$exec($line.WaitMessage)"] {
            foreach but {init run initandrun} {
                APSEnableButton .userFrame.ops.$but.button
            }
            changeWidgetState 1
	    set ABORT 1
        }
    } else {
        puts "$exec($line.WaitMessage)? press 'y' to continue or 'n' to abort"
        set answer [gets stdin]
        set answer [string tolower $answer]
        if {$answer=="n" || $anwer=="no"} {
            set ABORT 1
        }
    }
}

proc WaitTime {line} {
    global Step exec
    if [string length $exec($line.waitTime)] {
        if {$exec($line.waitTime)>1} {
            APSWaitWithUpdate -waitSeconds [format %.0f $exec($line.waitTime)] -updateInterval 1 -abortVariable ABORT
        } else {
            set time [format %.0f [expr 1000 * $exec($line.waitTime)]]
            after $time
        }
    } else {
        SetMainStatus "Warning, the pause time is not given."
    }
}

proc CheckOutputFileExistence {args} {
    global execLines exec outputFileList outputDir outputRootname logFile logFileSuffix
    global appendToLogFile appendToStatsFile popupInterface Stats

    
    for {set i 0} {$i<$execLines} {incr i} {
        set outputfile ""
        set type ""
        if $exec($i.valid) {
            switch $exec($i.name) {
                RunStatistics {
                    if {![string length $outputDir] || ![string length $outputRootname]} {
                        return -code error "output directory or root name is not provided!"
                    }
                    if ![string length $Stats(fileSuffix)] {
                        return -code error "The suffix of file name for run statistics is not given."
                    }
                    set Stats(file) $outputDir/${outputRootname}-$Stats(fileSuffix)
                    if [file exist $Stats(file)] {
                        set outputfile $Stats(file)
                        set type Statistics
                    }
                }
                RunSDDSProgram {
                    if ![string length $exec(${i}.sdds_outputSuffix)] {
                        set exec($i.sdds_outputfile) ""
                    } else {
                        if {![string length $outputDir] || ![string length $outputRootname]} {
                            return -code error "output directory or root name is not provided!"
                        }
                        set exec($i.sdds_outputfile) $outputDir/${outputRootname}-$exec($i.sdds_outputSuffix)
                        if [file exist $exec($i.sdds_outputfile)] {
                            set outputfile $exec($i.sdds_outputfile)
                            set type "RunSDDSProgram $exec($i.sdds_program)"
                        }
                    }
                }
                ReadValue {
                    if {![string length $outputDir] || ![string length $outputRootname]} {
                        return -code error "output directory or root name is not provided!"
                    }
                    if ![string length $logFileSuffix] {
                        return -code error "logFileSuffix for ReadValue is not provided!"
                    }
                    set logFile $outputDir/${outputRootname}-$logFileSuffix
                    if [file exists $logFile] {
                        set outputfile $logFile
                        set type ReadValue
                    }
                }
            }
            if [string length $outputfile] {
                lappend outputFileList $outputfile
                set answer OverWrite
                if $popupInterface {
                    set answer [APSMultipleChoice [APSUniqueName .] \
                                  -question "$type output file $outputfile already exists,append, overwrite or abort?" -returnList {OverWrite Append Abort} \
                                  -labelList {Overwrite Append Abort} ]
                }
                switch $answer {
                    OverWrite {
                        file delete -force $outputfile
                        set appendToLogFile 0
                        set appendToStatsFile 0
                    }
                    Append {
                        #do nothing
                        if [string match "RunSDDSProgram*" $type] {
                            set exec($i.sdds_append) 1
                        }
                        if [string match "ReadValue*" $type] {
                            set appendToLogFile 1
                        }
                        if [string match "Statistics*" $type] {
                            set appendToStatsFile 1
                        }
                    }
                    Abort {
                        return -code error "$type output file $outputfile already exist."
                    }
                }
            }
            
        }
    }
}

proc RunPostprocess {args} {
    global postProcess postProcessLines globalParseVariables Step ExpSteps StepString ExecLine logFileSuffix
    eval global $globalParseVariables
    if !$postProcessLines {
        return
    }
    
    SetMainStatus "Post-processing ..."
    for {set i 0} {$i<$postProcessLines} {incr i} {
        if $postProcess($i.valid) {
            if [catch {TestPostprocess $i} result] {
                return -code error $result
            }
        }
    }
    SetMainStatus "done."
}

proc InitFinalize {args} {
    global init initLines finalize finalizeLines  outputDir outputRootname Description Step
    set type init
    APSParseArguments {type}
    
    set lines [set ${type}Lines]
    if !$lines {
        return
    }
    SetMainStatus "running $type ...."
    for {set i 0} {$i<$lines} {incr i} {
        if [set ${type}($i.valid)] {
            switch [set ${type}($i.type)] {
                RunScript {
                    set script [set ${type}($i.name)]
                    if [string length $script] {
                        if [catch {eval $script} result] {
                            return -code error "$result"
                        }
                    }
                }
                SetPV {
                    set pv [set ${type}($i.name)]
                    set readback [set ${type}($i.readback)]
                    set value [set ${type}($i.value)]
                    set tol [set ${type}($i.tolerance)]
                    set lower [expr $value-$tol]
                    set upper [expr $value+$tol]
                    if [catch {exec cavput -list=$pv=$value -pend=30} result] {
                        return -code error $result
                    }
                    if [string length $readback] {
                        catch {exec cawait -waitfor=$readback,lowerLimit=$low,upperLimit=$upper -timeLimit=100}
                    }
                }
            }
        }
    }
    SetMainStatus "done"
}

proc Resume {args} {
    global PAUSE RESUME
    
    if !$PAUSE {
        SetMainStatus "Pause experiment is not effective yet (the experiment may be in the middle of some execution), can not resume."
        return
    }
    foreach butt {pause run initandrun resume} {
        APSEnableButton .userFrame.ops.$butt.button
    }
    set PAUSE 0
    set RESUME 1
    RUN
}

proc Terminate {} {
    global initLines init finalizeLines final execLines exec initialized Step RunPostprocess
    
    changeWidgetState 1
    APSEnableButton .userFrame.ops.init.button
    APSEnableButton .userFrame.ops.initandrun.button
    APSDisableButton .userFrame.ops.pause.button 
    APSDisableButton .userFrame.ops.term.button
    
    #run postprocess
    if {$RunPostprocess} {
        if [catch {RunPostprocess} result] {
            SetMainStatus "Error in running post-process: $result"
            return
        }
    }
    if {!$initLines && !$finalizeLines} {
	return
    }
    SetMainStatus "Terminating ..."
    if $finalizeLines {
        if [catch {InitFinalize -type finalize} result] {
            return -code error $result
        }
    } else {
        for {set i [expr $initLines -1]} {$i>=0} {incr i -1} {
            if $init($i.valid) {
                switch $init($i.type) {
                    RunScript {
                        if [string length $init($i.name)] {
                            if [catch {eval $init($i.name)} result] {
                                return -code error "$result"
                            }
                        }
                    }
                    SetPV {
                        if [catch {APScavput -list=$init($i.name)=$init($i.orig) -pend=30} result] {
                            return -code error $result
                        }
                        set lower [expr $init($i.orig) - $init($i.tolerance)]
                        set upper [expr $init($i.orig) + $init($i.tolerance)]
                        catch {exec cawait -waitfor=$init($i.readback),lowerLimit=$low,upperLimit=$upper}
                    }
                }
            }
        }
    }
   
    return done
}

set Step 0
set RESUME 0
proc RUN {args} {
    global exec execLines Step ExpSteps PauseStep ABORT PAUSE restoreControl popupInterface STARTED outputRootname
    global outputDir PauseLine RunPostprocess outputFileChecked initialized StepString ExecLine autoIncr Interval RESUME
    global pvVarList pvLinksSetup daily
    
    if !$pvLinksSetup {
	catch {pv unlink $pvVarList}
	catch {pv unlink $controlPVVarList}
        SetupPVLinks
    }
    if [llength $pvVarList] {
        eval global $pvVarList
        if [pv getw $pvVarList 30] {
            return -code error "ReadValue: Can not read pv values: $errorCode"
        }
    }
    set initialize 0 
    APSStrictParseArguments {initialize}
    
    if {$autoIncr && !$RESUME}  {
        IncrRootname
    }
    set hasQueryUser 0
    set readvalue 0
    for {set i 0} {$i<$execLines} {incr i} {
        if {$exec($i.valid) && $exec($i.name)=="QueryUser"} {
            set hasQueryUser 1
            break
        }
    }
    if $popupInterface {
        foreach but {init initandrun run} {
            APSDisableButton .userFrame.ops.$but.button
        }
        foreach but {pause term} {
            APSEnableButton .userFrame.ops.$but.button 
        }
        changeWidgetState 0
    }
    SetMainStatus "Starting experiment..."
    if {$popupInterface && !$STARTED} {
        set restoreControl [APSYesNoPopUp "Do you want to restore control values after experiment?"]
    } 
    if {!$STARTED && !$outputFileChecked} {
        if [catch {CheckOutputFileExistence} result] {
            SetMainStatus "$result"
            if $popupInterface {
                foreach but {init run initandrun} {
                    APSEnableButton .userFrame.ops.$but.button
                }
                foreach but {pause resume term} {
                    APSDisableButton .userFrame.ops.$but.button
                }
            }
            changeWidgetState 1
            return
        }
      #  set outputFileChecked 1
    }
    if {[string length $outputDir] && ![file exist $outputDir]} {
        exec mkdir $outputDir
    }
    if $popupInterface {
        .userFrame.main.frame.tn select 0
	ReadValue 0 -savefile 0
    }
   
    set ABORT 0
    set PAUSE 0
    set RESUME 0
    set initialized 0
    if $initialize {
        #run initializations
        if [catch {InitFinalize -type init} result] {
            SetMainStatus "Error in initializing: $result"
            return
        }
        set initialized 1
    }
    #setup pv links
    if !$STARTED {
        if [catch {SetupPVLinks} result] {
            SetMainStatus "Error in setting up pv links: $result"
            return
        }
    }
    set STARTED 1
    #run execution lines
   
    for {set Step $PauseStep} {$Step<$ExpSteps} {incr Step} {
	if $daily {
	    set outputDir [APSGoToDailyDirectory]
	    UpdateOutputFiles
	}
	if [llength $pvVarList] {
	    if [pv getw $pvVarList 30] {
		return -code error "ReadValue: Can not read pv values: $errorCode"
	    }
	}
	if $popupInterface {
	    ReadValue 0 -savefile 0
	}
        set StepString [format %06d $Step]
        set start [clock seconds]
        for {set line $PauseLine} {$line<$execLines} {incr line} {
	    if [llength $pvVarList] {
		if [pv getw $pvVarList 30] {
		    return -code error "ReadValue: Can not read pv values: $errorCode"
		}
	    }
	    set ExecLine $line
            if $exec($line.valid) {
                if $ABORT {
                    SetMainStatus "Experiment was manually stopped."
                    closeExperiment
                    return
                }
                if $PAUSE {
                    SetMainStatus "Experiment was paused."
                    set PauseStep $Step
                    set PauseLine [expr $line -1]
                    if $PauseLine<0 {
                        set PauseLine 0
                    }
                    return
                }
                if {$exec($line.name)=="ReadValue" && $hasQueryUser} {
                    set readvalue 1
                    continue
                }
                SetMainStatus "Step $Step: line $line: run $exec($line.name)..."
                if [catch {eval $exec($line.name) $line} result] {
                    SetMainStatus "Error in running $exec($line.name): $result"
                    closeExperiment
                    return
                }
            }
        }
        if {$readvalue} {
            if [catch {ReadValue 0} result] {
                SetMainStatus "Error in running ReadValue: $result"
                closeExperiment
                return
            }
        }
        set PauseLine 0
        SetMainStatus "Step $Step done, taking [expr [clock seconds]-$start] seconds."
        if {$Interval>1} {
            APSWaitWithUpdate -waitSeconds [format %.0f $Interval] -updateInterval 1 -abortVariable ABORT
        } else {
            set time [format %.0f [expr $Interval * 1000]]
            after $time
        }
    }
    #run finalizations
     SetMainStatus "run finalize"
    if [catch {InitFinalize -type finalize} result] {
        SetMainStatus "Error in finalizing: $result"
        closeExperiment
        return
    }
    if $popupInterface {
        changeWidgetState 1
        foreach but {init run initandrun} {
            APSEnableButton .userFrame.ops.$but.button
        }
        foreach but {pause term resume} {
            APSDisableButton .userFrame.ops.$but.button
        }
    }
    SetMainStatus "close experiment..."
    closeExperiment
    SetMainStatus "run post-process ..."
    if {$RunPostprocess} {
        if [catch {RunPostprocess} result] {
            SetMainStatus "Error in running post-process: $result"
            return
        }
    }
    #if $popupInterface {
       # APSAlertBox .alert -errorMessage "Experiment is done!" -type done -name "Experiment Done"
    #}
    bell
    SetMainStatus "Experiment Done."
}

set KnobLines 0
proc CreateKnobWidget {parent} {
    global KnobScroll KnobScrollFrame 
    global KnobLines changeWidgetStateList

    APSFrame .knob -parent $parent -name "Knob Files" -packOption "-side top -fill x"
    set w $parent.knob.frame
    $w configure -relief flat -borderwidth 0
    APSFrame .desc -parent $w -packOption "-side top -fill x"
    set w1 $w.desc.frame
    $w1 configure -relief flat -borderwidth 0
    APSLabel .txt -parent $w1 -text "Press Add Knob File button to add entry for entering knob file" 
    APSFrame .scroll -parent $w -packOption "-side top -fill x"
    set w2 $w.scroll.frame
    $w2 configure -relief flat -borderwidth 0
    set KnobScroll [APSScroll .pvScroll -parent $w2]
    set KnobScrollFrame $w2.pvScroll

    APSFrame .but -parent $w -packOption "-side top -fill x"
    set w3 $w.but.frame
    $w3 configure -relief flat -borderwidth 0
    APSButton .add -parent $w3 -text "Add Knob File ..." \
        -command "MakeNewKnobLine $KnobScroll" \
        -contextHelp "Press to add another konb entry line." -width ""
    APSButton .clear -parent $w3 -text "Clear All" \
        -command "ClearKnobSettings $KnobScroll" \
        -contextHelp "Press to clear all arguments lines." -width ""
    lappend changeWidgetStateList $w3.add.button
    lappend chagneWidgetStateList $w3.clear.button
}

proc CreateNewKnobLine {widget0 line} {
    global KnobFile  KnobScrollFrame  changeWidgetStateList brief
    if [winfo exist $widget0.m$line] {
        destroy $widget0.m$line
    }
    if [winfo exist $widget0.opt$line] {
        destroy $widget0.opt$line
    }
    if !$KnobFile($line.valid) {
        return
    }
    APSFrame .m$line -parent $widget0
    set w $widget0.m$line.frame
    $w configure -relief flat -borderwidth 0
    APSLabeledEntry .com -parent $w -label "Knob File:" -textVariable KnobFile($line.file) \
        -width 80 -packOption "-side left" -commandButton 1
    APSButton .delete -parent $w -text "DELETE" -command "DeleteKnobLine $widget0 $line" -width ""
    APSButton .view -parent $w -text "VIEW" -command "exec sddsedit $KnobFile($line.file) &" -width ""
    .userFrame.main.frame.tn select 1
    tkwait visibility $w.com
    APSScrollAdjust $KnobScrollFrame -numVisible 9
}

proc ClearKnobSettings {widget0 args} {
    set popup 1
    APSParseArguments {popup}
    
    global KnobFile KnobLines changeWidgetStateList
    if !$KnobLines {
        return
    }
    if $popup {
        if {![APSYesNoPopUp "Really? Clear all knob file settings?"]} { return }
    }
    for {set i 0} {$i<$KnobLines} {incr i} {
        if [winfo exists $widget0.m$i] {
            destroy $widget0.m$i
        }
        if [winfo exist $widget0.opt$i] {
            destroy $widget0.opt$i
        }
    }
    set KnobLines 0
}

proc MakeNewKnobLine {widget0 args} {
    global KnobFile KnobLines KnobScrollFrame  changeWidgetStateList popupInterface
    set line ""
    set file "" 
    APSParseArguments {line file}
    
    set KnobFile($KnobLines.file) $file
    set KnobFile($KnobLines.valid) 1
    if $popupInterface {
        CreateNewKnobLine $widget0 $KnobLines
    }
    incr KnobLines
}

##########################################################################
#globals
set outputFileWidget ""
set initialized 0
set outputFileChecked 0
set appendToStatsFile 0
set appendToLogFile 0
set Timeout 100
set Interval 1
set runStatsWithTest 0
set changeWidgetStateList ""
set apsScriptCommand ExperimentDesigner
set globalList {STARTED ABORT PAUSE PauseLine PauseStep pvLinksSetup statsFileSetup logFileSetup \
                 logCombine}
foreach name $globalList {
    set $name 0
}
foreach nm {monitor init finalize postProcess exec} {
    set ${nm}Lines 0
    set ${nm}Scroll ""
}

#1.pv widget ---------
set monitorNameList [list type name var max min unit NoCheck \
                       controlAction controlIncrement controlInitial \
                       controlFinal controlFixedValues controlListOfValues setpointVar]
set controlvarList [list controlAction controlIncrement controlInitial controlFinal controlFixedValues \
                      controlListOfValues]
foreach var $controlvarList {
    set $var ""
}
set saveListNames {pvSavedList eqSavedList parSavedList controlSavedList setpointSavedList readbackSavedList scriptSavedList knobSavedList}
foreach save $saveListNames {
    set $save ""
}
set monitorVarList [list monitorScroll PVname PVvariable PVtype PVmin PVmax \
                 controlSetValue controlChangeValue controlAction controlListValue \
                 controlInitial controlFinal changeControlFile ControlRelative \
                 EQentry EQvariable EQunits PARvalue PARvariable PARunits PARtypes \
                 pvSavedList controlSavedList setpointSavedList eqSavedList parSavedList]
foreach var $monitorVarList {
    set $var ""
}
set ControlRelative 0
set NoCheck 0
set readDir ""
#2. init/finalize widget ----------
set initFinalVarList {initFinalLine initFinalName initFinalValue initFinalOrig initFinalScript \
                        initFinalType initFinalReadback initFinalTolerance initFinalMode \
                        initFinalMakeType initFinalScroll}
foreach name $initFinalVarList {
    set $name ""
}
set initFinalLine 0
set initFinalTolerance 0.01

#4. execution widget ---------
set Description ""
set ExpSteps 5
set Steps 5
set RunPostprocess 1
set StartStep 0
set PauseStep 0
set Interval 1

set home [pwd]
#set home $env(HOME)
set readDir ""
set saveDir ""
#set home /home/oxygen/SHANG/test/experimentDesigner
set globalVarList [list Description outputDir outputRootname ExpSteps Step StepString Timeout Interval logFileSuffix]
set globalVarDescList {"Experiment description" "the directory for writing output files to" "the rootname of output files, the file name looks like \$outputDir/\$\{outputRootname\}*" "The total steps ran in the experiment" "Current running step in the experiment" "Current running step in %06d format" "The maximum allowed waiting time when any PV is out of range" "the waiting time between two steps" "the log file suffix name of ReadValue"}

set statsTypeList {mean stDev sigma min max spread MAD median}
set execLine 0
set Stats(combine) 0
set Stats(fileID) ""
set Stats(mean) 1
set Stats(stDev) 1
set Stats(sigma) 1
set Stats(min) 0
set Stats(max) 0
set Stats(spread) 0
set Stats(average) 5
set Stats(pause) 0.1
set Stats(MAD) 0
set Stats(median) 0
set Stats(fileSuffix) Stats
set logFileSuffix Readvalue
set logFileID ""
set logFile ""

set execVarList [list tiemout execName execMode execLine waitTime sddsProgramName runScript setPV setPVValue \
                   sddsProgramInput sddsProgramOutputSuffix sddsProgramOutput sddsProgramAppend customProgramName \
                   sddsProgramAppendToPage sddsProgramSteps sddsProgramInterval sddsProgramUserOption \
                   queryColumnName queryColumnDataType queryColumnUnits WaitMessage QueryColumnNames]
foreach var $execVarList {
    set $var ""
}
set execParseArgumentsList {name setPV setPVValue waitTime sdds_append sdds_appendToPage sdds_inputfile \
                              sdds_program sdds_outputSuffix sdds_steps sdds_interval sdds_userOption runscript \
                              WaitMessage query_columnName query_dataType query_units customProgramName}
                              
set sddsProgramOutputSuffix sdds
set sddsProgramAppend 0
set sddsProgramAppendToPage 0
set sddsProgramSteps 5
set sddsProgramInterval 1

set postProcessScroll ""
set postProcessScrollFrame ""
set monitorLineVariables ""
#commandline arguments
set configuration ""
set popupInterface 1
set initialize 1
set help 0
set outputRootname ""
set restoreControl 1
set brief 0
set pvSavedList ""
set controlSavedList ""
set knobSavedList ""
set debug 1
set globalParseVariables {outputDir outputRootname Description Step ExpSteps Timeout Interval debug}
if [catch {set outputDir [APSGoToDailyDirectory]}] {
   set outputDir .
}

set usage "usage: ExperimentDesigner -configuration <dirname> -outputDir <string> -outputRootname <stirng> -popupInterface <1|0> -initialize <1|0> -help <0|1> -brief <0|1>"
set args $argv
set applicationName ExperimentDesigner
APSStrictParseArguments {configuration popupInterface initialize help outputRootname outputDir restoreControl brief applicationName}
if $help {
    puts stderr $usage
    exit
}
if !$popupInterface {
    wm withdraw .
}
if $popupInterface {
    APSApplication . -name $applicationName -version $CVSRevisionAuthor \
      -overview {This is the ExperimentDesigner 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 SaveConfigure
    .menu.file.menu insert 1 command -label "Load Config..." -underline 0 \
      -command LoadConfiguration
    
    set mainStatus "Use one of \"Add ...\" buttons to provide input."
    APSScrolledStatus .status -parent .userFrame -textVariable mainStatus -width 100 \
      -withButtons 1 -packOption "-fill x"
    if !$brief {
        set sectionList [list ProcessVariables KnobFiles \
                             Initialization ExecutionDesign Finalization \
                             OutputFiles Postprocess ArgumentsPassHelp]
        set tabFrameWidgetList [APSTabFrame .main -parent .userFrame -label "" \
                                    -labelList $sectionList \
                                  -width 1050 -height 450 -packOption "-fill both -expand true"]
        CreatePVWidget [lindex $tabFrameWidgetList 0]
        CreateKnobWidget [lindex $tabFrameWidgetList 1]
        CreateInitWidget [lindex $tabFrameWidgetList 2]
        CreateExecuteWidget [lindex $tabFrameWidgetList 3]
        CreateFinalizationWidget [lindex $tabFrameWidgetList 4]
        set outputFileWidget [lindex $tabFrameWidgetList 5]
        CreatePostprocessWidget [lindex $tabFrameWidgetList 6]
        CreateHelpWidget [lindex $tabFrameWidgetList 7]
        #can not add contextHelp for each tab widget
        set apsContextHelp(.userFrame.main) "\"ProcessVariable\" panel is for adding PVs for monitoring or control PVs changing values in experiment\n\n\"Initialization\" panel is for adding initialization of experiment\n\n\"ExecutionDesign\" panel is for designing complicated execution chain - a series of different executions\n\n\"Finalization\" panel is for executions after the experiment.\n\n\"OutputFiles\" panel is for displaying and processing output files (all output files are listed here)\n\n\"PostProcess\" panel is for specifying post-processing to be run in the end of the experiment."
    } else {
        set sectionList {ProcessVariables OutputFiles Postprocess}
        set tabFrameWidgetList [APSTabFrame .main -parent .userFrame -label "" \
                                  -labelList $sectionList \
                                  -width 1050 -height 450 -packOption "-expand true -fill both"]
        CreatePVWidget [lindex $tabFrameWidgetList 0]
        set outputFileWidget [lindex $tabFrameWidgetList 1]
        APSLabeledEntry .dir -parent $outputFileWidget -label "output directory: " -width 82 \
          -textVariable outputDir -packOption "-fill x -expand true"
        bind $outputFileWidget.dir.entry <Leave> UpdateOutputFiles
        APSLabeledEntry .root -parent $outputFileWidget -label "output rootname:" -width 82 \
          -textVariable outputRootname
        bind $outputFileWidget.root.entry <Leave> UpdateOutputFiles
        CreatePostprocessWidget [lindex $tabFrameWidgetList 2]
    }
    ttk::frame .userFrame.ops -borderwidth 4 -relief raised
    pack .userFrame.ops -side top -fill x
    set w .userFrame.ops
    APSButton .init -parent $w -text INITIALIZE \
      -command "InitFinalize -type init" \
      -contextHelp "Launches a monitoring subprocess to initilize data before running experiment" -width ""
    APSButton .run -parent $w -text RUN \
      -command RUN -width ""
    APSButton .initandrun -parent $w -text "INITIALIZE+RUN" -command "RUN -initialize 1" \
      -contextHelp "Initialize and Run Experiment" -width ""
    APSButton .pause -parent $w -text PAUSE -command {set PAUSE 1;APSEnableButton $w.resume.button} \
      -contextHelp "Interrupting a monitoring subprocess." -width ""
    APSButton .resume -parent $w -text RESUME \
      -command Resume \
      -contextHelp "Resume experiment after being paused" -width ""
    APSDisableButton $w.resume.button
    APSButton .term -parent $w -text TERMINATE -command "set ABORT 1;Terminate" \
      -contextHelp "Terminate experiment and restore original settings." -width ""
    if !$brief {
        APSButton .clear -parent $w -text "CLEAR ALL" -command "ClearPVSettings $monitorScroll;\
ClearInitFinalizeSettings $initScroll init;ClearExecSettings $execScroll;ClearInitFinalizeSettings $finalizeScroll finalize;ClearPostprocessSettings $postProcessScroll" \
          -contextHelp "Clears all of the settings for UserCustomMonitor, including PV names and filenames." -width ""
        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..." -width ""
        lappend changeWidgetStateList ${w}.clear.button ${w}.namecap.button
    }
    lappend changeWidgetStateList ${w}.run.button
    .userFrame.ops.pause.button configure -state disabled
    .userFrame.ops.resume.button configure -state disabled
    .userFrame.ops.term.button configure -state disabled
}

if [string length $configuration] {
    if ![file exist $configuration] {
        puts stderr "$configuration directory does not exist!"
        exit
    }
    if ![file isdirectory $configuration] {
        puts stderr "$configuration is not a directory!"
        exit
    }
    LoadConfiguration -configuration $configuration
    if !$popupInterface {
        if $initialize {
            if [catch {InitFinalize -type init} result] {
                SetMainStatus "$result"
                bell
                exit
            }
        }
        RUN
        exit
    }
}
