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

# Modified version of SROrbitControllaw

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)]

if {[info exists tcl_pkgPath]} {
    set expectLib [file join [lindex $tcl_pkgPath 0] expect5.45.3 libexpect5.45.3.so]
    if {($tcl_platform(os) == "Linux") && [file exists $expectLib]} {
        load $expectLib
    } else {
        if {[catch {package require Expect} results]} {
            APSAlertBox [APSUniqueName .] -errorMessage "$results"
            exit 1
        }
    }
} else {
    if {[catch {package require Expect} results]} {
        APSAlertBox [APSUniqueName .] -errorMessage "$results"
        exit 1
    }
}
APSDebugPath

# S-MOFB:AGG:{P}:BpmGroup40M
# S-MOFB:AGG:{P}:BpmErrorGroup40M
# S-MOFB:AGG:{P}:BpmGroup80M
# S-MOFB:AGG:{P}:BpmErrorGroup80M
# S-MOFB:AGG:{P}:BpmGroup160M
# S-MOFB:AGG:{P}:BpmErrorGroup160M
# S-MOFB:AGG:{P}:BpmGroup240M
# S-MOFB:AGG:{P}:BpmGroupError240M

set totalBPMOptions [list 40 80 160 240]

proc SetSROrbitStatus {text args} {
    global SROrbitStatus
    set code ""
    APSParseArguments {code}
    set SROrbitStatus "[exec date +%H:%M:%S] $text"
    update
    switch $code {
        error -
        warning {
            bell
        }
        default {
        }
    }
}

proc SetDefaultPars {args} {
    global OC ready validatedFile validatedTimeStamp
    set ready 0
    foreach plane {h v} {
        set validatedFile($plane) ?
        set validatedTimeStamp($plane) 0
    }

    #common parameters
    set OC(steps) 100000000
    set OC(h.gain) 0.2
    set OC(v.gain) 0.2
    set OC(rate) 50
    set OC(hold) 0
    set OC(BPMLimit) 1500
    set OC(hcorrLimitMargin) 0
    set OC(vcorrLimitMargin) 0
    set OC(dataDir) /home/helios/oagData/sr/orbitControllaw/lattices/default
    set OC(RCTimeout) 30
    set OC(triggerModulus) 1
    set OC(pvTest) 1
    set OC(dryRun) 0
    set OC(verbose) 0
    set OC(logStats) 0
    set OC(logGlitch) 0
    set OC(logActuators) 0
    set OC(logAll) 0
    set OC(outputRoot) [pwd]/mofbLog

    #plane specific
    set OC(h.responseDir) h.defaultMOFB
    set OC(v.responseDir) v.defaultMOFB

    set OC(h.controllawRCPV) "S:RC:OrbitControlLawXC"
    set OC(h.controllawRCDesc) "SR x-orbit MOFB"
    set OC(h.pvtestRCPV) S-MOFB:RC:X:TestsC
    set OC(h.pvtestRCDesc) "SR x-orbit MOFB tests"

    set OC(v.controllawRCPV) "S:RC:OrbitControlLawYC"
    set OC(v.controllawRCDesc) "SR y-orbit MOFB"
    set OC(v.pvtestRCPV) S-MOFB:RC:Y:TestsC
    set OC(v.pvtestRCDesc) "SR y-orbit MOFB tests"

    set OC(h.DCCTLimit) 0.5
    set OC(v.DCCTLimit) 0.5
    
    #reference file
    set OC(defaultSetpointRef) /home/helios/oagData/SCR/snapshots/SR/SR-UserBeamPreferred.gz
    set OC(defaultGainRef) /home/helios/oagData/SCR/snapshots/SBPMs/SBPMs-Preferred.gz
    set OC(defaultOffsetRef) /home/helios/oagData/SCR/snapshots/SR/SR-BPMOffsetReference.gz
    set OC(defaultCorrRef) /home/helios/oagData/SCR/snapshots/SR/SR-UserBeamPreferred.gz
    
    set OC(correctorReferenceFile) $OC(defaultCorrRef)
    set OC(setpointReferenceFile)  $OC(defaultSetpointRef)
    set OC(gainReferenceFile)      $OC(defaultGainRef)
    set OC(offsetReferenceFile)    $OC(defaultOffsetRef)

}


