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

#
# $Log: not supported by cvs2svn $
# Revision 1.8  2004/02/25 22:58:27  shang
# added extension .resp to the response matrix filename
#
# Revision 1.7  2004/02/13 17:20:43  shang
# replaced APSMakeSafeQualifierString by APSMakeSafeQualifierStringForEval to fix
# the mixing character , and / problem for comment
#
# Revision 1.6  2004/02/13 16:03:49  shang
# added "save raw data" option to save the raw data into rawData sub-directory
#
# Revision 1.5  2003/11/11 20:37:20  shang
# changed the default directory of loading configuration to be the same as
# reponse file and used file selection dialog for saving configurations.
#
# Revision 1.4  2003/11/06 22:18:54  shang
# changed the symbolic name of readbacks from <pvname> to <pvname>Slope
# in definition file to be consistent with the column names of the inverse
# response matrix
#
# Revision 1.3  2003/10/16 15:03:16  shang
# fixed the mis-spelling "dialy" to "daily"
#
# Revision 1.2  2003/10/15 19:49:12  shang
# fixed the problem in processing inverse response matrix
#
# Revision 1.1  2003/09/30 22:33:52  shang
# first version for response matrix measurement
#
# the interface is copied from quickExperiment, SHANG

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 CVSRevisionAuthor "\$Revision: 1.9 $ \$Author: shang $"

APSApplication . -name quickResponseMeasurement -version $CVSRevisionAuthor \
  -overview {This is the quickResponseMeasurement utility.  It provides a simple interface to the sddsexperiment program, which allows varying and measuring EPICS data, and logging the results and some statistical analysis to an SDDS file.  The data can be plotted after it is collected. You can also manipulate and display the data offline using the SDDS toolkit programs.}


set Measurements 0
set MeasurementLines 1

set Variables 0
set VariableLines 1

set mainStatus "Press ADD to enter more PV names for measurements and variables"

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

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

APSFrame .measurement -parent .userFrame -label Measurements -packOption \
  "-side top -fill x" -contextHelp \
  "Enter data for measurement PVs in this frame."
  
APSFrame .variable -parent .userFrame  -label Actuators -packOption \
  "-side top -fill x" -contextHelp "Enter data for variable PVs in this frame."

set Steps 5
APSLabeledEntry .steps -parent .userFrame.variable.frame -label \
  "Steps: " -textVariable Steps -width 10 -contextHelp "Enter the number of steps (or samples) in this field." -packOption "-anchor w"

set varScroll [APSScroll .varScroll -parent .userFrame.variable.frame]
set varScrollFrame .userFrame.variable.frame.varScroll

APSButton .addblank -parent .userFrame.variable.frame -text "ADD" \
  -command "MakeNewVariableLine $varScroll" -contextHelp "Press to add another variable PV name entry line.\n\nDuring the experiment all variables will change together with the same number of steps with step size determined from their respective initial and final values."

APSButton .addcopy -parent .userFrame.variable.frame -text "ADD Copy" \
  -command "MakeNewVariableLine $varScroll -addCopy 1" -contextHelp "Press to add another variable PV name entry line.\n\nDuring the experiment all variables will change together with the same number of steps with step size determined from their respective initial and final values."
#$widget.op.frame.add configure -bd 0
APSButton .load -parent .userFrame.variable.frame -text "LOAD NAMES..." \
  -command LoadVariableData -contextHelp "Press to select a file from which to load variable PV names  and corresponding information -- initial and final value etc."
APSButton .save -parent .userFrame.variable.frame -text "SAVE CONFIG..." \
  -command SaveVariableData -contextHelp "Press to save variable configuration data to a file."


set measScroll [APSScroll .measScroll -parent .userFrame.measurement.frame]
set measScrollFrame .userFrame.measurement.frame.measScroll

APSButton .add -parent .userFrame.measurement.frame -text "ADD" -command \
  "MakeNewMeasurementLine $measScroll" -contextHelp \
  "Press to add another measurement PV name entry line."

APSButton .addcopy -parent .userFrame.measurement.frame -text "ADD Copy" -command \
  "MakeNewMeasurementLine $measScroll -addCopy 1" -contextHelp \
  "Press to add another measurement PV name entry line using previous entry as starting point."

APSButton .load -parent .userFrame.measurement.frame -text "LOAD NAMES..." \
  -command LoadMeasurementData -contextHelp "Press to select a file from which to load PV names.  File may be a BURT request file, sddsmonitor input file, or a measurement configuration saved from this tool."

APSButton .save -parent .userFrame.measurement.frame -text "SAVE CONFIG..." \
  -command SaveMeasurementData -contextHelp "Press to save measurement configuration data to a file.  Measurements can be reloaded later using LOAD NAMES..."

APSFrame .execution -parent .userFrame -contextHelp "Enter execution parameters here"

