merge default
authorEdouard Tisserant <edouard.tisserant@gmail.com>
Tue, 17 May 2022 14:49:23 +0200
changeset 3466 eadb3a85ceb7
parent 3465 2f5b5c14a650 (current diff)
parent 3455 2716cd8e498d (diff)
child 3467 d8b9ed779728
merge default
svghmi/ui.py
--- 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'" {