proc MakeOptionAndParameterFrame {widget args} {
    set parent ""
    APSParseArguments {parent}

    global OC
    
    APSFrame $widget -parent $parent -label "sddscontrollaw options and test parameters" \
      -contextHelp "Frame to specify command line options for the sddscontrollaw command and test parameters."
    
    set widgetList [APSTabFrame .options -parent $parent$widget.frame -label "" \
                      -labelList {Configuration Options "Expert Options"} \
                      -width 900 -height 220 -packOption "-expand yes -fill both"]
    
    pack $parent$widget.frame.options -side top

    # Configuration frame
    set index 0
    set w0 [lindex $widgetList $index]
    incr index
    set w $w0
    global rateList
    if [catch {exec cavget -info -list=S39-DAQTBT:MOFB:TurnsPerFeedbackIntervalC | fgrep Index | editstring -stream -edit="Z(S/H/10d"} rateList] {
        return -code error "Error: $rateList"
    }
    set sortedRateList [lsort -integer $rateList]
    APSRadioButtonFrame .rate -parent $w -label "Rate (Hz) " -orientation horizontal -variable OC(rate) \
                        -buttonList $sortedRateList -valueList $sortedRateList
    APSRadioButtonFrame .mode -parent $w -label "Mode: " -orientation horizontal -variable OC(hold) \
                        -buttonList [list "Hold at present" "Zero BPM errors"] -valueList [list 1 0]
    foreach plane {h v} Plane {H V} {
        APSLabeledEntry .${plane}config  -parent $w -label "$Plane configuration: "  \
	                -textVariable OC($plane.responseDir) -width 40 \
	                -contextHelp \
	                "Name of the directory for the response file for the $Plane plane which can be used with sddscontrollaw."
        APSButton .${plane}pick -parent $w.${plane}config -text "F " -size small -command "PickFileFromDir -plane $plane"
        APSButton .${plane}install -parent $w.${plane}config -text "I " -size small -command "InstallFile -plane $plane"
    }
    APSButton .validate -parent $w -command ValidateConfigurations \
              -text Validate -contextHelp "Validate the H and V configurations to ensure consistency."
    APSButton .setup -parent $w -command SetupConfiguration \
              -text "Set up" -contextHelp "Set up the IOCs for the named H and V configurations and rate."
    APSButton .checkwf -parent $w -text "Check for frozen waveforms" -command "CheckForFrozenWaveforms" \
	-contextHelp "Do a quick check to see if waveforms are updating."

    # Options frame
    set w0 [lindex $widgetList $index]
    incr index
    APSFrameGrid .grid -parent $w0 -xList {x1 x2} 
    set w $w0.grid.x1
    APSLabeledEntry .steps -parent $w -label "Steps" \
      -textVariable OC(steps) -width 10 \
      -contextHelp "Enter the number of steps to perform."
    APSLabeledEntry .hgain -parent $w -label "Gain for H Plane" \
      -textVariable OC(h.gain) -width 10 \
      -contextHelp "Enter the gain (i.e. fraction of correction at each interval)"
    APSLabeledEntry .vgain -parent $w -label "Gain for V Plane" \
      -textVariable OC(v.gain) -width 10 \
      -contextHelp "Enter the gain (i.e. fraction of correction at each interval)"

    APSLabeledEntry .timeout -parent $w -label "runControl timeout (s) " \
      -textVariable OC(RCTimeout) -width 10 -contextHelp \
      "Enter the run control timeout in seconds."

    APSLabeledEntry .triggerModulus -parent $w -label "Trigger downsampling modulus " \
      -textVariable OC(triggerModulus) -width 10 -contextHelp \
      "Enter the modulus for trigger downsampling. 1 means use all triggers. 10 means use 1/10. Etc."

    APSRadioButtonFrame .dryRun -parent $w -label "Dry run: " -valueList "0 1" -buttonList "No Yes" -variable OC(dryRun) -orientation horizontal \
      -contextHelp "If Yes, then nothing is written to the actuators."
    APSRadioButtonFrame .verbose -parent $w -label "Verbose: " -valueList "0 1" -buttonList "No Yes" -variable OC(verbose) -orientation horizontal \
      -contextHelp "If Yes, then output is written at each step. Not recommended for high rates."

    set w $w0.grid.x2
    
    APSLabeledEntry .currLimitH -parent $w -label "S35DCCT lower limit (mA) for H plane" \
      -textVariable OC(h.DCCTLimit) -width 10 \
      -contextHelp "Enter the lower limit of S35DCCT readback below which the sddscontrollaw correction iteration is skipped."
    APSLabeledEntry .currLimitV -parent $w -label "S35DCCT lower limit (mA) for V plane" \
      -textVariable OC(v.DCCTLimit) -width 10 \
      -contextHelp "Enter the lower limit of S35DCCT readback below which the sddscontrollaw correction iteration is skipped."
    APSLabeledEntry .bpmLimit -parent $w -label "BPM max. limit (um)" \
      -textVariable OC(BPMLimit) -width 10 \
      -contextHelp "Enter the range of bpm readback outside of which the sddscontrollaw correction iteration is skipped."
    APSRadioButtonFrame .logStats -parent $w -label "Log stats: " -valueList "0 1" -buttonList "No Yes" -variable OC(logStats) -orientation horizontal \
      -contextHelp "If Yes, then statistics logging is enabled. Not recommended for high rates."
    APSRadioButtonFrame .logActuators -parent $w -label "Log actuators: " -valueList "0 1" -buttonList "No Yes" -variable OC(logActuators) -orientation horizontal \
      -contextHelp "If Yes, then actuator logging is enabled. Not recommended for high rates."
    APSRadioButtonFrame .logGlitch -parent $w -label "Log glitches: " -valueList "0 1" -buttonList "No Yes" -variable OC(logGlitch) -orientation horizontal \
      -contextHelp "If Yes, then glitch logging is enabled. Not recommended for high rates."

    # Options frame
    set w0 [lindex $widgetList $index]
    incr index

    APSRadioButtonFrame .logAll -parent $w0 -label "Log all: " -valueList "0 1" -buttonList "No Yes" -variable OC(logAll) -orientation horizontal \
      -contextHelp "If Yes, then full logging is enabled. Not recommended for high rates."

    APSLabeledEntry .outputRoot -parent $w0 -label "Output root name (incl. dir.): " \
      -textVariable OC(outputRoot) -width 80 -contextHelp \
      "Provide <dirName>/<rootname> for full output file."

    APSLabeledEntry .hTestRC -parent $w0 -label "H-plane pv-test RC record:" \
        -textVariable  OC(h.pvtestRCPV) -width 20
    APSLabeledEntry .hClawRC -parent $w0 -label "H-plane CLAW RC record:" \
        -textVariable  OC(h.controllawRCPV) -width 20
    APSLabeledEntry .vTestRC -parent $w0 -label "V-plane pv-test RC record:" \
        -textVariable  OC(v.pvtestRCPV) -width 20
    APSLabeledEntry .vClawRC -parent $w0 -label "V-plane CLAW RC record:" \
        -textVariable  OC(v.controllawRCPV) -width 20

    return 0
}

