Example #1
0
    def __init__(self,
                 group=None,
                 target=None,
                 name=None,
                 args=(),
                 kwargs=None,
                 daemon=None) -> None:
        super().__init__(group=group,
                         target=target,
                         name=name,
                         args=args,
                         kwargs=kwargs,
                         daemon=daemon)
        self.args = args
        self.kwargs = kwargs

        def joined(session, details):
            print("session ready")
            self.mySession = session

        comp = Component(transports="ws://host.docker.internal:8090/ws",
                         realm=u"racelog.state")
        comp.on_join(joined)

        comp.start(loop=self)
class Publisher:
    def __init__(self):
        self.component = None

    async def connect(self, crossbar_url):
        """Method used to initialize a session for the publisher

        :param crossbar_url: url hosting the crossbar instance
        """
        log.debug(f"Connecting to {crossbar_url}")

        self.component = Component(transports=crossbar_url, realm='realm1')

        loop = asyncio.get_event_loop()
        txaio.config.loop = loop

        session_ready = asyncio.Event()

        async def setup_session(created_session, _details):
            """Callback method used to retrieve a session and notify interested parties with an event"""
            self.session = created_session
            nonlocal session_ready
            session_ready.set()

        self.component.start(loop=loop)
        self.component.on_join(setup_session)

        await session_ready.wait()

    def publish(self, topic, payload):
        """Publish the payload on the specified topic

        :param topic: target topic
        :param payload: payload to be published
        """
        self.session.publish(topic, payload)
        log.debug(f"data is published to : {topic}")
Example #3
0
    def test_asyncio_component(event_loop):
        orig_loop = txaio.config.loop
        txaio.config.loop = event_loop

        comp = Component(
            transports=[
                {
                    "url": "ws://localhost:12/bogus",
                    "max_retries": 1,
                    "max_retry_delay": 0.1,
                }
            ]
        )

        # if having trouble, try starting some logging (and use
        # "py.test -s" to get real-time output)
        # txaio.start_logging(level="debug")
        f = comp.start(loop=event_loop)
        txaio.config.loop = event_loop
        finished = txaio.create_future()

        def fail():
            finished.set_exception(AssertionError("timed out"))
            txaio.config.loop = orig_loop
        txaio.call_later(4.0, fail)

        def done(f):
            try:
                f.result()
                finished.set_exception(AssertionError("should get an error"))
            except RuntimeError as e:
                if 'Exhausted all transport connect attempts' not in str(e):
                    finished.set_exception(AssertionError("wrong exception caught"))
            finished.set_result(None)
            txaio.config.loop = orig_loop
            assert comp._done_f is None
        f.add_done_callback(done)
        return finished
Example #4
0
    def test_asyncio_component_404(event_loop):
        """
        If something connects but then gets aborted, it should still try
        to re-connect (in real cases this could be e.g. wrong path,
        TLS failure, WebSocket handshake failure, etc)
        """
        orig_loop = txaio.config.loop
        txaio.config.loop = event_loop

        class FakeTransport(object):
            def close(self):
                pass

            def write(self, data):
                pass

        fake_transport = FakeTransport()
        actual_protocol = [None]  # set in a closure below

        def create_connection(protocol_factory=None, server_hostname=None, host=None, port=None, ssl=False):
            if actual_protocol[0] is None:
                protocol = protocol_factory()
                actual_protocol[0] = protocol
                protocol.connection_made(fake_transport)
                return txaio.create_future_success((fake_transport, protocol))
            else:
                return txaio.create_future_error(RuntimeError("second connection fails completely"))

        with mock.patch.object(event_loop, 'create_connection', create_connection):
            event_loop.create_connection = create_connection

            comp = Component(
                transports=[
                    {
                        "url": "ws://localhost:12/bogus",
                        "max_retries": 1,
                        "max_retry_delay": 0.1,
                    }
                ]
            )

            # if having trouble, try starting some logging (and use
            # "py.test -s" to get real-time output)
            # txaio.start_logging(level="debug")
            f = comp.start(loop=event_loop)
            txaio.config.loop = event_loop

            # now that we've started connecting, we *should* be able
            # to connetion_lost our transport .. but we do a
            # call-later to ensure we're after the setup stuff in the
            # event-loop (because asyncio doesn't synchronously
            # process already-completed Futures like Twisted does)

            def nuke_transport():
                actual_protocol[0].connection_lost(None)  # asyncio can call this with None
            txaio.call_later(0.1, nuke_transport)

            finished = txaio.create_future()

            def fail():
                finished.set_exception(AssertionError("timed out"))
                txaio.config.loop = orig_loop
            txaio.call_later(1.0, fail)

            def done(f):
                try:
                    f.result()
                    finished.set_exception(AssertionError("should get an error"))
                except RuntimeError as e:
                    if 'Exhausted all transport connect attempts' not in str(e):
                        finished.set_exception(AssertionError("wrong exception caught"))
                finished.set_result(None)
                txaio.config.loop = orig_loop
            f.add_done_callback(done)
            return finished
