gnss/Docs/start.md

23 KiB
Raw Blame History

Оффлайн-карта с треком по WebSocket

Стек: Python (FastAPI) + WebSocket + React (Leaflet + Tauri) Цель: хранить карту полностью локально на устройстве и в реальном времени отображать движение по координатам GNSS.

1. Цели и общие требования

1.1. Функциональные цели

  1. Отображать карту местности без доступа к интернету.
  2. Получать от устройства GNSS координаты в реальном времени.
  3. Передавать координаты в клиентское приложение через WebSocket, без периодических запросов.
  4. В React-приложении:
    1. показывать текущую позицию на карте.
    2. рисовать трек движения (историю точек).
    3. при необходимости в будущем — отображать дополнительные слои (контуры поля, границы, маршруты).

1.2. Нефункциональные требования

  1. Полный оффлайн:
    1. ни карта, ни координаты не должны требовать внешних сервисов.
    2. все файлы тайлов карты находятся локально на диске устройства.
  2. Устойчивость:
    1. временные потери GNSS-сигнала не должны ломать интерфейс.
    2. разрыв WebSocket-соединения не должен ломать отображение данных с фронта.
  3. Расширяемость:
    1. добавлять новые типы данных (например, скорость, курс, высоту).
    2. подключать реальные GNSS-устройства вместо генераторов.
    3. добавлять слои на карту: полигоны, точки, маршруты.
  4. Простота развёртывания:
    1. backend и frontend должны запускаться стандартными командами.
    2. конфигурация (путь к карте, параметры GNSS) выносится в настройки.

2. Архитектура решения

2.1. Высокоуровневая схема

Система логически делится на три части:

  1. Сервис получения GNSS-данных (внутри backend)
    • модуль в Python, который:
      1. подключается к приёмнику GNSS.
      2. читает сырые данные.
      3. преобразует их в нормализованный формат (широта, долгота, метки времени).
  2. Backend-сервер (FastAPI)
    • HTTP-интерфейсы:
      1. выдача тайлов карты по пути вида /tiles/zoom/x/y.png.
      2. вспомогательные эндпоинты (health-check, конфигурация и т.п.).
    • WebSocket-интерфейс:
      1. постоянное соединение с клиентом.
      2. отправка координат в режиме “push”.
    • работа с файловой системой:
      1. поиск нужных тайлов.
      2. выдача заглушки при отсутствии тайла.
  3. Клиент (React + Leaflet)
    • модуль карты:
      1. инициализацирует Leaflet-карту.
      2. использует локальный URL тайлов (через backend).
    • модуль трека:
      1. поддерживает подключение по WebSocket.
      2. хранит массив полученных точек.
      3. обновляет отображение карты при получении новых данных.

Связи:

  1. GNSS → модуль GNSS backend’а → WebSocket → React → Leaflet.
  2. React → HTTP (через Leaflet) → backend → файловая система с тайлами.

2.2. Логические компоненты

Backend:

  1. Модуль инициализации приложения FastAPI.
  2. Модуль выдачи тайлов:
    1. логика построения пути к тайлу по {zoom}/{x}/{y}.
    2. проверка наличия файла.
    3. выдача или оригинального тайла, или заглушки.
  3. Модуль GNSS:
    1. абстракция источника координат.
    2. возможность переключить данный источник:
      1. на реальное устройство.
  4. Модуль WebSocket:
    1. управление подключениями клиентов.
    2. цикл отправки координат.
    3. обработка разрывов соединения и ошибок. Frontend:
  5. Модуль подключения WebSocket:
    1. открытие соединения.
    2. обработка входящих сообщений с координатами.
    3. восстановление соединения при разрыве.
  6. Модуль управления треком:
    1. добавление точки в список.
  7. Модуль карты:
    1. инициализация Leaflet-карты.
    2. настройка источника тайлов на локальный backend.
    3. отображение маркера последней точки.

3. Оффлайн-карта: концепция и хранение

3.1. Формат карты

В начальной версии планируется использовать растровые тайлы:

  1. карта заранее разбивается на набор изображений (PNG или JPEG).
  2. тайлы организованы в структуре каталогов по схеме:
    1. уровень масштабирования (zoom).
    2. координаты тайла по X и Y. Преимущества:
  3. Leaflet “из коробки” умеет работать с тайлами такого формата.
  4. не требуется дополнительный сервер картографических данных — достаточно отдавать файлы по HTTP.
  5. относительно просто генерировать карту для нужного региона с помощью внешних инструментов (QGIS, GDAL и др.).

3.2. Структура хранения тайлов

На уровне файловой системы тайлы лежат в диапазоне, например:

  1. tiles/10/567/321.png — тайл уровня зума 10, координаты X=567, Y=321.
  2. tiles/11/1134/643.png — тот же район на другом зуме.

Backend ничего не знает о том, как эти тайлы были созданы, он:

  1. получает значение zoom, x и y из URL.
  2. строит путь к файлу.
  3. проверяет наличие файла.
  4. отдаёт файл, если он есть.
  5. если файлы нет — отдаёт заранее подготовленную картинку-заглушку.

