Added stub code for runtime WAMP client. Added runtime command line switch to select WAMP url and Nevow web site port. Web port is now fixed, next port number is not tested if bind fails.
authorEdouard Tisserant
Thu, 05 Feb 2015 23:32:31 +0100
changeset 1439 a68cd4253259
parent 1438 19ebe96b41c0
child 1440 e8daabf2c438
Added stub code for runtime WAMP client. Added runtime command line switch to select WAMP url and Nevow web site port. Web port is now fixed, next port number is not tested if bind fails.
Beremiz_service.py
runtime/NevowServer.py
runtime/WampClient.py
tests/wamp/.crossbar/config.json
tests/wamp/README
tests/wamp/beremiz.xml
tests/wamp/plc.xml
tests/wamp/py_ext_0@py_ext/baseconfnode.xml
tests/wamp/py_ext_0@py_ext/pyfile.xml
--- a/Beremiz_service.py	Thu Feb 05 01:35:02 2015 +0100
+++ b/Beremiz_service.py	Thu Feb 05 23:32:31 2015 +0100
@@ -36,13 +36,15 @@
            -a        - autostart PLC (0:disable 1:enable) (default:0)
            -x        - enable/disable wxTaskbarIcon (0:disable 1:enable) (default:1)
            -t        - enable/disable Twisted web interface (0:disable 1:enable) (default:1)