proc InstallFile {args} {
    set plane ""
    APSParseArguments {plane}
    global OC
    set oldDir [pwd]
    cd $OC(dataDir)

    set file $OC($plane.responseDir)/config
    if ![file exist $file] {
	return -code error "$file does not exist, can not install!"
    }
    if {![APSMultipleChoice [APSUniqueName .] \
            -question "Link $OC($plane.responseDir) to $plane.defaultMOFB? Are you sure?" -returnList {1 0} \
            -labelList {Yes No}]} {
        return
    }
    file delete -force ${plane}.defaultMOFB
    exec ln -s $OC($plane.responseDir) $plane.defaultMOFB
    cd $oldDir
}

proc PickFileFromDir {args} {
    set plane ""
    APSParseArguments {plane}
    global OC
    set desc 0
    set descFile  [set OC(dataDir)]/${plane}configDesc.sdds
    if [file exist $descFile] {
	set desc 1
    }
    if $desc {
	set itemList [split [exec sdds2stream $descFile -col=Description] \n]
	set choiceList [exec sdds2stream $descFile -col=Filename]
    } else {
	set oldDir [pwd]
	cd $OC(dataDir)
	set fileList [lsort -descreaing [glob -complain ${plane}.*]]
	if ![llenght $fileList] {
	    return -code error "No configuration found!"
	}
	set choiceList ""
	set itemList ""
	foreach file $fileList {
	    if [file isdirectory $OC(dataDir)/$file] {
		lappend choiceList $file
		if {[file type $OC(dataDir)/$file]=="link"} {
		    set link [file tail [file readlink $OC(dataDir)/$file]]
		    lappend itemList "$file -> $link"
		} else {
		    set description [lindex [exec sdds2stream -par=Description $OC(dataDir)/$file/config] 0]
		    lappend itemList "$file -> $description"
		}
	    }
	}
    }
    
    if ![llength $choiceList] {
	return -code error "No configuration found1"
    }
    set OC($plane.responseDir) [APSChooseItemFromList -name ChoiceList -height 30 \
                 -itemList $itemList -returnList $choiceList -multiItem 0]
    
}

proc BringUpBPMADT {args} {
    set plane ""
    APSParseArguments {plane}

   
    exec adt -f /home/helios/oagData/ADTFiles/srBpm/sr.bpm.lowPass1s.error.pv -a /home/helios/oagData/ADTFiles/srBpm &

}

proc AbortProcess {args} {
    set plane ""
    set type controllaw
    APSParseArguments {plane type}
    global OC
    set RCPV $OC($plane.${type}RCPV)
    if [catch {exec cavget -list=$RCPV.RUN } running] {
	return -code error "Error readding $plane runControlPV: $result"
    }
    if !$running {
	SetSROrbitStatus "$plane $type is not running."
	return
    }
    SetSROrbitStatus "Aborting $plane $type..."
    if [catch {exec cavput -list=$RCPV.ABRT=1 } result] {
	return -code error "Error abort $plane $type: $result"
    }
    exec cawait -waitFor=$RCPV.RUN,equalTo=0
    exec cavput -list=$RCPV.CLR=1
    SetSROrbitStatus "$plane $type aborted."
}

proc InfoDisplay {args} {
    set plane ""
    set type controllaw
    APSParseArguments {plane type}
    global OC
    set RCPV $OC($plane.${type}RCPV)
    SetSROrbitStatus "plane=$plane type=$type RCPV=$RCPV"
    exec medm -local -x -macro RCPV=$RCPV /usr/local/iocapps/adlsys/sr/psApp/APSRunControlSingle.adl  &
}

proc Test {args} {
    set plane ""
    APSParseArguments {plane}
    global OC
    set dir $OC(dataDir)/$OC(${plane}.responseDir)
    
    set testsFile $dir/tests
    if ![file exists $testsFile] {
        SetSROrbitStatus "Tests file not present. Try the Generate button."
        return
    }
    
    set tmpRoot /tmp/[APSTmpString]
    if [catch {exec sddssnapshot $testsFile -pipe=out -nameOfData=Value \
		   | sddsprocess -pipe=in $tmpRoot.1 "-test=col,Value MinimumValue < Value MaximumValue > ||" \
		   -nowarnings } result] {
	return -code error "Error check tests: $result"
    }
    APSAddToTmpFileList -ID oc -fileList $tmpRoot.1
    
    set rows [exec sdds2stream -rows=bare $tmpRoot.1]
    if !$rows {
	SetSROrbitStatus "$plane tests passed."
	return
    }
    SetSROrbitStatus "$plane tests failed."  -code error 
    if [catch {exec sddsprintout $tmpRoot.1 $tmpRoot.print \
		   -col=ControlName,format=%30s -col=Value,format=%15.0f \
		   -col=MinimumValue,format=%15.0f -col=MaximumValue,format=%15.0f \
		   "-title=$plane tests out of range." } result] {
	return -code error "Error printing out of range PVs: $result"
    }
    APSFileDisplayWindow [APSUniqueName .fileDisp] -fileName $tmpRoot.print -width 100 -height 30 -deleteOnClose 1
}