Таким образом, карта является полностью статичной, а логика backend — максимально простой.

3.3. Генерация и обновление карты

Это отдельный процесс, который выполняется вне основной системы:

  1. Выбирается необходимый регион (поля, районы полётов).
  2. В GIS-инструменте формируется растровая подложка.
  3. Эта подложка нарезается в тайлы нужных уровней масштабирования.
  4. Готовый набор тайлов копируется на устройство в папку tiles/.

В дальнейшем можно:

  1. обновлять карту, подменяя содержимое tiles/.
  2. хранить несколько наборов карт (например, по сезонам) и включать нужный через конфиг.

4. Работа с координатами и GNSS

4.1. Источник данных

Фактически координаты приходят либо:

  1. от реального GNSS-приёмника, подключённого по:
    1. последовательному порту.
    2. USB.
    3. сетевому протоколу.
  2. либо от программного симулятора, который генерирует “виртуальный маршрут”.

Задача backend — предоставить единый интерфейс, не зависящий от конкретного железа.

4.2. Нормализация координат

Сырые данные могут быть в формате NMEA или другом специфичном виде. Модуль GNSS:

  1. парсит входные данные.
  2. приводит координаты к стандартной системе:
    1. широта (latitude) в градусах.
    2. долгота (longitude) в градусах.
    3. опционально — высота, скорость, курс.
  3. добавляет метку времени (например, UNIX timestamp или ISO-строку).
  4. формирует структуру, удобную для отправки на фронт.

Важно, чтобы координаты были в той системе, которую ожидает Leaflet (обычно WGS84).

4.3. Частота обновления

  1. Модуль GNSS определяет:
    1. как часто появляются новые точки.
    2. как часто обновлять данные на фронте:
    3. возможен вариант “как пришло — так отправили”.
    4. либо накопление точек и отправка пачками (например, раз в секунду).

Частота обновления — важный параметр, влияющий на:

  1. плавность трека.
  2. нагрузку на канал связи.
  3. нагрузку на фронт.

5. Потоки данных и жизненный цикл приложения

5.1. Запуск backend

  1. При старте FastAPI:
    1. инициализирует конфигурацию (настройки путей, порты, параметры GNSS).
    2. проверяет наличие базовых директорий (tiles/, заглушка тайла).
    3. при необходимости пробует подключиться к GNSS-источнику.
  2. Запускается HTTP-сервер:
    1. обслуживает выдачу тайлов.
    2. отвечает на запросы.

5.2. Запуск frontend

  1. React-приложение загружается в оболочке Tauri (Бинарник)
  2. После инициализации:
    1. открывается WebSocket-соединение с backend.
    2. инициализируется карта:
      1. начальный центр (по умолчанию либо по первой полученной точке).
      2. указание backend-урла тайлов.
  3. При успешном подключении по WebSocket:
    1. приложение начинает получать координаты.
    2. каждая новая точка добавляется в список.
    3. карта обновляет положение маркера и линии трека.

5.3. Реакция на ошибки и разрывы

  1. Если WebSocket-соединение закрывается:
    1. фронт может попытаться переподключиться через интервалы.
    2. на карте можно оставить последнюю известную позицию.
  2. Если нет GNSS-данных:
    1. backend может отправлять специальные сообщения о состоянии (например, “нет сигнала”).
    2. фронт может отображать статичную карту без движения или соответствующее уведомление.
  3. Если тайл для карты отсутствует:
    1. backend отдаёт заглушку.
    2. на карте вместо участка появляется условный “серый” квадрат, но интерфейс остаётся рабочим.

6. Основные этапы разработки

Этап 1. Подготовка окружения и структуры проекта

  1. Создать общий репозиторий с двумя основными директориями: backend и frontend.
  2. Настроить базовую систему сборки и запуска:
    • для backend — запуск FastAPI через Uvicorn.
    • для frontend — сборщик (Vite + Tauri).

Этап 2. Скелет backend-приложения

  1. Инициализировать приложение FastAPI.
  2. Добавить endpoint для проверки, что сервер поднялся.
  3. Настроить CORS так, чтобы frontend мог связаться с backend в локальной разработке

На этом этапе можно убедиться, что базовая связка “frontend - backend по HTTP” работает.

Этап 3. Механизм выдачи тайлов

  1. Определить директорию хранения тайлов (tiles/).
  2. Реализовать логику извлечения тайла по запросу вида /tiles/{zoom}/{x}/{y}.png.
  3. Добавить заглушку для отсутствующих тайлов.
  4. Прогнать простой тест: вручную запросить существующий и несуществующий тайл и убедиться, что:
    1. первый отдаётся корректно.
    2. второй отдаёт заглушку.

Этап 4. Проработка GNSS-абстракции

  1. Определить интерфейс модуля “источник координат”:
    1. формат выдаваемых данных.
    2. тип возвращаемой структуры (координаты + время).
  2. На этом этапе использовать симулятор вместо реального устройства:
    1. сделать виртуальную траекторию (линия, круг, восьмёрка и т.п.).
    2. выдавать новые точки с заданным интервалом.
  3. Важно: модуль должен быть реализован так, чтобы позже его можно было легко заменить реализацией, работающей с реальным железом.

