Files
elmer/CHANGELOG.md
T
“Naeel” aa37a1a70c v0.48.0 — пробинг ELM327, трехуровневый профиль, рефакторинг obd/
- obd/probe.py: трехуровневый каскад (L0/L1/L2)
- obd/commands.py: каталог всех AT-команд с метаданными
- obd/classifier.py: классификация ответов + определение уровня
- obd/connection.py: транспортный слой (SerialTransport)
- obd/protocol.py: init() только база, без ATAT1/ATST
- api/db.py: таблица device_profiles по BT MAC
- api/scripts.py: три уровня скриптов (l0/l1/l2)
- api/routes.py: /elm/probe, /elm/profile/<mac>, /script?level=
- web/templates/index.html: v0.48.0
- CHANGELOG.md, doc/architecture.md, resume.txt: версии
2026-06-07 05:55:02 +04:00

16 KiB
Raw Blame History

elmAI — Changelog / Полное описание проекта

Файл для нового агента: прочитай — и ты в курсе всего. Актуально: v0.48.0, 7 июня 2026


1. Идентификация

Поле Значение
Название elmAI (ранее Elmer, elmAI rebrand в конце мая)
Суть OBD2-диагностика: Android → ELM327 → LLM (анализ ошибок)
Репозиторий сервера gitea.services.ngcloud.ru/Nail/elmer
Репозиторий Android github.com/Repinoid/elmer-android (отдельный!)
Сервер (prod) obdai.ru (5.172.178.213)
Язык сервера Python 3 + Flask + gunicorn
Язык клиента Kotlin, minSdk 24
LLM-провайдер api.aillm.ru (OpenAI-совместимый)
База данных SQLite (WAL mode)

2. Архитектура (master — продакшен, актуально)

📱 Android → ELM327 (Bluetooth SPP)
    │
    ▼ ScriptRunnerService (фоновая служба)
    │  выполняет скрипт: OBD-команды → ждёт ответы → пишет лог
    │  HTTPS POST /api/v1/session/upload
    ▼
🌐 obdai.ru (nginx :443 → gunicorn :8000)
    │
    ├── web/app.py          — точка входа Flask
    ├── api/routes.py       — 5 эндпоинтов
    ├── api/db.py           — SQLite (sessions, cars, dtc, params)
    ├── api/parser.py       — парсинг батча ELM-ответов (VIN, DTC, PID)
    ├── api/scripts.py      — сборка диагностических скриптов
    ├── api/config.py       — загрузка config.yaml (c lru_cache)
    ├── brain/client.py     — Diagnoser: HTTP к api.aillm.ru
    ├── brain/prompts.py    — SYSTEM_PROMPT (10 правил, табличный формат)
    └── obd/protocol.py     — AndrOBD-стейт-машина (1:1 копия ElmProt.java)

Модули подробно

api/ — REST + БД + парсинг

5 эндпоинтов:

Эндпоинт Метод Что делает Время
/api/v1/ping GET {"ok": true} — проверка сервера ~5ms
/api/v1/ping-llm GET Проверка LLM (кэш 60с, глобальная переменная) ~2s
/api/v1/script?mode=full GET Выдача скрипта диагностики (JSON со steps) ~50ms
/api/v1/session/upload POST Приём батча + LLM-анализ, идемпотентность ~30-120s
/api/v1/chat POST Свободный вопрос к LLM (с историей) ~5-15s

Ключевые особенности:

  • upload_session(): принимает responses (массив {cmd, raw, decoded}) + request_id (UUID для идемпотентности). Если request_id уже есть в БД — возвращает кэшированный ответ (200).
  • LLM fallback: если нет API key или LLM ошибка — возвращает format_no_llm() (сырые данные без анализа).
  • _build_diagnosis_prompt(): собирает промпт из VIN, DTC stored/pending, параметров, raw_log. Требует глубокого разбора.
  • /chat: передаёт историю как массив messages[{role, content}], срез последних 10.
  • config.load()@lru_cache(maxsize=1), сбрасывать рестартом процесса.

База данных (SQLite, WAL):

  • sessions — сводная таблица (клиент, ELM, авто, LLM, request_id, response_json для кэша)
  • cars — VIN → id (уникальные)
  • diagnostic_tokens, llm_messages, ecu_parameters, dtc_codes — детальные таблицы (не используются в upload, только legacy)
  • PRAGMA journal_mode=WAL, busy_timeout=30000, check_same_thread=False

Парсер (parser.py):

  • parse_batch(responses){vin, dtc_stored[], dtc_pending[], parameters[{name,value}], raw_log[]}
  • Парсит из decoded поля, fallback из сырого HEX (490201..., 43..., 47...)
  • DTC из HEX: декодирует P/B/C/U коды из байтов после 43/47
  • PID: всё что с : в decoded, кроме VIN/DTC/ELM/Protocol

