svghmi/svghmi_server.py
branchsvghmi
changeset 2799 f5da343b9b63
parent 2798 ddb2c4668a6b
child 2819 3b99c908f43b
equal deleted inserted replaced
2798:ddb2c4668a6b 2799:f5da343b9b63
     4 # This file is part of Beremiz
     4 # This file is part of Beremiz
     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 
    10 
    10 from twisted.web.server import Site
    11 from twisted.web.server import Site
    11 from twisted.web.resource import Resource
    12 from twisted.web.resource import Resource
    12 from twisted.internet import reactor
    13 from twisted.internet import reactor
    13 from twisted.web.static import File
    14 from twisted.web.static import File
    14 
    15 
    15 from autobahn.twisted.websocket import WebSocketServerFactory, WebSocketServerProtocol
    16 from autobahn.twisted.websocket import WebSocketServerFactory, WebSocketServerProtocol
       
    17 from autobahn.websocket.protocol import WebSocketProtocol
    16 from autobahn.twisted.resource import  WebSocketResource
    18 from autobahn.twisted.resource import  WebSocketResource
    17 
    19 
    18 # TODO multiclient :
    20 # TODO multiclient :
    19 # session list lock
    21 # session list lock
    20 # svghmi_sessions = []
    22 # svghmi_sessions = []
    23 
    25 
    24 svghmi_send_collect = PLCBinary.svghmi_send_collect
    26 svghmi_send_collect = PLCBinary.svghmi_send_collect
    25 svghmi_send_collect.restype = ctypes.c_int # error or 0
    27 svghmi_send_collect.restype = ctypes.c_int # error or 0
    26 svghmi_send_collect.argtypes = [
    28 svghmi_send_collect.argtypes = [
    27     ctypes.POINTER(ctypes.c_uint32),  # size
    29     ctypes.POINTER(ctypes.c_uint32),  # size
    28     ctypes.POINTER(ctypes.c_char_p)]  # data ptr
    30     ctypes.POINTER(ctypes.c_void_p)]  # data ptr
    29 # TODO multiclient : switch to arrays
    31 # TODO multiclient : switch to arrays
    30 
    32 
    31 svghmi_recv_dispatch = PLCBinary.svghmi_recv_dispatch
    33 svghmi_recv_dispatch = PLCBinary.svghmi_recv_dispatch
    32 svghmi_recv_dispatch.restype = ctypes.c_int # error or 0
    34 svghmi_recv_dispatch.restype = ctypes.c_int # error or 0
    33 svghmi_recv_dispatch.argtypes = [
    35 svghmi_recv_dispatch.argtypes = [
    36 # TODO multiclient : switch to arrays
    38 # TODO multiclient : switch to arrays
    37 
    39 
    38 class HMISession(object):
    40 class HMISession(object):
    39     def __init__(self, protocol_instance):
    41     def __init__(self, protocol_instance):
    40         global svghmi_session
    42         global svghmi_session
    41 
    43         
    42         # TODO: kill existing session for robustness
    44         # Single client :
    43         assert(svghmi_session is None)
    45         # Creating a new HMISession closes pre-existing HMISession
    44 
    46         if svghmi_session is not None:
       
    47             svghmi_session.close()
    45         svghmi_session = self
    48         svghmi_session = self
    46         self.protocol_instance = protocol_instance
    49         self.protocol_instance = protocol_instance
    47 
    50 
    48         # TODO multiclient :
    51         # TODO multiclient :
    49         # svghmi_sessions.append(self)
    52         # svghmi_sessions.append(self)
    50         # get a unique bit index amont other svghmi_sessions,
    53         # get a unique bit index amont other svghmi_sessions,
    51         # so that we can match flags passed by C->python callback
    54         # so that we can match flags passed by C->python callback
    52 
    55 
    53     def __del__(self):
    56     def close(self):
    54         global svghmi_session
    57         global svghmi_session
    55         assert(svghmi_session)
    58         if svghmi_session == self:
    56         svghmi_session = None
    59             svghmi_session = None
    57 
    60         self.protocol_instance.sendClose(WebSocketProtocol.CLOSE_STATUS_CODE_NORMAL)
    58         # TODO multiclient :
       
    59         # svghmi_sessions.remove(self)
       
    60 
    61 
    61     def onMessage(self, msg):
    62     def onMessage(self, msg):
    62         # pass message to the C side recieve_message()
    63         # pass message to the C side recieve_message()
    63         svghmi_recv_dispatch(len(msg), msg)
    64         svghmi_recv_dispatch(len(msg), msg)
    64 
    65 
    65         # TODO multiclient : pass client index as well
    66         # TODO multiclient : pass client index as well
    66 
    67 
    67 
    68 
    68     def sendMessage(self, msg):
    69     def sendMessage(self, msg):
    69         self.sendMessage(msg, True)
    70         self.protocol_instance.sendMessage(msg, True)
       
    71         return 0
    70 
    72 
    71 class HMIProtocol(WebSocketServerProtocol):
    73 class HMIProtocol(WebSocketServerProtocol):
    72 
    74 
    73     def __init__(self, *args, **kwargs):
    75     def __init__(self, *args, **kwargs):
    74         self._hmi_session = None
    76         self._hmi_session = None
    75         WebSocketServerProtocol.__init__(self, *args, **kwargs)
    77         WebSocketServerProtocol.__init__(self, *args, **kwargs)
    76 
    78 
    77     def onOpen(self):
    79     def onOpen(self):
       
    80         assert(self._hmi_session is None)
    78         self._hmi_session = HMISession(self)
    81         self._hmi_session = HMISession(self)
    79         print "open"
       
    80 
    82 
    81     def onClose(self, wasClean, code, reason):
    83     def onClose(self, wasClean, code, reason):
    82         del self._hmi_session
       
    83         self._hmi_session = None
    84         self._hmi_session = None
    84         print "close"
       
    85 
    85 
    86     def onMessage(self, msg, isBinary):
    86     def onMessage(self, msg, isBinary):
       
    87         assert(self._hmi_session is not None)
    87         self._hmi_session.onMessage(msg)
    88         self._hmi_session.onMessage(msg)
    88         # print msg
       
    89         #self.sendMessage(msg, binary)
       
    90 
    89 
    91 class HMIWebSocketServerFactory(WebSocketServerFactory):
    90 class HMIWebSocketServerFactory(WebSocketServerFactory):
    92     protocol = HMIProtocol
    91     protocol = HMIProtocol
    93 
    92 
    94 svghmi_root = None
    93 svghmi_root = None
    96 svghmi_send_thread = None
    95 svghmi_send_thread = None
    97 
    96 
    98 def SendThreadProc():
    97 def SendThreadProc():
    99    global svghmi_session
    98    global svghmi_session
   100    size = ctypes.c_uint32()
    99    size = ctypes.c_uint32()
   101    ptr = ctypes.c_char_p()
   100    ptr = ctypes.c_void_p()
   102    res = 0
   101    res = 0
   103    while svghmi_send_collect(ctypes.byref(size), ctypes.byref(ptr)) == 0 and \
   102    while True:
   104          svghmi_session is not None and \
   103        res=svghmi_send_collect(ctypes.byref(size), ctypes.byref(ptr))
   105          svghmi_session.sendMessage(ctypes.string_at(ptr,size)) == 0:
   104        if res == 0:
   106          pass
   105            # TODO multiclient : dispatch to sessions
       
   106            if svghmi_session is not None:
       
   107                svghmi_session.sendMessage(ctypes.string_at(ptr.value,size.value))
       
   108        elif res not in [errno.EAGAIN, errno.ENODATA]:
       
   109            break
   107 
   110 
   108        # TODO multiclient : dispatch to sessions
       
   109 
   111 
   110 
   112 
   111 
   113 
   112 # Called by PLCObject at start
   114 # Called by PLCObject at start
   113 def _runtime_svghmi0_start():
   115 def _runtime_svghmi0_start():
   123     svghmi_send_thread.start()
   125     svghmi_send_thread.start()
   124 
   126 
   125 
   127 
   126 # Called by PLCObject at stop
   128 # Called by PLCObject at stop
   127 def _runtime_svghmi0_stop():
   129 def _runtime_svghmi0_stop():
   128     global svghmi_listener, svghmi_root, svghmi_send_thread
   130     global svghmi_listener, svghmi_root, svghmi_send_thread, svghmi_session
       
   131     if svghmi_session is not None:
       
   132         svghmi_session.close()
   129     svghmi_root.delEntity("ws")
   133     svghmi_root.delEntity("ws")
   130     svghmi_root = None
   134     svghmi_root = None
   131     svghmi_listener.stopListening()
   135     svghmi_listener.stopListening()
   132     svghmi_listener = None
   136     svghmi_listener = None
   133     # plc cleanup calls svghmi_(locstring)_cleanup and unlocks send thread
   137     # plc cleanup calls svghmi_(locstring)_cleanup and unlocks send thread