#
# $Log: not supported by cvs2svn $
# Revision 1.22  2004/07/30 17:54:39  borland
# Added APSGetSDDSUnits.
#
# Revision 1.21  2000/04/03 20:14:39  emery
# Indented lines.
#
# Revision 1.20  1999/11/02 15:55:58  borland
# Added some sdds close statements that are needed.
#
# Revision 1.19  1999/03/12 15:58:26  soliday
# Added APSGetSDDSRowCount, APSSDDSCheck, APSSDDSCollapse, APSSDDSExpand, and
# APSSDDSConvert.
#
# Revision 1.18  1998/10/13 19:23:17  borland
# Removed debugging statements.
#
# Revision 1.17  1998/10/13 19:07:49  borland
# Added APSGetSDDSColumns, which reads several columns from an SDDS file and
# puts the results into named variables.
#
# Revision 1.16  1996/09/23 19:36:06  saunders
# If sdds extension doesn't exist, use old routines (6 ms penalty per call).
#
# Revision 1.15  1996/09/19 23:46:44  borland
# Got rid of puts in APSCheckSDDSFile and use catchable error returns.
#
# Revision 1.14  1996/09/19 19:41:11  saunders
# Changed so that "" is returned when non-existant page selected. This
# to retain compatibility.
#
# Revision 1.13  1996/09/19 13:38:56  borland
# Added the word "exec" in front of use of sddscheck .
#
# Revision 1.12  1996/09/16 20:16:29  saunders
# Changed "puts stderr" error checking to catchable returns.
#
# Revision 1.11  1996/09/04 19:38:55  saunders
# Added use of tclSDDS library.
#
# Revision 1.10  1996/07/17 19:50:10  borland
# Added -ignoreFormats option to sdds2stream invocations to avoid problems
# with fields getting padded with spaces.
#
# Revision 1.9  1996/07/01  16:20:42  borland
# Fixed bug in APSCheckSDDSFile for compressed files.
#
# Revision 1.8  1996/03/29  20:29:13  saunders
# Trim extra spaces from first line when doing APSCheckSDDSFile, so it
# is less sensitive to extra spaces in hand created SDDS files.
#
# Revision 1.7  1996/02/22  19:59:35  borland
# Removed debugging statement from APSCheckSDDSFile.
#
# Revision 1.6  1996/02/12  21:43:23  borland
# Change APSCheckSDDSFile to use sddscheck when file extension is gz, F, or
# Z.
#
# Revision 1.5  1996/02/12  21:36:47  borland
# Added close statement to APSCheckSDDSFile.
#
# Revision 1.4  1996/01/16  21:20:12  saunders
# Added Log for CVS. Should have done this a long time ago.
#
#

proc APSGetSDDSColumnHelp {} {
    return "Usage: APSGetSDDSColumn\n\[-fileName <string>\]\t\n\[-sddsFD <fd>\]\t\n\[-column <string>\]\t\n\[-page <string>\]\t\n\nExtracts column from sdds file and returns it as a tcl list. File may be specified with explicit path using -fileName, or given as an <fd> from a \"sdds open\" call. The latter being much more efficient for repeated accesses."
}
proc APSGetSDDSColumn {args} {
    set fileName "data.sdds"
    set column "data"
    set page -1
    set sddsFD -1

    set cmdList [info commands]
    if {[lsearch -exact $cmdList "sdds"] == -1} {
        return [eval APSGetSDDSColumnOld $args]
    }

    APSStrictParseArguments {fileName column page sddsFD}
    
    if {$page <= 0} {
        set pageOption ""
    } else {
        set pageOption "-page $page"
    }
    if {$sddsFD == -1} {
        if [catch {sdds open $fileName} fd] {
            return -code error "APSGetSDDSColumn:$fd"
        } 
        if [catch {eval sdds getColumn $fd $column $pageOption} dataList] {
            catch {sdds close $fd}
            if {![string compare $dataList "EOF"]} {
                return ""
            } else {
                return -code error "APSGetSDDSColumn:$dataList"
            }
        } else {
            catch {sdds close $fd}
            return -code ok $dataList
        }
    } else {
        set fd $sddsFD
        if [catch {eval sdds getColumn $fd $column $pageOption} dataList] {
            if {![string compare $dataList "EOF"]} {
                return ""
            } else {
                return -code error "APSGetSDDSColumn:$dataList"
            }
        } else {
            return -code ok $dataList
        }
    }
}