set interval 1
set PostChangePause 3
set rampSteps 10
set inputFile /home/helios/oagData/quickResponse/ltpInjRespMeasTemplate.exp
set topupInputFile /home/helios/oagData/quickResponse/topupRespMeasTemplate.exp
set checkTopup 0
set definitionFile ""
set responseFile ""
set invResponseFile ""
set comment ""
set numToAverage 3
set doStd 0
set doSigma 0
set minimum 0
set saveRawData 0
APSFrameGrid .grid -parent .userFrame.execution.frame -xList {x1 x2}
set w .userFrame.execution.frame.grid.x1
APSLabeledEntry .average -parent $w -label "No. to average:" -width 10 -textVariable numToAverage \
  -contextHelp "The number of averages of the readback values."
APSLabeledEntry .interval -parent $w -label "Interval (s): " \
  -textVariable interval -width 10 \
  -contextHelp "Enter the time in seconds between measurements."
APSLabeledEntry .minimum -parent $w -label "Minimum singular value ratio:" -width 10 \
  -textVariable minimum \
  -contextHelp "minimumSingularValueRatio used in sddspseudoinverse:\nReject singular values less than the\nlargest singular value times this ratio."
APSRadioButtonFrame .topup -parent $w -label "Check topup?" -buttonList {Yes No} \
    -valueList {1 0} -variable checkTopup -orientation horizontal \
    -contextHelp "if checked, the experiment will wait for topup shots."

set w .userFrame.execution.frame.grid.x2
APSLabeledEntry .postChangePause -parent $w -label "Post change pause (s): " \
  -textVariable PostChangePause -width 10 \
  -contextHelp "Enter the time in seconds to wait after a variable change and before measurements are taken."
APSLabeledEntry .rampSteps -parent $w -label "Initial Ramp Steps: " \
  -textVariable rampSteps -width 10 \
  -contextHelp "Enter the steps for ramping pvs from current value to the initial values."

APSCheckButtonFrame .std -parent $w -buttonList {stddev sigma} -label {} \
      -variableList "doStd doSigma" -orientation horizontal -allNone 0 \
      -contextHelp "Press these buttons to enable standard deviation or sigma calculation by sddsexperiment."

APSRadioButtonFrame .saverawdata -parent $w -label "Save raw data:" -buttonList {yes no} \
  -valueList {1 0}  -variable saveRawData  -packOption "-side left" -orientation horizontal \
  -contextHelp "if save raw data is chosen, then raw data will save under sub-directory rawData."

if {0} {
APSLabeledEntry .execution.frame.input -parent .userFrame -label "Input file: " \
  -textVariable inputFile -width 80 \
  -contextHelp "Enter a name for the input file in this field, if you wish to.  Otherwise, a filename is generated for you. \
NOTE: This is the name of an input file to sddsexperiment that quickExperiment generates for you.  If the file you name \
exists already, it will be overwritten.  Use this field if you want quickExperiment to put the sddsexperiment input file \
in a place where you can retrieve it for later commandline use."
bind .userFrame.execution.frame.input.entry <Key> {set tmpInputFile 0}
}
APSLabeledEntry .execution.frame.output -parent .userFrame -label "Response file: " \
  -textVariable responseFile -width 80 \
  -contextHelp "Enter a name for the output file to hold the response matrix."
bind .userFrame.execution.frame.output.entry <Leave> SetInvResponseFile
APSLabeledEntry .execution.frame.inv -parent .userFrame -label "Inverse file: " \
  -textVariable invResponseFile -width 80 \
  -contextHelp "Enter a name for inverse response matrix file"

APSButton .daily -parent .userFrame.execution.frame.output -size small -text "daily" \
  -command "set responseFile [APSGoToDailyDirectory]" -packOption "-side right"
APSLabeledEntry .execution.frame.comment -parent .userFrame -label "Comment: " \
  -textVariable comment -width 80 \
  -contextHelp "Enter the comment what you want to be saved as a parameter in the output file."

APSFrame .ops -parent .userFrame \
  -contextHelp "Actions are started in this frame."
APSButton .ops.frame.run -parent .userFrame -text RUN -command RunExperiment \
  -contextHelp "Launches a sddsexperiment subprocess to collect the data."
APSButton .ops.frame.clear -parent .userFrame -text "CLEAR ALL" \
  -command "ClearVarSettings $varScroll; ClearMeasSettings $measScroll; set responseFile \"\"; set logplot 0" \
  -contextHelp "Clears all of the settings for quickExperiment, including PV names and filenames."
APSButton .ops.frame.clearVar -parent .userFrame -text "CLEAR MEAS" \
  -command "ClearMeasSettings $measScroll" \
  -contextHelp "Clears the measurement PV settings only."
APSButton .ops.frame.clearMeas -parent .userFrame -text "CLEAR VARS" \
  -command "ClearVarSettings $varScroll" \
  -contextHelp "Clears the variable PV settings only."
