andrej@1511: #!/usr/bin/env python andrej@1511: # -*- coding: utf-8 -*- andrej@1511: andrej@1667: # This file is part of Beremiz runtime. andrej@1511: # andrej@1511: # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD andrej@1680: # Copyright (C) 2017: Andrey Skvortsov andrej@1511: # andrej@1667: # See COPYING.Runtime file for copyrights details. andrej@1511: # andrej@1667: # This library is free software; you can redistribute it and/or andrej@1667: # modify it under the terms of the GNU Lesser General Public andrej@1667: # License as published by the Free Software Foundation; either andrej@1667: # version 2.1 of the License, or (at your option) any later version. andrej@1667: andrej@1667: # This library is distributed in the hope that it will be useful, andrej@1511: # but WITHOUT ANY WARRANTY; without even the implied warranty of andrej@1667: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU andrej@1667: # Lesser General Public License for more details. andrej@1667: andrej@1667: # You should have received a copy of the GNU Lesser General Public andrej@1667: # License along with this library; if not, write to the Free Software andrej@1667: # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA andrej@1511: andrej@1826: andrej@1826: from __future__ import print_function Edouard@1438: import os andrej@1680: import util.paths as paths Edouard@1438: from nevow import rend, appserver, inevow, tags, loaders, athena Edouard@1438: from nevow.page import renderer Edouard@1438: from twisted.python import util Edouard@1439: from twisted.internet import reactor Edouard@1438: Edouard@1438: xhtml_header = ''' Edouard@1438: Edouard@1438: ''' Edouard@1438: Edouard@1453: WorkingDir = None Edouard@1453: andrej@1736: Edouard@1438: class PLCHMI(athena.LiveElement): Edouard@1438: Edouard@1438: initialised = False Edouard@1438: Edouard@1438: def HMIinitialised(self, result): Edouard@1438: self.initialised = True Edouard@1438: Edouard@1438: def HMIinitialisation(self): Edouard@1438: self.HMIinitialised(None) Edouard@1438: andrej@1736: Edouard@1438: class DefaultPLCStartedHMI(PLCHMI): Edouard@1438: docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[ Edouard@1438: tags.h1["PLC IS NOW STARTED"], Edouard@1438: ]) Edouard@1438: andrej@1736: Edouard@1438: class PLCStoppedHMI(PLCHMI): Edouard@1438: docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[ Edouard@1438: tags.h1["PLC IS STOPPED"], Edouard@1438: ]) Edouard@1438: andrej@1736: Edouard@1438: class MainPage(athena.LiveElement): Edouard@1438: jsClass = u"WebInterface.PLC" Edouard@1438: docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[ Edouard@1438: tags.div(id='content')[ andrej@1776: tags.div(render=tags.directive('PLCElement')), Edouard@1438: ]]) Edouard@1438: Edouard@1438: def __init__(self, *a, **kw): Edouard@1438: athena.LiveElement.__init__(self, *a, **kw) Edouard@1438: self.pcl_state = False Edouard@1438: self.HMI = None Edouard@1438: self.resetPLCStartedHMI() Edouard@1438: Edouard@1438: def setPLCState(self, state): Edouard@1438: self.pcl_state = state Edouard@1438: if self.HMI is not None: Edouard@1438: self.callRemote('updateHMI') Edouard@1438: Edouard@1438: def setPLCStartedHMI(self, hmi): Edouard@1438: self.PLCStartedHMIClass = hmi Edouard@1438: Edouard@1438: def resetPLCStartedHMI(self): Edouard@1438: self.PLCStartedHMIClass = DefaultPLCStartedHMI Edouard@1438: Edouard@1438: def getHMI(self): Edouard@1438: return self.HMI Edouard@1438: Edouard@1438: def HMIexec(self, function, *args, **kwargs): Edouard@1438: if self.HMI is not None: andrej@1740: getattr(self.HMI, function, lambda: None)(*args, **kwargs) Edouard@1438: athena.expose(HMIexec) Edouard@1438: Edouard@1438: def resetHMI(self): Edouard@1438: self.HMI = None Edouard@1438: Edouard@1438: def PLCElement(self, ctx, data): Edouard@1438: return self.getPLCElement() Edouard@1438: renderer(PLCElement) Edouard@1438: Edouard@1438: def getPLCElement(self): Edouard@1438: self.detachFragmentChildren() Edouard@1438: if self.pcl_state: Edouard@1438: f = self.PLCStartedHMIClass() Edouard@1438: else: Edouard@1438: f = PLCStoppedHMI() Edouard@1438: f.setFragmentParent(self) Edouard@1438: self.HMI = f Edouard@1438: return f Edouard@1438: athena.expose(getPLCElement) Edouard@1438: Edouard@1438: def detachFragmentChildren(self): Edouard@1438: for child in self.liveFragmentChildren[:]: Edouard@1438: child.detach() Edouard@1438: andrej@1736: Edouard@1438: class WebInterface(athena.LivePage): Edouard@1438: Edouard@1438: docFactory = loaders.stan([tags.raw(xhtml_header), andrej@1767: tags.html(xmlns="http://www.w3.org/1999/xhtml")[ andrej@1767: tags.head(render=tags.directive('liveglue')), andrej@1767: tags.body[ andrej@1767: tags.div[ andrej@1767: tags.div(render=tags.directive("MainPage")) andrej@1767: ]]]]) Edouard@1438: MainPage = MainPage() Edouard@1438: PLCHMI = PLCHMI Edouard@1438: Edouard@1438: def __init__(self, plcState=False, *a, **kw): Edouard@1438: super(WebInterface, self).__init__(*a, **kw) Anton@1682: self.jsModules.mapping[u'WebInterface'] = paths.AbsNeighbourFile(__file__, 'webinterface.js') Edouard@1438: self.plcState = plcState Edouard@1438: self.MainPage.setPLCState(plcState) Edouard@1438: Edouard@1438: def getHMI(self): Edouard@1438: return self.MainPage.getHMI() Edouard@1438: Edouard@1438: def LoadHMI(self, hmi, jsmodules): Edouard@1438: for name, path in jsmodules.iteritems(): Edouard@1438: self.jsModules.mapping[name] = os.path.join(WorkingDir, path) Edouard@1438: self.MainPage.setPLCStartedHMI(hmi) Edouard@1438: Edouard@1438: def UnLoadHMI(self): Edouard@1438: self.MainPage.resetPLCStartedHMI() Edouard@1438: Edouard@1438: def PLCStarted(self): Edouard@1438: self.plcState = True Edouard@1438: self.MainPage.setPLCState(True) Edouard@1438: Edouard@1438: def PLCStopped(self): Edouard@1438: self.plcState = False Edouard@1438: self.MainPage.setPLCState(False) Edouard@1438: Edouard@1438: def renderHTTP(self, ctx): Edouard@1438: """ Edouard@1438: Force content type to fit with SVG Edouard@1438: """ Edouard@1438: req = inevow.IRequest(ctx) Edouard@1438: req.setHeader('Content-type', 'application/xhtml+xml') Edouard@1438: return super(WebInterface, self).renderHTTP(ctx) Edouard@1438: Edouard@1438: def render_MainPage(self, ctx, data): Edouard@1438: f = self.MainPage Edouard@1438: f.setFragmentParent(self) Edouard@1438: return ctx.tag[f] Edouard@1438: Edouard@1438: def child_(self, ctx): Edouard@1438: self.MainPage.detachFragmentChildren() Edouard@1438: return WebInterface(plcState=self.plcState) Edouard@1438: Edouard@1438: def beforeRender(self, ctx): Edouard@1438: d = self.notifyOnDisconnect() Edouard@1438: d.addErrback(self.disconnected) Edouard@1438: Edouard@1438: def disconnected(self, reason): Edouard@1438: self.MainPage.resetHMI() andrej@1782: # print reason andrej@1782: # print "We will be called back when the client disconnects" Edouard@1438: andrej@1736: Edouard@1439: def RegisterWebsite(port): Edouard@1438: website = WebInterface() Edouard@1438: site = appserver.NevowSite(website) Edouard@1438: Edouard@1438: listening = False Edouard@1439: reactor.listenTCP(port, site) andrej@1826: print(_('HTTP interface port :'), port) Edouard@1439: return website Edouard@1438: andrej@1736: andrej@1831: class statuslistener(object): Edouard@1438: def __init__(self, site): Edouard@1438: self.oldstate = None Edouard@1438: self.site = site Edouard@1438: Edouard@1438: def listen(self, state): Edouard@1438: if state != self.oldstate: Edouard@1453: action = {'Started': self.site.PLCStarted, Edouard@1453: 'Stopped': self.site.PLCStopped}.get(state, None) andrej@1756: if action is not None: andrej@1756: action() Edouard@1438: self.oldstate = state Edouard@1438: andrej@1736: Edouard@1438: def website_statuslistener_factory(site): Edouard@1438: return statuslistener(site).listen