--- a/runtime/NevowServer.py Mon Aug 06 13:14:50 2018 +0200
+++ b/runtime/NevowServer.py Mon Aug 06 14:30:43 2018 +0200
@@ -26,10 +26,19 @@
from __future__ import absolute_import
from __future__ import print_function
import os
-from nevow import appserver, inevow, tags, loaders, athena
+import platform
+from zope.interface import implements
+from nevow import appserver, inevow, tags, loaders, athena, url, rend
from nevow.page import renderer
+from formless import annotate
+from formless import webform
+from formless import configurable
from twisted.internet import reactor
+
import util.paths as paths
+from runtime.loglevels import LogLevels, LogLevelsDict
+
+PAGE_TITLE = 'Beremiz Runtime Web Interface'
xhtml_header = '''<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
@@ -37,6 +46,7 @@
'''
WorkingDir = None
+_PySrv = None
class PLCHMI(athena.LiveElement):
@@ -49,7 +59,6 @@
def HMIinitialisation(self):
self.HMIinitialised(None)
-
class DefaultPLCStartedHMI(PLCHMI):
docFactory = loaders.stan(
tags.div(render=tags.directive('liveElement'))[
@@ -119,19 +128,113 @@
for child in self.liveFragmentChildren[:]:
child.detach()
+class ConfigurableBindings(configurable.Configurable):
+
+ def __init__(self):
+ configurable.Configurable.__init__(self, None)
+ self.bindingsNames = []
+
+ def getBindingNames(self, ctx):
+ return self.bindingsNames
+
+ def addExtension(self, name, desc, fields, btnlabel, callback):
+ def _bind(ctx):
+ return annotate.MethodBinding(
+ 'action_'+name,
+ annotate.Method(arguments=[
+ annotate.Argument(*field)
+ for field in fields],
+ label = desc),
+ action = btnlabel)
+ setattr(self, 'bind_'+name, _bind)
+
+ setattr(self, 'action_'+name, callback)
+
+ self.bindingsNames.append(name)
+
+ConfigurableSettings = ConfigurableBindings()
+
+class ISettings(annotate.TypedInterface):
+ platform = annotate.String(label = _("Platform"),
+ default = platform.system() + " " + platform.release(),
+ immutable = True)
+ # TODO version ?
+
+ def sendLogMessage(
+ ctx = annotate.Context(),
+ level = annotate.Choice(LogLevels,
+ required=True,
+ label=_("Log message level")),
+ message = annotate.String(label=_("Message text"))):
+ pass
+ sendLogMessage = annotate.autocallable(sendLogMessage,
+ label=_("Send a message to the log"),
+ action=_("Send"))
+
+customSettingsURLs = {
+}
+
+class SettingsPage(rend.Page):
+ # We deserve a slash
+ addSlash = True
+
+ # This makes webform_css url answer some default CSS
+ child_webform_css = webform.defaultCSS
+
+ implements(ISettings)
+
+
+ docFactory = loaders.stan([tags.html[
+ tags.head[
+ tags.title[_("Beremiz Runtime Settings")],
+ tags.link(rel='stylesheet',
+ type='text/css',
+ href=url.here.child("webform_css"))
+ ],
+ tags.body[
+ tags.h1["Runtime settings:"],
+ webform.renderForms('staticSettings'),
+ tags.h2["Extensions settings:"],
+ webform.renderForms('dynamicSettings'),
+ ]]])
+
+ def configurable_staticSettings(self, ctx):
+ return configurable.TypedInterfaceConfigurable(self)
+
+ def configurable_dynamicSettings(self, ctx):
+ return ConfigurableSettings
+
+ def sendLogMessage(self, level, message, **kwargs):
+ level = LogLevelsDict[level]
+ if _PySrv.plcobj is not None:
+ _PySrv.plcobj.LogMessage(level, "Web form log message: " + message )
+
+ def locateChild(self, ctx, segments):
+ if segments[0] in customSettingsURLs :
+ return customSettingsURLs[segments[0]](ctx, segments)
+ return super(SettingsPage, self).locateChild(ctx, segments)
+
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.head(render=tags.directive('liveglue'))[
+ tags.title[PAGE_TITLE],
+ tags.link(rel='stylesheet',
+ type='text/css',
+ href=url.here.child("webform_css"))
+ ],
tags.body[
tags.div[
- tags.div(render=tags.directive("MainPage"))
+ tags.div(render=tags.directive("MainPage")),
]]]])
MainPage = MainPage()
PLCHMI = PLCHMI
+ def child_settings(self, context):
+ return SettingsPage()
+
def __init__(self, plcState=False, *a, **kw):
super(WebInterface, self).__init__(*a, **kw)
self.jsModules.mapping[u'WebInterface'] = paths.AbsNeighbourFile(__file__, 'webinterface.js')
@@ -184,6 +287,7 @@
# print "We will be called back when the client disconnects"
+
def RegisterWebsite(port):
website = WebInterface()
site = appserver.NevowSite(website)
@@ -209,3 +313,9 @@
def website_statuslistener_factory(site):
return statuslistener(site).listen
+
+
+def SetServer(pysrv):
+ global _PySrv
+ _PySrv = pysrv
+