## # @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 [sicstime] eend [sicstime] start_seconds [clock seconds] timestamp 0 ::nexus::nxreopenfile ::nexus::save_data $point ::nexus::makelinks ::nexus::set_plotdata_info ::nexus::nxclosefile } else { eend [sicstime] 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 , remove alias # data alias , set as an alias for 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 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 ] { 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" } } namespace eval ::nexus::chopperadapter { } proc ::nexus::chopperadapter::save {sobj nxalias data_type args} { 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 {[info exists attribute(units)]} { nxscript putattribute $nxalias units $attribute(units) } } proc ::nexus::chopperadapter::sdsinfo {sobj 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.42 $}] 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