runtime/NevowServer.py
changeset 2279 70143c20d2c0
parent 2260 74205edac761
child 2262 4195545e2d17
--- a/runtime/NevowServer.py	Fri Aug 10 17:45:33 2018 +0300
+++ b/runtime/NevowServer.py	Fri Aug 10 18:07:38 2018 +0300
@@ -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 as platform_module
+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):
@@ -120,21 +130,132 @@
             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_module.system() +
+                               " " + platform_module.release(),
+                               immutable=True)
+
+    # TODO version ?
+
+    # pylint: disable=no-self-argument
+    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')
+        self.jsModules.mapping[u'WebInterface'] = paths.AbsNeighbourFile(
+            __file__, 'webinterface.js')
         self.plcState = plcState
         self.MainPage.setPLCState(plcState)
 
@@ -194,6 +315,7 @@
 
 
 class statuslistener(object):
+
     def __init__(self, site):
         self.oldstate = None
         self.site = site
@@ -209,3 +331,8 @@
 
 def website_statuslistener_factory(site):
     return statuslistener(site).listen
+
+
+def SetServer(pysrv):
+    global _PySrv
+    _PySrv = pysrv