APSButton .ops.frame.inv -parent .userFrame -text "InvRespMatrix" \
  -command RecalculateInv \
  -contextHelp "recimpute the inverse matrix with different combinations of actuators/measurements and minimum singular value ratio."

#set configDir [file dirname $inputFile]
proc RecalculateInv {args} {
    global responseFile invResponseFile
    if ![file exist $responseFile] {
        return -code error "The response file does not exist!"
    }
    exec InvRespMatrix -inputFile $responseFile -outputFile $invResponseFile &
}

proc LoadMeasurementData {} {
    global env measScroll configDir responseFile
    if ![info exist configDir] {
	set configDir [file dirname $responseFile]
    }
    set filename [APSFileSelectDialog .chooseInputFile -width 40 -path $configDir]
    if [string length $filename]==0 { 
        return 
    }
    if [string first " " $filename]!=-1 {
        SetMainStatus "Filename may not have spaces in it."
        return
    }
    if {![APSCheckSDDSFile -fileName $filename]} {
        SetMainStatus "$filename is not an SDDS file."
        return
    }
    set configDir [file dirname $filename]
    set colNames [APSGetSDDSNames -fileName $filename -class column]
    if {[llength $colNames]<1} {
        SetMainStatus "$filename doesn't contain any data columns from which to get PV names."
        return 
    }
    if {[lsearch -exact $colNames "ControlName"]!=-1} {
        set columnName ControlName
    } elseif {[lsearch -exact $colNames "Device"]!=-1} {
        set columnName Device
    } else {
        SetMainStatus "$filename doesn't contain any data columns from which to get PV names."
        return
    }
    set names [exec sdds2stream $filename -column=$columnName -page=1]
    if [lsearch -exact $colNames "SymbolicName"]>=0 {
        set symbols [exec sdds2stream $filename -column=SymbolicName -page=1]
    } else {
        set symbols $names
    }
    
    if {[llength $names]==0} {
        SetMainStatus "$filename has no process variable names."
        return
    }
    ClearMeasSettings $measScroll 0
    foreach name $names symbol $symbols {
        MakeNewMeasurementLine $measScroll -pvName $name -symbolName $symbol
    }
    SetMainStatus "Measurement data loaded from $filename."
}

proc SaveMeasurementData {args} {
    global MeasurementLines MeasPVname configDir MeasSymName
    set filename ""
    APSParseArguments {filename}
    set pvList ""
    set symbolList ""
    for {set i 1} {$i<$MeasurementLines} {incr i} {
        if [string length $MeasPVname($i)]!=0 {
            if [catch {SearchPVName -pvName $MeasPVname($i)} result] {
                SetMainStatus "$result"
                continue
            } else {
                lappend pvList $MeasPVname($i)
                if [string length $MeasSymName($i)] {
                    lappend symbolList $MeasSymName($i)
                } else {
                    lappend symbolList $MeasPVname($i)
                }
            }
        }
    }
    if ![llength $pvList] {
        SetMainStatus "No measurements specified--can't save."
        return
    }
    if ![string length $filename] {
	global configDir responseFile
	if ![info exist configDir] {
	    set configDir [file dirname $responseFile]
	}
	set filename [APSFileSelectDialog .choosedirectory -listDir $configDir \
			  -title "output file for saving measurement configuration" -checkValidity 0 ] 
    }
    if [string length $filename]==0 {
        SetMainStatus "Filename is provided!"
        return
    }
    set configDir [file dirname $filename]
    if [string first " " $filename]!=-1 {
        SetMainStatus "Filename may not contain spaces--nothing saved."
        return
    }
    if [file exist $filename] {
        if ![APSYesNoPopUp "$filename already exist, overwrite it?"] {
            SetMainStatus "configuration saving is cancelled."
            return
        }
	exec rm $filename
        
    }
    if [catch {exec sddsmakedataset -defaultType=string -col=ControlName -data=[join $pvList ,] \
                 -col=ReadbackName -data=[join $symbolList ,] \
                 -col=SymbolicName -data=[join $symbolList ,] $filename} result] {
        return -code error $result
    }
    SetMainStatus "Save completed to file $filename."
}

