#!/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)]

APSDebugPath

# Needed for APSParseArguments
set args $argv

# Floating point formats
set offsetFormat "%12.6f"
set gainFormat "%7.6f"

# Initialize global vars
set cwd [pwd]
set status "Initializing..."
set configFileName ""
set configFileDir $cwd
set knobList {}
set knobsEnabled 0
set serverMode 0
set fileName ""
set invokingPort -1
set apsKnobFirstServerID ""
set windowName tclKnobs

set usage "usage: tclKnobs -fileName <filename> \[-configFileDir <string>\] \[-serverMode 1\] \[-invokingPort <portNumber>\] \[-apsKnobFirstServerID <string>\] \[-windowName <string>\]"

if [APSStrictParseArguments {serverMode invokingPort fileName configFileDir windowName apsKnobFirstServerID}] {
    return -code error $usage
}

if {$serverMode} {
    set knobsEnabled 1
    set fileName ""
}

#
# Permits selection of an SDDS knob configuration file. A knob control
# widget is added to the scroll box for each page in the file.
#
proc openDialog {} {
    global configFileDir
    set configFileName \
      [APSFileSelectDialog .openDialog -listDir $configFileDir \
         -contextHelp "Select a knob configuration file" \
         -title "Select Knob Configuration File:"]
    update idletasks
    openConfigFile $configFileName
}

