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

#
# $Log: not supported by cvs2svn $
# Revision 1.10  2004/01/03 22:44:06  borland
# Now use file select dialog for saving files.
#
# Revision 1.9  2001/01/27 19:55:33  borland
# Added decay constant.
#
# Revision 1.8  2000/12/17 21:23:28  borland
# Now handles integer-value PVs.
#
# Revision 1.7  2000/01/18 22:21:09  borland
# Added "random" mode to OscillatePVs.
# Added support for this to cawave.tcl routines, including new
# APSGetRandomNumbers procedure.
#
# Revision 1.6  1998/02/27 20:40:55  borland
# Displays result of return from APScaWave.
#
# Revision 1.5  1997/08/14 21:31:02  borland
# Added some context help.
#
# Revision 1.4  1997/03/12 22:04:17  borland
# Added Program entry to menu bar, with quickMonitor and namecapture entries.
#
# Revision 1.3  1997/03/12 21:58:51  borland
# Fixed bug in Save code (wasn't keeping track of input filename).
#
# Revision 1.2  1997/03/12 18:38:04  borland
# Added read and save file operations.
#
# Revision 1.1  1997/03/12 14:57:57  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)]
set CVSRevisionAuthor "\$Revision: 1.11 $ \$Author: shang $"
APSDebugPath

set PVList {}
set amplitudeList {}
set period 60
set stepsPerPeriod 10
set decayConstant 0
set periods 1
set function sin
set Alert no
set resetOnAbort 1
set configFile ""
set args $argv 
set usage "Usage: OscillatePVs {-configFile <name> | -PVList <names> -function {sin | cos | twCenter | random} \
 -amplitudeList <list> -period <seconds> \[-decayConstant <1/period>\] -stepsPerPeriod <number> -periods <number> \[-resetOnAbort 1\]}"

if {[APSStrictParseArguments \
    {PVList function amplitudeList period decayConstant stepsPerPeriod periods resetOnAbort configFile}] \
      || [llength $PVList]!=[llength $amplitudeList] \
      || [lsearch -exact [list sin cos twCenter random] $function]==-1 \
      || $period<1 || $stepsPerPeriod<2 || $decayConstant<0} {
    puts stderr $usage
    foreach variable {PVList function amplitudeList period stepsPerPeriod periods decayConstant} {
        puts stderr "$variable: [subst \$$variable]"
        }
    exit 1
}

lappend parameterList period stepsPerPeriod periods function resetOnAbort decayConstant
lappend columnList pvName amplitude 
set paramType(period) SDDS_DOUBLE
set paramType(decayConstant) SDDS_DOUBLE
set paramType(stepsPerPeriod) SDDS_LONG
set paramType(periods) SDDS_LONG
set paramType(function) SDDS_STRING
set paramType(resetOnAbort) SDDS_LONG
set columnType(pvName) SDDS_STRING
set columnType(amplitude) SDDS_DOUBLE
set columnVarRoot(pvName) pvName
set columnVarRoot(amplitude) pvAmp

#
# Permits reading in an SDDS file. 
#
set workingDir ""
proc readFile {args} {
    set fileName ""
    set scrollFrame ""
    if [APSStrictParseArguments {fileName scrollFrame}] {
        return -code error "readFile: bad arguments"
    }

    global workingDir parameterList columnList paramType columnType cpIndex 

    if ![string length $fileName] {
        set fileName \
          [APSFileSelectDialog .readDialog -listDir $workingDir \
             -contextHelp "Select a file to edit." \
             -title "Select file to edit."]
        update idletasks
    }

    if {![string length $fileName] || ![file exists $fileName]} {
        return
    }
    if ![APSCheckSDDSFile -fileName $fileName] {
        APSSetVarAndUpdate status "$fileName is not an SDDS file."
        return
    }
    set workingDir [file dirname $fileName]
    if [catch {sdds open $fileName r} inputID] {
        APSSetVarAndUpdate status "Error reading $fileName: $inputID"
        return
    }

    foreach parameterName $parameterList {
        global $parameterName
        if [catch {sdds getParameter $inputID $parameterName} $parameterName] {
            if [string compare $parameterName decayConstant]==0 {
                set $parameterName 0
                continue
            }
            APSSetVarAndUpdate status \
              "Error getting parameter data for $parameterName: [subst \$$parameterName]"
            return
        }
    }

    foreach columnName $columnList {
        global $columnName
        if [catch {sdds getColumn $inputID $columnName} ${columnName}List] {
            APSSetVarAndUpdate status "Error getting column data for $columnName: [subst \$${columnName}List]"
            return
        }
    }

    global cpIndex
    while {$cpIndex} {
        DeletePVSlot $scrollFrame
    }
    set index 0
    foreach elem $pvNameList {
        AddPVSlot -parent $scrollFrame -pvName [lindex $pvNameList $index] \
          -amplitude [lindex $amplitudeList $index]
        incr index
    }

    if [catch {sdds close $inputID} result] {
        APSSetVarAndUpdate status "Problem closing SDDS file: $result"
        return
    }
    APSSetVarAndUpdate status "File read successfully."
    global inputFile
    set inputFile $fileName
}

