runtime/NevowServer.py
changeset 1438 19ebe96b41c0
child 1439 a68cd4253259
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/runtime/NevowServer.py	Thu Feb 05 01:35:02 2015 +0100
@@ -0,0 +1,180 @@
+import os
+from nevow import rend, appserver, inevow, tags, loaders, athena
+from nevow.page import renderer
+from twisted.python import util
+
+xhtml_header = '''<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+'''
+
+class PLCHMI(athena.LiveElement):
+
+    initialised = False
+
+    def HMIinitialised(self, result):
+        self.initialised = True
+
+    def HMIinitialisation(self):
+        self.HMIinitialised(None)
+
+class DefaultPLCStartedHMI(PLCHMI):
+    docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[
+                                            tags.h1["PLC IS NOW STARTED"],
+                                            ])
+
+class PLCStoppedHMI(PLCHMI):
+    docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[
+                                            tags.h1["PLC IS STOPPED"],
+                                            ])
+
+class MainPage(athena.LiveElement):
+    jsClass = u"WebInterface.PLC"
+    docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[
+                                                    tags.div(id='content')[
+                                                    tags.div(render = tags.directive('PLCElement')),
+                                                    ]])
+
+    def __init__(self, *a, **kw):
+        athena.LiveElement.__init__(self, *a, **kw)
+        self.pcl_state = False
+        self.HMI = None
+        self.resetPLCStartedHMI()
+
+    def setPLCState(self, state):
+        self.pcl_state = state
+        if self.HMI is not None:
+            self.callRemote('updateHMI')
+
+    def setPLCStartedHMI(self, hmi):
+        self.PLCStartedHMIClass = hmi
+
+    def resetPLCStartedHMI(self):
+        self.PLCStartedHMIClass = DefaultPLCStartedHMI
+
+    def getHMI(self):
+        return self.HMI
+
+    def HMIexec(self, function, *args, **kwargs):
+        if self.HMI is not None:
+            getattr(self.HMI, function, lambda:None)(*args, **kwargs)
+    athena.expose(HMIexec)
+
+    def resetHMI(self):
+        self.HMI = None
+
+    def PLCElement(self, ctx, data):
+        return self.getPLCElement()
+    renderer(PLCElement)
+
+    def getPLCElement(self):
+        self.detachFragmentChildren()
+        if self.pcl_state:
+            f = self.PLCStartedHMIClass()
+        else:
+            f = PLCStoppedHMI()
+        f.setFragmentParent(self)
+        self.HMI = f
+        return f
+    athena.expose(getPLCElement)
+
+    def detachFragmentChildren(self):
+        for child in self.liveFragmentChildren[:]:
+            child.detach()
+
+class WebInterface(athena.LivePage):
+
+    docFactory = loaders.stan([tags.raw(xhtml_header),
+                                tags.html(xmlns="http://www.w3.org/1999/xhtml")[
+                                    tags.head(render=tags.directive('liveglue')),
+                                    tags.body[
+                                        tags.div[
+                                                tags.div( render = tags.directive( "MainPage" ))
+                                                ]]]])
+    MainPage = MainPage()
+    PLCHMI = PLCHMI
+
+    def __init__(self, plcState=False, *a, **kw):
+        super(WebInterface, self).__init__(*a, **kw)
+        self.jsModules.mapping[u'WebInterface'] = util.sibpath(__file__, 'webinterface.js')
+        self.plcState = plcState
+        self.MainPage.setPLCState(plcState)
+
+    def getHMI(self):
+        return self.MainPage.getHMI()
+
+    def LoadHMI(self, hmi, jsmodules):
+        for name, path in jsmodules.iteritems():
+            self.jsModules.mapping[name] = os.path.join(WorkingDir, path)
+        self.MainPage.setPLCStartedHMI(hmi)
+
+    def UnLoadHMI(self):
+        self.MainPage.resetPLCStartedHMI()
+
+    def PLCStarted(self):
+        self.plcState = True
+        self.MainPage.setPLCState(True)
+
+    def PLCStopped(self):
+        self.plcState = False
+        self.MainPage.setPLCState(False)
+
+    def renderHTTP(self, ctx):
+        """
+        Force content type to fit with SVG
+        """
+        req = inevow.IRequest(ctx)
+        req.setHeader('Content-type', 'application/xhtml+xml')
+        return super(WebInterface, self).renderHTTP(ctx)
+
+    def render_MainPage(self, ctx, data):
+        f = self.MainPage
+        f.setFragmentParent(self)
+        return ctx.tag[f]
+
+    def child_(self, ctx):
+        self.MainPage.detachFragmentChildren()
+        return WebInterface(plcState=self.plcState)
+
+    def beforeRender(self, ctx):
+        d = self.notifyOnDisconnect()
+        d.addErrback(self.disconnected)
+
+    def disconnected(self, reason):
+        self.MainPage.resetHMI()
+        #print reason
+        #print "We will be called back when the client disconnects"
+
+def RegisterWebsite(reactor):
+    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
+
+class statuslistener:
+    def __init__(self, site):
+        self.oldstate = None
+        self.site = site
+
+    def listen(self, state):
+        if state != self.oldstate:
+            {'Started': self.site.PLCStarted,
+            'Stopped': self.site.PLCStopped}[state]()
+            self.oldstate = state
+
+def website_statuslistener_factory(site):
+    return statuslistener(site).listen