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

set auto_path [linsert $auto_path 0  /usr/local/oag/apps/lib/$env(HOST_ARCH)]
set auto_path [linsert $auto_path 0 /usr/local/oag/lib_patch/$env(HOST_ARCH)]
# $Log: not supported by cvs2svn $
# Revision 1.15  2000/01/21 19:08:19  sereno
# Fixed bug in controlQuantityDef file creation.  Revised test condition handling
# when reading in a configuration file.
#
# Revision 1.14  1999/09/03 21:22:40  sereno
# Added feature to print the test conditions in the info window containing the
# correctors and bpms if there are more than 5 test conditions.  N. Sereno.
#
# Revision 1.13  1999/09/03 00:33:10  sereno
# Fixed problem reading test PVs from a configuration file.  N. Sereno.
#
# Revision 1.12  1999/09/01 01:45:22  sereno
# Fixed beamline page computation (used to determine which correctors and
# bpms to select) when creating the controllaw definition file.  N. Sereno.
#
# Revision 1.11  1999/08/31 20:21:35  sereno
# Fixed problem reading leutl bpms and correctors from the bpm
# and actuator lists.  Added beamline information when writing
# a configuration file.  N. Sereno
#
# Revision 1.10  1999/08/25 07:26:46  sereno
# Fixed problem with reading actuator/bpm control and symbolic names from
# the actuator/bpm list files.  N. Sereno.
#
# Revision 1.9  1999/07/10 22:28:13  borland
# Modified procedure that makes definitions files so it uses the
# controlQuantityDefs.sdds file.
#
# Revision 1.8  1999/07/10 21:26:09  borland
# Eliminated cascade menu for LEUTL subareas.  Now have only LEUTL/LEUTL (from
# gun to undulator) and LEUTL/LINAConly.
#
# Revision 1.7  1999/06/30 19:36:08  borland
# Now looks in directory above measurement directory for install files first.
#
# Revision 1.6  1999/06/30 18:11:06  borland
# Now uses the actuatorDefinitions.sdds file for creating the control-name-to-
# symbolic-name correspondence.
#
# Revision 1.5  1999/05/01 04:33:45  sereno
# N. Sereno - Fixed bug in procedure that creates the definition file.  The
# script would give the error "No beamline chosen.  Choose a beamline" for
# vertical response matrices only.
#
# Revision 1.4  1999/05/01 04:14:39  sereno
# N. Sereno - Fixed a bug when constructing the PV names in the definition
# file for linac correctors L1:SC4:HZ and L1:SC4:VL.  These correctors have
# setpoint PVs that are different from other linac correctors.
#
# Revision 1.3  1999/03/10 19:06:36  sereno
# Added the CVS log header.
#

set CVSRevisionAuthor "\$Revision: 1.16 $ \$Author: sereno $"
set resultsDir /home/helios/oagData/controllaw/
set beamline ""
set invMatrixFile ""
set logDataDir .
set runControlPV ""
set runControlDesc ""
set gain 0.2
set pause 1
set limit 3
set steps 100
set testPVs {}
set testLows {}
set testHighs {}
set samples 1
set controlMode integral
set actionLimit 0
set logFile ""
set configFile ""
set args $argv 
set usage "Usage: quickSDDSControllaw -invMatrixFile <response-file> \
-runControlPV <PVname> -runControlDesc <string> -logData {0|1} \
-gain <number> -pause <seconds> -steps <number> -samples <number> -limit <limit> \
-testPVs <PVnameList> -testLows <numberList> -testHighs <numberList> -controlMode \{integral | proportional\} \
-actionLimit <value> -configFile <filename> -logDataDir <logdata-directory>"

set argVarList {invMatrixFile runControlPV runControlDesc \
                  gain pause steps limit \
                  testPVs testLows testHighs samples controlMode actionLimit \
                  logData configFile logDataDir}