#
# Reads knob configuration file, and adds a knob control widget to the
#  scroll box for each page in the file. Channel access connections are
#  established. If application not in server mode, errors cause alert
#  boxes. If in serverMode, errors cause a catchable error return. This
#  procedure returns the list of knob names found in the config file.
#
proc openConfigFile {fileName} {
    global env status knobList gainFormat offsetFormat
    global errorCode configFileDir configFileName serverMode
    global scrollWidget

    set configFileName $fileName
    set configFileDir [file dirname $fileName]

    #
    # Validate selected config file
    #
    if {![APSCheckSDDSFile -fileName $configFileName]} {
        if {!$serverMode} {
            APSAlertBox .badFile -errorMessage \
              "The file you selected ($configFileName) is not an SDDS file."
            return
        } else {
            return -code error "The file you selected ($configFileName) is not an SDDS file."
        }
    }
    #
    # Check for duplicates of ControlNames within a single page.
    #
    if [catch {exec sddssort $configFileName -pipe=out -column=ControlName -unique=count \
                 | sddsprocess -pipe -nowarning -filter=column,IdenticalCount,1,1,! \
                 "-test=param,n_rows 0 >" \
                 | sddscollapse -pipe \
                 | sdds2stream -rows -pipe | token -n=1} dups] {
        return -code error "Problem scanning file $configFileName for duplicate names: $dups"
    }
    if [expr $dups>0] {
        return -code error "File $configFileName contains duplicate ControlNames within a single page."
    }

    #
    # Open configuration file
    #
    if [catch {sdds open $configFileName} fd] {
        if {!$serverMode} {
            APSAlertBox .badFile -errorMessage \
              "Unable to open requested file: $fd"
            return
        } else {
            return -code error "Unable to open requested file: $fd"
        }
    }

    set configParms [APSGetSDDSNames -sddsFD $fd -class parameter]
    set configCols [APSGetSDDSNames -sddsFD $fd]
    set hasParmControlName [lsearch -exact $configParms ControlName]
    set hasParmControlUnits [lsearch -exact $configParms ControlUnits]
    set hasParmGain [lsearch -exact $configParms Gain]
    set hasParmControlType [lsearch -exact $configParms ControlType]
    set hasColControlName [lsearch -exact $configCols ControlName]
    set hasColWeight [lsearch -exact $configCols Weight]

    if {$hasParmControlName == -1 || $hasParmControlUnits == -1 || \
          $hasParmGain == -1 || $hasColControlName == -1 || \
          $hasColWeight == -1} {
        if {!$serverMode} {
            APSAlertBox .badFile -errorMessage \
              "The file you selected is missing needed parameters and/or columns"
            sdds close $fd
            return
        } else {
            sdds close $fd
            return -code error "The file you selected is missing needed parameters and/or columns"
        }
    }

    #
    # Load data to vars
    #
    set status "Loading knob configuration, please wait..."
    foreach knobName $knobList {
        set infoWindow ".${knobName}Info"
        if {[winfo exists $infoWindow]} {
            destroy $infoWindow
        }
        deleteKnob $knobName
    }
    update idletasks
    set knobList {}
    set page 1
    if [catch {APSGetSDDSParameter -sddsFD $fd \
                 -parameter ControlName -page $page} res] {
        set moreData ""
    } else {
        set moreData [lindex $res 0]
    }

    while {$moreData != ""} {
        global $moreData
        lappend knobList $moreData
        
        # Initialize array containing all data for this knob instance
        set ${moreData}(name) $moreData
        set ${moreData}(offset) [format $offsetFormat 0.0]
        set ${moreData}(widget) ""
        set ${moreData}(downButton) ""
        set ${moreData}(upButton) ""
        set ${moreData}(lowLimits) {}
        set ${moreData}(highLimits) {}
        set ${moreData}(saveOffset) ""
        set ${moreData}(saveGain) ""
        set ${moreData}(saveVector) ""
        set ${moreData}(units) [lindex [APSGetSDDSParameter -fileName \
                                          $configFileName -parameter \
                                          ControlUnits -page $page] 0]
        set gain [lindex [APSGetSDDSParameter -fileName \
                            $configFileName -parameter \
                            Gain -page $page] 0]
        set ${moreData}(gain) [format $gainFormat $gain]
        set ${moreData}(initialGain) [set ${moreData}(gain)]
        if [catch {APSGetSDDSParameter -fileName \
                     $configFileName -parameter \
                     KnobDescription -page $page} res] {
            set ${moreData}(desc) ""
        } else {
            set ${moreData}(desc) [lindex $res 0]
        }
        if {$hasParmControlType!=-1 && \
              ([lindex [APSGetSDDSParameter -sddsFD $fd \
                          -parameter ControlType -page $page] 0] == "dev")} {

            if [catch {APSGetSDDSParameter -sddsFD $fd \
                         -parameter ControlMsg -page $page} res] {
                set controlMsg ""
            } else {
                set controlMsg [lindex $res 0]
            }
            set nameVector [APSGetSDDSColumn -sddsFD $fd \
                              -column ControlName -page $page]
            set ${moreData}(nameVector) {}
            foreach name $nameVector {
                set cmd "exec devSend $name"
                if [catch $cmd result] {
                    set status "Unable to resolve $name as a device, using as a pv instead."
                    update idletasks
                    lappend ${moreData}(nameVector) $name
                } else {
                    set resultList [split $result " \n\t"]
                    set resultList2 {}
                    foreach thing $resultList {
                        if {[llength $thing]} {
                            lappend resultList2 $thing
                        }
                    }
                    set devIndex [lsearch -exact $resultList2 $controlMsg]
                    if {$devIndex == -1} {
                        lappend ${moreData}(nameVector) $name
                    } else {
                        set pvIndex [expr $devIndex + 1]
                        lappend ${moreData}(nameVector) \
                          [lindex $resultList2 $pvIndex]
                    }
                }
            }
        } else {
            set ${moreData}(nameVector) \
              [APSGetSDDSColumn -sddsFD $fd \
                 -column ControlName -page $page]
        }
        set ${moreData}(weightVector) \
          [APSGetSDDSColumn -sddsFD $fd \
             -column Weight -page $page]
        
        # Handle special case of one PV knob
        if {[llength [set ${moreData}(nameVector)]] == 0} {
            set ${moreData}(nameVector) [list [set ${moreData}(name)]]
            set ${moreData}(weightVector) [list 1.0]
        }
        incr page
        if [catch {APSGetSDDSParameter -sddsFD $fd \
                     -parameter ControlName -page $page} res] {
            set moreData ""
        } else {
            set moreData [lindex $res 0]
        }
    }
    sdds close $fd
    
    #
    # Display knob control widgets
    #
    if {!$serverMode} {
        foreach knobName $knobList {
            knobFrame $knobName $scrollWidget.frame.canvas.frame
        }
        set numVisible [llength $knobList]
        if {$numVisible > 8} {
            set numVisible 8
        }
        APSScrollAdjust $scrollWidget -numVisible $numVisible
        update idletasks
    }

    #
    # Make CA connections
    #
    set status "Making CA connections"
    foreach knobName $knobList {
        set nameList [set ${knobName}(nameVector)]
        eval global $nameList
        pv linkw $nameList $nameList
        if [knobGetCAstate $nameList] {
            set status "Unable to open all channel access connections."
            if {!$serverMode} {
                return
            } else {
                return -code error "connections failed"
            }
        }
        knobGetControlLimits $knobName
        knobGetVector $knobName
        knobUpdateFormattedVars $knobName
    }

    #
    # Create save/restore menu entries
    #
    .menu.save.menu add command -label "Save All" \
      -command "knobSave null 1"
    .menu.save.menu add separator
    .menu.restore.menu add command -label "Restore All" \
      -command "knobRestore null 1" -state disabled
    .menu.restore.menu add separator

    foreach knobName $knobList {
        .menu.save.menu add command -label "$knobName" \
          -command "knobSave $knobName"
        .menu.restore.menu add command -label "$knobName" \
          -command "knobRestore $knobName" -state disabled
    }

    set status "...done."
    set status "Use arrows to adjust knob in \"gain\" size ticks."
    return $knobList
}

