// HMI Tree computed from VARIABLES.CSV in svghmi.py
const "hmitree", "ns:GetHMITree()";
noindex > HMI_CURRENT_PAGE
const "categories", "exsl:node-set($_categories)";
const "_indexed_hmitree" apply "$hmitree", mode="index";
const "indexed_hmitree", "exsl:node-set($_indexed_hmitree)";
emit "preamble:hmi-tree" {
| var hmi_hash = [«$hmitree/@hash»];
| var heartbeat_index = «$indexed_hmitree/*[@hmipath = '/HEARTBEAT']/@index»;
foreach "$indexed_hmitree/*"
| /* «@index» «@hmipath» */ "«substring(local-name(), 5)»"`if "position()!=last()" > ,`
template "*", mode="index" {
param "parentpath", "''";
when "count(ancestor::*)=0" > /
when "count(ancestor::*)=1" > /«@name»
otherwise > «$parentpath»/«@name»
when "not(local-name() = $categories/noindex)" {
attrib "index" > «$index»
attrib "hmipath" > «$path»
apply "*[1]", mode="index"{
with "index", "$index + 1";
with "parentpath" > «$path»
apply "*[1]", mode="index"{
with "parentpath" > «$path»
apply "following-sibling::*[1]", mode="index" {
with "index", "$index + count(exsl:node-set($content)/*)";
with "parentpath" > «$parentpath»
// "HMI:WidgetType:param1:param2@path1,path1min,path1max@path2"
// widget type="WidgetType" id="blah456" {
// path value=".path1" index=".path1" min="path1min" max="path1max" type="PAGE_LOCAL";
// path value="/path1" index="348" type="HMI_INT";
// path value="path4" index="path4" type="HMI_LOCAL";
template "*", mode="parselabel" {
const "label","@inkscape:label";
const "description", "substring-after($label,'HMI:')";
const "_args", "substring-before($description,'@')";
when "$_args" value "$_args";
otherwise value "$description";
const "_type", "substring-before($args,':')";
when "$_type" value "$_type";
foreach "str:split(substring-after($args, ':'), ':')" {
const "paths", "substring-after($description,'@')";
foreach "str:split($paths, '@')" {
if "string-length(.) > 0" path {
const "pathminmax", "str:split(.,',')";
const "path", "$pathminmax[1]";
const "pathminmaxcount", "count($pathminmax)";
when "$pathminmaxcount = 3" {
attrib "min" > «$pathminmax[2]»
attrib "max" > «$pathminmax[3]»
when "$pathminmaxcount = 2" {
error > Widget id:«$id» label:«$label» has wrong syntax of path section «$pathminmax»
when "regexp:test($path,'^\.[a-zA-Z0-9_]+$')" {
attrib "type" > PAGE_LOCAL
when "regexp:test($path,'^[a-zA-Z0-9_]+$')" {
attrib "type" > HMI_LOCAL
const "item", "$indexed_hmitree/*[@hmipath = $path]";
const "pathtype", "local-name($item)";
if "$pathminmaxcount = 3 and not($pathtype = 'HMI_INT' or $pathtype = 'HMI_REAL')" {
error > Widget id:«$id» label:«$label» path section «$pathminmax» use min and max on non mumeric value
attrib "index" > «$item/@index»
attrib "type" > «$pathtype»
const "_parsed_widgets" {
widget type="VarInitPersistent" {
apply "$hmi_elements", mode="parselabel";
const "parsed_widgets","exsl:node-set($_parsed_widgets)";
result "$parsed_widgets/widget[@id = $id]";
def "func:is_descendant_path" {
// TODO : use HMI tree to answer more accurately
result "string-length($ancest) > 0 and starts-with($descend,$ancest)";
def "func:same_class_paths" {
const "class_a", "$indexed_hmitree/*[@hmipath = $a]/@class";
const "class_b", "$indexed_hmitree/*[@hmipath = $b]/@class";
result "$class_a and $class_b and $class_a = $class_b";
template "*", mode="testtree"{
> «$indent» «local-name()»
foreach "@*" > «local-name()»="«.»"
apply "*", mode="testtree" {
with "indent" value "concat($indent,'>')"
apply "$hmitree", mode="testtree";
apply "$indexed_hmitree", mode="testtree";
apply "$parsed_widgets", mode="testtree";