--- 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):