proc LoadVariableData {} {
    global env varScroll configDir responseFile
    if ![info exist configDir] {
	set configDir [file dirname $responseFile]
    }
    set filename [APSFileSelectDialog .chooseInputFile -width 40 -path $configDir]
    if [string length $filename]==0 { 
        return 
    }
    if [string first " " $filename]!=-1 {
        SetMainStatus "Filename may not have spaces in it."
        return
    }
    if {![APSCheckSDDSFile -fileName $filename]} {
        SetMainStatus "$filename is not an SDDS file."
        return
    }
    set configDir [file dirname $filename]
    set colNames [APSGetSDDSNames -fileName $filename -class column]
    if {[llength $colNames]<1} {
        SetMainStatus "$filename doesn't contain any data columns from which to get PV names."
        return 
    }
    if {[lsearch -exact $colNames "ControlName"]!=-1} {
        set columnName ControlName
    } elseif {[lsearch -exact $colNames "Device"]!=-1} {
        set columnName Device
    } else {
        SetMainStatus "$filename doesn't contain any data columns from which to get PV names."
        return
    }
    set names [exec sdds2stream $filename -col=$columnName -page=1]
    if {[llength $names]==0} {
        SetMainStatus "$filename has no process variable names."
        return
    }
    if [lsearch -exact $colNames SymbolicName]>=0 {
        set symbols [exec sdds2stream $filename -col=SymbolicName -page=1]
    } else {
        set symbols $names
    }
    set initialValues ""
    if [lsearch -exact $colNames "InitialValue"]!=-1 {
        set initialValues [exec sdds2stream $filename -column=InitialValue -page=1]
    } else {
        for {set i 0} {$i< [llength $names]} {incr i} {
            lappend initialValues 0
        }
    }
    set finalValues ""
    if [lsearch -exact $colNames "FinalValue"]!=-1 {
        set finalValues [exec sdds2stream $filename -column=FinalValue -page=1]
    } else {
        for {set i 0} {$i< [llength $names]} {incr i} {
            lappend finalValues 0
        }
    }
    set relativeValues ""
    if [lsearch -exact $colNames "Relative"]!=-1 {
        set relativeValues [exec sdds2stream $filename -column=Relative -page=1]
    } else {
        for {set i 0} {$i< [llength $names]} {incr i} {
            lappend relativeValues 0
        }
    }
    
    ClearVarSettings $varScroll 0
    foreach name $names symbol $symbols init $initialValues final $finalValues relative $relativeValues {
        MakeNewVariableLine $varScroll -pvName $name  -symbolName $symbol \
          -initVal [format %.3f $init] -finalVal [format %.3f $final] -relative $relative
    }
    SetMainStatus "Measurement data loaded from $filename."
}

proc SaveVariableData {args} {
    global VariableLines VarPVname configDir InitValue FinalValue Relative VarSymName
    set filename ""
    APSParseArguments {filename}
    set pvList ""
    set symbolList ""
    set initList ""
    set finalList ""
    set relativeList ""
    for {set i 1} {$i<$VariableLines} {incr i} {
        if [string length $VarPVname($i)]!=0 {
            if [catch {SearchPVName -pvName $VarPVname($i)} result] {
                SetMainStatus "$result"
                continue
            } else {
                lappend pvList $VarPVname($i)
                lappend initList $InitValue($i)
                lappend finalList $FinalValue($i)
                lappend relativeList $Relative($i)
                if [string length $VarSymName($i)] {
                    lappend symbolList $VarSymName($i)
                } else {lappend symbolList $VarPVname($i) }
            }
        }
    }
    if ![llength $pvList] {
        SetMainStatus "No measurements specified--can't save."
        return
    }
    if ![string length $filename] {
	global configDir  responseFile
	if ![info exist configDir] {
	    set configDir [file dirname $responseFile]
	}
	set filename [APSFileSelectDialog .choosedirectory -listDir $configDir \
			  -title "output file for saving variable configuration" -checkValidity 0 ] 
    }
    if [string length $filename]==0 {
        SetMainStatus "Filename is provided!"
        return
    }
    set configDir [file dirname $filename]
    if [string first " " $filename]!=-1 {
        SetMainStatus "Filename may not contain spaces--nothing saved."
        return
    }
    if [file exist $filename] {
        if ![APSYesNoPopUp "$filename already exist, overwrite it?"] {
            SetMainStatus "configuration saving is cancelled."
            return
        }
	exec rm $filename
    }
    if [catch {exec sddsmakedataset $filename -defaultType=string -col=ControlName -data=[join $pvList ,] \
                 -col=ReadbackName -data=[join $pvList ,] \
                 -col=SymbolicName -data=[join $symbolList ,] \
                 -col=InitialValue,type=double -data=[join $initList ,] \
                 -col=FinalValue,type=double -data=[join $finalList ,] \
                 -col=Relative,type=short -data=[join $relativeList ,] } result] {
        return -code error $result
    }
    SetMainStatus "Save completed to file $filename."
}

proc ClearMeasSettings {widget0 {popUp 1}} {
    global MeasurementLines MeasPVname doStd doSigma Average 

    if {$popUp} {
	if {![APSYesNoPopUp "Really? Clear measurement settings?"]} { return }
    }
    for {set i $MeasurementLines} {$i>0} {incr i -1} {
        if {[winfo exists $widget0.m$i]} { destroy $widget0.m$i}
        set MeasPVname($i) ""
    }
    set MeasurementLines 1
    SetMainStatus "Press ADD to enter measurement or variable PV names for sddsexperiment"
}

