ФАЙЛ «SERVER_SITE.PY»

Основные задачи файла:

1. Получение данных о портах и RTSP-потоке: Из командной строки.

2. Настройка сокетов и WebSocket соединений: Для приема и отправки данных.

3. Захват видео с камеры: И отправка закодированных изображений клиентам.

4. Асинхронное управление: Веб-сервером и сокетами, с использованием Tornado и asyncio.

ИНИЦИАЛИЗАЦИЯ БИБЛИОТЕК

import asyncio
import base64
import contextlib
import io
import json
import sys
import threading
from PIL import Image
import cv2
import tornado.ioloop
import tornado.web
import tornado.websocket
import socket

- asyncio: Модуль для асинхронного программирования.

- base64: Модуль для кодирования и декодирования данных в формате base64.

- contextlib: Контекстные менеджеры и утилиты для работы с ними.

- io: Модуль для работы с потоками ввода/вывода.

- json: Модуль для работы с JSON-данными.

- sys: Модуль для работы с параметрами командной строки и другими системными функциями.

- threading: Модуль для многопоточности.

- PIL.Image: Библиотека для обработки изображений.

- cv2: Библиотека OpenCV для обработки изображений и видео.

- tornado.ioloop, tornado.web, tornado.websocket: Tornado - веб-фреймворк для создания веб-приложений и работы с WebSocket.

- socket: Модуль для сетевого взаимодействия.

ПАРАМЕТРЫ ПОДКЛЮЧЕНИЯ

global mass
mass=None

if len(sys.argv) > 1:
    # Первый аргумент после имени скрипта будет содержать данные о портах    
    port_data = sys.argv[1]
    port_data = port_data.split(',')
    print(port_data)
    ports = port_data[0].split(':')    
    ports2 = port_data[1].split(':')
    ip_site=ports[0]
    port_site=ports[1]
    host = ports2[0]
    port = int(ports2[1])
    rtsp = port_data[2]
else:    
    print("Данные о портах не были переданы")

Этот блок кода обрабатывает аргументы командной строки, чтобы установить параметры подключения:

- sys.argv: Используется для получения аргументов командной строки.

- port_data: Информация о портах для веб-сервера и RTSP потока.

НАСТРОЙКА СОКЕТА

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 65536)
server_socket.bind((host, port))
server_socket.listen(5)
print(f"Server listening on {host}:{port}")

Здесь происходит создание и настройка сокета для прослушивания входящих соединений.

ИНИЦИАЛЗАЦИЯ КЛАССА MainHandler

class MainHandler(tornado.web.RequestHandler):
    """
        Обработка GET-запроса.

        Описание:
        Метод вызывается при получении GET-запроса от клиента. Он отвечает за рендеринг HTML-шаблона 'in.html'.
    """
    def get(self):
        self.render("in.html")
        

В этом классе происходит обработка GET-запроса и рендеринг HTML-шаблона.

ИНИЦИАЛИЗАЦИЯ КЛАССА SimpleWebSocket

class SimpleWebSocket(tornado.websocket.WebSocketHandler):
    def open(self):
        """
        Обработка открытия WebSocket.

        Описание:
        Метод вызывается при установлении нового WebSocket соединения с клиентом. Он инициирует асинхронную задачу отправки обновлений клиенту.
        """
        print("WebSocket opened")
        asyncio.create_task(self.send_updates())

    async def send_updates(self):
        """
        Асинхронная отправка обновлений клиенту.

        Описание:
        Метод выполняется в бесконечном цикле, отправляя клиенту кадры (frame) и данные графика (plot), если они доступны. Использует глобальную переменную 'mass' для отправки данных графика.
        """
        while True:
            frame = get_frame()
            
            # plot_data = create_plotly_figure()
            if frame:
                await self.write_message(json.dumps({'type': 'frame', 'frame': frame}))
            global mass
            if not mass==None:
                await self.write_message(json.dumps({'type': 'plot', 'plot': mass}))
            await asyncio.sleep(0)  # Передача управления для обработки других задач

    def on_message(self, message):
        """
        Обработка входящих сообщений от клиента.

        Описание:
        Метод вызывается при получении сообщения от клиента. В текущей реализации метод не выполняет никаких действий.
        """
        # print(message)
        pass

    def on_close(self):
        """
        Обработка закрытия WebSocket.

        Описание:
        Метод вызывается при закрытии WebSocket соединения. Выводит сообщение о закрытии соединения.
        """
        print("WebSocket closed")

