SVGHMI: Quicker update path for input widget when pressing on buttons, do not wait until data comes back, and simply update value text of the pressed widget. Updated PLC prog for more amimated value to display
include yslt_noindent.yml2
// overrides yslt's output function to set CDATA
decl output(method, cdata-section-elements="xhtml:script");
istylesheet
/* From Inkscape */
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
/* Our namespace to invoke python code */
xmlns:ns="beremiz"
extension-element-prefixes="ns func"
exclude-result-prefixes="ns str regexp exsl func" {
/* This retrieves geometry obtained through "inkscape -S"
* already parsed by python and presented as a list of
* <bbox x="0" y="0" w="42" h="42">
*/
const "geometry", "ns:GetSVGGeometry()";
const "hmitree", "ns:GetHMITree()";
const "hmi_elements", "//svg:*[starts-with(@inkscape:label, 'HMI:')]";
const "hmi_geometry", "$geometry[@Id = $hmi_elements/@id]";
const "hmi_pages", "$hmi_elements[func:parselabel(@inkscape:label)/widget/@type = 'Page']";
const "default_page" choose {
when "count($hmi_pages) > 1" {
const "Home_page",
"$hmi_pages[func:parselabel(@inkscape:label)/widget/arg[1]/@value = 'Home']";
choose {
when "$Home_page" > Home
otherwise {
error "No Home page defined!";
}
}
}
when "count($hmi_pages) = 0" {
error "No page defined!";
}
otherwise > «func:parselabel($hmi_pages/@inkscape:label)/widget/arg[1]/@value»
}
const "_categories" {
noindex > HMI_ROOT
noindex > HMI_LABEL
noindex > HMI_CLASS
noindex > HMI_PLC_STATUS
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)";
template "*", mode="index" {
param "index", "0";
param "parentpath", "''";
const "content" {
const "path"
choose {
when "local-name() = 'HMI_ROOT'" > «$parentpath»
otherwise > «$parentpath»/«@name»
}
choose {
when "not(local-name() = $categories/noindex)" {
xsl:copy {
attrib "index" > «$index»
attrib "hmipath" > «$path»
foreach "@*" xsl:copy;
}
/* no node expected below value nodes */
}
otherwise {
apply "*[1]", mode="index"{
with "index", "$index";
with "parentpath" > «$path»
}
}
}
}
copy "$content";
apply "following-sibling::*[1]", mode="index" {
with "index", "$index + count(exsl:node-set($content)/*)";
with "parentpath" > «$parentpath»
}
}
/* Identity template :
* - copy every attributes
* - copy every sub-elements
*/
template "@* | node()", mode="identity_svg" {
/* use real xsl:copy instead copy-of alias from yslt.yml2 */
xsl:copy apply "@* | node()", mode="identity_svg";
}
/*const "mark" > =HMI=\n*/
/* copy root node and add geometry as comment for a test */
template "/" {
comment > Made with SVGHMI. https://beremiz.org
html xmlns="http://www.w3.org/1999/xhtml" {
head;
body style="margin:0;" {
xsl:copy {
comment {
apply "$hmi_geometry", mode="testgeo";
}
comment {
apply "$hmitree", mode="testtree";
}
comment {
apply "$indexed_hmitree", mode="testtree";
}
apply "@* | node()", mode="identity_svg";
}
script{
call "scripts";
}
}
}
}
func:function name="func:parselabel" {
param "label";
const "description", "substring-after($label,'HMI:')";
const "_args", "substring-before($description,'@')";
const "args" choose {
when "$_args" value "$_args";
otherwise value "$description";
}
const "_type", "substring-before($args,':')";
const "type" choose {
when "$_type" value "$_type";
otherwise value "$args";
}
const "ast" if "$type" widget {
attrib "type" > «$type»
foreach "str:split(substring-after($args, ':'), ':')" {
arg {
attrib "value" > «.»
}
}
const "paths", "substring-after($description,'@')";
foreach "str:split($paths, '@')" {
path {
attrib "value" > «.»
}
}
}
func:result select="exsl:node-set($ast)"
}
function "scripts"
{
| //(function(){
|
| var hmi_hash = [«$hmitree/@hash»];
/* TODO re-enable
||
function evaluate_js_from_descriptions() {
var Page;
var Input;
var Display;
var res = [];
||
const "midmark" > \n«$mark»
apply """//*[contains(child::svg:desc, $midmark) or \
starts-with(child::svg:desc, $mark)]""",2
mode="code_from_descs";
||
return res;
}
||
*/
| var hmi_widgets = {
foreach "$hmi_elements" {
const "widget", "func:parselabel(@inkscape:label)/widget";
| «@id»: {
| type: "«$widget/@type»",
| args: [
foreach "$widget/arg"
| "«@value»"`if "position()!=last()" > ,`
| ],
| indexes: [
foreach "$widget/path" {
const "hmipath","@value";
const "hmitree_match","$indexed_hmitree/*[@hmipath = $hmipath]";
if "count($hmitree_match) = 0"
error > No match for HMI «$hmipath»;
| «$hmitree_match/@index»`if "position()!=last()" > ,`
}
| ],
| element: document.getElementById("«@id»"),
apply "$widget", mode="widget_defs" with "hmi_element",".";
| }`if "position()!=last()" > ,`
}
| }
|
| var hmitree_types = [
foreach "$indexed_hmitree/*" {
| /* «@index» «@hmipath» */ "«substring(local-name(), 5)»"`if "position()!=last()" > ,`
}
| ]
|
| var page_desc = {
foreach "$hmi_pages" {
const "desc", "func:parselabel(@inkscape:label)/widget";
const "page", ".";
const "p", "$hmi_geometry[@Id = $page/@id]";
const "page_ids","""$hmi_geometry[@Id != $page/@id and
@x >= $p/@x and @y >= $p/@y and
@x+@w <= $p/@x+$p/@w and @y+@h <= $p/@y+$p/@h]/@Id""";
const "page_elements", "$hmi_elements[@id = $page_ids]";
| "«$desc/arg[1]/@value»": {
| id: "«@id»",
| widgets: [
foreach "$page_ids" {
| hmi_widgets.«.»`if "position()!=last()" > ,`
}
| ]
| }`if "position()!=last()" > ,`
}
| }
|
| var default_page = "«$default_page»";
include text svghmi.js
| //})();
}
/*
Parses:
"HMI:WidgetType:param1:param2@path1@path2"
Into:
widget type="WidgetType" {
arg value="param1";
arg value="param2";
path value="path1";
path value="path2";
}
*/
template "*", mode="page_desc" {
}
template "*", mode="code_from_descs" {
||
{
var path, role, name, priv;
var id = "«@id»";
||
/* if label is used, use it as default name */
if "@inkscape:label"
|> name = "«@inkscape:label»";
| /* -------------- */
// this breaks indent, but fixing indent could break string literals
value "substring-after(svg:desc, $mark)";
// nobody reads generated code anyhow...
||
/* -------------- */
res.push({
path:path,
role:role,
name:name,
priv:priv
})
}
||
}
template "bbox", mode="testgeo"{
| ID: «@Id» x: «@x» y: «@y» w: «@w» h: «@h»
}
template "*", mode="testtree"{
param "indent", "''";
> «$indent» «local-name()»
foreach "@*" > «local-name()»=«.»
> \n
apply "*", mode="testtree" {
with "indent" value "concat($indent,'>')"
};
}
template "widget[@type='Display']", mode="widget_defs" {
param "hmi_element";
| frequency: 5,
| dispatch: function(value) {
choose {
when "$hmi_element[self::svg:text]"{
// TODO : care about <tspan> ?
| this.element.textContent = String(value);
}
otherwise {
error > Display widget as a group not implemented
}
}
| },
}
template "widget[@type='Meter']", mode="widget_defs" {
| frequency: 10,
}
template "widget[@type='Input']", mode="widget_defs" {
param "hmi_element";
| frequency: 5,
const "value_elt_id","$hmi_element//*[self::svg:text][@inkscape:label='value'][1]/@id";
if "not($value_elt_id)" error > Input widget must have a text element
| value_elt: document.getElementById("«$value_elt_id»"),
| dispatch: function(value) {
| this.value_elt.textContent = String(value);
| },
const "edit_elt_id","$hmi_element/*[@inkscape:label='edit'][1]/@id";
| init: function() {
if "$edit_elt_id" {
| document.getElementById("«$edit_elt_id»").addEventListener(
| "click",
| evt => alert('XXX TODO : Edit value'));
}
foreach "$hmi_element/*[regexp:test(@inkscape:label,'^[=+\-][0-9]+')]" {
| document.getElementById("«@id»").addEventListener(
| "click",
| evt => {let new_val = change_hmi_value(this.indexes[0], "«@inkscape:label»");
| this.value_elt.textContent = String(new_val);});
/* could gray out value until refreshed */
}
| },
}
template "widget[@type='Button']", mode="widget_defs" {
}
template "widget[@type='Toggle']", mode="widget_defs" {
| frequency: 5,
}
template "widget[@type='Change']", mode="widget_defs" {
// HMI:Change:-10@/PUMP/VALUE
// HMI:Change:+1@/PUMP/VALUE
// HMI:Change:=42@/PUMP/VALUE
| frequency: 5,
}
// | frequency: 10`apply ".", mode="refresh_frequency"`,
// template "widget", mode="refresh_frequency" > 10
/*
template "widget[@type='Meter']", mode="refresh_frequency" > 10
template "widget[@type='Display']", mode="refresh_frequency" > 5
template "widget[@type='Input']", mode="refresh_frequency" > 5
*/
}