proc ClearVarSettings {widget0 {popUp 1}} {
    global VariableLines VarPVname InitValue FinalValue Relative

    if {$popUp} {
	if {![APSYesNoPopUp "Really? Clear variable settings?"]} { return }
    }
    for {set i $VariableLines} {$i>0} {incr i -1} {
        if {[winfo exists $widget0.m$i]} { destroy $widget0.m$i}
        set VarPVname($i) ""
        set InitValue($i) 0
        set FinalValue($i) 0
        set Relative($i) 0
    }
    set VariableLines 1
    SetMainStatus "Press ADD to enter measurement or variable PV names for sddsexperiment"
}

proc MakeNewMeasurementLine {widget0 args} {
    global measScrollFrame
    global MeasurementLines MeasPVname MeasSymName

    set addCopy 0
    APSParseArguments {addCopy}
    APSFrame .m$MeasurementLines -parent $widget0  -packOption "-anchor w"
    set widget $widget0.m$MeasurementLines.frame
    $widget configure -relief flat -bd 0

    APSFrame .op  -parent $widget -packOption "-side left" 
    $widget.op.frame configure -relief flat -bd 0 
    APSFrame .data  -parent $widget -height 1
    $widget.data.frame configure -relief flat -bd 0 

    if {!$addCopy} { 
        set pvName ""
        set symbolName ""
    } else {
        set lastMeas [expr $MeasurementLines - 1]
        set pvName $MeasPVname($lastMeas)
        set symbolName $MeasSymName($lastMeas)
    }        
    APSParseArguments {pvName symbolName}
    
    APSButton .delete -parent $widget.op.frame -text "D" -size small \
      -command "DeleteMeasurementLine $widget0 $MeasurementLines" \
      -contextHelp "Press to delete the corresponding PV name entry line." 
    $widget.op.frame.delete configure -bd 0
    $widget.op.frame.delete.button configure -font -adobe-courier-medium-r-normal-*-12-*-*-*-*-*-*-*

    APSButton .clear -parent $widget.op.frame -text "C" -size small \
      -command "ClearMeasurementLine $MeasurementLines" \
      -contextHelp "Press to clear the corresponding PV name entry line." 
    $widget.op.frame.clear configure -bd 0
    $widget.op.frame.clear.button configure -font -adobe-courier-medium-r-normal-*-12-*-*-*-*-*-*-*
    
    set MeasPVname($MeasurementLines) $pvName
    APSLabeledEntry .x -parent $widget.data.frame -label "PV:" -textVariable \
      MeasPVname($MeasurementLines) -width 30 \
      -contextHelp "Enter the name of a readback process variable (PV) in this field." \
      -packOption "-side left"
    $widget.data.frame.x configure -bd 0
    set MeasSymName($MeasurementLines) $symbolName
    APSLabeledEntry .y -parent $widget.data.frame -label "Symbolic Name:" -textVariable \
      MeasSymName($MeasurementLines) -width 30 \
      -contextHelp "Enter the symbolic name of a readback process variable (PV) in this field." \
      -packOption "-side left"
    $widget.data.frame.y configure -bd 
    incr MeasurementLines
    tkwait visibility $widget.data.frame.y
    APSScrollAdjust $measScrollFrame -numVisible 5
}