if [llength $args] {
    if {[APSStrictParseArguments $argVarList]} {
        puts stderr $usage
        foreach variable $argVarList {
            puts stderr "$variable: [subst \$$variable]"
            }
        exit 1
    }
    if [string length $configFile] {
        if ![file exists $configFile] {
            puts stderr "$configFile not found"
            exit 1
        }
        source $configFile
    }
    if {$gain==0 || $pause<=0 || $steps<=0 || $samples<=0 || \
          $limit<=0 || [lsearch -exact [list integral proportional] $controlMode]==-1 || \
          $actionLimit<0} {
        puts stderr $usage
        foreach variable $argVarList {
            puts stderr "$variable: [subst \$$variable]"
        }
        exit 1
    }
    if {[llength $testPVs] && \
          ([llength $testPVs]!=[llength $testLows] || [llength $testPVs]!=[llength $testHighs])} {
        puts stderr $usage
        exit 1
    }
}

proc APSArrayContentsList {args} {
    set elements ""
    set arrayName ""
    set all 0

    if {[APSStrictParseArguments {elements arrayName all}] || ![string length $arrayName]} {
        return -code error "APSArrayContentsList: bad arguments"
    }
    global $arrayName
    if ![info exists $arrayName] {
        return ""
        # return -code error "APSArrayContentsList: array $arrayName not found."
    }
    set returnList ""
    if !$all {
        foreach elem $elements {
            lappend returnList [subst \$${arrayName}($elem)]
        }
    } else {
        foreach elem [array names $arrayName] {
            lappend returnList [subst \$${arrayName}($elem)]
        }
    }
    return $returnList
}

proc APSMakeControllawTestsFile {args} {
    set testPVs ""
    set testLows "" 
    set testHighs ""
    if {[APSStrictParseArguments {testPVs testLows testHighs}]} {
        return -code error "APSMakeControllawTestsFile: bad arguments"
    }
    set tmpFile /tmp/[APSTmpString]
    set tmpFile1 /tmp/[APSTmpString]
    APSAddToTempFileList $tmpFile
    if [catch {sdds open $tmpFile w} fid] {
        return -code error "APSMakeControllawTestsFile: $fid"
    }
    if {[catch \
           { sdds defineColumn $fid ControlName -type SDDS_STRING
               sdds defineColumn $fid MinimumValue -type SDDS_DOUBLE
               sdds defineColumn $fid MaximumValue -type SDDS_DOUBLE
               sdds defineColumn $fid SleepTime -type SDDS_DOUBLE
               sdds writeLayout $fid
               sdds startPage $fid [llength $testPVs]
               eval sdds setColumn $fid ControlName $testPVs
               eval sdds setColumn $fid MinimumValue $testLows
               eval sdds setColumn $fid MaximumValue $testHighs
               eval sdds setColumn $fid SleepTime [APSReplicateItem -item 3 -number [llength $testPVs]]
               sdds writePage $fid
               sdds close $fid} result]} {
        return -code error "APSMakeControllawTestsFile: $result"
    }
    return $tmpFile
}

proc APSMakeControllawDefFile {args} {

    global status
    set invMatrixFile ""

    if {[APSStrictParseArguments {invMatrixFile}] || $invMatrixFile==""} {
        return -code error "APSMakeControllawDefFile: bad arguments"
    }
    set directory [file dirname ${invMatrixFile}]
    set controlDefFile ${directory}/[lindex [split [file tail $invMatrixFile] ".inv"] 0].def
    return $controlDefFile
}