proc saveFile {} {
    global inputFile
    saveFileAs -fileName $inputFile
}

#
# Allows saving the data into a named file.
#
set fileSelectDir [pwd]
proc saveFileAs {args} {
    set fileName ""
    APSStrictParseArguments {fileName}
    global fileSelectDir

    global workingDir parameterList columnList paramType columnType cpIndex columnVarRoot
    if !$cpIndex {
        APSSetVarAndUpdate status "Can't save--no PVs given."
        return
    }

    APSSetVarAndUpdate status "Saving data..."

    if ![string length $fileName] {
        set outputFile [APSFileSelectDialog .chooseInputFile -listDir $fileSelectDir -checkValidity 0]
        if ![string length $outputFile] return
    } else {
        set outputFile $fileName
    }
    set fileSelectDir [file dirname $outputFile]
    
    if [catch {sdds open $outputFile w SDDS_BINARY} fid] {
        APSSetVarAndUpdate status "$fid"
        return
    }
    foreach parameter $parameterList {
        if [catch {eval sdds defineParameter $fid $parameter -type $paramType($parameter)} result] {
            APSSetVarAndUpdate status "$result"
            return
        }
    }

    foreach column $columnList {
        if [catch {eval sdds defineColumn $fid $column -type $columnType($column)} result] {
            APSSetVarAndUpdate status "$result"
            return
        }
    }
    
    if {[catch {sdds writeLayout $fid} result] || [catch {sdds startPage $fid $cpIndex} result]} {
        APSSetVarAndUpdate status "$result"
        return
    }

    foreach parameter $parameterList {
        global $parameter
        if [catch {sdds setParameter $fid $parameter [subst \$$parameter] } result] {
            APSSetVarAndUpdate status "$result"
            return
        }
    }

    foreach column $columnList {
        set dataList ""
        for {set index 1} {$index<=$cpIndex} {incr index} {
            global $columnVarRoot($column)$index
            lappend dataList [subst \$$columnVarRoot($column)$index]
        }
        if [catch {eval sdds setColumn $fid $column $dataList} result] {
            APSSetVarAndUpdate status "$result"
            return
        }
    }

    if {[catch {sdds writePage $fid} result] || \
          [catch {sdds close $fid} result]} {
        APSSetVarAndUpdate status "$result"
        return
    }
    APSSetVarAndUpdate status "Data saved to $outputFile"
}


set cpIndex 0
proc AddPVSlot {args} {
    set parent ""
    set pvName ""
    set amplitude 0
    APSStrictParseArguments {parent pvName amplitude}

    global cpIndex
    incr cpIndex
    set widget .$cpIndex

    global pvName$cpIndex pvAmp$cpIndex  pvInteger$cpIndex
    set pvName$cpIndex $pvName
    set pvAmp$cpIndex $amplitude
    set  pvInteger$cpIndex 0

    APSFrame $widget -parent $parent.frame.canvas.frame -label "" -relief flat
    set w $parent.frame.canvas.frame$widget.frame
    APSLabeledEntry .le0 -parent $w -label "PV name: " \
      -width 32 -textVariable pvName$cpIndex -packOption "-side  left" \
      -contextHelp "Enter the name of the process variable you want to oscillate."
    APSLabeledEntry .le1 -parent $w -label "Amplitude: " \
      -width 32 -textVariable pvAmp$cpIndex -packOption "-side  left" \
      -contextHelp "Enter the amplitude of the oscillation. The PV will be oscillated about the value it has when the script is Run."
    APSRadioButtonFrame .rb0 -parent $w -label "Integer? " \
      -variable pvInteger$cpIndex -valueList {0 1} -buttonList {No Yes} 
    
    tkwait visibility $w.le1
    APSScrollAdjust $parent -numVisible 5
}

proc MakeAddDeleteButtons {widget args} {
    set parent ""
    set scrollFrame ""
    APSStrictParseArguments {parent scrollFrame}
    APSFrame $widget -parent $parent -label ""
    set w $parent$widget.frame
    APSButton .add -parent $w -text Add -command \
      "AddPVSlot -parent $scrollFrame" -contextHelp \
      "Add a slot for another PV to include in the oscillation."
    APSButton .del -parent $w -text Delete -command \
      "DeletePVSlot $scrollFrame" -contextHelp \
      "Delete the last slot from the list of PVs to include in the oscillation."
}

proc DeletePVSlot {scrollFrame} {
    global cpIndex
    if $cpIndex {
        destroy $scrollFrame.frame.canvas.frame.$cpIndex
        incr cpIndex -1
    }
}

proc MakeRunStopButtons {widget args} {
    set parent ""
    APSStrictParseArguments {parent}
    APSFrame $widget -parent $parent -label ""
    set w $parent$widget.frame
    
    APSButton .run -parent $w -text Run -command \
      "RunOscillation $w" -contextHelp \
      "Begins the oscillations."
    APSButton .stop -parent $w -text Stop -command \
      "set abortOscillation 1" -contextHelp \
      "Aborts the oscillations at the next step."
    APSDisableButton $w.stop.button
}