proc APSGetSDDSParameterHelp {} {
    return "Usage: APSGetSDDSParameter\n\[-fileName <string>\]\t\n\[-sddsFD <fd>\]\t\n\[-parameter <string>\]\t\n\[-page <string>\]\t\n\nExtracts parameter from sdds file and returns it. File may be specified with explicit path using -fileName, or given as an <fd> from a \"sdds open\" call. The latter being much more efficient for repeated accesses."
}
proc APSGetSDDSParameter {args} {
    set fileName "data.sdds"
    set parameter "data"
    set page -1
    set sddsFD -1

    set cmdList [info commands]
    if {[lsearch -exact $cmdList "sdds"] == -1} {
        return [eval APSGetSDDSParameterOld $args]
    }

    APSStrictParseArguments {fileName parameter page sddsFD} 

    if {$page <= 0} {
        set pageOption ""
    } else {
        set pageOption "-page $page"
    }
    if {$sddsFD == -1} {
        if [catch {sdds open $fileName} fd] {
            return -code error "APSGetSDDSParameter:$fd"
        } 
        if [catch {eval sdds getParameter $fd $parameter $pageOption} dataList] {
            catch {sdds close $fd}
            if {![string compare $dataList "EOF"]} {
                return ""
            } else {
                return -code error "APSGetSDDSParameter:$dataList"
            }
        } else {
            catch {sdds close $fd}
            return -code ok $dataList
        }
    } else {
        set fd $sddsFD
        if [catch {eval sdds getParameter $fd $parameter $pageOption} dataList] {
            if {![string compare $dataList "EOF"]} {
                return ""
            } else {
                return -code error "APSGetSDDSParameter:$dataList"
            }
        } else {
            return -code ok $dataList
        }
    }
}

proc APSGetSDDSNamesHelp {} {
    return "Usage: APSGetSDDSNames\n\[-fileName <string>\]\t\n\[-sddsFD <fd>\]\t\n\[-class <string>\] where <string> is column (default), parameter, or array.\t\n\nExtracts data names by class from an sdds file\nand returns it as a tcl list. File may be specified with explicit path using -fileName, or given as an <fd> from a \"sdds open\" call. The latter being much more efficient for repeated accesses. "
}
proc APSGetSDDSNames {args} {
    set fileName "data.sdds"
    set class column
    set sddsFD -1

    set cmdList [info commands]
    if {[lsearch -exact $cmdList "sdds"] == -1} {
        return [eval APSGetSDDSNamesOld $args]
    }

    APSStrictParseArguments {fileName class sddsFD}

    if {$class!="column" && $class!="parameter" &&
        $class!="array"} {
        return {}
    }
    if {$sddsFD == -1} {
        if [catch {sdds open $fileName} fd] {
            return -code error "APSGetSDDSNames:$fd"
        } 
        if [catch {sdds getNames $fd $class} dataList] {
            catch {sdds close $fd}
            return -code error "APSGetSDDSNames:$dataList"
        } else {
            catch {sdds close $fd}
            return -code ok $dataList
        }
    } else {
        set fd $sddsFD
        if [catch {sdds getNames $fd $class} dataList] {
            return -code error "APSGetSDDSNames:$dataList"
        } else {
            return -code ok $dataList
        }
    }
}

proc APSCheckSDDSFileHelp {} {
    return "Usage: APSCheckSDDSFile\n\[-fileName <string>\]\t\n\nVerify that given fileName is an SDDS1 file. \nReturns 1 if true, 0 otherwise."
}
proc APSCheckSDDSFile {args} {
    set fileName "data.sdds"
    APSStrictParseArguments {fileName}

    if {![file exists $fileName] || [file isdirectory $fileName]} {
        return -code ok 0
    }

    if {[string compare [file extension $fileName] ".gz"]==0 || \
          [string compare [file extension $fileName] ".F"]==0 || \
          [string compare [file extension $fileName] ".Z"]==0} {
        catch {exec sddscheck $fileName} result
        return -code ok [expr [string compare $result ok]==0]
    }

    if {[catch {open $fileName r} fd1]} {
        return -code error -errorinfo "APSCheckSDDSFile:$fd1" 0
    } else {
        gets $fd1 line
        close $fd1
        set line [string trim $line]
        if {[string compare $line "SDDS1"]==0} { return 1 }
        if {[string compare $line "SDDS2"]==0} { return 1 }
        if {[string compare $line "SDDS3"]==0} { return 1 }
        if {[string compare $line "SDDS4"]==0} { return 1 }
        if {[string compare $line "SDDS5"]==0} { return 1 }
        return -code ok 0
    }
}

