# HG changeset patch # User Edouard Tisserant # Date 1652791763 -7200 # Node ID eadb3a85ceb7f494a8c385511723878db32d8bd6 # Parent 2f5b5c14a650b4e661c45f302b8bf26ced3b5740# Parent 2716cd8e498d58b224a423c7f2a7d2793ffb0a1e merge default diff -r 2f5b5c14a650 -r eadb3a85ceb7 svghmi/parse_labels.ysl2 --- a/svghmi/parse_labels.ysl2 Tue May 17 13:22:20 2022 +0200 +++ b/svghmi/parse_labels.ysl2 Tue May 17 14:49:23 2022 +0200 @@ -44,7 +44,12 @@ if "$type" widget { attrib "id" > «$id» attrib "type" > «$type» - if "$freq" attrib "freq" > «$freq» + if "$freq" { + if "not(regexp:test($freq,'^[0-9]*(\.[0-9]+)?[smh]'))" { + error > Widget id:«$id» label:«$label» has wrong syntax of frequency forcing «$freq» + } + attrib "freq" > «$freq» + } foreach "str:split(substring-after($args, ':'), ':')" { arg { attrib "value" > «.» diff -r 2f5b5c14a650 -r eadb3a85ceb7 svghmi/sprintf.js --- a/svghmi/sprintf.js Tue May 17 13:22:20 2022 +0200 +++ b/svghmi/sprintf.js Tue May 17 14:49:23 2022 +0200 @@ -15,7 +15,7 @@ not_json: /[^j]/, text: /^[^\x25]+/, modulo: /^\x25{2}/, - placeholder: /^\x25(?:([1-9]\d*)\$|\(([^)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijostTuvxX])/, + placeholder: /^\x25(?:([1-9]\d*)\$|\(([^)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijostTuvxXD])/, key: /^([a-z_][a-z_\d]*)/i, key_access: /^\.([a-z_][a-z_\d]*)/i, index_access: /^\[(\d+)\]/, @@ -78,6 +78,51 @@ case 'i': arg = parseInt(arg, 10) break + case 'D': + /* + + select date format with width + select time format with precision + %D => 13:31 AM (default) + %1D => 13:31 AM + %.1D => 07/07/20 + %1.1D => 07/07/20, 13:31 AM + %1.2D => 07/07/20, 13:31:55 AM + %2.2D => May 5, 2022, 9:29:16 AM + %3.3D => May 5, 2022 at 9:28:16 AM GMT+2 + %4.4D => Thursday, May 5, 2022 at 9:26:59 AM Central European Summer Time + + see meaning of DateTimeFormat's options "datestyle" and "timestyle" in MDN + */ + + let [datestyle, timestyle] = [ph.width, ph.precision].map(val => { + 1: "short", + 2: "medium", + 3: "long", + 4: "full" + }[val]); + + if(timestyle === undefined && datestyle === undefined){ + timestyle = "short"; + } + + let options = { + dateStyle: datestyle, + timeStyle: timestyle, + hour12: false + } + + /* get lang from globals */ + let lang = get_current_lang_code(); + arg = Date(arg).toLocaleString('en-US', options); + + /* + TODO: select with padding char + a: absolute time and date (default) + r: relative time + */ + + break case 'j': arg = JSON.stringify(arg, null, ph.width ? parseInt(ph.width) : 0) break diff -r 2f5b5c14a650 -r eadb3a85ceb7 svghmi/svghmi.js --- a/svghmi/svghmi.js Tue May 17 13:22:20 2022 +0200 +++ b/svghmi/svghmi.js Tue May 17 14:49:23 2022 +0200 @@ -18,16 +18,7 @@ function init_widgets() { Object.keys(hmi_widgets).forEach(function(id) { let widget = hmi_widgets[id]; - let init = widget.init; - if(typeof(init) == "function"){ - try { - init.call(widget); - } catch(err) { - console.log(err); - } - } - if(widget.forced_frequency !== undefined) - widget.frequency = widget.forced_frequency; + widget.do_init(); }); }; @@ -264,6 +255,11 @@ } }); +// returns en_US, fr_FR or en_UK depending on selected language +function get_current_lang_code(){ + return cache[langcode_local_index]; +} + function setup_lang(){ let current_lang = cache[lang_local_index]; let new_lang = switch_langnum(current_lang); diff -r 2f5b5c14a650 -r eadb3a85ceb7 svghmi/ui.py --- a/svghmi/ui.py Tue May 17 13:22:20 2022 +0200 +++ b/svghmi/ui.py Tue May 17 14:49:23 2022 +0200 @@ -621,17 +621,20 @@ self.args_box.Show(len(args)!=0) for arg, prefillarg in izip(args,prefillargs): self.AddArgToSignature(arg, prefillarg) + + # TODO support predefined path count (as for XYGraph) paths = defs.findall("path") self.paths_box.Show(len(paths)!=0) for path in paths: self.AddPathToSignature(path) - for widget in widgets: - widget_type = widget.get("type") - for path in widget.iterchildren("path"): - path_value = path.get("value") - path_accepts = map( - str.strip, path.get("accepts", '')[1:-1].split(',')) + # # TODO DEAD CODE ? + # for widget in widgets: + # widget_type = widget.get("type") + # for path in widget.iterchildren("path"): + # path_value = path.get("value") + # path_accepts = map( + # str.strip, path.get("accepts", '')[1:-1].split(',')) self.main_panel.SetupScrolling(scroll_x=False) diff -r 2f5b5c14a650 -r eadb3a85ceb7 svghmi/widget_pathslider.ysl2 --- a/svghmi/widget_pathslider.ysl2 Tue May 17 13:22:20 2022 +0200 +++ b/svghmi/widget_pathslider.ysl2 Tue May 17 14:49:23 2022 +0200 @@ -1,8 +1,8 @@ // widget_pathslider.ysl2 widget_desc("PathSlider") { longdesc - || - PathSlider - + || + PathSlider - || shortdesc > Slide an SVG element along a path by dragging it @@ -10,7 +10,7 @@ path name="value" accepts="HMI_INT,HMI_REAL" > value path name="min" count="optional" accepts="HMI_INT,HMI_REAL" > min path name="max" count="optional" accepts="HMI_INT,HMI_REAL" > max - + arg name="min" count="optional" accepts="int,real" > minimum value arg name="max" count="optional" accepts="int,real" > maximum value } @@ -25,7 +25,7 @@ pathLength = undefined; precision = undefined; origPt = undefined; - + scanPath() { this.pathLength = this.path_elt.getTotalLength(); @@ -67,7 +67,7 @@ bestPoint = beforePoint, bestLength = beforeLength, bestDistance = beforeDistance; - } else if ((afterLength = bestLength + precision) <= this.pathLength && + } else if ((afterLength = bestLength + precision) <= this.pathLength && (afterDistance = distance2(afterPoint = this.path_elt.getPointAtLength(afterLength))) < bestDistance) { bestPoint = afterPoint, bestLength = afterLength, diff -r 2f5b5c14a650 -r eadb3a85ceb7 svghmi/widgets_common.ysl2 --- a/svghmi/widgets_common.ysl2 Tue May 17 13:22:20 2022 +0200 +++ b/svghmi/widgets_common.ysl2 Tue May 17 14:49:23 2022 +0200 @@ -85,7 +85,7 @@ const "freq" choose { when "$widget/@freq" - > «$widget/@freq» + > "«$widget/@freq»" otherwise > undefined } @@ -183,6 +183,37 @@ this.pending = indexes.map(() => undefined); this.bound_unhinibit = this.unhinibit.bind(this); this.forced_frequency = freq; + this.clip = true; + } + + do_init(){ + if(widget.forced_frequency !== undefined){ + let s = widget.forced_frequency; + /* + once every 10 seconds : 10s + once per minute : 1m + once per hour : 1h + once per day : 1d + */ + let unit = s.slice(-1); + let factor = { + "s":1, + "m":60, + "h":3600, + "d":86400}[unit]; + + widget.frequency = factor ? 1/(factor * Number(s.slice(0,-1))) + : Number(s); + } + + let init = this.init; + if(typeof(init) == "function"){ + try { + init.call(this); + } catch(err) { + console.log(err); + } + } } unsub(){ @@ -269,14 +300,16 @@ if(realindex == undefined) return undefined; let old_val = cache[realindex]; let new_val = eval_operation_string(old_val, opstr); - new_val = this.clip_min_max(index, new_val); + if(this.clip) + new_val = this.clip_min_max(index, new_val); return apply_hmi_value(realindex, new_val); } _apply_hmi_value(index, new_val) { let realindex = this.get_variable_index(index); if(realindex == undefined) return undefined; - new_val = this.clip_min_max(index, new_val); + if(this.clip) + new_val = this.clip_min_max(index, new_val); return apply_hmi_value(realindex, new_val); } @@ -395,8 +428,10 @@ param "hmi_element"; const "widget_type","@type"; foreach "str:split($labels)" { - const "name","."; - const "elt","$result_widgets[@id = $hmi_element/@id]//*[@inkscape:label=$name][1]"; + const "absolute", "starts-with(., '/')"; + const "name","substring(.,number($absolute)+1)"; + const "widget","$result_widgets[@id = $hmi_element/@id]"; + const "elt","($widget//*[not($absolute) and @inkscape:label=$name] | $widget/*[$absolute and @inkscape:label=$name])[1]"; choose { when "not($elt/@id)" { if "$mandatory='yes'" {