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

#
# $Log: not supported by cvs2svn $
# Revision 1.31  2010/01/25 20:06:01  shang
# added "View Installed Status" button to view all binary status in one GUI with colored check buttons, and text status in one window.
#
# Revision 1.30  2010/01/20 22:20:31  shang
# modified to make the xray bpm P1 and P2 bpms always have the same preamplifier number.
#
# Revision 1.29  2009/09/29 02:22:36  lemery
# Removed extraneous " for time setting.
# Added procedure name to some error returns.
# In comments review, added way to check whether the old
# configs have missing Names from a new fields, and ignore them.
# Fixed the section that adds OkForRMSCalculationH (or V) fields
# to save file if none existed in read file. (Problem introduced
# in version 1.27 but I don't know why this did not cause a problem
# all these years.)
#
# Revision 1.28  2009/09/24 13:35:08  shang
# added SDDS_DOUBLE type to include the corrector limit field so that one change the limit of individual corrector. The limits will be used in orbit correction, the SROrbitControllaw no longer has corrector limit inputs.
#
# Revision 1.27  2006/08/08 20:02:41  shang
# added OkForRMSCalculation button; modified to prevent different users change the same
# device at the same time but allow changing different devices, the other devices changed by
# other users will be updated (no longer lost).
#
# Revision 1.26  2006/05/24 17:45:47  shang
# added "-system $system" to ReedApplicationConfig line so that the label can be shown for all BPMs including those that are not in the config.sdds; set the deviceList to orderedFullDeviceList after reading configuration so that one can add new device, otherwise, one can only change the device from previous configuration; added -nowarnings to process changeDevice in SaveConfiguration proc to avoid return errors when there is no match; modified to avoid overwriting deviceList (global variable) in processing change device loging.
#
# Revision 1.25  2006/02/22 22:30:57  borland
# Sped up the checking for the comments.sdds files by just using file exist instead
# of sddscheck.
#
# Revision 1.24  2006/02/22 20:57:20  soliday
# Fixed GUI problem I introduced with test, vcorrectors and hcorrectors systems.
#
# Revision 1.23  2006/02/22 20:01:40  soliday
# H.Shang's changes that I ment to commit last time.
#
# Revision 1.22  2006/02/22 19:41:45  soliday
# Changes made by H.Shang and R.Soliday. The GUI has changed to be a
# little more compact.
#
# Revision 1.21  2006/02/13 15:58:31  borland
# Changed location of BPM test area to be more generic.
#
# Revision 1.20  2006/01/20 19:27:21  borland
# Now allows save even if only the comment has changed.
#
# Revision 1.19  2006/01/19 15:07:28  borland
# Now saves all individual files if the equations are regenerated.
#
# Revision 1.18  2006/01/18 19:16:25  borland
# Added comment history button.
#
# Revision 1.17  2006/01/18 18:47:28  borland
# Added email button to scrolled list window for overview display.
#
# Revision 1.16  2006/01/18 18:01:25  borland
# If combinedFilesOnly=0, now stores data for each device in a separate directory,
# which makes the history review much faster.  Still stores all the data in a
# single file, so other applications are unaffected.
#
# Revision 1.15  2005/07/29 13:30:26  borland
# Added -nowarning option to several instances of sddsexpand.
#
# Revision 1.14  2004/05/13 14:58:36  emery
# Added button for the BPM special feature to run
# the external script makeSRXrayBPMequations.
#
# Revision 1.13  2004/01/21 16:09:49  borland
# Fixed bug introduced in last round of changes that resulted in wrong TimeStamp and
# User being displayed for in history printouts.
#
# Revision 1.12  2004/01/16 17:32:53  borland
# Fixed bug that resulted in duplication of records for devices.
#
# Revision 1.11  2003/11/21 22:42:02  borland
# This version works properly when the fields.sdds or suffixes.sdds file have
# lines added.  This allows adding suffixes or fields without having to
# manually modify old configuration files.
#
# Revision 1.10  2003/03/25 23:27:14  emery
# Reformatted some lines.
#
# Revision 1.9  2002/05/22 18:58:01  borland
# Sets the BadBO for the ID BPMs now also.
#
# Revision 1.8  2002/05/22 18:13:23  borland
# Added button to allow setting the BadBO PVs on the SR bpms.
#
# Revision 1.7  2002/05/22 15:37:54  borland
# No longer excludes 'Comments' field from consideration in preparing history
# output.
#
# Revision 1.6  2001/10/19 23:18:37  shang
# modified ShowHistoryCorrectors {} and ShowHistoryBPMs {} procedures so that it
# only retrieves the record of device that has been changed
#
# Revision 1.5  2000/10/25 15:39:31  borland
# Now invokes print feature of APSScrolledListWindow for overview function.
#
# Revision 1.4  2000/10/20 20:06:30  borland
# Fixed show history function for BPMs.
#
# Revision 1.3  2000/10/19 17:06:45  borland
# Added support for enumerated fields in the configuration.
#
# Revision 1.2  2000/10/19 16:16:03  borland
# Added plane sensitivity for nonexistence PVs.  Really just affects enable/
# disable for other widgets.
#
# Revision 1.1  2000/10/17 16:14:33  borland
# First version of a generalized ManageSRBPMStatus.
#
# Revision 1.1  2000/10/13 16:52:43  borland
# First version.
#
#

set CVSRevisionAuthor "\$Revision: 1.32 $ \$Author: shang $"

proc setStatus {text} {
    global status
    set status "[clock format [clock seconds] -format "%m%d %H:%M:%S"]  $text"
    update
}

set SRDeviceStatusDebug 0

proc debugStatus {text} {
    global SRDeviceStatusDebug
    if !$SRDeviceStatusDebug return
    setStatus "$text"
}

proc CheckAddDevice {deviceName} {
    #
    # The purpose of this procedure is to add a device to the database.
    # It is only needed in the event that the suffixes file has changed.
    # Basically, as the user tries to add data for the new BPM, an entry is created.
    #
    global requiredFieldList defaultValueList deviceList orderedFullDeviceList
    if [lsearch -exact $orderedFullDeviceList $deviceName]==-1 {
        return -code error "Error: $deviceName not in ordered list.  Seek expert help!"
    }
    if [lsearch -exact $deviceList $deviceName]!=-1 return
    debugStatus "CheckAddDevice adding $deviceName"
    foreach field $requiredFieldList default $defaultValueList {
        set $field\($deviceName\) $default
    }
}