proc CheckForFrozenWaveforms {args} {
    global nTotalBPMs
    SetSROrbitStatus "Checking waveforms: wait about 10 seconds"
    set tmpRoot /tmp/[APSTmpString]
    APSAddToTempFileList $tmpRoot.sdds $tmpRoot.txt
    exec sddswmonitor -pvNames=S-MOFB:AGG:X:BpmErrorGroup${nTotalBPMs}M,S-MOFB:AGG:Y:BpmErrorGroup${nTotalBPMs}M -interval=0.1 -steps=100 $tmpRoot.sdds
    exec sddsenvelope $tmpRoot.sdds -pipe=out -copy=Index -standardDeviation=*:* \
        | sddsprocess -pipe \
      "-define=column,Suspect,S-MOFB:AGG:X:BpmErrorGroup${nTotalBPMs}MStDev 0 == S-MOFB:AGG:Y:BpmErrorGroup${nTotalBPMs}MStDev 0 == || ? 1 : 0 $ ,type=short" \
      "-process=Suspect,Sum,SuspectSum" \
      | sddsprintout -pipe=in -parameter=SuspectSum,format=%4.0f \
      -column=Index -column=*StDev* -column=Suspect $tmpRoot.txt
    file delete $tmpRoot.sdds
    APSFileDisplayWindow [APSUniqueName .wfCheckDisplay] -fileName $tmpRoot.txt -width 100 -height 30 -deleteOnClose 1
}

proc MakePlaneActionFrame {args} {
    set plane ""
    set parent ""
    APSParseArguments {plane parent}
    global OC

    APSFrame .common -parent $parent -width 900 -height 75 -label "common" \
             -packOption {-side top -anchor nw} 
    set w $parent.common.frame
    APSButton .gen -parent $w -text "Generate" -command "GenerateControllawFiles -plane $plane" \
	-contextHelp "Generate controllaw files. Useful for testing for problems ahead of starting controllaw."
    APSButton .mofbMEDM -parent $w -text "Status Display"  -command "exec medm -local -x /C2/screens/adl/iocs/mofb/mofb.adl &"
    
    APSFrame .test -parent $parent -width 900 -height 75 -label "sddspvtest" \
             -packOption {-side top -anchor nw} 
    set w $parent.test.frame
    APSButton .start1 -parent $w -text "Start" -command "StartPVTest -plane $plane" \
	-contextHelp "Start PV test subprocess."
    APSButton .abort -parent $w -text "Abort" -command "AbortProcess -type pvtest -plane $plane"
    APSButton .info -parent $w -text "Info" -command "InfoDisplay -type pvtest -plane $plane"
    APSButton .test -parent $w -text "Test once" -command "Test -plane $plane"

    
    APSFrame .controllaw -parent $parent -width 900 -height 75 -label "sddscontrollaw" \
             -packOption {-side top -anchor nw} 
    set w $parent.controllaw.frame
    APSButton .start -parent $w -text "Full Start" -command "StartControllaw -regenerate 1 -plane $plane" \
	-contextHelp "Re-generate controllaw files and start controllaw."
    APSButton .start1 -parent $w -text "Start" -command "StartControllaw -plane $plane" \
	-contextHelp "Start controllaw without re-generating controllaw files."
    
    APSButton .abort -parent $w -text "Abort" -command "AbortProcess -type controllaw -plane $plane"
    APSButton .info -parent $w -text "Info" -command "InfoDisplay -type controllaw -plane $plane"
    
    APSButton .adt -parent $w -text "BPM ADT" -command "BringUpBPMADT -plane $plane"
    
}


proc MakeActionFrame {widget args} {
    set parent ""
    APSParseArguments {parent}

    APSFrame $widget -parent $parent -label "Action Frame" -contextHelp "Frame for running orbit correction and associated tests."
    set wList [APSTabFrame .plane -parent $parent$widget.frame -label "" \
	-labelList {Horizontal Vertical} -width 900 -height 160 -packOption "-expand yes -fill both"]
    
    MakePlaneActionFrame -plane h -parent [lindex $wList 0]
    MakePlaneActionFrame -plane v -parent [lindex $wList 1]
}