Скрипты (scripts.py):

  • build_full_script(): 7 PID (ОЖ, RPM, скорость, дроссель, нагрузка, STFT, LTFT)
  • build_default_script(): 1 PID (0105 — температура ОЖ) для отладки

brain/ — LLM

  • Diagnoser(api_key, model="gpt-oss-120b", base_url, timeout=180)
  • diagnose(system, user_prompt, history=None) → str
  • Ошибки: LLMError с безопасным для клиента сообщением (без деталей)
  • Различает: Timeout, HTTP 429 («слишком много запросов»), HTTP 5xx, HTTP 4xx
  • SYSTEM_PROMPT: 10 правил, формат ответа — таблицы, степени уверенности в %, план действий по приоритету
  • Модели: gpt-oss-120b (основная), qwen3-6-27b-fp8 (быстрая, но с CoT leak bug)

obd/protocol.py — ELM327 стейт-машина (AndrOBD)

Состояния: UNDEFINED → INITIALIZING → READY → BUSY → READY, ошибка → ERROR/DISCONNECTED

Классификация ответов (Rsp.identify):

Ответ Тип Реакция
> PROMPT Конец ответа
OK OK Уменьшить таймаут
SEARCHING... SEARCHING Нормально при ините
NODATA NODATA Увеличить таймаут, ATST
UNABLE/BUS BUSY/CAN ERROR BUS ERROR DISCONNECTED → ATPC → ATSP0
ERROR/DATA ERROR/BUFFER FULL ERROR ATWS (warm start)
Всё остальное DATA Успех, уменьшить таймаут

Ключевые особенности (1:1 с AndrOBD):

  • Побайтовое чтение с поллингом 1ms (НЕ readLine!)
  • > (0x3E) — не спецсигнал, а разделитель строк как CR/LF
  • Адаптивный таймаут: 50-2000ms, шаг 20ms, ATST = timeout/4
  • _write(): reset_input_buffer() перед записью — чистит хвосты
  • Инициализация: ATSP0 → ATAT1 → ATST → ATS0 → ATL0 → ATE0 (без ATZ)
  • BUS ERROR recovery: ATPC → ATSP0
  • _read(): требует > перед возвратом, иначе TimeoutError

web/app.py — точка входа

  • sys.path.insert(0, корень_проекта) — чтобы импортировать api/, brain/, obd/
  • config = load() — глобально
  • register_api(app) — подключает эндпоинты
  • /elmer.apksend_from_directory("static", "app-debug.apk")

3. Версии и история

Система версионирования

  • Сервер: web/templates/index.html (два места: подзаголовок и подпись APK)
  • Android: android/app/build.gradle.ktsversionName
  • Документация: заголовки .md файлов
  • Менять одновременно во всех местах

История версий (сервер)

v0.48.0 (7 июня 2026)

  • Пробинг ELM327: трехуровневый каскад (L0/L1/L2)
  • Рефакторинг obd/: разделение на независимые сервисы
    • commands.py — каталог всех AT-команд
    • classifier.py — классификация ответов + определение уровня
    • connection.py — транспортный слой (SerialTransport)
  • Fix: init() больше не шлёт ATAT1/ATST (висли на клонах v1.5)
  • Скрипты: три уровня (build_script_l0/l1/l2)
  • БД: таблица device_profiles по BT MAC
  • API: POST /api/v1/elm/probe, GET /api/v1/elm/profile/<mac>
  • api/routes.py/script?level=0|1|2

v0.36.0-dev (31 мая 2026)

  • Ребрендинг Elmer → elmAI (лого, сайт)
  • Opus review: серверные фиксы (WAL, идемпотентность, таймауты LLM, кэш ping-llm)
  • api/config.py@lru_cache, api/db.py — контекстный менеджер + request_id
  • brain/client.py — обработка ошибок (различает 429, 5xx, Timeout), DEFAULT_MODEL
  • obd/protocol.pyreset_input_buffer(), не затирать ERROR
  • CI/CD: GitHub Actions для Android APK (сборка + деплой на сервер)
  • Правило №1 в .instructions.md: вопрос → только ответ, никаких действий

v0.35.0-dev (29-30 мая 2026)

  • Рефакторинг архитектуры: elmer/api/ + brain/ + obd/
  • Удалён мёртвый код (elmer/diagnose.py, elmer/elm.py, elmer/prompts.py, web/raw_endpoint.py)
  • doc/architecture.md — полное описание структуры
  • obd/protocol.py — вынесен из elm_proto, доработан

v0.13.0-dev (28 мая 2026) — AndrOBD стейт-машина

  • Стейт-машина 1:1 с AndrOBD (ElmProt.java)
  • 5 багов исправлено (команды подряд без пауз, ATST не читал ответ, частичный read, таймаут 200ms, BUS ERROR recovery)
  • AdaptiveTiming (500ms start, 50-2000ms range, ATST)
  • OkHttp timeout 30→120с
  • tools/mock_elm327_v2.py — мок с реалистичными задержками
  • tools/test_androbd.py — тест стейт-машины (2/3 зелёные)