proc ProcessSelectionChange {} {
    # This procedure is called when the user changes the device he is working on.
    # It's main job is to copy the data from the widgets back to the arrays for the
    # device that was just worked on, and to copy data for the new device-to-edit
    # the the working widgets.

    global sectorSelection deviceTypeSelection system
    global sectorSelectionSaved deviceTypeSelectionSaved
    global SelectionWidget deviceList
    
    APSDisableWidget $SelectionWidget

    global requiredFieldList
    set lastDeviceName [GetDeviceName -system $system -sector $sectorSelectionSaved -device $deviceTypeSelectionSaved]
    set newDeviceName [GetDeviceName -system $system -sector $sectorSelection -device $deviceTypeSelection]
   
    CheckAddDevice $newDeviceName
    set FieldsHaveChanged 0
    global DeviceName
    foreach field $requiredFieldList {
        global $field
        if [string compare [set $field\(0\)] [set $field\($lastDeviceName\)]]!=0 {
            set FieldsHaveChanged 1
        }
        set $field\(${lastDeviceName}\) [set $field\(0\)]
        if {[regexp {ID|BM} $lastDeviceName type b c] && $field=="PreampSerialNumber"} {
            regexp {:(.*)} $lastDeviceName a pv c
            #pv has to be P1 or P2, the xbpm preamplifier are the same for P1 and P2 at the same sector
            if {$pv=="P2"} {
                set otherPV [regsub {P2} $lastDeviceName P1]
            } else {
                set otherPV [regsub {P1} $lastDeviceName P2]
            }
            set ${field}($otherPV) [set ${field}(${lastDeviceName})]
        }
        set $field\(0\) [set $field\(${newDeviceName}\)]
    }

    setStatus "$FieldsHaveChanged fields were changed for $lastDeviceName"
    if $FieldsHaveChanged { 
        global OriginalComment changedDeviceList
        lappend changedDeviceList $lastDeviceName
        if [string compare $Comments($lastDeviceName) $OriginalComment($lastDeviceName)]==0 {
            setStatus "WARNING: You changed $lastDeviceName but didn't change the comment.\nYou should always enter a new comment whenever you change a device's status."
            bell
        }
    }
    set OriginalComment\($lastDeviceName\) $Comments($lastDeviceName)

    set sectorSelectionSaved $sectorSelection
    set deviceTypeSelectionSaved $deviceTypeSelection

    foreach plane {H V ""} {
        if [info exists Nonexistent$plane] {
            if [set Nonexistent$plane\($newDeviceName\)] {
                set state disabled
            } else {
                set state normal
            }
            ChangeInputWidgetStates -state $state -plane $plane
        }
    }
    APSEnableWidget $SelectionWidget
}

proc ChangeInputWidgetStates {args} {
    set state normal
    set plane ""
    APSStrictParseArguments {state plane}
    global inputFieldWidgetList inputFieldPlaneList
    foreach item $inputFieldWidgetList itemPlane $inputFieldPlaneList {
        if [string compare $itemPlane $plane] continue
        catch {$item configure -state $state}
    }
}

proc CheckConfigChanged {args} {
    set changedDeviceList ""
    APSParseArguments {changedDeviceList}
    
    if ![llength $changedDeviceList] {
        return
    }
    setStatus "$changedDeviceList"
    global ConfigMTime0 loadingDate dataDir 
    set mtime [GetConfigMTime]
    if {$mtime>$ConfigMTime0} {
        set tmpRoot /tmp/[APSTmpString]
        #set changeList [lsort [glob -nocomplain changeLog${loadingDate}*]]
	set currentDate [clock format $mtime  -format "%Y %j"]

	set changeList [APSFindFilesBetweenDates \
			    -rootname changeLog \
			    -directory $dataDir \
			    -startDateList $loadingDate\
			    -endDateList $currentDate]
	
        if ![llength $changeList] {
            return -code error "The config file was changed after loading, you need reload the configuration before saving."
        }
        APSAddToTmpFileList -ID SRStatus -fileList $tmpRoot.1
        if [catch {eval exec sddscombine $changeList -merge -pipe=out \
                       | sddssort -pipe -col=Time \
                       | sddsprocess -pipe "-filter=col,Time,0,$ConfigMTime0,!"  \
                       -nowarnings \
		       | sddsprocess -pipe -nowarnings "-filter=col,Time,0,$mtime" \
                       | sddssort -pipe=in -col=DeviceName -col=Time,decreasing -numericHigh  -unique \
                       $tmpRoot.1 } result] {
            return -code error $result
        }
        set rows [exec sdds2stream -rows=bar $tmpRoot.1]
        if !$rows {
            return -code error "No changes were logged, even though the config.sdds file changed."
        }
        if [catch {sdds load $tmpRoot.1 changeData} result] {
            return -code error $result
        }
        set deviceList [lindex $changeData(Column.DeviceName) 0]
        set timeList [lindex $changeData(Column.Time) 0]
        foreach device $changedDeviceList {
            set index [lsearch $deviceList $device]
            if [lsearch $deviceList $device]>=0 {
                set user [lindex [lindex $changeData(Column.User) 0] $index]
                set workstation [lindex [lindex $changeData(Column.Station) 0] $index]
                set timeStamp [lindex [lindex $changeData(Column.TimeStamp) 0] $index]
                set time [lindex $timeList $index]
                if {$time>$ConfigMTime0} {
                    return -code error "$device has been changed by $user on $workstation at $timeStamp"
                }
            }
        }
        unset changeData
        #update the fields of devices that were changed
        set option -match=col
        set first 1
        foreach device $deviceList { 
            if {!$first} {
                append option ,DeviceName=$device,||
            } else {
                append option ,DeviceName=$device
            }
            set first 0
        }
        global requiredFieldList
        setStatus "updating the changed devices: [join $deviceList ,]"
        foreach field $requiredFieldList {
            global $field
        }
        APSAddToTmpFileList -ID SRStatus -fileList $tmpRoot.2
        if [catch {exec sddsprocess config.sdds $option $tmpRoot.2} result] {
            return -code error $result
        }
        if [catch {sdds load $tmpRoot.2 newData} result] {
            return -code error $result
        }
        set index 0
        foreach device [lindex $newData(Column.DeviceName) 0] {
            foreach field $requiredFieldList {
                set $field\($device\) [lindex [lindex $newData(Column.$field) 0] $index]
            }
            set OriginalComment($device) $Comments($device)
            incr index
        }
        setStatus "updating done." 
	set loadingDate $currentDate
    }
}