proc APSSRGenerateMofbControllawFiles {args} {
    global OC
    set config ""
    set currLimit 0.5
    set BPMLimit 10000
    set corrLimitMargin 0.5
    set dataDir /home/helios/oagData/sr/orbitControllaw/lattices/default
    APSParseArguments {dataDir config BPMLimit corrLimitMargin currLimit}
    set oldDir [pwd]
    set plane [string index $config 0]
    
    if {$plane!="h" && $plane!="v"} {
	return -code error "APSSRGenerateMofbControllawFiles1: invalid config provided, should be \[hv\].*"
    }
    set dir $dataDir/$config
    if ![file exist $dir] {
	return -code error "$dir does not exist!"
    }
    
    set config $dir/config
    if {![file exist $config]} {
	return -code error "$dir/config does not exist, please create the configuration first!"
    }
    if ![file exist $dir/irm] {
	return -code error "$dir/irm does not exist, please use the OC configuration tool to compute the irm first!"
    }
    SetSROrbitStatus "Generating controllaw files for $plane..."

    set tmpRoot /tmp/[APSTmpString]
    switch $plane {
	h {
	    set coord x
            set Coord X
	}
	v {
	    set coord y
            set Coord Y
	}
    }

    APSAddToTmpFileList -ID config -fileList "$tmpRoot.corr $tmpRoot.defns1 $tmpRoot.defns2 $tmpRoot.curr.tests $tmpRoot.bpm.tests"
    APSAddToTmpFileList -ID config -fileList "$tmpRoot.corr.hi $tmpRoot.corr.lo $tmpRoot.corr.tests $tmpRoot.bpmOrder"
    
    # Generate definitions file, for correctors only
    SetSROrbitStatus "Generating definitions file..."
    if [catch {exec sddsprocess $dir/config -pipe=out -match=param,NameType=CorrectorNames \
                 -print=col,ControlName,%s:PS:SetCurrentC,Name \
                 | tee $tmpRoot.corr \
                 | sddsconvert -pipe=in -rename=col,Name=SymbolicName $tmpRoot.defns1 } result] {
	return -code error "APSSRGenerateMofbControllawFiles (1a): error generating definitions file: $result"
    }
    if [catch {exec sddsprocess $dir/config -pipe=out -match=param,NameType=MonitorNames \
                 -print=col,ControlName,%s,Name \
                 | sddsconvert -pipe=in -rename=col,Name=SymbolicName $tmpRoot.defns2 } result] {
	return -code error "APSSRGenerateMofbControllawFiles (1b): error generating definitions file: $result"
    }
    if [catch {exec sddscombine $tmpRoot.defns1 $tmpRoot.defns2 -merge -overwrite $dir/definitions} result] {
	return -code error "APSSRGenerateMofbControllawFiles (1c): error generating definitions file: $result"
    }
    
    #current test value
    SetSROrbitStatus "Generating tests file..."
    if [catch {exec sddsmakedataset $tmpRoot.curr.tests -col=ControlName,type=string \
                    -data=S-DCCT:CurrentM,S-MOFB:AGG:$Coord:OpMessageAlertM \
		   -col=MinimumValue,type=double -data=$currLimit,0 \
		   -col=MaximumValue,type=double -data=300,0} result] {
	return -code error "APSSRGenerateMofbControllawFiles (2): error generating current tests: $result"
    }
    
    if [catch {exec sddsprocess $dir/config $tmpRoot.bpm.tests -match=param,NameType=MonitorNames -print=col,ControlName,%s:$coord:SampledAdjustedM,Name \
		   "-define=col,MinimumValue,$BPMLimit chs" \
		   "-define=col,MaximumValue,$BPMLimit"  } result] {
	return -code error "APSSRGenerateMofbControllawFiles (3): Error creating bpm test file: $result"
    }
      
    if [catch {exec sddsprocess $tmpRoot.corr -reedit=col,ControlName,ei/.DRVH/ -pipe=out \
		   | sddscasr -save -pipe \
		   | sddsprocess -pipe -scan=col,MaximumValue,ValueString,%lf -reedit=col,ControlName,%/.DRVH// \
		   | sddsprocess -pipe "-redefine=col,MaximumValue,MaximumValue $corrLimitMargin -" \
		   | sddsconvert -pipe=in $tmpRoot.corr.hi \
		   -retain=col,Name,Flag,Weight,Despike,ElectronicsType,ControlName,SymbolicName,MinimumValue,MaximumValue
	exec sddsprocess $tmpRoot.corr -reedit=col,ControlName,ei/.DRVL/ -pipe=out \
		   | sddscasr -save -pipe \
		   | sddsprocess -pipe -scan=col,MinimumValue,ValueString,%lf \
		   | sddsprocess -pipe=in $tmpRoot.corr.lo "-redefine=col,MinimumValue,MinimumValue $corrLimitMargin +" 
	exec sddsxref  $tmpRoot.corr.hi  $tmpRoot.corr.lo \
		   -match=Name -take=MinimumValue $tmpRoot.corr.tests } result] {
	return -code error "APSSRGenerateMofbControllawFiles (4): Error creating corr test file from driveHigh and driveLow: $result"
    }
    
    # Assemble tests file
    if [catch {exec sddscombine $tmpRoot.curr.tests $tmpRoot.bpm.tests $tmpRoot.corr.tests -merge -over $dir/tests } result] {
	return -code error "APSSRGenerateMofbControllawFiles (5): Error generating tests file: $result"
    }

    # Make waveform file with devices in the same order as the irm and Index matching the BPM waveform
    SetSROrbitStatus "Generating waveform file..."
    set BPMList [list]
    for {set ds 1} {$ds<40} {incr ds 2} {
        set bpmList  [split [string trim [exec cavget -list=[format S%02d $ds]-DAQTBT:MOFB:BpmListM -charArray] " \""]]
        foreach bpm $bpmList {
            if [string match *p? $bpm] {
                eval lappend BPMList [os editstring %/P/:P/ [string toupper $bpm]]
            }
        }
    }
    SetSROrbitStatus "BPMList: $BPMList"
    SetSROrbitStatus "Making BPM waveform file"
    if [catch {exec sddsmakedataset -pipe=out -column=BPMName,type=string -data=[join $BPMList ,] \
             | sddsprocess -pipe=in $tmpRoot.bpmOrder -define=col,Index,i_row,type=long} result] {
        SetSROrbitStatus "Error: $result" -code error
        return
    }
    # This incidentally will detect if there is a mismatch between the BPM list in the IOC and the IRM.
    global nTotalBPMs
    if $OC(hold) {
        set waveformPV S-MOFB:AGG:$Coord:BpmGroup${nTotalBPMs}M
    } else {
        set waveformPV S-MOFB:AGG:$Coord:BpmErrorGroup${nTotalBPMs}M
    }
    if [catch {exec sddsquery -column -sddsOutput $dir/irm -pipe=out \
                 | sddsprocess -pipe -match=column,Name=*:P? -define=col,IrmIndex,i_row,type=long \
                 "-print=parameter,WaveformPV,$waveformPV" \
                 | sddsconvert -pipe -rename=column,Name=DeviceName \
                 | sddsxref $tmpRoot.bpmOrder -pipe -match=DeviceName=BPMName -take=Index -nowarning \
                 | sddssort -pipe=in -column=IrmIndex $dir/waveform} result] {
        SetSROrbitStatus "Error: $result" -code error
        return
    }
    if [catch {exec sdds2stream -rows=bare $dir/waveform} nRows] {
        SetSROrbitStatus "Error: $result" -code error
        return
    }
    if [expr $nRows!=$nTotalBPMs] {
        SetSROrbitStatus "Error: only $nRows BPMs in the waveform after matching with IRM! Try setup again." -code error
        file delete -force $dir/waveform
        return
    }

    SetSROrbitStatus "controllaw file setup for $plane done."
    APSDeleteTmpFileList -ID config
    cd $oldDir
}
    
