You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

213 lines
8.9 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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://<host>:<port>/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: <token>`
- `Authorization: Bearer <token>`
`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` и разделите условия использования.