SVGHMI: Added relative changes of HMI value from widgets.
--- a/svghmi/gen_index_xhtml.xslt Tue Oct 22 22:58:55 2019 +0200
+++ b/svghmi/gen_index_xhtml.xslt Thu Oct 24 10:02:07 2019 +0200
@@ -320,323 +320,357 @@
</xsl:text>
<xsl:text>
</xsl:text>
+ <xsl:text>var cache = hmitree_types.map(_ignored => undefined);
+</xsl:text>
+ <xsl:text>
+</xsl:text>
<xsl:text>function dispatch_value(index, value) {
</xsl:text>
<xsl:text> let widgets = subscribers[index];
</xsl:text>
<xsl:text>
</xsl:text>
- <xsl:text> // TODO : value cache
+ <xsl:text> if(widgets.size > 0) {
+</xsl:text>
+ <xsl:text> for(let widget of widgets){
+</xsl:text>
+ <xsl:text> let idxidx = widget.indexes.indexOf(index);
+</xsl:text>
+ <xsl:text> if(idxidx == -1){
+</xsl:text>
+ <xsl:text> throw new Error("Dispatching to widget not interested, should not happen.");
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> let d = widget.dispatch;
+</xsl:text>
+ <xsl:text> if(typeof(d) == "function" && idxidx == 0){
+</xsl:text>
+ <xsl:text> return d.call(widget,value);
+</xsl:text>
+ <xsl:text> }else if(typeof(d) == "object" && d.length >= idxidx){
+</xsl:text>
+ <xsl:text> d[idxidx].call(widget,value);
+</xsl:text>
+ <xsl:text> }/* else dispatch_0, ..., dispatch_n ? */
+</xsl:text>
+ <xsl:text> /*else {
+</xsl:text>
+ <xsl:text> throw new Error("Dunno how to dispatch to widget at index = " + index);
+</xsl:text>
+ <xsl:text> }*/
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> cache[index] = value;
</xsl:text>
<xsl:text>
</xsl:text>
- <xsl:text> if(widgets.size > 0) {
-</xsl:text>
- <xsl:text> for(let widget of widgets){
-</xsl:text>
- <xsl:text> let idxidx = widget.indexes.indexOf(index);
-</xsl:text>
- <xsl:text> if(idxidx == -1){
-</xsl:text>
- <xsl:text> throw new Error("Dispatching to widget not interested, should not happen.");
+ <xsl:text>};
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text>function init_widgets() {
+</xsl:text>
+ <xsl:text> Object.keys(hmi_widgets).forEach(function(id) {
+</xsl:text>
+ <xsl:text> let widget = hmi_widgets[id];
+</xsl:text>
+ <xsl:text> let init = widget.init;
+</xsl:text>
+ <xsl:text> if(typeof(init) == "function"){
+</xsl:text>
+ <xsl:text> return init.call(widget);
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> });
+</xsl:text>
+ <xsl:text>};
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text>// Open WebSocket to relative "/ws" address
+</xsl:text>
+ <xsl:text>var ws = new WebSocket(window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws'));
+</xsl:text>
+ <xsl:text>ws.binaryType = 'arraybuffer';
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text>const dvgetters = {
+</xsl:text>
+ <xsl:text> INT: [DataView.prototype.getInt16, 2],
+</xsl:text>
+ <xsl:text> BOOL: [DataView.prototype.getInt8, 1]
+</xsl:text>
+ <xsl:text> /* TODO */
+</xsl:text>
+ <xsl:text>};
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text>// Register message reception handler
+</xsl:text>
+ <xsl:text>ws.onmessage = function (evt) {
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> let data = evt.data;
+</xsl:text>
+ <xsl:text> let dv = new DataView(data);
+</xsl:text>
+ <xsl:text> let i = 0;
+</xsl:text>
+ <xsl:text> //console.log("Recv something.");
+</xsl:text>
+ <xsl:text> try {
+</xsl:text>
+ <xsl:text> for(let hash_int of hmi_hash) {
+</xsl:text>
+ <xsl:text> if(hash_int != dv.getUint8(i)){
+</xsl:text>
+ <xsl:text> throw new Error("Hash doesn't match");
+</xsl:text>
+ <xsl:text> };
+</xsl:text>
+ <xsl:text> i++;
+</xsl:text>
+ <xsl:text> };
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> //console.log("Recv something GOOD.");
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> while(i < data.byteLength){
+</xsl:text>
+ <xsl:text> let index = dv.getUint32(i, true);
+</xsl:text>
+ <xsl:text> //console.log("Recv something index is "+index);
+</xsl:text>
+ <xsl:text> i += 4;
+</xsl:text>
+ <xsl:text> let iectype = hmitree_types[index];
+</xsl:text>
+ <xsl:text> if(iectype != undefined){
+</xsl:text>
+ <xsl:text> let [dvgetter, bytesize] = dvgetters[iectype];
+</xsl:text>
+ <xsl:text> let value = dvgetter.call(dv,i,true);
+</xsl:text>
+ <xsl:text> dispatch_value(index, value);
+</xsl:text>
+ <xsl:text> i += bytesize;
+</xsl:text>
+ <xsl:text> } else {
+</xsl:text>
+ <xsl:text> throw new Error("Unknown index "+index)
</xsl:text>
<xsl:text> }
</xsl:text>
- <xsl:text> let d = widget.dispatch;
-</xsl:text>
- <xsl:text> if(typeof(d) == "function" && idxidx == 0){
-</xsl:text>
- <xsl:text> return d.call(widget,value);
-</xsl:text>
- <xsl:text> }else if(typeof(d) == "object" && d.length >= idxidx){
-</xsl:text>
- <xsl:text> d[idxidx].call(widget,value);
-</xsl:text>
- <xsl:text> }/* else dispatch_0, ..., dispatch_n ? */
-</xsl:text>
- <xsl:text> /*else {
-</xsl:text>
- <xsl:text> throw new Error("Dunno how to dispatch to widget at index = " + index);
-</xsl:text>
- <xsl:text> }*/
+ <xsl:text> };
+</xsl:text>
+ <xsl:text> } catch(err) {
+</xsl:text>
+ <xsl:text> // 1003 is for "Unsupported Data"
+</xsl:text>
+ <xsl:text> // ws.close(1003, err.message);
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> // TODO : remove debug alert ?
+</xsl:text>
+ <xsl:text> alert("Error : "+err.message+"\nHMI will be reloaded.");
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> // force reload ignoring cache
+</xsl:text>
+ <xsl:text> location.reload(true);
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text>};
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text>function send_blob(data) {
+</xsl:text>
+ <xsl:text> if(data.length > 0) {
+</xsl:text>
+ <xsl:text> ws.send(new Blob([new Uint8Array(hmi_hash)].concat(data)));
+</xsl:text>
+ <xsl:text> };
+</xsl:text>
+ <xsl:text>};
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text>const typedarray_types = {
+</xsl:text>
+ <xsl:text> INT: Int16Array,
+</xsl:text>
+ <xsl:text> BOOL: Uint8Array
+</xsl:text>
+ <xsl:text> /* TODO */
+</xsl:text>
+ <xsl:text>};
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text>function send_reset() {
+</xsl:text>
+ <xsl:text> send_blob(new Uint8Array([1])); /* reset = 1 */
+</xsl:text>
+ <xsl:text>};
+</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>
+ <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>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> // periods are in ms
+</xsl:text>
+ <xsl:text> let previous_period = subscriptions[index];
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> let new_period = 0;
+</xsl:text>
+ <xsl:text> if(widgets.size > 0) {
+</xsl:text>
+ <xsl:text> let maxfreq = 0;
+</xsl:text>
+ <xsl:text> for(let widget of widgets)
+</xsl:text>
+ <xsl:text> if(maxfreq < widget.frequency)
+</xsl:text>
+ <xsl:text> maxfreq = widget.frequency;
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> if(maxfreq != 0)
+</xsl:text>
+ <xsl:text> new_period = 1000/maxfreq;
</xsl:text>
<xsl:text> }
</xsl:text>
+ <xsl:text>
+</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>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text>
+</xsl:text>
<xsl:text> }
</xsl:text>
- <xsl:text>};
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text>function init_widgets() {
-</xsl:text>
- <xsl:text> Object.keys(hmi_widgets).forEach(function(id) {
-</xsl:text>
- <xsl:text> let widget = hmi_widgets[id];
-</xsl:text>
- <xsl:text> let init = widget.init;
-</xsl:text>
- <xsl:text> if(typeof(init) == "function"){
-</xsl:text>
- <xsl:text> return init.call(widget);
-</xsl:text>
- <xsl:text> }
-</xsl:text>
- <xsl:text> });
-</xsl:text>
- <xsl:text>};
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text>// Open WebSocket to relative "/ws" address
-</xsl:text>
- <xsl:text>var ws = new WebSocket(window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws'));
-</xsl:text>
- <xsl:text>ws.binaryType = 'arraybuffer';
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text>const dvgetters = {
-</xsl:text>
- <xsl:text> INT: [DataView.prototype.getInt16, 2],
-</xsl:text>
- <xsl:text> BOOL: [DataView.prototype.getInt8, 1]
-</xsl:text>
- <xsl:text> /* TODO */
-</xsl:text>
- <xsl:text>};
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text>// Register message reception handler
-</xsl:text>
- <xsl:text>ws.onmessage = function (evt) {
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> let data = evt.data;
-</xsl:text>
- <xsl:text> let dv = new DataView(data);
-</xsl:text>
- <xsl:text> let i = 0;
-</xsl:text>
- <xsl:text> //console.log("Recv something.");
-</xsl:text>
- <xsl:text> try {
-</xsl:text>
- <xsl:text> for(let hash_int of hmi_hash) {
-</xsl:text>
- <xsl:text> if(hash_int != dv.getUint8(i)){
-</xsl:text>
- <xsl:text> throw new Error("Hash doesn't match");
-</xsl:text>
- <xsl:text> };
-</xsl:text>
- <xsl:text> i++;
-</xsl:text>
- <xsl:text> };
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> //console.log("Recv something GOOD.");
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> while(i < data.byteLength){
-</xsl:text>
- <xsl:text> let index = dv.getUint32(i, true);
-</xsl:text>
- <xsl:text> //console.log("Recv something index is "+index);
-</xsl:text>
- <xsl:text> i += 4;
-</xsl:text>
- <xsl:text> let iectype = hmitree_types[index];
-</xsl:text>
- <xsl:text> if(iectype != undefined){
-</xsl:text>
- <xsl:text> let [dvgetter, bytesize] = dvgetters[iectype];
-</xsl:text>
- <xsl:text> let value = dvgetter.call(dv,i,true);
-</xsl:text>
- <xsl:text> dispatch_value(index, value);
-</xsl:text>
- <xsl:text> i += bytesize;
-</xsl:text>
- <xsl:text> } else {
-</xsl:text>
- <xsl:text> throw new Error("Unknown index "+index)
-</xsl:text>
- <xsl:text> }
-</xsl:text>
- <xsl:text> };
-</xsl:text>
- <xsl:text> } catch(err) {
-</xsl:text>
- <xsl:text> // 1003 is for "Unsupported Data"
-</xsl:text>
- <xsl:text> // ws.close(1003, err.message);
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> // TODO : remove debug alert ?
-</xsl:text>
- <xsl:text> alert("Error : "+err.message+"\nHMI will be reloaded.");
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> // force reload ignoring cache
-</xsl:text>
- <xsl:text> location.reload(true);
+ <xsl:text> send_blob(delta);
+</xsl:text>
+ <xsl:text>};
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text>function send_hmi_value(index, value) {
+</xsl:text>
+ <xsl:text> let iectype = hmitree_types[index];
+</xsl:text>
+ <xsl:text> let jstype = typedarray_types[iectype];
+</xsl:text>
+ <xsl:text> send_blob([
+</xsl:text>
+ <xsl:text> new Uint8Array([0]), /* setval = 0 */
+</xsl:text>
+ <xsl:text> new Uint32Array([index]),
+</xsl:text>
+ <xsl:text> new jstype([value])]);
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> cache[index] = value;
+</xsl:text>
+ <xsl:text>};
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text>function change_hmi_value(index, opstr) {
+</xsl:text>
+ <xsl:text> let op = opstr[0];
+</xsl:text>
+ <xsl:text> let given_val = opstr.slice(1);
+</xsl:text>
+ <xsl:text> let old_val = cache[index]
+</xsl:text>
+ <xsl:text> let new_val;
+</xsl:text>
+ <xsl:text> switch(op){
+</xsl:text>
+ <xsl:text> case "=":
+</xsl:text>
+ <xsl:text> eval("new_val"+opstr);
+</xsl:text>
+ <xsl:text> break;
+</xsl:text>
+ <xsl:text> case "+":
+</xsl:text>
+ <xsl:text> case "-":
+</xsl:text>
+ <xsl:text> case "*":
+</xsl:text>
+ <xsl:text> case "/":
+</xsl:text>
+ <xsl:text> if(old_val != undefined)
+</xsl:text>
+ <xsl:text> new_val = eval("old_val"+opstr);
+</xsl:text>
+ <xsl:text> break;
</xsl:text>
<xsl:text> }
</xsl:text>
- <xsl:text>};
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text>function send_blob(data) {
-</xsl:text>
- <xsl:text> if(data.length > 0) {
-</xsl:text>
- <xsl:text> ws.send(new Blob([new Uint8Array(hmi_hash)].concat(data)));
-</xsl:text>
- <xsl:text> };
-</xsl:text>
- <xsl:text>};
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text>const typedarray_types = {
-</xsl:text>
- <xsl:text> INT: Int16Array,
-</xsl:text>
- <xsl:text> BOOL: Uint8Array
-</xsl:text>
- <xsl:text> /* TODO */
-</xsl:text>
- <xsl:text>};
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text>function send_reset() {
-</xsl:text>
- <xsl:text> send_blob(new Uint8Array([1])); /* reset = 1 */
-</xsl:text>
- <xsl:text>};
-</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>
- <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>
- <xsl:text>
-</xsl:text>
- <xsl:text> // periods are in ms
-</xsl:text>
- <xsl:text> let previous_period = subscriptions[index];
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> let new_period = 0;
-</xsl:text>
- <xsl:text> if(widgets.size > 0) {
-</xsl:text>
- <xsl:text> let maxfreq = 0;
-</xsl:text>
- <xsl:text> for(let widget of widgets)
-</xsl:text>
- <xsl:text> if(maxfreq < widget.frequency)
-</xsl:text>
- <xsl:text> maxfreq = widget.frequency;
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> if(maxfreq != 0)
-</xsl:text>
- <xsl:text> new_period = 1000/maxfreq;
-</xsl:text>
- <xsl:text> }
-</xsl:text>
- <xsl:text>
-</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>
- <xsl:text> }
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> }
-</xsl:text>
- <xsl:text> send_blob(delta);
-</xsl:text>
- <xsl:text>};
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text>function send_hmi_value(index, value) {
-</xsl:text>
- <xsl:text> let iectype = hmitree_types[index];
-</xsl:text>
- <xsl:text> let jstype = typedarray_types[iectype];
-</xsl:text>
- <xsl:text> send_blob([
-</xsl:text>
- <xsl:text> new Uint8Array([0]), /* setval = 0 */
-</xsl:text>
- <xsl:text> new Uint32Array([index]),
-</xsl:text>
- <xsl:text> new jstype([value])]);
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text>};
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text>function change_hmi_value(index, opstr) {
-</xsl:text>
- <xsl:text> let op = opstr[0];
-</xsl:text>
- <xsl:text> if(op == "=")
-</xsl:text>
- <xsl:text> return send_hmi_value(index, Number(opstr.slice(1)));
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> alert('Change '+opstr+" TODO !!! (index :"+index+")");
+ <xsl:text> if(new_val != undefined && old_val != new_val)
+</xsl:text>
+ <xsl:text> return send_hmi_value(index, new_val);
</xsl:text>
<xsl:text>}
</xsl:text>
--- a/svghmi/svghmi.js Tue Oct 22 22:58:55 2019 +0200
+++ b/svghmi/svghmi.js Thu Oct 24 10:02:07 2019 +0200
@@ -1,10 +1,10 @@
// svghmi.js
+var cache = hmitree_types.map(_ignored => undefined);
+
function dispatch_value(index, value) {
let widgets = subscribers[index];
- // TODO : value cache
-
if(widgets.size > 0) {
for(let widget of widgets){
let idxidx = widget.indexes.indexOf(index);
@@ -22,6 +22,9 @@
}*/
}
}
+
+ cache[index] = value;
+
};
function init_widgets() {
@@ -147,14 +150,28 @@
new Uint32Array([index]),
new jstype([value])]);
+ cache[index] = value;
};
function change_hmi_value(index, opstr) {
let op = opstr[0];
- if(op == "=")
- return send_hmi_value(index, Number(opstr.slice(1)));
-
- alert('Change '+opstr+" TODO !!! (index :"+index+")");
+ let given_val = opstr.slice(1);
+ let old_val = cache[index]
+ let new_val;
+ switch(op){
+ case "=":
+ eval("new_val"+opstr);
+ break;
+ case "+":
+ case "-":
+ case "*":
+ case "/":
+ if(old_val != undefined)
+ new_val = eval("old_val"+opstr);
+ break;
+ }
+ if(new_val != undefined && old_val != new_val)
+ return send_hmi_value(index, new_val);
}
var current_page;