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: 
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 = '''<?xml version="1.0" encoding="utf-8"?>
Edouard@1438: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
Edouard@1438: "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
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()
Edouard@1438:         #print reason
Edouard@1438:         #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@1595:     print _("HTTP interface port :"), port
Edouard@1439:     return website
Edouard@1438: 
andrej@1736: 
Edouard@1438: class statuslistener:
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