5

I am trying to get familiar with Django channels and web-sockets. I have a task - constantly streaming data to whoever connects to the channel.
Currently, this is a piece of code from official tutorial with some improvements.

class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = f'chat_{self.room_name}'

        if not hasattr(self, 'vehicle'):
            # this produses constant data stream
            self.vehicle = connect('/dev/ttyACM0', wait_ready=True, rate=4)

        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )

        await self.accept()

        # this part does not work!
        await self.send(text_data=json.dumps({
            'message': {
                        'mess': "Hi",
                        'yaw': self.vehicle._yaw,
                        'pitch': self.vehicle._pitch,
                        'roll': self.vehicle._roll,
                       }
        }))


    async def disconnect(self, close_code):
        # Leave room group
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )

But now this code disconnects without showing anything in the front. I have found this answer, but that loop also does not work.
If I move that while loop to the separate method and call it from receive method (which I won't show here for now to be short) - it works, but when a new user enters the channel, he doesn't see messages from the while loop. But after restarting the loop, messages are sent to all users.

How can I make the data stream available for all users at any time they enter the channel?

Winston
  • 601
  • 1
  • 9
  • 29
Chiefir
  • 2,561
  • 1
  • 27
  • 46
  • I don't see any constant element in your code – Ken4scholars Feb 01 '19 at 20:23
  • @Ken4scholars I mean I want to stream the data which changes 4 times in a second (it is commented where I am getting it from). So I want to emit that data to a front-end may be 1 time in a second. – Chiefir Feb 01 '19 at 20:28
  • can you post the connect method that performs the constant streaming? – Ken4scholars Feb 01 '19 at 20:29
  • @Ken4scholars this is not necessary - that is main method from DroneKit library, actually that path is a USB port where Pixhawk is connected and it produces some data (about its position, for example) 4 ticks in a second. That method works ok. – Chiefir Feb 01 '19 at 20:32
  • It is quite necessary. How exactly does it produce the data and continuously run? It is obviously not a generator since you're not consuming the data in a loop in your consumer. So if it has a continuously running while loop then it means it never returns anything back to the connect method calling it. Meaning that everything below the line `self.vehicle = connect('/dev/ttyACM0', wait_ready=True, rate=4)` never runs – Ken4scholars Feb 01 '19 at 20:38
  • You need also know that an async method cannot just call a sync method like you're doing, else it will block and not return control to the event loop. You may want to post the code of the connect method so that it will be easier to help you – Ken4scholars Feb 01 '19 at 20:43
  • No, that code is perfectly reachable, there is no blocking while loop, but there are multitreads and a lot of complicated logic which I currently can't explain. At usual you make a connection to a drone like this and next just send commands to it and receive info. If you still need to know how this method works I will attach a link to its repo asap I will be at my laptop (now I am from smartphone). – Chiefir Feb 01 '19 at 20:46
  • @Ken4scholars but it does not block a code - if I put a print after it - I see it. – Chiefir Feb 01 '19 at 20:47
  • please do attach the link. Again, just to be sure, put a print statement after `await self.accept()`. Does it get printed? – Ken4scholars Feb 01 '19 at 20:49
  • @Ken4scholars it is in the lower part of this page https://github.com/dronekit/dronekit-python/blob/master/dronekit/__init__.py – Chiefir Feb 01 '19 at 20:50
  • @Ken4scholars I am on laptop now :) Have checked prints once more - yes, print after `self.accept()` works ok, I see its output. P.S. `connect` method begins from 3085 line from previous link. – Chiefir Feb 01 '19 at 21:01
  • So does the print statement get shown every 4 seconds as you said? – Ken4scholars Feb 01 '19 at 21:02
  • @Ken4scholars no-no-no, that is not what I meant. May be go to chat? – Chiefir Feb 01 '19 at 21:03
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/187766/discussion-between-chiefir-and-ken4scholars). – Chiefir Feb 01 '19 at 21:03

1 Answers1

5

This worked:

class ChatConsumer(AsyncWebsocketConsumer):
    vehicle_keeper = []

    async def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = f'chat_{self.room_name}'

        # Join room group
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )

        await self.accept()

        if not self.vehicle_keeper:
            self.vehicle = connect('/dev/ttyACM0', wait_ready=True, rate=4)
            self.vehicle_keeper.append(self.vehicle)
        else:
            self.vehicle = self.vehicle_keeper[0]

        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'drone_position',
                'message': "HELLO!"
            }
        )

    async def drone_position(self, event):
        while True:
            await asyncio.sleep(1)
            await self.send(text_data=json.dumps({
                'message': {
                    'mess': event['message'],
                    'yaw': self.vehicle._yaw,
                    'pitch': self.vehicle._pitch,
                    'roll': self.vehicle._roll,
                }
            }))

The key is in vehicle_keeper list, which keeps vehicle connection in a global list and when new consumer comes - it uses existing connection and not making its own.

Chiefir
  • 2,561
  • 1
  • 27
  • 46