proc WriteConfigFile {args} {
    global argVarList
    set invMatrixFile ""
    set runControlPV ""
    set runControlDesc ""
    set gain 0
    set pause 1
    set limit 0
    set steps 100
    set testPVs {}
    set testLows {}
    set testHighs {}
    set samples 10
    set pretest ""
    set controlMode integral
    set actionLimit 0
    set logData 0
    set configFile ""
    set callback "APSSetVarAndUpdate status "
    set logDataDir ""

    if {[APSStrictParseArguments $argVarList] || \
          ![string length $invMatrixFile] || \
          $gain==0 || $pause<=0 || $steps<=0 || $samples<=0 || \
          $limit<=0 || [lsearch -exact [list integral proportional] $controlMode]==-1 || \
          $actionLimit<0} {
        return -code error "WriteConfigFile: Bad options."
        }
    if {[llength $testPVs]!=[llength $testLows] || \
          [llength $testPVs]!=[llength $testHighs]} {
        return -code error "WriteConfigFile: Bad test lists."
    }
    set filename [APSInfoDialog [APSUniqueName .] -name "File query" -width 40 \
                    -infoMessage "Enter a filename" \
                    -contextHelp \
                    "Enter the name of a file to which to write the configuration."]
    if ![string length $filename] {
        eval $callback {"No file name provided for writing."}
        return -code ok
    }
    if [file exists $filename] {
        if [catch {APSMultipleChoice [APSUniqueName .] \
                     -question "File exists. What do you want to do?" \
                     -labelList {Overwrite Cancel} \
                     -returnList  {Overwrite Cancel}} choice] {
            return -code error "WriteConfigFile: $choice"
        }
        if [string compare $choice Cancel]==0 return
    }
    if [catch {open $filename w} fid] {
        return -code error "WriteConfigFile: $fid"
    }
    foreach variable $argVarList {
        puts $fid "set $variable \{[set $variable]\}"
    }
    catch {close $fid}
    eval $callback {"Data written to $filename"}
}
    
proc ReadConfigFile {displayProcedure} {

    global status testIndex testPVName
    set callback "APSSetVarAndUpdate status "

    set filename [APSInfoDialog [APSUniqueName .] -name "File query" -width 40 -infoMessage \
                    "Enter a filename" -contextHelp \
                    "Enter the name of a file from which to read the configuration."]

    set status "Controllaw configuration file $filename selected."

    if ![string length $filename] {
        eval $callback {"No file name provided for reading."}
        return -code ok
    }
    if ![file exists $filename] {
        return -code error "ReadConfigFile: file not found."
    }
    
    # First, delete existing tests and widgets.

    set testPVSize [array size testPVName]
    set testPVList [array names testPVName]
    if {$testIndex>0} {
        for {set i 0} {$i<$testIndex} {incr i} {
            if {[winfo exists .userFrame.tests.frame.fg.top.t${i}.frame]} {
                DeleteTestEntry .userFrame.tests.frame.fg.top.t${i} -index $i
            }
        }
    }
    set testIndex 0
    uplevel #0 source $filename
    uplevel #0 $displayProcedure
}

proc DisplayActuatorsElementsAndTests {args} {

    global testIndex testPVName lowTest highTest
    set invMatrixFile ""
    set testPVs ""
    set testHighs ""
    set testLows ""

    if {[APSStrictParseArguments {invMatrixFile testPVs testHighs testLows}] || $invMatrixFile==""} {
        return -code error "DisplayActuatorsElementsAndTests: Bad options."
    }

    set colNames [APSGetSDDSNames -fileName $invMatrixFile -class column]
    set bpmNames [lreplace $colNames [lsearch $colNames ActuatorNames] [lsearch $colNames ActuatorNames]]
    set actuatorNames [APSGetSDDSColumn -fileName $invMatrixFile -column ActuatorNames]

    set widgetName [APSUniqueName .]
    APSInfoWindow $widgetName \
      -name "Correctors and bpms for matrix file [file tail ${invMatrixFile}]." \
      -infoMessage "Correctors - $actuatorNames\nBPMs -      $bpmNames\n\nTest Conditions\n---------------\nTest PVs - $testPVs\n\nTest low limits - $testLows\n\nTest high limits - $testHighs" -width 120

    if {$testPVs!=""} {
        set index 0
        foreach testPV $testPVs {
            AddTestEntry .t -parent .userFrame.tests.frame.fg.top \
		-low [format %.5f [lindex $testLows $index]] \
		-high [format %.2f [lindex $testHighs $index]] \
              -pvName $testPV
            incr index
        }
    }
}