Этап 5. Добавление WebSocket-эндпоинта в backend

  1. Создать WebSocket-обработчик:
    1. принимать подключение от клиента.
    2. при подключении запускать цикл, в котором:
      1. берётся следующая точка из GNSS-источника.
      2. отправляется на клиента.
  2. Обработать возможные сценарии:
    1. разрыв соединения клиентом.
    2. ошибки в процессе отправки.
    3. отключение GNSS-источника.

На этом этапе backend уже умеет:

  1. выдавать тайлы.
  2. в реальном времени отдавать поток координат любому подключенному клиенту по WebSocket.

Этап 6. Скелет frontend-приложения

  1. Инициализировать React-приложение.
  2. Подготовить базовую структуру компонентов:
    1. основной компонент приложения.
    2. компонент карты.
    3. будущий модуль для WebSocket.
  3. Настроить запуск фронта и убедиться, что базовый “каркас” работает.

Этап 7. Интеграция Leaflet в frontend

  1. Подключить Leaflet и его React-обёртку.
  2. Создать компонент карты, который:
    1. инициализирует карту с некоторым стартовым центром.
    2. использует URL http://localhost:8000/tiles/{z}/{x}/{y}.png в качестве источника тайлов.
  3. Проверить:
    1. что при старте приложение отображает карту (в рамках подготовленной области).
    2. что при перемещении и масштабировании запросы к тайлам уходят на backend.

На этом этапе карта уже отображается локально, без участия внешних сервисов.

Этап 8. Подключение WebSocket на frontend

  1. Создать модуль, который:
    1. открывает WebSocket-соединение с backend’ом по адресу ws://localhost:8000/ws/track.
    2. слушает сообщения и добавляет новые координаты в локальное состояние приложения.
  2. Реализовать простую логику:
    1. хранить массив точек.
    2. по каждой новой точке:
      1. добавлять её в массив.
      2. обновлять центр карты.
  3. Связать модуль WebSocket с компонентом карты:
    1. передавать массив точек как пропсы.
    2. отображать:
      1. маркер последней точки.
      2. линию, соединяющую все точки (трек).

Этап 9. Обработка ошибок и повышение устойчивости

  1. Добавить обработку ошибок WebSocket на frontend:
    1. логировать причины разрыва.
    2. реализовать простую стратегию повторного подключения (например, попытка подключиться раз в несколько секунд при разрыве).
  2. На backend:
    1. в явном виде логировать ошибки GNSS и WebSocket.
    2. при необходимости — реализовать таймауты и защиту от зависаний.

Этап 10. Подключение реального GNSS-устройства

  1. Вместо симулятора реализовать модуль чтения реальных данных:
    1. добавить конфиг с параметрами подключения: порт, скорость, протокол.
    2. реализовать парсинг данных.
  2. Убедиться, что внешний интерфейс модуля GNSS не изменился:
    1. по-прежнему выдаются координаты в ожидаемом формате.
  3. Протестировать работу системы “в поле”:
    1. убедиться, что карта и трек корректно отражают реальное движение.

Этап 11. Оптимизация и подготовка к эксплуатации

  1. Оптимизация производительности:
    1. ограничение длины трека, если ожидается очень длинное использование без перезапуска.
    2. настройка частоты обновления координат.
  2. Упаковка приложения:
    1. вариант 1: backend + frontend как отдельные пакеты.
    2. вариант 2: объединение фронта и бека в один пакет.
  3. Документация по развёртыванию:
    1. пошаговое руководство по установке зависимостей.
    2. инструкции по замене тайлов карты.
    3. инструкции по смене GNSS-источника.

7. Возможные дальнейшие расширения

После того как базовый функционал будет устойчиво работать, его можно расширять:

  1. Отображение контуров полей
    1. загрузка контуров из локальных файлов (например, GeoJSON).
    2. визуализация границ поля поверх карты.
  2. Планирование маршрута
    1. формирование линии проходов (полос) по ширине захвата.
    2. привязка трека к этим линиям (показ отклонения).
  3. Запись и экспорт данных
    1. сохранение трека в файл (например, GeoJSON, GPX).
    2. экспорт для дальнейшего анализа.
  4. Конфигурация через UI
    1. настройка масштабов карты, уровней тайлов.
    2. переключение наборов карт (разные сезоны, регионы).

8. Итоговая сводка

Суть решения: на устройстве устанавливается локальный сервер на Python (FastAPI), который одновременно:

  1. отдаёт статические файлы тайлов карты с диска,
  2. и по WebSocket непрерывно передаёт координаты из приёмника ГЛОНАСС.

React-приложение с Leaflet открывает эту локальную карту (через HTTP) и подписывается на WebSocket, чтобы в реальном времени обновлять положение маркера и трек движения. Всё — от карты до координат — живёт локально, не зависит от интернета и может работать в любых условиях, где есть только устройство, приёмник и локальная сеть (или даже один компьютер).