proc APSGetSDDSColumnsHelp {args} {
    return "Usage: APSGetSDDSColumns -fileName <string> -columnList <list> -variableList <list>\n\
Puts column data from an SDDS file for a series of columns into a series of variables."
}

proc APSGetSDDSColumns {args} {
    set fileName ""
    set columnList ""
    set variableList ""
    APSStrictParseArguments {fileName columnList variableList} 
    if {![string length $fileName] || ![llength $columnList] || 
        ![llength $variableList] || [llength $columnList]!=[llength $variableList]} {
        return -code error "APSGetSDDSColumns: bad arguments" }
    if ![file exists $fileName] {
        return -code error "APSGetSDDSColumns: $fileName not found" }
    
    if [catch {sdds open $fileName r} fid] {
        catch {sdds close $fid}
        return -code error "APSGetSDDSColumns: $fid" 
    }
    set index 0
    foreach column $columnList {
        if [catch {sdds getColumn $fid $column} newData] {
            return -code error "APSGetSDDSColumns: $newData" }
        uplevel set [lindex $variableList $index] {""}
        eval uplevel lappend [lindex $variableList $index] $newData
        incr index
    }
    catch {sdds close $fid}
}

#
# Old versions retained so that scripts based on tclsh won't break
# These are invoked if no sdds extension is detected.
#

proc APSGetSDDSColumnOld {args} {
    set fileName "data.sdds"
    set column ""
    set page 0
    APSStrictParseArguments {fileName column page} 

    set extraArgs -noquotes
    if {$page > 0} {
        set extraArgs "$extraArgs -page=$page"
    }
    set cmd "exec sdds2stream -column=$column -ignoreFormats $fileName $extraArgs"
    if {[catch $cmd dataString]} {
        return -code error "APSGetSDDSColumn:$dataString"
    } else {
        set dataList [split $dataString "\n"]
        return $dataList
    }
}

proc APSGetSDDSParameterOld {args} {
    set fileName "data.sdds"
    set parameter ""
    set page 0
    APSStrictParseArguments {fileName parameter page} 

    set extraArgs -noquotes
    if {$page > 0} {
        set extraArgs "$extraArgs -page=$page"
    }
    set cmd "exec sdds2stream -parameter=$parameter -ignoreFormats $fileName $extraArgs"
    if {[catch $cmd dataString]} {
        return -code error "APSGetSDDSParameter:$dataString"
    } else {
        set dataList [split $dataString "\n"]
        return $dataList
    }
}

proc APSGetSDDSNamesOld {args} {
    set fileName "data.sdds"
    set class column
    APSStrictParseArguments {fileName class}

    if {$class!="column" && $class!="parameter" &&
        $class!="array"} {
        return {}
    }
    set cmd "exec sddsquery -$class $fileName"
    if {[catch $cmd dataString]} {
        return -code error "APSGetSDDSNames:$dataString"
    } else {
        set dataList [split $dataString "\n"]
        return $dataList
    }
}

proc APSGetSDDSRowCountHelp {} {
    return "Usage: APSGetSDDSRowCount\n\[-fileName <string>\]\t\n\[-sddsFD <fd>\]\t\n\[-page <integer>\]\t\n\nCounts the rows in each page of the sdds file and returns it as a tcl list. If the page option is given, it returns the number of rows in that page only."
}