proc APSRunRespMatrixControllaw {args} {

    global beamline
    set invMatrixFile ""
    set controlDefFile ""
    set runControlPV ""
    set runControlDesc ""
    set gain 0.2
    set pause 1
    set limit 3
    set steps 100
    set testPVs {}
    set testLows {}
    set testHighs {}
    set samples 1
    set pretest ""
    set controlMode integral
    set actionLimit 0
    set logData 0
    set callback "APSSetVarAndUpdate status "
    set logDataDir ""

    if {[APSStrictParseArguments {invMatrixFile controlDefFile runControlPV runControlDesc gain pause steps limit testPVs \
                                  testLows testHighs samples logData pretest callback controlMode actionLimit logDataDir}] || \
          $invMatrixFile=="" || $controlDefFile=="" || $gain==0 || $pause<=0 || $steps<=0 || $samples<=0 || \
          $limit<0 || [lsearch -exact [list integral proportional] $controlMode]==-1 || \
          $actionLimit<0} {
        return -code error "APSRunRespMatrixControllaw: Bad options."
    }

    if {[llength $testPVs]!=[llength $testLows] || [llength $testPVs]!=[llength $testHighs]} {
        return -code error "APSRunRespMatrixControllaw: Bad test lists."
    }
    set testOption ""
    set testsFile [file root $invMatrixFile].tests
    
    if [file exist $testsFile] {
	set testOption -testValues=$testsFile
    } elseif [llength $testPVs] {
        if [catch {APSMakeControllawTestsFile \
                     -testPVs $testPVs -testLows $testLows \
                     -testHighs $testHighs} testsFile] {
            return -code error "APSRunRespMatrixControllaw: $testsFile"
        }
        set testOption -testValues=$testsFile
    } 

    eval lappend cmd sddscontrollaw $invMatrixFile $testOption -controlQuantity=$controlDefFile \
      -gain=$gain -interval=$pause -steps=$steps -average=$samples \
      -delta=value=$limit -verbose -$controlMode
    if [string length $runControlPV] {
        lappend cmd -runControlPV=string=$runControlPV 
        if [string length $runControlDesc] {
            lappend cmd "-runControlDescription=string=$runControlDesc"
        }
    }
    if $actionLimit>0 {
        lappend cmd -actionLimit=value=$actionLimit
    }

    if $logData {
        set invMatrixFileName [file tail ${invMatrixFile}]
        lappend cmd -controlLogFile=${logDataDir}/${invMatrixFileName}-[exec date +%Y-%m%d-%H%M%S].log
        lappend cmd ${logDataDir}/${invMatrixFileName}-[exec date +%Y-%m%d-%H%M%S].record
    }

    if {[string length $pretest] && [catch {eval $pretest} result]} {
        return -code error "APSRunRespMatrixControllaw: $result"
    }

    set nameString "Controllaw initiated using matrix [file tail ${invMatrixFile}] from previously saved setup file."

    
    APSExecLog [APSUniqueName .] -name $nameString \
      -callback "$callback -mode ok" \
      -abortCallback "$callback -mode abort" \
      -cancelCallback "$callback -mode cancel" \
      -width 120 -height 16 -lineLimit 1000 -contextHelp \
      "Shows progress of sddscontrollaw process that is correcting a selected beamline." \
      -unixCommand $cmd
    return -code ok "Controllaw initiated."
}

set feedbackRunning 0

proc FeedbackPretest {} {
    global feedbackRunning
    if $feedbackRunning {
        return -code error "Can't start feedback---already running."
    }
    set feedbackRunning 1
}

