5 # Copyright (C) 2019: Edouard TISSERANT |
5 # Copyright (C) 2019: Edouard TISSERANT |
6 # See COPYING file for copyrights details. |
6 # See COPYING file for copyrights details. |
7 |
7 |
8 from __future__ import absolute_import |
8 from __future__ import absolute_import |
9 import errno |
9 import errno |
|
10 from threading import RLock, Timer |
10 |
11 |
11 from twisted.web.server import Site |
12 from twisted.web.server import Site |
12 from twisted.web.resource import Resource |
13 from twisted.web.resource import Resource |
13 from twisted.internet import reactor |
14 from twisted.internet import reactor |
14 from twisted.web.static import File |
15 from twisted.web.static import File |
18 from autobahn.twisted.resource import WebSocketResource |
19 from autobahn.twisted.resource import WebSocketResource |
19 |
20 |
20 # TODO multiclient : |
21 # TODO multiclient : |
21 # session list lock |
22 # session list lock |
22 # svghmi_sessions = [] |
23 # svghmi_sessions = [] |
|
24 # svghmi_watchdogs = [] |
23 |
25 |
24 svghmi_session = None |
26 svghmi_session = None |
|
27 svghmi_watchdog = None |
25 |
28 |
26 svghmi_send_collect = PLCBinary.svghmi_send_collect |
29 svghmi_send_collect = PLCBinary.svghmi_send_collect |
27 svghmi_send_collect.restype = ctypes.c_int # error or 0 |
30 svghmi_send_collect.restype = ctypes.c_int # error or 0 |
28 svghmi_send_collect.argtypes = [ |
31 svghmi_send_collect.argtypes = [ |
29 ctypes.POINTER(ctypes.c_uint32), # size |
32 ctypes.POINTER(ctypes.c_uint32), # size |
59 svghmi_session = None |
62 svghmi_session = None |
60 self.protocol_instance.sendClose(WebSocketProtocol.CLOSE_STATUS_CODE_NORMAL) |
63 self.protocol_instance.sendClose(WebSocketProtocol.CLOSE_STATUS_CODE_NORMAL) |
61 |
64 |
62 def onMessage(self, msg): |
65 def onMessage(self, msg): |
63 # pass message to the C side recieve_message() |
66 # pass message to the C side recieve_message() |
64 svghmi_recv_dispatch(len(msg), msg) |
67 return svghmi_recv_dispatch(len(msg), msg) |
65 |
68 |
66 # TODO multiclient : pass client index as well |
69 # TODO multiclient : pass client index as well |
67 |
|
68 |
70 |
69 def sendMessage(self, msg): |
71 def sendMessage(self, msg): |
70 self.protocol_instance.sendMessage(msg, True) |
72 self.protocol_instance.sendMessage(msg, True) |
71 return 0 |
73 return 0 |
|
74 |
|
75 class Watchdog(object): |
|
76 def __init__(self, initial_timeout, callback): |
|
77 self._callback = callback |
|
78 self.lock = RLock() |
|
79 self.initial_timeout = initial_timeout |
|
80 self.callback = callback |
|
81 with self.lock: |
|
82 self._start() |
|
83 |
|
84 def _start(self): |
|
85 self.timer = Timer(self.initial_timeout, self.trigger) |
|
86 self.timer.start() |
|
87 |
|
88 def _stop(self): |
|
89 if self.timer is not None: |
|
90 self.timer.cancel() |
|
91 self.timer = None |
|
92 |
|
93 def cancel(self): |
|
94 with self.lock: |
|
95 self._stop() |
|
96 |
|
97 def feed(self): |
|
98 with self.lock: |
|
99 self._stop() |
|
100 self._start() |
|
101 |
|
102 def trigger(self): |
|
103 self._callback() |
|
104 self.feed() |
72 |
105 |
73 class HMIProtocol(WebSocketServerProtocol): |
106 class HMIProtocol(WebSocketServerProtocol): |
74 |
107 |
75 def __init__(self, *args, **kwargs): |
108 def __init__(self, *args, **kwargs): |
76 self._hmi_session = None |
109 self._hmi_session = None |
83 def onClose(self, wasClean, code, reason): |
116 def onClose(self, wasClean, code, reason): |
84 self._hmi_session = None |
117 self._hmi_session = None |
85 |
118 |
86 def onMessage(self, msg, isBinary): |
119 def onMessage(self, msg, isBinary): |
87 assert(self._hmi_session is not None) |
120 assert(self._hmi_session is not None) |
88 self._hmi_session.onMessage(msg) |
121 |
|
122 result = self._hmi_session.onMessage(msg) |
|
123 if result == 1 : # was heartbeat |
|
124 if svghmi_watchdog is not None: |
|
125 svghmi_watchdog.feed() |
89 |
126 |
90 class HMIWebSocketServerFactory(WebSocketServerFactory): |
127 class HMIWebSocketServerFactory(WebSocketServerFactory): |
91 protocol = HMIProtocol |
128 protocol = HMIProtocol |
92 |
129 |
93 svghmi_root = None |
130 svghmi_root = None |
112 else: |
149 else: |
113 # this happens when finishing |
150 # this happens when finishing |
114 break |
151 break |
115 |
152 |
116 |
153 |
117 |
154 def watchdog_trigger(): |
|
155 print("SVGHMI watchdog trigger") |
|
156 |
118 |
157 |
119 # Called by PLCObject at start |
158 # Called by PLCObject at start |
120 def _runtime_svghmi0_start(): |
159 def _runtime_svghmi0_start(): |
121 global svghmi_listener, svghmi_root, svghmi_send_thread |
160 global svghmi_listener, svghmi_root, svghmi_send_thread, svghmi_watchdog |
122 |
161 |
123 svghmi_root = Resource() |
162 svghmi_root = Resource() |
124 svghmi_root.putChild("ws", WebSocketResource(HMIWebSocketServerFactory())) |
163 svghmi_root.putChild("ws", WebSocketResource(HMIWebSocketServerFactory())) |
125 |
164 |
126 svghmi_listener = reactor.listenTCP(8008, Site(svghmi_root)) |
165 svghmi_listener = reactor.listenTCP(8008, Site(svghmi_root)) |
127 |
166 |
128 # start a thread that call the C part of SVGHMI |
167 # start a thread that call the C part of SVGHMI |
129 svghmi_send_thread = Thread(target=SendThreadProc, name="SVGHMI Send") |
168 svghmi_send_thread = Thread(target=SendThreadProc, name="SVGHMI Send") |
130 svghmi_send_thread.start() |
169 svghmi_send_thread.start() |
131 |
170 |
|
171 svghmi_watchdog = Watchdog(5, watchdog_trigger) |
132 |
172 |
133 # Called by PLCObject at stop |
173 # Called by PLCObject at stop |
134 def _runtime_svghmi0_stop(): |
174 def _runtime_svghmi0_stop(): |
135 global svghmi_listener, svghmi_root, svghmi_send_thread, svghmi_session |
175 global svghmi_listener, svghmi_root, svghmi_send_thread, svghmi_session, svghmi_watchdog |
|
176 |
|
177 if svghmi_watchdog is not None: |
|
178 svghmi_watchdog.cancel() |
|
179 svghmi_watchdog = None |
|
180 |
136 if svghmi_session is not None: |
181 if svghmi_session is not None: |
137 svghmi_session.close() |
182 svghmi_session.close() |
138 svghmi_root.delEntity("ws") |
183 svghmi_root.delEntity("ws") |
139 svghmi_root = None |
184 svghmi_root = None |
140 svghmi_listener.stopListening() |
185 svghmi_listener.stopListening() |