Edouard@2771: #!/usr/bin/env python Edouard@2771: # -*- coding: utf-8 -*- Edouard@2771: Edouard@2771: # This file is part of Beremiz Edouard@2771: # Copyright (C) 2019: Edouard TISSERANT Edouard@2771: # See COPYING file for copyrights details. Edouard@2771: Edouard@2771: from __future__ import absolute_import Edouard@2771: Edouard@2771: from twisted.web.server import Site Edouard@2771: from twisted.web.resource import Resource Edouard@2771: from twisted.internet import reactor Edouard@2771: from twisted.web.static import File Edouard@2771: Edouard@2771: from autobahn.twisted.websocket import WebSocketServerFactory, WebSocketServerProtocol Edouard@2771: from autobahn.twisted.resource import WebSocketResource Edouard@2771: Edouard@2773: # TODO multiclient : Edouard@2773: # session list lock Edouard@2773: # svghmi_sessions = [] Edouard@2773: Edouard@2773: svghmi_session = None Edouard@2771: Edouard@2774: svghmi_send_collect = PLCBinary.svghmi_send_collect Edouard@2774: svghmi_send_collect.restype = ctypes.c_int # error or 0 Edouard@2774: svghmi_send_collect.argtypes = [ Edouard@2774: ctypes.POINTER(ctypes.c_uint32), # size Edouard@2774: ctypes.POINTER(ctypes.c_void_p)] # data ptr Edouard@2774: # TODO multiclient : switch to arrays Edouard@2774: Edouard@2774: svghmi_recv_dispatch = PLCBinary.svghmi_recv_dispatch Edouard@2774: svghmi_recv_dispatch.restype = ctypes.c_int # error or 0 Edouard@2774: svghmi_recv_dispatch.argtypes = [ Edouard@2775: ctypes.c_uint32, # size Edouard@2774: ctypes.POINTER(ctypes.c_void_p)] # data ptr Edouard@2774: # TODO multiclient : switch to arrays Edouard@2774: Edouard@2774: def SendThreadProc(): Edouard@2774: assert(svghmi_session) Edouard@2774: size = ctypes.c_uint32() Edouard@2774: ptr = ctypes.c_void_p() Edouard@2775: res = 0 Edouard@2775: while svghmi_send_collect(ctypes.byref(size), ctypes.byref(ptr)) == 0 and \ Edouard@2775: svghmi_session is not None and \ Edouard@2775: svghmi_session.sendMessage(ctypes.string_at(ptr,size)) == 0: Edouard@2775: pass Edouard@2774: Edouard@2775: # TODO multiclient : dispatch to sessions Edouard@2774: Edouard@2771: class HMISession(object): Edouard@2771: def __init__(self, protocol_instance): Edouard@2773: global svghmi_session Edouard@2775: Edouard@2773: # TODO: kill existing session for robustness Edouard@2773: assert(svghmi_session is None) Edouard@2773: Edouard@2773: svghmi_session = self Edouard@2773: self.protocol_instance = protocol_instance Edouard@2771: Edouard@2771: # TODO multiclient : Edouard@2773: # svghmi_sessions.append(self) Edouard@2771: # get a unique bit index amont other svghmi_sessions, Edouard@2771: # so that we can match flags passed by C->python callback Edouard@2775: Edouard@2771: def __del__(self): Edouard@2773: global svghmi_session Edouard@2773: assert(svghmi_session) Edouard@2773: svghmi_session = None Edouard@2773: Edouard@2773: # TODO multiclient : Edouard@2773: # svghmi_sessions.remove(self) Edouard@2771: Edouard@2772: def onMessage(self, msg): Edouard@2771: # TODO : pass it to the C side recieve_message() Edouard@2771: # update HMITree Edouard@2771: # - values Edouard@2771: # - refresh rates / subsriptions Edouard@2771: Edouard@2771: # TODO multiclient : pass client index as well Edouard@2771: pass Edouard@2775: Edouard@2774: def sendMessage(self, msg): Edouard@2774: self.sendMessage(msg, True) Edouard@2771: Edouard@2771: class HMIProtocol(WebSocketServerProtocol): Edouard@2771: Edouard@2771: def __init__(self, *args, **kwargs): Edouard@2771: self._hmi_session = None Edouard@2771: WebSocketServerProtocol.__init__(self, *args, **kwargs) Edouard@2771: Edouard@2771: def onOpen(self): Edouard@2771: self._hmi_session = HMISession(self) Edouard@2771: print "open" Edouard@2771: Edouard@2771: def onClose(self, wasClean, code, reason): Edouard@2771: del self._hmi_session Edouard@2771: self._hmi_session = None Edouard@2771: print "close" Edouard@2771: Edouard@2771: def onMessage(self, msg, isBinary): Edouard@2771: self._hmi_session.onMessage(msg) Edouard@2771: print msg Edouard@2771: #self.sendMessage(msg, binary) Edouard@2775: Edouard@2771: svghmi_root = None Edouard@2771: svghmi_listener = None Edouard@2775: svghmi_send_thread = None Edouard@2775: Edouard@2771: Edouard@2771: # Called by PLCObject at start Edouard@2771: def _runtime_svghmi0_start(): Edouard@2775: global svghmi_listener, svghmi_root, svghmi_send_thread Edouard@2771: Edouard@2771: svghmi_root = Resource() Edouard@2771: Edouard@2771: wsfactory = WebSocketServerFactory() Edouard@2771: wsfactory.protocol = HMIProtocol Edouard@2771: Edouard@2775: svghmi_root.putChild("ws", WebSocketResource(wsfactory)) Edouard@2771: Edouard@2771: sitefactory = Site(svghmi_root) Edouard@2771: Edouard@2771: svghmi_listener = reactor.listenTCP(8008, sitefactory) Edouard@2771: Edouard@2771: # start a thread that call the C part of SVGHMI Edouard@2775: svghmi_send_thread = Thread(target=SendThreadProc, name="SVGHMI Send") Edouard@2775: svghmi_send_thread.start() Edouard@2771: Edouard@2771: Edouard@2771: # Called by PLCObject at stop Edouard@2771: def _runtime_svghmi0_stop(): Edouard@2775: global svghmi_listener, svghmi_root, svghmi_send_thread Edouard@2775: svghmi_root.delEntity("ws") Edouard@2775: svghmi_root = None Edouard@2772: svghmi_listener.stopListening() Edouard@2775: svghmi_listener = None Edouard@2775: # plc cleanup calls svghmi_(locstring)_cleanup and unlocks send thread Edouard@2775: svghmi_send_thread.join() Edouard@2775: svghmi_send_thread = None Edouard@2775: