|
1 import os |
|
2 from nevow import rend, appserver, inevow, tags, loaders, athena |
|
3 from nevow.page import renderer |
|
4 from twisted.python import util |
|
5 |
|
6 xhtml_header = '''<?xml version="1.0" encoding="utf-8"?> |
|
7 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" |
|
8 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> |
|
9 ''' |
|
10 |
|
11 class PLCHMI(athena.LiveElement): |
|
12 |
|
13 initialised = False |
|
14 |
|
15 def HMIinitialised(self, result): |
|
16 self.initialised = True |
|
17 |
|
18 def HMIinitialisation(self): |
|
19 self.HMIinitialised(None) |
|
20 |
|
21 class DefaultPLCStartedHMI(PLCHMI): |
|
22 docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[ |
|
23 tags.h1["PLC IS NOW STARTED"], |
|
24 ]) |
|
25 |
|
26 class PLCStoppedHMI(PLCHMI): |
|
27 docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[ |
|
28 tags.h1["PLC IS STOPPED"], |
|
29 ]) |
|
30 |
|
31 class MainPage(athena.LiveElement): |
|
32 jsClass = u"WebInterface.PLC" |
|
33 docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[ |
|
34 tags.div(id='content')[ |
|
35 tags.div(render = tags.directive('PLCElement')), |
|
36 ]]) |
|
37 |
|
38 def __init__(self, *a, **kw): |
|
39 athena.LiveElement.__init__(self, *a, **kw) |
|
40 self.pcl_state = False |
|
41 self.HMI = None |
|
42 self.resetPLCStartedHMI() |
|
43 |
|
44 def setPLCState(self, state): |
|
45 self.pcl_state = state |
|
46 if self.HMI is not None: |
|
47 self.callRemote('updateHMI') |
|
48 |
|
49 def setPLCStartedHMI(self, hmi): |
|
50 self.PLCStartedHMIClass = hmi |
|
51 |
|
52 def resetPLCStartedHMI(self): |
|
53 self.PLCStartedHMIClass = DefaultPLCStartedHMI |
|
54 |
|
55 def getHMI(self): |
|
56 return self.HMI |
|
57 |
|
58 def HMIexec(self, function, *args, **kwargs): |
|
59 if self.HMI is not None: |
|
60 getattr(self.HMI, function, lambda:None)(*args, **kwargs) |
|
61 athena.expose(HMIexec) |
|
62 |
|
63 def resetHMI(self): |
|
64 self.HMI = None |
|
65 |
|
66 def PLCElement(self, ctx, data): |
|
67 return self.getPLCElement() |
|
68 renderer(PLCElement) |
|
69 |
|
70 def getPLCElement(self): |
|
71 self.detachFragmentChildren() |
|
72 if self.pcl_state: |
|
73 f = self.PLCStartedHMIClass() |
|
74 else: |
|
75 f = PLCStoppedHMI() |
|
76 f.setFragmentParent(self) |
|
77 self.HMI = f |
|
78 return f |
|
79 athena.expose(getPLCElement) |
|
80 |
|
81 def detachFragmentChildren(self): |
|
82 for child in self.liveFragmentChildren[:]: |
|
83 child.detach() |
|
84 |
|
85 class WebInterface(athena.LivePage): |
|
86 |
|
87 docFactory = loaders.stan([tags.raw(xhtml_header), |
|
88 tags.html(xmlns="http://www.w3.org/1999/xhtml")[ |
|
89 tags.head(render=tags.directive('liveglue')), |
|
90 tags.body[ |
|
91 tags.div[ |
|
92 tags.div( render = tags.directive( "MainPage" )) |
|
93 ]]]]) |
|
94 MainPage = MainPage() |
|
95 PLCHMI = PLCHMI |
|
96 |
|
97 def __init__(self, plcState=False, *a, **kw): |
|
98 super(WebInterface, self).__init__(*a, **kw) |
|
99 self.jsModules.mapping[u'WebInterface'] = util.sibpath(__file__, 'webinterface.js') |
|
100 self.plcState = plcState |
|
101 self.MainPage.setPLCState(plcState) |
|
102 |
|
103 def getHMI(self): |
|
104 return self.MainPage.getHMI() |
|
105 |
|
106 def LoadHMI(self, hmi, jsmodules): |
|
107 for name, path in jsmodules.iteritems(): |
|
108 self.jsModules.mapping[name] = os.path.join(WorkingDir, path) |
|
109 self.MainPage.setPLCStartedHMI(hmi) |
|
110 |
|
111 def UnLoadHMI(self): |
|
112 self.MainPage.resetPLCStartedHMI() |
|
113 |
|
114 def PLCStarted(self): |
|
115 self.plcState = True |
|
116 self.MainPage.setPLCState(True) |
|
117 |
|
118 def PLCStopped(self): |
|
119 self.plcState = False |
|
120 self.MainPage.setPLCState(False) |
|
121 |
|
122 def renderHTTP(self, ctx): |
|
123 """ |
|
124 Force content type to fit with SVG |
|
125 """ |
|
126 req = inevow.IRequest(ctx) |
|
127 req.setHeader('Content-type', 'application/xhtml+xml') |
|
128 return super(WebInterface, self).renderHTTP(ctx) |
|
129 |
|
130 def render_MainPage(self, ctx, data): |
|
131 f = self.MainPage |
|
132 f.setFragmentParent(self) |
|
133 return ctx.tag[f] |
|
134 |
|
135 def child_(self, ctx): |
|
136 self.MainPage.detachFragmentChildren() |
|
137 return WebInterface(plcState=self.plcState) |
|
138 |
|
139 def beforeRender(self, ctx): |
|
140 d = self.notifyOnDisconnect() |
|
141 d.addErrback(self.disconnected) |
|
142 |
|
143 def disconnected(self, reason): |
|
144 self.MainPage.resetHMI() |
|
145 #print reason |
|
146 #print "We will be called back when the client disconnects" |
|
147 |
|
148 def RegisterWebsite(reactor): |
|
149 website = WebInterface() |
|
150 site = appserver.NevowSite(website) |
|
151 |
|
152 website_port = 8009 |
|
153 website_port_range = 10 |
|
154 |
|
155 listening = False |
|
156 port_offset = 0 |
|
157 while not listening and port_offset < website_port_range: |
|
158 try: |
|
159 reactor.listenTCP(website_port + port_offset, site) |
|
160 listening = True |
|
161 print "Http interface port :",website_port + port_offset |
|
162 return website |
|
163 except: # TODO narrow exception |
|
164 port_offset += 1 |
|
165 |
|
166 return None |
|
167 |
|
168 class statuslistener: |
|
169 def __init__(self, site): |
|
170 self.oldstate = None |
|
171 self.site = site |
|
172 |
|
173 def listen(self, state): |
|
174 if state != self.oldstate: |
|
175 {'Started': self.site.PLCStarted, |
|
176 'Stopped': self.site.PLCStopped}[state]() |
|
177 self.oldstate = state |
|
178 |
|
179 def website_statuslistener_factory(site): |
|
180 return statuslistener(site).listen |