proc FeedbackCallback {args} {
    global feedbackRunning
    set mode ""
    APSStrictParseArguments {mode}
    set feedbackRunning 0
    bell 
    switch $mode {
        ok {
            APSSetVarAndUpdate status "Feedback process has completed."
            return
        }
        default {
            APSSetVarAndUpdate status "Feedback process has been terminated."
            return
        }
    }
}

proc MakeTestsFrame {widget args} {
    set testPVList ""
    set parent ""
    set testLows ""
    set testHighs ""
    APSStrictParseArguments {testPVList parent testLows testHighs}
    
    APSFrame $widget -parent $parent -label  "Tests" 
    set w $parent$widget.frame
    APSFrameGrid .fg -parent $w -yList {top bottom}

    global testIndex 
    set testIndex 0
    set w1 $w.fg.top
    set index 0
    foreach elem $testPVList {
        AddTestEntry .t -parent $w1 \
          -low [lindex $testLows $index] \
          -high [lindex $testHighs $index] \
          -pvName $elem 
        incr index
    }

    set w2 $w.fg.bottom
    APSButton .add -parent $w2 -text Add -command \
      "AddTestEntry .t -parent $w1"
}

proc AddTestEntry {widget args} {
    set parent ""
    set pvName ""
    set low 0
    set high 0
    APSStrictParseArguments {parent pvName low high}

    global testIndex testPVName lowTest highTest
    set widget $widget$testIndex
    set testPVName($testIndex) $pvName
    set lowTest($testIndex) $low
    set highTest($testIndex) $high
    APSFrame $widget -parent $parent -label "" -relief ridge
    set w $parent$widget.frame
    APSLabeledEntry .pvName -parent $w -label "PV name: " \
      -textVariable testPVName($testIndex) -width 30
    APSLabeledEntryFrame .limits -parent $w -label "Limits (low,high): " \
      -variableList "lowTest($testIndex) highTest($testIndex)" \
      -width 10 -contextHelp "Low and high limits, respectively, for tests on >{$testPVName($testIndex)}<" \
      -orientation horizontal
    APSButton .delete -parent $w -text Delete -command \
      "DeleteTestEntry $parent$widget -index $testIndex"
    incr testIndex
}

proc DeleteTestEntry {widget args} {
    set index 0
    APSStrictParseArguments {index}
    
    global testIndex testPVName lowTest highTest
    destroy $widget 
    unset testPVName($index)
    unset lowTest($index)
    unset highTest($index)
}

proc LaunchRunControlMEDM {args} {
    set runControlPV ""
    APSStrictParseArguments {runControlPV}
    if ![string length $runControlPV] {
        APSSetVarAndUpdate status "No run control PV named, so no info."
        return
    }
    if [catch {exec medm -attach -x -macro "RCPV=$runControlPV" ./sr/psApp/APSRunControlSingle.adl & \
             } result] {
        APSSetVarAndUpdate status "$result"
    }
}

proc SetMatrixDir {args} {

    global status resultsDir dataDir beamlineFile invMatrixFile beamline importTestsFile
    global testPVs testLows testHighs testIndex testPVName

    set beamline ""
    set importTestsFile ""
    APSStrictParseArguments {beamline}

    set status "$beamline beamline selected."
    set beamlineChoice $beamline
    
    set resultsDir /home/helios/oagData/controllaw/

    switch -- $beamlineChoice {
        RFGUN {
            append resultsDir LEUTL/RFGUNonly
        } LEUTL {
            append resultsDir LEUTL/LEUTL
        } LTP {
            append resultsDir LTP/usingL5
        } PTB {
            append resultsDir PTB/PTBonly
        } BTS {
            append resultsDir BTS/BTSonly
        } LINAC {
            append resultsDir LEUTL/LINAConly
	} LEA {
	    append resultsDir LEA
        } default {
            exit 0
        }
    }

    set dataDir $resultsDir
    set beamlineFile $dataDir/measurements/${beamline}.beamline
    
    set invMatrixFile [APSFileSelectDialog .invMatrixFileSelectWidget \
                         -path ${dataDir} \
                         -pattern *.inv \
                         -title "Matrix File Selection"]

    if ![string length ${invMatrixFile}] {
        set status "Please select a valid inverse response matrix file"
        return 0
    }
    
    # First, delete existing tests and widgets.

    set testPVSize [array size testPVName]
    set testPVList [array names testPVName]
    if {$testIndex>0} {
        for {set i 0} {$i<$testIndex} {incr i} {
            if {[winfo exists .userFrame.tests.frame.fg.top.t${i}.frame]} {
                DeleteTestEntry .userFrame.tests.frame.fg.top.t${i} -index $i
            }
        }
    }
    set testIndex 0
    
    DisplayActuatorsElementsAndTests -invMatrixFile $invMatrixFile 

    set status "Inverse response matrix file $invMatrixFile chosen."
    return 1
}

