merged default wxPython4
authorEdouard Tisserant <edouard.tisserant@gmail.com>
Sun, 13 Feb 2022 21:05:19 +0100
branchwxPython4
changeset 3426 c2c366964913
parent 3425 ee3b84d09ccf (current diff)
parent 3421 70ef15fca028 (diff)
child 3428 803ce245f72f
merged default
Beremiz_service.py
tests/projects/svghmi/svghmi_0@svghmi/svghmi.svg
--- a/Beremiz_service.py	Sun Feb 13 21:01:27 2022 +0100
+++ b/Beremiz_service.py	Sun Feb 13 21:05:19 2022 +0100
@@ -32,6 +32,7 @@
 import threading
 import shlex
 import traceback
+import threading
 from threading import Thread, Semaphore, Lock, currentThread
 from builtins import str as text
 from past.builtins import execfile
@@ -46,6 +47,14 @@
 from runtime.Stunnel import ensurePSK
 import util.paths as paths
 
+# In case system time is ajusted, it is better to use
+# monotonic timers for timers and other timeout based operations.
+# hot-patch threading module to force using monitonic time for all 
+# Thread/Timer/Event/Condition
+
+from runtime.monotonic_time import monotonic
+threading._time = monotonic
+
 try:
     from runtime.spawn_subprocess import Popen
 except ImportError:
--- a/ProjectController.py	Sun Feb 13 21:01:27 2022 +0100
+++ b/ProjectController.py	Sun Feb 13 21:05:19 2022 +0100
@@ -796,7 +796,7 @@
         # Compute offset before ST resulting of transformation from user POUs
         self.ProgramOffset = IECCodeContent.count("\n")
 
-        IECCodeContent += open(self._getIECgeneratedcodepath(), "r").read()
+        POUsIECCodeContent = open(self._getIECgeneratedcodepath(), "r").read()
 
         IECcodepath = self._getIECcodepath()
 
@@ -805,9 +805,11 @@
 
         with open(IECcodepath, "w") as plc_file:
             plc_file.write(IECCodeContent)
+            plc_file.write(POUsIECCodeContent)
 
         hasher = hashlib.md5()
         hasher.update(IECCodeContent)
