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.
--- 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=" "/>
+ <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>