#
# Display configuration information for knob
#
proc knobInfo {knob} {
    global $knob
    set infoWindow ".k${knob}Info"
    set exists [winfo exists $infoWindow]
    if {$exists} {
        raise $infoWindow
    } else {
        APSWindow $infoWindow -name "KnobInfo: $knob"
        label $infoWindow.userFrame.comp -text Components -bg Grey 
        pack $infoWindow.userFrame.comp -side top -fill x
        label $infoWindow.userFrame.tabt -text "        PV                 Weight          Current Value"
        pack $infoWindow.userFrame.tabt -side top
        set widgetList {}
        set nameList [set ${knob}(nameVector)]
        regsub -all \\. [set ${knob}(nameVector)] "" nameList0
        set weightList [set ${knob}(weightVector)]
        foreach item $nameList0 {
            lappend widgetList "k$item"
        }
        APSScroll .si -parent $infoWindow.userFrame
        set scrollParent $infoWindow.userFrame.si.frame.canvas.frame
        set i 0
        foreach item $nameList {
            global $item
            set widgetPart [lindex $nameList0 $i]
            set formattedItem "formatted$item"
            global $formattedItem
            
            APSFrameGrid .fg$widgetPart -parent $scrollParent -xList {pv w v}
            set weight [lindex $weightList $i]
            set widgetName [lindex $widgetList $i]
            label $scrollParent.fg$widgetPart.pv.$widgetName -text $item -width 29
            pack $scrollParent.fg$widgetPart.pv.$widgetName -side top
            label $scrollParent.fg$widgetPart.w.$widgetName -text \
              [format "%6e" $weight] -width 12
            pack $scrollParent.fg$widgetPart.w.$widgetName -side top
            label $scrollParent.fg$widgetPart.v.$widgetName -textvariable \
              $formattedItem -width 24
            pack $scrollParent.fg$widgetPart.v.$widgetName -side top
            #	    tkwait visibility $scrollParent.fg$widgetPart.v.$widgetName
            incr i
        }
        tkwait visibility $scrollParent
        APSScrollAdjust $infoWindow.userFrame.si -numVisible 15
    }
}

#
# Verify connection state after CA operation.
#    Return 0 if OK for all "linked" tcl vars in nameList, -1 otherwise
#    Sets status variable for each bad status.
#
proc knobGetCAstate {nameList} {
    global status errorCode
    set returnCode 0
    set stateList [pv info $nameList state]
    foreach item $stateList {
        if {[lindex $item 1] != "OK"} {
            set status "CA problem with [lindex $item 0]: $errorCode"
            set returnCode -1
        }
    }
    return $returnCode
}

#
# Load pv drive limits into knob array elements
#
proc knobGetControlLimits {knob} {
    global $knob
    set pvList [set ${knob}(nameVector)]
    eval global $pvList

    set lowList [pv info $pvList drvl]
    set highList [pv info $pvList drvh]
    set lowLimits {}
    set highLimits {}
    set i 0
    foreach item $lowList {
        lappend lowLimits [lindex $item 1]
        lappend highLimits [lindex [lindex $highList $i] 1]
        incr i
    }
    set ${knob}(lowLimits) $lowLimits
    set ${knob}(highLimits) $highLimits
}