proc SaveConfiguration {} {
    global requiredFieldList requiredFieldTypeList requiredFieldDescripList env
    global deviceName deviceList changedDeviceList assumeAllChanged
    global OriginalComment orderedFullDeviceList system ConfigMTime0
    global sectorSelection deviceTypeSelection sectorSelectionSaved deviceTypeSelectionSaved system
    
    set newDeviceName [GetDeviceName -system $system -sector $sectorSelection -device $deviceTypeSelection]
    setStatus "[pwd]"
    if {![APSMultipleChoice [APSUniqueName .] \
            -question "Really? Save and install this configuration?" \
            -labelList "Yes No" -returnList "1 0"]} {
        return
    }

    foreach field $requiredFieldList {
        global $field
    }

    if !$assumeAllChanged {
        set FieldsHaveChanged 0
        foreach field $requiredFieldList {
            if [string compare $field Comments]!=0 {
                if [string compare [set $field\(0\)] [set $field\($newDeviceName\)]]!=0 {
                    setStatus "$field changed for $newDeviceName"
                    incr FieldsHaveChanged
                }
            }
        }
        
        if $FieldsHaveChanged {
            if [string compare $Comments(0) $OriginalComment($newDeviceName)]==0 {
                if {![APSMultipleChoice [APSUniqueName .] \
                        -question "WARNING: You changed $newDeviceName but didn't change the comment.\nYou should always enter a new comment whenever you change a device's status.  What do you want to do?" \
                        -labelList {"Continue" "Abort"} \
                        -returnList {1 0}]} {
                    return
                }
            }
            lappend changedDeviceList $newDeviceName
        } else {
            if [string compare $Comments(0) $OriginalComment($newDeviceName)]!=0 {
                lappend changedDeviceList $newDeviceName
            }
            set FieldsHaveChanged 1
        }
        
        debugStatus "Fields have changed for $newDeviceName"

        if [llength $changedDeviceList]==0 {
            setStatus "Nothing has changed, no save needed."
            return
        } else {
            
        }
    }
    if [catch {CheckConfigChanged -changedDeviceList $changedDeviceList} result] {
	APSAlertBox .warning -errorMessage "$result; status not changed - another user online." -type warning
        setStatus "$result"
	setStatus "$changedDeviceList status not changed - another user online."
        return
    }
    foreach field $requiredFieldList {
        set $field\($newDeviceName\) [set $field\(0\)]
    }
    set OriginalComment\($newDeviceName\) $Comments($newDeviceName)

    global combinedFilesOnly 
    setStatus "Saving combined file..."
    debugStatus "[llength $deviceList] devices present" 

    foreach field $requiredFieldList type $requiredFieldTypeList description $requiredFieldDescripList {
        set dataList ""
        # Use the ordered device list so the file isn't saved in some random order
        foreach device $orderedFullDeviceList {
            if [lsearch -exact $deviceList $device]!=-1 {
                lappend dataList [set $field\($device\)]
            }
        }
        set outputData(Column.$field) [list $dataList]
        set outputData(ColumnInfo.$field) "type $type description \"$description\""
    }
    
    set outputData(ColumnNames) $requiredFieldList
    set outputData(ParameterNames) "Time TimeStamp User Host"
    set outputData(ParameterInfo.Time) "type SDDS_LONG units s"
    set theTime [clock seconds]
    set outputData(Parameter.Time) [list $theTime]
    set outputData(Parameter.TimeStamp) [list [clock format $theTime -format %Y/%m/%d@%H:%M:%S]]
    global apsScriptUser apsScriptHost
    set outputData(Parameter.User) [list $apsScriptUser]
    set outputData(Parameter.Host) [list $apsScriptHost]
    
    set filename config-[clock format $theTime -format %Y-%j-%m%d-%H%M%S]
    if [catch {sdds save $filename outputData
        file delete -force config.sdds 
        exec ln -s $filename config.sdds} result] {
        unset outputData
        return -code error "SaveConfiguration: Unable to save file: $result"
    }
    set ConfigMTime0 $theTime
    unset outputData
    setStatus "Data saved to $filename."
    
    if $assumeAllChanged {
        if [catch {exec sdds2stream -column=DeviceName config.sdds} changedDeviceList] {
            return -code error "$changeDeviceList"
        }
    } else {
        set changedDeviceList [lsort -unique $changedDeviceList]
    }
    if [llength $changedDeviceList] {
        set timeStamp [clock format $theTime -format "%Y %m/%d %H:%M:%S"]
        regsub {config-} $filename "changeLog" filename1 
        if [catch {exec sddsmakedataset -pipe=out \
                       -col=DeviceName,type=string -data=[join $changedDeviceList ,] \
                       | sddsprocess -pipe=in $filename1 \
                       -print=col,User,$env(USER) \
                       -define=col,Time,$theTime,type=long \
                       "-print=col,TimeStamp,$timeStamp" } result] {
            setStatus "SaveConfiguration: Unable to create changeLog file: $result"
        } 
    }
    if !$combinedFilesOnly {
        setStatus "Saving individual files"
        
        foreach changedDevice $changedDeviceList {
            setStatus "Working on $changedDevice"
            if ![file exist $changedDevice] {
                exec mkdir -m 777 $changedDevice
            }
            if [catch {exec sddsprocess $filename $changedDevice/[os editstring Z- $filename] \
                           -match=column,DeviceName=$changedDevice -nowarnings} result] {
                return -code error "SaveConfiguration: $result"
            }
            if [string match "bpm*" $system] {
                if [catch {ShowHistoryBPMs -deviceName $changedDevice -commentsOnly 1 \
                               -display 0} result] {
                    return -code error "SaveConfiguration: $result"
                }
            }
        }
        setStatus "Data saved in [llength $changedDeviceList] individual files"
    }
    set changedDeviceList ""
    set assumeAllChanged 0
}

proc GetDeviceName {args} {
    set sector ""
    set device ""
    set system ""
    APSParseArguments {sector device system}
    if [regexp {booster} $system] {
        set deviceName $sector$device
    } else {
        if [regexp {ID} $device] {
            set device [regsub "ID" $device "IDFE-XBPM"]
        } elseif [regexp {BM} $device] {
            set device [regsub "BM" $device "BMFE-XBPM"]
        }
        if [regexp "SB" $sector] {
            set deviceName S${sector}-$device
        } else {
            set deviceName S${sector}$device
        }
    }
    
    return $deviceName
}

proc ReadConfiguration {args} {
    set interactive 0
    APSStrictParseArguments {interactive}
    global requiredFieldList requiredFieldTypeList 
    global deviceName  deviceList system
    global defaultValueList orderedFullDeviceList

    if $interactive {
        set configList [lsort -decreasing [glob -nocomplain config-????-???-????-??????]]
        set configFile [APSChooseItemFromList -name "Choose configuration" \
                          -itemList $configList -returnIndices 0]
        if ![string length $configFile] return
    } else {
        set configFile config.sdds
    }

    setStatus "Reading configuration..."
    if [catch {sdds load $configFile inputData} result] {
        setStatus "Unable to read configuration: $result"
        return
    }
    set exampleField [lindex $inputData(ColumnNames) 0]
    debugStatus "File has [llength [lindex $inputData(Column.$exampleField) 0]] rows"

    # Check if all "required" fields are in the configuration file.  If not,
    # insert the default value from the fields.sdds file for that column.
    foreach field $requiredFieldList default $defaultValueList {
        if [lsearch -exact $inputData(ColumnNames) $field]==-1 {
            setStatus "Warning: field $field not found in config.sdds"
            if [regexp {OkForRMSCalculation} $field] {
                set valList ""
                foreach device [lindex $inputData(Column.DeviceName) 0] {
                    if {[regexp {A:P0} $device] || [regexp {A:P1} $device] || \
			[regexp {B:P0} $device] || [regexp {B:P1} $device] } {
                        lappend valList 1
                    } else {
                        lappend valList 0
                    }
                }
                set inputData(Column.$field) [list $valList]
            } else {
                set inputData(Column.$field) [list [APSReplicateItem -item $default -number [llength [lindex $inputData(Column.$exampleField) 0]]]]
            }
        }
        global $field
    }

    global OriginalComment
    set index 0
    foreach device [lindex $inputData(Column.DeviceName) 0] {
       # if [lsearch -exact $orderedFullDeviceList $device]==-1 {
       #     continue
       # }
        foreach field $requiredFieldList {
            set $field\($device\) [lindex [lindex $inputData(Column.$field) 0] $index]
        }
        if [lsearch -exact $orderedFullDeviceList $device]==-1 {
            return -code error "Error: $device not in full ordered list.  Seek expert help!"
        }
        set OriginalComment($device) [lindex [lindex $inputData(Column.Comments) 0] $index]
        incr index
    }
    set deviceList [array names OriginalComment]
   
    setStatus "Configuration data for [llength $deviceList] devices loaded"

    global sectorSelection deviceTypeSelection
    
    set newDeviceName [GetDeviceName -system $system -sector $sectorSelection -device $deviceTypeSelection]
    foreach field $requiredFieldList {
        set $field\(0\) [set $field\($newDeviceName\)]
    }
    set deviceList $orderedFullDeviceList 
    unset inputData
    ProcessSelectionChange
}