proc MakeNewVariableLine {widget0 args} {
    global varScrollFrame VarSymName
    global VariableLines VarPVname 
    global InitValue FinalValue Relative

    set addCopy 0
    set pvName ""
    set symbolName ""
    set initVal 0
    set finalVal 0
    set relative 0
    APSParseArguments {addCopy}
    APSFrame .m$VariableLines -parent $widget0 -packOption "-anchor w -fill x"
    set widget $widget0.m$VariableLines.frame
    $widget configure -relief flat -bd 0
    APSFrame .op  -parent $widget -packOption "-side left" 
    $widget.op.frame configure -relief flat -bd 0
    APSFrame .data  -parent $widget -height 1
    $widget.data.frame configure -relief flat -bd 0

    if $addCopy {
        set lastVar [expr $VariableLines - 1]
        set pvName $VarPVname($lastVar)
        set symbolName $VarSymName($lastVar)
        set initVal $InitValue($lastVar)
        set finalVal $FinalValue($lastVar)
        set relative $Relative($lastVar)
    }        
    APSParseArguments {pvName symbolName initVal finalVal relative}

    APSButton .delete -parent $widget.op.frame -text "D" -size small \
      -command "DeleteVariableLine $widget0 $VariableLines" \
      -contextHelp "Press to delete the corresponding PV name entry line." 
    $widget.op.frame.delete configure -bd 0
    $widget.op.frame.delete.button configure -font -adobe-courier-medium-r-normal-*-12-*-*-*-*-*-*-*

    APSButton .clear -parent $widget.op.frame -text "C" -size small \
      -command "ClearVariableLine $VariableLines" \
      -contextHelp "Press to clear the corresponding PV name entry line." 
    $widget.op.frame.clear configure -bd 0
    $widget.op.frame.clear.button configure -font -adobe-courier-medium-r-normal-*-12-*-*-*-*-*-*-*
    
    set VarPVname($VariableLines) $pvName
    APSLabeledEntry .x -parent $widget.data.frame -label "PV:" -textVariable \
      VarPVname($VariableLines) -width 20 \
      -contextHelp "Enter the name of a variable process variable (PV) in this field." \
      -packOption "-side left"
    $widget.data.frame.x configure -bd 0
    set VarSymName($VariableLines) $symbolName
    APSLabeledEntry .y -parent $widget.data.frame -label "Symbolic Name:" -textVariable \
      VarSymName($VariableLines) -width 20 \
      -contextHelp "Enter the symbolic name of a variable process variable (PV) in this field." \
      -packOption "-side left"
    $widget.data.frame.y configure -bd 0
    
    set InitValue($VariableLines) $initVal
    APSLabeledEntry .init -parent $widget.data.frame -label "Initial:" -textVariable \
      InitValue($VariableLines) -width 6 \
      -contextHelp "Enter the initial value for the variable process variable (PV) in this field." \
      -packOption "-side left"
    $widget.data.frame.init configure -bd 0
    set FinalValue($VariableLines) $finalVal
    APSLabeledEntry .final -parent $widget.data.frame -label "Final:" -textVariable \
      FinalValue($VariableLines) -width 6 \
      -contextHelp "Enter the final value for the variable process variable (PV) in this field." \
      -packOption "-side left"
    $widget.data.frame.final configure -bd 0
    set Relative($VariableLines) $relative
    APSCheckButtonFrame .rel -parent $widget.data.frame -label {} -buttonList {Relative} \
      -variableList Relative($VariableLines) -orientation horizontal \
      -contextHelp "If checked, then the variation range is defined relative to the PV's original value." \
      -packOption "-side left"
    $widget.data.frame.rel configure -bd 0
    
    incr VariableLines
    tkwait visibility $widget.data.frame.rel
    APSScrollAdjust $varScrollFrame -numVisible 5
}

proc DeleteMeasurementLine {widget0 number} {
    global MeasurementLines MeasPVname MeasSymName
    if {$number} {
        destroy $widget0.m$number
        set MeasPVname($number) ""
        set MeasSymName($number) ""
    } else {
        bell
    }
}

proc ClearMeasurementLine {number} {
    global MeasurementLines MeasPVname
    set MeasPVname($number) ""
}

proc DeleteVariableLine {widget0 number} {
    global VariableLines VarPVname VarSymName
    if {$number} {
        destroy $widget0.m$number
        set VarPVname($number) ""
        set VarSymName($number) ""
    } else {
        bell
    }
}

proc ClearVariableLine {number} {
    global VariableLines VarPVname 
    global InitValue FinalValue Relative

    set VarPVname($number) ""
    set InitValue($number) 0
    set FinalValue($number) 0
    set Relative($number) 0
}

