SVGHMI: More configuration parameters : network interface, TCP port, URL path and watchdog enabling. svghmi
authorEdouard Tisserant <edouard.tisserant@gmail.com>
Mon, 05 Jul 2021 10:51:02 +0200
branchsvghmi
changeset 3269 5d174cdf4d98
parent 3268 d22782b9591f
child 3270 38f7122ccbf9
SVGHMI: More configuration parameters : network interface, TCP port, URL path and watchdog enabling.
svghmi/svghmi.py
svghmi/svghmi_server.py
tests/svghmi/py_ext_0@py_ext/pyfile.xml
tests/svghmi/svghmi_0@svghmi/confnode.xml
--- a/svghmi/svghmi.py	Thu Jul 01 14:33:14 2021 +0200
+++ b/svghmi/svghmi.py	Mon Jul 05 10:51:02 2021 +0200
@@ -279,11 +279,15 @@
     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
       <xsd:element name="SVGHMI">
         <xsd:complexType>
-          <xsd:attribute name="OnStart" type="xsd:string" use="optional"/>
-          <xsd:attribute name="OnStop" type="xsd:string" use="optional"/>
-          <xsd:attribute name="OnWatchdog" type="xsd:string" use="optional"/>
-          <xsd:attribute name="WatchdogInitial" type="xsd:integer" use="optional"/>
-          <xsd:attribute name="WatchdogInterval" type="xsd:integer" use="optional"/>
+          <xsd:attribute name="OnStart" type="xsd:string" use="optional" default="chromium {url}"/>
+          <xsd:attribute name="OnStop" type="xsd:string" use="optional" default="echo 'please close chromium window at {url}'"/>
+          <xsd:attribute name="EnableWatchdog" type="xsd:boolean" use="optional" default="false"/>
+          <xsd:attribute name="OnWatchdog" type="xsd:string" use="optional" default="chromium {url}"/>
+          <xsd:attribute name="WatchdogInitial" type="xsd:integer" use="optional" default="30"/>
+          <xsd:attribute name="WatchdogInterval" type="xsd:integer" use="optional" default="5"/>
+          <xsd:attribute name="Port" type="xsd:integer" use="optional" default="8008"/>
+          <xsd:attribute name="Interface" type="xsd:string" use="optional" default="localhost"/>
+          <xsd:attribute name="Path" type="xsd:string" use="optional" default=""/>
         </xsd:complexType>
       </xsd:element>
     </xsd:schema>
@@ -532,50 +536,96 @@
 
         res += ((target_fname, open(target_path, "rb")),)
 
+        port = self.GetParamsAttributes("SVGHMI.Port")["value"]
+        interface = self.GetParamsAttributes("SVGHMI.Interface")["value"]
+        path = self.GetParamsAttributes("SVGHMI.Path")["value"].format(name=view_name)
+        enable_watchdog = self.GetParamsAttributes("SVGHMI.EnableWatchdog")["value"]
+        url="http://"+interface+("" if port==80 else (":"+str(port))
+            ) + (("/"+path) if path else ""
+            ) + ("#watchdog" if enable_watchdog else "")
+
         svghmi_cmds = {}
         for thing in ["Start", "Stop", "Watchdog"]:
              given_command = self.GetParamsAttributes("SVGHMI.On"+thing)["value"]
              svghmi_cmds[thing] = (
                 "Popen(" +
-                repr(shlex.split(given_command.format(port="8008", name=view_name))) +
+                repr(shlex.split(given_command.format(
+                    port=port, 
+                    name=view_name,
+                    url=url))) +
                 ")") if given_command else "pass # no command given"
 
         runtimefile_path = os.path.join(buildpath, "runtime_%s_svghmi_.py" % location_str)
         runtimefile = open(runtimefile_path, 'w')
         runtimefile.write("""
 # TODO : multiple watchdog (one for each svghmi instance)
-def svghmi_watchdog_trigger():
+def svghmi_{location}_watchdog_trigger():
     {svghmi_cmds[Watchdog]}
 
 svghmi_watchdog = None
 
 def _runtime_{location}_svghmi_start():
-    global svghmi_watchdog
+    global svghmi_watchdog, svghmi_servers
+
+    srv = svghmi_servers.get("{interface}:{port}", None)
+    if srv is not None:
+        svghmi_root, svghmi_listener, path_list = srv 
+        if '{path}' in path_list:
+            raise Exception("SVGHMI {view_name}: path {path} already used on {interface}:{port}")
+    else:
+        svghmi_root = Resource()
+        svghmi_root.putChild("ws", WebSocketResource(HMIWebSocketServerFactory()))
+
+        svghmi_listener = reactor.listenTCP({port}, Site(svghmi_root), interface='{interface}')
+        path_list = []
+        svghmi_servers["{interface}:{port}"] = (svghmi_root, svghmi_listener, path_list)
+
     svghmi_root.putChild(
-        '{view_name}',
+        '{path}',
         NoCacheFile('{xhtml}',
-        defaultType='application/xhtml+xml'))
+            defaultType='application/xhtml+xml'))
+
+    path_list.append("{path}")
 
     {svghmi_cmds[Start]}
 
-    svghmi_watchdog = Watchdog(
-        {watchdog_initial}, 
-        {watchdog_interval}, 
-        svghmi_watchdog_trigger)
+    if {enable_watchdog}:
+        if svghmi_watchdog is None:
+            svghmi_watchdog = Watchdog(
+                {watchdog_initial}, 
+                {watchdog_interval}, 
+                svghmi_{location}_watchdog_trigger)
+        else:
+            raise Exception("SVGHMI {view_name}: only one watchdog allowed")
+
 
 def _runtime_{location}_svghmi_stop():
-    global svghmi_watchdog
+    global svghmi_watchdog, svghmi_servers
+
     if svghmi_watchdog is not None:
         svghmi_watchdog.cancel()
         svghmi_watchdog = None
 
-    svghmi_root.delEntity('{view_name}')
+    svghmi_root, svghmi_listener, path_list = svghmi_servers["{interface}:{port}"]
+    svghmi_root.delEntity('{path}')
+
+    path_list.remove('{path}')
+
+    if len(path_list)==0:
+        svghmi_root.delEntity("ws")
+        svghmi_listener.stopListening()
+        svghmi_servers.pop("{interface}:{port}")
+
     {svghmi_cmds[Stop]}
 
         """.format(location=location_str,
                    xhtml=target_fname,
                    view_name=view_name,
                    svghmi_cmds=svghmi_cmds,
+                   port = port,
+                   interface = interface,
+                   path = path,
+                   enable_watchdog = enable_watchdog,
                    watchdog_initial = self.GetParamsAttributes("SVGHMI.WatchdogInitial")["value"],
                    watchdog_interval = self.GetParamsAttributes("SVGHMI.WatchdogInterval")["value"],
                    ))
