Ejemplo n.º 1
0
    def __do_request(self, request_type, _uri=None, callback=None, subscription=None, **kwargs):
        """
        Create and forward a generic WAMP request to the decoupler

        :type request_type: WampRequestType
        :type _uri: str | None
        :type callback: (*Any) -> None | None
        :type subscription: Subscription | None
        :return: Result from WampRequest, None if request failed.
        :rtype: dict | None
        """
        if not self._client_thread.is_alive():
            return

        # Make sure the current thread has the event loop set
        asyncio.set_event_loop(self._loop)

        async def _async_request(future):
            """
            :type future: asyncio.Future
            """
            request = WampRequest(request_type, _uri, kwargs, callback, subscription, future)
            await self._decoupler.put_request(request)
            await future  # The client worker is responsible for completing the future

        forwarded_future = asyncio.Future()
        concurrent_future = asyncio.run_coroutine_threadsafe(_async_request(forwarded_future), self._loop)
        self._decoupler.set_caller_future(concurrent_future)
        concurrent_future.result()
        self._decoupler.set_caller_future(None)

        # If the decoupled client worker never set the future, it failed and/or died and we return None
        if forwarded_future.done():
            return forwarded_future.result()
Ejemplo n.º 2
0
    def __init__(self, url=None, allow_exception=False):
        """
        :param url: URL of the Wwise Authoring API WAMP server, defaults to ws://127.0.0.1:8080/waapi
        :type: str
        :param allow_exception: Allow errors on call and subscribe to throw an exception. Default is False.
        :type allow_exception: bool
        :raises: CannotConnectToWaapiException
        """
        super(WaapiClient, self).__init__()

        self._allow_exception = allow_exception
        self._url = url or "ws://127.0.0.1:8080/waapi"
        self._client_thread = None
        """:type: Thread"""

        self._loop = asyncio.get_event_loop()
        if not self._loop.is_running():
            if not self._loop.is_closed():
                self._loop.close()
            if platform == 'win32':
                #  Prefer the ProactorEventLoop event loop on Windows
                self._loop = asyncio.ProactorEventLoop()
            else:
                self._loop = asyncio.new_event_loop()
            asyncio.set_event_loop(self._loop)

        self._decoupler = None
        """:type: AutobahnClientDecoupler"""

        self._subscriptions = set()
        """:type: set[EventHandler]"""

        # Connect on instantiation (RAII idiom)
        if not self.__connect():
            raise CannotConnectToWaapiException("Could not connect to " + self._url)
Ejemplo n.º 3
0
    def run(self):
        try:
            asyncio.set_event_loop(self._loop)

            txaio.use_asyncio()
            txaio.config.loop = self._loop

            # Info is too verbose, use error by default
            # TODO: Make logging level for autobahn configurable
            txaio.start_logging(level='error')

            # create a WAMP-over-WebSocket transport client factory
            transport_factory = WampWebSocketClientFactory(
                lambda: self._akcomponent_factory(self._decoupler, self.
                                                  _callback_executor, self.
                                                  _allow_exception),
                url=self._url)

            # Basic settings with most features disabled
            transport_factory.setProtocolOptions(failByDrop=False,
                                                 openHandshakeTimeout=5.,
                                                 closeHandshakeTimeout=1.)

            isSecure, host, port, _, _, _ = parse_ws_url(self._url)
            transport, protocol = self._loop.run_until_complete(
                self._loop.create_connection(transport_factory,
                                             host,
                                             port,
                                             ssl=isSecure))

            try:
                self._loop.run_forever()
            except KeyboardInterrupt:
                # wait until we send Goodbye if user hit ctrl-c
                # (done outside this except so SIGTERM gets the same handling)
                pass

            # give Goodbye message a chance to go through, if we still
            # have an active session
            if protocol._session:
                self._loop.run_until_complete(protocol._session.leave())

            self._loop.close()
        except Exception as e:
            errorStr = pformat(e)
            stderr.write(errorStr + "\n")

            # Wake the caller, this thread will terminate right after so the
            # error can be detected by checking if the thread is alive
            self._decoupler.set_joined()

        self._decoupler.unblock_caller()
Ejemplo n.º 4
0
    def disconnect(self):
        """
        Gracefully disconnect from the Waapi server.

        :return: True if the call caused a successful disconnection, False otherwise.
        :rtype: bool
        """
        if self.is_connected() and self.__do_request(WampRequestType.STOP):
            # Wait for the runner thread to gracefully exit and the asyncio loop to close
            if self._client_thread.is_alive():
                self._client_thread.join()

            self._subscriptions.clear()  # No need to unsubscribe, subscriptions will be dropped anyways

            # Create a new loop for upcoming uses
            if asyncio.get_event_loop().is_closed():
                asyncio.set_event_loop(asyncio.new_event_loop())

            return True

        # Only the caller that truly caused the disconnection return True
        return False
Ejemplo n.º 5
0
    def disconnect(self):
        """
        Gracefully disconnect from the Waapi server.

        :return: True if successfully disconnected, False otherwise.
        :rtype: bool
        """
        if not self.is_connected():
            return False

        self.__do_request(WampRequestType.STOP)
        self._subscriptions.clear(
        )  # No need to unsubscribe, subscriptions will be dropped anyways

        # Wait for the runner thread to gracefully exit and the asyncio loop to close
        self._client_thread.join()

        assert (asyncio.get_event_loop().is_closed())
        # Create a new loop for upcoming uses
        asyncio.set_event_loop(asyncio.new_event_loop())

        return True
Ejemplo n.º 6
0
    def run(self):
        try:
            asyncio.set_event_loop(self._loop)

            # Start the loop ourselves to skip the sigterm signal handler since
            # it is not supported from a different thread
            coro = self._runner.run(
                lambda config: self._akcomponent_factory(config, self._decoupler, self._allow_exception),
                start_loop=False
            )

            transport, protocol = self._loop.run_until_complete(coro)

            # Info is too verbose, use error by default
            # TODO: Make logging level for autobahn configurable
            txaio.start_logging(level='error')

            try:
                self._loop.run_forever()
            except KeyboardInterrupt:
                # wait until we send Goodbye if user hit ctrl-c
                # (done outside this except so SIGTERM gets the same handling)
                pass

            # give Goodbye message a chance to go through, if we still
            # have an active session
            if protocol._session:
                self._loop.run_until_complete(protocol._session.leave())

            self._loop.close()

        except Exception as e:
            pprint(e)

            # Wake the caller, this thread will terminate right after so the
            # error can be detected by checking if the thread is alive
            self._decoupler.set_joined()

        self._decoupler.unblock_caller()