# LoraTester Server Единый HTTP-сервер для телеметрии LoRa, GPS устройств, истории статистики, треков и чата. ## Быстрый старт ```bash cd server python -m venv .venv .venv\Scripts\activate # Windows pip install -r requirements.txt python flask_app.py ``` Откройте http://localhost:7634 ## Переменные окружения | Переменная | По умолчанию | |------------|----------------| | `LORATESTER_HOST` | `0.0.0.0` | | `LORATESTER_PORT` | `7634` | | `LORATESTER_DB` | `./loratester.db` | | `LORATESTER_TELEMETRY_LIMIT` | `5000` (записей истории на устройство) | | `LORATESTER_TRACK_POINTS_LIMIT` | `10000` (точек на один трек) | | `LORATESTER_ELEVATION_URL` | `http://192.168.1.109:8085/v1/elevation` | | `LORATESTER_ELEVATION_PROBE_TTL` | `60` (сек, кэш проверки доступности) | | `LORATESTER_ELEVATION_TIMEOUT` | `8` (сек, таймаут HTTP к сервису высот) | ## Docker Compose ```bash cd server docker compose up -d --build ``` Проверка: ```bash curl http://127.0.0.1:7634/api/health | jq ``` Ожидается `"elevation_ok": true` если локальный Open-Meteo доступен с хоста/контейнера. Переопределить URL высот (`.env` рядом с `docker-compose.yml`): ```env LORATESTER_ELEVATION_URL=http://192.168.1.109:8085/v1/elevation ``` БД хранится в volume `loratester-data` (`/data/loratester.db` внутри контейнера). ## Деплой (grigowashere.ru:7634) ```bash cd /srv/storage/disk2/services/LoraTester/server docker compose up -d --build ``` Или без Docker: ```bash cd /srv/storage/disk2/services/LoraTester/server pip install -r requirements.txt export LORATESTER_DB=/srv/storage/disk2/services/LoraTester/loratester.db export LORATESTER_ELEVATION_URL=http://192.168.1.109:8085/v1/elevation uvicorn fastapi_app:app --host 0.0.0.0 --port 7634 ``` После обновления кода **обязательно перезапустите** сервис. При старте выполняются миграции SQLite (`devices`, `telemetry.meta`, таблицы `tracks`). Проверка: ```bash curl http://127.0.0.1:7634/api/health ``` Ожидается `"db_ok": true`, `"schema_version": 4`, `"elevation_ok": true`. Если БД создана вручную и схема битая (`no such table: devices` / `no such column: t.meta`): 1. Остановить сервис 2. `cp loratester.db loratester.db.bak` 3. Удалить `loratester.db` (или оставить бэкап и дать миграциям дописать колонки после рестарта с новым кодом) 4. Запустить снова — `init_db()` создаст полную схему ## API ### Телеметрия (только Android, заголовок `X-Lora-Client: android`) - `POST /api/telemetry` — `{device_id, lat?, lon?, rssi?, meta?, fields?, role?, ts?}` - `GET /api/devices` — последнее состояние устройств - `GET /api/telemetry?device_id=&limit=&since=&until=&role=` — история (без `raw_frame`) - `GET /api/stats/history?device_id=` — то же, alias ### Треки (запись с Android) - `POST /api/tracks/start` — `{device_id}` → `{track_id}` - `POST /api/tracks/{id}/points` — `{points: [{ts, lat, lon, altitude_gps?, rssi?, role?, meta?}]}` - `POST /api/tracks/{id}/finish` - `GET /api/tracks?device_id=` - `GET /api/tracks/{id}` — метаданные + точки (высота terrain через локальный Open-Meteo) ### Команды (очередь на устройство) - `POST /api/commands` — `{from_device_id, to_device_id, kind, payload?}` `kind`: `at` (`payload.line` — одна строка, или `payload.lines` — массив макроса), `mode` (`payload.role`: TX/RX), `stats_push` (снимок meta/rssi/role/sf/bw) `from_device_id`: `web` или `android-xxxxxxxx` Макрос обычно: `S` (стоп TX/RX), затем `AT+FQ=`, `AT+PW=`, `AT+SF=`, `AT+BW=`, `AT+CR=`, `AT+PL=`, `AT+TM=`, при необходимости `AT+TX` / `AT+RX`. ### Профиль высот (веб, треки) - `POST /api/elevation/profile` — `{points: [{lat, lon}], step_m?: 10}` → срез рельефа (локальный Open-Meteo) - `GET /api/tracks/{id}/elevation-profile?step_m=10` — то же по сохранённому треку - `GET /api/elevation/nearest-hill?lat=&lon=&radius_m=5000` — ближайшая возвышенность (прокси Open-Meteo) - `GET /api/commands/pending?device_id=` — Android, доставка + `delivered_at` - `GET /api/commands?to_device_id=&limit=` — история (веб) ### Синхронный трек (два устройства) - `POST /api/paired-tracks/start` — `{device_ids?: [a,b], initiator?, device_id?}` → сессия `armed`, `start_at = now+3s` - `GET /api/paired-tracks/active` — `{active, session?}` - `POST /api/paired-tracks/ack` — Android: `{session_id, device_id, track_id}` - `POST /api/paired-tracks/cancel` — `{session_id?}` ### Прочее - `POST /api/chat` — `{device_id, text}` - `GET /api/chat?since=0` - `GET /api/health` — `{ok, db_ok, schema_version, database_path, elevation_ok, elevation_url, elevation_error}` ## FastAPI (прод) ```bash uvicorn fastapi_app:app --host 0.0.0.0 --port 7634 ``` Flask (`flask_app.py`) — тот же API для локальной разработки. ## Тесты ```bash cd server pip install httpx pytest python -m pytest tests/ -v ``` ## Android URL: `http://grigowashere.ru:7634`. На карте: **Начать/Остановить трекинг пути** — точки с GPS, статистикой приёма и высотой (локальный Open-Meteo на сервере). Вкладка **Статистика** — история с сервера. Telnet: `127.0.0.1:2727` — мост COM→telnet на устройстве.