proc GenerateControllawFiles {args} {
    set plane ""
    APSParseArguments {plane}
    global OC
    set oldDir [pwd]
    set dir $OC(dataDir)/$OC($plane.responseDir)

    if ![file exist $dir] {
	return -code error "$dir does not exist!"
    }
    set config $dir/config
    if {![file exist $config]} {
	return -code error "$dir/config does not exist, please create the configuration first!"
    }
    if ![file exist $dir/irm] {
	return -code error "$dir/irm does not exist, please use the OC configuration tool to compute the irm first!"
    }
    SetSROrbitStatus "Generating controllaw files for $plane..."
    if [catch {APSSRGenerateMofbControllawFiles -config $OC($plane.responseDir) \
                 -currLimit $OC($plane.DCCTLimit) -statusCallback SetSROrbitStatus \
                 -BPMLimit $OC(BPMLimit) -corrLimitMargin $OC(${plane}corrLimitMargin) \
                 -dataDir $OC(dataDir)  } result] {
	return -code error "Error generating controllaw files: $result"
    }
    
    # Generate simple test file for sddscontrollaw
    set dir $OC(dataDir)/$OC(${plane}.responseDir)
    set XY(h) X
    set XY(v) Y
    if [catch {exec sddsmakedataset $dir/clawtests \
                 -column=ControlName,type=string \
                 -data=$OC(${plane}.pvtestRCPV).RUN,S-MOFB:AGG:MissedDataUpdateM,S-MOFB:AGG:$XY($plane):MissedDataUpdateCtrM,S-MPS:MPSM1:BeamCurrentM,S01-DAQTBT:MOFB:SumMinM \
                 -column=MinimumValue,type=double -data=1,0,0,$OC($plane.DCCTLimit),0.6e6 \
                 -column=MaximumValue,type=double -data=1,0,0,300,1e9 } result] {
	return -code error "Error generating controllaw test file: $result"        
    }
    SetSROrbitStatus "Done generating controllaw files for $plane."

    #generate definition file
    set tmpRoot /tmp/[APSTmpString]
    switch $plane {
	h {
	    set coord x
	}
	v {
	    set coord y
	}
    }
    if [catch {exec sddsprocess $config -match=par,NameType=MonitorNames $tmpRoot.bpm \
                 -print=col,ControlName,%s,Name -print=col,SymbolicName,%s,Name 
        exec sddsprocess $config -match=par,NameType=CorrectorNames $tmpRoot.corr \
                 -edit=col,ControlName,Name,ei/:PS:SetCurrentC/ \
                 -print=col,SymbolicName,%s,Name 
        exec sddscombine $tmpRoot.bpm $tmpRoot.corr -merge -overwrite -retain=col,ControlName,SymbolicName $dir/definitions } result] {
	return -code error "Error generating definition file: $result"
    }
    eval file delete $tmpRoot.bpm $tmpRoot.corr
}

proc StartPVTest {args} {
    global OC
    set plane ""
    APSParseArguments {plane}

    set config $OC(${plane}.responseDir)
    SetSROrbitStatus "Starting $plane PV tests..."
    switch $plane {
	h {
	    set i0 0
	    set i1 1
	}
	v {
	    set i0 2
	    set i1 3
	}
    }
    set testRCPV $OC($plane.pvtestRCPV)
    set testRCDesc $OC($plane.pvtestRCDesc)
    set clawRCPV $OC($plane.controllawRCPV)
    if [catch {AbortProcess -type pvtest -plane $plane} result] {
	return -code error "Error aborting previous pvtest: $result"
    }
    global tabFrameWidgetListForLog
    APSExecLog .sr${plane}oc -parent [lindex $tabFrameWidgetListForLog $i1] \
	-lineLimit 2048 -width 95 -height 40  \
	-name "SR ${plane}-orbit Correction" \
	-unixCommand "sddspvtest $config/tests -time=365,day -verbose -interval=1 -monitor -pvOutput=$clawRCPV.SUSP,fail=1,pass=0 -runControlPV=string=$testRCPV \"-runControlDesc=string=$testRCDesc\"" \
	-callback "" \
	-cancelCallback "AbortProcess -type pvtest -plane $plane" \
	-abortCallback "AbortProcess -type pvtest -plane $plane"
    
}