class Handler():
    
    def __init__(self, connection, manager):
           
        if os.getenv('OCP_TEST_RUN', "0") != "1":
            raise Exception("Test Handler created, but test environment variable not set")
          
        self.__manager = manager  
        self.__session = None
        self.__logger  = logging.getLogger("Test handler")
        
        #register the Error raise filter to ensure that during testing all error messages lead to test stop
        #Note: Attach to handler, as adding to logger itself does not propagate to child loggers
        #logging.getLogger().handlers[0].addFilter(ErrorFilter())
        
        # set the test loglevel      
        loglevel = os.getenv('OCP_TEST_LOG_LEVEL', "Warn")
        if loglevel == "Warn":
            loglevel = logging.WARN
        elif loglevel == "Error":
            loglevel = logging.ERROR
        elif loglevel == "Info":
            loglevel = logging.INFO
        elif loglevel == "Debug":
            loglevel = logging.DEBUG
        elif loglevel == "Trace":
            loglevel = logging.DEBUG
            
        logging.getLogger().setLevel(loglevel)
        
        #run the handler
        asyncio.ensure_future(self._startup(connection))
        
          
    async def _startup(self, connection):
        
        
        self.__connection = connection
        await connection.api.waitTillReady()
        
        #register ourself to the OCP node
        await connection.api.subscribe("testhandler", self.__receiveSync, "ocp.documents..content.Document.sync", options=SubscribeOptions(match="wildcard"))
        
        #connect to testserver       
        uri = os.getenv('OCP_TEST_SERVER_URI', '')
        self.test = Component(transports=uri, realm = "ocptest")
        self.test.on('join', self.__onJoin)
        self.test.on('leave', self.__onLeave)
        
        self.test.start()
    
    
    async def __onJoin(self, session, details):
        #little remark that we joined (needed for test executable, it waits for this)
        FreeCAD.Console.PrintMessage("Connection to OCP test server established\n")
        
        #store the session for later use
        self.__session = session
        
        #get all the testing functions in this class!
        methods = [func for func in dir(self) if callable(getattr(self, func))]
        rpcs = [rpc for rpc in methods if '_rpc' in rpc]
    
        #build the correct uri 
        uri = os.getenv('OCP_TEST_RPC_ADDRESS', '')
        if uri == '':
            raise ('No rpc uri set for testing')
        
        for rpc in rpcs:
            rpc_uri = uri + "." + rpc[len('_rpc'):]
            await session.register(getattr(self, rpc), rpc_uri)
    
        #inform test framework that we are set up!
        try:
            await session.call("ocp.test.triggerEvent", uri, True)
        except Exception as e:
            print("Exception in event call: ", str(e))
        
    
    async def __onLeave(self, session, reason):
        
        self.__session = None
        
        #inform test framework that we are not here anymore!
        await session.call("ocp.test.triggerEvent", os.getenv('OCP_TEST_RPC_ADDRESS', ''), False)


    async def waitTillCloseout(self, docId, timeout=30):
        # wait till all tasks in the document with given ID are finished
        try:
            for entity in self.__manager.getEntities():
                if entity.onlinedoc != None and entity.id == docId:
                    await entity.onlinedoc.waitTillCloseout(timeout)
                    self.__logger.debug(f"Closeout finished for {docId}")
                    return True
            
            return False
                    
        except Exception as e: 
            self.__logger.error(f"Trigger synchronize failed, cannot wait for closeout of current actions: {e}")
            return False


    async def synchronize(self, docId, numFCs):
        #Synchronize all other involved FC instances. When this returns one can call waitForSync on the TestServer. The numFCs must not 
        #include the FC instance it is called on, only the remaining ones
        #Note:
        #   We trigger the FC instances to sync themself via the DML doc. This is to make sure that the sync is called only after all 
        #   operations have been received by the FC instance. 
        #   1. Do all known operations
        #   2. Emit sync event in DML document
        
        self.__logger.debug(f"Start syncronisation for: {docId[-5:]}")
                
        #we wait till all tasks are finished
        if not await self.waitTillCloseout(docId):
            return
 
        #register the sync with the testserver
        await self.__session.call("ocp.test.registerSync", docId, numFCs)
        
        #and now issue the event that all FC instances know that they should sync.
        self.__logger.debug(f"Work done, trigger sync events via dml: {docId[-5:]}")
        uri = f"ocp.documents.{docId}.content.Document.sync"
        await self.__connection.api.call(uri, docId)

     
    async def  __receiveSync(self, docId):
        #received a sync event from DML document
        # 1. Wait till all actions are finished
        # 2. Inform the TestServer, that we are finished
        
        self.__logger.debug(f"Request for sync received: {docId[-5:]}")
        #await asyncio.sleep(0.05)
        
        #wait till everything is done!
        try:
            entities = self.__manager.getEntities()
            for entity in entities:
                if entity.onlinedoc and entity.id == docId:
                    await entity.onlinedoc.waitTillCloseout(30)
                    
        except Exception as e: 
            print(f"Participation in synchronize failed, cannot wait for closeout of current actions: {e}")
            return

        #call testserver that we received and executed the sync!
        self.__logger.debug("Work done, send sync event")
        await self.__session.call("ocp.test.sync", docId)

    
    async def _rpcShareDocument(self, name):
        pass
    
    async def _rpcUnshareDocument(self, name):
        pass
    
    
    async def _rpcAddNodeToDocument(self):
        pass
    
    async def _rpcExecuteCode(self, code):
        
        code.insert(0, "import FreeCADGui as Gui")
        code.insert(0, "import FreeCAD as App")
        
        exec(
            f'async def __ex(): ' +
            ''.join(f'\n {line}' for line in code)
        )

        return await locals()['__ex']()
