Files
sics/site_ansto/instrument/config/nexus/nxscripts_common_1.tcl
Ferdi Franceschini ef7c2a6f87 histmem.c
sicvar.c
SICS-93 Save histogram data and instrument state during an acquisition

hipadaba_configuration_common.tcl
SICS-185 Preserve case on hdb node names

hmm_configuration_common_1.tcl
nxscripts_common_1.tcl
sicvar.c
SICS-174 Allow saving of histmem data and instrument status during a count operation.

wombat_configuration.tcl
echidna_configuration.tcl
platypus_configuration.tcl
kowari_configuration.tcl
quokka_configuration.tcl
SICS-153 Move setup of statemon to new server_init function for the new sics_uid state variable

server_config.tcl
SICS-153 set attributes on the sics_suid var which is now created by MakeStateMon
SICS-187 Add cold source info (TODO move to platypus,quokka and pelican configs)

utility.tcl
SICS-185 Added normalgetatt to preserve case on hdb node names etc.

sans/config/optics/aperture_configuration.tcl
New file, Provides lookup tables for attenuation and entrance apertures. (SICS-157)

sans/config/optics/optics.tcl
Load the new aperture_configuration.tcl

sans/config/parameters/parameters.tcl
SICS-157 Add instrument parameters section with derived parameters and their dependencies

statemon.c
SICS-153 Create sics_suid sicsvariable when loading statemon and increment it on each status change.

r2635 | ffr | 2008-06-23 12:41:12 +1000 (Mon, 23 Jun 2008) | 38 lines
2012-11-15 13:39:42 +11:00

1060 lines
36 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
sicslist setatt data_run_number mutable true
sicslist setatt timestamp mutable true
sicslist setatt timestamp units seconds
namespace eval nexus {
variable data_gp_path "/data"
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 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}}
}
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}}
}
}
}
##
# @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
} else {
::nexus::process_filetype_policy $type filetype_spec
}
} 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 ] {
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 ] {
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
array set hm_prop [attlist $hm]
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 [attlist $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 {[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}
default {error "ERROR: [info level -1]->::nexus::sicsvariable::save, unknown type $data_type"}
}
nxscript putslab $nxalias [list $index] [list 1] nxscript_data
} else {
nxscript putfloat $nxalias [SplitReply [$sobj]]
}
}
##
# @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 [attlist $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 [attlist $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)
}
}
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.38 $}]
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