Moved twisted/nevow/athena away from Berermiz_service.py + some minor cleanup
authorEdouard Tisserant
Thu, 05 Feb 2015 01:35:02 +0100
changeset 1438 19ebe96b41c0
parent 1437 04177743b066
child 1439 a68cd4253259
Moved twisted/nevow/athena away from Berermiz_service.py + some minor cleanup
Beremiz_service.py
runtime/NevowServer.py
runtime/PLCObject.py
--- a/Beremiz_service.py	Mon Feb 02 23:45:30 2015 +0100
+++ b/Beremiz_service.py	Thu Feb 05 01:35:02 2015 +0100
@@ -340,7 +340,10 @@
     return res
 
 class Server():
-    def __init__(self, servicename, ip_addr, port, workdir, argv, autostart=False, statuschange=None, evaluator=default_evaluator, website=None):
+    def __init__(self, servicename, ip_addr, port,
+                 workdir, argv, autostart=False,
+                 statuschange=None, evaluator=default_evaluator,
+                 website=None):
         self.continueloop = True
         self.daemon = None
         self.servicename = servicename
@@ -371,7 +374,9 @@
     def Start(self):
         pyro.initServer()
         self.daemon=pyro.Daemon(host=self.ip_addr, port=self.port)
-        self.plcobj = PLCObject(self.workdir, self.daemon, self.argv, self.statuschange, self.evaluator, self.website)
+        self.plcobj = PLCObject(self.workdir, self.daemon, self.argv,
+                                self.statuschange, self.evaluator,
+                                self.website)
         uri = self.daemon.connect(self.plcobj,"PLCObject")
 
         print "Pyro port :",self.port
@@ -412,185 +417,42 @@
             if havewx:
                 from twisted.internet import wxreactor
                 wxreactor.install()
-            from twisted.internet import reactor, task
-            from twisted.python import log, util
-            from nevow import rend, appserver, inevow, tags, loaders, athena
-            from nevow.page import renderer
+            from twisted.internet import reactor
 
             havetwisted = True
         except:
-            print "Twisted unavailable !"
+            print "Twisted unavailable."
             havetwisted = False
 
+pyruntimevars = {}
+statuschange = []
+registerserverto = []
+
 if havetwisted:
 
-    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__, os.path.join('runtime', '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"
-
     if havewx:
         reactor.registerWxApp(app)
-    website = WebInterface()
-    site = appserver.NevowSite(website)
-
-    website_port = 8009
-    listening = False
-    while not listening:
-        try:
-            reactor.listenTCP(website_port, site)
-            listening = True
-        except:
-            website_port += 1
-    print "Http interface port :",website_port
-else:
-    website = None
+
+    # 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 havewx:
     from threading import Semaphore
     wx_eval_lock = Semaphore(0)
     main_thread = currentThread()
 
-    def statuschange(status):
+    def statuschangeTskBar(status):
         wx.CallAfter(taskbar_instance.UpdateIcon,status)
 
+    statuschange.append(statuschangeTskBar)
+
     def wx_evaluator(obj, *args, **kwargs):
         tocall,args,kwargs = obj.call
         obj.res = default_evaluator(tocall, *args, **kwargs)
@@ -607,10 +469,18 @@
             wx_eval_lock.acquire()
             return o.res
 
-    pyroserver = Server(servicename, given_ip, port, WorkingDir, argv, autostart, statuschange, evaluator, website)
+    pyroserver = Server(servicename, given_ip, port,
+                        WorkingDir, argv, autostart,
+                        statuschange, evaluator, pyruntimevars)
+
     taskbar_instance = BeremizTaskBarIcon(pyroserver, enablewx)
 else:
-    pyroserver = Server(servicename, given_ip, port, WorkingDir, argv, autostart, website=website)
+    pyroserver = Server(servicename, given_ip, port,
+                        WorkingDir, argv, autostart,
+                        statuschange, pyruntimevars=pyruntimevars)
+
+for registrar in registerserverto :
+    registrar(pyroserver)
 
 # Exception hooks s
 import threading, traceback
--- /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
--- a/runtime/PLCObject.py	Mon Feb 02 23:45:30 2015 +0100
+++ b/runtime/PLCObject.py	Thu Feb 05 01:35:02 2015 +0100
@@ -51,7 +51,7 @@
     sys.stdout.flush()
 
 class PLCObject(pyro.ObjBase):
-    def __init__(self, workingdir, daemon, argv, statuschange, evaluator, website):
+    def __init__(self, workingdir, daemon, argv, statuschange, evaluator, pyruntimevars):
         pyro.ObjBase.__init__(self)
         self.evaluator = evaluator
         self.argv = [workingdir] + argv # force argv[0] to be "path" to exec...
@@ -65,7 +65,7 @@
         self.daemon = daemon
         self.statuschange = statuschange
         self.hmi_frame = None
-        self.website = website
+        self.pyruntimevars = pyruntimevars
         self._loading_error = None
         self.python_runtime_vars = None
         self.TraceThread = None
@@ -85,7 +85,8 @@
 
     def StatusChange(self):
         if self.statuschange is not None:
-            self.statuschange(self.PLCStatus)
+            for callee in self.statuschange:
+                callee(self.PLCStatus)
 
     def LogMessage(self, *args):
         if len(args) == 2:
@@ -262,8 +263,9 @@
     def PythonRuntimeInit(self):
         MethodNames = ["init", "start", "stop", "cleanup"]
         self.python_runtime_vars = globals().copy()
+        self.python_runtime_vars.update(self.pyruntimevars)
+
         self.python_runtime_vars["WorkingDir"] = self.workingdir
-        self.python_runtime_vars["website"] = self.website
         for methodname in MethodNames :
             self.python_runtime_vars["_runtime_%s"%methodname] = []
         self.python_runtime_vars["PLCObject"] = self
@@ -300,17 +302,12 @@
 
         self.PythonRuntimeCall("init")
 
-        if self.website is not None:
-            self.website.PLCStarted()
 
 
     def PythonRuntimeCleanup(self):
         if self.python_runtime_vars is not None:
             self.PythonRuntimeCall("cleanup")
 
-        if self.website is not None:
-            self.website.PLCStopped()
-
         self.python_runtime_vars = None
 
     def PythonThreadProc(self):