#
# $Log: not supported by cvs2svn $
# Revision 1.28  2008/11/18 22:28:08  borland
# Added some comments to explain confusing code.
#
# Revision 1.27  2008/06/04 22:18:43  shang
# replaced the executable "replace" with "replaceText", since it conflicts with the new solaris and linux utilities
#
# Revision 1.26  2007/03/01 15:54:42  soliday
# Added calls to APSLogScriptAction to store initialization values
# for APSMpMatrix.
#
# Revision 1.25  2003/10/16 18:13:53  soliday
# Modified MpRespMatrixCalcInverseModified procedure so that it still works
# if the output file does not contain columns that match *:*
#
# Revision 1.24  2003/08/20 17:53:13  soliday
# Fixed issues with LTP
#
# Revision 1.23  2003/08/20 17:18:10  soliday
# Removed debugging statements.
#
# Revision 1.22  2003/03/18 20:23:47  emery
# Fixed list of run control PVs for BTS and PTB.
#
# Revision 1.21  2003/03/18 20:19:10  emery
# CHange NewTemplate file so that the sigma is recorded.
#
# Revision 1.20  2003/03/18 19:34:31  emery
# Added suspension of beamline controllaws as preexecution commands.
#
# Revision 1.19  2002/11/14 18:19:38  soliday
# Added the ability to use LTP using L4
#
# Revision 1.18  2001/03/13 20:45:36  borland
# Added RG2only system.
#
# Revision 1.17  2001/01/25 15:21:02  borland
# When forming the matrix, now sets a parameter (ForceCausality) to indicate
# whether the forceCausality program was used.
#
# Revision 1.16  2000/04/26 16:44:54  soliday
# Added APSMpStep proc options.
#
# Revision 1.15  1999/07/11 18:07:30  borland
# Revised installation yet again.  Now installs the response matrix, the
# inverse matrix, and the definitions files.  This will make it easier
# to alter the matrix (to remove correctors, say) in the future. Default
# rootnames are H<system> and V<system>.
#
# Revision 1.14  1999/07/11 15:05:57  borland
# Added the option to delete data after review.  The data isn't really deleted,
# but it is left out of the matrix.
#
# Revision 1.13  1999/07/10 22:13:32  borland
# Allows specifying the number of actuator values and points to average
# before/after each measurement, if prompt/check selected.
#
# Revision 1.12  1999/07/10 21:15:22  borland
# Uses Soliday's modifications that read new setup files.  Added user-specified
# rootname for installation.  Added dialogs for setting/changing the actuator
# half-range before/following an experiment.
#
# Revision 1.11  1999/06/30 19:30:11  borland
# Completely revamped the install procedue. Installed data is now given a single
# rootname.  It is installed in the directory above the measuremnets directory.
#
# Revision 1.10  1999/06/30 18:18:21  borland
# Commented out special code to insert rf gun correctors.
#
# Revision 1.9  1999/06/30 15:13:38  borland
# Now uses actuator tail column for constructing PV names, if the column
# is found.  Accomodates inconsistent PV names in linac.
#
#

proc APSMpMatrixInfo {} {
    return "This procedure measures the response matrix, calculates the inverse matrix, and gives the user a choice to install this inverse matrix as the matrix for the controllaw."
}