proc APSGetSDDSRowCount { args } {
    set fileName ""
    set sddsFD -1
    set page -1
    APSStrictParseArguments {fileName page sddsFD}
    
    if {(![llength $fileName]) && ($sddsFD == -1)} {
        return -code error "APSGetSDDSRowCount:missing -fileName option"
    }
    if {$page <= 0} {
        set page ""
    } else {
        set page "-page $page"
    }

    set cmdList [info commands sdds]
    if {![llength $cmdList]} {
        if [catch {eval exec sdds2stream $fileName -rows $page} dataList] {
            return -code error "APSGetSDDSRowCount:$dataList"
        }
        foreach "count text" $dataList {
            lappend rowList $count
        }
        return -code ok $rowList
    }

    if {$sddsFD == -1} {
        if [catch {sdds open $fileName} fd] {
            return -code error "APSGetSDDSRowCount:$fd"
        } 
        if [catch {eval sdds getRowCount $fd $page} dataList] {
            catch {sdds close $fd}
            return -code error "APSGetSDDSRowCount:$dataList"
        } else {
            catch {sdds close $fd}
            return -code ok $dataList
        }
    } else {
        set fd $sddsFD
        if [catch {eval sdds getRowCount $fd $page} dataList] {
            return -code error "APSGetSDDSRowCount:$dataList"
        } else {
            return -code ok $dataList
        }
    }
}

proc APSSDDSCheckHelp {} {
    return "Usage: APSSDDSCheck\n\[-fileName <string>\]\t\n\[-sddsFD <fd>\]\t\n\[-printErrors 1\]\t\n\nDetermines if the sdds file has been corrupted. It reads the entire file and prints a message. If the file is ok, \"ok\" is printed. If the file has a problem \"corrupted\" is printed. If -printErrors is used, the first error message will be returned."
}

proc APSSDDSCheck { args } {
    set fileName ""
    set sddsFD -1
    set printErrors 0
    APSStrictParseArguments {fileName printErrors sddsFD}

    if {(![llength $fileName]) && ($sddsFD == -1)} {
        return -code error "APSSDDSCheck:missing -fileName option"
    }
    if {$printErrors} {
        set printErrors "-printErrors"
    } else {
        set printErrors ""
    }
    set cmdList [info commands sdds]
    if {![llength $cmdList]} {
        if [catch {eval exec sddscheck $fileName $printErrors} dataList] {
            return -code error "APSSDDSCheck:$dataList"
        }
        return -code ok $dataList
    }

    if {$sddsFD == -1} {
        if [catch {sdds open $fileName} fd] {
            return -code error "APSSDDSCheck:$fd"
        } 
        if [catch {eval sdds check $fd $printErrors} dataList] {
            catch {sdds close $fd}
            return -code error "APSSDDSCheck:$dataList"
        } else {
            catch {sdds close $fd}
            return -code ok $dataList
        }
    } else {
        set fd $sddsFD
        if [catch {eval sdds check $fd $printErrors} dataList] {
            return -code error "APSSDDSCheck:$dataList"
        } else {
            return -code ok $dataList
        }
    }
}

proc APSSDDSCollapseHelp {} {
    return "Usage: APSSDDSCollapse\n\[-inputFileName <string>\]\t\n\[-outputFileName <string>\]\t\n\[-sddsInputFD <fd>\]\t\n\[-sddsOutputFD <fd>\]\t\n\nReads data pages from the input sdds file and writes a new sdds file containing a single data page. This data page contains the parameters, with each parameter forming a column of the tabular data."
}

proc APSSDDSCollapse { args } {
    set inputFileName ""
    set outputFileName ""
    set sddsInputFD -1
    set sddsOutputFD -1
    APSStrictParseArguments {inputFileName outputFileName sddsInputFD sddsOutputFD}

    if {(![llength $inputFileName]) && ($sddsInputFD == -1)} {
        return -code error "APSSDDSCollapse:missing -inputFileName option"
    }
    if {(![llength $outputFileName]) && ($sddsOutputFD == -1)} {
        return -code error "APSSDDSCollapse:missing -outputFileName option"
    }
    set cmdList [info commands sdds]
    if {![llength $cmdList]} {
        if [catch {eval exec sddscollapse $inputFileName $outputFileName} results] {
            return -code error "APSSDDSCollapse:$results"
        }
        return -code ok
    }

    if {$sddsInputFD == -1} {
        if [catch {sdds open $inputFileName} fd] {
            return -code error "APSSDDSCollapse:$fd"
        }
        if {$sddsOutputFD == -1} {
            if [catch {sdds open $outputFileName w} fd2] {
                return -code error "APSSDDSCollapse:$fd2"
            }
        } else {
            set fd2 $sddsOutputFD
        }
        if [catch {eval sdds collapse $fd $fd2} results] {
            catch {sdds close $fd}
            if {$sddsOutputFD == -1} {
                catch {sdds close $fd2}
            }
            return -code error "APSSDDSCollapse:$results"
        } else {
            catch {sdds close $fd}
            if {$sddsOutputFD == -1} {
                catch {sdds close $fd2}
            }
            return -code ok
        }
    } else {
        set fd $sddsFD
        if {$sddsOutputFD == -1} {
            if [catch {sdds open $outputFileName w} fd2] {
                return -code error "APSSDDSCollapse:$fd2"
            }
        } else {
            set fd2 $sddsOutputFD
        }
        if [catch {eval sdds collapse $fd $fd2} results] {
            if {$sddsOutputFD == -1} {
                catch {sdds close $fd2}
            }
            return -code error "APSSDDSCollapse:$results"
        } else {
            if {$sddsOutputFD == -1} {
                catch {sdds close $fd2}
            }
            return -code ok
        }
    }
}

