SVGHMI: finished initial implementation of PAGE_LOCAL and HMI_LOCAL variables.
--- a/svghmi/gen_index_xhtml.xslt Sat Aug 08 15:53:28 2020 +0200
+++ b/svghmi/gen_index_xhtml.xslt Tue Aug 11 14:37:33 2020 +0200
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:str="http://exslt.org/strings" xmlns:func="http://exslt.org/functions" 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:xlink="http://www.w3.org/1999/xlink" 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" xmlns:debug="debug" xmlns:preamble="preamble" xmlns:declarations="declarations" xmlns:definitions="definitions" xmlns:epilogue="epilogue" xmlns:ns="beremiz" version="1.0" extension-element-prefixes="ns func exsl regexp str dyn" exclude-result-prefixes="ns func exsl regexp str dyn debug preamble epilogue declarations definitions">
- <xsl:output cdata-section-elements="xhtml:script" method="xml"/>
+<xsl:stylesheet xmlns:ns="beremiz" xmlns:definitions="definitions" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:func="http://exslt.org/functions" xmlns:epilogue="epilogue" xmlns:preamble="preamble" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:svg="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:str="http://exslt.org/strings" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:exsl="http://exslt.org/common" xmlns:declarations="declarations" xmlns:debug="debug" exclude-result-prefixes="ns func exsl regexp str dyn debug preamble epilogue declarations definitions" extension-element-prefixes="ns func exsl regexp str dyn" version="1.0">
+ <xsl:output method="xml" cdata-section-elements="xhtml:script"/>
<xsl:variable name="svg" select="/svg:svg"/>
<xsl:variable name="hmi_elements" select="//svg:*[starts-with(@inkscape:label, 'HMI:')]"/>
<xsl:variable name="hmitree" select="ns:GetHMITree()"/>
@@ -875,7 +875,7 @@
</xsl:when>
<xsl:when test="@type = 'PAGE_LOCAL'">
<xsl:text>"</xsl:text>
- <xsl:value-of select="substring(1,@value)"/>
+ <xsl:value-of select="substring(@value, 1)"/>
<xsl:text>"</xsl:text>
<xsl:if test="position()!=last()">
<xsl:text>,</xsl:text>
@@ -959,36 +959,54 @@
</xsl:text>
<xsl:text>
</xsl:text>
+ <xsl:text>var cache = hmitree_types.map(_ignored => undefined);
+</xsl:text>
+ <xsl:text>
+</xsl:text>
<xsl:text>function page_local_index(varname, pagename){
</xsl:text>
<xsl:text> let pagevars = hmi_locals[pagename];
</xsl:text>
+ <xsl:text> let new_index;
+</xsl:text>
<xsl:text> if(pagevars == undefined){
</xsl:text>
- <xsl:text> let new_index = next_available_index++;
-</xsl:text>
- <xsl:text> hmi_locals[pagename] = {varname:new_index}
-</xsl:text>
- <xsl:text> return new_index;
+ <xsl:text> new_index = next_available_index++;
+</xsl:text>
+ <xsl:text> hmi_locals[pagename] = {[varname]:new_index}
+</xsl:text>
+ <xsl:text> console.log("pagelocalindex insert",varname, pagename, new_index);
</xsl:text>
<xsl:text> } else {
</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> console.log("pagevars",pagevars);
+</xsl:text>
<xsl:text> let result = pagevars[varname];
</xsl:text>
- <xsl:text> if(result==undefined){
-</xsl:text>
- <xsl:text> let new_index = next_available_index++;
-</xsl:text>
- <xsl:text> pagevars[varname] = new_index;
-</xsl:text>
- <xsl:text> return new_index;
+ <xsl:text> if(result != undefined) {
+</xsl:text>
+ <xsl:text> console.log("pagelocalindex reuse",varname, pagename, result);
+</xsl:text>
+ <xsl:text> return result;
</xsl:text>
<xsl:text> }
</xsl:text>
- <xsl:text> return result;
+ <xsl:text>
+</xsl:text>
+ <xsl:text> new_index = next_available_index++;
+</xsl:text>
+ <xsl:text> pagevars[varname] = new_index;
+</xsl:text>
+ <xsl:text> console.log("pagelocalindex addwidget",varname, pagename, new_index);
</xsl:text>
<xsl:text> }
</xsl:text>
+ <xsl:text> cache[new_index] = "";
+</xsl:text>
+ <xsl:text> return new_index;
+</xsl:text>
<xsl:text>}
</xsl:text>
<xsl:text>
@@ -1050,7 +1068,7 @@
</xsl:text>
<xsl:text> index += this.offset;
</xsl:text>
- <xsl:text> subscribers[index].delete(this);
+ <xsl:text> subscribers(index).delete(this);
</xsl:text>
<xsl:text> }
</xsl:text>
@@ -1078,9 +1096,7 @@
</xsl:text>
<xsl:text> let index = this.get_variable_index(i);
</xsl:text>
- <xsl:text> if(index > last_remote_index) return;
-</xsl:text>
- <xsl:text> subscribers[index].add(this);
+ <xsl:text> subscribers(index).add(this);
</xsl:text>
<xsl:text> }
</xsl:text>
@@ -1116,8 +1132,6 @@
</xsl:text>
<xsl:text> if(typeof(index) == "string"){
</xsl:text>
- <xsl:text> let page = this.relativeness[varnum];
-</xsl:text>
<xsl:text> index = page_local_index(index, this.container_id);
</xsl:text>
<xsl:text> } else {
@@ -1152,7 +1166,7 @@
</xsl:text>
<xsl:text> new_hmi_value(index, value, oldval) {
</xsl:text>
- <xsl:text> try {
+ <xsl:text> /* try {*/
</xsl:text>
<xsl:text> // TODO avoid searching, store index at sub()
</xsl:text>
@@ -1192,11 +1206,11 @@
</xsl:text>
<xsl:text> }
</xsl:text>
- <xsl:text> } catch(err) {
+ <xsl:text> /* } catch(err) {
</xsl:text>
<xsl:text> console.log(err);
</xsl:text>
- <xsl:text> }
+ <xsl:text> }*/
</xsl:text>
<xsl:text> }
</xsl:text>
@@ -1557,7 +1571,22 @@
<xsl:text>" is not a svg::text element</xsl:text>
</xsl:message>
</xsl:if>
- <xsl:text> fields: [],
+ <xsl:variable name="field_initializer">
+ <xsl:for-each select="path">
+ <xsl:choose>
+ <xsl:when test="@type='HMI_STRING'">
+ <xsl:text>""</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>0</xsl:otherwise>
+ </xsl:choose>
+ <xsl:if test="position()!=last()">
+ <xsl:text>,</xsl:text>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:variable>
+ <xsl:text> fields: [</xsl:text>
+ <xsl:value-of select="$field_initializer"/>
+ <xsl:text>],
</xsl:text>
</xsl:template>
<preamble:display/>
@@ -2955,14 +2984,6 @@
<xsl:text>" is not valid.</xsl:text>
</xsl:message>
</xsl:if>
- <xsl:text> console.log("</xsl:text>
- <xsl:value-of select="@id"/>
- <xsl:text>", "</xsl:text>
- <xsl:value-of select="$value_expr"/>
- <xsl:text>", </xsl:text>
- <xsl:value-of select="$value_expr"/>
- <xsl:text>);
-</xsl:text>
<xsl:text> id("</xsl:text>
<xsl:value-of select="@id"/>
<xsl:text>").setAttribute("xlink:href",
@@ -2976,14 +2997,6 @@
</xsl:template>
<xsl:template mode="json_table_elt_render" match="svg:text">
<xsl:param name="value_expr"/>
- <xsl:text> console.log("</xsl:text>
- <xsl:value-of select="@id"/>
- <xsl:text>", "</xsl:text>
- <xsl:value-of select="$value_expr"/>
- <xsl:text>", </xsl:text>
- <xsl:value-of select="$value_expr"/>
- <xsl:text>);
-</xsl:text>
<xsl:text> id("</xsl:text>
<xsl:value-of select="@id"/>
<xsl:text>").textContent = String(</xsl:text>
@@ -3034,8 +3047,6 @@
<xsl:variable name="data_elt" select="$result_svg_ns//*[@id = $hmi_element/@id]/*[@inkscape:label = 'data']"/>
<xsl:text> spread_json_data: function(jdata) {
</xsl:text>
- <xsl:text> console.log(jdata);
-</xsl:text>
<xsl:apply-templates mode="json_table_render" select="$data_elt/*">
<xsl:with-param name="objname" select="'jdata'"/>
</xsl:apply-templates>
@@ -3704,7 +3715,7 @@
<xsl:comment>
<xsl:apply-templates select="document('')/*/debug:*"/>
</xsl:comment>
- <html xmlns="http://www.w3.org/1999/xhtml" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <html xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/1999/xhtml">
<head/>
<body style="margin:0;overflow:hidden;">
<xsl:copy-of select="$result_svg"/>
@@ -3745,8 +3756,6 @@
</xsl:text>
<xsl:text>
</xsl:text>
- <xsl:text>var cache = hmitree_types.map(_ignored => undefined);
-</xsl:text>
<xsl:text>var updates = {};
</xsl:text>
<xsl:text>var need_cache_apply = [];
@@ -3757,7 +3766,7 @@
</xsl:text>
<xsl:text>function dispatch_value(index, value) {
</xsl:text>
- <xsl:text> let widgets = subscribers[index];
+ <xsl:text> let widgets = subscribers(index);
</xsl:text>
<xsl:text>
</xsl:text>
@@ -3857,6 +3866,8 @@
</xsl:text>
<xsl:text> // -> pass Number(index) instead
</xsl:text>
+ <xsl:text> console.log("apply updated local variable ",index, updates[index]);
+</xsl:text>
<xsl:text> dispatch_value(Number(index), updates[index]);
</xsl:text>
<xsl:text> delete updates[index];
@@ -4057,19 +4068,65 @@
</xsl:text>
<xsl:text>
</xsl:text>
- <xsl:text>// subscription state, as it should be in hmi server
-</xsl:text>
- <xsl:text>// hmitree indexed array of integers
-</xsl:text>
- <xsl:text>var subscriptions = hmitree_types.map(_ignored => 0);
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text>// subscription state as needed by widget now
-</xsl:text>
- <xsl:text>// hmitree indexed array of Sets of widgets objects
-</xsl:text>
- <xsl:text>var subscribers = hmitree_types.map(_ignored => new Set());
+ <xsl:text>var subscriptions = [];
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text>function subscribers(index) {
+</xsl:text>
+ <xsl:text> let entry = subscriptions[index];
+</xsl:text>
+ <xsl:text> let res;
+</xsl:text>
+ <xsl:text> if(entry == undefined){
+</xsl:text>
+ <xsl:text> res = new Set();
+</xsl:text>
+ <xsl:text> subscriptions[index] = [res,0];
+</xsl:text>
+ <xsl:text> }else{
+</xsl:text>
+ <xsl:text> [res, _ign] = entry;
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> return res
+</xsl:text>
+ <xsl:text>}
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text>function get_subscription_period(index) {
+</xsl:text>
+ <xsl:text> let entry = subscriptions[index];
+</xsl:text>
+ <xsl:text> if(entry == undefined)
+</xsl:text>
+ <xsl:text> return 0;
+</xsl:text>
+ <xsl:text> let [_ign, period] = entry;
+</xsl:text>
+ <xsl:text> return period;
+</xsl:text>
+ <xsl:text>}
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text>function set_subscription_period(index, period) {
+</xsl:text>
+ <xsl:text> let entry = subscriptions[index];
+</xsl:text>
+ <xsl:text> if(entry == undefined){
+</xsl:text>
+ <xsl:text> subscriptions[index] = [new Set(), period];
+</xsl:text>
+ <xsl:text> } else {
+</xsl:text>
+ <xsl:text> entry[1] = period;
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text>}
</xsl:text>
<xsl:text>
</xsl:text>
@@ -4079,7 +4136,7 @@
</xsl:text>
<xsl:text>// PLC will periodically send variable at given frequency
</xsl:text>
- <xsl:text>subscribers[heartbeat_index].add({
+ <xsl:text>subscribers(heartbeat_index).add({
</xsl:text>
<xsl:text> /* type: "Watchdog", */
</xsl:text>
@@ -4097,19 +4154,21 @@
</xsl:text>
<xsl:text>
</xsl:text>
+ <xsl:text>
+</xsl:text>
<xsl:text>function update_subscriptions() {
</xsl:text>
<xsl:text> let delta = [];
</xsl:text>
- <xsl:text> for(let index = 0; index < subscribers.length; index++){
-</xsl:text>
- <xsl:text> let widgets = subscribers[index];
+ <xsl:text> for(let index in subscriptions){
+</xsl:text>
+ <xsl:text> let widgets = subscribers(index);
</xsl:text>
<xsl:text>
</xsl:text>
<xsl:text> // periods are in ms
</xsl:text>
- <xsl:text> let previous_period = subscriptions[index];
+ <xsl:text> let previous_period = get_subscription_period(index);
</xsl:text>
<xsl:text>
</xsl:text>
@@ -4143,15 +4202,19 @@
</xsl:text>
<xsl:text> if(previous_period != new_period) {
</xsl:text>
- <xsl:text> subscriptions[index] = new_period;
-</xsl:text>
- <xsl:text> delta.push(
-</xsl:text>
- <xsl:text> new Uint8Array([2]), /* subscribe = 2 */
-</xsl:text>
- <xsl:text> new Uint32Array([index]),
-</xsl:text>
- <xsl:text> new Uint16Array([new_period]));
+ <xsl:text> set_subscription_period(index, new_period);
+</xsl:text>
+ <xsl:text> if(index <= last_remote_index){
+</xsl:text>
+ <xsl:text> delta.push(
+</xsl:text>
+ <xsl:text> new Uint8Array([2]), /* subscribe = 2 */
+</xsl:text>
+ <xsl:text> new Uint32Array([index]),
+</xsl:text>
+ <xsl:text> new Uint16Array([new_period]));
+</xsl:text>
+ <xsl:text> }
</xsl:text>
<xsl:text> }
</xsl:text>
@@ -4167,11 +4230,11 @@
</xsl:text>
<xsl:text> if(index > last_remote_index){
</xsl:text>
- <xsl:text> cache[index] = value;
-</xsl:text>
<xsl:text> console.log("updated local variable ",index,value);
</xsl:text>
- <xsl:text> /* TODO : dispatch value ASAP */
+ <xsl:text> updates[index] = value;
+</xsl:text>
+ <xsl:text> requestHMIAnimation();
</xsl:text>
<xsl:text> return;
</xsl:text>
@@ -4375,7 +4438,7 @@
</xsl:text>
<xsl:text>
</xsl:text>
- <xsl:text> container_id = String([page_name, page_index]);
+ <xsl:text> container_id = page_name + (page_index != undefined ? page_index : "");
</xsl:text>
<xsl:text>
</xsl:text>
--- a/svghmi/svghmi.js Sat Aug 08 15:53:28 2020 +0200
+++ b/svghmi/svghmi.js Tue Aug 11 14:37:33 2020 +0200
@@ -1,12 +1,11 @@
// svghmi.js
-var cache = hmitree_types.map(_ignored => undefined);
var updates = {};
var need_cache_apply = [];
function dispatch_value(index, value) {
- let widgets = subscribers[index];
+ let widgets = subscribers(index);
let oldval = cache[index];
cache[index] = value;
@@ -156,18 +155,41 @@
send_blob(new Uint8Array([1])); /* reset = 1 */
};
-// subscription state, as it should be in hmi server
-// hmitree indexed array of integers
-var subscriptions = hmitree_types.map(_ignored => 0);
-
-// subscription state as needed by widget now
-// hmitree indexed array of Sets of widgets objects
-var subscribers = hmitree_types.map(_ignored => new Set());
+var subscriptions = [];
+
+function subscribers(index) {
+ let entry = subscriptions[index];
+ let res;
+ if(entry == undefined){
+ res = new Set();
+ subscriptions[index] = [res,0];
+ }else{
+ [res, _ign] = entry;
+ }
+ return res
+}
+
+function get_subscription_period(index) {
+ let entry = subscriptions[index];
+ if(entry == undefined)
+ return 0;
+ let [_ign, period] = entry;
+ return period;
+}
+
+function set_subscription_period(index, period) {
+ let entry = subscriptions[index];
+ if(entry == undefined){
+ subscriptions[index] = [new Set(), period];
+ } else {
+ entry[1] = period;
+ }
+}
// artificially subscribe the watchdog widget to "/heartbeat" hmi variable
// Since dispatch directly calls change_hmi_value,
// PLC will periodically send variable at given frequency
-subscribers[heartbeat_index].add({
+subscribers(heartbeat_index).add({
/* type: "Watchdog", */
frequency: 1,
indexes: [heartbeat_index],
@@ -176,13 +198,14 @@
}
});
+
function update_subscriptions() {
let delta = [];
- for(let index = 0; index < subscribers.length; index++){
- let widgets = subscribers[index];
+ for(let index in subscriptions){
+ let widgets = subscribers(index);
// periods are in ms
- let previous_period = subscriptions[index];
+ let previous_period = get_subscription_period(index);
// subscribing with a zero period is unsubscribing
let new_period = 0;
@@ -199,11 +222,13 @@
}
if(previous_period != new_period) {
- subscriptions[index] = new_period;
- delta.push(
- new Uint8Array([2]), /* subscribe = 2 */
- new Uint32Array([index]),
- new Uint16Array([new_period]));
+ set_subscription_period(index, new_period);
+ if(index <= last_remote_index){
+ delta.push(
+ new Uint8Array([2]), /* subscribe = 2 */
+ new Uint32Array([index]),
+ new Uint16Array([new_period]));
+ }
}
}
send_blob(delta);
@@ -211,9 +236,9 @@
function send_hmi_value(index, value) {
if(index > last_remote_index){
- cache[index] = value;
console.log("updated local variable ",index,value);
- /* TODO : dispatch value ASAP */
+ updates[index] = value;
+ requestHMIAnimation();
return;
}
@@ -315,7 +340,7 @@
}
var new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index;
- container_id = String([page_name, page_index]);
+ container_id = page_name + (page_index != undefined ? page_index : "");
new_desc.widgets.map(([widget,relativeness])=>widget.sub(new_offset,relativeness,container_id));
--- a/svghmi/widget_display.ysl2 Sat Aug 08 15:53:28 2020 +0200
+++ b/svghmi/widget_display.ysl2 Tue Aug 11 14:37:33 2020 +0200
@@ -18,7 +18,14 @@
if "$hmi_element[not(self::svg:text)]"
error > Display Widget id="«$hmi_element/@id»" is not a svg::text element
- | fields: [],
+ const "field_initializer" foreach "path" {
+ choose{
+ when "@type='HMI_STRING'" > ""
+ otherwise 0
+ }
+ if "position()!=last()" > ,
+ }
+ | fields: [«$field_initializer»],
}
emit "preamble:display"
--- a/svghmi/widgets_common.ysl2 Sat Aug 08 15:53:28 2020 +0200
+++ b/svghmi/widgets_common.ysl2 Tue Aug 11 14:37:33 2020 +0200
@@ -32,7 +32,7 @@
when "not(@type)"
error > Widget «$widget/@type» id="«$eltid»" : No match for path "«@value»" in HMI tree
when "@type = 'PAGE_LOCAL'"
- > "«substring(1,@value)»"`if "position()!=last()" > ,`
+ > "«substring(@value, 1)»"`if "position()!=last()" > ,`
when "@type = 'HMI_LOCAL'"
> hmi_local_index("«@value»")`if "position()!=last()" > ,`
}
@@ -75,21 +75,25 @@
var last_remote_index = hmitree_types.length - 1;
var next_available_index = hmitree_types.length;
+ var cache = hmitree_types.map(_ignored => undefined);
+
function page_local_index(varname, pagename){
let pagevars = hmi_locals[pagename];
+ let new_index;
if(pagevars == undefined){
- let new_index = next_available_index++;
- hmi_locals[pagename] = {varname:new_index}
- return new_index;
+ new_index = next_available_index++;
+ hmi_locals[pagename] = {[varname]:new_index}
} else {
let result = pagevars[varname];
- if(result==undefined){
- let new_index = next_available_index++;
- pagevars[varname] = new_index;
- return new_index;
- }
- return result;
- }
+ if(result != undefined) {
+ return result;
+ }
+
+ new_index = next_available_index++;
+ pagevars[varname] = new_index;
+ }
+ cache[new_index] = "";
+ return new_index;
}
function hmi_local_index(varname){
@@ -119,7 +123,7 @@
let index = this.indexes[i];
if(this.relativeness[i])
index += this.offset;
- subscribers[index].delete(this);
+ subscribers(index).delete(this);
}
this.offset = 0;
this.relativeness = undefined;
@@ -133,8 +137,7 @@
if(!this.unsubscribable)
for(let i = 0; i < this.indexes.length; i++) {
let index = this.get_variable_index(i);
- if(index > last_remote_index) return;
- subscribers[index].add(this);
+ subscribers(index).add(this);
}
need_cache_apply.push(this);
}
@@ -152,7 +155,6 @@
get_variable_index(varnum) {
let index = this.indexes[varnum];
if(typeof(index) == "string"){
- let page = this.relativeness[varnum];
index = page_local_index(index, this.container_id);
} else {
if(this.relativeness[varnum]){