svghmi/svghmi_server.py
branchsvghmi
changeset 2822 9101a72a1da0
parent 2819 3b99c908f43b
child 2823 d631f8671c75
equal deleted inserted replaced
2821:d92d201d22e1 2822:9101a72a1da0
     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()