Refactoring UI
parent
a568083cce
commit
f327c8f0bb
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,216 @@
|
||||
# API Reference
|
||||
|
||||
Базовый URL: `http://<listen_host>:<listen_port>`
|
||||
|
||||
Контент ответов: `application/json; charset=utf-8`
|
||||
|
||||
## GET /health
|
||||
|
||||
Проверка состояния сервиса.
|
||||
|
||||
### 200 OK
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"updated_at_utc": "2026-03-02T12:34:56+00:00",
|
||||
"error": ""
|
||||
}
|
||||
```
|
||||
|
||||
### 503 Service Unavailable
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "warming_up",
|
||||
"updated_at_utc": null,
|
||||
"error": "no data yet"
|
||||
}
|
||||
```
|
||||
|
||||
## GET /result
|
||||
|
||||
Последний итоговый результат расчета.
|
||||
|
||||
### 200 OK
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"updated_at_utc": "2026-03-02T12:35:01+00:00",
|
||||
"data": {
|
||||
"timestamp_utc": "2026-03-02T12:35:01+00:00",
|
||||
"selected_frequency_hz": 868100000.0,
|
||||
"selected_frequency_mhz": 868.1,
|
||||
"position": { "x": 1.2, "y": 2.3, "z": 0.4 },
|
||||
"rmse_m": 0.52,
|
||||
"frequency_table": []
|
||||
},
|
||||
"output_delivery": {
|
||||
"enabled": true,
|
||||
"status": "ok",
|
||||
"ok_count": 1,
|
||||
"error_count": 0,
|
||||
"skipped_count": 0,
|
||||
"servers": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 503 Service Unavailable
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "warming_up",
|
||||
"updated_at_utc": null,
|
||||
"error": "no data yet"
|
||||
}
|
||||
```
|
||||
|
||||
## GET /frequencies
|
||||
|
||||
Таблица решений по частотам + статус доставки.
|
||||
|
||||
### 200 OK
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"updated_at_utc": "2026-03-02T12:35:01+00:00",
|
||||
"selected_frequency_hz": 868100000.0,
|
||||
"selected_frequency_mhz": 868.1,
|
||||
"frequency_table": [
|
||||
{
|
||||
"frequency_hz": 433920000.0,
|
||||
"frequency_mhz": 433.92,
|
||||
"position": { "x": 1.0, "y": 2.0, "z": 0.3 },
|
||||
"rmse_m": 0.65,
|
||||
"exact": false
|
||||
}
|
||||
],
|
||||
"output_delivery": {
|
||||
"enabled": true,
|
||||
"status": "partial"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## GET /config
|
||||
|
||||
Возвращает текущий конфиг.
|
||||
|
||||
Особенность:
|
||||
- поле `runtime.write_api_token` редактируется в ответе (`""`),
|
||||
- добавляется `runtime.write_api_token_set` (bool).
|
||||
|
||||
### 200 OK
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"config_path": "config.json",
|
||||
"config": {
|
||||
"runtime": {
|
||||
"write_api_token": "",
|
||||
"write_api_token_set": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## POST /refresh
|
||||
|
||||
Принудительный запуск одного цикла опроса и расчета.
|
||||
|
||||
Тело запроса: `{}` (или любой JSON-объект).
|
||||
|
||||
### Заголовки при включенном токене
|
||||
|
||||
- `X-API-Token: <token>` или
|
||||
- `Authorization: Bearer <token>`
|
||||
|
||||
### 200 OK
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"updated_at_utc": "2026-03-02T12:35:20+00:00"
|
||||
}
|
||||
```
|
||||
|
||||
### 401 Unauthorized
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"error": "unauthorized: missing or invalid API token"
|
||||
}
|
||||
```
|
||||
|
||||
### 500 Internal Server Error
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"error": "Output server(s) rejected payload: sink-a"
|
||||
}
|
||||
```
|
||||
|
||||
## POST /config
|
||||
|
||||
Валидация и горячее применение нового конфига.
|
||||
|
||||
Ограничения:
|
||||
- тело должно быть JSON-объектом,
|
||||
- максимальный размер `1_000_000` байт.
|
||||
|
||||
### Заголовки при включенном токене
|
||||
|
||||
- `X-API-Token: <token>` или
|
||||
- `Authorization: Bearer <token>`
|
||||
|
||||
### 200 OK
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"saved": true,
|
||||
"save_error": "",
|
||||
"restart_required": false,
|
||||
"applied": true,
|
||||
"config_path": "config.json"
|
||||
}
|
||||
```
|
||||
|
||||
### 400 Bad Request
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"error": "Config validation failed: input.receivers must contain at least 3 objects."
|
||||
}
|
||||
```
|
||||
|
||||
### 413 Payload Too Large
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"error": "Config payload too large: 1500000 bytes, max is 1000000"
|
||||
}
|
||||
```
|
||||
|
||||
### 401 Unauthorized
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"error": "unauthorized: missing or invalid API token"
|
||||
}
|
||||
```
|
||||
|
||||
## UI и статические файлы
|
||||
|
||||
- `GET /` и `GET /ui` — веб-интерфейс.
|
||||
- `GET /static/*` — JS/CSS.
|
||||
|
||||
@ -0,0 +1,198 @@
|
||||
# Config Reference
|
||||
|
||||
Ниже описана структура `config.json` для `service.py`.
|
||||
|
||||
Полный пример: [../config.template.json](../config.template.json)
|
||||
|
||||
## Корневые блоки
|
||||
|
||||
- `model` — параметры радиомодели RSSI -> distance.
|
||||
- `solver` — параметры решателя пересечения сфер.
|
||||
- `runtime` — HTTP-порт, polling, защита, выходные серверы.
|
||||
- `input` — входные ресиверы, источники, фильтры, агрегация.
|
||||
|
||||
## model
|
||||
|
||||
```json
|
||||
"model": {
|
||||
"tx_power_dbm": 20.0,
|
||||
"tx_gain_dbi": 0.0,
|
||||
"rx_gain_dbi": 0.0,
|
||||
"path_loss_exponent": 2.0,
|
||||
"reference_distance_m": 1.0,
|
||||
"min_distance_m": 0.001
|
||||
}
|
||||
```
|
||||
|
||||
- `tx_power_dbm` (float, required)
|
||||
- `tx_gain_dbi` (float, optional)
|
||||
- `rx_gain_dbi` (float, optional)
|
||||
- `path_loss_exponent` (float, optional)
|
||||
- `reference_distance_m` (float, optional)
|
||||
- `min_distance_m` (float, optional)
|
||||
|
||||
## solver
|
||||
|
||||
```json
|
||||
"solver": {
|
||||
"tolerance": 0.001,
|
||||
"z_preference": "positive"
|
||||
}
|
||||
```
|
||||
|
||||
- `tolerance` (float, optional)
|
||||
- `z_preference` (`"positive"` | `"negative"`)
|
||||
|
||||
## runtime
|
||||
|
||||
```json
|
||||
"runtime": {
|
||||
"listen_host": "0.0.0.0",
|
||||
"listen_port": 8081,
|
||||
"poll_interval_s": 1.0,
|
||||
"write_api_token": "",
|
||||
"output_servers": []
|
||||
}
|
||||
```
|
||||
|
||||
- `listen_host` (string, optional)
|
||||
- `listen_port` (int, optional)
|
||||
- `poll_interval_s` (float, optional)
|
||||
- `write_api_token` (string, optional)
|
||||
|
||||
### runtime.output_servers (рекомендуется)
|
||||
|
||||
Список выходных серверов. Можно задать несколько целей доставки.
|
||||
|
||||
```json
|
||||
"output_servers": [
|
||||
{
|
||||
"name": "sink-main",
|
||||
"ip": "192.168.1.100"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Поля:
|
||||
- `name` (string)
|
||||
- `ip` (string)
|
||||
|
||||
Автоматически:
|
||||
- если `enabled` не задан, выход считается включенным при непустом `ip`;
|
||||
- `port`, `path`, `timeout_s` берутся по умолчанию (`8080`, `/triangulation`, `3.0`).
|
||||
|
||||
Примечание:
|
||||
- legacy-поля (`enabled`, `port`, `path`, `timeout_s`, `frequency_filter_*`) по-прежнему поддерживаются для обратной совместимости.
|
||||
|
||||
### runtime.output_server (legacy)
|
||||
|
||||
Одиночная цель. Поддерживается для обратной совместимости.
|
||||
|
||||
## input
|
||||
|
||||
```json
|
||||
"input": {
|
||||
"mode": "http_sources",
|
||||
"aggregation": "median",
|
||||
"source_timeout_s": 3.0,
|
||||
"default_input_filter": {},
|
||||
"receivers": []
|
||||
}
|
||||
```
|
||||
|
||||
- `mode` — для автосервиса только `"http_sources"`.
|
||||
- `aggregation` — `"median"` или `"mean"`.
|
||||
- `source_timeout_s` — timeout входных HTTP-запросов.
|
||||
- `default_input_filter` — общий фильтр, автоматически применяемый ко всем ресиверам.
|
||||
- `receivers` — массив ресиверов, минимум 3.
|
||||
|
||||
### input.default_input_filter
|
||||
|
||||
```json
|
||||
"default_input_filter": {
|
||||
"enabled": false,
|
||||
"min_frequency_mhz": 0.0,
|
||||
"max_frequency_mhz": 1000000000.0,
|
||||
"min_rssi_dbm": -200.0,
|
||||
"max_rssi_dbm": 50.0
|
||||
}
|
||||
```
|
||||
|
||||
Ограничения:
|
||||
- `max_frequency_mhz >= min_frequency_mhz`
|
||||
- `max_rssi_dbm >= min_rssi_dbm`
|
||||
|
||||
### input.receivers[]
|
||||
|
||||
```json
|
||||
{
|
||||
"receiver_id": "r0",
|
||||
"center": { "x": 0.0, "y": 0.0, "z": 0.0 },
|
||||
"frequencies_mhz": [433.92, 868.1],
|
||||
"access": { "url": "http://host:9000/measurements", "api_token": "" },
|
||||
"input_filter": {
|
||||
"enabled": false,
|
||||
"min_frequency_mhz": 0.0,
|
||||
"max_frequency_mhz": 1000000000.0,
|
||||
"min_rssi_dbm": -200.0,
|
||||
"max_rssi_dbm": 50.0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Обязательные поля:
|
||||
- `receiver_id`
|
||||
- `center.x`, `center.y`, `center.z`
|
||||
- URL источника:
|
||||
- `access.url` (предпочтительно) или
|
||||
- `source_url` (legacy)
|
||||
|
||||
Дополнительно:
|
||||
- `access.api_token` или `source_api_token` — токен входного сервера (добавляется как `Authorization: Bearer ...`).
|
||||
- `input_filter` — override фильтра для конкретного ресивера.
|
||||
- `frequencies_mhz` — список разрешённых частот для ресивера; в расчёт попадут только они.
|
||||
|
||||
## Пример минимально рабочего конфига
|
||||
|
||||
```json
|
||||
{
|
||||
"model": { "tx_power_dbm": 20.0 },
|
||||
"solver": { "tolerance": 0.001, "z_preference": "positive" },
|
||||
"runtime": {
|
||||
"listen_host": "0.0.0.0",
|
||||
"listen_port": 8081,
|
||||
"poll_interval_s": 1.0,
|
||||
"output_servers": [
|
||||
{
|
||||
"name": "sink",
|
||||
"ip": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
"input": {
|
||||
"mode": "http_sources",
|
||||
"aggregation": "median",
|
||||
"source_timeout_s": 3.0,
|
||||
"receivers": [
|
||||
{
|
||||
"receiver_id": "r0",
|
||||
"center": { "x": 0.0, "y": 0.0, "z": 0.0 },
|
||||
"frequencies_mhz": [433.92, 868.1],
|
||||
"access": { "url": "http://127.0.0.1:9001/measurements" }
|
||||
},
|
||||
{
|
||||
"receiver_id": "r1",
|
||||
"center": { "x": 10.0, "y": 0.0, "z": 0.0 },
|
||||
"frequencies_mhz": [433.92, 868.1],
|
||||
"access": { "url": "http://127.0.0.1:9002/measurements" }
|
||||
},
|
||||
{
|
||||
"receiver_id": "r2",
|
||||
"center": { "x": 0.0, "y": 8.0, "z": 0.0 },
|
||||
"frequencies_mhz": [433.92, 868.1],
|
||||
"access": { "url": "http://127.0.0.1:9003/measurements" }
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue