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

# $Log: not supported by cvs2svn $
# Revision 1.9  2007/03/13 15:58:21  borland
# Added test button for selected users.
#
# Revision 1.8  2006/04/19 18:18:42  borland
# Added three models for the lifetime dependence on current.
#
# Revision 1.7  2006/04/18 18:13:44  emery
# Allow a larger lifetime of 10000 minutes intead of 6000.
#
# Revision 1.6  2003/11/03 16:59:48  soliday
# Changed SRlifeTimeSUB to SRlifeTimeM. This pv works when topup is both enabled
# or disabled.
#
# Revision 1.5  1999/06/02 21:41:08  borland
# Now computes based on time interval in addition to target current.
#
# Revision 1.4  1998/04/09 21:35:44  emery
# Added a bind to the target entry box for running the
# ComputeEstimatedTime on the <return> keystroke.
#
# Revision 1.3  1997/09/16 08:09:53  emery
# Changed the lifetime limits for "bad data" from 50 hrs to 100 hrs.
#
# Revision 1.2  1997/06/06 23:22:30  borland
# Added test to ensure that target is less than actual.
#
# Revision 1.1  1997/06/06 23:19:07  borland
# First version.
#
#

set auto_path [linsert $auto_path 0  /usr/local/oag/apps/lib/$env(HOST_ARCH)]
set auto_path [linsert $auto_path 0 /usr/local/oag/lib_patch/$env(HOST_ARCH)]
APSDebugPath

set CVSRevisionAuthor "\$Revision: 1.10 $ \$Author: soliday $"

APSApplication . -name "SRFillPredictor" -version $CVSRevisionAuthor -overview \
    "An application to predict how long it will be until the beam current decays to a specified value."

set tcl_precision 12

if {[pv linkw {SRcurrent SRlifetime} \
       {S-DCCT:CurrentM S-INJ:MPS:LifetimeMinutesM}] != 0} {
    APSAlertBox .err[APSTmpString] -errorMessage "Unable to make channel access connections.\n$errorCode" 
    exit
}
if {[pv getw {SRcurrent SRlifetime}]} {
    APSAlertBox .err[APSTmpString] -errorMessage "Unable to read pvs.\n$errorCode" 
    exit
}
set SRlifetimeHrs [expr $SRlifetime/60.0]

set targetCurrent 50
set estimatedTime ?
set hoursFromNow 12

set text  "When the beam lifetime is Touschek-dominated (e.g., for hybrid or 24 bunch mode), you should use the variable variable bunch length model. When the beam lifetime is gas-scattering dominated (e.g,. 324 or 1296 bunch mode), you should use the constant lifetime model.  The constant bunch length model is useful in the gas-scattering-dominated case where the gas pressure is proportional to current."

set physicsModel VBL
APSRadioButtonFrame .physics -parent .userFrame -label "Lifetime model: " \
  -variable physicsModel -gridPack "-column 0 -row 0 -sticky e -columnspan 2" \
  -orientation horizontal -buttonList {"Variable bunch length" "Constant lifetime" "Constant bunch length"} \
  -valueList {VBL CL CBL} -contextHelp $text

APSLabeledEntry .target0 -parent .userFrame -textVariable targetCurrent \
  -label "Target (mA)" -width 30 -gridPack "-column 0 -row 1 -sticky e" -contextHelp \
  "Enter a value less than the present amount of stored beam, then press \"Compute estimated time\" to predict when that current will be reached."
APSButton .computeT -parent .userFrame -text "Compute estimated time" \
  -command {catch {ComputeEstimatedTime -targetCurrent $targetCurrent} prediction} \
  -gridPack "-column 1 -row 1 -sticky w" -contextHelp \
  "After entering a target value, press this button to predict when that current will be reached."
APSLabeledEntry .target1 -parent .userFrame -textVariable hoursFromNow \
  -label "Hours from now: " -width 30 \
  -gridPack "-column 0 -row 2 -sticky e" -contextHelp \
  "Enter a number, in hours, for the elapsed time after which you want to predict the remaining beam current.  Press \"Compute estimated current\" to make the prediction."
APSButton .computeI -parent .userFrame -text "Compute estimated current" \
  -command {catch {ComputeEstimatedCurrent -hoursFromNow $hoursFromNow} prediction} \
  -gridPack "-column 1 -row 2 -sticky w" -contextHelp \
  "After entering the \"Hours from now\" value, press this button to predict what the current will be."
APSLabeledOutput .current -parent .userFrame -textVariable SRcurrent \
  -label "Current (mA): " -width 20 -gridPack "-column 0 -row 3"
APSLabeledOutput .lifetime -parent .userFrame -textVariable SRlifetimeHrs \
  -label "Lifetime (hrs): " -width 20 -gridPack "-column 1 -row 3"
APSLabeledOutput .est -parent .userFrame -textVariable prediction \
  -label "Prediction: " -width 60 -gridPack "-column 0 -row 4 -columnspan 2" -contextHelp \
  "The prediction for the beam current will show up here."

if [lsearch -exact [list borland lemery jiaox sereno shang soliday] $env(USER)]!=-1 { 
    APSButton .test -parent .userFrame -text "Test" -command TestCalcs -gridPack "-column 0 -row 5"
}

proc TestCalcs {} {
    global hoursFromNow targetCurrent
    set text1 [ComputeEstimatedCurrent -hoursFromNow 12]
    set text2 [ComputeEstimatedTime -targetCurrent $targetCurrent]
    puts stderr $text1
    puts stderr $text2
}

proc ComputeEstimatedTime {args} {
    set targetCurrent 60
    APSStrictParseArguments {targetCurrent}
    global SRcurrent SRlifetime physicsModel SRlifetimeHrs hoursFromNow
    if {[pv getw {SRcurrent SRlifetime}]} {
        bell
        return "CA error"
    }
    set SRlifetimeHrs [expr $SRlifetime/60.0]
    if {$SRcurrent<1 || $SRlifetime>10000 || $targetCurrent<1 || $targetCurrent>$SRcurrent} {
        bell
        return "Bad data"
    }
    switch $physicsModel {
        VBL {
            set power [expr 2./3]
            set secondsToGo [expr 1.5*60*$SRlifetime*(pow($SRcurrent/$targetCurrent, $power)-1)]
        }
        CBL {
            set secondsToGo [expr 60*$SRlifetime*($SRcurrent/$targetCurrent-1)]
        }
        default {
            # Constant lifetime
            set secondsToGo [expr 60*$SRlifetime*log($SRcurrent/$targetCurrent)]
        }
    }
    set timeNow [exec timeconvert -now | token -last]
    set hoursFromNow [expr $secondsToGo/3600.0]
    return "[format %.2f $targetCurrent] mA expected at [exec timeconvert -seconds=[expr $timeNow+$secondsToGo] -text]"
}

proc ComputeEstimatedCurrent {args} {
    set hoursFromNow 12
    APSStrictParseArguments {hoursFromNow}
    if [expr $hoursFromNow<=0] {
        bell
        return "Bad data"
    }
    global SRcurrent SRlifetime physicsModel targetCurrent SRlifetimeHrs
    if {[pv getw {SRcurrent SRlifetime}]} {
        bell
        return "CA error"
    }
    set SRlifetimeHrs [expr $SRlifetime/60.0]
    if {$SRcurrent<1 || $SRlifetime>10000} {
        bell
        return "Bad data."
    }
    set secs [expr $hoursFromNow*3600.0]
    switch $physicsModel {
        VBL {
            set Iexpected [expr $SRcurrent/pow(1+2./3.*$secs/60.0/$SRlifetime, 1.5)]
        } 
        CBL {
            set Iexpected [expr $SRcurrent/(1+$secs/60.0/$SRlifetime)]
        }
        default {
            set Iexpected [expr $SRcurrent*exp(-$secs/60.0/$SRlifetime)]
        }
    }
    set timeNow [exec timeconvert -now | token -last]
    set targetCurrent $Iexpected
    return "[format %.2f $Iexpected] mA expected at [exec timeconvert -seconds=[expr $timeNow+$secs] -text]"
}

