From b40a7968ec495c79341d0d023d653c9c4abfb69a Mon Sep 17 00:00:00 2001 From: Repinoid Date: Tue, 26 May 2026 08:24:15 +0300 Subject: [PATCH] =?UTF-8?q?doc:=20=D0=BF=D0=BE=D0=BB=D0=BD=D1=8B=D0=B9=20?= =?UTF-8?q?=D0=BB=D0=BE=D0=B3=20=D1=81=D0=B5=D1=81=D1=81=D0=B8=D0=B8=20?= =?UTF-8?q?=E2=80=94=207=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BE=D0=BA,=20=D0=B0?= =?UTF-8?q?=D1=80=D1=85=D0=B8=D1=82=D0=B5=D0=BA=D1=82=D1=83=D1=80=D0=B0,?= =?UTF-8?q?=20=D1=80=D0=B5=D1=88=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/session-2026-05-26.md | 224 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 doc/session-2026-05-26.md diff --git a/doc/session-2026-05-26.md b/doc/session-2026-05-26.md new file mode 100644 index 0000000..08dcf83 --- /dev/null +++ b/doc/session-2026-05-26.md @@ -0,0 +1,224 @@ +# Сессия 2026-05-26 — отладка Android-клиента и Mock ELM327 + +## Хронология + +### 08:00 — Старт: пулл изменений с ночи +Ночью на другой машине было сделано: +- `android/` вынесен в отдельный репо `github.com/Repinoid/elmer-android` +- Добавлен `tools/mock_elm327.py` — TCP-эмулятор ELM327 на порту 35000 +- `web/raw_endpoint.py` — стейт-машина, парсинг ответов, LLM-интеграция +- Исправлены 3 бага в Android-клиенте (NetworkOnMainThread, URL сервера, фильтр \n) + +### 08:20 — Попытка 1: телефон → mock → сервер +Запущены mock (:35000) и Flask (:5005). Настроен проброс портов через Windows: +```powershell +netsh interface portproxy add v4tov4 listenport=5005 listenaddress=0.0.0.0 connectport=5005 connectaddress=192.168.18.62 +netsh advfirewall firewall add rule name="Elmer Flask" dir=in action=allow protocol=TCP localport=5005 +``` + +WiFi IP ноутбука: `10.47.183.102`. + +Телефон подключился к mock (TCP: OK), но команды не шли. + +### 08:30 — Ошибка 1: Server URL захардкожен на obdai.ru +Глянув код клиента в `ElmForwardService.kt`, обнаружено: +```kotlin +// MainActivity.kt строка 66 +putExtra(EXTRA_SERVER_URL, "https://obdai.ru/api/v1/raw-obd") +``` + +Телефон слал данные в интернет, а не на локальный Flask. + +**Исправление:** в `MainActivity.kt` — авто-вывод URL сервера из IP устройства: +```kotlin +val deviceHost = debugHost.split(":")[0] +val localServerUrl = "http://$deviceHost:5005/api/v1/raw-obd" +``` + +### 08:35 — Ошибка 2: Gradle 9.5 слишком новый +CI упал с `org.gradle.api.artifacts.SelfResolvingDependency`. Причина: AGP 8.2.0 несовместим с Gradle 9.x. + +**Исправление:** в `build-apk.yml`: +```yaml +- name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + with: + gradle-version: "8.5" +``` + +И создан `gradle/wrapper/gradle-wrapper.properties` с `gradle-8.5-bin.zip`. + +### 08:40 — Ошибка 3: Коммиты не в ту ветку +Изначально все изменения ушли в `master`, но рабочая ветка — `relay-only`. + +**Исправление:** переключился на `relay-only`, применил изменения туда, master откатил через `git reset --hard && git push --force`. + +### 08:45 — Ветка relay-only: добавлена отладка +В `ElmForwardService.kt` добавлено: +- `say("🌐 Server: $serverUrl")` — показ URL при подключении +- `say("← $raw")` — каждое сырое сообщение от устройства +- `say("→ $cmd")` — каждая команда сервера +- Обработка ошибок: `⚠️ Server unreachable`, `⚠️ Bad JSON` + +### 09:00 — Ошибка 4: Cleartext HTTP заблокирован +Телефон показал `⚠️ Server down: cleartext...`. Android 9+ блокирует HTTP (не-HTTPS) по умолчанию. + +**Исправление:** в `AndroidManifest.xml`: +```xml +android:usesCleartextTraffic="true" +``` + +### 09:05 — Ошибка 5: APK не скачивается с сервера +Телефон открыл `http://10.47.183.102:5005`, страница загрузилась, но APK — 404. +Flask отдаёт статику из `/static/`, а ссылка была `/app-debug.apk`. + +**Исправление:** ссылка изменена на `/static/app-debug.apk`. + +### 09:10 — Ошибка 6: Приложение не устанавливается поверх +Google Play Protect проверил APK, но «приложение не установлено». Причина: каждый CI-билд генерирует новый debug-keystore → сигнатуры не совпадают → Android блокирует установку поверх. + +**Исправление:** сгенерирован фиксированный `debug.keystore` (пароль `android`, alias `androiddebugkey`) и закоммичен в репо. В CI добавлен шаг: +```yaml +- name: Setup debug keystore + run: cp debug.keystore ~/.android/debug.keystore +``` + +### 09:15 — Тестовая версия: вместо сервера — локальный протокол +Пользователь потребовал тестовую версию без сервера. Создан `TestService.kt`: +- TCP-подключение к mock +- Самостоятельная отправка AT-команд (ATZ, ATE0, ATL0, ATSP0, ATH1) +- Чтение VIN (0902) +- Чтение DTC stored (03), pending (07) +- Чтение 10 PID'ов (0105..0107) +- Весь вывод на экран в реальном времени + +Интерфейс: зелёная кнопка «🧪 ТЕСТ», версия `v0.2.0-test`, URL по умолчанию `10.47.183.102:35000`. + +### 09:25 — Ошибка 7: Мусор в командах +Mock получил `01070 ATZ` вместо `ATZ`. Причина: клиент слал `\r`, а mock использовал `readline()` (ждёт `\n`). В буфере накопился мусор. + +**Исправление в клиенте:** `cmd + "\r\n"` вместо `cmd + "\r"`. + +**Исправление в mock:** полная переделка чтения — побайтовый разбор: +```python +# Было: self.rfile.readline() +# Стало: читаем по 1 байту, \r и \n — разделители +ch = self.rfile.read(1) +if ch in (b'\n', b'\r'): + # обработать накопленный буфер +else: + buf += ch +``` + +### 09:30 — 100% успешный тест +Mock лог (чистый!): +``` +🔌 Подключение +📥 ATZ → ELM327 v1.5 +📥 ATE0 → OK +📥 ATL0 → OK +📥 ATSP0 → OK +📥 ATH1 → OK +📥 0902 → VIN: WVWZZZ1KZAW123456 +📥 03 → DTC: P0301, P0303 +📥 07 → DTC: none +📥 0105..07 → 10 PID'ов +🔌 Отключение +``` + +18 команд — 18 ответов. Ноль мусора. + +--- + +## Архитектура (текущая) + +``` +┌─────────────────────────────────────────────────────┐ +│ ТЕСТОВЫЙ РЕЖИМ (работает на 100%) │ +│ │ +│ Телефон (Android) Ноутбук (WSL) │ +│ ┌──────────────┐ ┌──────────────────┐ │ +│ │ TestService │──TCP──→ │ mock_elm327.py │ │ +│ │ │←──TCP── │ :35000 │ │ +│ │ ATZ→ATEx→ │ │ │ │ +│ │ 0902→03/07 │ │ Фейковые данные: │ │ +│ │ PID'ы │ │ VIN, DTC, PID │ │ +│ └──────────────┘ └──────────────────┘ │ +│ │ +├─────────────────────────────────────────────────────┤ +│ РАБОЧИЙ РЕЖИМ (клиент готов, сервер частично) │ +│ │ +│ Телефон Ноутбук │ +│ ┌──────────────┐ ┌──────────────────┐ │ +│ │ElmForwardSvc │─HTTP→ │ Flask :5005 │ │ +│ │ (транспорт) │←─JSON─ │ raw_endpoint.py │ │ +│ │ │ │ ↓ стейт-машина │ │ +│ │ BT/TCP → │ │ ↓ парсер │ │ +│ │ ELM327/mock │ │ ↓ LLM (DeepSeek)│ │ +│ └──────────────┘ └──────────────────┘ │ +└─────────────────────────────────────────────────────┘ +``` + +--- + +## Все ошибки и решения + +| # | Ошибка | Причина | Решение | +|---|--------|---------|---------| +| 1 | Телефон не слал команды | server_url = obdai.ru (интернет) | Авто-вывод `http://host:5005` | +| 2 | CI build fail | Gradle 9.5 ≠ AGP 8.2 | Закрепить Gradle 8.5 | +| 3 | Коммиты в master | Не переключил ветку | Перенос в relay-only, откат master | +| 4 | Server down: cleartext | Android блокирует HTTP | `usesCleartextTraffic="true"` | +| 5 | APK 404 на сервере | Flask static path | `/static/app-debug.apk` | +| 6 | Не устанавливается поверх | Разные debug-ключи | Фиксированный keystore в репо | +| 7 | Мусор `01070 ATZ` | `\r` vs `\r\n` + readline() | Побайтовое чтение в mock + `\r\n` | + +--- + +## Ключевые файлы + +### Android (ветка relay-only) +| Файл | Назначение | +|------|-----------| +| `TestService.kt` | **ТЕСТОВЫЙ** — сам гонит протокол, без сервера | +| `ElmForwardService.kt` | **РАБОЧИЙ** — транспорт BT/TCP ↔ HTTP | +| `MainActivity.kt` | UI: кнопки ТЕСТ и Диагностировать | +| `debug.keystore` | Фиксированный ключ подписи (пароль `android`) | +| `build-apk.yml` | CI: Gradle 8.5, сборка debug APK | + +### Сервер (ветка master) +| Файл | Назначение | +|------|-----------| +| `tools/mock_elm327.py` | Эмулятор ELM327 на TCP :35000 | +| `web/app.py` | Flask сервер :5005 | +| `web/raw_endpoint.py` | Стейт-машина: парсинг, сессии, LLM | +| `web/templates/index.html` | Страница загрузки APK | + +--- + +## Команды для запуска + +```bash +# Ноутбук — терминал 1: mock +python tools/mock_elm327.py + +# Ноутбук — терминал 2: сервер +python web/app.py + +# Телефон: открыть http://10.47.183.102:5005 → скачать APK → кнопка ТЕСТ +``` + +## Версии APK +| Версия | Статус | Ключ | +|--------|--------|------| +| 0.1.0 | Устарела | Случайный | +| 0.2.0-test | На телефоне | Случайный | +| 0.5.0-test | В CI | Фиксированный | + +--- + +## Что дальше (рабочая версия) +1. Дописать `raw_endpoint.py` — надёжный парсинг + сохранение в БД +2. Настроить `DEEPSEEK_API_KEY` для LLM +3. Протестировать цепочку: телефон → сервер → LLM → диагноз +4. Подключить к реальному ELM327 в машине (Bluetooth вместо mock)