v0.11.0-prod (25-27 мая 2026) — fat-client архитектура

  • Fat-client: телефон сам гоняет протокол, сервер только батч-анализ
  • Скрипт диагностики: GET /api/v1/script
  • Загрузка батча: POST /api/v1/session/upload
  • Эндпоинт /chat
  • Исследование 19 ELM/BT проектов → AndrOBD = золотой стандарт
  • doc/elm-reference.md — 1100+ строк паттернов
  • Домен obdai.ru, решение НЕ деплоить до стабильного ELM↔Android

v0.x — ранние версии (fat-client ветка)

  • TestService.kt — зелёная кнопка: самостоятельный прогон протокола (100% работает)
  • ElmForwardService.kt — транспорт BT/TCP ↔ HTTP (проблемы: deadlock, паузы)
  • Побайтовое чтение в mock (исправлен мусор \r vs \r\n)
  • Фиксированный debug.keystore (пароль android, alias androiddebugkey)
  • AndroidManifest.xml: usesCleartextTraffic="true"

4. Android-клиент (отдельный репо)

Структура

app/src/main/java/ru/elmer/client/
├── elm/
│   └── ElmProtocol.kt        — ELM327 стейт-машина (AndrOBD)
├── obd/
│   └── ObdDecoder.kt         — декодер PID/DTC/VIN
├── server/
│   └── ServerClient.kt       — HTTP к серверу (retry 3x, OkHttp)
├── script/
│   ├── ScriptEngine.kt       — движок скриптов
│   └── ScriptRunnerService.kt — фоновая диагностика
├── db/
│   └── SessionDb.kt          — локальная SQLite история
└── ui/
    └── MainActivity.kt       — UI + кнопки

Что НЕ ДОРАБОТАНО (по opus-fix-plan, этап 3-4):

  • request_id на клиенте (UUID до цикла ретраев, в JSON + заголовок Idempotency-Key)
  • X-Api-Key через BuildConfig.API_KEY из local.properties
  • ElmProtocol.sendCommand() — не затирать ERROR, дренаж буфера
  • SessionDb.onUpgrade() — ALTER TABLE вместо DROP TABLE
  • MainActivity — троттлинг /ping-llm (не чаще 60с), убрать дублирующий receiver
  • ScriptRunnerService — null intent → stopSelf, try/finally для progress.stop()
  • Exponential backoff в ретраях

5. Деплой

# Сервер
ssh obdai.ru "cd /opt/elmer && git pull origin master && sudo systemctl restart elmer"

# APK (локально)
cd android && ./gradlew assembleDebug
scp app/build/outputs/apk/debug/app-debug.apk obdai.ru:/opt/elmer/web/static/app-debug.apk

Сервер: gunicorn -w 4 -b 127.0.0.1:8000 web.app:app, nginx :443 → :8000, SSL certbot. Ветка: master — продакшен. Никаких Docker, Kubernetes. Сервер на голом железе. Android APK: GitHub Actions → сборка → авто-деплой на сервер.


6. Правила работы (критически важно!)

  1. Вопрос в любой форме → только ответить. НИЧЕГО НЕ ПРЕДПРИНИМАТЬ. Только прямые императивы («сделай», «исправь», «напиши») — команда к действию.
  2. Не выдумывать инфраструктуру. Никаких Docker, K8s. GitHub Actions можно (Android).
  3. Читать документацию перед действиями. doc/architecture.md — канонический источник.
  4. Не редактировать отчёты Опуса (doc/opus-review*.md — только для чтения).
  5. После правок: коммит → пуш → (если сервер) деплой через SSH.
  6. Версию менять в трёх местах: index.html, build.gradle.kts, доки.
  7. Не редактировать файлы Android-репо (elmer-android) — это отдельный репо.

7. Известные архитектурные решения

  • Почему не Docker: владелец принципиально против. Всё вручную через git + systemd.
  • Почему fat-client (скрипт + батч): в движении связи с сервером нет. Телефон сам гоняет протокол, потом заливает данные.
  • Почему AndrOBD (не своё): AndrOBD — 10 лет продакшена, 1993, вылизанный протокол. Копировать 1:1, не изобретать.
  • Почему не PostgreSQL: SQLite достаточно для одного сервера. Миграция будет когда-нибудь потом.
  • Почему obdai.ru не в продакшене: пока не отлажен ELM↔Android на 100%. Телефон + ноутбук в одной WiFi — быстрее и надёжнее.

8. Ветки

Ветка Описание
master Продакшен (актуальная: v0.36.0-dev)
opus-fixes Правки по отчётам Опуса (31 мая; влита в master)
arch-refactor Рефакторинг elmer/ → api/ brain/ obd/
fat-client Старая fat-client архитектура (устарела)
androbd-proto Прототип AndrOBD стейт-машины (устарела)
elm-layer-v2 Старый ELM-слой (устарела)