proc APSSDDSExpandHelp {} {
    return "Usage: APSSDDSExpand\n\[-inputFileName <string>\]\t\n\[-outputFileName <string>\]\t\n\[-sddsInputFD <fd>\]\t\n\[-sddsOutputFD <fd>\]\t\n\[-noWarnings 1\]\t\n\nAll columns in the input file are turned into parameters in the output file. For each row of each page in the input file, APSSDDSExpand creates a new page with parameter values equal to the column values for that page and row."
}

proc APSSDDSExpand { args } {
    set inputFileName ""
    set outputFileName ""
    set sddsInputFD -1
    set sddsOutputFD -1
    set noWarnings 0
    APSStrictParseArguments {inputFileName outputFileName sddsInputFD sddsOutputFD noWarnings}

    if {(![llength $inputFileName]) && ($sddsInputFD == -1)} {
        return -code error "APSSDDSExpand:missing -inputFileName option"
    }
    if {(![llength $outputFileName]) && ($sddsOutputFD == -1)} {
        return -code error "APSSDDSExpand:missing -outputFileName option"
    }
    if {$noWarnings} {
        set noWarnings "-noWarnings"
    } else {
        set noWarnings ""
    }

    set cmdList [info commands sdds]
    if {![llength $cmdList]} {
        if [catch {eval exec sddsexpand $inputFileName $outputFileName $noWarnings} results] {
            return -code error "APSSDDSExpand:$results"
        }
        return -code ok
    }

    if {$sddsInputFD == -1} {
        if [catch {sdds open $inputFileName} fd] {
            return -code error "APSSDDSExpand:$fd"
        }
        if {$sddsOutputFD == -1} {
            if [catch {sdds open $outputFileName w} fd2] {
                return -code error "APSSDDSExpand:$fd2"
            }
        } else {
            set fd2 $sddsOutputFD
        }
        if [catch {eval sdds expand $fd $fd2 $noWarnings} results] {
            catch {sdds close $fd}
            if {$sddsOutputFD == -1} {
                catch {sdds close $fd2}
            }
            return -code error "APSSDDSExpand:$results"
        } else {
            catch {sdds close $fd}
            if {$sddsOutputFD == -1} {
                catch {sdds close $fd2}
            }
            return -code ok
        }
    } else {
        set fd $sddsFD
        if {$sddsOutputFD == -1} {
            if [catch {sdds open $outputFileName w} fd2] {
                return -code error "APSSDDSExpand:$fd2"
            }
        } else {
            set fd2 $sddsOutputFD
        }
        if [catch {eval sdds expand $fd $fd2 $noWarnings} results] {
            if {$sddsOutputFD == -1} {
                catch {sdds close $fd2}
            }
            return -code error "APSSDDSExpand:$results"
        } else {
            if {$sddsOutputFD == -1} {
                catch {sdds close $fd2}
            }
            return -code ok
        }
    }
}