set outputListLength 0
proc RunExperiment {} {
    global MeasurementLines MeasPVname doStd doSigma Average comment
    global VariableLines VarPVname InitValue FinalValue Relative
    global responseFile inputFile experimentDone minimum invResponseFile topupInputFile
    global PostChangePause interval Steps  
    global numToAverage doStd doSigma VarSymName saveRawData rampSteps checkTopup
 
    #search for controlname
    if [catch {SaveDefinitionFile} result] {
        SetMainStatus "$result"
        return
    }
    if $checkTopup {
        set input $topupInputFile   
    } else {
        set input $inputFile
    }
    SetMainStatus "Starting experiemnt ...."
    if {![file exist $input]} {
        SetMainStatus "$input does not exit!"
        bell
        return
    }
    if {[string length $responseFile]==0 || [file isdirector $responseFile]} {
        SetMainStatus "Supply the response filename."
        bell
        return
    }
    if {[string length $invResponseFile]==0 || [file isdirector $invResponseFile]} {
        SetMainStatus "Supply the inverse response filename."
        bell
        return
    }
    if ![string match "*.resp" $responseFile] {
        set responseFile ${responseFile}.resp
    }
    if {[file exists $responseFile]} {
        set ok [APSYesNoPopUp "$responseFile already exists, overwrite it?"]
        if {!$ok} {
            SetMainStatus "Supply a new response matrix filename"
            return
        } else {
            exec rm $responseFile
        }
    }
    if {[file exists $invResponseFile]} {
        set ok [APSYesNoPopUp "$invResponseFile already exist, overwrite it?"]
        if {!$ok} {
            SetMainStatus "Supply a new inverse response filename"
            return
        } else {
            exec rm $invResponseFile
        }
    }
    #save measurements into a file
    if $saveRawData {
        set outDir [file dirname $responseFile]/rawData
        set rootname [file tail $responseFile]
        if ![file exist $outDir] {
            if [catch {exec mkdir -p $outDir} result] {
                SetMainStatus "Can not make directory $outDir: $result."
                return
            }
        }
        set monFile ${outDir}/${rootname}.mon
    } else {
        set monFile /tmp/[APSTmpString].mon
        APSAddToTmpFileList -ID response -fileList $monFile
    }
    if [catch {SaveMeasurementData -filename $monFile} result] {
        SetMainStatus "Error in saving measurement configuration: $result"
        return
    }
    set varSymList ""
    set varList ""
    set initList ""
    set finalList ""
    set relativeList ""
    for {set i 1} {$i<$VariableLines} {incr i} {
        if {[string length $VarPVname($i)]} {
            if [catch {SearchPVName -pvName $VarPVname($i) } result] {
                SetMainStatus $result
            } else {
                lappend varList $VarPVname($i)
                lappend initList $InitValue($i)
                lappend finalList $FinalValue($i)
                lappend relativeList $Relative($i)
                if [string length $VarSymName($i)] {
                    lappend varSymList $VarSymName($i)
                } else {lappend varSymList $VarPVname($i)}
            }
        }
    }
    if {![llength $varList] } {
        SetMainStatus "No valid variable PV names given."
        bell
        return
    }
    
    #check for duplicate PVs
    set FoundDuplicate 0
    set PVlist [lsort -ascii $varList]
    for {set i 1; set j 0} {$i<[llength $PVlist]} {incr i; incr j} {
        if {![string compare [set PV [lindex $PVlist $j]] [lindex $PVlist $i]] } {
            APSInfoWindow .info$i -name "Duplicate PV $PV" \
              -infoMessage "Found two PV of the same name ($PV). Check both lists of measurement and variable PVs.\n\nsddsexperiment not started."
            set FoundDuplicate 1
        }
    }
    if {$FoundDuplicate} {return}
    if $saveRawData {
        set tmpRoot ${outDir}/${rootname}
    } else {
        set tmpRoot /tmp/[APSTmpString]
    }
    set fileList ""
    set processList ""
    set readbacks [exec sdds2stream $monFile -col=ReadbackName -page=1]
    set dir [file dirname $responseFile]
    set bpmFileList ""
    foreach variable $varList symbol $varSymList initial $initList final $finalList relative $relativeList {
        set experimentDone 0
        if !$saveRawData {
            APSAddToTmpFileList -ID response -fileList ${tmpRoot}_$symbol.resp
        }
        if [file exist ${tmpRoot}_$symbol.resp] {
            exec rm ${tmpRoot}_$symbol.resp
        }
        lappend processList ${tmpRoot}_$symbol.resp
        set macro -macro=monitorFile=$monFile,numToAve=$numToAverage,stddev=$doStd,sigma=$doSigma,actuatorName=$variable,columnName=$symbol,nPoints=$Steps,initialValue=$initial,finalValue=$final,relative=$relative,postChangePause=$PostChangePause,interval=$interval,rampSteps=$rampSteps
        APSExecLog .runExperiment -width 80 -unixCommand \
          "sddsexperiment $input ${tmpRoot}_$symbol.resp $macro \"-comment=[APSMakeSafeQualifierStringForEval $comment]\" -verbose" \
          -cancelCallback "set experimentDone cancelled" \
          -abortCallback "set experimentDone aborted" \
          -callback "set experimentDone done"
        if !$experimentDone {
            tkwait variable experimentDone
        }
        if {$experimentDone!="done"} {
            SetMainStatus "Experiment was cancelled!"
            return
        } else {
            #process data
            if $saveRawData {
                APSAddToTmpFileList -ID response -fileList ${tmpRoot}_$symbol.slope
            }
            if [catch {exec sddsslopes ${tmpRoot}_$symbol.resp ${tmpRoot}_$symbol.slope \
                         -independentVariable=$symbol -col=[join $readbacks ,] \
                         -exclude= } result] {
                SetMainStatus "Error in getting slopes: $result; Inverse response matrix is not created"
                return
            }
            set col ""
            set color 0
            foreach meas $readbacks {
                append col " -col=$symbol,$meas -graph=sym,scale=2,connected,type=$color,sub=$color"
                incr color
            }
            eval exec sddsplot ${tmpRoot}_$symbol.resp -legend $col &
            lappend fileList ${tmpRoot}_$symbol.slope
            if [catch {exec sddsconvert ${tmpRoot}_$symbol.slope -delete=col,*Intercept -pipe=out \
                         | sddstranspose -pipe \
                         | sddsconvert -pipe=in -rename=col,OldColumnNames=BPMNames ${tmpRoot}_$symbol.bpm } result] {
                return -code error $result
            }
            lappend bpmFileList ${tmpRoot}_$symbol.bpm
        }
    }
    set renames ""
    foreach meas $readbacks {
        lappend renames ${meas}Slope=${meas}
    }
    if ![llength $bpmFileList] {
        return -code error "No reponse file generated!"
    }
    if [llength $bpmFileList]>1 {
        if [catch {eval exec sddsxref $bpmFileList $responseFile -leave=BPMNames} result] {
            return -code error $result
        }
    } else {
        file copy [lindex $bpmFileList 0] $responseFile
    }
    if [catch {exec sddspseudoinverse $responseFile -pipe=out \
                 -minimumSingularValueRatio=$minimum -oldColumnNames=Actuators \
                 | sddsprocess -pipe=in $invResponseFile "-print=par,comment,$comment" } result] {
        SetMainStatus "Error in processing data: $result; Inverse response matrix is not created"
        return
    }
    set condition [exec sdds2stream $invResponseFile -par=ConditionNumber -page=1]
    SetMainStatus "Inverse Response matrix $invResponseFile created, condition number is $condition." 
}