В этом классе происходит обработка открытия, получения сообщений и закрытия WebSocket соединения, так же асинхронная отправка обновлений клиенту.

ЗАХВАТ ВИДЕО

cap=cv2.VideoCapture(rtsp)
def get_frame():
    """
    Захват кадра из видеопотока и его преобразование в строку base64.

    Библиотеки:
    - cv2: для захвата видео
    - io: для работы с потоками ввода/вывода
    - PIL: для работы с изображениями
    - base64: для кодирования данных в формат base64

    Возвращает:
    - str: Строка, содержащая изображение в формате JPEG, закодированное в base64.

    Описание:
    Функция захватывает кадр из видеопотока, преобразует его в формат JPEG и кодирует в строку base64.
    Если захват кадра не удался, функция возвращает None.
    """
    _, img = cap.read()
    if img is not None:
        buffer = io.BytesIO()
        pil_img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
        pil_img.save(buffer, format='JPEG')
        img_str = base64.b64encode(buffer.getvalue()).decode('utf-8')
        return img_str
    else:
        return None

В этом блоке кода инициализируется функция для захвата кадра из RTSP потока и его кодирования в формат base64.

ЗАПУСК TORNADO

async def start_tornado():
    """
    Настройка и запуск асинхронного веб-сервера Tornado.

    Библиотеки:
    - tornado.web: для создания веб-приложения
    - tornado.websocket: для работы с WebSocket
    - asyncio: для асинхронного программирования

    Параметры:
    - Нет параметров.

    Описание:
    Функция создает экземпляр веб-приложения Tornado с обработчиками для HTTP и WebSocket,
    запускает его на указанном IP-адресе и порту, и ожидает событий.
    """
    app= tornado.web.Application([
        (r"/", MainHandler),
        (r"/websocket", SimpleWebSocket),
    ])
    app.listen(port_site,ip_site)
    print(f"Сервер Tornado запущен на http://{ip_site}:{port_site}")
    await asyncio.Event().wait()

Это асинхронная функция для настройки и запуска веб-сервера Tornado.

ОБРАБОТКА СОКЕТОВ

def async_task():
    """
    Обработка входящих соединений и данных от клиентов.

    Библиотеки:
    - socket: для работы с сетевыми сокетами
    - threading: для многопоточности

    Параметры:
    - Нет параметров.

    Возвращает:
    - Ничего не возвращает.

    Описание:
    Функция ожидает входящие соединения на сокете сервера. При установлении соединения
    данные от клиента считываются и сохраняются в глобальной переменной `mass`.
    После обработки данных соединение закрывается.
    """
    global mass
    while True:
        client_socket, addr = server_socket.accept()
        print(f"Connected by {addr}")
        try:
            while True:
                data = client_socket.recv(65536).decode('utf-8')
                if not data:
                    mass=None
                    break
                mass=data
        finally:
            client_socket.close()
            print("Connection closed.")
            # return

Это функция для обработки входящих соединений через сокеты и получения данных от клиентов

ГЛАВНАЯ ФУНКЦИЯ

async def main():
    # Создание задачи для асинхронной функции
    socket_thread = threading.Thread(target=async_task, daemon=True)
    socket_thread.start()
    with contextlib.suppress(KeyboardInterrupt):
        # Запуск сервера Tornado
        await start_tornado()
        socket_thread.join()

Это основная функция запуска, которая инициализирует поток для обработки сокетов и запускает сервер Tornado.

ЗАВЕРШЕНИЕ РАБОТЫ

if __name__ == "__main__":
    asyncio.run(main())
    server_socket.close()
    print("Server stopped.")

Здесь происходит закрытие сокета и сервера Tornado

Процесс подготовки кода:

  1. Создайте файл и назовите его Server_site.py

  1. Поэтапно скопируйте код в файл:

  1. Установите необходимые библиотеки: в данном случае у нас не предустановлены следующие библиотеки: PIL (Pillow), cv2 и tornado. Если у вас не установлены эти библиотеки, выполните следующие действия:

    1. Откройте терминал VS Code (Terminal -> New Termial или комбинацией Ctrl + Shift + `)

    2. В терминале поочередно введите и запустите следующие команды: pip install pillow; pip install opencv-python, pip install tornado

Last updated