Files
sics/site_ansto/instrument/config/nexus/nxscripts_common_1.tcl
Ferdi Franceschini 746f495d69 SICS-354 nxscripts_common_1.tcl
Now using a nexus dictionary variable to set the entry name in data files to prevent duplicate or wrong entry names.
Make sure that the isNewFile flag is reset on the first "save" call to prevent file number being incremented multiple times

SICS-394 sans commands.tcl
Added "selbs", "selbsn", and "selbsxz" commands to select and position a beamstop in a safe manner.

SICS-394 sans motor_configuration.tcl
Added new beamstop motor drivers.

SICS-394 sans parameters.tcl
Added BeamStop parameter to record which beamstop has been selected.

r2832 | ffr | 2009-12-03 13:04:57 +1100 (Thu, 03 Dec 2009) | 13 lines
2012-11-15 16:57:31 +11:00

1347 lines
46 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 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 save_count int user save_count true experiment true true
::utility::mkVar currpoint int user currpoint true experiment true true
::utility::mkVar nexus_datatype text user DataType true data false true
::utility::mkVar file_status text user file_status true experiment true true
::utility::mkVar file_set_list Text user file_set true experiment true 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 {
nexus_datatype "UNKNOWN"
set exports [list newfile closefile save data newfile_collection save_collection]
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 Sets the default naming convention for the NXentry group.
variable NXentry_nm_convention
array set NXentry_nm_convention {
"echidna" "simple"
"wombat" "simple"
"kowari" "simple"
"quokka" "fname"
"platypus" "simple"
}
}
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 {}
namespace eval ::histogram_memory {
variable histmem_axes
array set histmem_axes {
TOF /instrument/detector/time
HOR /instrument/detector/x_pixel_offset
VER /instrument/detector/y_pixel_offset
PER /instrument/run_number
SVAR /instrument/run_number
}
}
##
# @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 {idNum postfix} {
if [ catch {
array set inst_mnem {quokka QKK wombat WBT echidna ECH kowari KWR koala KOL taipan TPN platypus PLP pelican PLN}
# 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]]
set fmtStr [format "%s/%s%07d.%s" [::nexus::datapath] $inst_mnem([instname]) $idNum $postfix]
} message ] {
return -code error "([info level 0]) $message"
}
return $fmtStr
}
proc ::nexus::process_filetype_policy {filetype} {
if [ catch {
if {$filetype == "BEAM_MONITOR"} {
::hdb::set_save / true
::hdb::set_save $::histogram_memory::HP_HMM false
set ::counter::HP_BM [getatt monitor_counts hdb_path]
hsetprop $::counter::HP_BM link data_set
hsetprop $::counter::HP_BM @signal 1
} else {
::hdb::set_save / true
hsetprop $::histogram_memory::HP_HMM datatype_savelist $filetype
hsetprop $::histogram_memory::HP_HMM link data_set
::histogram_memory::set_axes $filetype
}
foreach sobj [sicslist link data_set] {
hsetprop [getatt $sobj hdb_path] link data_set
}
foreach sobj [sicslist link parameters_group] {
hsetprop [getatt $sobj hdb_path] link parameters_group
}
} message ] {
return -code error "([info level 0]) $message"
}
}
##
# @brief Initialise state variables
proc ::nexus::init {} {
variable state
variable nexusdic
variable currFilename
variable isNewFile
variable save_count_arr
variable start_seconds_array
variable file_states
array set state {
file,open "false"
file,namestyle "data"
file,format "hdf"
file,fileset "false"
file,incr_datnum "false"
file,labels @singlefile
}
set nexusdic "nexus.dic"
array set currFilename ""
array set isNewFile ""
array set save_count_arr ""
array set start_seconds_array ""
array set file_states {U "UNKNOWN" O "OPEN" C "CLOSED" S "SAVING"}
save_count 0
currpoint 0
file_status UNKNOWN
}
proc ::nexus::ic_initialize {} {
}
proc getBaseName {fileName} {
set baseName [lindex [split [file tail $fileName] "."] 0]
return $baseName
}
##
# @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 {FileName} {
variable nexusdic
variable state
if [ catch {
if {$state(file,open) == "true"} {
error "Can't create a new file because the current file is still open"
}
switch $state(file,format) {
"hdf" {set create_type create5}
"xml" {set create_type createxml}
default {
error "ERROR: Invalid file format $state(file,format)"
}
}
set nxdict_path [::nexus::gen_nxdict $nexusdic]
if {$state(file,incr_datnum) == true} {
sicsdatanumber incr
}
if [catch {nxscript $create_type $FileName $nxdict_path} message] {
killfile
error $message
}
set state(file,open) false
} message ] {
return -code error "([info level 0]) $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
# /data/currentfiletype == UNKNOWN
proc ::nexus::newfile {type {namestyle data}} {
::nexus::newfile_collection -filetype $type -savetype $namestyle
}
##
# @brief Returns a list of paths matching the given property
#
# @param hpath, subtree path to search
# @param prop, name of property, can be 'abc*' but not 'a*c'
proc ::nexus::findHdbProps {hpath prop} {
if [ catch {
set hpList ""
foreach hp [hlist $hpath] {
if {$hpath == "/"} {
set subList [::nexus::findHdbProps /$hp $prop]
} else {
set subList [::nexus::findHdbProps $hpath/$hp $prop]
}
if {[llength $subList] > 0} {
lappend hpList [join $subList]
}
}
if {[string length [hfindprop $hpath $prop]] > 0 } {
lappend hpList $hpath
}
} message ] {
return -code error "([info level 0]) $message"
}
return [join $hpList]
}
##
# @brief Let's make a collection of files (ie a file-set) or just one.
#
# @param -labels L or {L1 L2 ..}, optional. A list of labels which identify a file-set
# If you don't specify a list of labels then only one file is created
proc ::nexus::newfile_collection {args} {
variable state
variable currFilename
variable isNewFile
variable save_count_arr
variable file_states
set valid_options [list "-labels" "-filetype" "-savetype"]
set required_options [list "-filetype" "-savetype"]
if [ catch {
::utility::check_valid_options $args $valid_options
::utility::check_required_options $args $required_options
array set param $args
set file_suffix [SplitReply [SicsDataPostFix]]
switch $file_suffix {
hdf - nx.hdf - h5 - nx5 {set state(file,format) hdf}
xml {set state(file,format) xml}
default { error "ERROR: Invalid file suffix $file_suffix" }
}
set state(file,namestyle) $param(-savetype)
array unset save_count_arr
array unset currFilename
array unset isNewFile
if {$param(-savetype) == "scratch"} {
set state(file,incr_datnum) false
} else {
set state(file,incr_datnum) true
}
set idNum [expr 1 + [SplitReply [sicsdatanumber]]]
if [info exists param(-labels)] {
set state(file,fileset) "true"
set state(file,labels) $param(-labels)
if {$param(-savetype) == "scratch"} {
foreach fid $state(file,labels) {
set save_count_arr($fid) 0
set isNewFile($fid) "true"
set currFilename($fid) [format "%s/scratch_%s.%s" [::nexus::datapath] $fid $file_suffix]
lappend files $currFilename($fid)
}
} else {
foreach fid $state(file,labels) {
set save_count_arr($fid) 0
set isNewFile($fid) "true"
set currFilename($fid) [newFileName $idNum $file_suffix]
incr idNum
lappend files $currFilename($fid)
}
}
file_set_list [join $files ,]
} else {
set state(file,fileset) "false"
set state(file,labels) @singlefile
set save_count_arr(@singlefile) 0
set isNewFile(@singlefile) "true"
if {$param(-savetype) == "scratch"} {
set currFilename(@singlefile) [format "%s/scratch.%s" [::nexus::datapath] $file_suffix]
} else {
set currFilename(@singlefile) [newFileName $idNum $file_suffix]
}
}
save_count 0
currpoint 0
file_status $file_states(U)
if {$param(-filetype) == "clear"} {
::hdb::set_save / false
nexus_datatype "UNKNOWN"
file_set_list "UNKNOWN"
# dataFileName "UNKNOWN"
} else {
foreach hp [split [::nexus::findHdbProps / link]] {
hdelprop $hp link
}
foreach hp [::nexus::findHdbProps / @*] {
foreach {pname pval} [hfindprop $hp @*] {
hdelprop $hp $pname
}
}
::nexus::process_filetype_policy $param(-filetype)
nexus_datatype $param(-filetype)
}
} message ] {
return -code error "([info level 0]) $message"
} else {
return OK
}
}
##
# @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 filestatus} {
set valid_caller "::nexus::save_collection"
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 != $valid_caller} {
error "ERROR: [lindex [info level 0] 0] can only be called via the $valid_caller command, not by $caller"
}
foreach child [hlist /] {
if {[hpropexists /$child data] && [hgetpropval /$child data] == "true"} {
::nexus::savetree $child $point $filestatus
}
}
} message ] {
return -code error "([info level 0]) $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}} {
if [ catch {
::nexus::save_collection -index $point
} message ] {
return -code error "([info level 0]) $message"
}
}
##
# @brief Save data in a file from a file-set
proc ::nexus::save_collection {args} {
variable state
variable start_seconds
variable start_seconds_array
variable currFilename
variable isNewFile
variable save_count_arr
variable file_states
set valid_options [list "-index" "-label"]
set required_options [list "-index"]
if [ catch {
set caught_exception false
::utility::check_valid_options $args $valid_options
::utility::check_required_options $args $required_options
array set param $args
set point $param(-index)
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
if [info exists param(-label)] {
if {[lsearch -exact $state(file,labels) $param(-label)] == -1} {
error "ERROR: The label must be one of $state(file,labels)"
}
set data_label $param(-label)
} else {
if {$state(file,fileset) == "true"} {
error "ERROR: You must set -label when saving to a file-set"
}
set data_label @singlefile
}
if {$isNewFile($data_label)} {
set isNewFile($data_label) "false"
::nexus::createfile $currFilename($data_label)
dataFileName $currFilename($data_label)
estart [sicstime]
eend [sicstime]
array unset start_seconds_array
set start_seconds [clock seconds]
# set start_seconds_array($data_label) $start_seconds
timestamp 0
file_status $file_states(O)
::nexus::nxreopenfile $currFilename($data_label)
file_status $file_states(S)
set baseName [getBaseName $currFilename($data_label)]
set entryName [gen_NXentry_name $baseName]
nxscript updatedictvar pa_entryName $entryName
::nexus::save_data $point newfile
} else {
eend [sicstime]
# timestamp [expr {[clock seconds] - $start_seconds_array($data_label)}]
timestamp [expr {[clock seconds] - $start_seconds}]
dataFileName $currFilename($data_label)
file_status $file_states(O)
::nexus::nxreopenfile $currFilename($data_label)
file_status $file_states(S)
set baseName [getBaseName $currFilename($data_label)]
set entryName [gen_NXentry_name $baseName]
nxscript updatedictvar pa_entryName $entryName
::nexus::save_data $point oldfile
}
} message ] {
set caught_exception true
}
if {[info exists data_label] && [info exists currFilename($data_label)]} {
::nexus::nxclosefile $currFilename($data_label)
file_status $file_states(C)
incr save_count_arr($data_label)
save_count $save_count_arr($data_label)
currpoint $point
} else {
set message "$message !! FAILED ASSERTION: data_lable and/or currFilename undefined !!"
}
if {$caught_exception} {
return -code error "([info level 0]) $message"
} else {
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 {filename} {
global cfPath
variable state
variable nexusdic
set valid_caller "::nexus::save_collection"
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 != $valid_caller} {
error "ERROR: [lindex [info level 0] 0] can only be called via the $valid_caller command, not by $caller"
}
if {$state(file,open) == "false"} {
nxscript reopen $filename $cfPath(nexus)/$nexusdic
set state(file,open) true
}
} message ] {
return -code error "([info level 0]) $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 {filename} {
variable state
set valid_caller "::nexus::save_collection"
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 != $valid_caller} {
error "ERROR: [lindex [info level 0] 0] can only be called via the $valid_caller command, not by $caller"
}
if {$state(file,open) == "true"} {
nxscript close
set state(file,open) false
set flist [split $filename "/"]
set fname [lindex $flist [expr [llength $flist] - 1] ]
clientput "$fname updated" "event"
}
} message ] {
return -code error "([info level 0]) $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 aux_data 1|2|3|4|5|6|7|8|9 datsource
# Records that /data/aux_datan 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} {
# TODO This is obsolete
}
proc ::nexus::data_junk {args} {
variable state
variable data_gp_path
if [ catch {
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" - "aux_data" {
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/${opt}_$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
}
}
}
}
default {error "ERROR: [info level -1]->data, Unsupported option $opt"}
}
} message ] {
return -code error "([info level 0]) $message"
}
}
##
# @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 filestatus} {
if [ catch {
foreach child [hlist /$hpath] {
array unset p_arr
array set p_arr [hlistprop /$hpath/$child tcllist]
if {([info exists p_arr(type)] == 0) || ($p_arr(type) != "nxvgroup")} {
set data_type [lindex [split [hinfo /$hpath/$child] , ] 0]
if {[info exists p_arr(data)] && ($p_arr(data) == true) && ($p_arr(nxsave) == true) } {
if {[info exists p_arr(nxalias)]} {
if {[info exists p_arr(savecmd)]} {
if {[info exists p_arr(mutable)] && ($p_arr(mutable) == "true") } {
$p_arr(savecmd) $p_arr(sicsdev) $p_arr(nxalias) /$hpath/$child $data_type $filestatus point $pt
} else {
$p_arr(savecmd) $p_arr(sicsdev) $p_arr(nxalias) /$hpath/$child $data_type $filestatus
}
} else {
if {[info exists p_arr(mutable)] && ($p_arr(mutable) == "true") } {
nxscript puthdb /$hpath/$child point $pt
} else {
nxscript puthdb /$hpath/$child
}
if {$filestatus == "newfile"} {
if {[info exists p_arr(link)] && ($p_arr(link) != "@none")} {
nxscript makelink $p_arr(link) $p_arr(nxalias)
}
}
}
}
::nexus::savetree $hpath/$child $pt $filestatus
}
}
}
} message ] {
return -code error "([info level 0]) $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 {[hpropexists /$hpath data] && [hgetpropval /$hpath data] == true} {
foreach child [hlist /$hpath] {
if {[hpropexists /$hpath/$child data] && [hgetpropval /$hpath/$child data] == true} {
set nxclass [hgetpropval /$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 [hlistprop /$hpath tcllist]
set data_type [lindex [split [hinfo /$hpath] , ] 0]
if {$data_type != "none" || $p_arr(type) == "nxvgroup"} {
#XXX Do we need to check data_type here. This would skip NXVGROUP nodes
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(sdsname)]} {
set sdsName $p_arr(sdsname)
} else {
set sdsName $name
}
if {[info exists p_arr(mutable)] && $p_arr(mutable) == "true"} {
set isMutable "true"
} else {
set isMutable "false"
}
if {[info exists p_arr(savecmd)]} {
set SDSstr "[$p_arr(sdsinfo) $sdsName $data_type $p_arr(sicsdev) mutable $isMutable hpath /$hpath]"
} else {
set SDSstr "[$p_arr(sdsinfo) $sdsName $data_type mutable $isMutable hpath /$hpath]"
}
set nxdictionary($alias) "$dictPath/SDS $SDSstr"
} elseif {[info exists p_arr(type)] && $p_arr(type) == "nxvgroup"} {
set nxdictionary($alias) "$dictPath/NXVGROUP"
}
}
}
}
} message ] {
return -code error "([info level 0]) $message"
}
}
##
# @brief Generate the NXentry name
#
# @param baseName, this is the data file basename it is used as the entry name
# when the 'fname' naming convention is selected.
proc ::nexus::gen_NXentry_name {baseName} {
variable NXentry_nm_convention
set nm_conv $NXentry_nm_convention([instname])
switch $nm_conv {
"simple" {
return "entry1"
}
"fname" {
return "${baseName}"
}
default {
return -code error "ERROR: Unknown NXentry naming convention $nm_conv"
}
}
}
##
# @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 baseName The base name of the file without suffix
# @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 {[hgetpropval /$hp data] == true} {
set nxclass [hgetpropval /$hp klass]
::nexus::_gen_nxdict $hp /\$(pa_entryName),NXentry $hp $nxclass
}
}
set fh [open $nxdict_path w]
puts $fh "##NXDICT-1.0"
puts $fh pa_entryName=entry1
puts $fh pa_hmmdimstr=-1,0
puts $fh pa_hmmrank=1
puts $fh pa_hmmdatname=hmm
foreach {n v} [array get nxdictionary] {
puts $fh "$n = $v"
}
close $fh
} message ] {
return -code error "([info level 0]) $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 type chopperadapter] {
sicslist setatt $sobj savecmd ::nexus::chopperadapter::save
sicslist setatt $sobj sdsinfo ::nexus::chopperadapter::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 ] {
return -code error "([info level 0]) $message"
}
}
##
# @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 hpath data_type filestatus args} {
variable HMOBJ
if [catch {
set point [lindex $args 1]
array set pa [hlistprop $hpath tcllist]
foreach sdsname $pa(hmmdatname) rank $pa(hmmrank) dimstr $pa(hmmdimstr) hmmslabstart $pa(hmmslabstart) hmmslabend $pa(hmmslabend) hmmperiodsize $pa(hmmperiodsize) axes $pa(@axes) signal $pa(@signal) {
nxscript updatedictvar pa_hmmdatname $sdsname
if {$pa(mutable)} {
nxscript updatedictvar pa_hmmrank [expr {$rank+1}]
nxscript updatedictvar pa_hmmdimstr "-1,$dimstr"
} else {
nxscript updatedictvar pa_hmmrank $rank
nxscript updatedictvar pa_hmmdimstr $dimstr
}
set max_period [SplitReply [$HMOBJ configure maximum_period]]
set datsize $hmmperiodsize
if {$pa(save_periods) == "all_periods"} {
$HMOBJ configure read_data_period_number -1
set datsize [expr $hmmperiodsize * (1 + $max_period)]
} elseif { $max_period > 0} {
$HMOBJ configure read_data_period_number $point
}
broadcast nxscript putslab $nxalias [join [list $point $hmmslabstart ]] [join [list 1 $hmmslabend]] $HMOBJ 0 $datsize 1
nxscript putslab $nxalias [join [list $point $hmmslabstart ]] [join [list 1 $hmmslabend]] $HMOBJ 0 $datsize 1
#####
if {$filestatus == "newfile"} {
if [hpropexists $hpath link] {
set link [hgetpropval $hpath link]
nxscript makelink $link $nxalias
}
nxscript putattribute $nxalias axes $axes
nxscript putattribute $nxalias signal $signal
}
}
} message ] {
return -code error "([info level 0]) $message"
}
}
proc ::nexus::histmem::sdsinfo {sdsName data_type hm args} {
return "\$(pa_hmmdatname) -type NX_INT32 -LZW -rank \$(pa_hmmrank) -dim {\$(pa_hmmdimstr)}"
}
# 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 hpath data_type filestatus args} {
if [ catch {
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]]
}
}
if {$filestatus == "newfile"} {
if [hpropexists $hpath link] {
set link [hgetpropval $hpath link]
nxscript makelink $link $nxalias
}
foreach {propName propValue} [hfindprop $hpath @*] {
set attName [string range $propName 1 end]
nxscript putattribute $nxalias $attName $propValue
}
}
} message ] {
return -code error "([info level 0]) $message"
}
}
proc ::nexus::motor::sdsinfo {sdsName data_type motor args} {
if [ catch {
array set param $args
array set attribute [::utility::normalattlist $motor]
if {[info exists attribute(units)]} {
set units_att " -attr {units,$attribute(units)} "
} else {
set units_att " "
}
set dtype [::nexus::hdb2nx_type $data_type]
set name_att " -attr {long_name,$attribute(long_name)} "
if {$param(mutable) == true} {
set sdsStr "$sdsName -type $dtype -rank 1 -dim {-1} $units_att $name_att"
} else {
set sdsStr "$sdsName -type $dtype $units_att $name_att"
}
} message ] {
return -code error "([info level 0]) $message"
}
return $sdsStr
}
##
# @brief Save data from a 'getset macro'
#
# NOTE: Currently just saves floats
namespace eval ::nexus::macro {}
proc ::nexus::macro::getset_save {sobj nxalias hpath data_type filestatus 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"}
}
}
if {$filestatus == "newfile"} {
if [hpropexists $hpath link] {
set link [hgetpropval $hpath link]
nxscript makelink $link $nxalias
}
foreach {propName propValue} [hfindprop $hpath @*] {
set attName [string range $propName 1 end]
nxscript putattribute $nxalias $attName $propValue
}
}
} message ] {
return -code error "([info level 0]) $message"
}
}
##
# @brief Define the scientific data set path for the nexus dictionary.
proc ::nexus::macro::getset_sdsinfo {sdsName data_type sobj args} {
if [ catch {
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} {
set sdsStr "$sdsName -type $dtype -rank 1 -dim {-1} $units_att $name_att"
} else {
set sdsStr "$sdsName -type $dtype $units_att $name_att"
}
} message ] {
return -code error "([info level 0]) $message"
}
return $sdsStr
}
####
proc ::nexus::environment_controller::save {evc nxalias hpath data_type filestatus args} {
if [ catch {
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]]
}
if {$filestatus == "newfile"} {
if [hpropexists $hpath link] {
set link [hgetpropval $hpath link]
nxscript makelink $link $nxalias
}
foreach {propName propValue} [hfindprop $hpath @*] {
set attName [string range $propName 1 end]
nxscript putattribute $nxalias $attName $propValue
}
}
} message ] {
return -code error "([info level 0]) $message"
}
}
proc ::nexus::environment_controller::sdsinfo {sdsName data_type evc args} {
if [ catch {
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} {
set sdsStr "$sdsName -type $dtype -rank 1 -dim {-1} $units_att $name_att"
} else {
set sdsStr "$sdsName -type $dtype $units_att $name_att"
}
} message ] {
return -code error "([info level 0]) $message"
}
return $sdsStr
}
namespace eval ::nexus {
}
proc ::nexus::sicsvariable::save {svar nxalias hpath data_type filestatus args} {
if [ catch {
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 {$filestatus == "newfile"} {
if [hpropexists $hpath link] {
set link [hgetpropval $hpath link]
nxscript makelink $link $nxalias
}
foreach {propName propValue} [hfindprop $hpath @*] {
set attName [string range $propName 1 end]
nxscript putattribute $nxalias $attName $propValue
}
}
} message ] {
return -code error "::nexus::sicsvariable::save, $message"
}
}
proc ::nexus::sicsvariable::sdsinfo {sdsName data_type sobj args} {
if [ catch {
array set param $args
set dtype [::nexus::hdb2nx_type $data_type]
array set attribute [::utility::normalattlist $sobj]
if {[info exists attribute(units)]} {
set units_att " -attr {units,$attribute(units)} "
} else {
set units_att " "
}
if {$param(mutable) == true} {
set sdsStr "$sdsName -type $dtype -rank 1 -dim {-1} $units_att"
} else {
set sdsStr "$sdsName -type $dtype $units_att"
}
} message ] {
return -code error "([info level 0]) $message"
}
return $sdsStr
}
namespace eval ::nexus::scobj {}
proc ::nexus::scobj::sdsinfo {sdsName data_type args} {
if [ catch {
array set param $args
set dtype [::nexus::hdb2nx_type $data_type]
switch $data_type {
text {
set dimdef [subst {-dim {[string length [hval $param(hpath)]]}}]
set sdsStr "$sdsName -type $dtype $dimdef"
}
default {
if {$param(mutable) == true} {
set sdsStr "$sdsName -type $dtype -rank 1 -dim {-1}"
} else {
set sdsStr "$sdsName -type $dtype"
}
}
}
} message ] {
return -code error "([info level 0]) $message"
}
return $sdsStr
}
namespace eval ::nexus::chopperadapter { }
proc ::nexus::chopperadapter::save {sobj nxalias hpath data_type filestatus args} {
if [ catch {
array set attribute [attlist $sobj]
set val [SplitReply [$sobj]]
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::chopperadapter::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::chopperadapter::save, unknown type $data_type"}
}
}
if {$filestatus == "newfile"} {
if [hpropexists $hpath link] {
set link [hgetpropval $hpath link]
nxscript makelink $link $nxalias
}
foreach {propName propValue} [hfindprop $hpath @*] {
set attName [string range $propName 1 end]
nxscript putattribute $nxalias $attName $propValue
}
}
} message ] {
return -code error "([info level 0]) $message"
}
}
proc ::nexus::chopperadapter::sdsinfo {sdsName data_type sobj args} {
if [ catch {
array set param $args
set dtype [::nexus::hdb2nx_type $data_type]
array set attribute [::utility::normalattlist $sobj]
if {[info exists attribute(units)]} {
set units_att " -attr {units,$attribute(units)} "
} else {
set units_att " "
}
if {$param(mutable) == true} {
set sdsStr "$sdsName -type $dtype -rank 1 -dim {-1} $units_att"
} else {
set sdsStr "$sdsName -type $dtype $units_att"
}
} message ] {
return -code error "([info level 0]) $message"
}
return $sdsStr
}
proc ::nexus::singlecounter::save {counter nxalias hpath data_type filestatus args} {
todo_msg "Save counter: $counter"
}
proc ::nexus::singlecounter::sdsinfo {sdsName data_type counter args} {
todo_msg "Get sdsinfo for counter: $counter"
}
##
# @brief Save command for hdb nodes associated with a tcl macro
#
# The macro must return the name of a 1D associative array when called with -get_data_ref.
proc ::nexus::script::save {script nxalias hpath data_type filestatus 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 -get_data_ref]
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 {$filestatus == "newfile"} {
if [hpropexists $hpath link] {
set link [hgetpropval $hpath link]
nxscript makelink $link $nxalias
}
foreach {propName propValue} [hfindprop $hpath @*] {
set attName [string range $propName 1 end]
nxscript putattribute $nxalias $attName $propValue
}
}
} message ] {
return -code error "([info level 0]) $message"
}
}
proc ::nexus::script::sdsinfo {sdsName data_type sobj args} {
if [ catch {
array set param $args
set dtype [::nexus::hdb2nx_type $data_type]
array set attribute [::utility::normalattlist $sobj]
if {[info exists attribute(units)]} {
set units_att " -attr {units,$attribute(units)} "
} else {
set units_att " "
}
if {[getatt $sobj klass] == "sensor"} {
if {$param(mutable) == true} {
set sdsStr "$sdsName -type $dtype -rank 1 -dim {-1} $units_att"
} else {
set sdsStr "$sdsName -type $dtype $units_att"
}
} else {
set darray [$sobj -get_data_ref]
set size [SplitReply [$darray used]]
if {$param(mutable) == true} {
set sdsStr "$sdsName -type $dtype $units_att -rank 2 -dim {-1,$size}"
} else {
set sdsStr "$sdsName -type $dtype $units_att -rank 1 -dim {$size}"
}
}
} message ] {
#TODO CHECK ERROR: failed to open alias ::histogram_memory::y_bin etc.
return -code error "([info level 0]) $message"
}
return $sdsStr
}
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
# 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.49 $}]
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
publish ::nexus::data user