proc PrepareControllawOptions {args} {
    global OC
    set plane ""
    APSParseArguments {plane}

    set config $OC(${plane}.responseDir)
    if [catch {APSSRPrepareControllawOptions -config $config \
                 -dataDir $OC(dataDir) \
                 -runControlDesc $OC($plane.controllawRCDesc) \
                 -runControlPV $OC($plane.controllawRCPV) \
                 -RCTimeout $OC(RCTimeout) \
                 -testValues 0 \
                 -averages 1 \
                 -averageInterval 0 \
                 -dryRun $OC(dryRun) \
                 -verbose $OC(verbose) \
                 -FFcompensation 0 \
                 -logActuators $OC(logStats) \
                 -logStats $OC(logStats) \
                 -logGlitch $OC(logGlitch) \
                 -steps $OC(steps) \
                 -gain $OC($plane.gain) \
                 -interval 0.0001  } options] {
	return -code error "Error preparing options: $options"
    }
    set XY(h) X
    set XY(v) Y
    lappend options -triggerPV=S-MOFB:AGG:$XY($plane):BpmGroupUpdatedM,modulus=$OC(triggerModulus) -endOfLoopPV=S-MOFB:AGG:$XY($plane):AlgorithmCompleteC
    lappend options -waveform=$OC(dataDir)/$config/waveform,read
    if $OC(hold) {
        lappend options -holdPresentValues
    }
    if $OC(logAll) {
        lappend options ${OC(outputRoot)}-$plane -generations
    }
    return $options
}

proc StartControllaw {args} {
    set plane ""
    set regenerate 0
    APSParseArguments {plane regenerate}
    global OC
    if $regenerate {
	if [catch {GenerateControllawFiles -plane $plane} result] {
	    SetSROrbitStatus "Error generating controllaw files for $plane: $result" -code error 
	    return
	}
    }
    SetSROrbitStatus "Preparing $plane controllaw options..."
    if [catch {PrepareControllawOptions -plane $plane} options] {
        SetSROrbitStatus "Error preparing controllaw options: $options" -code error 
        return
       }
    set dir $OC(dataDir)/$OC(${plane}.responseDir)
    lappend options -testValues=$dir/clawtests
    SetSROrbitStatus "sddscontrollaw options: $options"
    switch $plane {
	h {
	    set i0 0
	    set i1 1
	}
	v {
	    set i0 2
	    set i1 3
	}
    }
    SetSROrbitStatus "$plane options: $options"
    if [catch {AbortProcess -type controllaw -plane $plane} result] {
	return -code error "Error aborting previous controllaw: $result"
    }
    global tabFrameWidgetListForLog
    APSExecLog .sr${plane}oc -parent [lindex $tabFrameWidgetListForLog $i0] \
	-lineLimit 2048 -width 95 -height 40  \
	-name "SR ${plane}-orbit Correction" \
	-unixCommand "sddscontrollaw $options" \
	-callback "" \
	-cancelCallback "AbortProcess -type controllaw -plane $plane" \
	-abortCallback "AbortProcess -type controllaw -plane $plane"
    SetSROrbitStatus "$plane OC started."
}

proc ValidateConfigurations {} {
    global OC validatedFiles totalBPMOptions maxBpmsPerSector nTotalBPMs

    foreach plane {h v} {
        if ![file exists $OC(dataDir)/$OC($plane.responseDir)/config] {
            SetSROrbitStatus "Error: $OC(dataDir)/$OC($plane.responseDir)/config not found" -code error 
            return 0
        }
        set BPMList($plane) [exec sddsprocess $OC(dataDir)/$OC($plane.responseDir)/config -pipe=out -match=parameter,NameType=MonitorNames \
                               | sdds2stream -pipe -column=Name]
        set nBPMs($plane) [llength $BPMList($plane)]
        if [lsearch -exact $totalBPMOptions $nBPMs($plane)]==-1 {
            SetSROrbitStatus "Error: $OC($plane.responseDir) has $nBPMs($plane) BPMs selected. Must be one of [join $totalBPMOptions ,]" -code error 
            return 0
        }
    }
    if { $nBPMs(h) != $nBPMs(v) } {
        SetSROrbitStatus "Error: $OC(x.responseDir) has $nBPMs(h) BPMs selected while $OC(y.responseDir) has $nBPMs(v) selected."
        return 0
    }
    set nTotalBPMs $nBPMs(h)
    SetSROrbitStatus "Both configurations have $nTotalBPMs BPMs"
    
    # BPM lists must be the same
    foreach plane {h v} otherPlane {v h} {
        foreach bpm [set BPMList($otherPlane)] {
            if [lsearch $BPMList($plane) $bpm]==-1 {
                SetSROrbitStatus "Error: $bpm is in $otherPlane-plane configuration but not in $plane-plane configuration" -code error 
                return 0
            }
        }
    }
    SetSROrbitStatus "BPM lists match"

    # Must have same number of BPMs per double-sector
    set nBPMsPerSector [expr $nTotalBPMs/20]
    if ![file exists /home/helios/oagData/sr/orbitControllaw/TBT-DAQ-BpmLocations.sdds] {
        SetSROrbitStatus "Error: /home/helios/oagData/sr/orbitControllaw/TBT-DAQ-BpmLocations.sdds" -code error 
        return 0
    }
    foreach plane {h v} {
        if [catch {exec sddsprocess $OC(dataDir)/$OC($plane.responseDir)/config -pipe=out -match=parameter,NameType=MonitorNames \
                     | sddsxref -pipe /home/helios/oagData/sr/orbitControllaw/TBT-DAQ-BpmLocations.sdds -match=Name=BpmName -take=DoubleSector -nowarning \
                     | sddssort -col=DoubleSector -pipe \
                     | sddsbreak -pipe -change=DoubleSector \
                     | sddsprocess -pipe -process=DoubleSector,first,%s -define=parameter,Number,n_rows,type=short \
                     | sddscollapse -pipe \
                     | sddsprocess -pipe -filter=col,Number,$nBPMsPerSector,$nBPMsPerSector,! -nowarning \
                     | sdds2stream -pipe -column=DoubleSector} result] {
            SetSROrbitStatus "Error: $result" -code error 
            return 0
        }
        if [string length $result]!=0 {
            SetSROrbitStatus "Error: some double-sectors have more or less than $nBPMsPerSector BPMs"
            SetSROrbitStatus "$result" -code error 
            return 0
        }
    }
    SetSROrbitStatus "Configuration has $nBPMsPerSector BPMs per double-sector pair"

    foreach plane {h v} {
        set validatedFiles($plane) $OC(dataDir)/$OC($plane.responseDir)/config
        set validatedTimeStamp($plane) [file mtime $OC(dataDir)/$OC($plane.responseDir)/config]
    }
    
    SetSROrbitStatus "Configurations passed validation"
    
    return 1
}

