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