set abortOscillation 0
proc RunOscillation {buttonRoot} {
    global cpIndex period stepsPerPeriod periods function resetOnAbort
    global decayConstant Alert
    if !$cpIndex {
        APSSetVarAndUpdate status "No PVs given to oscillate."
        return
    }
    set pvList ""
    set ampList ""
    set varList ""
    for {set index 1} {$index<=$cpIndex} {incr index} {
        global pvName$index pvAmp$index pvVar$index pvInteger$index
        set pvVar$index 0
        lappend pvList [subst \$pvName$index]
        lappend ampList [subst \$pvAmp$index]
        lappend varList [subst pvVar$index]
        lappend integerList [subst \$pvInteger$index]
    }
    global abortOscillation
    set abortOscillation 0
    APSDisableButton $buttonRoot.run.button
    APSEnableButton $buttonRoot.stop.button
    APSSetVarAndUpdate status "Starting oscillation"
    if {[catch {APScaWave -pvList $pvList -amplitudeList $ampList -variableList $varList \
                  -integerList $integerList -decayConstant $decayConstant \
                  -period $period -periods $periods -stepsPerPeriod $stepsPerPeriod \
                  -postStepCallback PostStepCallback -abortVariable abortOscillation \
                  -function $function -resetOnAbort $resetOnAbort} result] || \
          [string length $result]} {
        APSSetVarAndUpdate status "$result"
    }
    APSDisableButton $buttonRoot.stop.button
    APSEnableButton  $buttonRoot.run.button
    APSSetVarAndUpdate status "Done with oscillation"
    global Alert
    if {$Alert!="no"} {
        APSAlertBox .alertinfo -name "Ramp Done" -type done -name Done \
          -errorMessage "Done with ramp PVs." -modeless 1 -beep $Alert
    }
}

proc PostStepCallback {step steps} {
    APSSetVarAndUpdate status "Done with step $step of $steps"
}

APSApplication . -name OscillatePVs -version $CVSRevisionAuthor \
    -overview {This tool oscillates the values of process variables using three function types.}
set status Working
APSScrolledStatus .status -parent .userFrame -width 60 -textVariable status

APSLabeledEntry .periods -parent .userFrame -label "Periods: " \
  -contextHelp "Number of periods to execute" -textVariable periods -width 30
APSLabeledEntry .stepsPerPeriod -parent .userFrame -label "Steps per period: " \
  -contextHelp "Number of steps for each period." -textVariable stepsPerPeriod -width 30
APSLabeledEntry .period -parent .userFrame -label "Period (s): " \
  -contextHelp "Period of oscillation, in seconds." \
  -textVariable period -width 30
APSLabeledEntry .decay -parent .userFrame -label "Decay Constant (1/period): " \
  -contextHelp "Decay constant in units of the period." \
  -textVariable decayConstant -width 30
APSRadioButtonFrame .rb1 -parent .userFrame -label "Reset on abort: " \
  -orientation horizontal -variable resetOnAbort \
  -buttonList {Yes No} -valueList {1 0} -contextHelp \
  "Choose whether to reset PV to starting values when the oscillation is aborted."
APSRadioButtonFrame .rb0 -parent .userFrame -label "Function: " \
  -orientation horizontal -variable function -buttonList {Sine "Triangle wave" "Random number"} \
  -valueList {sin twCenter random} -contextHelp \
  "Choose the function to use for ramping."
APSRadioButtonFrame .alert -parent .userFrame -label "Alert after ramp is done or aborted?" \
  -buttonList {No "Beep Once" "Beep Continuously"} -valueList {no once continuous} \
  -variable Alert -orientation horizontal \
  -contextHelp "choose to play alert sound when oscillation is done or aborted."
MakeRunStopButtons .runStop -parent .userFrame

APSFrame .pvFrame -parent .userFrame -label "PV List Control" 
set name [APSUniqueName .x]
APSScroll $name -parent .userFrame.pvFrame.frame
set mainScrollFrame .userFrame.pvFrame.frame$name

MakeAddDeleteButtons .addDel -parent .userFrame -scrollFrame $mainScrollFrame

# Add Read..., Save, Save as... entries to File menu
.menu.file.menu insert 1 command -label "Read..." -command "readFile -scrollFrame $mainScrollFrame"
.menu.file.menu insert 1 command -label "Save" -command saveFile
.menu.file.menu insert 1 command -label "Save as..." -command saveFileAs

APSMenubarAddMenu .programs -text Programs -parent .menu
.menu.programs.menu add command -label quickMonitor -underline 0 -command "exec quickMonitor &"
.menu.programs.menu add command -label namecapture -underline 0 -command "exec namecapture &"


if [string length $configFile] {
    if ![file exists $configFile] {
        puts stderr "Not found: $configFile"
        exit 1
    }
    readFile -fileName $configFile -scrollFrame $mainScrollFrame
} elseif [llength $PVList] {
    set index 0
    foreach elem $PVList {
        AddControlPanel -parent $mainScrollFrame \
          -pvName $elem -amplitude [lindex $amplitudeList $index]
        incr index
    }
} 