class API(QtCore.QObject, Utils.AsyncSlotObject):
    #Class to handle the WAMP connection to the OCP node
       
    def __init__(self, node, logger):
        
        QtCore.QObject.__init__(self)
        
        self.__node = node
        self.__wamp = None
        self.__session = None        
        self.__readyEvent = asyncio.Event()
        self.__registered = {}          # key: [(args, kwargs)]
        self.__registeredSessions = {}  # key: [sessions]
        self.__subscribed = {}
        self.__subscribedSessions = {}
        self.__logger = logger
        self.__settings = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod").GetGroup("Collaboration")

        # connect to node ready events for auto reconnect
        self.__node.runningChanged.connect(self.__nodeChange)

    
    async def waitTillReady(self):
        await self.__readyEvent.wait()
        
   
    async def connectToNode(self):
        
        self.__logger.debug(f"Try to connect")
        
        if not self.__node.running:
            raise Exception("Cannot connect API if node is not running")
        
        if self.connected:
            return
        
        #make the OCP node connection!            
        uri = f"ws://{self.__node.apiUri}:{self.__node.apiPort}/ws"
        self.__wamp = Component(  transports={
                                    "url":uri,
                                    "serializers": ['msgpack'],
                                    "initial_retry_delay": 10
                                },
                                realm = "ocp")
        self.__wamp.on('join', self.__onJoin)
        self.__wamp.on('leave', self.__onLeave)
        self.__wamp.on('disconnect', self.__onDisconnect)
        self.__wamp.on('ready', self.__onReady)

        #run the component
        self.__wamp.start()
        
        
    async def disconnectFromNode(self):

        # close the connection
        self.__logger.debug(f"Try to disconnect")
        if self.__wamp:
            await self.__wamp.stop()
            self.__wamp = None
   
    
    async def register(self, key, *args, **kwargs):
        # Registers an API function. It stays registered over multiple session and reconnects
        # Can be unregistered with the given key. Note: multiple register and subscribe calls can be 
        # made with a single key
                
        self.__registered[key] = self.__registered.get(key, []) + [(args, kwargs)]
        if self.connected:
            try:
                self.__registeredSessions[key] = self.__registeredSessions.get(key, [])  + [await self.__session.register(*args, **kwargs)]
            except:
                pass
    
    
    async def subscribe(self, key, *args, **kwargs):
        # Subscribe to API event. It stays subscribed over multiple session and reconnects
               
        self.__subscribed[key] = self.__subscribed.get(key, []) + [(args, kwargs)]
        if self.connected:
            try:
                self.__subscribedSessions[key] = self.__subscribedSessions.get(key, [])  + [await self.__session.subscribe(*args, **kwargs)]
            except:
                pass
    
    
    async def closeKey(self, key):

        if key in self.__registered:
            #remove register entries and close sessions
            del self.__registered[key]
            for session in self.__registeredSessions.pop(key, []):
                await session.unregister()          
        
        if key in self.__subscribed:
            #remove subscribe entries and close sessions
            del self.__subscribed[key]
            for session in self.__subscribedSessions.pop(key, []):
                await session.unsubscribe()

            
    async def call(self, *args, **kwargs):
        # calls api function
        
        if not self.connected: 
            raise Exception("Not connected to Node, cannot call API function")
        
        # add a default timeout if the caller did no do already
        if "options" in kwargs:
            opts = kwargs["options"]
            if not opts.timeout:
                opts.timeout = 5000
        else:
            kwargs["options"] = wamp.CallOptions(timeout=5000)
            
        # call
        return await self.__session.call(*args, **kwargs)
        
    
    # Node callbacks
    # ********************************************************************************************
    
    @asyncSlot()
    async def __nodeChange(self):
        
        self.__logger.debug(f"Node change callback, node running: {self.__node.running}")
        
        if self.reconnect and self.__node.running:
            await self.connectToNode()
            
        if self.__wamp and not self.__node.running:
            await self.disconnectFromNode()
    
    
    # Wamp callbacks
    # ********************************************************************************************
    
    async def __onJoin(self, session, details):
       
        self.__logger.debug(f"Join WAMP session")
        self.__session = session
        
        # register all functions
        for key, argsList in self.__registered.items():
            
            sessions = []
            for args in argsList:
                sessions.append(await self.__session.register(*(args[0]), **(args[1])))
            
            self.__registeredSessions[key] = sessions
            
        # subscribe to all events
        for key, argsList in self.__subscribed.items():
            
            sessions = []
            for args in argsList:
                sessions.append(await self.__session.subscribe(*(args[0]), **(args[1])))
            
            self.__subscribedSessions[key] = sessions
                    
            
    async def __onLeave(self, session, reason):
        
        self.__logger.debug(f"Leave WAMP session: {reason}")
        
        # clear all registered and subscribed session objects
        self.__registeredSessions = {}
        self.__subscribedSessions = {}
        
        self.__readyEvent.clear()
        self.__session = None
        
        self.connectedChanged.emit()            
        
        
    async def __onDisconnect(self, *args, **kwargs):
        self.__logger.info("API closed")
        
        
    async def __onReady(self, *args):
        self.connectedChanged.emit()
        self.__readyEvent.set()
        self.__logger.info("API ready")
       
        
    # Qt Property/Signal API used from the UI
    # ********************************************************************************************
    
    #signals for property change (needed to have QML update on property change)
    connectedChanged         = QtCore.Signal()
    __reconnectChanged       = QtCore.Signal()


    @QtCore.Property(bool, notify=connectedChanged)
    def connected(self):
        return self.__session != None
    
    
    def getReconnect(self):
        return self.__settings.GetBool("APIReconnect", True)
    
    QtCore.Slot(bool)
    def setReconnect(self, value):
       self.__settings.SetBool("APIReconnect", value)
       self.__reconnectChanged.emit()
       
    reconnect = QtCore.Property(bool, getReconnect, setReconnect, notify=__reconnectChanged)
 
    @Utils.AsyncSlot()
    async  def toggleConnectedSlot(self):
        if self.connected:
            await self.disconnectFromNode()
        else:
            await self.connectToNode()
            await self.waitTillReady()           
