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



set auto_path [linsert $auto_path 0  /usr/local/oag/apps/lib/$env(HOST_ARCH)]
set auto_path [linsert $auto_path 0 /usr/local/oag/lib_patch/$env(HOST_ARCH)]
APSDebugPath
#source APSMeasureOneVariableResponse.tcl
set CVSRevisionAuthor "\$Revision: 1.24 $ \$Author: soliday $"

set actuator ""
set readback ""
set runControlPV ""
set runControlDesc ""
set gain 0
set factor 0.5
set pause 1
set hold ""
set limit 0
set steps 1000000
set testPVs {}
set testLows {}
set testHighs {}
set samples 1
set samplesPause 1
set controlMode integral
set actionLimit 0
set logFile ""
set configFile ""
set logData 0
set logDataDir /home/helios/oagData/controllaw/maintainReadback/loggedData
set args $argv 

#following variables are needed for GetGain{}
set initialValue -0.1
set finalValue 0.1
set readingType ave
set average 5
set numSteps 5
set interval 1
set changePause 5

set usage "Usage: maintainReadback -actuator <PVname> -readback <PVname> -runControlPV <PVname> \
-runControlDesc <string> -hold <value> -logData {0|1} -logDataDir <string> \
-gain <number> -pause <seconds> -steps <number> -samples <number> -samplesPause <seconds> \
-limit <limit> -testPVs <PVnameList> -testLows <numberList> -testHighs <numberList> \
-controlMode \{integral | proportional\} -actionLimit <value> -configFile <filename> -run 1"

set argVarList {actuator readback runControlPV runControlDesc gain pause steps hold limit \
                  testPVs testLows testHighs samples samplesPause controlMode actionLimit \
                  logData configFile}
set run 0
if [llength $args] {
    if {[APSStrictParseArguments [concat $argVarList run]]} {
        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 {![string length $actuator] || ![string length $readback] || \
          $gain==0 || $pause<=0 || $steps<=0 || $samples<=0 || $samplesPause<=0 \
          || ![string length $hold] || \
          $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 RunControllaw {} {
    global actuator readback runControlPV runControlDesc 
    global gain pause hold limit steps 
    global testPVName  lowTest highTest
    global samples samplesPause controlMode logData actionLimit logDataDir
    
    APSDisableButton .userFrame.run.button 
    APSDisableButton .userFrame.getgain.button 
    catch {APSRun1VariableControllaw -actuator $actuator -readback $readback \
             -runControlPV $runControlPV -runControlDesc $runControlDesc \
             -gain $gain -pause $pause -hold $hold -limit $limit -steps $steps \
             -testPVs [APSArrayContentsList -all 1 -arrayName testPVName] \
             -testLows [APSArrayContentsList -all 1 -arrayName lowTest] \
             -testHighs [APSArrayContentsList -all 1 -arrayName highTest] \
             -samples $samples -samplesPause $samplesPause -callback FeedbackCallback \
             -pretest FeedbackPretest -controlMode $controlMode \
             -logData $logData -logDataDir $logDataDir \
             -actionLimit $actionLimit} status
    update idletasks
    APSEnableButton .userFrame.run.button
    APSEnableButton .userFrame.getgain.button 
}


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 APSMakeControllawMatrixFile {args} {
    set actuator ""
    set readback ""
    if {[APSStrictParseArguments {actuator readback}] || \
          ![string length $actuator] || ![string length $readback] } {
        return -code error "APSMakeControllawMatrixFile: Bad options (actuator or readback is blank)."
    }
    set tmpFile /tmp/$readback.[pid]
    APSAddToTempFileList $tmpFile
    if [catch {sdds open $tmpFile w} fid] {
        return -code error "APSMakeControllawMatrixFile: $fid"
    }
    if {[catch \
           { sdds defineColumn $fid ControlName -type SDDS_STRING
               sdds defineColumn $fid $readback -type SDDS_DOUBLE
               sdds writeLayout $fid
               sdds startPage $fid 1
               sdds setColumn $fid ControlName $actuator
               sdds setColumn $fid $readback 1
               sdds writePage $fid
               sdds close $fid} result]} {
        return -code error "APSMakeControllawMatrixFile: $result"
    }
    return $tmpFile
}

proc APSMakeControllawTestsFile {args} {
    set testPVs ""
    set testLows "" 
    set testHighs ""
    if {[APSStrictParseArguments {testPVs testLows testHighs}]} {
        return -code error "APSMakeControllawTestsFile: unrecognized arguments"
    }
    set tmpFile /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 APSMakeControllawOffsetsFile {args} {
    set hold 0
    set readback ""
    if {[APSStrictParseArguments {hold readback}]} {
        return -code error "APSMakeControllawOffsetsFile: unrecognized"
    }
    set tmpFile /tmp/[APSTmpString]
    APSAddToTempFileList $tmpFile
    if [catch {sdds open $tmpFile w} fid] {
        return -code error "APSMakeControllawOffsetsFile: $fid"
    }
    if {[catch \
           { sdds defineColumn $fid ControlName -type SDDS_STRING
               sdds defineColumn $fid Offset -type SDDS_DOUBLE
               sdds writeLayout $fid
               sdds startPage $fid 1
               eval sdds setColumn $fid ControlName $readback
               eval sdds setColumn $fid Offset $hold
               sdds writePage $fid
               sdds close $fid} result]} {
        return -code error "APSMakeControllawOffsetsFile: $result"
    }
    return $tmpFile
}

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

    if {[APSStrictParseArguments $argVarList] || \
          ![string length $actuator] || \
          ![string length $readback] || \
          $gain==0 || \
          $pause<=0 || \
          $steps<=0 || \
          $samples<=0 || \
          $samplesPause<=0 || \
          ![string length $hold] || \
          $limit<0 || \
          [lsearch -exact [list integral proportional] $controlMode]==-1 || \
          $actionLimit<0 } {
        eval $callback {"WriteConfigFile: Bad options."}
        foreach arg $argVarList {
            eval $callback {"$arg = [set $arg]"}
        }
        bell
        return
    }
    if {[llength $testPVs]!=[llength $testLows] || \
          [llength $testPVs]!=[llength $testHighs]} {
        return -code error "WriteConfigFile: Bad test lists."
    }
    set filename [APSFileSelectDialog .save -checkValidity 0 -title "Save To File:" -contextHelp  "Enter a file in which to save 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 {
        if [info exists $variable] {
            puts $fid "set $variable \{[set $variable]\}"
        }
    }
    catch {close $fid}
    eval $callback {"Data written to $filename"}
}
    
proc ReadConfigFile {args} {
    set callback "APSSetVarAndUpdate status "
    set filename [APSFileSelectDialog .save -checkValidity 1 -title "Open a File:" -contextHelp  "Enter a file to read the configuration."]
    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."
    }
    uplevel #0 source $filename
    global testPVs testLows testHighs
    MakeTestsFrame .tests -testPVList $testPVs -testLows $testLows -testHighs $testHighs -parent .userFrame
}
    
proc APSRun1VariableControllaw {args} {
    set actuator ""
    set readback ""
    set runControlPV ""
    set runControlDesc ""
    set gain 0
    set pause 1
    set hold 0
    set limit 0
    set steps 100
    set testPVs {}
    set testLows {}
    set testHighs {}
    set samples 10
    set samplesPause 1
    set pretest ""
    set controlMode integral
    set actionLimit 0
    set logData 0
    set callback "APSSetVarAndUpdate status "
    set logDataDir ""

    if {[APSStrictParseArguments {actuator readback runControlPV runControlDesc \
                                    gain pause steps hold limit \
                                    testPVs testLows testHighs samples samplesPause \
                                    logData logDataDir \
                                    pretest callback controlMode actionLimit}]} {
        return -code error "APSRun1VariableControllaw: unrecognized options"
    }
    if ![string length $actuator] {
        return -code error "APSRun1VariableControllaw: actuator value is blank"
    }
    if ![string length $readback] {
        return -code error "APSRun1VariableControllaw: readback value is blank"
    }
    if $gain==0 {
        return -code error "APSRun1VariableControllaw: gain is zero"
    }
    if $pause<=0 {
        return -code error "APSRun1VariableControllaw: pause is zero or negative"
    }
    if $steps<=0 {
        return -code error "APSRun1VariableControllaw: steps value is zero or negative"
    }
    if $samples<=0 {
        return -code error "APSRun1VariableControllaw: samples value is zero or negative"
    }
    if $samplesPause<=0 {
        return -code error "APSRun1VariableControllaw: samplesPause value is zero or negative"
    }
    if ![string length $hold] {
        return -code error "APSRun1VariableControllaw: hold value not given"
    }
    if $limit<0 {
        return -code error "APSRun1VariableControllaw: change limit is negative"
    }
    if [lsearch -exact [list integral proportional] $controlMode]==-1 {
        return -code error "APSRun1VariableControllaw: controlMode is not integral or proportional"
    }
    if $actionLimit<0 {
        return -code error "APSRun1VariableControllaw: actionLimit is zero or negative"
    }
    if {[llength $testPVs]!=[llength $testLows] || [llength $testPVs]!=[llength $testHighs]} {
        return -code error "APSRun1VariableControllaw: Bad test lists."
    }

    if [catch {APSMakeControllawMatrixFile -actuator $actuator -readback $readback } matrixFile] {
        return -code error "APSRun1VariableControllaw: $matrixFile"
    }

    set testOption ""
    if [llength $testPVs] {
        if [catch {APSMakeControllawTestsFile \
                     -testPVs $testPVs -testLows $testLows \
                     -testHighs $testHighs} testsFile] {
            return -code error "APSRun1VariableControllaw: $testsFile"
        }
        set testOption -testValues=$testsFile
    }

    if [catch {APSMakeControllawOffsetsFile -hold $hold -readback $readback} offsetsFile] {
        return -code error "APSRun1VariableControllaw: $offsetsFile"
    }

    eval lappend cmd sddscontrollaw $matrixFile $testOption -offsets=$offsetsFile \
               -gain=$gain -interval=$pause -steps=$steps -average=$samples,interval=$samplesPause \
               -verbose -$controlMode
    if [string length $runControlPV] {
        lappend cmd -runControlPV=string=$runControlPV 
        if [string length $runControlDesc] {
            lappend cmd "-runControlDescription=string=$runControlDesc"
        }
    }
    if $limit>0 {
        lappend cmd -deltaLimit=value=$limit
    }
    if $actionLimit>0 {
        lappend cmd -actionLimit=value=$actionLimit
    }
    if $logData {
        if ![string length $logDataDir] {
            set logDataDir ./
        }
        lappend cmd $logDataDir/$readback-[exec date +%Y-%m%d-%H%M%S].log
    }
    if {[string length $pretest] && [catch {eval $pretest} result]} {
        return -code error "APSRun1VariableControllaw: $result"
    }

    APSExecLog [APSUniqueName .] -name "Feedback on $readback using $actuator" \
      -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 doing feedback on $readback using $actuator." \
      -unixCommand $cmd
    return -code ok "Feedback 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}

    set w $parent$widget.frame
    if ![winfo exists $parent$widget] {
        APSFrame $widget -parent $parent -label "Tests" 
        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
    if ![winfo exists $w2.add] {
        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 GetGain {args} {
    global initialValue finalValue average numSteps interval changePause
    global gain readback actuator factor relativeToOrig output
    global readingType

    set relativeToOrig 1
    if {$readback=="" || $actuator==""} {
       APSSetVarAndUpdate status "the readback or control pv name is not given!"
       bell
       return
    }
    APSDisableButton .userFrame.run.button 
    APSDisableButton .userFrame.getgain.button
    set output /tmp/[APSTmpString]
    if [winfo exists .gainbox] {
        destroy .gainbox
    }
    APSWindow .gainbox -name "maintainReadback:GetGain" \
      -contextHelp "Dialog box for GetGain of maintainReadback." \
      -closeButton 0
    pack [frame .gainbox.userFrame.labelFrame -relief flat] -fill x -expand true
    APSLabeledEntry .init -parent .gainbox -label "initial value:" -textVariable initialValue \
      -width 32 -contextHelp "please give the initial value of setpoint for experiment."
    APSLabeledEntry .final -parent .gainbox -label "final value:" -textVariable finalValue \
      -width 32 -contextHelp "please give the final value of setpoint for experiment."
    APSLabeledEntry .average -parent .gainbox -label "average:" -textVariable average \
      -width 32 -contextHelp "the number of averages of the readings"
    APSLabeledEntry .interval -parent .gainbox -label "interval (s):" -textVariable interval \
      -width 32 -contextHelp "the interval between two readings"
    APSLabeledEntry .pause -parent .gainbox -label "post pause (s):" -textVariable changePause \
      -width 32 -contextHelp "pause time after each change of setpoint"
    APSLabeledEntry .step -parent .gainbox -label "steps :" -textVariable numSteps \
      -width 32 -contextHelp "number of values of setpoint"
    APSLabeledEntry .factor -parent .gainbox -label "gain factor:" -textVariable factor \
      -width 32 -contextHelp "the factor of gain, gain=changeInReadback/changeInControl*factor"
    APSRadioButtonFrame .reading -parent .gainbox -label "reading type" -variable \
      readingType -buttonList {Ave. Min. Max. Midpt.} -valueList {ave min max midpt} \
      -orientation horizontal -contextHelp "Choose the type of readback statistic to use."
    APSRadioButtonFrame .relative -parent .gainbox -label "relative to original?" -variable \
      relativeToOrig -buttonList {Yes No} -valueList {1 0} -orientation horizontal\
      -contextHelp "the changed setpoint value is relative to its original value, yes or no?"
    
    APSDialogBoxAddButton .run -parent .gainbox -text "run..." -command {
        APSDisableButton .gainbox.buttonRow.run.button
        APSDisableButton .userFrame.run.button 
        APSDisableButton .userFrame.getgain.button
        APSSetVarAndUpdate status "Running experiment ..."
        if [catch {APSMeasureOneVariableResponse -control $actuator -readback $readback \
            -average $average -initialValue $initialValue -finalValue $finalValue \
            -changePause $changePause -interval $interval -steps $numSteps \
            -relativeToOrig $relativeToOrig -output $output -interactive 1 } result] {
            APSSetVarAndUpdate status "$result"
            destroy .expx
        } else {
            set gain [expr (1./$result)*$factor]
            APSSetVarAndUpdate status "GetGain done."
            exec sddsplot $output -col=$actuator,$readback -graphic=symbol,scale=2 \
             -col=$actuator,${readback}Fit -graphic=line -end &
            APSSetVarAndUpdate status "The fit slope is $result."
            APSSetVarAndUpdate status "Maximum \"gain\" is [expr 1./$result]"
            APSSetVarAndUpdate status "Using $gain"
            destroy .expx
        }
        
        APSEnableButton .userFrame.run.button 
        APSEnableButton .userFrame.getgain.button
        APSEnableButton .gainbox.buttonRow.run.button
   }
   APSAddToTempFileList $output
}

APSApplication . -name "maintainReadback-$actuator-$readback" \
 -version $CVSRevisionAuthor \
  -overview {This tool provides general-purpose control of a single readback using a single actuator.}

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

set width 60
APSLabeledEntry .readback -parent .userFrame -label "Readback: " \
  -textVariable readback -width $width -contextHelp \
  "The PV name that will be controlled."
APSLabeledEntry .actuator -parent .userFrame -label "Actuator: " \
  -textVariable actuator -width $width -contextHelp \
  "The PV name used for controlling the readback."
APSLabeledEntry .runControl -parent .userFrame -label "Run Control PV: " \
  -textVariable runControlPV -width $width -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 $width -contextHelp \
  "The runControl description string.  Will appear on the run control MEDM screen."
APSLabeledEntry .hold -parent .userFrame -label "Hold: " \
  -textVariable hold -width $width -contextHelp \
  "Value at which to hold the readback."
APSLabeledEntry .limit -parent .userFrame -label "Change limit: " \
  -textVariable limit -width $width -contextHelp \
  "Maximum amount by which to change the actuator in one step."
APSLabeledEntry .gain -parent .userFrame -label "Gain*Response: " \
  -textVariable gain -width $width -contextHelp \
  "The reciprocal of 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 $width -contextHelp \
  "If the error is below this value, no action is taken."
APSLabeledEntry .pause -parent .userFrame -label "Pause (s): " \
  -textVariable pause -width $width -contextHelp \
  "The interval between corrections."
APSLabeledEntry .steps -parent .userFrame -label "Steps: " \
  -textVariable steps -width $width -contextHelp \
  "The number of correction steps."
APSLabeledEntry .samples -parent .userFrame -label "Samples: " \
  -textVariable samples -width $width -contextHelp \
  "The number of readings of the readback to take (at Pause/Samples intervals) to obtain the value to correct."
APSLabeledEntry .samplesPause -parent .userFrame -label "Pause/Samples (s): " \
  -textVariable samplesPause -width $width -contextHelp \
  "The interval between two readings of the readback."
APSLabeledEntry .dir -parent .userFrame -label "Log data directory:" -width $width \
  -textVariable logDataDir
APSButton .daily -parent .userFrame.dir -packOption "-anchor e" \
      -text "daily" -size small \
      -command {set logDataDir [APSGoToDailyDirectory]}
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."
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 \
    RunControllaw -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."
.menu.file.menu insert 1 command -label "Save" -command \
  {WriteConfigFile -actuator $actuator -readback $readback \
     -runControlPV $runControlPV -runControlDesc $runControlDesc \
     -gain $gain -pause $pause -hold $hold -limit $limit -steps $steps \
     -testPVs [APSArrayContentsList -all 1 -arrayName testPVName] \
     -testLows [APSArrayContentsList -all 1 -arrayName lowTest] \
     -testHighs [APSArrayContentsList -all 1 -arrayName highTest] \
     -samples $samples -samplesPause $samplesPause -controlMode $controlMode \
     -logData $logData -logDataDir $logDataDir \
     -actionLimit $actionLimit } 
.menu.file.menu insert 1 command -label "Save as..." -command \
  {WriteConfigFile -actuator $actuator -readback $readback \
     -runControlPV $runControlPV -runControlDesc $runControlDesc \
     -gain $gain -pause $pause -hold $hold -limit $limit -steps $steps \
     -testPVs [APSArrayContentsList -all 1 -arrayName testPVName] \
     -testLows [APSArrayContentsList -all 1 -arrayName lowTest] \
     -testHighs [APSArrayContentsList -all 1 -arrayName highTest] \
     -samples $samples -samplesPause $samplesPause -controlMode $controlMode \
     -logData $logData -logDataDir $logDataDir \
     -actionLimit $actionLimit } 
.menu.file.menu insert 1 command -label "Read..." -command \
  ReadConfigFile

APSButton .getgain -parent .userFrame -text "Measure response..." -command "GetGain"

APSSetVarAndUpdate status Ready

if $run {
    RunControllaw
}