+        hasher.update(POUsIECCodeContent)
         self.IECcodeDigest = hasher.hexdigest()
 
         return True
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/runtime/monotonic_time.py	Sun Feb 13 21:05:19 2022 +0100
@@ -0,0 +1,159 @@
+# vi: set ft=python sw=4 ts=4 et:
+
+'''monotonic time for Python 2 and 3, on Linux, FreeBSD, Mac OS X, and Windows.
+
+Copyright 2010, 2011, 2017 Gavin Beatty <gavinbeatty@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+'''
+
+from __future__ import print_function
+from __future__ import unicode_literals
+
+__author__ = 'Gavin Beatty <gavinbeatty@gmail.com>'
+__version__ = '2.1.0.dev0'
+__date__ = '2017-05-28'
+__all__ = ['monotonic']
+
+import contextlib
+import ctypes
+import errno
+import os
+import platform
+import sys
+import time
+
+_machine64 = (platform.machine(), sys.maxsize > 2**32)
+
+
+class _NS():
+    pass
+
+
+class _mach_timespec(ctypes.Structure):
+    _fields_ = [('tv_sec', ctypes.c_uint), ('tv_nsec', ctypes.c_int)]
+
+
+class _posix_timespec(ctypes.Structure):
+    _fields_ = [('tv_sec', ctypes.c_long), ('tv_nsec', ctypes.c_long)]
+
+
+def _timespec_to_seconds(ts):
+    return float(ts.tv_sec) + float(ts.tv_nsec) * 1e-9
+
+
+def _get_ctypes_libmacho_macho_functions():
+    libmacho = ctypes.CDLL('/usr/lib/system/libmacho.dylib', use_errno=True)
+    macho = _NS()
+    macho.get_host = libmacho.mach_host_self
+    macho.get_host.argtypes = []
+    macho.get_host.restype = ctypes.c_uint
+    macho.get_clock = libmacho.host_get_clock_service
+    macho.get_clock.argtypes = [ctypes.c_uint,
+                                ctypes.c_int,
+                                ctypes.POINTER(ctypes.c_uint)
+                                ]
+    macho.get_time = libmacho.clock_get_time
+    macho.get_time.argtypes = [ctypes.c_uint, ctypes.POINTER(_mach_timespec)]
+    macho.deallocate = libmacho.mach_port_deallocate
+    macho.deallocate.argtypes = [ctypes.c_uint, ctypes.c_uint]
+    return libmacho, macho
+
+
+def _get_ctypes_clock_gettime(library):
+    clock_gettime = ctypes.CDLL(library, use_errno=True).clock_gettime
+    clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(_posix_timespec)]
+    return clock_gettime
+
+
+def _call_ctypes_clock_gettime(clock_gettime, clockid):
+    timespec = _posix_timespec()
+    ret = clock_gettime(clockid, ctypes.pointer(timespec))
+    if int(ret) != 0:
+        errno_ = ctypes.get_errno()
+        raise OSError(errno_, os.strerror(errno_))
+    return timespec
+
+
+_py_monotonic = getattr(time, 'monotonic', None)
+if _py_monotonic is not None:
+    monotonic = _py_monotonic
+elif sys.platform.startswith('linux'):
+    _clock_gettime = _get_ctypes_clock_gettime('librt.so.1')
+
+    def monotonic():
+        clockid = ctypes.c_int(1)
+        timespec = _call_ctypes_clock_gettime(_clock_gettime, clockid)
+        return _timespec_to_seconds(timespec)
+elif sys.platform.startswith('freebsd'):
+    _clock_gettime = _get_ctypes_clock_gettime('libc.so')
+
+    def monotonic():
+        clockid = ctypes.c_int(4)
+        timespec = _call_ctypes_clock_gettime(_clock_gettime, clockid)
+        return _timespec_to_seconds(timespec)
+elif sys.platform.startswith('darwin') and _machine64 == ('x86_64', True):
+    _libmacho, _macho = _get_ctypes_libmacho_macho_functions()
+
+    @contextlib.contextmanager
+    def _deallocate(task, port):
+        try:
+            yield
+        finally:
+            if int(_macho.deallocate(task, port)) == 0:
+                return
+            errno_ = ctypes.get_errno()
+            raise OSError(errno_, os.strerror(errno_))
+
+    def monotonic():
+        task = ctypes.c_uint.in_dll(_libmacho, 'mach_task_self_')
+        host = _macho.get_host()
+        with _deallocate(task, host):
+            clock = ctypes.c_uint(0)
+            clockid = ctypes.c_int(0)
+            ret = _macho.get_clock(host, clockid, ctypes.pointer(clock))
+            if int(ret) != 0:
+                errno_ = ctypes.get_errno()
+                raise OSError(errno_, os.strerror(errno_))
+            with _deallocate(task, clock):
+                timespec = _mach_timespec()
+                ret = _macho.get_time(clock, ctypes.pointer(timespec))
+                if int(ret) != 0:
+                    errno_ = ctypes.get_errno()
+                    raise OSError(errno_, os.strerror(errno_))
+                return _timespec_to_seconds(timespec)
+elif sys.platform.startswith('win32'):
+    _GetTickCount = getattr(ctypes.windll.kernel32, 'GetTickCount64', None)
+
+    if _GetTickCount is not None:
+        _GetTickCount.restype = ctypes.c_uint64
+    else:
+        _GetTickCount = ctypes.windll.kernel32.GetTickCount
+        _GetTickCount.restype = ctypes.c_uint32
+
+    def monotonic():
+        return float(_GetTickCount()) * 1e-3
+else:
+    def monotonic():
+        msg = 'monotonic not supported on your platform'
+        raise OSError(errno.ENOSYS, msg)
+
+
+if __name__ == '__main__':
+    print(monotonic())
--- a/svghmi/gen_index_xhtml.xslt	Sun Feb 13 21:01:27 2022 +0100
+++ b/svghmi/gen_index_xhtml.xslt	Sun Feb 13 21:05:19 2022 +0100
@@ -1459,8 +1459,6 @@
 </xsl:text>
     <xsl:text>        this.element = id(elt_id);
 </xsl:text>
-    <xsl:text>        if(freq !== undefined) this.frequency = freq;
-</xsl:text>
     <xsl:text>        this.args = args;
 </xsl:text>
     <xsl:text>        this.indexes = indexes;
@@ -1477,6 +1475,8 @@
 </xsl:text>
     <xsl:text>        this.bound_unhinibit = this.unhinibit.bind(this);
 </xsl:text>
+    <xsl:text>        this.forced_frequency = freq;
+</xsl:text>
     <xsl:text>    }
 </xsl:text>
     <xsl:text>
@@ -2402,7 +2402,7 @@
 </xsl:text>
     <xsl:text>console.log("Entering state </xsl:text>
     <xsl:value-of select="@name"/>
-    <xsl:text>");
+    <xsl:text>", this.frequency);
 </xsl:text>
     <xsl:apply-templates mode="actions" select="*"/>
     <xsl:text>    }
@@ -8237,6 +8237,10 @@
 </xsl:text>
           <xsl:text>        }
 </xsl:text>