proc SetupConfiguration {} {
    global OC ready

    set ready 0
    if [ValidateConfigurations]==0 {
        SetSROrbitStatus "Error: configurations failed validation" -code error 
        return
    }

    set rate $OC(rate)

    # Set the number to average
    if [expr $rate<100] {
        set nAve 2712
    } else {
        set nAve [expr int(1800*100.0/$rate)]
    }
    SetSROrbitStatus "Setting number of averages to $nAve"
    
    if [catch {exec cavput -list=S -range=begin=1,end=39,format=%02i,interval=2  -list=-DAQTBT:MOFB:NumTurnsToAverageC=$nAve} result] {
        SetSROrbitStatus "Error: $result" -code error 
        return
    }

    # Set the rate
    global rateList
    set rateCode [lsearch $rateList $rate]
    SetSROrbitStatus "Setting rate to $rate ($rateCode)"
    if [catch {exec cavput -list=S -range=begin=1,end=39,format=%02i,interval=2 -list=-DAQTBT:MOFB:TurnsPerFeedbackIntervalC=$rateCode} result] {
        SetSROrbitStatus "Error: $result" -code error 
        return
    }        

    SetSROrbitStatus "Setting BPM selections"
    # Set the BPM lists
    set tmpRoot /tmp/[APSTmpString]
    APSAddToTempFileList $tmpRoot.1
    # Plane doesn't matter since they are validated to be the same
    set plane h 
    if [catch {exec sddsprocess $OC(dataDir)/$OC($plane.responseDir)/config -pipe=out -match=parameter,NameType=MonitorNames \
                 | sddsxref -pipe /home/helios/oagData/sr/orbitControllaw/TBT-DAQ-BpmLocations.sdds -match=Name=BpmName -take=DoubleSector -nowarning \
                 | sddssort -col=DoubleSector -pipe \
                 | sddsbreak -pipe -change=DoubleSector \
                 | sddsprocess -pipe=in $tmpRoot.1 -process=DoubleSector,first,%s0} result] {
        SetSROrbitStatus "Error: $result" -code error 
        return
    }
    if [catch {sdds load $tmpRoot.1 data} result] {
        SetSROrbitStatus "Error: $result" -code error 
    }
    set putList [list]
    foreach DoubleSector $data(Parameter.DoubleSector0) BPMList $data(Column.Name) {
        set bpmList [list]
        foreach BPM $BPMList {
            eval lappend bpmList [string tolower [os editstring %/:// $BPM]]
        }
        SetSROrbitStatus "$bpmList"
#        if [catch {exec caput -S ${DoubleSector}-DAQTBT:MOFB:BpmListExpC [join $bpmList |]} result] {
#            SetSROrbitStatus "Error: $result" -code error 
#            return
#        }
        lappend putList ${DoubleSector}-DAQTBT:MOFB:BpmListExpC=[join $bpmList |]
    }
    if [catch {exec cavput -charArray -list=[join $putList ,]} result] {
        SetSROrbitStatus "Error: $result" -code error 
        return
    }
    set ready 1
    SetSROrbitStatus "Setup completed. Ready to go."
}


SetDefaultPars
cd $OC(dataDir)

set menuBackground \#597EE1
set args $argv 
if {[APSStrictParseArguments {menuBackground}] || ![string length $menuBackground]} {
    puts stderr "usage: SRMofbControllaw \[-menuBackground <color>(\#597EE1)]"
}

APSApplication . -name "SRMofbControllaw" \
  -overview "SRMofbControllaw provides controls for executing sddscontrollaw for correcting the SR orbit using the Medium-speed Orbit FeedBack system." -menuBackground $menuBackground

set SROrbitStatus "Initializing ..."
APSScrolledStatus .status -parent .userFrame -width 60 \
  -textVariable SROrbitStatus -packOption "-fill both -expand true"

MakeOptionAndParameterFrame .opt -parent .userFrame
MakeActionFrame .act -parent .userFrame

#create execLog window
set parent ""
APSFrame .execLog -parent $parent -packOption "-expand yes -fill both -side top"
$parent.execLog.frame configure -relief flat -bd 0

set sectionList [list controllaw_x pvtest_x controllaw_y pvtest_y]

set tabFrameWidgetListForLog [APSTabFrame .log -parent $parent.execLog.frame \
                                -label "" \
                                -labelList $sectionList \
                                -width 955 -height 700 \
                                -packOption "-expand yes -fill both"]

update
wm geometry .execLog +[expr [winfo rootx .] + 50]+[expr [winfo rooty .] + 300]

wm protocol $parent.execLog WM_DELETE_WINDOW {return} 