--- a/svghmi/svghmi_server.py	Thu Jul 01 14:33:14 2021 +0200
+++ b/svghmi/svghmi_server.py	Mon Jul 05 10:51:02 2021 +0200
@@ -142,8 +142,7 @@
 class HMIWebSocketServerFactory(WebSocketServerFactory):
     protocol = HMIProtocol
 
-svghmi_root = None
-svghmi_listener = None
+svghmi_servers = {}
 svghmi_send_thread = None
 
 def SendThreadProc():
@@ -165,19 +164,14 @@
             # this happens when finishing
             break
 
-
-def watchdog_trigger():
-    print("SVGHMI watchdog trigger")
-    
+def AddPathToSVGHMIServers(path, factory):
+    for k,v in svghmi_servers.iteritems():
+        svghmi_root, svghmi_listener, path_list = v
+        svghmi_root.putChild(path, factory())
 
 # Called by PLCObject at start
 def _runtime_00_svghmi_start():
-    global svghmi_listener, svghmi_root, svghmi_send_thread
-
-    svghmi_root = Resource()
-    svghmi_root.putChild("ws", WebSocketResource(HMIWebSocketServerFactory()))
-
-    svghmi_listener = reactor.listenTCP(8008, Site(svghmi_root), interface='localhost')
+    global svghmi_send_thread
 
     # start a thread that call the C part of SVGHMI
     svghmi_send_thread = Thread(target=SendThreadProc, name="SVGHMI Send")
@@ -186,14 +180,10 @@
 
 # Called by PLCObject at stop
 def _runtime_00_svghmi_stop():
-    global svghmi_listener, svghmi_root, svghmi_send_thread, svghmi_session
+    global svghmi_send_thread, svghmi_session
 
     if svghmi_session is not None:
         svghmi_session.close()
-    svghmi_root.delEntity("ws")
-    svghmi_root = None
-    svghmi_listener.stopListening()
-    svghmi_listener = None
     # plc cleanup calls svghmi_(locstring)_cleanup and unlocks send thread
     svghmi_send_thread.join()
     svghmi_send_thread = None
--- a/tests/svghmi/py_ext_0@py_ext/pyfile.xml	Thu Jul 01 14:33:14 2021 +0200
+++ b/tests/svghmi/py_ext_0@py_ext/pyfile.xml	Mon Jul 05 10:51:02 2021 +0200
@@ -87,7 +87,7 @@
   <start>
     <xhtml:p><![CDATA[
 
-svghmi_root.putChild("alarms", AlarmJsonResource())
+AddPathToSVGHMIServers("alarms", AlarmJsonResource)
 
 
 ]]></xhtml:p>
--- a/tests/svghmi/svghmi_0@svghmi/confnode.xml	Thu Jul 01 14:33:14 2021 +0200
+++ b/tests/svghmi/svghmi_0@svghmi/confnode.xml	Mon Jul 05 10:51:02 2021 +0200
@@ -1,2 +1,2 @@
 <?xml version='1.0' encoding='utf-8'?>
-<SVGHMI xmlns:xsd="http://www.w3.org/2001/XMLSchema" OnWatchdog="echo Watchdog for {name} !" OnStart="chromium http://127.0.0.1:{port}/{name}" OnStop="echo Closing {name}" WatchdogInitial="10" WatchdogInterval="5"/>
+<SVGHMI xmlns:xsd="http://www.w3.org/2001/XMLSchema" OnWatchdog="echo Watchdog for {name} !" WatchdogInitial="10" WatchdogInterval="5" EnableWatchdog="true" Path="{name}"/>