# Архитектура elmAI > v0.42.0-dev (основной APK) / v0.4.0-dev (raw-реле), 10 июля 2026 ## Общая схема ``` 📱 Android (elmer-android) │ ├── Bluetooth ──── 🔌 ELM327 ──── ECU (OBD-II) │ ├── app/ (основное приложение) │ └── HTTPS POST /api/v1/session/upload │ └── raw/ (реле) └── HTTP-поллинг /api/v1/elm/raw/* │ ▼ 🌐 Сервер (5.172.178.213) ├── nginx :443 → gunicorn :8000 ├── api/raw_elm.py — командная очередь для реле (SQLite) ├── api/ — REST, БД, скрипты ├── brain/ — LLM-клиент ├── obd/ — ELM327 протокол (Python) └── web/ — точка входа Flask, статика ``` ## Два режима работы ### Режим 1: Основное приложение (`app/`) Прямая диагностика: телефон → ELM → скрипт → батч → сервер → LLM ### Режим 2: Raw-реле (`raw/`) Тупой ретранслятор: сервер диктует команды, телефон передаёт в ELM и возвращает ответы. Используется для интерактивной диагностики и тестирования. ``` Copilot/сервер Android (raw) ELM327 │ │ │ ├─ POST /cmd ──────────→│ │ │ ├─ sendCommand() ─────→│ │ │←─ raw response ──────┤ │←─ POST /response ─────┤ │ │ │ │ ├─ GET /response?wait=N─→ (поллинг ответа) │ │←─ {raw: "41 0C ..."}─┤ │ ``` ## Структура сервера ``` elmer/ ├── api/ │ ├── config.py # Загрузка config.yaml │ ├── db.py # SQLite (sessions, cars, dtc, command_queue) │ ├── routes.py # Эндпоинты: script, upload, chat, probe │ ├── dtc.py # Эндпоинты DTC │ ├── ping.py # Эндпоинты проверки │ ├── scripts.py # Сборка диагностических скриптов │ ├── parser.py # Парсинг ответов ELM327 │ └── raw_elm.py # Командная очередь для реле (SQLite command_queue) │ ├── brain/ │ ├── client.py # Diagnoser — HTTP к LLM │ └── prompts.py # SYSTEM_PROMPT для диагностики │ ├── obd/ │ └── protocol.py # Python-версия AndrOBD стейт-машины │ ├── web/ │ ├── app.py # Точка входа Flask │ ├── templates/ │ │ └── index.html # Страница загрузки APK │ └── static/ │ ├── app-debug.apk # Основной APK │ └── elm-raw-v022.apk # Raw-реле APK │ ├── doc/ # Документация ├── config.yaml # LLM API key, порты └── requirements.txt ``` ## API эндпоинты ### Основные (app/) | Метод | Путь | Описание | |---|---|---| | GET | /api/v1/ping | Проверка сервера | | GET | /api/v1/ping-llm | Проверка LLM | | GET | /api/v1/script?mode= | Скрипт диагностики | | POST | /api/v1/session/upload | Загрузка батча + LLM | | POST | /api/v1/chat | Вопрос к LLM | ### Raw-реле (raw/) | Метод | Путь | Описание | |---|---|---| | POST | /api/v1/elm/raw/hello | Android: «я готов» | | POST | /api/v1/elm/raw/cmd | Copilot: поставить команду в очередь | | GET | /api/v1/elm/raw/cmd | Android: забрать команду | | POST | /api/v1/elm/raw/response | Android: отправить ответ | | GET | /api/v1/elm/raw/response | Copilot: прочитать ответ (с device_id) | | GET | /api/v1/elm/raw/status | Статус устройства | ## SQLite: command_queue Таблица для очереди команд raw-реле: ```sql CREATE TABLE command_queue ( id INTEGER PRIMARY KEY AUTOINCREMENT, device_id TEXT NOT NULL, seq INTEGER NOT NULL, cmd TEXT NOT NULL, status TEXT DEFAULT 'pending', -- pending | done raw_response TEXT, elapsed_ms INTEGER, prompt INTEGER DEFAULT 0, error TEXT, created_at TEXT DEFAULT (datetime('now')), responded_at TEXT ); ``` - `hello` чистит старые команды для device_id - `cmd` (POST) добавляет команду со статусом `pending` - `cmd` (GET) атомарно забирает pending → обновляет статус - `response` (POST) сохраняет ответ - `response` (GET) возвращает ответы с фильтром по device_id и seq ## Android (отдельный репо: elmer-android) ``` android/ ├── app/ — основное приложение (диагностика) │ └── src/.../ru/elmer/client/ │ ├── ElmProtocol.kt # ELM327 стейт-машина (AndrOBD) │ ├── ObdDecoder.kt # Декодер PID/DTC/VIN │ ├── ServerClient.kt # HTTP к серверу │ ├── ScriptEngine.kt # Движок скриптов │ ├── ScriptRunnerService.kt # Фоновая диагностика │ ├── SessionDb.kt # Локальная история │ └── MainActivity.kt # UI │ └── raw/ — реле (ретранслятор команд) └── src/.../ru/elmer/raw/ ├── ElmProtocol.kt # 1:1 копия app/ElmProtocol.kt ├── ElmActor.kt # Single-thread executor ├── RawRelayService.kt # Foreground-сервис: BT + поллинг ├── RelayClient.kt # HTTP-клиент к /api/v1/elm/raw/* └── MainActivity.kt # Минимальный UI (выбор BT, статус) ``` ## RawRelayService — главный цикл ``` relayLoop(): 1. Bluetooth connect 2. ElmProtocol.init() # AndrOBD: ATSP0→ATAT1→ATST→ATS0→ATL0→ATE0 3. client.hello() # HTTP → /api/v1/elm/raw/hello 4. while running: cmd = client.pollCommand() # GET /api/v1/elm/raw/cmd raw = actor.sendBlocking(cmd, 5000) client.postResponse(seq, cmd, raw) ``` ## CI/CD и деплой ### Сервер (elmer) ``` Локально: git push gitea master Сервер: ssh obdai.ru cd /opt/elmer && git pull origin master sudo systemctl restart elmer ``` - Репо: `gitea.services.ngcloud.ru/Nail/elmer` - Ветка: `master` - Сервис: `gunicorn -w 4 -b 127.0.0.1:8000 web.app:app` - Прокси: nginx :443 → 127.0.0.1:8000 ### Android APK **Два репо:** `elmer/` (gitea) и `elmer/android/` (github) ``` # Основной APK cd android && ./gradlew :app:assembleDebug scp app/build/outputs/apk/debug/app-debug.apk obdai.ru:/opt/elmer/web/static/ # Raw-реле APK cd android && ./gradlew :raw:assembleDebug scp raw/build/outputs/apk/debug/raw-debug.apk obdai.ru:/opt/elmer/web/static/elm-raw-v022.apk ``` - Репо Android: `github.com/Repinoid/elmer-android` - Ветка: `opus-fixes` - APK на сервере: `/opt/elmer/web/static/` ### Версионирование | Где | Файл | |-----|------| | Основной APK | `android/app/build.gradle.kts` → `versionName` | | Raw APK | `android/raw/build.gradle.kts` → `versionName` | | Сайт (основной) | `/opt/elmer/templates/index.html` | | Сайт (raw) | `/opt/elmer/templates/index.html` (та же строка) | **Важно:** Flask использует `/opt/elmer/templates/index.html`. Файл `/opt/elmer/web/templates/index.html` — резервная копия.