Files
sics/site_ansto/instrument/config/hipadaba/hipadaba_configuration_common.tcl
Ferdi Franceschini 3a3b6764ee SICS-377 hipadaba_configuration_common.tcl
One typo and forgetting to clear a temporary variable meant that the list of candidates for removal never shrank while pruning

reflectometer/config/motors/sct_batmotor.tcl
The beam attenuator is on axis D not A.  Also provide statemon feedback, "busy" or "idle"

sans/config/velsel/velsel.tcl
Update velocity selector parameters for NVS 43

SICS-374 sans/config/velsel/sct_velsel.tcl
Set velocity selector identifier

script_context_util.tcl
Don't override the "klass' attribute, just make sure that it matches the hdb klass property if it's defined.

event.h and statemon.c
Added statemon BUSY and IDLE events for the scbat because it when it oscillates it's busy doing something.

r2875 | ffr | 2010-01-22 20:41:36 +1100 (Fri, 22 Jan 2010) | 18 lines
2012-11-15 16:59:48 +11:00

961 lines
32 KiB
Tcl

##
# @file hipadaba_configuration_common.tcl
# @brief Commands for building the hdb tree from sics object properties and an instrument dictionary.
#
# hobj policy: data=false control=parent nxsave=false klass=parent
# subtree/group_path policy: data=parent control=parent nxsave=parent klass=parent
# type=macro & klass=command -> kind=command
# type=macro & klass!=command -> kind=script
# kind=script must have access property (read_only, read_write) and dtype and dlen
source $cfPath(hipadaba)/instdict_specification.tcl
source $cfPath(hipadaba)/common_instrument_dictionary.tcl
InstallHdb
namespace eval ::hdb {
namespace export buildHDB attlist
set sobjadd_state(depends) false
set NXlog_template {
NXlog {
$name {
$paramarr(time)
$paramarr(value)
$paramarr(raw_value)
$paramarr(description)
$paramarr(average_value)
$paramarr(average_value_error)
$paramarr(minimum_value)
$paramarr(maximum_value)
$paramarr(duration)
}
}
}
set NXnote_template {
NXnote {
$name {
$paramarr(author)
$paramarr(date)
$paramarr(type)
$paramarr(file_name)
$paramarr(description)
$paramarr(data)
}
}
}
set NXbeam_template {
$name {
$paramarr(distance)
$paramarr(incident_energy)
$paramarr(final_energy)
$paramarr(energy_transfer)
$paramarr(incident_wavelength)
$paramarr(incident_wavelength_spread)
$paramarr(incident_beam_divergence)
$paramarr(final_wavelength)
$paramarr(incident_polarization)
$paramarr(final_polarization)
$paramarr(final_wavelength_spread)
$paramarr(final_beam_divergence)
$paramarr(flux)
}
}
# NOTE: paramarr(offset) was added for Quokka's DetPosYOffsetmm parameter
set NXgeometry_template {
NXgeometry {
geometry {
sobjlist {$paramarr(geomdescription)}
NXshape {
shape {
sobjlist {$paramarr(shape) $paramarr(size)}
}
}
NXtranslation {
position {
sobjlist {$paramarr(position) $paramarr(offset) $paramarr(coordinate_scheme)}
NXgeometry {
geometry {
link {
target {$paramarr(refpos)}
nxalias {$paramarr(position)}
}
}
}
}
}
NXorientation {
orientation {
sobjlist {$paramarr(orientation)}
}
}
}
}
}
set NXaperture_template [subst -novariables {
NXaperture {
$name {
sobjlist {$paramarr(material) $paramarr(description)}
[ set NXgeometry_template ]
}
}
} ]
set NXvelocity_selector_template [subst -novariables {
NXvelocity_selector {
$name {
sobjlist {
$paramarr(type)
$paramarr(rotation_speed)
$paramarr(radius)
$paramarr(spwidth)
$paramarr(length)
$paramarr(num)
$paramarr(twist)
$paramarr(table)
$paramarr(height)
$paramarr(width)
$paramarr(wavelength)
$paramarr(wavelength_spread)
}
[ set NXgeometry_template ]
}
}
} ]
}
proc ::hdb::MakeLog {name klass paramlist} {
variable NXlog_template
array set paramarr $paramlist
set newtable [list]
prune_NX newtable $NXlog_template
::hdb::subtree_macro $name $klass $newtable
}
proc ::hdb::MakeNote {name klass paramlist} {
variable NXnote_template
array set paramarr $paramlist
set newtable [list]
prune_NX newtable $NXnote_template
::hdb::subtree_macro $name $klass $newtable
}
proc ::hdb::MakeBeam {name klass paramlist} {
variable NXbeam_template
array set paramarr $paramlist
set newtable [list]
prune_NX newtable $NXbeam_template
::hdb::subtree_macro $name $klass $newtable
}
proc ::hdb:MakeEnvironment {name klass paramlist} {
variable NXenvironment_template
array set paramarr $paramlist
set newtable [list]
prune_NX newtable $NXenvironment_template
::hdb::subtree_macro $name $klass $newtable
}
proc ::hdb::MakeGeometry {name klass paramlist} {
variable NXgeometry_template
array set paramarr $paramlist
set newtable [list]
prune_NX newtable $NXgeometry_template
::hdb::subtree_macro $name $klass $newtable
}
##
# @brief Generates an hdb subtree macro from a named list of SICS objects.
#
# NOTE: Currently the only SICS objects supported are 'sicsvariable' and 'macro'.
# @param name, This is the name of the aperture.
# @paramlist, A name value list of aperture parameters. All parameters are optional.
proc ::hdb::MakeAperture {name paramlist} {
array set paramarr $paramlist
variable NXaperture_template
set newtable [list]
prune_NX newtable $NXaperture_template
::hdb::subtree_macro $name instrument $newtable
}
proc ::hdb::MakeVelocity_Selector {name paramlist} {
variable NXvelocity_selector_template
array set paramarr $paramlist
set newtable [list]
prune_NX newtable $NXvelocity_selector_template
::hdb::subtree_macro $name instrument $newtable
}
##
# @brief This simplifies a NeXus-class template by removing unnecessary branches.
# A NeXus-class template is a keyed-list which has Tcl variables for some of the nodes,
# if the Tcl variables aren't defined for some branch then that branch is removed.
# All other variables are expanded in place, also all 'sobjlists' are split up into type
# specific lists. This is intended as a helper function for commands which generate
# NeXus-class keyed lists from a simple set of optional parameters.
#
# @param NXklist, This is a keyed list representation of the NeXus class which will be augmented
# with the pruned nx_template. Note this can just be an empty list.
# @param nx_template, The NeXus-class template which will be pruned.
# @param path, (optional, default="") Parent path in recursive calls.
# @param node, (optional, default="") Current node in recursive calls.
# @param level, (optional,default=1) The location of the template parameters in the callstack.
proc prune_NX {NXklist nx_template {path ""} {node ""} {level 1}} {
upvar $NXklist newtable
if {$path == ""} {
set currpath $node
} else {
set currpath $path/$node
}
foreach {n v} $nx_template {
switch $n {
"sobjlist" {
set has_sobj 0
foreach var $v {
if {[string index $var 0] == "$"} {
set vn [string range $var 1 end]
upvar $level $vn lvar
if [info exists lvar] {
foreach sobj $lvar {
lappend [getatt $sobj type]_list $sobj
}
set has_sobj 1
}
} else {
foreach sobj $var {
lappend [getatt $sobj type]_list $sobj
}
set has_sobj 1
}
}
if {$has_sobj} {
if [info exists sicsvariable_list] {
::utility::tabset newtable $currpath/sicsvariable [subst {{$sicsvariable_list}}]
}
if [info exists macro_list] {
::utility::tabset newtable $currpath/macro [subst {{$macro_list}}]
}
} else {
}
}
"link" {
set linktarget ""
array set linkinfo $v
if {[string index $linkinfo(target) 0] == "$"} {
set vn [string range $linkinfo(target) 1 end]
upvar $level $vn lvar
if [info exists lvar] {
set linktarget $lvar
}
} else {
set linktarget $linkinfo(target)
}
if {[string index $linkinfo(nxalias) 0] == "$"} {
set vn [string range $linkinfo(nxalias) 1 end]
upvar $level $vn avar
if [info exists avar] {
set linkname $avar
}
} else {
set linkname $linkinfo(nxalias)
}
if {$linktarget != ""} {
::utility::tabset newtable $currpath/link/target [subst {{$linktarget}}]
::utility::tabset newtable $currpath/link/nxalias [subst {{$linkname}}]
}
}
default {
if {[string range $n 0 1] == "NX"} {
set node $n
} elseif {[string index $n 0] == "$"} {
set vn [string range $n 1 end]
upvar $level $vn lvar
if [info exists lvar] {
set node $lvar
} else {
}
} else {
set node $n
}
prune_NX newtable $v $currpath $node [expr $level+1]
}
}
}
}
##
# @brief Make an aperture
#
# @param args optional name and description variables
#proc MakeAperture {apname nxgeometry args} {
# set nxaperture [::hdb::NXaperture $apname $nxgeometry $args]
# ::hdb::subtree_macro $apname instrument $nxaperture
#}
##
# @brief Generate a subtree macro procedure
#
# @param Name of the subtree macro
# @klass Category which the macro belongs to (usually a NeXus class)
# @klist A keyed list which describes the subtree.
proc ::hdb::subtree_macro {name klass klist} {
set st_macroname ${name}_subtree_macro
proc ::hdb::$st_macroname {} "return [list $klist]"
::hdb::set_subtree_props ::hdb::$st_macroname $klass
}
##
# @brief Publish an hdb_subtree macro and initialise it's property list
#
# @param st_name The name of the hdb_subtree macro
# @param klass Where should the subtree be placed in the hdb heirarchy
# @param control (optional, default=true) Add it to the control interface?
# @param privilege (optional, default=user) Modification privilege.
proc ::hdb::set_subtree_props {st_name klass {control "true"} {privilege "user"} } {
publish $st_name mugger
sicslist setatt $st_name klass $klass
sicslist setatt $st_name control $control
sicslist setatt $st_name privilege $privilege
sicslist setatt $st_name kind "hdb_subtree"
sicslist setatt $st_name long_name "@none"
sicslist setatt $st_name data "true"
sicslist setatt $st_name nxsave "true"
}
# @brief Add a subtree to a given hipadaba path.
#
# @param hpath, Basepath for subtree
# @param object, SICS object name
# @param subtree, A nested Tcl list which represents the subtree
# @param type, the SICS object type if we are adding SICS object node. Optional, default = @none.
# @param makenode, type of node to make. Optional, default = @none.
proc ::hdb::add_subtree {hpath subtree {object @none} {type @none} {makenode @none}} {
set ::errorInfo ""
set SICStypes {sicsvariable macro}
if [catch {
switch $makenode {
"@none" {
foreach {n v} $subtree {
if {[lsearch -exact $::nexus_classes $n] >= 0} {
add_subtree $hpath $v $object $n NXclass
} elseif {[lsearch -exact $SICStypes $n] >= 0} {
add_subtree $hpath $v $object $n sicsobject
} elseif {$n=="link"} {
add_subtree $hpath $v $object $n link
} else {
error "ERROR:Unknown type, '$n'"
}
}
}
"NXclass" {
foreach {item val} $subtree {
add_hpath $hpath $item
hsetprop $hpath/$item klass $type
add_subtree $hpath/$item $val $object
}
}
"sicsobject" {
foreach item $subtree {
if {$item==$object} {
error "ERROR: Infinite recursion, cannot add $item as a node to it's own hdb subtree"
}
set objtype [getatt $item type]
if {$type != $objtype} {
error "ERROR: Specified type of '$type' doesn't match actual type, '$objtype', for $item"
}
sobjadd $hpath $item
}
}
"link" {
set target [::utility::tabget subtree target]
set nxalias [::utility::tabget subtree nxalias]
foreach l $nxalias t $target {
set refname [normalgetatt $t long_name]
::hdb::add_hpath $hpath $refname
hsetprop $hpath/$refname data "true"
hsetprop $hpath/$refname nxsave "false"
hsetprop $hpath/$refname control "false"
hsetprop $hpath/$refname link $t
hsetprop $hpath/$refname nxalias ${l}_link
hsetprop $hpath/$refname type nxvgroup
hsetprop $hpath/$refname klass @none
}
}
default {
error "ERROR: Unknown node type, $makenode"
}
}
} message ] {
if {$::errorCode=="NONE"} {return $hpath}
return -code error $message
}
}
##
# @brief Add an hdb path to the hdb tree at the given basePath
#
# @param basePath This is the parent for the new hdb path
# @param path Add this path to the basePath
# @param priv This is the access privilege for the new hdb path
# @param dtype The new hdb node will have this type
# @param dlen Array length if the hdb node represents array data
proc ::hdb::add_hpath {basePath path {priv spy} {dtype none} {dlen ""}} {
set catch_status [ catch {
set parent $basePath
array set prop_arr [hlistprop $basePath tcllist]
foreach child [split $path /] {
if {[lsearch [hlist $parent] $child] == -1} {
hmake $parent/$child $priv $dtype $dlen
if {$basePath != "/"} {
hsetprop $parent/$child data $prop_arr(data)
hsetprop $parent/$child control $prop_arr(control)
hsetprop $parent/$child nxsave $prop_arr(nxsave)
hsetprop $parent/$child klass @none
hsetprop $parent/$child type $prop_arr(type)
}
}
if {$parent == "/"} {
set parent /$child
} else {
set parent $parent/$child
}
}
return $parent
} message ]
handle_exception $catch_status $message
}
##
# @brief Attach a command parameter node to the given hdb "command path"
#
# @param hpath hdb "command path", this will be the parent for the parameter node
# @param sobj name of SICS variable which corresponds to a command parameter
# @param name The hdb node name that the variable will be added under
# @see command
proc ::hdb::add_cmd_par {hpath sobj name} {
hattach $hpath $sobj $name
foreach {prop pval} [::utility::normalattlist $sobj] {
hsetprop $hpath/$name $prop $pval
}
hsetprop $hpath/$name data false
}
##
# @brief Attach a feedback node to the given hdb "command path"
#
# @param hpath hdb "command path", this will be the parent for the feedback node
# @param sobj name of the SICS variable which corresponds to a command feedback value
# @param name The hdb node name that the variable will be added under
# @see command
proc ::hdb::add_feedback {hpath sobj name} {
hattach $hpath $sobj $name
foreach {prop pval} [attlist $sobj] {
hsetprop $hpath/$name $prop $pval
}
hsetprop $hpath/$name privilege READ_ONLY
}
##
# @brief Adds a hdb path to the given base path with the given properties
#
# @param args a list of name value pairs which provide the node properties\n
#\targs can just be "path a/b/c prop_list {kind xxx long_name junk ..}"\n
#\tor "node nnn kind kkk long_name junk prop_list {propA aaa propB bbb ...}"
proc ::hdb::add_node {basePath args} {
global nodeindex
variable sobjadd_state
array unset arg_array
set catch_status [ catch {
array set arg_array $args
if {[info exists arg_array(path)] && [info exists arg_array(prop_list)]} {
add_hpath $basePath $arg_array(path)
if {$basePath == "/"} {
set node_path /$arg_array(path)
} else {
set node_path $basePath/$arg_array(path)
}
# if {[info exists arg_array(prop_list)]} {
foreach {prop pval} $arg_array(prop_list) {
hsetprop $node_path $prop $pval
}
# }
return $node_path
}
if {![info exists arg_array(dlen)]} {
set arg_array(dlen) ""
}
set gp_path [file dirname $arg_array(node)]
set node_name [file tail $arg_array(node)]
if {$gp_path != "."} {
add_hpath $basePath $gp_path
set basePath $basePath/$gp_path
hsetprop $basePath type part
}
if {[lsearch [hlist $basePath] $node_name] == -1} {
#TODO allow hdb nodes of type drivable countable environment
array set attribute [::utility::normalattlist $node_name]
switch $arg_array(kind) {
command {
# A command is a macro, node=macro name
set command $node_name
set cmd_path [add_command $basePath $command]
set node_path $cmd_path
# The extra arguments for add_node are supplied by the command parameters
# and command feedback procedures.
if {[string length [info procs ${command}_parameters]] > 0} {
${command}_parameters add_node $cmd_path
} else {
$command -map param ::hdb::add_cmd_par $cmd_path
}
if {[string length [info procs ${command}_feedback]] > 0} {
add_hpath $cmd_path feedback
hsetprop $cmd_path/feedback type part
${command}_feedback add_node $cmd_path/feedback
} else {
add_hpath $cmd_path feedback
hsetprop $cmd_path/feedback type part
$command -map feedback ::hdb::add_feedback $cmd_path/feedback
}
}
hobj {
set hobj_long_name [normalgetatt $node_name long_name]
hattach $basePath $node_name $hobj_long_name
set node_path $basePath/$hobj_long_name
hsetprop $node_path data [getatt $node_name data]
hsetprop $node_path control [getatt $node_name control]
hsetprop $node_path nxsave [getatt $node_name nxsave]
hsetprop $node_path mutable [getatt $node_name mutable]
hsetprop $node_path klass [getatt $node_name klass]
if [info exists attribute(hdbchain)] {
foreach pmot [split $attribute(hdbchain) ,] {
hchain $node_path [lindex [split [getatt $pmot hdb_path] ,] 0]
}
}
foreach child [hlist $node_path] {
hsetprop $node_path/$child data false
hsetprop $node_path/$child control [getatt $node_name control]
hsetprop $node_path/$child nxsave false
hsetprop $node_path/$child klass [getatt $node_name klass]
}
}
script - getset {
# A r/w pair of scripts, node = a node path
set node_path $basePath/[normalgetatt $node_name long_name]
set data_type [getatt $node_name dtype]
set data_length [getatt $node_name dlen]
if {[getatt $node_name access] == "read_only"} {
hmakescript $node_path $node_name hdbReadOnly $data_type $data_length
} else {
hmakescript $node_path $node_name $node_name $data_type $data_length
}
hsetprop $node_path sicsdev $node_name
hsetprop $node_path nxalias $node_name
hsetprop $node_path data [getatt $node_name data]
hsetprop $node_path control [getatt $node_name control]
hsetprop $node_path klass [getatt $node_name klass]
hsetprop $node_path sdsinfo [getatt $node_name sdsinfo]
hsetprop $node_path savecmd [getatt $node_name savecmd]
#hmakescript $node_path $arg_array(rscript) $arg_array(wscript) $arg_array(dtype) $arg_array(dlen)
}
}
if {[info exists attribute(units)]} {
hsetprop $node_path units [normalgetatt $node_name units]
}
if {[info exists arg_array(prop_list)]} {
foreach {prop pval} $arg_array(prop_list) {
hsetprop $node_path $prop $pval
}
}
if {[info exists attribute(depends)]} {
foreach dep [split [normalgetatt $node_name depends] , ] {
set sobjadd_state(depends) true
::hdb::sobjadd $node_path $dep
set sobjadd_state(depends) false
}
}
if { $sobjadd_state(depends) == false && [info exists attribute(hdb_path)] } {
sicslist setatt $node_name hdb_path $attribute(hdb_path),$node_path
} else {
sicslist setatt $node_name hdb_path $node_path
}
return $node_path
}
} message ]
handle_exception $catch_status $message
}
##
# @brief Add a "command" node to basePath for the gumtree control interface.
# @param basePath parent for new command node
# @param command name of the command procedure
proc ::hdb::add_command {basePath command} {
array unset cmd_atts
array set cmd_atts [attlist $command]
if 0 {
if {[info exists cmd_atts(group)]} {
add_hpath $basePath $cmd_atts(group)
set basePath $basePath/$cmd_atts(group)
hsetprop $basePath type part
}
}
set hcom_long_name [normalgetatt $command long_name]
hcommand $basePath/$hcom_long_name $command
set cmd_path $basePath/$hcom_long_name
hsetprop $cmd_path privilege $cmd_atts(privilege)
hsetprop $cmd_path type $cmd_atts(kind)
hsetprop $cmd_path data $cmd_atts(data)
hsetprop $cmd_path control $cmd_atts(control)
hsetprop $cmd_path klass $cmd_atts(klass)
hsetprop $cmd_path nxsave $cmd_atts(nxsave)
hsetprop $cmd_path sicsdev $cmd_atts(id)
return $cmd_path
}
##
# @brief Retrieve the list of attributes for the given sics object
#
# @param sicsobj SICS object name
# @return a list of name value pairs for the sicsobj attributes
proc ::hdb::attlist {sicsobj} {
if [ catch {
foreach att [tolower_sicslist $sicsobj] {
lappend atts [split [string range $att 0 end-1] =]
}
return [join $atts]
} message ] {
if {$::errorCode=="NONE"} {return $message}
return -code error $message
}
}
##
# @brief List sics objects with the given sics type, klass and group
#
# @param atype one of the types given in instdict_specification.tcl
# @param aklass one of the klasses given in instdict_specification.tcl
# @return A Tcl list of SICS objects.
proc ::hdb::sobjlist {atype aklass} {
global sobj_sicstype_list
switch $atype {
command,macro {
return [intersection [tolower_sicslist type macro] [tolower_sicslist klass $aklass] [tolower_sicslist kind command]]
}
script,macro {
return [intersection [tolower_sicslist type macro] [tolower_sicslist klass $aklass] [tolower_sicslist kind script]]
}
"@any" {
foreach st $sobj_sicstype_list {
lappend sobjects [tolower_sicslist type $st]
}
return [intersection [join $sobjects] [tolower_sicslist klass $aklass]]
}
default {
return [intersection [tolower_sicslist type $atype] [tolower_sicslist klass $aklass]]
}
}
}
##
# @brief Get the named attribute from the given SICS object
#
# @param sicsobj a SICS object name
# @param attribute name of the attribute to be fetched
# @return the plain attribute value
proc ::hdb::getsobjatt {sicsobj attribute} {
string trim [lindex [join [split [tolower_sicslist $sicsobj $attribute] =]] 1]
}
##
# @brief Add the given sics object of sobjtype to the given hipadaba path (hpath must exist)
#
# @param hpath parent for new hdb node
# @param sobj SICS object name
# @param args Hmmm this doesn't get used, do we need it?
proc ::hdb::sobjadd {hpath sobj args} {
# TODO Check if args parameter needs to be here, it might be there in case the function is called
# with more than two arguments.
array unset sobjatt
set catch_status [ catch {
array set sobjatt [attlist $sobj]
if {[sicslist exists $sobj id] == false} {
sicslist setatt $sobj id $sobj
}
switch $sobjatt(type) {
motor - configurablevirtualmotor {
if {[info exists sobjatt(group)]} {
set hpath [add_hpath $hpath $sobjatt(group)]
if {[catch {hsetprop $hpath type part} err]} {clientput $err error}
}
if {[lsearch [hlist $hpath] $sobjatt(long_name)] == -1} {
set node_path [add_node $hpath node $sobj long_name [normalgetatt $sobj long_name] kind $sobjatt(kind)]
if {[catch {hsetprop $node_path savecmd $sobjatt(savecmd)} err]} {clientput $err error}
if {[catch {hsetprop $node_path sdsinfo $sobjatt(sdsinfo)} err]} {clientput $err error}
if {[catch {hsetprop $node_path nxalias $sobjatt(nxalias)} err]} {clientput $err error}
if {[catch {hsetprop $node_path mutable $sobjatt(mutable)} err]} {clientput $err error}
} else {
clientput "ERROR: $hpath/$sobjatt(long_name) for $sobj exists" error
}
}
macro {
# access attribute = ro,rw
if {[info exists sobjatt(group)]} {
set hpath [add_hpath $hpath $sobjatt(group)]
if {[catch {hsetprop $hpath type part} err]} {clientput $err error}
}
if {[lsearch [hlist $hpath] $sobjatt(long_name)] >= 0} {
clientput "ERROR: $hpath/$sobjatt(long_name) for $sobj exists" error
} elseif {$sobjatt(kind) == "hdb_subtree"} {
add_subtree $hpath [$sobj]
} else {
set node_path [add_node $hpath kind $sobjatt(kind) node $sobj priv $sobjatt(privilege) ]
if [info exists sobjatt(mutable)] {
if {[catch {hsetprop $node_path mutable $sobjatt(mutable)} err]} {clientput $err error}
}
}
}
sicsvariable {
if {[info exists sobjatt(group)]} {
set hpath [add_hpath $hpath $sobjatt(group)]
if {[catch {hsetprop $hpath type part} err]} {clientput $err error}
}
if {[lsearch [hlist $hpath] $sobjatt(long_name)] == -1} {
set node_path [add_node $hpath node $sobj long_name [normalgetatt $sobj long_name] kind $sobjatt(kind)]
if {[catch {hsetprop $node_path sicsdev $sobj} err]} {clientput $err error}
if {[catch {hsetprop $node_path nxalias $sobj} err]} {clientput $err error}
if {[catch {hsetprop $node_path savecmd $sobjatt(savecmd)} err]} {clientput $err error}
if {[catch {hsetprop $node_path sdsinfo $sobjatt(sdsinfo)} err]} {clientput $err error}
if {[catch {hsetprop $node_path mutable $sobjatt(mutable)} err]} {clientput $err error}
if {[catch {hsetprop $node_path privilege $sobjatt(privilege)} err]} {clientput $err error}
} else {
clientput "ERROR: $hpath/$sobjatt(long_name) for $sobj exists" error
}
}
node {
}
singlecounter {
# TODO
todo_msg "$sobjatt(type) case, add $sobj to $hpath"
}
histmem {
if {[info exists sobjatt(group)]} {
set hpath [add_hpath $hpath $sobjatt(group)]
if {[catch {hsetprop $hpath type part} err]} {clientput $err error}
}
if {[lsearch [hlist $hpath] $sobjatt(long_name)] == -1} {
set node_path [add_node $hpath node $sobj long_name [normalgetatt $sobj long_name] kind $sobjatt(kind)]
if {[catch {hsetprop $node_path savecmd $sobjatt(savecmd)} err]} {clientput $err error}
if {[catch {hsetprop $node_path sdsinfo $sobjatt(sdsinfo)} err]} {clientput $err error}
if {[catch {hsetprop $node_path nxalias $sobjatt(nxalias)} err]} {clientput $err error}
if {[catch {hsetprop $node_path mutable $sobjatt(mutable)} err]} {clientput $err error}
if {[catch {hsetprop $node_path sicsdev $sobj} err]} {clientput $err error}
} else {
clientput "ERROR: $hpath/$sobjatt(long_name) for $sobj exists" error
}
}
chopperadapter {
if {[info exists sobjatt(group)]} {
set hpath [add_hpath $hpath $sobjatt(group)]
hsetprop $hpath type part
}
if {[lsearch [hlist $hpath] $sobjatt(long_name)] == -1} {
set node_path [add_node $hpath node $sobj long_name [normalgetatt $sobj long_name] kind $sobjatt(kind)]
hsetprop $node_path sicsdev $sobj
hsetprop $node_path nxalias $sobj
hsetprop $node_path savecmd $sobjatt(savecmd)
hsetprop $node_path sdsinfo $sobjatt(sdsinfo)
hsetprop $node_path mutable $sobjatt(mutable)
hsetprop $node_path privilege $sobjatt(privilege)
} else {
clientput "ERROR: $hpath/$sobjatt(long_name) for $sobj exists" error
}
}
nxscript {
# TODO
todo_msg "$sobjatt(type) case, add $sobj to $hpath"
}
sicsdata {
# TODO
todo_msg "$sobjatt(type) case, add $sobj to $hpath"
}
scanobject {
# TODO
todo_msg "$sobjatt(type) case, add $sobj to $hpath"
}
# TODO Can this be replaced with a sct_* glob?
sct_object {
set node_name [normalgetatt $sobj long_name]
set node_path $hpath/$node_name
hfactory $node_path link $sobj
hsetprop $node_path type $sobjatt(type)
sicslist setatt $sobj hdb_path $node_path
}
environment_controller {
todo_msg "$sobjatt(type) case, add $sobj to $hpath"
}
default {
error "ERROR: Unknown sics object type $sobjatt(type)"
}
}
} message ]
handle_exception $catch_status $message
}
proc ::hdb::write_poll {pollnode val} {
hsetprop $pollnode poll_interval $val
sicspoll intervall $pollnode $val
}
proc ::hdb::read_poll {pollnode} {
return [getatt $pollnode]
}
##
# @brief Attaches the SICS objects of the given type and klass to the given hdb path
#
# The hipadaba node properties are constructed from the SICS object attribute list.
#
# @param hpath parent for the new SICS object nodes
# @param sobjtype One of the types in instdict_specification.tcl
# @param given_klass A klass in instdict_specification.tcl
# @see sobjadd
proc ::hdb::sobjtypeadd {hpath sobjtype given_klass} {
set catch_status [ catch {
foreach {sobj} [sobjlist $sobjtype $given_klass] {
array unset sobjatt
array set sobjatt [attlist $sobj]
if {[info exists sobjatt(privilege)] && $sobjatt(privilege) != "internal"} {
sobjadd $hpath $sobj
}
}
} message ]
handle_exception $catch_status $message
}
##
# @brief Remove empty instrument dictionary nodes
#
# @param instdict name of the instrument dictionary structure
#
# Nodes which have no child nodes and no associated sics objects for the
# current instrument are removed.
proc ::hdb::prune {instdict} {
upvar $instdict dict
array set dictarr $dict
foreach {n v} $dict {
lappend candidates $n
}
# While there are candidates for removal remove the childless ones
# which have an empty sobj list
while {[expr [llength $candidates]] > 0} {
set new_candidates ""
foreach name $candidates {
array unset dictval
array set dictval $dictarr($name)
if {[lsearch $dictval(property) nxvgroup] >= 0} {
# Link targets are not candidates for pruning
continue
}
set sobjects ""
if {[info exists dictval(sobj)]} {
foreach {t k} $dictval(sobj) {
lappend sobjects [sobjlist $t $k]
}
}
if { [llength [join $sobjects]] == 0 } {
if {[llength [array get dictarr $name/*]] == 0} {
array unset dictarr $name
set tempdict ""
foreach {n v} $dict {
if {$n != $name} {
lappend tempdict $n
lappend tempdict $v
}
}
set dict $tempdict
} else {
if {[lsearch $candidates $name/*] >= 0} {
lappend new_candidates $name
}
}
}
}
set candidates $new_candidates
}
return
}
##
# @brief Traverse the instrument dictionary and construct the hipadaba database.
#
# This command prunes the dictionary before building it.
#
# @param instDict name of the instrument dictionary structure
#
# @see prune
proc ::hdb::buildHDB {instDict} {
#TODO add data control nxsave nxtyp properties
upvar #0 $instDict dictionary
set catch_status [ catch {
prune dictionary
foreach {n v} $dictionary {
array unset varr
array set varr $v
array unset property_array
array set property_array $varr(property)
add_node / path $n prop_list $varr(property)
if {[info exists varr(sobj)]} {
foreach {sicstype sobj_klass} $varr(sobj) {
sobjtypeadd /$n $sicstype $sobj_klass
}
}
}
} message ]
handle_exception $catch_status $message
}
##
# @brief Set the save state for the given subtree
#
# @param hpath The save state of the nodes below this path will be set
# @param mode true or false
# @param top This is just here to make the recursion work from the top level, You don't need
# to set this
proc ::hdb::set_save {hpath mode {top true}} {
if [ catch {
if {$hpath != "/"} {
set hnode $hpath
if {[hpropexists $hnode data] && [hgetpropval $hnode data] != "false"} {
foreach hp [hlist $hnode] {
set_save $hnode/$hp $mode false
}
if {$top == "true"} {
hsetprop $hnode nxsave $mode
if {$mode == "true"} {
set hp ""
foreach ps [lrange [split [string trim $hnode /] /] 0 end-1] {
set hp $hp/$ps
hsetprop $hp nxsave true
}
}
} else {
hsetprop $hnode nxsave $mode
}
}
} else {
foreach hp [hlist /] {
::hdb::set_save /$hp $mode
}
}
} message ] {
return -code error "([info level 0]) $message"
}
}
namespace import ::hdb::*