Edouard@1440: #!/usr/bin/env python Edouard@1440: # -*- coding: utf-8 -*- andrej@1571: andrej@1571: # This file is part of Beremiz, a Integrated Development Environment for andrej@1571: # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. Edouard@1440: # andrej@1571: # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD Edouard@1440: # andrej@1571: # See COPYING file for copyrights details. Edouard@1440: # andrej@1571: # This program is free software; you can redistribute it and/or andrej@1571: # modify it under the terms of the GNU General Public License andrej@1571: # as published by the Free Software Foundation; either version 2 andrej@1571: # of the License, or (at your option) any later version. Edouard@1440: # andrej@1571: # This program is distributed in the hope that it will be useful, andrej@1571: # but WITHOUT ANY WARRANTY; without even the implied warranty of andrej@1571: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the andrej@1571: # GNU General Public License for more details. Edouard@1440: # andrej@1571: # You should have received a copy of the GNU General Public License andrej@1571: # along with this program; if not, write to the Free Software andrej@1571: # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Edouard@1440: andrej@1826: andrej@1881: from __future__ import absolute_import andrej@1826: from __future__ import print_function denis@2006: import sys andrej@1732: import traceback Edouard@2472: from functools import partial andrej@1832: from threading import Thread, Event Edouard@2470: from six import text_type as text andrej@1832: Edouard@1440: from twisted.internet import reactor, threads Edouard@1440: from autobahn.twisted import wamp Edouard@1440: from autobahn.twisted.websocket import WampWebSocketClientFactory, connectWS Edouard@1440: from autobahn.wamp import types Edouard@1440: from autobahn.wamp.exception import TransportLost Edouard@1440: from autobahn.wamp.serializer import MsgPackSerializer andrej@1832: andrej@2416: from runtime import PlcStatus Edouard@1440: Edouard@1440: _WampSession = None Edouard@1441: _WampConnection = None Edouard@1440: _WampSessionEvent = Event() Edouard@1440: andrej@1736: Edouard@1440: class WampSession(wamp.ApplicationSession): Edouard@1440: def onJoin(self, details): andrej@1841: global _WampSession Edouard@1440: _WampSession = self Edouard@1440: _WampSessionEvent.set() andrej@1826: print('WAMP session joined for :', self.config.extra["ID"]) Edouard@1440: Edouard@1440: def onLeave(self, details): andrej@1841: global _WampSession Edouard@1440: _WampSessionEvent.clear() Edouard@1440: _WampSession = None andrej@1826: print('WAMP session left') Edouard@1440: andrej@1749: andrej@1747: PLCObjDefaults = { andrej@1747: "StartPLC": False, andrej@1747: "GetTraceVariables": ("Broken", None), andrej@2416: "GetPLCstatus": (PlcStatus.Broken, None), andrej@1747: "RemoteExec": (-1, "RemoteExec script failed!") andrej@1747: } Edouard@1440: andrej@1736: Edouard@2472: def _WAMP_connector_factory(cls, uri, confnodesroot): Edouard@1440: """ Edouard@1440: WAMP://127.0.0.1:12345/path#realm#ID Edouard@1440: WAMPS://127.0.0.1:12345/path#realm#ID Edouard@1440: """ Edouard@2338: scheme, location = uri.split("://") Edouard@1440: urlpath, realm, ID = location.split('#') andrej@1740: urlprefix = {"WAMP": "ws", Edouard@2338: "WAMPS": "wss"}[scheme] Edouard@1440: url = urlprefix+"://"+urlpath Edouard@1440: Edouard@1440: def RegisterWampClient(): Edouard@1440: andrej@1753: # start logging to console Edouard@1440: # log.startLogging(sys.stdout) Edouard@1440: Edouard@1440: # create a WAMP application session factory Edouard@1440: component_config = types.ComponentConfig( andrej@2434: realm=text(realm), andrej@1744: extra={"ID": ID}) Edouard@1440: session_factory = wamp.ApplicationSessionFactory( andrej@1744: config=component_config) Edouard@2472: session_factory.session = cls Edouard@1440: Edouard@1440: # create a WAMP-over-WebSocket transport client factory Edouard@1440: transport_factory = WampWebSocketClientFactory( Edouard@1440: session_factory, andrej@1744: url=url, Edouard@1894: serializers=[MsgPackSerializer()]) Edouard@1440: Edouard@1440: # start the client from a Twisted endpoint Edouard@1440: conn = connectWS(transport_factory) andrej@1734: confnodesroot.logger.write(_("WAMP connecting to URL : %s\n") % url) Edouard@1440: return conn Edouard@1440: Edouard@1441: AddToDoBeforeQuit = confnodesroot.AppFrame.AddToDoBeforeQuit andrej@1750: Edouard@1441: def ThreadProc(): Edouard@1441: global _WampConnection Edouard@1441: _WampConnection = RegisterWampClient() Edouard@1441: AddToDoBeforeQuit(reactor.stop) Edouard@1441: reactor.run(installSignalHandlers=False) Edouard@1441: Edouard@1440: def WampSessionProcMapper(funcname): andrej@2434: wampfuncname = text('.'.join((ID, funcname))) andrej@1750: andrej@1740: def catcher_func(*args, **kwargs): andrej@1739: if _WampSession is not None: Edouard@1440: try: Edouard@1440: return threads.blockingCallFromThread( Edouard@1443: reactor, _WampSession.call, wampfuncname, andrej@1740: *args, **kwargs) andrej@1846: except TransportLost: andrej@1595: confnodesroot.logger.write_error(_("Connection lost!\n")) Edouard@1440: confnodesroot._SetConnector(None) andrej@1846: except Exception: Edouard@1440: errmess = traceback.format_exc() Edouard@1440: confnodesroot.logger.write_error(errmess+"\n") andrej@1826: print(errmess) andrej@1782: # confnodesroot._SetConnector(None) Edouard@1440: return PLCObjDefaults.get(funcname) Edouard@1440: return catcher_func Edouard@1440: Edouard@1440: class WampPLCObjectProxy(object): Edouard@1440: def __init__(self): andrej@1841: global _WampConnection Edouard@1440: if not reactor.running: Edouard@1440: Thread(target=ThreadProc).start() Edouard@1440: else: Edouard@1441: _WampConnection = threads.blockingCallFromThread( Edouard@1440: reactor, RegisterWampClient) Edouard@1440: if not _WampSessionEvent.wait(5): andrej@1872: _WampConnection.stopConnecting() andrej@1765: raise Exception(_("WAMP connection timeout")) Edouard@1440: Edouard@1440: def __del__(self): Edouard@1441: _WampConnection.disconnect() Edouard@1441: # Edouard@1441: # reactor.stop() Edouard@1440: Edouard@1440: def __getattr__(self, attrName): Edouard@1440: member = self.__dict__.get(attrName, None) Edouard@1440: if member is None: Edouard@1440: member = WampSessionProcMapper(attrName) Edouard@1440: self.__dict__[attrName] = member Edouard@1440: return member Edouard@1440: Edouard@2339: # TODO : GetPLCID() Edouard@2339: # TODO : PSK.UpdateID() Edouard@2339: Edouard@2463: return WampPLCObjectProxy Edouard@2472: Edouard@2472: WAMP_connector_factory = partial(_WAMP_connector_factory, WampSession)