#
# Get current knob vector (V)
#   Returns list of knob values if successful, empty list otherwise
#
proc knobGetVector {knob} {
    global $knob
    set pvList [set ${knob}(nameVector)]
    eval global $pvList
    pv getw $pvList
    if [knobGetCAstate $pvList] {
        return {}
    }
    set vector {}
    foreach pv $pvList {
        set newValue [set $pv]
        lappend vector $newValue
    }
    return $vector
}

#
# Put new knob vector
#
proc knobPutVector {knob valueList} {
    global $knob
    set pvList [set ${knob}(nameVector)]
    eval global $pvList
    set i 0
    foreach pv $pvList {
        set $pv [lindex $valueList $i]
        incr i
    }
    pv putw $pvList
    # This causes an update of global vars which have the current
    #   pv values in a more reasonable format for display.
    knobUpdateFormattedVars $knob
    return [knobGetCAstate $pvList]
}

#
# Update formatted knob vector (for display purposes)
#
proc knobUpdateFormattedVars {knob} {
    global $knob
    set pvList [set ${knob}(nameVector)]
    eval global $pvList
    foreach pv $pvList {
        set formatName "formatted$pv"
        global $formatName
        set $formatName [format "%6e" [set $pv]]
    }
}

#
# Take current knobVector and do vector addition to adjust knob values.
#    V + (gain*dir*weightVector)
# Return the new vector.
#
proc knobNewVector {knob dir currentVector} {
    global $knob
    set weightVector [set ${knob}(weightVector)]
    set nameVector [set ${knob}(nameVector)]
    eval global $nameVector
    set gain [set ${knob}(gain)]
    set i 0
    set newVector {}
    if {$dir == 1} {
        foreach e $currentVector {
            set adjustment [expr $gain * [lindex $weightVector $i]]
            set newValue [expr $e + $adjustment]
            lappend newVector $newValue
            incr i
        }
    } else {
        foreach e $currentVector {
            set adjustment [expr $gain * [lindex $weightVector $i]]
            set newValue [expr $e - $adjustment]
            lappend newVector $newValue
            incr i
        }
    }
    return $newVector
}

# 
# Process knob ticks. Returns knob offset after tick.
#
proc knobTick {knob dir} {
    global $knob offsetFormat knobsEnabled status

    if {!$knobsEnabled} {
        bell
        set status "You must enable the knobs first!"
        return
    }
    set currentOffset [set ${knob}(offset)]
    set gain [set ${knob}(gain)]
    set initialGain [set ${knob}(initialGain)]
    set gainRatio [expr $gain / $initialGain]
    set nameList [set ${knob}(nameVector)]
    
    if {$dir == "up"} {
        set highLimits [set ${knob}(highLimits)]
        set newVector [knobNewVector $knob 1 [knobGetVector $knob]]
        set pegged 0
        set i 0
        set peggedList ""
        foreach value $newVector name $nameList {
            if {$value > [lindex $highLimits $i]} {
                set pegged 1
                lappend peggedList  $name
            }
            incr i
        }
        if {$pegged} {
            bell
            set status "Knob $knob is pegged"
            set status "Pegged PVs: $peggedList"
            update
        } else {
            knobPutVector $knob $newVector
            set ${knob}(offset) [format $offsetFormat \
                                   [expr $currentOffset + $gainRatio]]
            set button [set ${knob}(upButton)]
        }
    } else {
        set lowLimits [set ${knob}(lowLimits)]
        set newVector [knobNewVector $knob -1 [knobGetVector $knob]]
        set pegged 0
        set i 0
        set peggedList ""
        foreach value $newVector name $nameList {
            if {$value < [lindex $lowLimits $i]} {
                set pegged 1
                lappend peggedList $name
            }
            incr i
        }
        if {$pegged} {
            bell
            set status "Knob $knob is pegged"
            set status "Pegged PVs: $peggedList"
            update
        } else {
            knobPutVector $knob $newVector
            set ${knob}(offset) [format $offsetFormat \
                                   [expr $currentOffset - $gainRatio]]
            set button [set ${knob}(downButton)]
        }
    }
    return [set ${knob}(offset)]
}

#
# Utility
#
proc getKnobOffset {knob} {
    global $knob
    return [set ${knob}(offset)]
}

