Files
sics/site_ansto/instrument/config/nexus/nxscripts_common_1.tcl
Ferdi Franceschini 0f96e4f82f server_config.tcl
server_init now calls ::counter::isc_initialize, ::histogram_memory::isc_initialize and ::scan::isc_initialize

scan.c
SICS-158  Restrict bmonscan and hmscan to only one scan variable

count_common.tcl
Create a mulicounter which uses the available monitors as channels.  The multicounter is used in bmonscans

hipadaba_configuration_common.tcl
Use _link as the suffix for the nxvgroup name when making nexus file links.

hmm_configuration_common.tcl
SICS-190 Create hdb entries for bm_counts, bm_event_rate, bm_status
SICS-164 Allow running histmem in simulated mode.

hmm_cylindrical_detector_configuration.tcl
Only refer to detector SICSVariables when necessary, otherwise we get dependency problems.

nxscripts_common_1.tcl
Separate histmem_filtype_spec from bmon_filetype_spec so that we can use the histem_filetyp_spec names to restrict the histmem scan filetypes.
Close the data file on error in proc ::nexus::save
Allow int and text types when saving macro data

scan_common_1.tcl
SICS-173  Allow users to force a scan when the instrument isn't ready
Added ic_initialize proc so we can make dependencies explicit in the init_server proc
SICS-186 Allow monitor modes for histmem scans
Allow selection of histmem file policies from runscan command

echidna, wombat, kowari, quokka and platypus component and server configurations
Added instrument specific configuration initialisation functions in the component configuration files.
Add a catch block to the top-level instrument server configuration file and printout the errorContext and callStack

r2641 | ffr | 2008-07-01 17:02:26 +1000 (Tue, 01 Jul 2008) | 34 lines
2012-11-15 13:39:55 +11:00

1079 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
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 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
} 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 ] {
::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
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 [ 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 [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.39 $}]
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