Beremiz_service.py
changeset 368 86ecd8374dae
parent 362 181231bf275f
child 369 bd54d41a7573
--- a/Beremiz_service.py	Fri Aug 07 18:27:50 2009 +0200
+++ b/Beremiz_service.py	Mon Aug 10 14:42:54 2009 +0200
@@ -34,12 +34,13 @@
            -h        - print this help text and quit
            -a        - autostart PLC (0:disable 1:enable)
            -x        - enable/disable wxTaskbarIcon (0:disable 1:enable)
+           -t        - enable/disable Twisted web interface (0:disable 1:enable)
            
            working_dir - directory where are stored PLC files
 """%sys.argv[0]
 
 try:
-    opts, argv = getopt.getopt(sys.argv[1:], "i:p:n:x:a:h")
+    opts, argv = getopt.getopt(sys.argv[1:], "i:p:n:x:t:a:h")
 except getopt.GetoptError, err:
     # print help information and exit:
     print str(err) # will print something like "option -a not recognized"
@@ -56,6 +57,8 @@
 autostart = False
 enablewx = True
 havewx = False
+enabletwisted = True
+havetwisted = False
 
 for o, a in opts:
     if o == "-h":
@@ -71,6 +74,8 @@
         name = a
     elif o == "-x":
         enablewx = int(a)
+    elif o == "-t":
+        enabletwisted = int(a)
     elif o == "-a":
         autostart = int(a)
     else:
@@ -426,7 +431,7 @@
     return callable(*args,**kwargs)
 
 class Server():
-    def __init__(self, name, ip, port, workdir, argv, autostart=False, statuschange=None, evaluator=default_evaluator):
+    def __init__(self, name, ip, port, workdir, argv, autostart=False, statuschange=None, evaluator=default_evaluator, website=None):
         self.continueloop = True
         self.daemon = None
         self.name = name
@@ -439,6 +444,7 @@
         self.autostart = autostart
         self.statuschange = statuschange
         self.evaluator = evaluator
+        self.website = website
     
     def Loop(self):
         while self.continueloop:
@@ -454,7 +460,7 @@
     def Start(self):
         pyro.initServer()
         self.daemon=pyro.Daemon(host=self.ip, port=self.port)
-        self.plcobj = PLCObject(self.workdir, self.daemon, self.argv, self.statuschange, self.evaluator)
+        self.plcobj = PLCObject(self.workdir, self.daemon, self.argv, self.statuschange, self.evaluator, self.website)
         uri = self.daemon.connect(self.plcobj,"PLCObject")
     
         print "The daemon runs on port :",self.port
@@ -481,7 +487,161 @@
             self.servicepublisher.UnRegisterService()
             del self.servicepublisher
         self.daemon.shutdown(True)
-        
+
+if enabletwisted:
+    try:
+        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
+        
+        havetwisted = True
+    except:
+        havetwisted = False
+
+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 DefaultPLCStartedHMI(athena.LiveElement):
+        docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[                                    
+                                             tags.h1["PLC IS NOW STARTED"],
+                                             ])
+    class PLCStoppedHMI(athena.LiveElement):
+        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(executeOnHMI)
+        
+        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()
+            self.HMI = f
+            f.setFragmentParent(self)
+            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()
+
+        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, plc, jsmodules):
+            for name, path in jsmodules.iteritems():
+                self.jsModules.mapping[name] = os.path.join(WorkingDir, path)
+            self.MainPage.setPLCStarted(plc)
+        
+        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)
+    res = WebInterface()
+    site = appserver.NevowSite(res)
+    reactor.listenTCP(8009, site)
+else:
+    res = None
 
 if havewx:
     from threading import Semaphore
@@ -510,12 +670,17 @@
             wx_eval_lock.acquire()
         return eval_res
 
-    pyroserver = Server(name, ip, port, WorkingDir, argv, autostart, statuschange, evaluator)
+    pyroserver = Server(name, ip, port, WorkingDir, argv, autostart, statuschange, evaluator, res)
     taskbar_instance = BeremizTaskBarIcon(pyroserver)
     
     pyro_thread=Thread(target=pyroserver.Loop)
     pyro_thread.start()
+else:
+    pyroserver = Server(name, ip, port, WorkingDir, argv, autostart, website=res)
+
+if havetwisted:
+    reactor.run()
+elif havewx:
     app.MainLoop()
 else:
-    pyroserver = Server(name, ip, port, WorkingDir, argv, autostart)
     pyroserver.Loop()