Beispiel #1
0
    async def _make_pings_task(self):
        """ Task that sends pings to all subscriptions in the queue.
            All subs in this queue have timed out.
        """
        while True:
            await self._check_timeouts()
            while not self._ping_pong_tasks.empty():
                try:
                    subscriber = await self._ping_pong_tasks.get()
                    if subscriber.is_dead():
                        self._delete_subscription(subscriber)
                    else:
                        await subscriber.ping()

                except (ConnectionResetError, BrokenPipeError):
                    Log.err(f'Connection unexpectedly closed {subscriber}')
                    self._delete_subscription(subscriber)

                finally:
                    # We want to remove the task from the queue regardless
                    # if it fails or completes.
                    self._ping_pong_tasks.task_done()

            # Go idle so other tasks can run.
            await asyncio.sleep(TASK_DELAY_PING)
Beispiel #2
0
    async def _enter_ping_pong(self) -> None:
        self._alive = True

        while self._alive:
            packet = await self.read()

            if packet.cmd == protocol.Commands.NEW_DATA:
                # New data published!
                await self._ack_new_data()

                # Unpack the data and send it to callback.
                data = packet.data.decode('utf-8')
                if self._data_received is not None:
                    self._data_received(data)
                else:
                    Log.err(f'No callback for new data provided!\n{data}')
            else:
                # Expecting a ping
                if not await protocol.async_cmd_ok(packet,
                                                   protocol.Commands.PING):
                    Log.err('Failed to get ping command. Exiting.')
                    return

                # Send a PONG back.
                await self._pong()
                Log.debug('[Client] Pong')

                # If provided, call pong callback.
                if self._pong_callback is not None:
                    self._pong_callback()

            await asyncio.sleep(SLEEP_DELAY)
Beispiel #3
0
    async def start(self, topic: str) -> bool:
        """
            Starts the subscription.
            param data_received is a callback that will be called
                when new data arrvies at the subscribed topic.
        """
        # Start connection and initialize a new Subscription.
        await self.open()
        await self.send(protocol.Commands.SUBSCRIBE)

        # Wait for ACK, before sending the topic.
        packet = await self.read()
        if not await protocol.async_cmd_ok(
                packet, protocol.Commands.SUBSCRIBE_ACK, self._writer):
            return

        # Send the topic we want to subscribe to.
        await self.send(protocol.Commands.SUBSCRIBE,
                        data=topic.encode('utf-8'))

        # Wait for OK/NOT OK.
        sub_ack = await self.read()
        if not await protocol.async_cmd_ok(sub_ack,
                                           protocol.Commands.SUBSCRIBE_OK):
            Log.err(f'Failed to subscribe to {topic}. Got no ACK.')
            return False

        # Enter ping-pong state where we just wait for data published to
        # the chosen topic and give the data to the callback function provided.
        await self._enter_ping_pong()
Beispiel #4
0
 def _set_identifier(self, topic: str) -> None:
     """ Sets the identification of the subscription.
         This consists of:
         1. Topic
         2. File descripter number from reader/writer stream.
     """
     self.topic = topic
     try:
         self.fd = self._writer.get_extra_info('socket').fileno()
     except AttributeError:
         # Streams are incorrect
         Log.err(f'Incorrect streams to subscription to {self.topic}')
         self.fd = None
Beispiel #5
0
    async def ping(self) -> None:
        """ Pings the subscriber and waits for a PONG back.
            If the subscriber doesn't pong back, the subscription is closed.
        """
        await protocol.send_packet(self._writer, protocol.Commands.PING)
        Log.debug(f'Ping {self}')

        pong = await protocol.read_packet(self._reader)
        if await protocol.async_cmd_ok(pong, protocol.Commands.PONG):
            # If PONG, reset timer.
            self._time = 0
        else:
            Log.err(f'Bad ping! {self._alive} -> {self._state}')
            # If no PONG, advance to next state, and potentially close.
            alive = self._next_state()
            if not alive:
                self.die()
Beispiel #6
0
    async def publish(self, topic: str, message: str) -> bool:
        """
            Returns if the publish is succesful or not.
            Throws ConnectionRefusedError.
        """
        publish = AsyncPublish(self._ip, self._port)
        try:
            pub_ok = await publish.start(topic, message)
        except ConnectionRefusedError:
            Log.err(f'[Client ]Failed to connect to server {self._ip} '
                    f'on port {self._port}')
            return

        if pub_ok:
            Log.info(f'[Client] Published "{message}" to topic "{topic}"')
        else:
            Log.info(f'[Client] Failed to publish "{message}" to'
                     f' topic "{topic}"')

        return pub_ok
Beispiel #7
0
    def get() -> dict:
        """
            Returns the configurations.
            If can't find the configuration file, default parameters are used
            and a new one is created on disk.
        """
        success = False

        # Only need to read the config once (assumes no live-changes).
        if Config._config is None:
            try:
                with open(CONFIG_PATH, 'rb') as f:
                    Config._config = json.load(f)
                    success = True
            except FileNotFoundError:
                Log.err(f'Failed to find config at {CONFIG_PATH}')
            except json.decoder.JSONDecodeError as e:
                Log.err(f'Failed to parse JSON {e}')

            if not success:
                Config._config = Config.__default
                Config._save_config(Config._config)

        return Config._config