Create pas/config/source/source.tcl config/source/source_common.tcl pelican_configuration.tcl deploySICS.sh Don't deploy script validator configs, they are obsolete server_config.tcl Source config has been moved to config/source and is specialised for each instrument by calling the instrument specific initialiser ::source:isc_initalize commands_common_1.tcl The monitor mode and preset are recorded when running the count command counter_common_1.tcl Report and record the counts and time for the multicounter and each monitor. Also record the mode and preset for the multicounter. hmm_configuration_common_1.tcl Cleanup, remove obsolete code. Report and record the hmm total counts and time. Allow monitor controlled acquisition from the histogram server nxscripts_common_1.tcl Add DataType to NXdata section, preserve case in text attributes. scan_common_1.tcl Provide sensible feedback for our bmonscan and hmscan objects. Set run_mode when doing a scan. Allow users to select the datatype for histmem scans and to force a scan wombat_configuration.tcl echidna_configuration.tcl platypus_configuration.tcl kowari_configuration.tcl quokka_configuration.tcl Load source config. quokka_configuration.tcl SICS-198 HACK, Add AttrotDeg and RotApDeg status feedback. sans/commands/commands.tcl Add status feedback for quokka "guide" command. sans/parameters/parameters.tcl Make sure data can be saved as xml (replace % with _percent) SICS-198 HACK, add AttRotDeg and RotApDeg status feedback. utility.tcl Fix nasty "params array generator" bug. SICS commands execute at runlevel 0 which means that the params anonymous array must be made at this level. Provide the normalattlist command to enable creating attribute lists with the case preserved. Added the runsics, runtestsics and sicsclient scripts to CVS. Added source configurations for echidna, wombat, kowari, quokka, platypus, and pelican to CVS r2644 | ffr | 2008-07-11 11:09:57 +1000 (Fri, 11 Jul 2008) | 53 lines
1086 lines
37 KiB
Tcl
1086 lines
37 KiB
Tcl
##
|
|
# @file nxscripts_common_1.tcl
|
|
# @brief Defines functions to create and save a nexus datafile based on the hdb tree metadata
|
|
#
|
|
|
|
MakeNXScript
|
|
sicsdatafactory new nxscript_data
|
|
#mkVar name type access long_name nxsave klass control data
|
|
::utility::mkVar start_seconds int user start_seconds false entry false false
|
|
::utility::mkVar estart Text user start_time true entry false true
|
|
::utility::mkVar eend Text user end_time true entry false true
|
|
::utility::mkVar timestamp int user time_stamp true entry false true
|
|
::utility::mkVar data_run_number int user run_number true instrument false true
|
|
::utility::mkVar nexus_datatype text user DataType true data false true
|
|
sicslist setatt data_run_number mutable true
|
|
sicslist setatt timestamp mutable true
|
|
sicslist setatt timestamp units seconds
|
|
sicslist setatt nexus_datatype mutable false
|
|
|
|
namespace eval nexus {
|
|
variable data_gp_path "/data"
|
|
nexus_datatype "UNKNOWN"
|
|
set exports [list newfile closefile save data]
|
|
eval namespace export $exports
|
|
if 0 {datafilename}
|
|
|
|
variable filetypes
|
|
set filetypes [list BEAM_MONITOR HISTOPERIOD_XYT TOTAL_HISTOGRAM_XY TOTAL_HISTOGRAM_XT TOTAL_HISTOGRAM_YT TOTAL_HISTOGRAM_X TOTAL_HISTOGRAM_Y TOTAL_HISTOGRAM_T ]
|
|
|
|
##
|
|
# @brief This is the nexus dictionary generated by the gen_nxdict function
|
|
variable nxdictionary
|
|
##
|
|
# @brief Records the current Nexus file state.
|
|
variable state
|
|
|
|
##
|
|
# @brief Specifies the save policy with an optional list of data link sources.
|
|
#
|
|
# NOTE: The ::histogram_memory::horizontal_axis and ::histogram_memory::vertical_axis are aliases which
|
|
# must be set by the instrument specific histogram memory configuration.
|
|
#
|
|
# TODO Put the filetype_spec in a separate file.
|
|
variable bmon_filetype_spec
|
|
array set bmon_filetype_spec {
|
|
BEAM_MONITOR {
|
|
link {axis 1 data_run_number}
|
|
link {data_set monitor_counts}
|
|
save_policy {include @all exclude {hmm hmm_xy hmm_xt hmm_yt hmm_x hmm_y hmm_t}}
|
|
}
|
|
}
|
|
variable histmem_filetype_spec
|
|
array set histmem_filetype_spec {
|
|
HISTOGRAM_XYT {
|
|
link {axis 1 data_run_number}
|
|
link {axis 2 ::histogram_memory::time_channel}
|
|
link {axis 3 ::histogram_memory::vertical_axis}
|
|
link {axis 4 ::histogram_memory::horizontal_axis}
|
|
link {data_set hmm}
|
|
save_policy {include @all exclude {hmm_xy hmm_xt hmm_yt hmm_x hmm_y hmm_t}}
|
|
}
|
|
HISTOGRAM_XY {
|
|
link {axis 1 data_run_number}
|
|
link {axis 2 ::histogram_memory::vertical_axis}
|
|
link {axis 3 ::histogram_memory::horizontal_axis}
|
|
link {data_set hmm_xy}
|
|
save_policy {include @all exclude {hmm hmm_xt hmm_yt hmm_x hmm_y hmm_t}}
|
|
}
|
|
HISTOGRAM_XT {
|
|
link {axis 1 data_run_number}
|
|
link {axis 2 ::histogram_memory::time_channel}
|
|
link {axis 3 ::histogram_memory::horizontal_axis}
|
|
link {data_set hmm_xt}
|
|
save_policy {include @all exclude {hmm_xy hmm hmm_yt hmm_x hmm_y hmm_t}}
|
|
}
|
|
HISTOGRAM_YT {
|
|
link {axis 1 data_run_number}
|
|
link {axis 2 ::histogram_memory::time_channel}
|
|
link {axis 3 ::histogram_memory::vertical_axis}
|
|
link {data_set hmm_yt}
|
|
save_policy {include @all exclude {hmm_xy hmm_xt hmm hmm_x hmm_y hmm_t}}
|
|
}
|
|
HISTOGRAM_X {
|
|
link {axis 1 data_run_number}
|
|
link {axis 2 ::histogram_memory::horizontal_axis}
|
|
link {data_set hmm_x}
|
|
save_policy {include @all exclude {hmm_xy hmm_xt hmm_yt hmm hmm_y hmm_t}}
|
|
}
|
|
HISTOGRAM_Y {
|
|
link {axis 1 data_run_number}
|
|
link {axis 2 ::histogram_memory::vertical_axis}
|
|
link {data_set hmm_y}
|
|
save_policy {include @all exclude {hmm_xy hmm_xt hmm_yt hmm_x hmm hmm_t}}
|
|
}
|
|
HISTOGRAM_T {
|
|
link {axis 1 data_run_number}
|
|
link {axis 2 ::histogram_memory::time_channel}
|
|
link {data_set hmm_t}
|
|
save_policy {include @all exclude {hmm_xy hmm_xt hmm_yt hmm_x hmm_y hmm}}
|
|
}
|
|
}
|
|
|
|
variable filetype_spec [concat [array get bmon_filetype_spec] [array get histmem_filetype_spec] ]
|
|
}
|
|
|
|
##
|
|
# @brief Strips the output of a SICS command leaving only the value\n
|
|
# TODO Replace $cmd_output with [$cmd_output] so we can just pass the command name
|
|
#
|
|
# @param cmd_output The output from a command substitution
|
|
# @return The unadorned value returned by the SICS command which produced cmd_output
|
|
proc getVal {cmd_output} {
|
|
return [string trim [lindex [split $cmd_output =] 1 ] ]
|
|
}
|
|
|
|
proc ::nexus::datapath {} {
|
|
return [SplitReply [sicsdatapath]]
|
|
}
|
|
##
|
|
# @brief Generate a filename from sicsdatanumber and sicsdatapath
|
|
#
|
|
# @param postfix This is the filename suffix, must be one of: nx.hdf, hdf, h5, nx5, xml
|
|
proc newFileName {postfix} {
|
|
|
|
array set inst_mnem {quokka QKK wombat WBT echidna ECH kowari KWR koala KOL taipan TPN platypus PLP pelican PLN}
|
|
set idNum [SplitReply [sicsdatanumber]]
|
|
# set prefix [SplitReply [sicsdataprefix]]
|
|
set date_time_arr [split [sicstime] " "]
|
|
set isodate [lindex $date_time_arr 0]
|
|
set isotime [string map {: -} [lindex $date_time_arr 1]]
|
|
return [format "%s/%s%07d.%s" [::nexus::datapath] $inst_mnem([instname]) $idNum $postfix]
|
|
}
|
|
|
|
proc ::nexus::process_filetype_policy {filetype filetype_spec} {
|
|
|
|
upvar $filetype_spec ft_spec
|
|
array set ft_spec_arr $ft_spec
|
|
if {[info exists ft_spec_arr($filetype)] == 0} {
|
|
error "$filetype is invalid, should be one of [array names ft_spec_arr]"
|
|
}
|
|
set ft_policy $ft_spec_arr($filetype)
|
|
::nexus::data clear
|
|
foreach {pol_type policy} $ft_policy {
|
|
switch $pol_type {
|
|
"link" {
|
|
::nexus::data $policy
|
|
}
|
|
"save_policy" {
|
|
foreach {save_action action_list} $policy {
|
|
switch $save_action {
|
|
"include" {
|
|
if {$action_list == "@all"} {
|
|
::hdb::set_save / true
|
|
} else {
|
|
foreach item $action_list {
|
|
if {[getatt $item type] == ""} {
|
|
error "ERROR: Unknown $item specified for inclusion in the data file"
|
|
}
|
|
::hdb::set_save [getatt $item hdb_path] true
|
|
}
|
|
}
|
|
}
|
|
"exclude" {
|
|
if {$action_list == "@all"} {
|
|
::hdb::set_save / false
|
|
} else {
|
|
foreach item $action_list {
|
|
if {[getatt $item type] == ""} {
|
|
error "ERROR: Unknown $item specified for exclusion from the data file"
|
|
}
|
|
::hdb::set_save [getatt $item hdb_path] false
|
|
}
|
|
}
|
|
}
|
|
default {
|
|
error "ERROR: Unknown save action $save_action specified in the save policy"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
default {
|
|
error "$pol_type is invalid, should be one of 'link' 'save_policy'"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
##
|
|
# @brief Initialise state variables
|
|
proc ::nexus::init {} {
|
|
variable state
|
|
variable nexusdic
|
|
array set state {file,new "true" file,open "false" file,namestyle "data"\
|
|
file,format "hdf" file,type "@none"}
|
|
set nexusdic "nexus.dic"
|
|
}
|
|
|
|
##
|
|
# @brief Create a nexus file
|
|
# This first generates a nexus dictionary file from the hdb tree and then creates a new
|
|
# nexus file based on that dictionary.
|
|
proc ::nexus::createfile {} {
|
|
global cfPath
|
|
variable nexusdic
|
|
variable state
|
|
variable data_gp_path
|
|
if [ catch {
|
|
if {$state(file,open) == "true"} {
|
|
error_msg "Can't create a new file because the current file is still open"
|
|
} elseif {$state(file,new) == "false"} {
|
|
error_msg "This function should only be called when state(file,new) = true"
|
|
}
|
|
|
|
set file_format [SplitReply [SicsDataPostFix]]
|
|
array set nxmode [list nx.hdf create5 hdf create5 h5 create5 nx5 create5 xml createxml]
|
|
set nxdict_path [::nexus::gen_nxdict $nexusdic]
|
|
if {$state(file,namestyle) == "scratch"} {
|
|
dataFileName [format "%s/scratch.%s" [::nexus::datapath] $file_format]
|
|
} else {
|
|
sicsdatanumber incr
|
|
dataFileName [newFileName $file_format]
|
|
}
|
|
hsetprop $data_gp_path currentfiletype [::utility::hgetplainprop $data_gp_path datatype]
|
|
nxscript $nxmode($file_format) [SplitReply [dataFileName]] $nxdict_path
|
|
set state(file,open) false
|
|
set state(file,new) false
|
|
} message ] {
|
|
if {$::errorCode=="NONE"} {return $message}
|
|
return -code error $message
|
|
}
|
|
}
|
|
|
|
##
|
|
# @brief Checks if the given file type is defined.
|
|
#
|
|
# @return 1 on success, 0 on failure
|
|
proc ::nexus::isValidFileType {type} {
|
|
variable filetypes
|
|
if {[lsearch $filetypes $type] == -1} {
|
|
return 0
|
|
} else {
|
|
return 1
|
|
}
|
|
}
|
|
##
|
|
# @brief Setup file state info for writing a new file.
|
|
#
|
|
# @type data file type as defined in config/nexus/datafiletype.tcl or clear
|
|
# @param namestyle scratch or data, default=data\n
|
|
# If namestyle=data, the save command will create numbered files using the ANSTO
|
|
# file naming convention.\n
|
|
# If namestyle=scratch, the save command will create scratch files.
|
|
#
|
|
# postconditions:
|
|
# state(file,open) true state(file,new) false
|
|
# /data/currentfiletype == UNKNOWN
|
|
proc ::nexus::newfile {type {namestyle data}} {
|
|
variable filetype_spec
|
|
variable state
|
|
variable data_gp_path
|
|
|
|
if [ catch {
|
|
set state(file,namestyle) $namestyle
|
|
set state(file,new) true
|
|
hsetprop $data_gp_path currentfiletype UNKNOWN
|
|
if {$type == "clear"} {
|
|
::nexus::data clear
|
|
::hdb::set_save / false
|
|
hsetprop $data_gp_path currentfiletype UNKNOWN
|
|
hsetprop $data_gp_path datatype UNKNOWN
|
|
nexus_datatype "UNKNOWN"
|
|
} else {
|
|
::nexus::process_filetype_policy $type filetype_spec
|
|
nexus_datatype $type
|
|
}
|
|
} message ] {
|
|
if {$::errorCode=="NONE"} {return $message}
|
|
return -code error $message
|
|
}
|
|
}
|
|
|
|
##
|
|
# @brief Save data to the currently open file and then close it.
|
|
#
|
|
# @param point This is the array index for mutable data elements
|
|
#
|
|
# This function provides the top level call to the recursive ::nexus::savetree
|
|
# function, it should only be called by the ::nexus::save command.
|
|
#
|
|
# @see ::nexus::savetree
|
|
# @see ::nexus::save
|
|
proc ::nexus::save_data {point} {
|
|
debug_msg "save point $point in [dataFileName]"
|
|
if [ catch {
|
|
if {[info level]<2} {
|
|
error "ERROR: The [lindex [info level 0] 0] command is for internal use only"
|
|
}
|
|
set caller [namespace origin [lindex [info level -1] 0]]
|
|
if {$caller != "::nexus::save"} {
|
|
error "ERROR: [lindex [info level 0] 0] can only be called via the '::nexus::save' command, not by $caller"
|
|
}
|
|
foreach child [hlist /] {
|
|
if {[::utility::hgetplainprop /$child data] == "true"} {
|
|
::nexus::savetree $child $point
|
|
}
|
|
}
|
|
} message ] {
|
|
if {$::errorCode=="NONE"} {return $message}
|
|
return -code error $message
|
|
}
|
|
}
|
|
|
|
##
|
|
# @brief save data collected by last data acquisition command.
|
|
#
|
|
# @param point experimental point number, this is the array index for mutable
|
|
# datasets in the nexus file. Optional, default = 0
|
|
#
|
|
# A new file will be created if the new file state has been set to true, or
|
|
# if the current data type doesn't match the current file type.
|
|
proc ::nexus::save {{point 0}} {
|
|
variable state
|
|
variable data_gp_path
|
|
|
|
if [ catch {
|
|
if {[string is integer $point] == 0} {
|
|
error_msg "save index must be an integer"
|
|
} elseif {$point < 0} {
|
|
error_msg "save index cannot be negative"
|
|
}
|
|
|
|
# ::data::gumtree_save -set run_number $point
|
|
data_run_number $point
|
|
|
|
set isNewFile [expr {$state(file,new) == "true"}]
|
|
set currFileType [::utility::hgetplainprop $data_gp_path currentfiletype]
|
|
set currDataType [::utility::hgetplainprop $data_gp_path datatype]
|
|
set dataTypeChanged [expr {$currFileType != $currDataType}]
|
|
if {$currDataType == "UNKNOWN"} {
|
|
error_msg "You must set the file type, eg 'newfile BEAM_MONITOR' or 'newfile BEAM_MONITOR scratch' "
|
|
}
|
|
|
|
if {$isNewFile || $dataTypeChanged} {
|
|
set state(file,new) true
|
|
::nexus::createfile
|
|
estart [lindex [sicstime] 1]
|
|
eend [lindex [sicstime] 1]
|
|
start_seconds [clock seconds]
|
|
timestamp 0
|
|
::nexus::nxreopenfile
|
|
::nexus::save_data $point
|
|
::nexus::makelinks
|
|
::nexus::set_plotdata_info
|
|
::nexus::nxclosefile
|
|
} else {
|
|
eend [lindex [sicstime] 1]
|
|
timestamp [expr {[clock seconds] - [SplitReply [start_seconds]]}]
|
|
::nexus::nxreopenfile
|
|
::nexus::save_data $point
|
|
::nexus::nxclosefile
|
|
}
|
|
} message ] {
|
|
::nexus::nxclosefile
|
|
if {$::errorCode=="NONE"} {return $message}
|
|
return -code error $message
|
|
}
|
|
return
|
|
}
|
|
|
|
##
|
|
# @brief Reopen the current file, close it with nxclosefile
|
|
# this should only be called by the ::nexus::save command.
|
|
#
|
|
# @see nxclosefile
|
|
# @see ::nexus::save
|
|
proc ::nexus::nxreopenfile {} {
|
|
global cfPath
|
|
variable state
|
|
variable nexusdic
|
|
if [ catch {
|
|
if {[info level]<2} {
|
|
error "ERROR: The [lindex [info level 0] 0] command is for internal use only"
|
|
}
|
|
set caller [namespace origin [lindex [info level -1] 0]]
|
|
if {$caller != "::nexus::save"} {
|
|
error "ERROR: [lindex [info level 0] 0] can only be called via the '::nexus::save' command, not by $caller"
|
|
}
|
|
if {$state(file,open) == "false"} {
|
|
nxscript reopen [SplitReply [dataFileName]] $cfPath(nexus)/$nexusdic
|
|
set state(file,open) true
|
|
}
|
|
} message ] {
|
|
if {$::errorCode=="NONE"} {return $message}
|
|
return -code error $message
|
|
}
|
|
}
|
|
|
|
##
|
|
# @brief Close the current file. You can reopen it with nxreopenfile
|
|
# this should only be called by the ::nexus::save command.
|
|
#
|
|
# @see nxreopenfile
|
|
# @see ::nexus::save
|
|
proc ::nexus::nxclosefile {} {
|
|
variable state
|
|
if [ catch {
|
|
if {[info level]<2} {
|
|
error "ERROR: The [lindex [info level 0] 0] command is for internal use only"
|
|
}
|
|
set caller [namespace origin [lindex [info level -1] 0]]
|
|
if {$caller != "::nexus::save"} {
|
|
error "ERROR: [lindex [info level 0] 0] can only be called via the '::nexus::save' command, not by $caller"
|
|
}
|
|
if {$state(file,open) == "true"} {
|
|
nxscript close
|
|
set state(file,open) false
|
|
set flist [split [SplitReply [dataFileName]] "/"]
|
|
set fname [lindex $flist [expr [llength $flist] - 1] ]
|
|
clientput "$fname updated" "event"
|
|
}
|
|
} message ] {
|
|
if {$::errorCode=="NONE"} {return $message}
|
|
return -code error $message
|
|
}
|
|
}
|
|
##
|
|
# @brief Records that a given data source should be linked to nexus data target.
|
|
#
|
|
# NOTE: If a link has already been recorded then it does nothing. This allows you to
|
|
# override default links set by a command. eg A "count" command may link axis_1 to
|
|
# the run number but a "scan" command, which uses the count command, can link axis_1 to
|
|
# a scan variable.
|
|
#
|
|
# Usage:
|
|
# data data_set datsource
|
|
# Records that /data/data_set should be linked to datsource and sets a data type identifier
|
|
# data axis 1|2|3|4 datsource
|
|
# Records that /data/axisn should be linked to datsource
|
|
# data clear
|
|
# Clears all link targets and sets the data type identifier to unknown
|
|
# data alias <name>, remove alias <name>
|
|
# data alias <name> <target>, set <name> as an alias for <target> unless it has already been defined.
|
|
proc ::nexus::data {args} {
|
|
variable state
|
|
variable data_gp_path
|
|
|
|
if {[llength $args] == 1} {
|
|
set arguments [lindex $args 0]
|
|
} else {
|
|
set arguments $args
|
|
}
|
|
|
|
set dpath $data_gp_path
|
|
set opt [lindex $arguments 0]
|
|
set arglist [lrange $arguments 1 end]
|
|
|
|
switch $opt {
|
|
"axis" {
|
|
debug_msg "'axis' case of switch"
|
|
set link_target [lindex $arguments 2]
|
|
if {[getatt $link_target privilege] == "internal"} {
|
|
error "[info level 0], Cannot link $link_target because it doesn't have an hdb node."
|
|
}
|
|
set axnum [lindex $arguments 1]
|
|
if {[string is integer $axnum] == 0} {
|
|
error "ERROR: [info level -1]->data, index for data axis should be an integer, not $axnum"
|
|
}
|
|
if {[getatt $link_target type] == ""} {
|
|
error "Unknown link target $link_target"
|
|
}
|
|
set hp $dpath/axis_$axnum
|
|
# if {[::utility::hgetplainprop $hp link] == "@none"} {
|
|
hsetprop $hp link [getatt [lindex $arguments 2] id]
|
|
hsetprop $hp long_name [getatt [lindex $arguments 2] long_name]
|
|
# }
|
|
}
|
|
"data_set" {
|
|
debug_msg "'data_set' case of switch"
|
|
set link_target [lindex $arguments 1]
|
|
if {[getatt $link_target type] == ""} {
|
|
error "Unknown link target $link_target"
|
|
}
|
|
hsetprop $dpath datatype [lindex [info level -1] 0]
|
|
set hp $dpath/data_set
|
|
# if {[::utility::hgetplainprop $hp link] == "@none"} {
|
|
hsetprop $hp link [getatt $link_target id]
|
|
hsetprop $hp long_name [getatt $link_target long_name]
|
|
# }
|
|
}
|
|
"clear" {
|
|
debug_msg "'clear' case of switch"
|
|
foreach child [hlist $dpath] {
|
|
hsetprop $dpath/$child link @none
|
|
hsetprop $dpath/$child long_name @none
|
|
}
|
|
}
|
|
"alias" {
|
|
debug_msg "'alias' case of switch"
|
|
set alias_name [lindex $arglist 0]
|
|
set alias_target [lindex $arglist 1]
|
|
switch $alias_target {
|
|
"" {
|
|
if {[info exists state(data,alias,$alias_name)]} {
|
|
definealias $alias_name
|
|
set state(data,alias,$alias_name) @none
|
|
}
|
|
}
|
|
default {
|
|
if {[info exists state(data,alias,$alias_name)]} {
|
|
if { $state(data,alias,$alias_name) == "@none" } {
|
|
definealias $alias_name $alias_target
|
|
}
|
|
} else {
|
|
definealias $alias_name $alias_target
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}
|
|
default {error "ERROR: [info level -1]->data, Unsupported option $opt"}
|
|
}
|
|
}
|
|
|
|
##
|
|
# @brief Make dataset links
|
|
#
|
|
proc ::nexus::makelinks {{hpath /}} {
|
|
if [ catch {
|
|
foreach child [hlist $hpath] {
|
|
if {$hpath == "/"} {
|
|
set newpath /$child
|
|
} else {
|
|
set newpath $hpath/$child
|
|
}
|
|
# clientput $newpath
|
|
array set p_arr [::utility::hlistplainprop $newpath]
|
|
if {$p_arr(data) == "true" && $p_arr(nxsave) == "true"} {
|
|
if {[info exists p_arr(type)] && $p_arr(type) == "nxvgroup"} {
|
|
if {$p_arr(link) != "@none"} {
|
|
# clientput "Link $p_arr(nxalias) to $p_arr(link)"
|
|
nxscript makelink $p_arr(nxalias) $p_arr(link)
|
|
}
|
|
}
|
|
::nexus::makelinks $newpath
|
|
}
|
|
}
|
|
} message ] {
|
|
if {$::errorCode=="NONE"} {return $message}
|
|
return -code error $message
|
|
}
|
|
}
|
|
##
|
|
# @brief Sets the "signal" and "axes" attributes on the plottable data
|
|
# Also sets the "axis" attribute for each of the axes.
|
|
proc ::nexus::set_plotdata_info {} {
|
|
variable data_gp_path
|
|
|
|
array unset axes
|
|
set hpath $data_gp_path
|
|
foreach child [hlist $hpath] {
|
|
array set p_arr [::utility::hlistplainprop $hpath/$child]
|
|
if {$p_arr(data) == true && $p_arr(nxsave) == true} {
|
|
if {[info exists p_arr(nxalias)]} {
|
|
if {[info exists p_arr(type)] && $p_arr(type) == "nxvgroup"} {
|
|
if {$p_arr(link) != "@none"} {
|
|
switch -glob $child {
|
|
"axis_*" {
|
|
set n [lindex [split $child _] 1]
|
|
set axes($n) [::utility::hgetplainprop $hpath/$child long_name]
|
|
nxscript putattribute $p_arr(link) axis $n
|
|
}
|
|
"data_set" {
|
|
nxscript putattribute $p_arr(link) signal 1
|
|
set data_set_alias $p_arr(link)
|
|
}
|
|
default {error "ERROR: [info level -1]->set_plotdata_info, Unsupported data path $hpath/$child"}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if {[info exists axes]} {
|
|
foreach n [lsort [array names axes]] {
|
|
lappend axes_list $axes($n)
|
|
}
|
|
nxscript putattribute $data_set_alias axes [join $axes_list :]
|
|
}
|
|
}
|
|
|
|
##
|
|
# @brief Traverse the hdb subtree from the given path and save the data in the currently open file
|
|
#
|
|
# @param hpath path of subtree to save, must not be "/"
|
|
# @param pt Current array index for mutable data (optional default=0)
|
|
proc ::nexus::savetree {hpath {pt 0}} {
|
|
set ::errorInfo ""
|
|
if [ catch {
|
|
foreach child [hlist /$hpath] {
|
|
array unset p_arr
|
|
array set p_arr [::utility::hlistplainprop /$hpath/$child]
|
|
if {[info exists p_arr(type)] && $p_arr(type) == "nxvgroup"} {
|
|
continue
|
|
}
|
|
set data_type [lindex [split [hinfo /$hpath/$child] , ] 0]
|
|
if {$p_arr(data) == true && $p_arr(nxsave) == true } {
|
|
if {[info exists p_arr(savecmd)] && [info exists p_arr(nxalias)] } {
|
|
if {[info exists p_arr(mutable)] && $p_arr(mutable) == "true" } {
|
|
$p_arr(savecmd) $p_arr(sicsdev) $p_arr(nxalias) $data_type point $pt
|
|
} else {
|
|
$p_arr(savecmd) $p_arr(sicsdev) $p_arr(nxalias) $data_type
|
|
}
|
|
} elseif {[info exists p_arr(savecmd)] || [info exists p_arr(nxalias)]} {
|
|
error_msg "/$hpath/$child must have both 'savecmd' and 'nxalias' properties\nThe actual property list for /$hpath/$child is [array get p_arr]"
|
|
}
|
|
::nexus::savetree $hpath/$child $pt
|
|
}
|
|
}
|
|
} message ] {
|
|
set message "$message, hpath=$hpath/$child"
|
|
if {$::errorCode=="NONE"} {return $message}
|
|
return -code error $message
|
|
}
|
|
}
|
|
|
|
##
|
|
# @brief Recursive portion of gen_nxdict function
|
|
#
|
|
# @param hpath hdb subtree path to generate dictionary fragment from, must not be "/"
|
|
# @param dictPath parent path for nexus dictionary fragment.
|
|
# @param name name for child dictionary path
|
|
# @param nxc Nexus class name
|
|
#
|
|
# If the klass name doesn't begin with NX then construct the SDS name by replacing '/' with '_' in the
|
|
# hdb path
|
|
#
|
|
# @see gen_nxdict
|
|
proc ::nexus::_gen_nxdict {hpath dictPath name nxc} {
|
|
variable nxdictionary
|
|
if [ catch {
|
|
if {[::utility::hgetplainprop /$hpath data] == "false"} {
|
|
debug_msg "$hpath doesn't have a data property"
|
|
return
|
|
}
|
|
foreach child [hlist /$hpath] {
|
|
if {[::utility::hgetplainprop /$hpath/$child data] == true} {
|
|
set nxclass [::utility::hgetplainprop /$hpath/$child klass]
|
|
if {$nxc == "NXentry"} {
|
|
::nexus::_gen_nxdict $hpath/$child $dictPath $child $nxclass
|
|
} elseif {[string range $nxc 0 1] == "NX"} {
|
|
::nexus::_gen_nxdict $hpath/$child $dictPath/$name,$nxc $child $nxclass
|
|
} else {
|
|
::nexus::_gen_nxdict $hpath/$child $dictPath ${name}_$child $nxclass
|
|
}
|
|
}
|
|
}
|
|
array set p_arr [::utility::hlistplainprop /$hpath]
|
|
set data_type [lindex [split [hinfo /$hpath] , ] 0]
|
|
if {$p_arr(data) == "true" && $p_arr(nxsave) == "true" && [info exists p_arr(nxalias)]} {
|
|
set alias $p_arr(nxalias)
|
|
if {[info exists p_arr(sdsinfo)]} {
|
|
if {[info exists p_arr(mutable)] && $p_arr(mutable) == "true"} {
|
|
set nxdictionary($alias) "$dictPath/SDS $name [$p_arr(sdsinfo) $p_arr(sicsdev) $data_type mutable true]"
|
|
} else {
|
|
set nxdictionary($alias) "$dictPath/SDS $name [$p_arr(sdsinfo) $p_arr(sicsdev) $data_type mutable false]"
|
|
}
|
|
} elseif {[info exists p_arr(type)] && $p_arr(type) == "nxvgroup"} {
|
|
set nxdictionary($alias) "$dictPath/NXVGROUP"
|
|
}
|
|
}
|
|
} message ] {
|
|
if {$::errorCode=="NONE"} {return $message}
|
|
return -code error $message
|
|
}
|
|
}
|
|
|
|
##
|
|
# @brief Generate a nexus dictionary file from the hdb tree
|
|
#
|
|
# An entry in the nexus dictionary is generated for each node in the
|
|
# hdb tree which has the following properties and values, data=true and nxsave=true
|
|
#
|
|
# @param nexusdic Name of the nexus dictionary that will be created.
|
|
# @return Full path to the nexus dictionary.
|
|
proc ::nexus::gen_nxdict {nexusdic} {
|
|
global cfPath
|
|
variable nxdictionary
|
|
if [ catch {
|
|
set nxdict_path $cfPath(nexus)/$nexusdic
|
|
array unset nxdictionary
|
|
foreach hp [hlist /] {
|
|
if {[::utility::hgetplainprop /$hp data] == true} {
|
|
set nxclass [::utility::hgetplainprop /$hp klass]
|
|
::nexus::_gen_nxdict $hp /entry1,NXentry $hp $nxclass
|
|
}
|
|
}
|
|
set fh [open $nxdict_path w]
|
|
puts $fh "##NXDICT-1.0"
|
|
puts $fh padim0=0
|
|
puts $fh padim1=0
|
|
puts $fh padim2=0
|
|
foreach {n v} [array get nxdictionary] {
|
|
puts $fh "$n = $v"
|
|
}
|
|
close $fh
|
|
} message ] {
|
|
if {$::errorCode=="NONE"} {return $message}
|
|
return -code error $message
|
|
}
|
|
return $nxdict_path
|
|
}
|
|
|
|
##
|
|
# @brief Set SICS object attributes which are required for creating nexus data files.
|
|
proc ::nexus::set_sobj_attributes {} {
|
|
if [ catch {
|
|
# SICS commands
|
|
sicslist setatt nxscript privilege internal
|
|
# SICS data objects
|
|
sicslist setatt nxscript_data privilege internal
|
|
|
|
foreach sobj [lrange [sicslist type motor] 1 end] {
|
|
sicslist setatt $sobj savecmd ::nexus::motor::save
|
|
sicslist setatt $sobj sdsinfo ::nexus::motor::sdsinfo
|
|
}
|
|
foreach sobj [sicslist type configurablevirtualmotor] {
|
|
sicslist setatt $sobj savecmd ::nexus::motor::save
|
|
sicslist setatt $sobj sdsinfo ::nexus::motor::sdsinfo
|
|
}
|
|
foreach sobj [sicslist type histmem] {
|
|
sicslist setatt $sobj savecmd ::nexus::histmem::save
|
|
sicslist setatt $sobj sdsinfo ::nexus::histmem::sdsinfo
|
|
}
|
|
foreach sobj [sicslist type sicsvariable] {
|
|
sicslist setatt $sobj savecmd ::nexus::sicsvariable::save
|
|
sicslist setatt $sobj sdsinfo ::nexus::sicsvariable::sdsinfo
|
|
}
|
|
foreach sobj [sicslist type singlecounter] {
|
|
sicslist setatt $sobj savecmd ::nexus::singlecounter::save
|
|
sicslist setatt $sobj sdsinfo ::nexus::singlecounter::sdsinfo
|
|
}
|
|
foreach sobj [sicslist type environment_controller] {
|
|
sicslist setatt $sobj savecmd ::nexus::environment_controller::save
|
|
sicslist setatt $sobj sdsinfo ::nexus::environment_controller::sdsinfo
|
|
}
|
|
foreach sobj [sicslist kind script] {
|
|
sicslist setatt $sobj savecmd ::nexus::script::save
|
|
sicslist setatt $sobj sdsinfo ::nexus::script::sdsinfo
|
|
}
|
|
foreach sobj [sicslist kind getset] {
|
|
sicslist setatt $sobj savecmd ::nexus::macro::getset_save
|
|
sicslist setatt $sobj sdsinfo ::nexus::macro::getset_sdsinfo
|
|
}
|
|
} message ] {
|
|
if {$::errorCode=="NONE"} {return $message}
|
|
return -code error $message
|
|
}
|
|
}
|
|
|
|
namespace eval ::nexus::histmem {}
|
|
namespace eval ::nexus::motor {}
|
|
namespace eval ::nexus::environment_controller {}
|
|
namespace eval ::nexus::sicsvariable {}
|
|
namespace eval ::nexus::singlecounter {}
|
|
namespace eval ::nexus::script {}
|
|
|
|
##
|
|
# @brief Convert the given hdb type to Nexus data type
|
|
#
|
|
# @param dtype hdb data type
|
|
# @return Nexus data type
|
|
proc ::nexus::hdb2nx_type {dtype} {
|
|
switch $dtype {
|
|
int {return NX_INT32}
|
|
intar {return NX_INT32}
|
|
intvarar {return NX_INT32}
|
|
float {return NX_FLOAT32}
|
|
floatar {return NX_FLOAT32}
|
|
floatvarar {return NX_FLOAT32}
|
|
text {return NX_CHAR}
|
|
default {error "ERROR: [info level -1]->hdb2nx_type, Unknown type $dtype"}
|
|
}
|
|
}
|
|
|
|
##
|
|
# @brief Save command for histogram memory data.
|
|
#
|
|
# The savecmd attribute of any histogram memory objects should be set to this function
|
|
#
|
|
# @see set_sobj_attributes
|
|
proc ::nexus::histmem::save {hm nxalias data_type args} {
|
|
if [catch {
|
|
set rank [SplitReply [$hm configure rank]]
|
|
|
|
set datalen 1
|
|
set indStartList [lindex $args 1]
|
|
set indLenList [list 1]
|
|
for {set i 0} {$i < $rank} {incr i} {
|
|
lappend indStartList 0
|
|
set dim$i [SplitReply [$hm configure dim$i]]
|
|
lappend indLenList [set dim$i]
|
|
set datalen [expr $datalen * [set dim$i]]
|
|
nxscript updatedictvar padim$i [set dim$i]
|
|
}
|
|
set data_start 0
|
|
set bank 1
|
|
nxscript putslab $nxalias $indStartList $indLenList $hm $data_start $datalen $bank
|
|
}] {
|
|
return -code error $::errorInfo
|
|
}
|
|
}
|
|
|
|
# TODO Get rank from /data
|
|
proc ::nexus::histmem::sdsinfo {hm data_type args} {
|
|
array set param $args
|
|
set rank [SplitReply [$hm configure rank]]
|
|
|
|
for {set i 0} {$i < $rank} {incr i} {lappend dimstr "\$(padim$i)"}
|
|
set dimstr [join $dimstr ,]
|
|
if {$param(mutable) == true} {
|
|
return " -type NX_INT32 -LZW -rank [expr $rank+1] -dim {-1,$dimstr}"
|
|
} else {
|
|
return " -type NX_INT32 -LZW -rank $rank -dim {$dimstr}"
|
|
}
|
|
}
|
|
|
|
# The save commands are called with the sobj name and nxalias
|
|
# The sdsinfo commands provide the SDS description for an nxdic
|
|
|
|
proc ::nexus::motor::save {motor nxalias data_type args} {
|
|
if {[lindex $args 0] == "point"} {
|
|
set index [lindex $args 1]
|
|
nxscript_data clear
|
|
nxscript_data putfloat 0 [getVal [$motor] ]
|
|
nxscript putslab $nxalias [list $index] [list 1] nxscript_data
|
|
} else {
|
|
if {[getatt $motor type] == "motor"} {
|
|
nxscript putmot $nxalias $motor
|
|
} else {
|
|
nxscript putfloat $nxalias [SplitReply [$motor]]
|
|
}
|
|
}
|
|
}
|
|
proc ::nexus::motor::sdsinfo {motor data_type args} {
|
|
array set param $args
|
|
array set attribute [::utility::normalattlist $motor]
|
|
set dtype [::nexus::hdb2nx_type $data_type]
|
|
if {[info exists attribute(units)]} {
|
|
set units_att " -attr {units,$attribute(units)} "
|
|
} else {
|
|
set units_att " "
|
|
}
|
|
set name_att " -attr {long_name,$attribute(long_name)} "
|
|
if {$param(mutable) == true} {
|
|
return " -type $dtype -rank 1 -dim {-1} $units_att $name_att"
|
|
} else {
|
|
return " -type $dtype $units_att $name_att"
|
|
}
|
|
}
|
|
##
|
|
# @brief Save data from a 'getset macro'
|
|
#
|
|
# NOTE: Currently just saves floats
|
|
namespace eval ::nexus::macro {}
|
|
proc ::nexus::macro::getset_save {sobj nxalias data_type args} {
|
|
if [ catch {
|
|
if {[lindex $args 0] == "point"} {
|
|
set index [lindex $args 1]
|
|
nxscript_data clear
|
|
set val [getVal [$sobj] ]
|
|
switch $data_type {
|
|
int {nxscript_data putint 0 $val}
|
|
float {nxscript_data putfloat 0 $val}
|
|
text {error "ERROR: [info level 0] Saving an array of text values is not implemented"}
|
|
default {error "ERROR: [info level 0] unknown type $data_type when saving $sobj"}
|
|
}
|
|
nxscript putslab $nxalias [list $index] [list 1] nxscript_data
|
|
} else {
|
|
switch $data_type {
|
|
int {nxscript putint $nxalias [SplitReply [$sobj]]}
|
|
float {nxscript putfloat $nxalias [SplitReply [$sobj]]}
|
|
text {nxscript puttext $nxalias [SplitReply [$sobj]]}
|
|
default {error "ERROR: [info level 0] Unknown data type $data_type when saving $sobj"}
|
|
}
|
|
}
|
|
} message ] {
|
|
if {$::errorCode=="NONE"} {return $message}
|
|
return -code error $message
|
|
}
|
|
}
|
|
|
|
##
|
|
# @brief Define the scientific data set path for the nexus dictionary.
|
|
proc ::nexus::macro::getset_sdsinfo {sobj data_type args} {
|
|
array set param $args
|
|
array set attribute [::utility::normalattlist $sobj]
|
|
set dtype [::nexus::hdb2nx_type $data_type]
|
|
if {[info exists attribute(units)]} {
|
|
set units_att " -attr {units,$attribute(units)} "
|
|
} else {
|
|
set units_att " "
|
|
}
|
|
set name_att " -attr {long_name,$attribute(long_name)} "
|
|
if {$param(mutable) == true} {
|
|
return " -type $dtype -rank 1 -dim {-1} $units_att $name_att"
|
|
} else {
|
|
return " -type $dtype $units_att $name_att"
|
|
}
|
|
}
|
|
####
|
|
proc ::nexus::environment_controller::save {evc nxalias data_type args} {
|
|
if {[lindex $args 0] == "point"} {
|
|
set index [lindex $args 1]
|
|
nxscript_data clear
|
|
nxscript_data putfloat 0 [getVal [$evc] ]
|
|
nxscript putslab $nxalias [list $index] [list 1] nxscript_data
|
|
} else {
|
|
nxscript putfloat $nxalias [SplitReply [$evc]]
|
|
}
|
|
}
|
|
proc ::nexus::environment_controller::sdsinfo {evc data_type args} {
|
|
array set param $args
|
|
array set attribute [::utility::normalattlist $evc]
|
|
set dtype [::nexus::hdb2nx_type $data_type]
|
|
if {[info exists attribute(units)]} {
|
|
set units_att " -attr {units,$attribute(units)} "
|
|
} else {
|
|
set units_att " "
|
|
}
|
|
set name_att " -attr {long_name,$attribute(long_name)} "
|
|
if {$param(mutable) == true} {
|
|
return " -type $dtype -rank 1 -dim {-1} $units_att $name_att"
|
|
} else {
|
|
return " -type $dtype $units_att $name_att"
|
|
}
|
|
}
|
|
|
|
namespace eval ::nexus {
|
|
}
|
|
|
|
proc ::nexus::sicsvariable::save {svar nxalias data_type args} {
|
|
array set attribute [attlist $svar]
|
|
set val [SplitReply [$svar]]
|
|
if {[lindex $args 0] == "point"} {
|
|
set index [lindex $args 1]
|
|
nxscript_data clear
|
|
switch $data_type {
|
|
int {nxscript_data putint 0 $val}
|
|
float {nxscript_data putfloat 0 $val}
|
|
default {error "ERROR: [info level -1]->::nexus::sicsvariable::save, unknown type $data_type"}
|
|
}
|
|
nxscript putslab $nxalias [list $index] [list 1] nxscript_data
|
|
} else {
|
|
switch $data_type {
|
|
int {nxscript putint $nxalias $val}
|
|
float {nxscript putfloat $nxalias $val}
|
|
text {nxscript puttext $nxalias $val}
|
|
default {error "ERROR: [info level -1]->::nexus::sicsvariable::save, unknown type $data_type"}
|
|
}
|
|
}
|
|
if {[info exists attribute(units)]} {
|
|
nxscript putattribute $nxalias units $attribute(units)
|
|
}
|
|
}
|
|
|
|
# TODO Add optional units to sicsvariables
|
|
proc ::nexus::sicsvariable::sdsinfo {svar data_type args} {
|
|
array set param $args
|
|
set dtype [::nexus::hdb2nx_type $data_type]
|
|
if {$param(mutable) == true} {
|
|
return " -type $dtype -rank 1 -dim {-1}"
|
|
} else {
|
|
return " -type $dtype"
|
|
}
|
|
}
|
|
|
|
proc ::nexus::singlecounter::save {counter nxalias data_type args} {
|
|
todo_msg "Save counter: $counter"
|
|
}
|
|
proc ::nexus::singlecounter::sdsinfo {counter data_type args} {
|
|
todo_msg "Get sdsinfo for counter: $counter"
|
|
}
|
|
|
|
##
|
|
# @brief Save command for hdb nodes associated with a tcl macro
|
|
#
|
|
# The macro must return a 1D associative array when called with -arrayname.
|
|
proc ::nexus::script::save {script nxalias data_type args} {
|
|
if [ catch {
|
|
array set attribute [attlist $script]
|
|
if {$attribute(klass) == "sensor"} {
|
|
if {[lindex $args 0] == "point"} {
|
|
set index [lindex $args 1]
|
|
nxscript_data clear
|
|
nxscript_data putfloat 0 [$script]
|
|
nxscript putslab $nxalias [list $index] [list 1] nxscript_data
|
|
} else {
|
|
nxscript putfloat $nxalias [$script]
|
|
}
|
|
} else {
|
|
set darray [$script -arrayname]
|
|
set size [array size $darray]
|
|
set size [SplitReply [$darray used]]
|
|
if {[lindex $args 0] == "point"} {
|
|
set index [lindex $args 1]
|
|
nxscript putslab $nxalias [list $index 0] [list 1 $size] $darray
|
|
} else {
|
|
nxscript putslab $nxalias [list 0] [list $size] $darray
|
|
}
|
|
if {[info exists attribute(units)]} {
|
|
nxscript putattribute $nxalias units $attribute(units)
|
|
}
|
|
}
|
|
} message ] {
|
|
if {$::errorCode=="NONE"} {return $message}
|
|
return -code error $message
|
|
}
|
|
}
|
|
|
|
proc ::nexus::script::sdsinfo {script data_type args} {
|
|
if [ catch {
|
|
array set param $args
|
|
set dtype [::nexus::hdb2nx_type $data_type]
|
|
if {[getatt $script klass] == "sensor"} {
|
|
if {$param(mutable) == true} {
|
|
return " -type $dtype -rank 1 -dim {-1}"
|
|
} else {
|
|
return " -type $dtype"
|
|
}
|
|
} else {
|
|
set darray [$script -arrayname]
|
|
set size [SplitReply [$darray used]]
|
|
if {$param(mutable) == true} {
|
|
return " -type $dtype -rank 2 -dim {-1,$size}"
|
|
} else {
|
|
return " -type $dtype -rank 1 -dim {$size}"
|
|
}
|
|
}
|
|
} message ] {
|
|
if {$::errorCode=="NONE"} {return $message}
|
|
return -code error $message
|
|
}
|
|
}
|
|
|
|
namespace import ::nexus::*
|
|
foreach expt $::nexus::exports {
|
|
publish $expt user
|
|
sicslist setatt $expt privilege internal
|
|
}
|
|
|
|
# TODO Return filename from nxcreatefile and call nxreopen nxclose etc
|
|
# TODO Make an nxscript namespace for all this.
|
|
|
|
# dictalias is a global hash which records the alias which the value of
|
|
# a sics object (eg motors) is written to. The has is indexed by the
|
|
# objects name. It is useful for making links to datasets.
|
|
# dim0 = vertical axis on detector
|
|
# dim1 = horizontal axis on detector
|
|
|
|
set tmpstr [string map {"$" ""} {$Name: not supported by cvs2svn $}]
|
|
set nx_content_release_tag [lindex $tmpstr [expr [llength $tmpstr] - 1]]
|
|
set tmpstr [string map {"$" ""} {$Revision: 1.40 $}]
|
|
set nx_content_revision_num [lindex $tmpstr [expr [llength $tmpstr] - 1]]
|
|
|
|
#namespace eval data {
|
|
# ##
|
|
# # @brief Nexus data save command for gumtree control interface
|
|
# #
|
|
# # @param run_number This is the run or scan point number, it serves as the array
|
|
# # index for nexus data sets which correspond to mutable data
|
|
# command gumtree_save {int: run_number} {
|
|
# ::nexus::save $run_number
|
|
# }
|
|
# sicslist setatt ::data::gumtree_save long_name save
|
|
# array set param [::data::gumtree_save -list param]
|
|
# ::utility::mkData $param(run_number) run_number instrument privilege READ_ONLY mutable true control false
|
|
# command gumtree_type {text:nx.hdf,xml type} {
|
|
# SicsDataPostFix $type
|
|
# }
|
|
# sicslist set ::data::gumtree_type long_name file_format
|
|
# ::data::gumtree_type -set type [SplitReply [SicsDataPostFix]]
|
|
#}
|
|
|
|
::nexus::init
|