proc APSSDDSConvertHelp {} {
    return "Usage: APSSDDSConvert\n\[-inputFileName <string>\]\t\n\[-outputFileName <string>\]\t\n\[-sddsInputFD <fd>\]\t\n\[-sddsOutputFD <fd>\]\t\n\[-dataMode {ascii | binary}\]\t\n\[-fromPage <integer>\]\t\n\[-toPage <integer>\]\t\n\[-recover {1 | clip}\]\t\n\[-delete {column | parameter | array},<matching-string>\[,...\]\]\t\n\[-retain {column | parameter | array},<matching-string>\[,...\]\]\t\n\[-rename {column | parameter | array},<oldname>=<newname>\[,...\]\]\t\n\[-editNames {column | parameter | array},<wildcard-string>,<edit-string>\]\t\n\[-acceptAllNames 1\]\t\n\nConverts sdds files between ascii and binary, and allows wildcard-based filtering-out of unwanted columns and rows. Any column or parameter matched by a deletion string is deleted unless it is matched by a retention string. The acceptAllNames option may be used to force the SDDS library to accept element names that have spaces and other normally unacceptable characters."
}

proc APSSDDSConvert { args } {
    set inputFileName ""
    set outputFileName ""
    set sddsInputFD -1
    set sddsOutputFD -1
    set dataMode ascii
    set fromPage -1
    set toPage -1
    set recover 0
    set delete ""
    set retain ""
    set rename ""
    set editNames ""
    set acceptAllNames 0
    APSStrictParseArguments {inputFileName outputFileName sddsInputFD sddsOutputFD dataMode fromPage toPage recover delete retain rename editNames acceptAllNames}

    if {(![llength $inputFileName]) && ($sddsInputFD == -1)} {
        return -code error "APSSDDSConvert:missing -inputFileName option"
    }
    if {(![llength $outputFileName]) && ($sddsOutputFD == -1)} {
        return -code error "APSSDDSConvert:missing -outputFileName option"
    }
    foreach option "fromPage toPage" {
        if {[set $option] >= 1} {
            set $option "-$option=[set $option]"
        } else {
            set $option ""
        }
    }
    if {$recover == "clip"} {
        set recover "-recover=clip"
    } elseif {$recover} {
        set recover "-recover"
    } else {
        set recover ""
    }
    foreach option "delete retain rename editNames" {
        if {[llength [set $option]]} {
            set $option "-$option=[set $option]"
        }
    }
    if {$acceptAllNames} {
        set acceptAllNames "-acceptAllNames"
    } else {
        set acceptAllNames ""
    }
    set cmdList [info commands sdds]
    if {![llength $cmdList]} {
        if [catch {eval exec sddsconvert $inputFileName $outputFileName -$dataMode $fromPage $toPage $delete $retain $rename $editNames $recover $acceptAllNames} results] {
            return -code error "APSSDDSConvert:$results"
        }
        return -code ok
    }

    if {$dataMode != "binary"} {
        set dataMode SDDS_ASCII
    } else {
        set dataMode SDDS_BINARY
    }
    if {$sddsInputFD == -1} {
        if [catch {sdds open $inputFileName} fd] {
            return -code error "APSSDDSConvert:$fd"
        }
        if {$sddsOutputFD == -1} {
            if [catch {sdds open $outputFileName w $dataMode} fd2] {
                return -code error "APSSDDSConvert:$fd2"
            }
        } else {
            set fd2 $sddsOutputFD
        }
        if [catch {eval sdds convert $fd $fd2 $fromPage $toPage $delete $retain $rename $editNames $recover $acceptAllNames} results] {
            catch {sdds close $fd}
            if {$sddsOutputFD == -1} {
                catch {sdds close $fd2}
            }
            return -code error "APSSDDSConvert:$results"
        } else {
            catch {sdds close $fd}
            if {$sddsOutputFD == -1} {
                catch {sdds close $fd2}
            }
            return -code ok
        }
    } else {
        set fd $sddsFD
        if {$sddsOutputFD == -1} {
            if [catch {sdds open $outputFileName w $dataMode} fd2] {
                return -code error "APSSDDSConvert:$fd2"
            }
        } else {
            set fd2 $sddsOutputFD
        }
        if [catch {eval sdds convert $fd $fd2 $fromPage $toPage $delete $retain $rename $editNames $recover $acceptAllNames} results] {
            if {$sddsOutputFD == -1} {
                catch {sdds close $fd2}
            }
            return -code error "APSSDDSConvert:$results"
        } else {
            if {$sddsOutputFD == -1} {
                catch {sdds close $fd2}
            }
            return -code ok
        }
    }
}

