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.
8.7 KiB
8.7 KiB
Triangulation Service
Сервис решает 3D-трилатерацию по 3 ресиверам:
- центры сфер: координаты ресиверов;
- радиусы сфер: расстояния, оцененные из RSSI с учетом частоты;
- расчет идет по одинаковым частотам, которые есть у всех 3 ресиверов;
- формируется таблица
frequency_table(по каждой частоте отдельное решение); - выбирается итоговая частота
selected_frequency_hzпо минимальномуrmse_m.
Что реализовано
- Автоматический polling 3 входных серверов (
http_sources). - Валидация входных payload с подробными ошибками.
- API:
GET /healthGET /resultGET /frequenciesPOST /refreshGET /configPOST /config
- UI (
/ui) с:- входными данными ресиверов;
- таблицей пересечений по частотам;
- итоговой позицией;
- статусом отправки на конечный сервер.
- Опциональный push результата на внешний сервер (
runtime.output_server).
Структура проекта
- service.py - автосервис + API + UI статик.
- triangulation.py - математика.
- config.template.json - шаблон конфига.
- web/index.html, web/styles.css, web/app.js - UI.
- docker-compose.yml - test/prod профили.
- docker/config.docker.test.json - тестовый конфиг.
- docker/mock_receiver.py - mock входные сервера (random RSSI).
- docker/mock_output_sink.py - mock конечный сервер.
Docker Compose: test/prod режимы
docker-compose.yml разделен на профили:
-
test:triangulation-testreceiver-r0,receiver-r1,receiver-r2output-sink
-
prod:triangulation-prod(читает ваш./config.json)
Это позволяет легко отключить тестовый режим и перейти на реальные сервера.
Быстрый старт: Test Mode
Поднимает все контейнеры для end-to-end проверки:
- 3 входных mock сервера с random данными;
- основной сервис;
- output-sink, принимающий отправленные результаты.
docker compose --profile test up --build
Открыть:
- UI:
http://127.0.0.1:38081/ui - Полный результат:
http://127.0.0.1:38081/result - Частоты:
http://127.0.0.1:38081/frequencies - Полученные output-sink данные (изнутри сети контейнеров):
docker compose --profile test exec output-sink wget -qO- http://127.0.0.1:8080/latest
Остановить:
docker compose --profile test down
Быстрый старт: Prod Mode
- Создайте
config.jsonиз шаблона:
cp config.template.json config.json
- Заполните ваши реальные:
input.receivers[].source_urlinput.receivers[].centerruntime.output_server
- Запустите:
docker compose --profile prod up --build
Доступ к API/UI в prod:
http://127.0.0.1:38082/uihttp://127.0.0.1:38082/resulthttp://127.0.0.1:38082/frequencies
Остановить:
docker compose --profile prod down
Как проверить, что данные приходят и отправляются
В UI (/ui) видно:
- блок
Ресиверы: входящие samples; - таблица
Таблица пересечений по частотам: решения по каждой общей частоте; - блок
Отправка на конечный сервер: статус доставки (ok/error), HTTP-код, время, target.
Дополнительно:
GET /resultвозвращаетoutput_delivery.GET /frequenciesтоже возвращаетoutput_delivery.docker compose --profile test logs output-sink -fпоказывает факт приема.GET /latestнаoutput-sinkдоступен изнутри docker-сети.
Конфиг (основные поля)
Пример: config.template.json
Критичные поля:
input.mode: только"http_sources"для автосервиса.input.receivers: ровно 3 ресивера.input.aggregation:"median"или"mean".runtime.poll_interval_s: период опроса.runtime.output_server.enabled: push во внешний сервер.
Формат входных payload
Поддержка:
- объект с
measurements/samples/data; - или сразу массив измерений.
Измерение:
frequency_hz(илиfreq_hz/frequency/freq)amplitude_dbm(илиrssi_dbm/amplitude/rssi)
Пример:
{
"receiver_id": "r0",
"measurements": [
{ "frequency_hz": 433920000, "rssi_dbm": -61.5 },
{ "frequency_hz": 868100000, "rssi_dbm": -67.2 }
]
}
Если receiver_id передан, сервис сверяет его с ожидаемым receiver из конфига.
Валидация и ошибки некорректного контекста
Проверяется:
- тип payload;
- наличие измерений;
- числовые и конечные значения;
frequency_hz > 0;- соответствие
receiver_idпри наличии; - наличие общих частот у всех 3 ресиверов.
Ошибки содержат:
source_url=...- номер строки
row #... - проблемное поле.
Тесты
Запуск:
pytest -q
Покрытие:
- математика триангуляции;
- влияние частоты на RSSI->distance;
- интеграция
AutoService.refresh_once(); - валидационные сценарии;
- ошибки контекста (нет общих частот, bad field, receiver mismatch, network error, output reject).
Файл интеграционных тестов:
Локальный запуск без Docker
python service.py --config config.json
UI:
http://127.0.0.1:38081/ui
Защита write-endpoints токеном
Для защиты изменений состояния можно задать токен в конфиге:
{
"runtime": {
"write_api_token": "change-me"
}
}
После этого POST /refresh и POST /config требуют токен в одном из заголовков:
X-API-Token: <token>Authorization: Bearer <token>
Что важно:
GETendpoints остаются без токена.GET /configотдаетruntime.write_api_tokenв редактированном виде ("") и флагwrite_api_token_set.- В UI во вкладке
Serversесть полеWrite API token (session only):- токен хранится только в памяти браузера;
- используется для
POST /refreshиPOST /config.
Фильтры входных данных по каждому серверу
Для каждого ресивера в input.receivers[] можно задать input_filter:
{
"input_filter": {
"enabled": true,
"min_frequency_mhz": 430.0,
"max_frequency_mhz": 440.0,
"min_rssi_dbm": -80.0,
"max_rssi_dbm": -40.0
}
}
Смысл:
- фильтр применяется отдельно к данным каждого ресивера до триангуляции;
- участвуют только измерения, попавшие в диапазоны частоты и RSSI;
- если после фильтрации у ресивера нет данных, цикл расчета возвращает ошибку.