Как реализовать Django WebSockets?

Я пытаюсь создать простое приложение на Django, используя вебсокеты для отправки данных.

Согласно документации:

  1. Я добавил 'channels' в settings.py в INSTALLED APPS:
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "channels",
    "serial_mouse",
]

ASGI_APPLICATION = 'mouse_serial.asgi.application'
  1. Создали файл asgi.py, чтобы настроить ASGI:
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from serial_mouse import routing

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mouse_serial.settings')

application = ProtocolTypeRouter({
    'http': get_asgi_application(),
    'websocket': AuthMiddlewareStack(
        URLRouter(
            routing.websocket_urlpatterns
        )
    ),
})

  1. Настройте файл routing.py для вебсокетов:
from django.urls import path
from . import consumers

websocket_urlpatterns = [
    path('ws/serial/', consumers.SerialConsumer.as_asgi()),
]
  1. Реализовали потребителя WebSocket в только что созданном файле consumers.py:
import json
import asyncio
import os

import serial
import cv2
import time
from django.conf import settings
from channels.generic.websocket import AsyncWebsocketConsumer
from .models import Capture, MouseData


class SerialConsumer(AsyncWebsocketConsumer):
    def __init__(self, *args, **kwargs):
        super().__init__(args, kwargs)
        self.serial_task = None

    async def connect(self):
        await self.accept()
        self.serial_task = asyncio.create_task(self.read_serial())

    async def disconnect(self, close_code):
        self.serial_task.cancel()

    async def receive(self, text_data):
        data = json.loads(text_data)
        if data['action'] == 'capture':
            self.capture_image()
        elif data['action'] == 'mouse_move':
            self.save_mouse_data(data['x'], data['y'])

    async def read_serial(self):
        ser = serial.Serial('/dev/ttyUSB0', 9600)
        while True:
            line = ser.readline().decode('utf-8').strip()
            await self.send(text_data=json.dumps({
                'message': line,
            }))


    @staticmethod
    def capture_image():
        cam = cv2.VideoCapture(0)
        ret, frame = cam.read()
        if ret:
            captures_dir = os.path.join(settings.BASE_DIR, 'static', 'captures')
            if not os.path.exists(captures_dir):
                os.makedirs(captures_dir)
            filename = f'capture_{int(time.time())}.jpg'
            filepath = os.path.join(captures_dir, filename)
            cv2.imwrite(filepath, frame)
            Capture.objects.create(filename=f'captures/{filename}')

    @staticmethod
    def save_mouse_data(x, y):
        MouseData.objects.create(x=x, y=y)
  1. Реализованные функции views.py:
from django.shortcuts import render
from .models import Capture, MouseData

def index(request):
    return render(request, 'serial_mouse/index.html')

def view_data(request):
    captures = Capture.objects.all()
    mouse_data = MouseData.objects.all()
    return render(request, 'serial_mouse/view_data.html', {
        'captures': captures,
        'mouse_data': mouse_data
    })
  1. Обновлен index.html для использования WebSockets:
<!DOCTYPE html>
<html>
<head>
    <title>Mouse Serial App</title>
    {% load static %}
    <link rel="stylesheet" type="text/css" href="{% static 'styles.css' %}">
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
    <nav>
        <a href="/">Home</a>
        <a href="/view_data/">View Data</a>
    </nav>
    <button id="capture-btn">Capture</button>
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            console.log("Index.html script loaded.");

            const captureBtn = document.getElementById('capture-btn');
            const socket = new WebSocket('ws://127.0.0.1:8000/ws/serial/');

            socket.onopen = function() {
                console.log("WebSocket connection established.");
            };

            socket.onerror = function(error) {
                console.error("WebSocket error: ", error);
            };

            socket.onclose = function(event) {
                if (event.wasClean) {
                    console.log(`WebSocket connection closed cleanly, code=${event.code}, reason=${event.reason}`);
                } else {
                    console.error('WebSocket connection closed unexpectedly');
                }
            };

            document.addEventListener('mousemove', function(event) {
                const mouseData = {
                    action: 'mouse_move',
                    x: event.clientX,
                    y: event.clientY
                };
                console.log(`Sending mouse data: ${JSON.stringify(mouseData)}`);
                socket.send(JSON.stringify(mouseData));
            });

            captureBtn.onclick = function() {
                const captureAction = {'action': 'capture'};
                console.log(`Sending capture action: ${JSON.stringify(captureAction)}`);
                socket.send(JSON.stringify(captureAction));
            };

            socket.onmessage = function(event) {
                const data = JSON.parse(event.data);
                console.log(`Received message: ${data.message}`);
            };
        });
    </script>
</body>
</html>

Приложение запускается, однако я получаю:

Firefox can’t establish a connection to the server at ws://127.0.0.1:8000/ws/serial/.

WebSocket error:  
error { target: WebSocket, isTrusted: true, srcElement: WebSocket, eventPhase: 0, bubbles: false, cancelable: false, returnValue: true, defaultPrevented: false, composed: false, timeStamp: 261, … }

WebSocket connection closed unexpectedly

Та же проблема и в Chrome. Приложение отлично работает с Ajax. Однако оно не может установить соединение с веб-сокетами. Может быть, я что-то упускаю?

Я пробовал использовать разные браузеры. Я ожидаю, что приложение будет работать.

Вернуться на верх