|
|
# 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` и разделите условия использования.
|