# Triangulation Service Сервис автоматически собирает RSSI-измерения с нескольких входных серверов (ресиверов), группирует данные по одинаковым частотам и рассчитывает 3D-положение источника через пересечение сфер (трилатерация). Каждый ресивер задается: - координатами `center` (центр сферы), - измерениями `RSSI + частота` (для расчета радиуса через модель распространения), - URL источника входных данных. Сервис: - ведет актуальную таблицу решений по каждой общей частоте, - выбирает итоговое лучшее решение по минимальному `rmse_m`, - отправляет компактный результат на один или несколько выходных серверов, - отдает API + веб-интерфейс для мониторинга и настройки. ## Ключевые возможности - Автоматический polling входных серверов (`input.mode = "http_sources"`). - Поддержка `N >= 3` входных ресиверов. - Расчет по общим частотам, которые есть минимум у 3 ресиверов и разрешены в `input.receivers[].frequencies_mhz`. - Поддержка общего фильтра входа `input.default_input_filter` и override per-receiver (`input_filter`). - Поддержка нескольких выходных серверов `runtime.output_servers[]` с настройкой по имени и IP. - Горячее применение нового конфига через `POST /config` (без ручного рестарта процесса). - Защита write-endpoints токеном (`runtime.write_api_token`). - Русскоязычный UI (`/ui`) с вкладками: обзор, частоты, ресиверы, доставка, серверы, JSON-конфиг. - Интеграционные и юнит-тесты. ## Структура проекта - [service.py](./service.py) — основной автосервис, API, статика UI. - [triangulation.py](./triangulation.py) — математика трилатерации и сетевой отправки. - [config.template.json](./config.template.json) — шаблон основного конфига. - [web/index.html](./web/index.html), [web/app.js](./web/app.js), [web/styles.css](./web/styles.css) — веб-интерфейс. - [docker-compose.yml](./docker-compose.yml) — профили test/prod. - [docker/config.docker.test.json](./docker/config.docker.test.json) — тестовый docker-конфиг. - [docker/mock_receiver.py](./docker/mock_receiver.py) — генератор входных тестовых данных. - [docker/mock_output_sink.py](./docker/mock_output_sink.py) — тестовый приемник выходных payload. - [test_service_integration.py](./test_service_integration.py), [test_triangulation.py](./test_triangulation.py) — тесты. - [docs/API.md](./docs/API.md) — подробный API. - [docs/CONFIG_REFERENCE.md](./docs/CONFIG_REFERENCE.md) — справочник параметров конфига. - [docs/JSON_EXAMPLES.md](./docs/JSON_EXAMPLES.md) — шаблоны JSON. ## Быстрый старт ### Вариант 1: Docker (рекомендуется) Тестовый режим (все контейнеры: 3 mock-входа + сервис + mock-выход): ```bash docker compose --profile test up --build -d ``` Проверить: - UI: `http://127.0.0.1:38081/ui` - Health: `http://127.0.0.1:38081/health` - Result: `http://127.0.0.1:38081/result` - Frequencies: `http://127.0.0.1:38081/frequencies` Остановить: ```bash docker compose --profile test down ``` Прод-режим (ваш `config.json`): 1. Создайте `config.json` из шаблона: ```bash cp config.template.json config.json ``` 2. Заполните реальные URL/координаты/выходные серверы. 3. Запустите: ```bash docker compose --profile prod up --build -d ``` Доступ: - UI: `http://127.0.0.1:38082/ui` - API: `http://127.0.0.1:38082/*` Остановить: ```bash docker compose --profile prod down ``` ### Вариант 2: Локальный запуск Python Ubuntu: ```bash bash setup.sh source .venv/bin/activate python service.py --config config.json ``` Windows PowerShell: ```powershell ./setup.ps1 .\.venv\Scripts\Activate.ps1 python service.py --config config.json ``` ## Веб-интерфейс Открыть: `http://:/ui` Что доступно: - обзор итоговой позиции, - таблица всех частотных решений, - просмотр сырых данных ресиверов и статуса доставки, - настройка входных/выходных серверов (добавление/удаление, имена, URL, IP), - редактирование сырого JSON-конфига, - сохранение конфига в рантайме через API. Примечание: после `POST /config` сервис применяет конфиг автоматически (`applied: true`, `restart_required: false`). ## API (кратко) - `GET /health` — состояние сервиса. - `GET /result` — последнее итоговое решение + delivery status. - `GET /frequencies` — таблица решений по частотам + delivery status. - `POST /refresh` — принудительное обновление. - `GET /config` — текущий конфиг (с редактированием секрета токена). - `POST /config` — валидация + применение + попытка сохранения в файл. Полные примеры запросов/ответов: [docs/API.md](./docs/API.md). ## Конфигурация Базовый шаблон: [config.template.json](./config.template.json) Ключевые блоки: - `model` — радиомодель (RSSI -> расстояние). - `solver` — параметры решателя сфер. - `input` — источники входных данных, фильтры, агрегация. - `runtime` — HTTP сервис, polling, write token, выходные серверы. Важно по частотам: - для каждого входного сервера задайте `input.receivers[].frequencies_mhz`; - сервис использует в расчёте только частоты из конфигурации ресиверов. Полное описание параметров: [docs/CONFIG_REFERENCE.md](./docs/CONFIG_REFERENCE.md). ## Форматы JSON Поддерживаются: - компактный формат входа (`samples` + `f_mhz`/`rssi`), - legacy-алиасы полей (`measurements`, `data`, `frequency_hz`, `rssi_dbm` и др.), - минимальный выходной payload на конечные серверы: - `x` - `y` - `z` Готовые шаблоны: [docs/JSON_EXAMPLES.md](./docs/JSON_EXAMPLES.md). ## Безопасность write-endpoints Чтобы ограничить изменение состояния, задайте: ```json { "runtime": { "write_api_token": "change-me" } } ``` Тогда `POST /refresh` и `POST /config` требуют один из заголовков: - `X-API-Token: ` - `Authorization: Bearer ` `GET` endpoints доступны без токена. ## Тесты Запуск: ```bash pytest -q ``` Сценарии покрывают: - корректность трилатерации и преобразования RSSI, - валидацию payload и граничные случаи, - ошибки некорректного контекста, - output delivery, - безопасность API, - горячее применение конфига, - multi-input/multi-output поведение. ## Диагностика ### Ошибка: `port is already allocated` Значит локальный порт уже занят (например, `8080` или `38081`). Решения: - остановить конфликтующий сервис, - изменить публикацию порта в `docker-compose.yml`. ### Ошибка: `Config file not found` Проверьте путь в аргументе `--config` и наличие файла внутри контейнера/хоста. ## Лицензия Если нужна отдельная лицензия, добавьте файл `LICENSE` и разделите условия использования.