proc APSGetSDDSUnitsHelp {} {
    return "Usage: APSGetSDDSUnits\n\[-fileName <string>\]\t\n\[-sddsFD <fd>\]\t\n\[-class <string>\] where <string> is column (default), parameter, or array.\t\n\nExtracts data units by class from an sdds file\nand returns it as a tcl list. File may be specified with explicit path using -fileName, or given as an <fd> from a \"sdds open\" call. The latter being much more efficient for repeated accesses. "
}
proc APSGetSDDSUnits {args} {
    set fileName "data.sdds"
    set class column
    set sddsFD -1

    APSStrictParseArguments {fileName class sddsFD}

    if {$class!="column" && $class!="parameter" &&
        $class!="array"} {
        return {}
    }
    if {$sddsFD == -1} {
        if [catch {sdds open $fileName} fd] {
            return -code error "APSGetSDDSUnits:$fd"
        } 
        if [catch {sdds getUnits $fd $class} dataList] {
            catch {sdds close $fd}
            return -code error "APSGetSDDSUnits:$dataList"
        } else {
            catch {sdds close $fd}
            return -code ok $dataList
        }
    } else {
        set fd $sddsFD
        if [catch {sdds getUnits $fd $class} dataList] {
            return -code error "APSGetSDDSUnits:$dataList"
        } else {
            return -code ok $dataList
        }
    }
}

proc APSProcessLogOnChangeDataHelp {} {
    return "\
Usage: APSProcessLogOnChangeData
-inputFile <string>\]\t
\[-outputFile <string>\]\t

Inserts extra data points directly 
before changes are logged. This 
makes it easier to plot the data.
"
}

proc APSProcessLogOnChangeData {args} {
    set inputFile ""
    set outputFile ""
    APSStrictParseArguments {inputFile outputFile}

    if {$outputFile == ""} {
        set outputFile $inputFile
    }
    
    if {[catch {sdds load $inputFile data} result]} {
        return -code error "APSProcessLogOnChangeData: $result"
    }
    if {([llength $data(ColumnNames)] != 4) || 
        ([lsearch $data(ColumnNames) "PreviousRow"] == -1) || 
        ([lsearch $data(ColumnNames) "Time"] == -1) || 
        ([lsearch $data(ColumnNames) "Value"] == -1) || 
        ([lsearch $data(ColumnNames) "ControlNameIndex"] == -1)} {
        return -code error "APSProcessLogOnChangeData: Different columns in file than expected"
    }

    set pages [llength $data(Column.PreviousRow)]

    set PreviousRow ""
    set Time ""
    set Value ""
    set ControlNameIndex ""

    for {set page 0} {$page < $pages} {incr page} {
        set previousRowList [lindex $data(Column.PreviousRow) $page]
        set timeList [lindex $data(Column.Time) $page]
        set valueList [lindex $data(Column.Value) $page]
        set indexList [lindex $data(Column.ControlNameIndex) $page]
        set prListNew ""
        set tListNew ""
        set vListNew ""
        set iListNew ""
        set row 0
        catch {unset cnindex}
        foreach prList $previousRowList \
          tList $timeList \
          vList $valueList \
          iList $indexList {
              if {$prList < 0} {
                  append prListNew "$prList "
                  append tListNew "$tList "
                  append vListNew "$vList "
                  append iListNew "$iList "
                  set cnindex($iList) $row
                  incr row
              } else {
                  append prListNew "$cnindex($iList) "
                  append tListNew "[expr {$tList - .001}] "
                  append vListNew "[lindex $valueList $prList] "
                  append iListNew "$iList "
                  set cnindex($iList) $row
                  incr row
                  append prListNew "$cnindex($iList) "
                  append tListNew "$tList "
                  append vListNew "$vList "
                  append iListNew "$iList "                
                  set cnindex($iList) $row
                  incr row
              }
          }
        lappend PreviousRow $prListNew
        lappend Time $tListNew
        lappend Value $vListNew
        lappend ControlNameIndex $iListNew
    }
    set data(Column.PreviousRow) $PreviousRow
    set data(Column.Time) $Time
    set data(Column.Value) $Value
    set data(Column.ControlNameIndex) $ControlNameIndex

    if {[catch {sdds save $outputFile data} result]} {
        return -code error "APSProcessLogOnChangeData: $result"
    }
}