proc ShowHistory {} {
    global system
    switch $system {
        bpms -
        bpmsTest -
        boosterbpms {
            ShowHistoryBPMs
        }
        vcorrectors -
        hcorrectors {
            ShowHistoryCorrectors
        }
    }
}

proc ShowCommentHistory {} {
    global system
    switch $system {
        bpms -
        bpmsTest {
            ShowHistoryBPMs -commentsOnly 1
        }
        vcorrectors -
        hcorrectors {
            ShowHistoryCorrectors -commentsOnly 1
        }
    }
}

proc ShowHistoryBPMs {args} {
    set commentsOnly 0
    set deviceName ""
    set display 1
    APSStrictParseArguments {commentsOnly deviceName display}
    
    global sectorSelection deviceTypeSelection system combinedFilesOnly system
    if ![string length $deviceName] {
        set deviceName [GetDeviceName -system $system -sector $sectorSelection -device $deviceTypeSelection]
    }
    if ![file exist $deviceName] {
        exec mkdir -m 777 $deviceName
    }
    if $combinedFilesOnly {
        set fileList [lsort [glob -nocomplain config-????-???-????-??????]] 
    } else {
        if [file exist $deviceName/combinedData.sdds] {
            set fileList $deviceName/combinedData.sdds
            eval lappend fileList [lsort [glob -nocomplain $deviceName/????-???-????-??????]]
        } else {
            set fileList [lsort [glob -nocomplain $deviceName/????-???-????-??????]]
        }
    }
    # for some reason sdsdquery always return a "child killed: write on
    # pipe with no readers" error so I add readAll option.
    catch {eval exec sddscombine $fileList -pipe=out \
               | sddsquery -col -pipe=in -readAll} existingColumnList
    set existingColumnList [split $existingColumnList]

    # The file fields may have new names not included in existingColumnList 
    if [catch {sdds load fields.sdds fd} result] {
        return -code error "ShowHistoryBPMs: $result"
    }
    set option1 -print=column,Key,
    set option2 ""
    set names [lindex $fd(Column.Name) 0]
    set types [lindex $fd(Column.Type) 0]
    set excludedList [list timestamp time user host numbercombined filename devicename pagenumber]
    foreach name $names type $types {
        if {-1==[lsearch -exact $existingColumnList $name]} {
            continue
        }
        set nm [string tolower $name]
        if {[lsearch -exact $excludedList $nm]==-1} {
            if {[string compare $type string]==0} {
                set option1 ${option1}-%s
            } elseif {[string compare $type character]==0} {
                set option1 ${option1}-%c
            } elseif {[string compare $type long]==0} {
                set option1 ${option1}-%ld
            } elseif {[string compare $type short]==0} {
                set option1 ${option1}-%hd
            } elseif {[string compare $type double]==0 || [string compare $type float]==0} {
                set option1 ${option1}-%.6g
            } else {
                continue
            }
            set option2 ${option2},$name
        }
    }
    set option ${option1}${option2}

    set tmpFile /tmp/[APSTmpString]
    APSAddToTempFileList $tmpFile
    if [llength $fileList] {
        if {[catch {eval exec sddscombine $fileList -pipe=out \
                        | sddsprocess -pipe \
                        -match=column,DeviceName=$deviceName -nowarning \
                        $option \
                        | sddsexpand -pipe -nowarning \
                        | sddscollapse -pipe \
                        | sddsbreak -pipe -changeOf=Key \
                        | sddsprocess -pipe=in $tmpFile -clip=1,0,invert \
                        \"-print=column,Comment,%20s %10s %s,TimeStamp,User,Comments\"} result]} {
            puts $result
            return -code error "ShowHistoryBPMs(1): $result"
        }
        set tmpFileP /tmp/[APSTmpString]
        APSAddToTempFileList $tmpFileP
        
        if $commentsOnly {
            if [catch {exec sddscombine $tmpFile -pipe=out -merge \
			   | sddsprocess -pipe -reedit=col,Comments,%/\n// \
                           | tee $deviceName/comments.sdds \
                           | sddsprintout -pipe=in $tmpFileP -column=Comment -nolabel \
                           "-title=Comment history for $deviceName as of [clock format [clock seconds]]\n" } result] {
                return -code error "ShowHistoryBPMs(2): $result"
            }
        } else {
            if [catch {exec sddsexpand $tmpFile -pipe=out -nowarning \
                           | sddsprintout -pipe=in $tmpFileP -format=double=%5.0f,float=%5.0f -postPageLines=1 \
                           "-title=Status history for $deviceName as of [clock format [clock seconds]]\n" \
                           -parameter=TimeStamp,format=%20s -parameter=User,format=%10s \
                           -parameter=DeviceType,format=%10s,endsline \
                           -parameter=NonexistentH \
                           -parameter=NonexistentV,endsline \
                           -parameter=ResponseEquationH \
                           -parameter=ResponseEquationV,endsline \
                           -parameter=OkFor*H,edit=%/OkFor//ei/?/,endsline \
                           -parameter=OkFor*V,edit=%/OkFor//ei/?/,endsline \
                           -parameter=ButtonNameH -parameter=ButtonNameV,endsline \
                           -parameter=Comments,format=%30s} result] {
                return -code error "ShowHistoryBPMs(3): $result"
            }
        }
        if $display {
            APSFileDisplayWindow [APSUniqueName .] -comment "History for $deviceName" \
                -fileName $tmpFileP -deleteOnClose 1 -width 130 -height 40 -defaultButtons 1
        }
    }
}

proc ShowHistoryCorrectors {args} {
    set commentsOnly 0
    APSStrictParseArguments {commentsOnly}

    global sectorSelection deviceTypeSelection system
    if [regexp {booster} $system] {
        set deviceName $sectorSelection$deviceTypeSelection 
    } else {
        set deviceName S$sectorSelection$deviceTypeSelection
    }
    set fileList [lsort [glob -nocomplain config-????-???-????-??????]] 

    setStatus "Processing [llength $fileList] files..."

    # for some reason sdsdquery always return a "child killed: write on
    # pipe with no readers" error so I add readAll option.
    catch {eval exec sddscombine $fileList -pipe=out \
               | sddsquery -col -pipe=in -readAll} existingColumnList
    set existingColumnList [split $existingColumnList]

    # The file fields may have new names not included in existingColumnList 
    if [catch {sdds load fields.sdds fd} result] {
        return -code error "$result"
    }
    set option1 -print=column,Key,
    set option2 ""
    set names [lindex $fd(Column.Name) 0]
    set types [lindex $fd(Column.Type) 0]
    set excludedList [list timestamp time user host numbercombined filename devicename pagenumber]
    foreach name $names type $types {
        if {-1==[lsearch -exact $existingColumnList $name]} {
            continue
        }
        set nm [string tolower $name]
        if {[lsearch -exact $excludedList $nm]==-1} {
            if {[string compare $type string]==0} {
                set option1 ${option1}-%s
            } elseif {[string compare $type character]==0} {
                set option1 ${option1}-%c
            } elseif {[string compare $type long]==0} {
                set option1 ${option1}-%ld
            } elseif {[string compare $type short]==0} {
                set option1 ${option1}-%hd
            } elseif {[string compare $type double]==0 || [string compare $type float]==0} {
                set option1 ${option1}-%.6g
            } else {
                continue
            }
            set option2 ${option2},$name
        }
    }
    set option ${option1}${option2}

    set tmpFile /tmp/[APSTmpString]
    APSAddToTempFileList $tmpFile
    if [catch {eval exec sddscombine $fileList -pipe=out \
                 | sddsprocess -pipe -match=column,DeviceName=$deviceName -nowarning \
                 $option \
                 | sddsexpand -pipe -nowarning \
                 | sddscollapse -pipe \
                 | sddscombine -merge -pipe \
                 | sddsbreak -pipe -changeOf=Key \
                 | sddsprocess -pipe=in $tmpFile -clip=1,0,invert -nowarning} result] {
        return -code error "ShowHistoryCorrectors: $result"
    }

    set tmpFileP /tmp/[APSTmpString]
    APSAddToTempFileList $tmpFileP
    if $commentsOnly {
        if [catch {exec sddscombine $tmpFile -pipe=out -merge \
                       | sddsprocess -pipe \
                       "-print=column,Comment,%20s %10s %s,TimeStamp,User,Comments" \
                       | sddsprintout -pipe=in \
                       $tmpFileP -column=Comment -nolabel \
                       "-title=Comment history for $deviceName as of [clock format [clock seconds]]\n" } result] {
            return -code error "ShowHistoryCorrectors: $result"
        }
    } else {
        if [catch {exec sddsexpand $tmpFile -pipe=out -nowarning \
                       | sddsprintout -pipe=in \
                       $tmpFileP -format=double=%5.0f,float=%5.0f \
                       -postpagelines=2 \
                       "-title=Status history for $deviceName as of [clock format [clock seconds]]\n" \
                       -parameter=TimeStamp,format=%20s -parameter=User,format=%10s,endsline \
                       -parameter=Nonexistent,endsline \
                       -parameter=OkFor*,edit=%/OkFor//ei/?/,endsline \
                       -parameter=Comments,format=%30s} result] {
            return -code error "ShowHistoryCorrectors: $result"
        }
    }

    APSFileDisplayWindow [APSUniqueName .] -comment "History for $deviceName" \
      -fileName $tmpFileP -deleteOnClose 1 -width 130 -height 40 -defaultButtons 1
}

proc ShowOverview {} {
    global requiredFieldList deviceList orderedFullDeviceList

    set w [APSUniqueName .]
    set field [APSChooseItemFromList -name "Choose item to overview" \
      -itemList [concat None $requiredFieldList] -returnIndices 0]
    if [string compare $field None]==0 return
    global $field
    lappend dataList "$field values for all devices"
    foreach device $orderedFullDeviceList {
        if [lsearch -exact $deviceList $device]!=-1 {
            lappend dataList "[format %10s $device:] [set $field\($device\)]"
        }
    }
    APSScrolledListWindow [APSUniqueName .] \
      -name "$field values for all devices" \
      -itemList $dataList -printButton 1 -acceptButton 0 -emailButton 1
}

proc MakeSelectionWidgets {widget args} {
    set parent ""
    set system ""
    APSStrictParseArguments {parent system}

    APSFrame $widget -parent $parent -label ""
    set w $parent$widget.frame
    global SelectionWidget 
    set SelectionWidget $w

    global sectorList labelList commandList
    
    global sectorSelection sectorSelectionSaved
    set sectorSelection [lindex $sectorList 0]
    set sectorSelectionSaved $sectorSelection
    APSRadioButtonFrame .sector -parent $w -label "Sector: " \
      -variable sectorSelection -buttonList $labelList \
      -valueList $sectorList -orientation horizontal -limitPerRow 10 \
      -commandList $commandList

    global deviceTypeList deviceTypeSelection deviceTypeSelectionSaved
    set deviceTypeSelection [lindex $deviceTypeList 0] 
    set deviceTypeSelectionSaved $deviceTypeSelection
    APSRadioButtonFrame .type -parent $w -label "Type:   " \
      -variable deviceTypeSelection -buttonList $deviceTypeList \
      -valueList $deviceTypeList -orientation horizontal -limitPerRow 11 \
      -commandList [APSReplicateItem -number [llength $deviceTypeList] -item ProcessSelectionChange]
}

set disableCommentsTrace 0
proc UpdateComments {widget name1 name2 op} {
    if {$op == "write"} {
        global $name1 disableCommentsTrace
        if {$disableCommentsTrace == 0} {
            $widget delete 1.0 end
            $widget insert end [set $name1\($name2\)]
        }
    }
}

proc TypeComments {widget} {
    global Comments disableCommentsTrace
    set disableCommentsTrace 1
    set Comments(0) [string trimright [$widget get 1.0 end]]
    set disableCommentsTrace 0
}

proc UpdateXrayBPMPreamp {deviceName} {
    if [regexp {ID} $deviceName] {
        set type ID
    } elseif [regexp {BM} $deviceName] {
        set type BM
    } else {
        return
    }
    global PreampSerialNumber
    regexp {:(.*)} $deviceName a b c
    if {$b=="P1"} {
        set other [regsub {P1} $deviceName P2]
    } else {
        set other [regsub {P2} $deviceName P1]
    }
    set PreampSerialNumber($other) PreampSerialNumbe($deviceName)
}
proc MakeFieldWidgets {widget args} {
    set parent ""
    set system ""
    APSStrictParseArguments {parent system}

    APSFrame $widget -parent $parent -label ""
    set w $parent$widget.frame
    pack [frame $w.top] -fill x
    pack [frame $w.bottom] -fill x
    pack [frame $w.bottom.planeH] -fill x -side left
    pack [frame $w.bottom.planeV] -fill x -side left -padx 50

    global requiredFieldList requiredFieldTypeList requiredFieldDescripList requiredFieldPlaneList
    global requiredFieldIsEnumList requiredFieldEnumsList
    set maxLength 0
    foreach field $requiredFieldList {
        if [string length $field]>$maxLength {
            set maxLength [string length $field]
        }
    }
    incr maxLength
    set count 0
    global inputFieldWidgetList deviceTypeList inputFieldPlaneList
    if [regexp {booster} $system] {
        set name B1C0[lindex $deviceTypeList 0]
    } else {
        set name S01[lindex $deviceTypeList 0]
    }
    set deviceName ""
    foreach field $requiredFieldList type $requiredFieldTypeList descrip $requiredFieldDescripList plane \
      $requiredFieldPlaneList isEnum $requiredFieldIsEnumList enumValues $requiredFieldEnumsList {
          global $field
          set $field\(0\) [set $field\($name\)]
          incr count
          switch $type {
              SDDS_STRING {
                  if $isEnum {
                      APSRadioButtonFrame .w$count -parent $w.top \
                        -label "[APSMenuFramePadLength $field: $maxLength] " \
                        -variable $field\(0\) -contextHelp $descrip \
                        -valueList [split $enumValues ,] \
                        -buttonList [split $enumValues ,] \
                        -orientation horizontal 
                  } else {
                      if [lsearch -exact [list ResponseEquationH ResponseEquationV] $field]!=-1 {
                          APSLabeledEntry .w$count -parent $w.top -label "[APSMenuFramePadLength $field: $maxLength] " \
                            -textVariable $field\(0\) -width 80 -contextHelp $descrip
                          lappend inputFieldWidgetList $w.top.w$count.entry
                          lappend inputFieldPlaneList $plane
                      } elseif [lsearch -exact [list Comments] $field]!=-1 {
                          pack [frame $w.top.w$count] -fill x
                          pack [label $w.top.w$count.label -text "[APSMenuFramePadLength $field: $maxLength] "] -side left
                          APSScrolledText .st -parent $w.top.w$count -height 3
                          $w.top.w$count.st.text insert end [set $field\(0\)]
                          
                          trace add variable $field\(0\) write "UpdateComments $w.top.w$count.st.text"
                          bindtags $w.top.w$count.st.text "Text . all $w.top.w$count.st.text"
                          bind $w.top.w$count.st.text <KeyPress> "TypeComments $w.top.w$count.st.text"
                          bind $w.top.w$count.st.text <ButtonRelease-2> "TypeComments $w.top.w$count.st.text"
                      } elseif [string compare $field DeviceName]==0 {
                          APSLabeledOutput .w$count -parent $w.top -label "[APSMenuFramePadLength $field: $maxLength] " \
                            -textVariable $field\(0\) -width 18 -contextHelp $descrip
                          set deviceName [set $field\(0\)]
                          pack configure $w.top.w$count.entry -side left
                      } else {
                          if {($plane == "H") || ($plane == "V")} {
                              APSLabeledEntry .w$count -parent $w.bottom.plane$plane -label "[APSMenuFramePadLength $field: $maxLength] " \
                                -textVariable $field\(0\) -width 12 -contextHelp $descrip
                              
                              pack configure $w.bottom.plane$plane.w$count.entry -side left
                              lappend inputFieldWidgetList $w.bottom.plane$plane.w$count.entry
                          } else {
                              APSLabeledEntry .w$count -parent $w.top -label "[APSMenuFramePadLength $field: $maxLength] " \
                                -textVariable $field\(0\) -width 10 -contextHelp $descrip
                              pack configure $w.top.w$count.entry -side left
                              lappend inputFieldWidgetList $w.top.w$count.entry
                          }
                          lappend inputFieldPlaneList $plane
                      }
                  }
              }
              SDDS_SHORT {
                  if [string match $field Nonexistent] {
                      APSRadioButtonFrame .w$count -parent $w.top -label "[APSMenuFramePadLength $field: $maxLength] " \
                        -variable $field\(0\) -buttonList {Yes No} -valueList {1 0} -orientation horizontal \
                        -contextHelp $descrip -commandList \
                        {"ChangeInputWidgetStates -state disabled" \
                           "ChangeInputWidgetStates -state normal"}
                  } elseif [string compare $field NonexistentH]==0 {
                      APSRadioButtonFrame .w$count -parent $w.bottom.planeH -label "[APSMenuFramePadLength $field: $maxLength] " \
                        -variable $field\(0\) -buttonList {Yes No} -valueList {1 0} -orientation horizontal \
                        -contextHelp $descrip -commandList \
                        {"ChangeInputWidgetStates -state disabled -plane H" \
                           "ChangeInputWidgetStates -state normal -plane H"}
                  } elseif [string compare $field NonexistentV]==0 {
                      APSRadioButtonFrame .w$count -parent $w.bottom.planeV -label "[APSMenuFramePadLength $field: $maxLength] " \
                        -variable $field\(0\) -buttonList {Yes No} -valueList {1 0} -orientation horizontal \
                        -contextHelp $descrip -commandList \
                        {"ChangeInputWidgetStates -state disabled -plane V" \
                           "ChangeInputWidgetStates -state normal -plane V"}
                  } else {
                      if {($plane == "H") || ($plane == "V")} {
                          APSRadioButtonFrame .w$count -parent $w.bottom.plane$plane -label "[APSMenuFramePadLength $field: $maxLength] " \
                            -variable $field\(0\) -buttonList {Yes No} -valueList {1 0} -orientation horizontal \
                            -contextHelp $descrip
                          lappend inputFieldWidgetList $w.bottom.plane$plane.w$count.frame.button1
                          lappend inputFieldWidgetList $w.bottom.plane$plane.w$count.frame.button2
                      } else {
                          APSRadioButtonFrame .w$count -parent $w.top -label "[APSMenuFramePadLength $field: $maxLength] " \
                            -variable $field\(0\) -buttonList {Yes No} -valueList {1 0} -orientation horizontal \
                            -contextHelp $descrip
                          lappend inputFieldWidgetList $w.top.w$count.frame.button1
                          lappend inputFieldWidgetList $w.top$plane.w$count.frame.button2
                      }
                      lappend inputFieldPlaneList $plane
                      lappend inputFieldPlaneList $plane
                  }
              }
              SDDS_DOUBLE {
                  APSLabeledEntry .w$count -parent $w.top -label "[APSMenuFramePadLength $field: $maxLength] " \
                    -textVariable $field\(0\) -contextHelp $descrip
              }
          }
      }
}

proc MakeOperationWidgets {widget args} {
    set parent ""
    APSStrictParseArguments {parent}
    
    APSFrame $widget -parent $parent -label ""
    set w $parent$widget.frame
    
    APSButton .write -parent $w -text "Save/Install" -command "SaveConfiguration" \
      -contextHelp "Saves configuration to disk and installs it as the default."
    APSButton .read -parent $w -text "Read..." -command "ReadConfiguration -interactive 1" \
      -contextHelp "Reads a configuration chosen from a list of available configurations."
    APSButton .overview -parent $w -text "Overview" -command "ShowOverview" \
      -contextHelp "Shows the values of a specific setting for all devices."
    APSButton .history -parent $w -text "History (Current device)" -command "ShowHistory" \
      -contextHelp "Shows the history of all settings for the current device.  The history is drawn from the existing configuration files and doesn't include the present state of values in this application."
    APSButton .chistory -parent $w -text "Comment history (Current device)" -command "ShowCommentHistory" \
      -contextHelp "Shows the history of all settings for the current device.  The history is drawn from the existing configuration files and doesn't include the present state of values in this application."
}

proc PreBuildApplication {args} {
    set system ""
    APSStrictParseArguments {system}
    global CVSRevisionAuthor
    APSApplication . -name ManageSRDeviceStatus:$system -version $CVSRevisionAuthor \
      -overview "Storage ring device status tracking tool"
    setStatus "Working..."
    APSScrolledStatus .status -parent .userFrame -textVariable status -width 88 \
      -height 5 -packOption "-fill both -expand true"
    update
}

proc BuildApplication {args} {
    set system ""
    APSStrictParseArguments {system}

    MakeSelectionWidgets  .sel -parent .userFrame -system $system
    MakeFieldWidgets  .fields -parent .userFrame -system $system
    MakeOperationWidgets .ops -parent .userFrame
    AddSpecialFeatures .sf -parent .userFrame -system $system 
    setStatus "Ready."
    update
}

proc AddSpecialFeatures {widget args} {
    set system ""
    set parent ""
    APSStrictParseArguments {system parent}

    switch $system {
        bpms {
            AddSpecialFeaturesBPMs $widget -parent $parent
        }
        vcorrectors -
        hcorrectors {
        }
    }
}
set statusWin 0
proc ViewStatus {args} {
    global statusWin system
   
    if !$statusWin {
        switch $system {
            hcorrectors {
                exec SRCorrStatusViewer -plane H &
            }
            vcorrectors {
                exec SRCorrStatusViewer -plane V &
            }
            bpms {
                exec SRBPMStatusViewer &
            }
            boosterbpms {
                setStatus "boostser bpm status viewer does not exist yet."
                return
            }
        }
    }
    set statusWin 1
    set tmpFile /tmp/[APSTmpString]
    switch $system {
        bpms {
            if [catch {exec sddsprocess config.sdds -pipe=out -match=col,ResponseEquationH=,! \
                         | sddsprintout -pipe=in $tmpFile.1 -col=DeviceName,format=%20s "-title=config.sdds status printout" \
                         -col=ResponseEquationH,format=%50s -col=ResponseEquationV,format=%50s -width=140 } result] {
                return -code error $result
            }
            if [catch {exec sddsprintout config.sdds $tmpFile.2  -col=DeviceName,format=%11s -width=140 \
                         -col=ElectronicsType,label=Electronics,format=%12s \
                         -col=DeviceType,format=%11s -col=BPLDInterfaceSerialNumber,label=BPLDNumber,format=%11s \
                         -col=BergozCalibrationType,label=BergozCal,format=%10s -col=BergozSerialNumber,label=BergozSerial,format=%12s \
                         -col=FilterSerialNumber,label=FilterNumber,format=%12s -col=PreampSerialNumber,label=PreampNumber,format=%12s \
                          } result] {
                return -code error $result
            }
            exec cat $tmpFile.1 $tmpFile.2 > $tmpFile.printout
        }
        hcorrectors -
        vcorrectors {
            if [catch {exec sddsprintout config.sdds $tmpFile.printout \
                         -col=DeviceName -col=CorrectorLimit,format=%10.1f } result] {
                return -code error $result
            }
        }

    }
   
    APSFileDisplayWindow [APSUniqueName .fileDisp] -fileName $tmpFile.printout -width 140 -height 50\
      -comment "Printout for config.sdds" -deleteOnClose 1 -font "courier 12" \
      -printCommand "enscript -r"
    
}
proc AddSpecialFeaturesBPMs {widget args} {
    set parent ""
    APSStrictParseArguments {parent}
    
    APSFrame $widget -parent $parent -label ""
    set w $parent$widget.frame
    
    APSButton .view -parent $w -text "View Installed Status" -command "ViewStatus" -contextHelp \
      "bring up binary status displayer and print out the text status from installed status configuration."
    APSButton .xBadBO -parent $w -text "Set Bad BO's" \
      -command "SetBPMBadBOValues" \
      -contextHelp "Sets the BadBO process variables from the \"ok for orbit correction\" data.  The data used is that in the currently loaded configuration.  Generally, you should save the configuration first as a matter of good practise."
    APSButton .generateEquations -parent $w -text "Regenerate Equations" \
      -command {
          setStatus "Regenerating equations in new file..."
          exec makeSRXrayBPMequations
          set assumeAllChanged 1
          setStatus "Done. Please read in the new file with the READ button and make new save."
      } \
      -contextHelp "Generates equations for the Xray bpms based on source-distance data found in /home/helios/oagData/sr/IDs and /home/helios/oagData/sr/BMs. A new config-<yyyy>-<jjj>-<mmdd>-<hhmmss> file will be written. Read in the new file and Save/Install the data."
}

proc ReadApplicationConfig {args} {
    set system ""
    APSStrictParseArguments {system}
    
    if ![file exists fields.sdds] {
        APSAlertBox [APSUniqueName .] -errorMessage "Not found: fields.sdds"
        exit 1
    }
    if [catch {sdds load fields.sdds configData} result] {
        APSAlertBox [APSUniqueName .] -errorMessage "$result"
        exit 1
    }
    global requiredFieldList requiredFieldTypeList requiredFieldDescripList \
        requiredFieldWidthList requiredFieldPlaneList requiredFieldEnumsList requiredFieldIsEnumList \
        defaultValueList sectorList
    set requiredFieldList [lindex $configData(Column.Name) 0]
    set requiredFieldDescripList [lindex $configData(Column.Description) 0]
    set requiredFieldWidthList [lindex $configData(Column.PrintoutWidth) 0]
    set defaultValueList [lindex $configData(Column.DefaultValue) 0]
    set requiredFieldTypeList ""
    foreach type [lindex $configData(Column.Type) 0] {
        lappend requiredFieldTypeList SDDS_[string toupper $type]
    }
    
    if [lsearch -exact $configData(ColumnNames) IsEnumeratedField]!=-1 {
        set requiredFieldIsEnumList [lindex $configData(Column.IsEnumeratedField) 0]
        set requiredFieldEnumsList  [lindex $configData(Column.EnumeratedValueList) 0]
    } else {
        set requiredFieldIsEnumList [APSReplicateItem -item 0 -number [llength $requiredFieldList]]
        set requiredFieldEnumsList  [APSReplicateItem -item "" -number [llength $requiredFieldList]]
    }
    if [lsearch -exact $configData(ColumnNames) Plane]!=-1 {
        set requiredFieldPlaneList [lindex $configData(Column.Plane) 0]
        foreach item $requiredFieldPlaneList {
            if ![string length $item] continue
            if [lsearch -exact [list H V] $item]==-1 {
                APSAlertBox [APSUniqueName .] -errorMessage "Bad plane data in fields.sdds"
                exit 1
            }
        }
    } else {
        set requiredFieldPlaneList [APSReplicateItem -item "" -number [llength $requiredFieldList]]
    }

    if ![file exists suffixes.sdds] {
        APSAlertBox [APSUniqueName .] -errorMessage "Not found: suffixes.sdds"
        exit 1
    }
    if [catch {sdds load suffixes.sdds configData} result] {
        APSAlertBox [APSUniqueName .] -errorMessage "$result"
        exit 1
    }
    global deviceTypeList 
    set deviceTypeList [lindex $configData(Column.Suffix) 0]
    # set defaults, mostly 
    global orderedFullDeviceList OriginalComment
    foreach field $requiredFieldList {
        global $field
    }
    set index 0
    foreach sector $sectorList {
        if [regexp {booster} $system] {
            set name $sector
        } else {
            set name S$sector
        }
        foreach device $deviceTypeList {
            set deviceName [GetDeviceName -system $system -sector $sector -device $device]
            lappend orderedFullDeviceList $deviceName
            
            incr index
            foreach field $requiredFieldList defaultValue $defaultValueList {
                set $field\($deviceName\) $defaultValue
            }
            set DeviceName($deviceName) $deviceName
            set Comments($deviceName) ""
            set OriginalComment($deviceName) ""
            if [regexp {bpm} $system] {
                foreach plane {H V} {
                    set ButtonName$plane\($deviceName\) $deviceName
                }
            }
        }
    }
    if [regexp {booster} $system] {
        set name B1C0[lindex $deviceTypeList 0]
    } else {
        set name S01[lindex $deviceTypeList 0]
    }
    foreach field $requiredFieldList {
        set $field\(0\) [set $field\($name\)]
    }
    if {[regexp {bpm} $system] && ![regexp {booster} $system]} {
        global OkForRMSCalculationH OkForRMSCalculationV
        foreach pl {H V} {
            if [lsearch $requiredFieldList "OkForRMSCalculation$pl"]<0 {
                lappend requiredFieldList OkForRMSCalculation$pl
                lappend requiredFieldTypeList SDDS_SHORT
                lappend requiredFieldDescripList "button for rms calculation"
                lappend requiredFieldPlaneList $pl
                lappend requiredFieldIsEnumList 0
                lappend requiredFieldEnumsList ""
                lappend defaultValueList 0
            }
        }
        set OkForRMSCalculationH(0) 0
        set OkForRMSCalculationV(0) 0
        foreach device $orderedFullDeviceList {
            if {[regexp {A:P0} $device] || [regexp {A:P1} $device] || \
                    [regexp {B:P0} $device] || [regexp {B:P1} $device]} {
                set OkForRMSCalculationH($device) 1
                set OkForRMSCalculationV($device) 1
            } else {
                set OkForRMSCalculationH($device) 0
                set OkForRMSCalculationV($device) 0
            }
        }
    }
}

proc SetBPMBadBOValues {} {
    global requiredFieldList deviceList newDeviceName system
    
    if {[lsearch -exact $requiredFieldList OkForDCOrbitCorrectionH]==-1 || \
          [lsearch -exact $requiredFieldList OkForDCOrbitCorrectionV]==-1 } {
        return -code error "SetBPMBadBOValues: Internal error: field list is invalid!"
    }
    global OkForDCOrbitCorrectionH OkForDCOrbitCorrectionV
    global NonexistentH NonexistentV
    global sectorSelection deviceTypeSelection sectorSelectionSaved deviceTypeSelectionSaved
    if [regexp {booster} $system] {
        set newDeviceName $sectorSelection$deviceTypeSelection 
    } else {
        set newDeviceName S$sectorSelection$deviceTypeSelection 
    }

    set FieldsHaveChanged 0
    foreach field $requiredFieldList {
        global $field
        if [string compare $field Comments]!=0 {
            if [string compare [set $field\(0\)] [set $field\($newDeviceName\)]]!=0 {
                incr FieldsHaveChanged
            }
        }
    }

    set nDevices [llength $deviceList]
    foreach deviceName $deviceList {
        foreach HV {H V} xy {x y} {
            if [set Nonexistent${HV}\($deviceName\)] continue
            set state [set OkForDCOrbitCorrection${HV}\($deviceName\)]
            if $state {
                lappend cavputOptList $deviceName:ms:$xy=0
            } else {
                lappend cavputOptList $deviceName:ms:$xy=1
                setStatus "$deviceName $xy is bad"
            }
        }
    }
    setStatus "Setting BadBO values..."
    exec cavput -list=[join $cavputOptList ,] -list=:BadBO -pendIO=30
    setStatus "Done."
}

proc GetConfigMTime {args} {
    set filename [file tail [file readlink config.sdds]]
    regsub {config-} $filename "" dateString
    set year [string range $dateString 0 3]
    set month [string range $dateString 9 10]
    set day [string range $dateString 11 12]
    set hour [string range $dateString 14 15]
    set min [string range $dateString 16 17]
    set sec [string range $dateString 18 19]
    return [clock scan ${hour}:${min}:${sec} -base [clock scan ${year}-${month}-${day}]]
}

set apsScriptCommand $argv0
set args $argv
set system bpms
if {[APSStrictParseArguments {system}] || ![string length $system]} {
    APSAlertBox [APSUniqueName .] -errorMessage "Usage: ManageSRDeviceStatus -system {bpms|hcorrectors|vcorrectors}"
}

set combinedFilesOnly 1
switch $system {
    bpms {
        set dataDir /home/helios/oagData/sr/BPMStatus
        set combinedFilesOnly 0
    }
    bpmsTest {
        set dataDir /home/oxygen/[string toupper $env(USER)]/BPMStatus
        set combinedFilesOnly 0
    }
    hcorrectors {
        set dataDir /home/helios/oagData/sr/HCorrectorStatus
    }
    vcorrectors {
        set dataDir /home/helios/oagData/sr/VCorrectorStatus
    }
    test {
        set dataDir /home/helios/oagData/sr/HCorrectorStatusTest/
    }
    boosterbpms {
        set dataDir /home/helios/oagData/booster/BPMStatus
    }
    default {
        APSAlertBox [APSUniqueName .] -errorMessage "Usage: ManageSRDeviceStatus -system {bpms|hcorrectors|vcorrectors}"
    }
}

if {![file exists $dataDir] || ![file isdirectory $dataDir]} {
    APSAlertBox [APSUniqueName .] -errorMessage "$dataDir is nonexistent or not a directory"
}
cd $dataDir

set changedDeviceList ""
set assumeAllChanged 0
set ConfigMTime0 [GetConfigMTime]
set theTime [clock seconds]
set loadingDate [clock format $theTime -format "%Y %j"]


#following will generate comments.sdds if it does not exist for that device
if [string match "bpm*" $system] {
    set deviceList1 [exec sdds2stream config.sdds -col=DeviceName]
    foreach device $deviceList1 {
        if ![file exists $device/comments.sdds] {
           # puts "Making comment file for $device"
            setStatus "Making comment file for $device"
            ShowHistoryBPMs -commentsOnly 1 -deviceName $device -display 0
        }
    }
}
for {set sector 1} {$sector<=40} {incr sector} {
    lappend srSectorList [format %02d $sector]
    lappend srLabelList [format "%02d" $sector]
    lappend srCommandList "ProcessSelectionChange"
}

if {$system=="bpms"} {
    foreach sector {40 1 2} {
        set sectorf [format %02d $sector]
        lappend srSectorList ${sectorf}SB
        lappend srLabelList  ${sectorf}SB
        lappend srCommandList  "ProcessSelectionChange"
    }
}


for {set b 1} {$b<=4} {incr b} {
    for {set c 0} {$c<10} {incr c} {
        lappend boosterSectorList B${b}C${c}
        lappend boosterLabelList B${b}C${c}
        lappend boosterCommandList "ProcessSelectionChange"
    }
}



if [regexp {booster} $system] {
    set sectorList $boosterSectorList
    set labelList $boosterLabelList
    set commandList $boosterCommandList
} else {
    set sectorList $srSectorList
    set labelList $srLabelList
    set commandList $srCommandList
}

PreBuildApplication -system $system
ReadApplicationConfig -system $system
BuildApplication -system $system
ReadConfiguration
setStatus "Done."


# Local Variables:
# mode: tcl
# indent-tabs-mode: nil
# End:
