--- a/svghmi/svghmi.js Fri Oct 11 09:31:34 2024 +0200
+++ b/svghmi/svghmi.js Fri Oct 11 10:18:57 2024 +0200
@@ -143,7 +143,7 @@
function send_blob(data) {
- if(ws && data.length > 0) {
+ if(data.length > 0 && ws && ws.readyState == WebSocket.OPEN) { ws.send(new Blob([hmi_hash_u8].concat(data)));
@@ -178,6 +178,7 @@
+var subscriptions_update_requested = false; function subscribers(index) {
let entry = subscriptions[index];
@@ -312,7 +313,10 @@
function update_subscriptions() {
+ subscriptions_update_requested = false; + if(!ws || ws.readyState != WebSocket.OPEN) // dont' change subscriptions if not connected
@@ -349,6 +353,14 @@
+function request_subscriptions_update(){ + if(!subscriptions_update_requested){ + subscriptions_update_requested = true; + Promise.resolve().then(update_subscriptions); function send_hmi_value(index, value) {
if(index > last_remote_index){
dispatch_value(index, value);
@@ -513,7 +525,7 @@
new_desc.widgets.map(([widget,relativeness])=>widget.sub(new_offset,relativeness,container_id));
- update_subscriptions();
+ request_subscriptions_update(); current_subscribed_page = page_name;
current_page_index = page_index;
--- a/svghmi/widget_foreach.ysl2 Fri Oct 11 09:31:34 2024 +0200
+++ b/svghmi/widget_foreach.ysl2 Fri Oct 11 10:18:57 2024 +0200
@@ -19,6 +19,9 @@
Direct sub-elements can be either groups of widget to be spanned, labeled
"ClassName:offset", or buttons to control the spanning, labeled
+ In case of "ClassName:offset", offset for first element is 1. shortdesc > span widgets over a set of repeated HMI_NODEs
@@ -30,7 +33,7 @@
- if "count(path) != 1" error > ForEach widget «$hmi_element/@id» must have one HMI path given.
+ if "count(path) < 1" error > ForEach widget «$hmi_element/@id» must have one HMI path given. if "count(arg) != 1" error > ForEach widget «$hmi_element/@id» must have one argument given : a class name.
const "class","arg[1]/@value";
@@ -65,6 +68,7 @@
const "item_path", "$items_paths[$pos]";
| [ /* item="«$elt_label»" path="«$item_path»" */
if "count($elt)=0" error > Missing item labeled «$elt_label» in ForEach widget «$hmi_element/@id»
+ if "count($elt)>1" error > Duplicate item labeled «$elt_label» in ForEach widget «$hmi_element/@id» foreach "func:refered_elements($elt)[@id = $hmi_elements/@id][not(@id = $elt/@id)]" {
if "not(func:is_descendant_path(func:widget(@id)/path/@value, $item_path))"
error > Widget id="«@id»" label="«@inkscape:label»" is having wrong path. Accroding to ForEach widget ancestor id="«$hmi_element/@id»", path should be descendant of "«$item_path»".
@@ -74,75 +78,116 @@
+ | range: «count($hmi_index_items)», + | size: «count($unordered_items)», + items_subscribed = false; - for(let item of this.items){
- for(let widget of item) {
+ if(this.items_subscribed){ + for(let item of this.items){ + for(let widget of item) { + this.items_subscribed = false;
- this.relativeness = undefined;
- for(let i = 0; i < this.items.length; i++) {
- let item = this.items[i];
- let orig_item_index = this.index_pool[i];
- let item_index = this.index_pool[i+this.item_offset];
- let item_index_offset = item_index - orig_item_index;
- if(this.relativeness[0])
- item_index_offset += this.offset;
- for(let widget of item) {
- /* all variables of all widgets in a ForEach are all relative.
+ if(!this.items_subscribed){ + for(let i = 0; i < this.size; i++) { + let item = this.items[i]; + let orig_item_index = this.index_pool[i]; + let item_index = this.index_pool[i+this.position]; + let item_index_offset = item_index - orig_item_index; + if(this.relativeness[0]) + item_index_offset += this.offset; + for(let widget of item) { + /* all variables of all widgets in a ForEach are all relative. - TODO: allow absolute variables in ForEach widgets
- widget.sub(item_index_offset, widget.indexes.map(_=>true));
+ TODO: allow absolute variables in ForEach widgets + widget.sub(item_index_offset, widget.indexes.map(_=>true)); - sub(new_offset=0, relativeness=[]){
- this.offset = new_offset;
- this.relativeness = relativeness;
+ sub(new_offset, relativeness, container_id){ + let position_given = this.indexes.length > 2; + // sub() will call apply_cache() and then dispatch() + // undefining position forces dispatch() to call apply_position() + this.position = undefined; + super.sub(new_offset, relativeness, container_id); + // if position isn't given as a variable + // dispatch() to call apply_position() aren't called + // and items must be subscibed now. + // as soon as subribed apply range and size once for all + this.apply_hmi_value(1, this.range); + this.apply_hmi_value(3, this.size);
- this.items.forEach(item=>item.forEach(widget=>widget.apply_cache()));
+ apply_position(new_position){ + let old_position = this.position; + let limited_position = Math.round(Math.max(Math.min(new_position, this.range - this.size), 0)); + if(this.position == limited_position){ + this.position = limited_position; + request_subscriptions_update(); + jumps_need_update = true; + this.request_animate(); - let new_item_offset = eval(String(this.item_offset)+opstr);
- if(new_item_offset + this.items.length > this.index_pool.length) {
- if(this.item_offset + this.items.length == this.index_pool.length)
+ let new_position = eval(String(this.position)+opstr); + if(new_position + this.size > this.range) { + if(this.position + this.size == this.range) - new_item_offset = this.index_pool.length - this.items.length;
- } else if(new_item_offset < 0) {
- if(this.item_offset == 0)
- new_item_offset = this.index_pool.length - this.items.length;
+ new_position = this.range - this.size; + } else if(new_position < 0) { + new_position = this.range - this.size;
+ if(this.apply_position(new_position)){ + this.apply_hmi_value(2, this.position); - this.item_offset = new_item_offset;
- update_subscriptions();
- jumps_need_update = true;
+ dispatch(value, oldval, index) { + // Only care about position, others are constants + this.apply_position(value); + if(this.position != value){ + // widget refused or apply different value, force it back + this.apply_hmi_value(2, this.position);