# Note: by convention, this procedure has only one argument. It provides the
# parent frame into which you should pack all widgets.

APSApplication . -name "quickSDDSControllaw" \
  -version $CVSRevisionAuthor \
  -overview {This tool provides general purpose control of a beamline using a previously computed or measured response matrix.}

APSMenubarAddMenu .beamlineMenu \
  -parent .menu \
  -text "Defined Beamlines"

.menu.beamlineMenu.menu add command -label "LINAC" -command {SetMatrixDir -beamline LINAC}
.menu.beamlineMenu.menu add command -label "LEUTL" -command {SetMatrixDir -beamline LEUTL}
.menu.beamlineMenu.menu add command -label "LTP" -command {SetMatrixDir -beamline LTP}
.menu.beamlineMenu.menu add command -label "PTB" -command {SetMatrixDir -beamline PTB}
.menu.beamlineMenu.menu add command -label "BTS" -command {SetMatrixDir -beamline BTS}
.menu.beamlineMenu.menu add command -label "LEA" -command {SetMatrixDir -beamline LEA}

.menu.file.menu insert 1 command -label "Save" -command \
  {\
     if {[catch {WriteConfigFile -invMatrixFile $invMatrixFile \
                   -runControlPV $runControlPV -runControlDesc $runControlDesc \
                   -gain $gain -pause $pause -limit $limit -steps $steps \
                   -testPVs [APSArrayContentsList -all 1 -arrayName testPVName] \
                   -testLows [APSArrayContentsList -all 1 -arrayName lowTest] \
                   -testHighs [APSArrayContentsList -all 1 -arrayName highTest] \
                   -samples $samples -controlMode $controlMode \
                   -logData $logData -actionLimit $actionLimit \
                   -logDataDir ${logDataDir}} stderr]==1} {
         set status "No beamline chosen.  Choose a beamline."
     }
  }
.menu.file.menu insert 1 command -label "Save as..." -command \
  {\
     if {[catch {WriteConfigFile -invMatrixFile $invMatrixFile \
                   -runControlPV $runControlPV -runControlDesc $runControlDesc \
                   -gain $gain -pause $pause -limit $limit -steps $steps \
                   -testPVs [APSArrayContentsList -all 1 -arrayName testPVName] \
                   -testLows [APSArrayContentsList -all 1 -arrayName lowTest] \
                   -testHighs [APSArrayContentsList -all 1 -arrayName highTest] \
                   -samples $samples -controlMode $controlMode \
                   -logData $logData -actionLimit $actionLimit \
                   -logDataDir ${logDataDir}} stderr]==1} {
         set status "No beamline chosen.  Choose a beamline."
     }
  }
.menu.file.menu insert 1 command -label "Read..." -command \
  {ReadConfigFile {DisplayActuatorsElementsAndTests -invMatrixFile $invMatrixFile -testPVs $testPVs -testHighs $testHighs -testLows $testLows}}

set status "Choose a beamline to control from the menu."

APSScrolledStatus .status -parent .userFrame -width 60 -textVariable status
update