# 
# Process knob gain change. Return new gain.
#
proc knobGain {knob dir} {
    global $knob gainFormat knobsEnabled status

    if {!$knobsEnabled} {
        bell
        set status "You must enable the knobs first!"
        return
    }

    set gain [set ${knob}(gain)]
    if {$dir == "up"} {
        if {$gain > "1000000.0"} {
            return [set ${knob}(gain)]
        }
        set ${knob}(gain) [format $gainFormat [expr $gain * 10]]
    } else {
        if {$gain < "0.000001"} {
            return [set ${knob}(gain)]
        }
        set ${knob}(gain) [format $gainFormat [expr $gain / 10]]
    }
    return [set ${knob}(gain)]
}

#
# Utility
#
proc getKnobGain {knob} {
    global $knob
    return [set ${knob}(gain)]
}

# 
# Restore knob gain to value originally read from config file.
#
proc restoreGain {knob} {
    global $knob knobsEnabled status

    if {!$knobsEnabled} {
        bell
        set status "You must enable the knobs first!"
        return
    }
    set ${knob}(gain) [set ${knob}(initialGain)]
}

proc knobSave {knob {all 0}} {
    global status knobsEnabled knobList

    if {!$knobsEnabled} {
        bell
        set status "You must enable the knobs first!"
        return
    }

    if {$all} {
        set saveList $knobList
    } else { 
        set saveList [list $knob]
    }
    .menu.restore.menu entryconfigure "Restore All" -state normal
    foreach item $saveList {
        global $item
        set ${item}(saveOffset) [set ${item}(offset)]
        set ${item}(saveGain) [set ${item}(gain)]
        set ${item}(saveVector) [knobGetVector $item]
        .menu.restore.menu entryconfigure $item -state normal
    }
}

proc knobRestore {knob {all 0}} {
    global $knob status knobsEnabled knobList

    if {!$knobsEnabled} {
        bell
        set status "You must enable the knobs first!"
        return
    }

    set restoreList {}
    if {$all} {
        foreach item $knobList {
            set restoreState [.menu.restore.menu entrycget $item -state]
            if {![string compare $restoreState "normal"]} {
                lappend restoreList $item
            }
        }
    } else {
        set restoreList [list $knob]
    }
    foreach item $restoreList {
        global $item
        set ${item}(offset) [set ${item}(saveOffset)]
        set ${item}(gain) [set ${item}(saveGain)]
        knobPutVector $item [set ${item}(saveVector)]
    }
}

#
# Clean up a knob widget, associated ca connections, and array variable.
# Also, delete the save/restore menu entries.
#
proc deleteKnob {knob} {
    global $knob
    destroy [set ${knob}(widget)]
    unset ${knob}(name) 
    unset ${knob}(offset) 
    unset ${knob}(widget) 
    unset ${knob}(downButton)
    unset ${knob}(upButton)
    unset ${knob}(units) 
    unset ${knob}(gain) 
    unset ${knob}(initialGain)
    unset ${knob}(desc) 
    unset ${knob}(weightVector) 
    unset ${knob}(lowLimits)
    unset ${knob}(highLimits)
    unset ${knob}(saveOffset)
    unset ${knob}(saveGain)
    unset ${knob}(saveVector)

    set pvList [set ${knob}(nameVector)]
    eval global $pvList
    pv unlink $pvList

    unset ${knob}(nameVector) 

    .menu.save.menu delete 0 last
    .menu.restore.menu delete 0 last
}

