#!/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)]
set apsttk 1
APSStandardSetup
set scu 1
set oldscu 0
APSApplication . -name ScanScuAperture -version 1.0 \
  -overview {}

proc InitStatus {} {
    global status scu oldscu fileRoot
    if {$scu == $oldscu} {return}
    set scu2 [expr $scu + 1]

    set status "-- This programs scans Y orbit in ID$scu SCU$scu by changing S${scu}B:P1 and S${scu2}A:P1 setpoints.
   A user is required to start an appropriate orbit correction prior to running the orbit
   scan. Recommended orbit correction configurations are h.defaultDP and 
   v.SCU${scu}apertureScan (S${scu2}A:V3 is excluded due to its lower current limit).
-- P1 setpoints have to be zeros.
-- It is also recommended that after starting the orbit correction, the user verified its
   performance by running the command:
   \"cavput -list=S${scu}B:P1,S${scu2}A:P1 -list=:ms:y:SetpointAO=-1.5 -delta=factor=1 -pend=5\"
   in positive and negative directions.\n\n"
    
    append status "Ready..."
    set oldscu $scu
    set fileRoot [GetDailyDirectory]/BBA/SCU${scu}/scan01
    if {[winfo exists .userFrame.archived]} {
        .userFrame.buttons.listarchived.button configure -text "List Archived"
        ListArchived
    }
}

proc GetDailyDirectory {args} {
    global env
    set account $env(USER)
    set subdirectory ""
    set fourDigitYear 0
    APSParseArguments {account subdirectory fourDigitYear}
    set timeList [clock format [clock seconds] -format "%Y %m %d %H %M %S"]
   
    if {([string toupper $account] == "SR") || ([string toupper $account] == "LINAC") || ([string toupper $account] == "PAR") || ([string toupper $account] == "BOOSTER")} {
        set fourDigitYear 1
    }
    if {$fourDigitYear} {
        set year [lindex $timeList 0]
        set month [lindex $timeList 1]
    } else {
        # use only last two digits of year
        regexp {..$} [lindex $timeList 0] year
        regexp {^0?(.*)} $year {} year
        set month [string trimleft [lindex $timeList 1] 0]
    }
    set day [lindex $timeList 2]
    regexp {^0?(.*)} [lindex $timeList 3] {} hour
    if $hour<8 {
        set shift 1
    } elseif $hour<16 {
        set shift 2
    } else {
        set shift 3
    }
    global OAGGlobal
    set top $env(HOME)
    if {[info exists OAGGlobal(AtAPS)] && !$OAGGlobal(AtAPS)} {
	set top [file dirname $env(HOME)]/$account
    }
    if {$fourDigitYear} {
        set apsOutputDir $top/daily/${year}/${month}/${day}/$shift
    } else {
        set apsOutputDir $top/daily/[format %02ld%02ld $year $month]/${day}/$shift
    }
    if [string length $subdirectory] {
        append apsOutputDir /$subdirectory
    }
    return $apsOutputDir
}

proc CollectData {args} {
    global scu
    APSParseArguments {filename seconds}
    set monFile /home/helios/SR/SCU${scu}Interlocks/apertureScan.mon
    exec sddsmonitor $monFile $filename -steps=$seconds
}

proc RunScan {args} {
    global scu
    APSParseArguments {yDown yUp yStep seconds fileRoot}
    set y $yDown
    set counter 0
    set fileList ""
    set scu2 [expr $scu + 1]

    set returnString [exec cavget -list=S${scu}B:P1,S${scu2}A:P1 -list=:ms:y:SetpointAO -cavput]
    APSSetVarAndUpdate status "cavput -list=S${scu}B:P1,S${scu2}A:P1 -list=:ms:y:SetpointAO=$yDown -delta=factor=1 -pend=5"
    exec cavput -list=S${scu}B:P1,S${scu2}A:P1 -list=:ms:y:SetpointAO=$yDown -delta=factor=1 -pend=5

    APSSetVarAndUpdate status "$counter: $y"
    after 100
    if [catch {CollectData -filename $fileRoot.$counter -seconds $seconds} result] {
        return -code error "CollectData: $result"
    }
    lappend fileList $fileRoot.$counter
    while {$y < $yUp} {
        APSSetVarAndUpdate status "cavput -list=S${scu}B:P1,S${scu2}A:P1 -list=:ms:y:SetpointAO=$yStep -delta=factor=1 -pend=5"
        exec cavput -list=S${scu}B:P1,S${scu2}A:P1 -list=:ms:y:SetpointAO=$yStep -delta=factor=1 -pend=5
        set y [expr $y + $yStep]
        incr counter
        APSSetVarAndUpdate status "$counter: $y"
        if [catch {CollectData -filename $fileRoot.$counter -seconds $seconds} result] {
            return -code error "CollectData: $result"
        }
        lappend fileList $fileRoot.$counter
    }
    APSSetVarAndUpdate status "cavput -list=$returnString -pend=5"
    exec cavput -list=$returnString -pend=5
    eval exec sddscombine $fileList $fileRoot.sdds
    eval file delete $fileList
    APSSetVarAndUpdate status "$fileRoot.sdds created"
}

proc ProcessIDApScan {args} {
    global scu fileRoot

    set input $fileRoot.sdds
    set scu2 [expr $scu + 1]

    if {[catch {exec sddsbreak $input -pipe=out -change=S${scu}B:P1:ms:y:SetpointAO \
                  | sddsprocess -pipe -nowarning \
                  -filter=col,S${scu}B:P1:ms:y:ErrorCC,-0.01,0.01 \
                  -fclip=0,0.1,invert \
                  -process=Time,count,Count \
                  -filter=param,Count,0,5,! \
                  "-process=*Setpoint*,ave,%sAve" \
                  "-define=param,ySetpointAveAbs,S${scu}B:P1:ms:y:SetpointAOAve S${scu2}A:P1:ms:y:SetpointAOAve + 2 / abs,units=mm" \
                  | sddssort -pipe -param=ySetpointAveAbs,incr \
                  | sddsprocess -pipe=in $input.ref \
                  "-test=param,i_page 1 ==" \
                  -process=*Temp*,ave,%sAveRef -process=*Temp*,sigma,%sSigmaRef \
                  -process=SRlifeTime*,ave,LifetimeAveRef -process=SRlifeTime*,sigma,LifetimeSigmaRef} results]} {
        APSSetVarAndUpdate status "Error: $results"
        return -code error
    }

    if {[catch {exec sddsbreak $input -pipe=out -change=S${scu}B:P1:ms:y:SetpointAO \
                  | sddsprocess -pipe -nowarning \
                  -filter=col,S${scu}B:P1:ms:y:ErrorCC,-0.01,0.01 \
                  -fclip=0,0.1,invert \
                  -process=Time,count,Count \
                  -process=*Temp*,ave,%sAve -process=*Temp*,sigma,%sSigma \
                  -process=*DCCT,ave,S35DCCTAve -process=*DCCT,sigma,S35DCCTSigma \
                  -process=SRlifeTime*,ave,LifetimeAve -process=SRlifeTime*,sigma,LifetimeSigma \
                  -process=*Setpoint*,ave,%sAve \
                  "-define=param,ySetpointAve,S${scu}B:P1:ms:y:SetpointAOAve S${scu2}A:P1:ms:y:SetpointAOAve + 2 /,units=mm" \
                  | sddscollapse -pipe \
                  | sddsprocess -pipe -filter=col,Count,0,5,! \
                  | sddsxref -pipe $input.ref -leave=* -transfer=param,*Ref* \
                  | sddsprocess -pipe \
                  "-define=col,%sDelta,%s %sRef -,units=degK,select=*Temp*Ave" \
                  "-define=col,%sDeltaNorm,%s %sRef - 25 S35DCCTAve / sqr *,units=degK,select=*Temp*Ave" \
                  | sddssort -pipe=in $input.proc -column=ySetpointAve} results]} {
        APSSetVarAndUpdate status "Error: $results"
        return -code error
    }

    file delete $input.ref
    APSSetVarAndUpdate status "$fileRoot.sdds.proc created"
}

proc PerformIDApScanFitsMod {args} {
    global scu fileRoot fitTerms
    
    set input $fileRoot.sdds.proc
    set output $fileRoot

    # The factor of 1.07 is to account for the difference in position at the P0 vs P1 (used for steering)

    if {[catch {exec sddsmpfit $input -pipe=out \
                  -indep=ySetpointAve \
                  "-depen=*Temp\[1-9\]*Norm*" \
                  -terms=$fitTerms \
                  -generate \
                  | sddsprocess -pipe=in $output.fit \
                  "-redefine=param,%syMin,%sSlope 2 / %sCurvature / chs 1.07 *,units=mm,select=*Slope,edit=%/Slope//" \
                  "-define=param,%syMinSigma,%sSlopeSigma %sSlope / sqr %sCurvatureSigma %sCurvature / sqr + sqrt %syMin * abs,units=mm,select=*Slope,edit=%/Slope//"} results]} {
        APSSetVarAndUpdate status "Error: $results"
        return -code error
    }
    APSSetVarAndUpdate status "$fileRoot.fit created"

    if {[catch {exec sddscollapse $output.fit -pipe=out \
                  | sddsconvert -pipe "-retain=col,*Min,*MinSigma" \
                  | sddscollect -pipe -collect=suffix=AveDeltaNormyMin -collect=suffix=AveDeltaNormyMinSigma \
                  | sddsprocess -pipe -scan=col,Index,Rootname,%ld,edit=Z:ebkayf100d,type=long \
                  | sddssort -pipe=in $output.min -col=Index} results]} {
        APSSetVarAndUpdate status "Error: $results"
        return -code error
    }
    APSSetVarAndUpdate status "$fileRoot.min created"

}

proc Process {} {
    global fileRoot

    if ![file exists $fileRoot.sdds] {
        APSSetVarAndUpdate status "No file $fileRoot.sdds"
        return
    } 
    if [catch {ProcessIDApScan} result] {
        return
    }

    exec sddsplot -column=ySetpointAve,*Temp*AveDeltaNorm $fileRoot.sdds.proc -separate -graph=sym,scale=2,fill &

    catch {file delete $fileRoot.fit}
    if [catch {PerformIDApScanFitsMod} result] {
        return
    }

    exec sddsplot -unsup=y \
      -col=Index,AveDeltaNormyMin,AveDeltaNormyMinSigma  $fileRoot.min -graph=sym,fill "-ylabel=Beam Offset for Min. Temp. (mm)" \
      -col=Index,AveDeltaNormyMin,AveDeltaNormyMinSigma  $fileRoot.min -graph=error "-ylabel=Beam Offset for Min. Temp. (mm)" &
}

proc AddToArchive {} {
    global fileRoot scu
    if {![file exists $fileRoot.sdds.proc]} {
        APSSetVarAndUpdate status "No file $fileRoot.sdds.proc"
        return
    }
    if {![file exists $fileRoot.min]} {
        APSSetVarAndUpdate status "No file $fileRoot.min"
        return
    }
    sdds load /home/helios/OAG/oagData/sr/SCU-BBA/archived-data.sdds data
    set a [lindex $data(Column.SCU) 0]
    lappend a $scu
    set data(Column.SCU) [list $a]
    set a [lindex $data(Column.FileRoot) 0]
    lappend a $fileRoot
    set data(Column.FileRoot) [list $a]
    sdds save /home/helios/OAG/oagData/sr/SCU-BBA/archived-data.sdds data
    exec sddssort /home/helios/OAG/oagData/sr/SCU-BBA/archived-data.sdds -col=FileRoot -num -nowarn
    file delete /home/helios/OAG/oagData/sr/SCU-BBA/archived-data.sdds~
    APSSetVarAndUpdate status "Scan has been archived"
}

proc ListArchived {} {
    global scu archiveSelection
    set min [expr $scu - .1]
    set max [expr $scu + .1]
    set filterOption -filter=column,SCU,${min},${max}
    if {$scu == 6} {
        append filterOption ",SCU,-.1,.1,|"
    }
    if {[catch {exec sddsprocess /home/helios/OAG/oagData/sr/SCU-BBA/archived-data.sdds -pipe=out $filterOption | sdds2stream -pipe=in -col=FileRoot} scans]} {
        APSSetVarAndUpdate status "Error: $scans"
        bell
        return
    }
    set archiveSelection [join $scans]
    if {[winfo exists .userFrame.archived]} {
        if {[.userFrame.buttons.listarchived.button cget -text] == "Hide Archived"} {
            destroy .userFrame.archived
            destroy .userFrame.plotarchived
            destroy .userFrame.comparearchived
            .userFrame.buttons.listarchived.button configure -text "List Archived"
        } else {
            .userFrame.buttons.listarchived.button configure -text "Hide Archived"
        }
    } else {
        APSScrolledList .archived -parent .userFrame -listvar archiveSelection
        APSButton .plotarchived -parent .userFrame -text "Plot Archived" -width "" -command PlotArchived -contextHelp "Plot selected archived data"
        APSButton .comparearchived -parent .userFrame -text "Compare Archived" -width "" -command CompareArchived -contextHelp "Compare selected archived data"
        .userFrame.buttons.listarchived.button configure -text "Hide Archived"
    }
    APSSetVarAndUpdate status "Contents of /home/helios/OAG/oagData/sr/SCU-BBA/archived-data.sdds displayed"
}

proc PlotArchived {} {
    global archiveSelection
    set scans [.userFrame.archived.listbox curselection]
    foreach scan $scans {
        set fileRoot [lindex $archiveSelection $scan]
        exec sddsplot -column=ySetpointAve,*Temp*AveDeltaNorm $fileRoot.sdds.proc -separate -graph=sym,scale=2,fill -filename &
        exec sddsplot -filename -unsup=y \
          -col=Index,AveDeltaNormyMin,AveDeltaNormyMinSigma  $fileRoot.min -graph=sym,fill "-ylabel=Beam Offset for Min. Temp. (mm)" \
          -col=Index,AveDeltaNormyMin,AveDeltaNormyMinSigma  $fileRoot.min -graph=error "-ylabel=Beam Offset for Min. Temp. (mm)" &
    }
}

proc CompareArchived {} {
    global archiveSelection scu
    set scans [.userFrame.archived.listbox curselection]
    if {([llength $scans] < 2) || ([llength $scans] > 4)} {
        APSSetVarAndUpdate status "Select two scans to compare"
        return
    }
    set i 1
    set j 0
    foreach scan $scans {
        set scan$i [lindex $archiveSelection [lindex $scans $j]]
        set f$i [split [set scan$i] /]
        incr i
        incr j
    }

    set i 0
    if {[llength $scans] == 2} {
        foreach p1 $f1 p2 $f2 {
            if {$p1 == $p2} {
                incr i
            } else {
                set legend1 [join [lrange $f1 $i end] /]
                set legend2 [join [lrange $f2 $i end] /]
                break
            }
        }
    } elseif {[llength $scans] == 3} {
        foreach p1 $f1 p2 $f2 p3 $f3 {
            if {($p1 == $p2) && ($p1 == $p3)} {
                incr i
            } else {
                set legend1 [join [lrange $f1 $i end] /]
                set legend2 [join [lrange $f2 $i end] /]
                set legend3 [join [lrange $f3 $i end] /]
                break
            }
        }
    } elseif {[llength $scans] == 4} {
        foreach p1 $f1 p2 $f2 p3 $f3 p4 $f4 {
            if {($p1 == $p2) && ($p1 == $p3) && ($p1 == $p4)} {
                incr i
            } else {
                set legend1 [join [lrange $f1 $i end] /]
                set legend2 [join [lrange $f2 $i end] /]
                set legend3 [join [lrange $f3 $i end] /]
                set legend4 [join [lrange $f4 $i end] /]
                break
            }
        }
    }

    set col3 ""
    set col4 ""
    set pos /home/helios/SR/SCU1Interlocks/scu1SensorPositions.sdds
    set tmpOutput /tmp/[APSTmpString]
    exec sddsprocess $pos $tmpOutput -clip=0,1
    exec sddsxref $scan1.min $tmpOutput $tmpOutput.1
    exec sddsxref $scan2.min $tmpOutput $tmpOutput.2
    if {[llength $scans] >= 3} {
        exec sddsxref $scan3.min $tmpOutput $tmpOutput.3
        set col3 "-col=s,AveDeltaNormyMin,AveDeltaNormyMinSigma $tmpOutput.3 -legend=spec=$legend3"
    }
    if {[llength $scans] >= 4} {
        exec sddsxref $scan4.min $tmpOutput $tmpOutput.4
        set col4 "-col=s,AveDeltaNormyMin,AveDeltaNormyMinSigma $tmpOutput.4 -legend=spec=$legend4"
    }
    file delete $tmpOutput

    eval exec sddsplot -graph=error,vary=subtype -clip=0,1 \
      -subtic=xd=5,yd=2 \
      {"-ylabel=y Center (mm)"} {"-xlabel=chamber sensor position (m),scale=0.7"} \
      -pSpace=0.15,0.8,0.15,0.9 \
      -stagg=xincrement=0.005,data \
      {"-topline=SCU$scu chamber alignment history"} \
      -col=s,AveDeltaNormyMin,AveDeltaNormyMinSigma $tmpOutput.1 -legend=spec=$legend1 \
      -col=s,AveDeltaNormyMin,AveDeltaNormyMinSigma $tmpOutput.2 -legend=spec=$legend2 $col3 $col4 &
}

InitStatus
APSScrolledStatus .status -parent .userFrame -textVariable status -width 30 -height 12 -packOption "-fill both -expand true"

ttk::frame .userFrame.options1
pack .userFrame.options1 -anchor nw

ttk::frame .userFrame.options1.scuFrame
ttk::radiobutton .userFrame.options1.scuFrame.scu1 -text "SCU1" -variable scu -value 1 -command InitStatus
ttk::radiobutton .userFrame.options1.scuFrame.scu6 -text "SCU6" -variable scu -value 6 -command InitStatus
ttk::radiobutton .userFrame.options1.scuFrame.scu7 -text "HSCU7" -variable scu -value 7 -command InitStatus
grid .userFrame.options1.scuFrame -row 0 -column 1 -sticky w
pack .userFrame.options1.scuFrame.scu1 -side left
pack .userFrame.options1.scuFrame.scu6 -side left -padx 5
pack .userFrame.options1.scuFrame.scu7 -side left -padx 5

ttk::label .userFrame.options1.outputDir -text "Output file root: "
ttk::entry .userFrame.options1.outputDirEntry -textvariable fileRoot -width 55
set apsContextHelp(.userFrame.options1.outputDir) "Enter a name for the output file."
set apsContextHelp(.userFrame.options1.outputDirEntry) "Enter a name for the output file."
grid .userFrame.options1.outputDir -row 1 -column 0 -sticky e
grid .userFrame.options1.outputDirEntry -row 1 -column 1 -sticky w

ttk::label .userFrame.options1.yDown -text "Scan from y: "
ttk::entry .userFrame.options1.yDownEntry -textvariable yDown -width 15
set apsContextHelp(.userFrame.options1.yDown) "Scan orbit from this value."
set apsContextHelp(.userFrame.options1.yDownEntry) "Scan orbit from this value."
grid .userFrame.options1.yDown -row 2 -column 0 -sticky e
grid .userFrame.options1.yDownEntry -row 2 -column 1 -sticky w

ttk::label .userFrame.options1.yUp -text "Scan to y: "
ttk::entry .userFrame.options1.yUpEntry -textvariable yUp -width 15
set apsContextHelp(.userFrame.options1.yUp) "Scan orbit to this value."
set apsContextHelp(.userFrame.options1.yUpEntry) "Scan orbit to this value."
grid .userFrame.options1.yUp -row 3 -column 0 -sticky e
grid .userFrame.options1.yUpEntry -row 3 -column 1 -sticky w

ttk::label .userFrame.options1.yStep -text "Orbit step: "
ttk::entry .userFrame.options1.yStepEntry -textvariable yStep -width 15
set apsContextHelp(.userFrame.options1.yStep) "Orbit step size."
set apsContextHelp(.userFrame.options1.yStepEntry) "Orbit step size."
grid .userFrame.options1.yStep -row 4 -column 0 -sticky e
grid .userFrame.options1.yStepEntry -row 4 -column 1 -sticky w

ttk::label .userFrame.options1.seconds -text "Seconds to wait at each step: "
ttk::entry .userFrame.options1.secondsEntry -textvariable seconds -width 15
set apsContextHelp(.userFrame.options1.seconds) "At every orbit, the wait for temperature to settle will be this long."
set apsContextHelp(.userFrame.options1.secondsEntry) "At every orbit, the wait for temperature to settle will be this long."
grid .userFrame.options1.seconds -row 5 -column 0 -sticky e
grid .userFrame.options1.secondsEntry -row 5 -column 1 -sticky w

ttk::label .userFrame.options1.fitTerms -text "Number of polynomial terms for fit: "
ttk::entry .userFrame.options1.fitTermsEntry -textvariable fitTerms -width 15
set apsContextHelp(.userFrame.options1.fitTerms) "At every orbit, the wait for temperature to settle will be this long."
set apsContextHelp(.userFrame.options1.fitTermsEntry) "At every orbit, the wait for temperature to settle will be this long."
grid .userFrame.options1.fitTerms -row 6 -column 0 -sticky e
grid .userFrame.options1.fitTermsEntry -row 6 -column 1 -sticky w

ttk::frame .userFrame.buttons
pack .userFrame.buttons -anchor nw
APSButton .run -parent .userFrame.buttons -text "Run orbit scan" -width "" -command {
    if {[string length $fileRoot] == 0} {
        APSSetVarAndUpdate status "fileRoot is empty. Scan is not run."
    } else {
        if [catch {RunScan -yDown $yDown -yUp $yUp -yStep $yStep -seconds $seconds \
                     -fileRoot $fileRoot} result] {
            APSSetVarAndUpdate status "RunScan: $result"
            bell
        }
    }

} -contextHelp "Changes setpoints, records SCU chamber temperature."

APSButton .process -parent .userFrame.buttons -text "Process" -width "" -command Process -contextHelp "Process data"
APSButton .addtoarchive -parent .userFrame.buttons -text "Add To Archive" -width "" -command AddToArchive -contextHelp "Add current scan data to the archive"
APSButton .listarchived -parent .userFrame.buttons -text "List Archived" -width "" -command ListArchived -contextHelp "List archived data"

set yDown -1.5
set yUp 1.5
set yStep 0.25
set seconds 200
set fileRoot [GetDailyDirectory]/BBA/SCU${scu}/scan01
set fitTerms 3