APSLabeledEntry .runControl -parent .userFrame -label "Run Control PV: " \
  -textVariable runControlPV -width 32 -contextHelp \
  "The runControl PV name, used to ensure that only one instance of this control screen is run.  Optional."
APSLabeledEntry .runControlDesc -parent .userFrame -label "Run Control Description: " \
  -textVariable runControlDesc -width 32 -contextHelp \
  "The runControl description string.  Will appear on the run control MEDM screen."
APSLabeledEntry .limit -parent .userFrame -label "Change limit: " \
  -textVariable limit -width 32 -contextHelp \
  "Maximum amount by which to change the actuator in one step."
APSLabeledEntry .gain -parent .userFrame -label "Gain: " \
  -textVariable gain -width 32 -contextHelp \
  "The ratio of the change in the readback for a given change in the actuator, multiplied by a factor less than 1."
APSLabeledEntry .actionLim -parent .userFrame -label "Action limit: " \
  -textVariable actionLimit -width 32 -contextHelp \
  "If the error is below this value, no action is taken."
APSLabeledEntry .pause -parent .userFrame -label "Pause (s): " \
  -textVariable pause -width 32 -contextHelp \
  "The interval between corrections."
APSLabeledEntry .steps -parent .userFrame -label "Steps: " \
  -textVariable steps -width 32 -contextHelp \
  "The number of correction steps."
APSLabeledEntry .samples -parent .userFrame -label "Samples: " \
  -textVariable samples -width 32 -contextHelp \
  "The number of readings of the readback to take (at 1s intervals) to obtain the value to correct."
APSRadioButtonFrame .mode -parent .userFrame -label "Mode:                         " \
    -variable controlMode -buttonList {Integral Proportional} \
    -valueList {integral proportional} -orientation horizontal \
    -contextHelp "Select integral or proportional mode for control.  Integral is the usual choice."
APSLabeledEntry .logDataDir -parent .userFrame -label "Log Data Directory: " \
  -textVariable logDataDir -width 62 -contextHelp \
  "Log data directory."

APSCheckButtonFrame .logData -parent .userFrame -label "" \
  -buttonList {"Log data at every iteration"} \
  -variableList {logData} \
  -orientation horizontal \
  -contextHelp "Select to have the data logged at every iteration."
.userFrame.logData.frame configure -relief flat

MakeTestsFrame .tests -parent .userFrame -testPVList $testPVs \
  -testLows $testLows -testHighs $testHighs

APSButton .run -parent .userFrame -text Run -command \
  { \
      APSDisableButton .userFrame.run.button
      if {[catch {APSRunRespMatrixControllaw \
                    -invMatrixFile $invMatrixFile -controlDefFile \
                    [APSMakeControllawDefFile -invMatrixFile $invMatrixFile] \
                    -runControlPV $runControlPV -runControlDesc $runControlDesc \
                    -gain $gain -pause $pause -limit $limit -steps $steps \
                    -testPVs [APSArrayContentsList -all 1 -arrayName testPVName] \
                    -testLows [APSArrayContentsList -all 1 -arrayName lowTest] \
                    -testHighs [APSArrayContentsList -all 1 -arrayName highTest] \
                    -samples $samples -controlMode $controlMode -logData $logData \
                    -actionLimit $actionLimit -callback FeedbackCallback \
                    -logDataDir ${logDataDir}} result]==1} {
          set status "$result\nAPSRunRespMatrixControllaw:  Bad arguments.\nMake sure you have chosen a valid inverse matrix file."
      }

      update idletasks
      APSEnableButton .userFrame.run.button } -contextHelp \
  "Runs controllaw."

APSButton .info -parent .userFrame -text Info -command \
  {LaunchRunControlMEDM -runControlPV $runControlPV} \
  -contextHelp "Launches MEDM screen showing the run control status, if run control is being used."

APSSetVarAndUpdate status Ready.
