Files
sics/site_ansto/instrument/config/nexus/nxscripts_common_1.tcl
Ferdi Franceschini 01be487c52 pelican
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
2012-11-15 13:40:07 +11:00

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