+          <xsl:text>        if(widget.forced_frequency !== undefined)
+</xsl:text>
+          <xsl:text>            widget.frequency = widget.forced_frequency;
+</xsl:text>
           <xsl:text>    });
 </xsl:text>
           <xsl:text>};
@@ -8801,8 +8805,6 @@
 </xsl:text>
           <xsl:text>function send_hmi_value(index, value) {
 </xsl:text>
-          <xsl:text>    console.log("send_hmi_value("+index+", "+value+")")
-</xsl:text>
           <xsl:text>    if(index &gt; last_remote_index){
 </xsl:text>
           <xsl:text>        updates.set(index, value);
@@ -8851,7 +8853,9 @@
 </xsl:text>
           <xsl:text>function apply_hmi_value(index, new_val) {
 </xsl:text>
-          <xsl:text>    console.log("apply_hmi_value("+index+", "+new_val+")")
+          <xsl:text>    // Similarly to previous comment, taking decision to update based 
+</xsl:text>
+          <xsl:text>    // on cache content is bad and can lead to inconsistency
 </xsl:text>
           <xsl:text>    /*let old_val = cache[index];*/
 </xsl:text>
--- a/svghmi/svghmi.js	Sun Feb 13 21:01:27 2022 +0100
+++ b/svghmi/svghmi.js	Sun Feb 13 21:05:19 2022 +0100
@@ -26,6 +26,8 @@
                 console.log(err);
             }
         }
+        if(widget.forced_frequency !== undefined)
+            widget.frequency = widget.forced_frequency;
     });
 };
 
--- a/svghmi/widget_button.ysl2	Sun Feb 13 21:01:27 2022 +0100
+++ b/svghmi/widget_button.ysl2	Sun Feb 13 21:05:19 2022 +0100
@@ -146,7 +146,7 @@
 }
 template "state", mode="actions" {
     |     «@name»_action(){
-    | console.log("Entering state «@name»");
+    // | console.log("Entering state «@name»", this.frequency);
     apply "*", mode="actions";
     |     }
 }
@@ -163,7 +163,6 @@
 
 function "generated_button_class" {
     param "fsm";
-    |     frequency = 5;
 
     |     display = "inactive";
     |     state = "init";
@@ -202,6 +201,7 @@
 
 
 widget_class("Button"){
+    |     frequency = 5;
     const "fsm","exsl:node-set($_button_fsm)";
     call "generated_button_class" with "fsm", "$fsm";
 }
@@ -211,6 +211,7 @@
 }
 
 widget_class("PushButton"){
+    |     frequency = 20;
     const "fsm","exsl:node-set($_push_button_fsm)";
     call "generated_button_class" with "fsm", "$fsm";
 }
--- a/svghmi/widgets_common.ysl2	Sun Feb 13 21:01:27 2022 +0100
+++ b/svghmi/widgets_common.ysl2	Sun Feb 13 21:05:19 2022 +0100
@@ -174,7 +174,6 @@
         constructor(elt_id, freq, args, indexes, minmaxes, members){
             this.element_id = elt_id;
             this.element = id(elt_id);
-            if(freq !== undefined) this.frequency = freq;
             this.args = args;
             this.indexes = indexes;
             this.minmaxes = minmaxes;
@@ -183,6 +182,7 @@
             this.inhibit = indexes.map(() => undefined);
             this.pending = indexes.map(() => undefined);
             this.bound_unhinibit = this.unhinibit.bind(this);
+            this.forced_frequency = freq;
         }
 
         unsub(){
--- a/tests/projects/svghmi/svghmi_0@svghmi/svghmi.svg	Sun Feb 13 21:01:27 2022 +0100
+++ b/tests/projects/svghmi/svghmi_0@svghmi/svghmi.svg	Sun Feb 13 21:05:19 2022 +0100
@@ -128,11 +128,11 @@
      inkscape:current-layer="hmi0"
      showgrid="false"
      units="px"
-     inkscape:zoom="0.40092402"
-     inkscape:cx="-239.69441"
-     inkscape:cy="-2504.0401"
-     inkscape:window-width="3840"
-     inkscape:window-height="2096"
+     inkscape:zoom="0.20046201"
+     inkscape:cx="1401.1703"
+     inkscape:cy="-1495.7332"
+     inkscape:window-width="1600"
+     inkscape:window-height="836"
      inkscape:window-x="0"
      inkscape:window-y="27"
      inkscape:window-maximized="1"
@@ -7612,7 +7612,7 @@
   <g
      transform="matrix(0.57180538,0,0,0.57180538,1234.9549,1536.0365)"
      id="g443-2"
-     inkscape:label="HMI:PushButton@/SELECTION"
+     inkscape:label="HMI:PushButton|50@/SELECTION"
      style="stroke-width:1">
     <rect
        style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#ff6600;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"