proc SearchPVName {args} {
    set pvName ""
    APSParseArguments {pvName}
    if ![string length $pvName] {
        return
    }
    if [catch {exec cavget -list=$pvName -pend=10} result] {
       return -code error "$pvName not found: $result"
    }
    if {$result=="?"} {
        return -code error "$pvName not found"
    }
}

proc ProcessVariableFiles {args} {
    set processList ""
    set outputFile ""
    APSParseArguments {processList outputFile}
    
    if ![llength $processList] {return -code error "Input files are not provided!"}
    if ![string length $outputFile] { return -code error "output file is not given!"}
    set firstFile [lindex $processList 0]
    if ![regexp {\.(.*)} $firstFile a variable] {
        return -code error "Invalid filename provided."
    }
    set columnNames [exec sddsquery $firstFile -col]
    set readbacks ""
    
    foreach col $columnNames {
        if {$col=="ElapsedTime" || $col=="Time" || $col=="TimeOfDay"} {continue}
        if [string compare $col $variable]==0 {continue}
        lappend readbacks $col
    }
    set tmpRoot /tmp/[APSTmpString]
    foreach file $processList {
        if ![regexp {\.(.*)} $file a variable] {
            return -code error "Invalid filename provided."
        }
        foreach readback $readbacks {
            if [catch {exec sddspfit $file -col=$variable,$readback -pipe=out \
                         | sddsconvert -pipe -rename=par,Slope=${Readback}_Slop \
                     } result] {
                return -code error $result
            }
            
        }
        
    }
}

proc SetInvResponseFile {} {
    global responseFile invResponseFile definitionFile
    if [regexp {(.*)\.} $responseFile a name] {
        set invResponseFile $name.inv
        set definitionFile $name.def
    } else {
        set invResponseFile $responseFile.inv
        set definitionFile $responseFile.def
    }
}

proc SaveDefinitionFile {args} {
    global definitionFile MeasurementLines MeasPVname MeasSymName VarPVname VarSymName VariableLines
    
    set controlNames ""
    set symbolNames ""
    for {set i 1} {$i<$MeasurementLines} {incr i} {
        set MeasSymName($i) [string trim $MeasSymName($i)]
        set MeasPVname($i) [string trim $MeasPVname($i)]
        if [string length $MeasPVname($i)] {
            lappend controlNames $MeasPVname($i)
            if ![string length $MeasSymName($i)] {
                lappend symbolNames [set MeasPVname($i)]Slope
            } else {
                lappend symbolNames [set MeasSymName($i)]Slope
            }
        }
        
    }
    for {set i 1} {$i<$VariableLines} {incr i} {
        set VarPVname($i) [string trim $VarPVname($i)]
        set VarSymName($i) [string trim $VarSymName($i)]
        if {[string length $VarPVname($i)] } {
            lappend controlNames $VarPVname($i)
            if ![string length $VarSymName($i)] {
                lappend symbolNames $VarPVname($i)
            } else {
                lappend symbolNames $VarSymName($i)
            }
        }
    }
    if [llength $controlNames] {
        if [catch {exec sddsmakedataset -defaultType=string -col=ControlName \
                     -data=[join $controlNames ,] \
                     -col=SymbolicName -data=[join $symbolNames ,] \
                     $definitionFile } result] {
            return -code error $result
        }
    }
    SetMainStatus "Definitions are save to $definitionFile!"
}
  


MakeNewMeasurementLine $measScroll
MakeNewVariableLine $varScroll
# MakeNewMeasurementLine .userFrame.measurement.frame
# MakeNewVariableLine .userFrame.variable.frame