Example #7
0
def wampConnect(wamp_conf):
    """WAMP connection procedures.

    :param wamp_conf: WAMP configuration from settings.json file

    """

    LOG.info("WAMP connection precedures:")

    try:

        LOG.info("WAMP status @ boot:" + "\n- board = " + str(board.status) +
                 "\n- reconnection = " + str(reconnection) +
                 "\n- connected = " + str(connected))

        # LR creates the Autobahn Asyncio Component that points to the
        # WAMP Agent (main/registration agent)
        global component
        component = Component(transports=wamp_conf['url'],
                              realm=wamp_conf['realm'])

        # To manage the registration stage: we got the info for the main
        # WAMP agent and LR is going to connect to it starting the Component
        # with the new WAMP configuration.
        if connected == False and board.status == "registered" \
                and reconnection == False:
            component.start(loop)

        @component.on_join
        async def join(session, details):
            """Execute the following procedures when the board connects
            to Crossbar.

            :param details: WAMP session details

            """

            global connected
            connected = True

            # LIGHTNING-ROD STATES:
            # - REGISTRATION STATE: the first connection to Iotronic
            # - FIRST CONNECTION: the board become operative after registration
            # - LIGHTNING-ROD BOOT: the first connection to WAMP
            #                       after Lightning-rod starting
            # - WAMP RECOVERY: when the established WAMP connection fails

            global reconnection

            # reconnection flag is False when the board is:
            # - LIGHTNING-ROD BOOT
            # - REGISTRATION STATE
            # - FIRST CONNECTION
            #
            # reconnection flag is True when the board is:
            # - WAMP RECOVERY

            global SESSION
            SESSION = session

            # LOG.debug(" - session: " + str(details))

            board.session_id = details.session

            LOG.info(" - Joined in realm " + board.wamp_config['realm'] + ":")
            LOG.info("   - WAMP Agent: " + str(board.agent))
            LOG.info("   - Session ID: " + str(board.session_id))
            LOG.info("   - Board status:  " + str(board.status))

            if reconnection is False:

                if board.uuid is None:

                    ######################
                    # REGISTRATION STATE #
                    ######################
                    # If in the LR configuration file there is not the
                    # Board UUID specified it means the board is a new one
                    # and it has to call IoTronic in order to complete
                    # the registration.

                    try:

                        LOG.info(" - Board needs to be registered.")

                        rpc = u'stack4things.register'

                        with timeoutRPC(seconds=3, action=rpc):
                            res = await session.call(rpc,
                                                     code=board.code,
                                                     session=board.session_id)

                        w_msg = WM.deserialize(res)

                        # LOG.info(" - Board registration result: \n" +
                        #         json.loads(w_msg.message, indent=4))

                        if w_msg.result == WM.SUCCESS:

                            LOG.info("Registration authorized by IoTronic:\n" +
                                     str(w_msg.message))

                            # the 'message' field contains
                            # the board configuration to load
                            board.setConf(w_msg.message)

                            # We need to disconnect the client from the
                            # registration-agent in order to reconnect
                            # to the WAMP agent assigned by Iotronic
                            # at the provisioning stage
                            LOG.info(
                                "\n\nDisconnecting from Registration Agent "
                                "to load new settings...\n\n")

                            # We stop the Component in order to trigger the
                            # onDisconnect event
                            component.stop()

                        else:
                            LOG.error("Registration denied by Iotronic: " +
                                      str(w_msg.message))
                            Bye()

                    except exception.ApplicationError as e:
                        LOG.error("IoTronic registration error: " + str(e))
                        # Iotronic is offline the board can not call the
                        # "stack4things.connection" RPC. The board will
                        # disconnect from WAMP agent and retry later.

                        # TO ACTIVE BOOT CONNECTION RECOVERY MODE
                        reconnection = True
                        # We stop the Component in order to trigger the
                        # onDisconnect event
                        component.stop()

                    except Exception as e:
                        LOG.warning(" - Board registration call error: " +
                                    str(e))
                        Bye()

                else:

                    if board.status == "registered":
                        ####################
                        # FIRST CONNECTION #
                        ####################

                        # In this case we manage the first connection
                        # after the registration stage:
                        # Lightining-rod sets its status to "operative"
                        # completing the provisioning and configuration stage.
                        LOG.info("\n\n\nBoard is becoming operative...\n\n\n")
                        board.updateStatus("operative")
                        board.loadSettings()
                        LOG.info("WAMP status @ firt connection:" +
                                 "\n- board = " + str(board.status) +
                                 "\n- reconnection = " + str(reconnection) +
                                 "\n- connected = " + str(connected))
                        await IotronicLogin(board, session, details)

                    elif board.status == "operative":
                        ######################
                        # LIGHTNING-ROD BOOT #
                        ######################

                        # After join to WAMP agent, Lightning-rod will:
                        # - authenticate to Iotronic
                        # - load the enabled modules

                        # The board will keep at this stage until
                        # it will succeed to connect to Iotronic.
                        await IotronicLogin(board, session, details)

                    else:
                        LOG.error("Wrong board status '" + board.status + "'.")
                        Bye()

            else:

                #################
                # WAMP RECOVERY #
                #################

                LOG.info("IoTronic connection recovery:")

                try:

                    rpc = str(board.agent) + u'.stack4things.connection'

                    with timeoutRPC(seconds=3, action=rpc):
                        res = await session.call(rpc,
                                                 uuid=board.uuid,
                                                 session=details.session)

                    w_msg = WM.deserialize(res)

                    if w_msg.result == WM.SUCCESS:

                        LOG.info(" - Access granted to Iotronic.")

                        # LOADING BOARD MODULES
                        # If the board is in WAMP connection recovery state
                        # we need to register again the RPCs of each module
                        try:

                            moduleReloadInfo(session)

                            # Reset flag to False
                            reconnection = False

                            LOG.info("WAMP Session Recovered!")

                            LOG.info("\n\nListening...\n\n")

                        except Exception as e:
                            LOG.warning(
                                "WARNING - Could not reload modules: " +
                                str(e))
                            Bye()

                    else:
                        LOG.error("Access to IoTronic denied: " +
                                  str(w_msg.message))
                        Bye()

                except exception.ApplicationError as e:
                    LOG.error("IoTronic connection error:\n" + str(e))
                    # Iotronic is offline the board can not call
                    # the "stack4things.connection" RPC.
                    # The board will disconnect from WAMP agent and retry later

                    # TO ACTIVE WAMP CONNECTION RECOVERY MODE
                    reconnection = False
                    # We stop the Component in order to trigger the
                    # onDisconnect event
                    component.stop()

                except Exception as e:
                    LOG.warning(
                        "Board connection error after WAMP recovery: " +
                        str(e))
                    Bye()

        @component.on_leave
        async def onLeave(session, details):
            LOG.warning('WAMP Session Left: ' + str(details))

        @component.on_disconnect
        async def onDisconnect(session, was_clean):
            """Procedure triggered on WAMP connection lost, for istance
            when we call component.stop().

            :param connector: WAMP connector object
            :param reason: WAMP connection failure reason

            """

            LOG.warning('WAMP Transport Left: was_clean = ' + str(was_clean))
            global connected
            connected = False

            global reconnection

            LOG.info("WAMP status on disconnect:" + "\n- board = " +
                     str(board.status) + "\n- reconnection = " +
                     str(reconnection) + "\n- connected = " + str(connected))

            if board.status == "operative" and reconnection is False:

                #################
                # WAMP RECOVERY #
                #################
                # we need to recover wamp session and
                # we set reconnection flag to True in order to activate
                # the RPCs module registration procedure for each module

                reconnection = True

                # LR needs to reconncet to WAMP
                if not connected:
                    component.start(loop)

            elif board.status == "operative" and reconnection is True:

                ######################
                # LIGHTNING-ROD BOOT #
                ######################
                # At this stage if the reconnection flag was set to True
                # it means that we forced the reconnection procedure
                # because of the board is not able to connect to IoTronic
                # calling "stack4things.connection" RPC...
                # it means IoTronic is offline!

                # We need to reset the recconnection flag to False in order to
                # do not enter in RPCs module registration procedure...
                # At this stage the board tries to reconnect to
                # IoTronic until it will come online again.
                reconnection = False

                # LR needs to reconncet to WAMP
                if not connected:
                    component.start(loop)

            elif (board.status == "registered"):
                ######################
                # REGISTRATION STATE #
                ######################

                # LR was disconnected from Registration Agent
                # in order to connect it to the assigned WAMP Agent.

                LOG.debug("\n\nReconnecting after registration...\n\n")

                # LR load the new configuration and gets the new WAMP Agent
                board.loadSettings()

                # LR has to connect to the assigned WAMP Agent
                wampConnect(board.wamp_config)

            else:
                LOG.error("Reconnection wrong status!")

    except Exception as err:
        LOG.error(" - WAMP connection error: " + str(err))
        Bye()