proc APSMpMatrix {args} {
    global status
    
    
    # the final result (.def and inverse matrix file) go in the resultsDir
    # the temporary results go the dataDir.
    # Setting up experiment parameters
    set beamline LINAC
    #    set beamlineList LINAC
    set plane H
    set points 5
    set postChange 2.5
    set intermeasurement 0.5
    set average 5
    set checkResults 1
    set promptExperiments 0
    set filterColumns *P*
    set excludeColumns ""
    set useSeptum None
    set installResults 0
    set actuatorList ""
    set actuatorPVList ""
    set halfRangeList ""
    set bpmList ""
    set bpmPVList ""
    set bpmUnitsList ""
    APSMpStep init -promptDialog APSMpMatrixInitDialog -vars {points postChange intermeasurement average checkResults beamline plane promptExperiments filterColumns excludeColumns useSeptum installResults actuatorList actuatorPVList halfRangeList bpmList bpmPVList bpmUnitsList} 

    set paramString ""
    foreach param {points postChange intermeasurement average checkResults beamline plane promptExperiments filterColumns excludeColumns useSeptum installResults actuatorList actuatorPVList halfRangeList bpmList bpmPVList bpmUnitsList} {
        append paramString "$param=\"[set $param]\" "
    }
    catch {APSLogScriptAction -procedure APSMpMatrix -action initialize -state ok -parameters $paramString}


    set minimum 0.01
    if {$points < 2} {
        APSMpReturn error "\# of points must be two or more"
    } 
    
    set resultsDir /home/helios/oagData/controllaw/
    #    set resultsDir /home/oxygen/SOLIDAY/tcl/matrix/
    #    set resultsDir /home/helios/SERENO/linac/leutl/
    bell
    switch -- $beamline {
        LINAC+RG1 {
            append resultsDir LINAC/RG1PlusLinac
        }
        LINAC+RG2 {
            append resultsDir LINAC/RG2PlusLinac
        }
        LINAC-RG1 {
            append resultsDir LINAC/RG1
        }
        LINAC-RG2 {
            append resultsDir LINAC/RG2
        }
        LINAC-MIN-MAG {
            append resultsDir LINAC/MinMagLinac
        }
        RG1 {
            append resultsDir LEUTL/RG1only
        }
        RG2 {
            append resultsDir LEUTL/RG2only
        } LINAC {
            append resultsDir LEUTL/LINAConly
        } LEUTL {
            append resultsDir LEUTL/LEUTL
        } LTP-4 {
            global LTPusing
            append resultsDir LTP/using$LTPusing
	    set beamline LTP
        } LTP-5 {
            global LTPusing
            append resultsDir LTP/using$LTPusing
	    set beamline LTP
        } PTB {
            append resultsDir PTB/PTBonly
            set minimum 0.1
        } BTS {
            append resultsDir BTS/BTSonly
            set minimum 0.1
        } default {
            exit 0
        }
    }
    set dataDir $resultsDir/measurement
    set beamlineFile $dataDir/${beamline}.beamline
    set templateFile $dataDir/${beamline}:${plane}NewTemplate.exp
    
    cd $dataDir
    if ![llength $actuatorList] {
        APSMpReturn error "No actuators chosen"
    }
    if ![llength $bpmList] {
        APSMpReturn error "No BPMs chosen"
    }
    if {[string compare $plane H]==0} {
        set planeName Horizontal
        set matrix R12
    } else {
        set planeName Vertical
        set matrix R34
    }
    
    set fileId [open $templateFile w 0660]
    foreach bpm $bpmList bpmPV $bpmPVList bpmUnits $bpmUnitsList {
        puts $fileId "&measurement control_name=\"$bpmPV\", column_name=\"$bpm\",\n  units=\"$bpmUnits\", number_to_average=<average>, include_sigma=1\n&end" 
    }
    puts $fileId "\n&variable control_name=\"<PV>\", column_name=\"<actuator>\",\n  units=\"A\", relative_to_original=1, index_number=0,\n  index_limit=<points>, initial_value= -<halfRange>, final_value=<halfRange>\n&end"
    puts $fileId "&list_control_quantities filename=<actuator>.def &end"
    puts $fileId "&execute outputfile=\"<actuator>.sdds\"\n  post_change_pause=<postChange> intermeasurement_pause=<intermeasurement>\n&end"
    close $fileId
    catch {exec chmod a+rw $templateFile}
    
    # this procedure establishes some default variables and arrays for parameters.
    set parameterList {PV halfRange points postChange intermeasurement average}
    MpRespMatrixSetParameterDefaultsModified \
      -parameterList $parameterList \
      -halfRange [lindex $halfRangeList 0] \
      -points $points \
      -postChange $postChange \
      -intermeasurement $intermeasurement \
      -average $average
    # this procedure must be preceded by APSMpRespMatrixSetParameterDefaults
    # in order for the arguments to be recognized.
    foreach actuator $actuatorList actuatorPV $actuatorPVList halfRange $halfRangeList {
        MpRespMatrixSetActuatorParametersModified \
          -actuator $actuator \
          -PV $actuatorPV \
          -halfRange $halfRange \
          -points $points \
          -postChange $postChange \
          -intermeasurement $intermeasurement
    }
    #  bad equipment to remove from lists
    set badActuators ""
    set badBpms ""
    
    # put pre-execution commands here
    # such as reseting the time-outs for the pulsed magnets
    # and suspending controllaws.
    APSMpStep PreExecutionCommands
    if {[string match LTP $beamline]==1} {
        set rate [exec cavget -list=LI:TM:gunTrigCountCC]
        if {[catch {exec cavput -list=${beamline}:P${plane} -list=1,2,3,4 -list=:BPM.CAVG=$rate} results]} {
	    APSMpReturn error "APSMpMatrix: $results"
	}
    } elseif {[lsearch -exact [list PTB BTS] $beamline] != -1} {
        set rcList "${beamline}:ControlLawXRC ${beamline}:ControlLawYRC"
        if [catch {exec cavget -list=[join $rcList ,] -list=.SUSP \
                     -cavputForm \
                 } controllawMode] {
            APSMpReturn error "APSMpMatrix: Problem getting ${beamline} controllaw status: $controllawMode"
            return
        }
        if [catch {exec cavput -list=[join $rcList ,] -list=.SUSP=1 \
                 } result] {
            APSMpReturn error "APSMpMatrix: Problem suspending ${beamline} controllaws: $result"
        }
    }
    # take data
    APSMpStep SDDSExperimentCommands
    # send a list of one actuator at a time so that the data can be reviewed
    # and restarted at every step
    set actuatorList1 ""
    foreach actuator $actuatorList {
        if [file exists $actuator.sdds] {
            file rename -force -- $actuator.sdds $actuator.sdds~
        }
        if {!$promptExperiments || \
              [APSMpMatrixTakeDataDialog -actuator $actuator -question \
                 "Do experiment on $actuator?"]} {
            MpRespMatrixDoExperimentModified -actuatorList $actuator -templateFile $templateFile
            catch {eval file delete -force -- [glob *~]}
            set goodData 1
            if $checkResults {
                set goodData 0
                while {!$goodData} {
                    exec sddsplot -col=$actuator,($filterColumns),y1edit=i/Sigma/ \
                      $actuator.sdds \
                      -grap=error,conn -sep "-topline=Response for $actuator" -title= &
                    exec sddsplot \
                      -col=$actuator,($filterColumns),y1edit=i/Sigma/ \
                      $actuator.sdds -legend \
                      -grap=sym,conn=subtype,vary=subtype \
                      "-topline=Response for $actuator" -title= -end \
                      -col=$actuator,($filterColumns),y1edit=i/Sigma/ \
                      $actuator.sdds -legend -mode=y=meancenter \
                      -grap=sym,conn=subtype,vary=subtype \
                      "-topline=Response for $actuator" -title= \
                      &
                    set mode [APSMpMatrixTakeDataDialog -actuator $actuator -question \
                                "Data for $actuator acceptable?" -deleteButton 1]
                    switch $mode {
                        1 {
                            set goodData 1
                        }
                        0 {
                            MpRespMatrixDoExperimentModified -actuatorList $actuator \
                              -templateFile $templateFile
                        }
                        delete {
                            set goodData 0
                            break
                        }
                    }
                }
            }
            if $goodData {
                lappend actuatorList1 $actuator
            }
        }
    }
    set actuatorList [lsort -unique $actuatorList1]
    
    # put post-execution commands here
    APSMpStep PostExecutionCommands
    if {[lsearch -exact [list PTB BTS] $beamline]!=-1} {
        if {[catch {exec cavput -list=$controllawMode} result]} {
            APSMpReturn error "APSMpMatrix: Warning: Problem setting ${beamline} controllaw mode: $result"
        }
    }
    
    APSMpStep CombiningResults
    set dateformat [clock format [clock seconds] -format "-%Y-%m%d-%T"]
    set matrixRoot ${plane}${beamline}${dateformat}
    set fileList [eval glob [APSAffix $actuatorList -suffix .def]]
    catch {eval exec sddscombine $fileList -pipe=output -merge -overWrite \
             | sddssort -pipe=input ${matrixRoot}.def -col=ControlName  -unique}
    catch {eval file delete -force -- $fileList}
    
    APSMpStep CalculatingSlopes -proc "MpRespMatrixCalcSlopesModified"
    MpRespMatrixCalcSlopesModified -actuatorList $actuatorList 
    
    APSMpStep FormingMatrix -proc "MpRespMatrixFormMatrixModified"
    MpRespMatrixFormMatrixModified -actuatorList $actuatorList -badBpms $badBpms -badActuators $badActuators \
      -outFile $matrixRoot.resp -beamlineFile $beamlineFile -filterColumns $filterColumns \
      -excludeColumns $excludeColumns
    
    APSMpStep CalculatingInverse -proc "MpRespMatrixCalcInverseModified"
    MpRespMatrixCalcInverseModified -inFile $matrixRoot.resp -outFile \
      $matrixRoot.inv -minimum $minimum

    if $installResults {
        if {[string compare $plane H]==0} {
            set newFileName H${beamline}
        } else {
            set newFileName V${beamline}
        }
        APSMpStep InstallMatrix
        MpRespMatrixInstallDialogModified .install \
          -matrixRoot $matrixRoot -sourceDir $resultsDir/measurement \
          -copyRoot $newFileName$dateformat \
          -installRoot $newFileName \
          -installDir $resultsDir
        catch {exec chmod a+rw ${resultsDir}/*}
    }
    # Clean up files
    catch {file delete -force -- $templateFile ${templateFile}~ ${beamline}.${matrix}~}
    foreach actuator $actuatorList {
        catch {file delete -force -- $actuator.sdds $actuator.sdds~ $actuator.exp $actuator.exp~}
    }
    
    APSMpReturn ok
}

proc APSMpMatrixTakeDataDialog {args} {
    set actuator ""
    set question ""
    set deleteButton 0
    APSStrictParseArguments {actuator question deleteButton} 
    set w [APSUniqueName .]
    set actuator [MpRespMatrixRemoveColonsModified $actuator]
    global retakeMpMatrixData retakeMpMatrixDialogDone $actuator
    set retakeMpMatrixData 0
    set retakeMpMatrixDialogDone 0
    APSDialogBox $w -name "Experiment Dialog for $actuator" \
      -okCommand "set retakeMpMatrixDialogDone 1" \
      -cancelCommand "set retakeMpMatrixDialogDone 0"
    label $w.userFrame.labelq -text $question
    pack $w.userFrame.labelq
    $w.buttonRow.ok.button configure -text Yes
    $w.buttonRow.cancel.button configure -text No
    if $deleteButton {
        APSDialogBoxAddButton .del -parent $w -text Delete -command \
          "destroy $w; set retakeMpMatrixDialogDone delete"
    }
    APSLabeledEntry .le1 -parent $w.userFrame -label "Actuator half-range: " \
      -textVariable ${actuator}(halfRange) 
    APSLabeledEntry .le2 -parent $w.userFrame -label "Number of actuator values: " \
      -textVariable ${actuator}(points)
    APSLabeledEntry .le3 -parent $w.userFrame -label "Readings to average: " \
      -textVariable ${actuator}(average)
    APSEnableButton $w.buttonRow.ok.button
    tkwait variable retakeMpMatrixDialogDone
    return $retakeMpMatrixDialogDone
}

# Note: by convention, this procedure has only one argument. It provides the
#    parent frame into which you should pack all widgets.
proc APSMpMatrixInitDialog {frame} {
    # Note: this dialog procedure has access only to those variables explicitly
    # passed in via the -vars option of APSMpStep. You can access them here
    # as elements of the following global array (which always has the same
    # name as the machine procedure itself.
    global APSMpMatrix
    label $frame.label -text "Choose beamline for sddsexperiment, now"
    pack $frame.label
    tk_optionMenu $frame.beamMenu APSMpMatrix(beamline) LINAC LEUTL LTP-4 LTP-5 PTB BTS RG2 RG1 LINAC-RG1 LINAC-RG2 LINAC+RG1 LINAC+RG2 LINAC-MIN-MAG
    bind $frame.beamMenu.menu <FocusOut> "APSMpMatrixInitSecondaryDialog $frame"
    pack $frame.beamMenu
    APSMpMatrixInitSecondaryDialog $frame
}

proc APSMpMatrixInitSecondaryDialog {frame} {
    global APSMpMatrix
    set beamChoice [$frame.beamMenu cget -text]
    destroy ${frame}.matrixFrame
    APSFrame .matrixFrame -parent $frame
    set frameold $frame
    set frame ${frame}.matrixFrame.frame
    if {$beamChoice == "LTP-4"} {
        global LTPusing
        set LTPusing L4
        set beamChoice LTP
    } elseif {$beamChoice == "LTP-5"} {
        global LTPusing
        set LTPusing L5
        set beamChoice LTP
    }
    APSMpMatrixInitLastDialog $frame $beamChoice	
}

proc APSMpMatrixInitLastDialog {frame beamListChoice} {
    global APSMpMatrix
    destroy ${frame}.matrixFrame2
    APSFrame .matrixFrame2 -parent $frame
    set frame ${frame}.matrixFrame2.frame
    label $frame.label -text "Enter default values for sddsexperiment"
    pack $frame.label
    APSMpMatrixInitActuatorDialog $beamListChoice
    APSMpMatrixInitBPMDialog
    set APSMpMatrix(filterColumns) *P*
    APSRadioButtonFrame .rb1 -parent $frame -label "Plane:                     " -variable \
      APSMpMatrix(plane) -buttonList "H V" -valueList "H V" -orientation "horizontal" \
      -commandList "{APSMpMatrixInitActuatorDialog $beamListChoice; APSMpMatrixInitBPMDialog; APSActuatorBPMCheckButtons -frame $frame} \
			  {APSMpMatrixInitActuatorDialog $beamListChoice; APSMpMatrixInitBPMDialog;  APSActuatorBPMCheckButtons -frame $frame}"
    APSRadioButtonFrame .rb2 -parent $frame -label "Prompt for each experiment?" \
      -variable APSMpMatrix(promptExperiments) -buttonList {Yes No} -valueList {1 0} \
      -orientation horizontal 
    APSRadioButtonFrame .rb3 -parent $frame -label "Check results?             " -variable \
      APSMpMatrix(checkResults) -buttonList {Yes No} -valueList {1 0} \
      -orientation horizontal 
    APSRadioButtonFrame .rb4 -parent $frame -label "Install Matrix:            " -variable \
      APSMpMatrix(installResults) -buttonList {Yes No}  \
      -valueList {1 0} -orientation horizontal
    if {[string compare $beamListChoice "BTS"]==0} {
        APSRadioButtonFrame .rb5 -parent $frame -label "Use septum:                " \
          -variable APSMpMatrix(useSeptum) -buttonList {ES1 ES2 None} \
          -valueList {ES1 ES2 None} -orientation horizontal \
          -commandList "{APSMpMatrixInitActuatorDialog $beamListChoice;APSActuatorBPMCheckButtons -frame $frame} {APSMpMatrixInitActuatorDialog $beamListChoice;APSActuatorBPMCheckButtons -frame $frame} {APSMpMatrixInitActuatorDialog $beamListChoice;APSActuatorBPMCheckButtons -frame $frame}"  
    }
    pack [frame $frame.actuators]
    global halfRangeStart halfRangeEnd
    APSLabeledEntry .le1 -parent $frame -label "Half Range Start (Amps):" -textVariable \
      halfRangeStart
    APSLabeledEntry .le2 -parent $frame -label "Half Range End (Amps):" -textVariable \
      halfRangeEnd
    bind $frame.le1.entry <<Change>> "+ APSMpMatrixResetHalfRange"
    bind $frame.le2.entry <<Change>> "+ APSMpMatrixResetHalfRange"
    APSLabeledEntry .le3 -parent $frame -label "\# of points:" -textVariable \
      APSMpMatrix(points)
    APSLabeledEntry .le4 -parent $frame -label "Post Change Pause:" -textVariable \
      APSMpMatrix(postChange)
    APSLabeledEntry .le5 -parent $frame -label "Intermeasurement Pause:" -textVariable \
      APSMpMatrix(intermeasurement)
    event add <<Change>> <FocusOut> <ButtonRelease-2> <KeyRelease>
    frame $frame.actuatorFrame
    pack $frame.actuatorFrame -fill both -expand true -side top
    pack [frame $frame.bpms]
    APSLabeledEntry .le6 -parent $frame -label "Filter for BPM names:" -textVariable \
      APSMpMatrix(filterColumns)
    APSLabeledEntry .le7 -parent $frame -label "\# to average:" -textVariable \
      APSMpMatrix(average)
    APSActuatorBPMCheckButtons -frame $frame
}

proc APSMpMatrixResetHalfRange {} {
    # This procedure changes the half range for various special cases.
    # For example, it increases the half range as one moves down the beamline, on the (generally incorrect)
    # assumption that the response matrix is smaller.  This is defeated by having the starting and ending
    # values of the half range the same.
    global APSMpMatrix halfRangeStart halfRangeEnd
    if {([lsearch -exact [set APSMpMatrix(actuatorList)] L5SledTiming] == -1) && 
        ([lsearch -exact [set APSMpMatrix(actuatorList)] L4SledTiming] == -1)} {
        if [catch {set halfRangeStep [expr double($halfRangeEnd - $halfRangeStart) / ([llength [set APSMpMatrix(actuatorList)]] - 1)]} results] {return}
    } elseif {([lsearch -exact [set APSMpMatrix(actuatorList)] L5SledTiming] != -1) && 
              ([lsearch -exact [set APSMpMatrix(actuatorList)] L4SledTiming] != -1)} {
        if [catch {set halfRangeStep [expr double($halfRangeEnd - $halfRangeStart) / ([llength [set APSMpMatrix(actuatorList)]] - 3)]} results] {return}	
    } else {
        if [catch {set halfRangeStep [expr double($halfRangeEnd - $halfRangeStart) / ([llength [set APSMpMatrix(actuatorList)]] - 2)]} results] {return}	
    }
    set APSMpMatrix(halfRangeList) ""
    set halfRangeCurrent $halfRangeStart
    foreach parameter [set APSMpMatrix(actuatorList)] {
        if {($parameter == "L5SledTiming") || ($parameter == "L4SledTiming")} {
            lappend APSMpMatrix(halfRangeList) .07
        } elseif {$parameter == "B:[set APSMpMatrix(useSeptum)]"} {
            lappend APSMpMatrix(halfRangeList) 4.0
        } else {
            lappend APSMpMatrix(halfRangeList) [format "%.2f" $halfRangeCurrent]
            set halfRangeCurrent [expr $halfRangeCurrent + $halfRangeStep]
        }
    }
    global MasterListhalfRange
    set MasterListhalfRange [set APSMpMatrix(halfRangeList)]
}

proc APSActuatorBPMCheckButtons {args} {
    APSStrictParseArguments {frame}
    global APSMpMatrix
    set parameterList {actuator actuatorPV halfRange bpm bpmPV bpmUnits}
    foreach parameter $parameterList {
        global MasterList$parameter
        set MasterList$parameter [set APSMpMatrix(${parameter}List)]
    } 
    set index 0
    foreach parameter [set APSMpMatrix(actuatorList)] {
        global actuatorButton$index
        set actuatorButton$index 1
        lappend actuatorButtons actuatorButton$index
        incr index
    }
    foreach parameter [set APSMpMatrix(actuatorList)] {
        lappend actuatorCommands "APSMpMatrixInitAddRemoveDialog -index $index"
    }
    set index 0
    foreach parameter [set APSMpMatrix(bpmList)] {
        global bpmButton$index
        set bpmButton$index 1
        lappend bpmButtons bpmButton$index
        incr index
    }
    foreach parameter [set APSMpMatrix(bpmList)] {
        lappend bpmCommands "APSMpMatrixInitAddRemove2Dialog -index $index"
    }
    destroy $frame.actuators.cbf1
    destroy $frame.bpms.cbf2
    APSCheckButtonFrame .cbf1 -parent $frame.actuators -label "Actuators" \
      -buttonList [set APSMpMatrix(actuatorList)] \
      -variableList $actuatorButtons \
      -commandList $actuatorCommands \
      -limitPerRow 5 -allNone 1
    APSCheckButtonFrame .cbf2 -parent $frame.bpms -label "BPMs and Current Monitors" \
      -buttonList [set APSMpMatrix(bpmList)] \
      -variableList $bpmButtons \
      -commandList $bpmCommands \
      -limitPerRow 5  -allNone 1
    
}

proc APSMpMatrixInitAddRemoveDialog {args} {
    set index 0
    APSStrictParseArguments {index}
    global APSMpMatrix actuatorButton$index
    for {set i 0} {$i < $index} {incr i} {
        global actuatorButton$i
    }
    set parameterList {actuator actuatorPV halfRange}
    foreach parameter $parameterList {
        global MasterList$parameter
        set APSMpMatrix(${parameter}List) ""
        for {set i 0} {$i < $index} {incr i} {
            if {[set actuatorButton$i]} {
                lappend APSMpMatrix(${parameter}List) [lindex [set MasterList$parameter] $i]
            }
        }
    }
}

proc APSMpMatrixInitAddRemove2Dialog {args} {
    set index 0
    APSStrictParseArguments {index}
    global APSMpMatrix bmpButton$index
    for {set i 0} {$i < $index} {incr i} {
        global bpmButton$i
    }
    foreach parameter {bpm bpmPV bpmUnits} {
        global MasterList$parameter
        set APSMpMatrix(${parameter}List) ""
        for {set i 0} {$i < $index} {incr i} {
            if {[set bpmButton$i]} {
                lappend APSMpMatrix(${parameter}List) [lindex [set MasterList$parameter] $i]
            }
        }
    }
}

proc APSMpMatrixInitActuatorDialog {beamListChoice} {
    global APSMpMatrix halfRangeStart halfRangeEnd
    if {$beamListChoice == "LEUTL"} {
        set halfRangeStart .2
        set halfRangeEnd 1.04
        set APSMpMatrix(postChange) 2.0
        set APSMpMatrix(intermeasurement) 2.0
        set APSMpMatrix(average) 5
        set APSMpMatrix(points) 5
    } elseif {$beamListChoice == "LINAC"} {
        set halfRangeStart .2
        set halfRangeEnd 1.04
        set APSMpMatrix(postChange) 2.0
        set APSMpMatrix(intermeasurement) 2.0
        set APSMpMatrix(average) 5
        set APSMpMatrix(points) 5
    } elseif {$beamListChoice == "LTP"} {
        set halfRangeStart .75
        set halfRangeEnd .75
        set APSMpMatrix(postChange) 2.0
        set APSMpMatrix(intermeasurement) 1.0
        set APSMpMatrix(average) 5
        set APSMpMatrix(points) 5
    } elseif {$beamListChoice == "PTB"} {
        set halfRangeStart 1.0
        set halfRangeEnd 1.0
        set APSMpMatrix(postChange) 2.5
        set APSMpMatrix(intermeasurement) 0.5
        set APSMpMatrix(average) 25
        set APSMpMatrix(points) 5
    } elseif {$beamListChoice == "BTS"} {
        set halfRangeStart 4.0
        set halfRangeEnd 4.0
        set APSMpMatrix(postChange) 2.5
        set APSMpMatrix(intermeasurement) 0.5
        set APSMpMatrix(average) 5
        set APSMpMatrix(points) 5
    } elseif {$beamListChoice == "RG2"} {
        set halfRangeStart 0.05
        set halfRangeEnd 0.05
        set APSMpMatrix(postChange) 2.5
        set APSMpMatrix(intermeasurement) 0.5
        set APSMpMatrix(average) 5
        set APSMpMatrix(points) 5
    } elseif {$beamListChoice == "RG1"} {
        set halfRangeStart 0.05
        set halfRangeEnd 0.05
        set APSMpMatrix(postChange) 2.5
        set APSMpMatrix(intermeasurement) 0.5
        set APSMpMatrix(average) 5
        set APSMpMatrix(points) 5
    } elseif {$beamListChoice == "LINAC-RG1"} {
        set halfRangeStart 0.1
        set halfRangeEnd 0.2
        set APSMpMatrix(postChange) 5
        set APSMpMatrix(intermeasurement) 0.5
        set APSMpMatrix(average) 10
        set APSMpMatrix(points) 5
    } elseif {$beamListChoice == "LINAC-RG2"} {
        set halfRangeStart 0.1
        set halfRangeEnd 0.2
        set APSMpMatrix(postChange) 5
        set APSMpMatrix(intermeasurement) 0.5
        set APSMpMatrix(average) 10
        set APSMpMatrix(points) 5
    } elseif {$beamListChoice == "LINAC+RG1"} {
        set halfRangeStart 0.1
        set halfRangeEnd 0.2
        set APSMpMatrix(postChange) 5
        set APSMpMatrix(intermeasurement) 0.5
        set APSMpMatrix(average) 10
        set APSMpMatrix(points) 5
    } elseif {$beamListChoice == "LINAC+RG2"} {
        set halfRangeStart 0.1
        set halfRangeEnd 0.2
        set APSMpMatrix(postChange) 5
        set APSMpMatrix(intermeasurement) 0.5
        set APSMpMatrix(average) 10
        set APSMpMatrix(points) 5
    }
    set parameterList {actuator actuatorPV halfRange}
    foreach parameter $parameterList {
        set APSMpMatrix(${parameter}List) ""
    }
    if 0 {
        if {[set APSMpMatrix(beamline)] == "LINAC"} {
            if {[string compare H [set APSMpMatrix(plane)]]==0} {
                lappend APSMpMatrix(actuatorList) L1:H2
                lappend APSMpMatrix(actuatorPVList) L1:H2:SetDacCurrentC
            } else {
                lappend APSMpMatrix(actuatorList) L1:V2
                lappend APSMpMatrix(actuatorPVList) L1:V2:SetDacCurrentC
            }
        }
    }
    if {[set APSMpMatrix(beamline)] == "BTS"} {
        if {[set APSMpMatrix(plane)] == "H"} {
            if {"None" != [set APSMpMatrix(useSeptum)]} {
                lappend APSMpMatrix(actuatorList) B:[set APSMpMatrix(useSeptum)]
                lappend APSMpMatrix(actuatorPVList) B:[set APSMpMatrix(useSeptum)]:VoltageSetSendAO
            }
        }
    }
    set beamline [set APSMpMatrix(beamline)]
    if {($beamline == "LTP-4") || ($beamline == "LTP-5")} {
        set beamline LTP
    }
    set tmpFile /tmp/[APSTmpString]
    exec sddsprocess /home/helios/oagData/controllaw/actuatorList.sdds $tmpFile \
      -filter=column,Active,1,1
    sdds load $tmpFile aFile
    file delete $tmpFile
    set z 0
    foreach x $aFile(Parameter.Beamline) y $aFile(Parameter.Plane) {
        if {($x == $beamline) && ($y == [set APSMpMatrix(plane)])} {
            break
        }
        incr z
    }
    append APSMpMatrix(actuatorList) " [lindex $aFile(Column.Correctors) $z]"
    append APSMpMatrix(actuatorPVList) " [lindex $aFile(Column.CorrectorPVs) $z]"
    APSMpMatrixResetHalfRange
}

proc APSMpMatrixInitBPMDialog {} {
    global APSMpMatrix
    set parameterList {bpm bpmPV bpmUnits}
    foreach parameter $parameterList {
        set APSMpMatrix(${parameter}List) ""
    }
    set beamline [set APSMpMatrix(beamline)]
    if {($beamline == "LTP-4") || ($beamline == "LTP-5")} {
        set beamline LTP
    }
    
    sdds load /home/helios/oagData/controllaw/bpmList.sdds bFile
    set z 0
    foreach x $bFile(Parameter.Beamline) y $bFile(Parameter.Plane) {
        if {($x == $beamline) && ($y == [set APSMpMatrix(plane)])} {
            break
        }
        incr z
    }
    append APSMpMatrix(bpmList) " [lindex $bFile(Column.BPMs) $z]"
    append APSMpMatrix(bpmPVList) " [lindex $bFile(Column.BPMPVs) $z]"
    append APSMpMatrix(bpmUnitsList) " [lindex $bFile(Column.BPMUnits) $z]"
}

proc MpRespMatrixDoExperimentModified {args} {
    global actuator
    
    set templateFile ""
    set actuatorList ""
    APSStrictParseArguments {actuatorList templateFile}
    
    if ![llength $actuatorList] {
        APSMpReturn error "error in doExperiment: no actuators specified."
    }
    
    if ![string length $templateFile] {
        APSMpReturn error "error in doExperiment: no templateFile given."
    }
    
    foreach actuator $actuatorList {
        set actuatorName $actuator
        set actuator [MpRespMatrixRemoveColonsModified $actuator]
        global $actuator
        if {[file exists $actuatorName.sdds]} {
            file rename -force -- $actuatorName.sdds $actuatorName.sdds~ 
        }
        if ![array exists $actuator] {
            APSMpReturn error "error in doExperiment: global array $actuator does not exist."
        }
        if ![info exists ${actuator}(halfRange)] {
            APSMpReturn error "error in doExperiment: variable ${actuator}(halfRange) not defined."
        }
        if ![info exists ${actuator}(points)] {
            APSMpReturn error "error in doExperiment: variable ${actuator}(points) not defined."
        }
        if ![info exists ${actuator}(postChange)] {
            APSMpReturn error "error in doExperiment: variable ${actuator}(postChange) not defined."
        }
        if ![info exists ${actuator}(intermeasurement)] {
            APSMpReturn error "error in doExperiment: variable ${actuator}(intermeasurement) not defined."
        }
        set bigUglyOption -replace=$actuatorName,$${actuator}(PV),$${actuator}(halfRange),$${actuator}(points),$${actuator}(postChange),$${actuator}(intermeasurement),$${actuator}(average)
        eval exec replaceText $templateFile $actuatorName.exp \
          -orig=<actuator>,<PV>,<halfRange>,<points>,<postChange>,<intermeasurement>,<average> \
          [subst $bigUglyOption]
        catch {exec chmod a+rw $actuatorName.exp}
        set ExperimentDone 0
        catch {file delete -force -- $actuatorName.def $actuatorName.sdds}
        TogglePulsedMagnetEnables -location Gun
        after 2000
        APSExecLog .expactuator  -unixCommand "sddsexperiment $actuatorName.exp -verb -summ  " \
          -callback {global ExperimentDone; set ExperimentDone 1} 
        .expactuator.userFrame.text.text configure -width 95
        tkwait variable ExperimentDone
        catch {exec chmod a+rw $actuatorName.sdds}
        catch {exec chmod a+rw $actuatorName.def}
    }
}

proc MpRespMatrixCalcSlopesModified {args} {
    
    APSStrictParseArguments {actuatorList}
    
    foreach actuator $actuatorList {
        catch {file delete -force -- $actuator.slopes}
        if [file exists $actuator.sdds] {
            if [catch {exec sddsconvert $actuator.sdds -pipe=out -dele=col,*Time*,StDev*,Sigma*,*sum \
                         | sddsslopes -pipe=in $actuator.slopes \
                         -independentVariable=$actuator -exclude=$actuator -asci} result] {
                return -code error "APSMpRespMatrixCalcSlopes: $result"
            }
            if ![file exists $actuator.slopes] {
                return -code error "APSMpRespMatrixCalcSlopes: $actuator.slopes not created."
            }
            catch {exec chmod a+rw $actuator.slopes}
        } else {
            return -code error "APSMpRespMatrixCalcSlopes: Not found: $actuator.sdds"
        }
    }
}

proc MpRespMatrixFormMatrixModified {args} {
    
    set actuatorList {}
    set badBpms ""
    set badActuators ""
    set outFile ""
    set beamlineFile ""
    set filterColumns "*:*P*"
    # the following is an illegal name for an SDDS data element, so it is safe
    # to use as a -delete=column default.
    set excludeColumns "1DUMMY"
    
    APSStrictParseArguments {actuatorList badBpms badActuators outFile beamlineFile filterColumns excludeColumns}
    if ![string length $excludeColumns] {
        set excludeColumns "1DUMMY"
    }
    
    if ![llength $actuatorList] {
        APSMpReturn error "error in formMatrix: no actuators specified"
    }
    
    if ![string length $outFile] {
        APSMpReturn error "error in formMatrix: no outFile given"
    }
    
    set fileList [eval glob [APSAffix $actuatorList -suffix .slopes]]
    
    eval exec sddscombine $fileList $outFile.trans -merge -overWrite
    eval file delete -force -- $fileList
    catch {exec chmod a+rw $outFile.trans}
    
    if [llength $badBpms] {
        exec sddsconvert $outFile.trans -delete=col,[join ${badbpms} ","]
    }
    
    if [file exists ${beamlineFile}] {
        exec sddsconvert $outFile.trans -pipe=out \
          -rename=col,IndependentVariable=ActuatorNames \
          -dele=col,*Intercept -edit=col,*Slope,%/Slope// \
          | sddsconvert -pipe -retain=col,ActuatorNames,$filterColumns \
          -delete=col,$excludeColumns \
          | sddstranspose -pipe -oldcolumn=BPMNames \
          | forceCausality -pipe -beamline=${beamlineFile} \
          | sddsprocess -pipe=in $outFile "-convertunits=column,*:*,mm/A,,1" \
          -redefine=parameter,ForcedCausality,1,type=short 
    } else {
        exec sddsconvert $outFile.trans -pipe=out \
          -rename=col,IndependentVariable=ActuatorNames \
          -dele=col,*Intercept -edit=col,*Slope,"%/Slope//" \
          | sddsconvert -pipe -retain=col,ActuatorNames,$filterColumns \
          -delete=col,$excludeColumns \
          | sddstranspose -pipe -oldcolumn=BPMNames \
          | sddsprocess -pipe=in $outFile "-convertunits=column,*:*,mm/A,,1" \
          -redefine=parameter,ForcedCausality,0,type=short 
    }
    catch {exec chmod a+rw $outFile}
    file delete -force -- $outFile.trans
    if [llength $badActuators] {
        exec sddsconvert $outFile -delete=col,[join ${badActuators} ","]
    }
}

proc MpRespMatrixCalcInverseModified {args} {
    set minimum 1e-2
    
    APSStrictParseArguments {inFile outFile minimum}
    
    if {[catch {exec sddspseudoinverse $inFile -pipe=out \
                    -minimum=$minimum -oldColumnNames=ActuatorNames -ascii \
                    | sddsprocess -pipe=in $outFile "-convertunits=column,*:*,A/mm,,1"} results]} {
        exec sddspseudoinverse $inFile $outFile \
            -minimum=$minimum -oldColumnNames=ActuatorNames -ascii
    }
    catch {exec chmod a+rw $outFile}
}

proc MpRespMatrixSetParameterDefaultsModified {args} {
    global parameterList
    
    APSParseArguments parameterList
    APSParseArguments $parameterList
    foreach Parameter $parameterList {
        if [info exists $Parameter] {
            global ${Parameter}Default
            set ${Parameter}Default [subst $$Parameter]
        }
    }
}

proc MpRespMatrixRemoveColonsModified {word} {
    return "[join [split $word :] {}]"
}

proc MpRespMatrixSetActuatorParametersModified {args} {
    global parameterList
    
    APSParseArguments {actuator}
    
    global [set actuator [APSMpRespMatrixRemoveColons $actuator]]
    
    foreach Parameter $parameterList {
        global ${Parameter}Default
        if {[info exists ${Parameter}Default] && 
            ![info exists ${actuator}($Parameter)]} {
            set ${actuator}($Parameter) [subst $${Parameter}Default]
        }
    }
    
    APSParseArguments $parameterList
    foreach Parameter $parameterList {
        if {![info exists $Parameter]} {
            set $Parameter [subst $${Parameter}Default]
        }
        set ${actuator}($Parameter) [subst $${Parameter}]
    }
}

proc MpRespMatrixInstallDialogModified {widget args} {
    set parent "" 
    # arguments specifying what matrix to install.
    set sourceDir ""
    set matrixRoot ""
    # arguments specifying where to install it and under what name.
    # copyRoot is the name of the copy.  installRoot is the name of the link
    # that is made to the copy
    set copyRoot ""
    set installDir ""
    set installRoot ""
    APSStrictParseArguments {parent matrixRoot copyRoot sourceDir installDir installRoot}
    
    APSDialogBox $widget -parent $parent -name "Install matrix" \
      -contextHelp "Allows one to install new matrix."
    global installComment installRootname
    set installComment ""
    set installRootname $installRoot
    label $parent$widget.userFrame.label -text "Enter comment for new matrix file"
    pack $parent$widget.userFrame.label
    APSLabeledEntry .comment -parent $parent$widget.userFrame \
      -label Comment -textVariable installComment -width 60
    APSLabeledEntry .name -parent $parent$widget.userFrame \
      -label Rootname -textVariable installRootname  -width 60
    APSButton .install -parent $parent$widget.buttonRow -text Install \
      -command "MpRespMatrixInstallModified -matrixRoot $matrixRoot \
      -copyRoot $copyRoot -installDir $installDir -installRoot $installRootname \
      -sourceDir $sourceDir -comment $installComment ; destroy $parent$widget" \
      -contextHelp "Installs the newly measured matrix for controllaw usage."
    APSDisableButton $parent$widget.buttonRow.ok.button
    tkwait window $parent$widget
}

proc MpRespMatrixInstallModified {args} {
    global status
    set matrixRoot ""
    set copyRoot ""
    set installDir ""
    set installRoot ""
    set sourceDir ""
    set comment ""
    APSStrictParseArguments {matrixRoot copyRoot installDir installRoot sourceDir comment}
    
    foreach arg [list matrixRoot copyRoot installRoot] {
        if ![string length [set $arg]] {
            APSMpReturn error "Missing filename: $arg: [set $arg]"
        }
    }
    
    set matrixRoot [file join $sourceDir $matrixRoot]
    set matrixFile $matrixRoot.resp
    set inverseFile $matrixRoot.inv
    set defFile $matrixRoot.def
    if ![file exists $matrixFile] {
        APSMpReturn error "Not found: $matrixFile"
    }
    if ![file exists $inverseFile] {
        APSMpReturn error "Not found: $inverseFile"
    }
    if ![file exists $defFile] {
        APSMpReturn error "Not found: $defFile"
    }
    set copyRoot [file join $installDir $copyRoot] 
    set installMatrix [file join $installDir $installRoot.resp]
    set installInverse [file join $installDir $installRoot.inv]
    set installDef [file join $installDir $installRoot.def]
    
    if [catch {eval file delete -force -- $copyRoot.inv $copyRoot.def 
        file copy -force -- $defFile $copyRoot.def
        file copy -force -- $matrixFile $copyRoot.resp
        exec sddsprocess $inverseFile $copyRoot.inv "-print=para,Comment,$comment" 
        eval file delete -force -- $installMatrix $installDef $installInverse
        exec ln -s $copyRoot.resp $installMatrix
        exec ln -s $copyRoot.inv $installInverse
        exec ln -s $copyRoot.def $installDef } result ] {
        APSMpReturn error "APSMpRespMatrixInstallDialog: $result"
    }
    
    APSInfoWindow .info -infoMessage "New result installed as $installMatrix and $installDef." -modal 1
}
