diff -r a76ee5307bb7 -r 86ecd8374dae Beremiz_service.py --- 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 = ''' + +''' + + + 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()