#
# Create a frame for one knob
#
proc knobFrame {knob parent} {
    global $knob
    set kWidget .[APSUniqueName k]
    set ${knob}(widget) $parent$kWidget

    # Set up framing
    APSFrame $kWidget -parent $parent
    APSFrameGrid .fg -parent $parent$kWidget.frame -yList \
      {top bottom}
    set top $parent$kWidget.frame.fg.top
    set bottom $parent$kWidget.frame.fg.bottom

    # Build top frame
    APSLabeledOutput .knob -parent $top -label Knob: -width 28 \
      -textVariable ${knob}(name) -packOption "-side left" -contextHelp \
      "This is the composite knob name as given in the config file."
    APSLabeledOutput .desc -parent $top -label Desc: -width 38 \
      -textVariable ${knob}(desc) -packOption "-side left" -contextHelp \
      "This is the KnobDescription parameter from the config file."

    # Build bottom frame
    APSButton .tickDown -parent $bottom -text "<-" -command \
      "knobTick $knob down" -contextHelp "This button takes the current knob vector (V), weight vector (W), and gain (g), and writes the following:\n    V - (gW)" -fastClick 1
    set ${knob}(downButton) $bottom.tickDown.button

    APSButton .tickUp -parent $bottom -text "->" -command \
      "knobTick $knob up" -contextHelp "This button takes the current knob vector (V), weight vector (W), and gain (g), and writes the following:\n    V + (gW)" -fastClick 1
    set ${knob}(upButton) $bottom.tickUp.button

    APSLabeledOutput .offset -parent $bottom -label Offset: -width 12 \
      -textVariable ${knob}(offset) -packOption "-side left" -contextHelp \
      "Displays running sum of knob changes. The offset is normalized to the initial gain value. If you return the offset to zero, your knob vector will be back in its initial state."

    APSLabeledOutput .gain -parent $bottom -label Gain: -width 8 \
      -textVariable ${knob}(gain) -packOption "-side left" -contextHelp \
      "This value is multiplied against the weight vector (W) and then added or subtracted from the current knob vector (V)."

    APSButton .gainDown -parent $bottom -text "/10" -command \
      "knobGain $knob down" -contextHelp "Reduces gain by factor of 10."
    APSButton .gainUp -parent $bottom -text "*10" -command \
      "knobGain $knob up" -contextHelp "Increases gain by factor of 10."
    APSButton .gainRestore -parent $bottom -text "Restore Gain" -command \
      "restoreGain $knob" -contextHelp "Restore gain to original setting"
    APSButton .info -parent $bottom -text Info... -command "knobInfo $knob" \
      -contextHelp "Display more details on knob, including current values."
    tkwait visibility $bottom.info
}

#
# Set up graphical interface for tclKnobs
#
APSApplication . -name $windowName  \
  -overview "Assigns weighted vectors of process variables to tcl buttons. Weights and pv names are specified via an SDDS knobconfig file." \
  -contextHelp "Assigns weighted vectors of process variables to tcl buttons."

if {$serverMode} {
    wm withdraw .
}

# Add Open... entry to File menu
.menu.file.menu insert 1 command -label "Open..." -command openDialog

APSMenubarAddMenu .save -parent .menu -text "Save"
APSMenubarAddMenu .restore -parent .menu -text "Restore"

APSScrolledStatus .ss -parent .userFrame -textVariable status \
  -packOption "-side top -fill x" -height 4 -width 60 \
  -contextHelp "Provides execution status and operation hints."

APSRadioButtonFrame .enable -parent .userFrame -variable knobsEnabled \
  -valueList {0 1} -buttonList {Disabled Enabled} -orientation horizontal \
  -label "Knob Controls" -contextHelp "Disables/Enables all knob controls."

APSScroll .sk -parent .userFrame -packOption "-fill both -expand true"
set scrollWidget .userFrame.sk


set tclKnobsDebug 0
set tclKnobsDebugFD ""
proc LogTclKnobsDebug {text} {
    global tclKnobsDebug tclKnobsDebugFD
    if !$tclKnobsDebug return
    if ![string length $tclKnobsDebugFD] {
        set tclKnobsDebugFD [open /tmp/tclKnobsDebug.tmp w]
    }
    puts $tclKnobsDebugFD "$text"
    flush $tclKnobsDebugFD
}

# Set ourself up as an RPC server at a well known port
if {$serverMode} {
    #Loading the comm package in effect starts a communication server.
    #This is called the SecondServer because tclKnobs was started
    #by another application which is already running the FirstServer.
    package require comm
    set apsKnobSecondServerID "[::comm::comm self]"
    
    # Contact invoking process to let them know our port number
    ::comm::comm send $apsKnobFirstServerID "APSKnobChildUp $apsKnobSecondServerID"
    
    ::comm::comm hook lost {
        exit
    }
} else {
    tkwait visibility $scrollWidget
}

proc statusUpdate {varName index op} {
    global $varName

    puts stderr "status: [set $varName]"
}

# check configuration file directory
if ![string length $configFileDir] {
    set configFileDir .
}
if ![file isdirectory $configFileDir] {
    set status "Can't find configuration directory $configFileDir."
}

# Open up config file if given on command line
if {[string length $fileName]} {
    openConfigFile $fileName
} else {
    #    trace variable status w statusUpdate
    set status "Select a knob configuration from File/Open menu."
}
