--- 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" > «.»
--- 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
--- 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);
--- 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)
--- 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,
--- 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'" {