+           -w        - web server port or "off" (default:8009)
+           -c        - WAMP client config file or "off" (default:wampconf.json)
            -e        - python extension (absolute path .py)
 
            working_dir - directory where are stored PLC files
 """%sys.argv[0]
 
 try:
-    opts, argv = getopt.getopt(sys.argv[1:], "i:p:n:x:t:a:e:h")
+    opts, argv = getopt.getopt(sys.argv[1:], "i:p:n:x:t:a:w:c:e:h")
 except getopt.GetoptError, err:
     # print help information and exit:
     print str(err) # will print something like "option -a not recognized"
@@ -52,6 +54,8 @@
 # default values
 given_ip = None
 port = 3000
+webport = 8009
+wampconf = "wampconf.json"
 servicename = None
 autostart = False
 enablewx = True
@@ -82,6 +86,10 @@
         enabletwisted = int(a)
     elif o == "-a":
         autostart = int(a)
+    elif o == "-w":
+        webport = None if a == "off" else int(a)
+    elif o == "-c":
+        wampconf = None if a == "off" else a
     elif o == "-e":
         extensions.append(a)
     else:
@@ -433,15 +441,23 @@
     if havewx:
         reactor.registerWxApp(app)
 
-    # TODO add command line switch
-    try:
-        import runtime.NevowServer as NS
-        website = NS.RegisterWebsite(reactor)
-        pyruntimevars["website"] = website
-        statuschange.append(NS.website_statuslistener_factory(website))
-    except:
-        print "Nevow Web service failed."
-
+    if webport is not None :
+        try:
+            import runtime.NevowServer as NS
+            website = NS.RegisterWebsite(webport)
+            pyruntimevars["website"] = website
+            statuschange.append(NS.website_statuslistener_factory(website))
+        except Exception, e:
+            print "Nevow Web service failed.", e
+
+    if wampconf is not None :
+        try:
+            import runtime.WampClient as WC
+            WC.RegisterWampClient(wampconf)
+            pyruntimevars["wampsession"] = WC.GetSession
+            registerserverto.append(WC.SetServer)
+        except Exception, e:
+            print "WAMP client startup failed.", e
 
 if havewx:
     from threading import Semaphore
--- a/runtime/NevowServer.py	Thu Feb 05 01:35:02 2015 +0100
+++ b/runtime/NevowServer.py	Thu Feb 05 23:32:31 2015 +0100
@@ -2,6 +2,7 @@
 from nevow import rend, appserver, inevow, tags, loaders, athena
 from nevow.page import renderer
 from twisted.python import util
+from twisted.internet import reactor
 
 xhtml_header = '''<?xml version="1.0" encoding="utf-8"?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
@@ -145,25 +146,14 @@
         #print reason
         #print "We will be called back when the client disconnects"
 
-def RegisterWebsite(reactor):
+def RegisterWebsite(port):
     website = WebInterface()
     site = appserver.NevowSite(website)
 
-    website_port = 8009
-    website_port_range = 10
-
     listening = False
-    port_offset = 0
-    while not listening and port_offset < website_port_range:
-        try:
-            reactor.listenTCP(website_port + port_offset, site)
-            listening = True
-            print "Http interface port :",website_port + port_offset
-            return website
-        except: # TODO narrow exception
-            port_offset += 1
-
-    return None
+    reactor.listenTCP(port, site)
+    print "Http interface port :",port
+    return website
 
 class statuslistener:
     def __init__(self, site):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/runtime/WampClient.py	Thu Feb 05 23:32:31 2015 +0100
@@ -0,0 +1,74 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys
+from twisted.python import log
+
+from twisted.internet import reactor, ssl
+from autobahn.twisted import wamp
+from autobahn.twisted.websocket import WampWebSocketClientFactory, connectWS
+from autobahn.wamp import types
+import json
+
+_WampSession = None
+_PySrv = None
+
+class WampSession(wamp.ApplicationSession):
+    def onJoin(self, details):
+        global _WampSession
+        _WampSession = self
+        print 'WAMP session joined by :', self.config.extra["ID"]
+
+    def onLeave(self, details):
+        global _WampSession
+        _WampSession = None
+        print 'WAMP session left'
+
+
+def RegisterWampClient(wampconf):
+
+    WSClientConf = json.load(open(wampconf))
+
+    ## TODO log to PLC console instead
+    ## 0) start logging to console
+    log.startLogging(sys.stdout)
+
+    ## 1) create a WAMP application session factory
+    component_config = types.ComponentConfig(
+        realm = WSClientConf["realm"],
+        extra = {"ID":WSClientConf["ID"]})
+    session_factory = wamp.ApplicationSessionFactory(
+        config = component_config)
+    session_factory.session = WampSession
+
+    ## TODO select optimum serializer for passing session lists
+    ## optional: use specific set of serializers
+    #from autobahn.wamp.serializer import *
+    #serializers = []
+    ##serializers.append(JsonSerializer(batched = True))
+    ##serializers.append(MsgPackSerializer(batched = True))
+    #serializers.append(JsonSerializer())
+    ##serializers.append(MsgPackSerializer())
+    serializers = None
+
+    ## 2) create a WAMP-over-WebSocket transport client factory
+    transport_factory = WampWebSocketClientFactory(
+        session_factory,
+        url = WSClientConf["url"],
+        serializers = serializers,
+        debug = False,
+        debug_wamp = False)
+
+    ## 3) start the client from a Twisted endpoint
+    conn = connectWS(transport_factory)
+    print "WAMP clien connecting to :",WSClientConf["url"]
+    return conn
+
+def GetSession():
+    global _WampSession
+    return _WampSession
+
+def SetServer(pysrv):
+    global _PySrv
+    _PySrv = pysrv
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/wamp/.crossbar/config.json	Thu Feb 05 23:32:31 2015 +0100
@@ -0,0 +1,42 @@
+
+{
+   "controller": {
+   },
+   "workers": [
+      {
+         "type": "router",
+         "options": {
+            "pythonpath": [".."]
+         },
+         "realms": [
+            {
+               "name": "Automation",
+               "roles": [
+                  {
+                     "name": "anonymous",
+                     "permissions": [
+                        {
+                           "uri": "*",
+                           "publish": true,
+                           "subscribe": true,
+                           "call": true,
+                           "register": true
+                        }
+                     ]
+                  }
+               ]
+            }
+         ],
+         "transports": [
+            {
+               "type": "websocket",
+               "endpoint": {
+                  "type": "tcp",
+                  "port": 8888
+               },
+               "url": "ws://127.0.0.1:8888/"
+            }
+         ]
+      }
+   ]
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/wamp/README	Thu Feb 05 23:32:31 2015 +0100
@@ -0,0 +1,12 @@
+This project contains wamp client config to be loaded at runtime startup.  
+
+project_files/wampconf.json
+
+wampconf.json is in "Project Files", so it is copied to runtime's working directory, and then loaded after program transfer + runtime restart.
+
+Otherwise, wamp config file path can be forced :
+./Beremiz_service.py -c  /path/to/my/wampconf.json /working/dir
+
+Crossbar test router configuration is available in .crossbar directory. Start with :
+crossbar -d start
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/wamp/beremiz.xml	Thu Feb 05 23:32:31 2015 +0100
@@ -0,0 +1,4 @@
+<?xml version='1.0' encoding='utf-8'?>
+<BeremizRoot xmlns:xsd="http://www.w3.org/2001/XMLSchema" URI_location="PYRO://127.0.0.1:3000">
+  <TargetType/>
+</BeremizRoot>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/wamp/plc.xml	Thu Feb 05 23:32:31 2015 +0100
@@ -0,0 +1,118 @@
+<?xml version='1.0' encoding='utf-8'?>
+<project xmlns:ns1="http://www.plcopen.org/xml/tc6_0201" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.plcopen.org/xml/tc6_0201">
+  <fileHeader companyName="Beremiz" productName="Beremiz" productVersion="1" creationDateTime="2015-02-05T11:44:55" contentDescription=" &#10;&#10;"/>
+  <contentHeader name="WAMPTest" modificationDateTime="2015-02-05T13:45:38">
+    <coordinateInfo>
+      <fbd>
+        <scaling x="0" y="0"/>
+      </fbd>
+      <ld>
+        <scaling x="0" y="0"/>
+      </ld>
+      <sfc>
+        <scaling x="0" y="0"/>
+      </sfc>
+    </coordinateInfo>
+  </contentHeader>
+  <types>
+    <dataTypes/>
+    <pous>
+      <pou name="program0" pouType="program">
+        <interface>
+          <localVars>
+            <variable name="LocalVar0">
+              <type>
+                <DINT/>
+              </type>
+            </variable>
+          </localVars>
+          <externalVars>
+            <variable name="PyVar0">
+              <type>
+                <DINT/>
+              </type>
+            </variable>
+            <variable name="PyVar1">
+              <type>
+                <DINT/>
+              </type>
+            </variable>
+          </externalVars>
+        </interface>
+        <body>
+          <FBD>
+            <inVariable localId="1" executionOrderId="0" height="25" width="55" negated="false">
+              <position x="144" y="70"/>
+              <connectionPointOut>
+                <relPosition x="55" y="12"/>
+              </connectionPointOut>
+              <expression>PyVar0</expression>
+            </inVariable>
+            <block localId="3" typeName="ADD" executionOrderId="0" height="60" width="65">
+              <position x="245" y="52"/>
+              <inputVariables>
+                <variable formalParameter="IN1">
+                  <connectionPointIn>
+                    <relPosition x="0" y="30"/>
+                    <connection refLocalId="1">
+                      <position x="245" y="82"/>
+                      <position x="199" y="82"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
+                <variable formalParameter="IN2">
+                  <connectionPointIn>
+                    <relPosition x="0" y="50"/>
+                    <connection refLocalId="4">
+                      <position x="245" y="102"/>
+                      <position x="228" y="102"/>
+                      <position x="228" y="113"/>
+                      <position x="198" y="113"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
+              </inputVariables>
+              <inOutVariables/>
+              <outputVariables>
+                <variable formalParameter="OUT">
+                  <connectionPointOut>
+                    <relPosition x="65" y="30"/>
+                  </connectionPointOut>
+                </variable>
+              </outputVariables>
+            </block>
+            <inVariable localId="4" executionOrderId="0" height="25" width="73" negated="false">
+              <position x="125" y="101"/>
+              <connectionPointOut>
+                <relPosition x="73" y="12"/>
+              </connectionPointOut>
+              <expression>LocalVar0</expression>
+            </inVariable>
+            <outVariable localId="2" executionOrderId="0" height="25" width="55" negated="false">
+              <position x="344" y="70"/>
+              <connectionPointIn>
+                <relPosition x="0" y="12"/>
+                <connection refLocalId="3" formalParameter="OUT">
+                  <position x="344" y="82"/>
+                  <position x="310" y="82"/>
+                </connection>
+              </connectionPointIn>
+              <expression>PyVar1</expression>
+            </outVariable>
+          </FBD>
+        </body>
+      </pou>
+    </pous>
+  </types>
+  <instances>
+    <configurations>
+      <configuration name="config">
+        <resource name="resource1">
+          <task name="Task0" priority="0" interval="T#100ms">
+            <pouInstance name="Instance0" typeName="program0"/>
+          </task>
+        </resource>
+      </configuration>
+    </configurations>
+  </instances>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/wamp/py_ext_0@py_ext/baseconfnode.xml	Thu Feb 05 23:32:31 2015 +0100
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='utf-8'?>
+<BaseParams xmlns:xsd="http://www.w3.org/2001/XMLSchema" IEC_Channel="0" Name="py_ext_0"/>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/wamp/py_ext_0@py_ext/pyfile.xml	Thu Feb 05 23:32:31 2015 +0100
@@ -0,0 +1,22 @@
+<?xml version='1.0' encoding='utf-8'?>
+<PyFile xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+  <variables>
+    <variable name="PyVar0" type="DINT"/>
+    <variable name="PyVar1" type="DINT"/>
+  </variables>
+  <globals>
+    <xhtml:p><![CDATA[]]></xhtml:p>
+  </globals>
+  <init>
+    <xhtml:p><![CDATA[]]></xhtml:p>
+  </init>
+  <cleanup>
+    <xhtml:p><![CDATA[]]></xhtml:p>
+  </cleanup>
+  <start>
+    <xhtml:p><![CDATA[]]></xhtml:p>
+  </start>
+  <stop>
+    <xhtml:p><![CDATA[]]></xhtml:p>
+  </stop>
+</PyFile>