doc: полный лог сессии — архитектура, ошибки, решение с offline-скриптами
This commit is contained in:
@@ -48,6 +48,89 @@ CI упал с `org.gradle.api.artifacts.SelfResolvingDependency`. Причин
|
||||
|
||||
И создан `gradle/wrapper/gradle-wrapper.properties` с `gradle-8.5-bin.zip`.
|
||||
|
||||
---
|
||||
|
||||
## ЧАСТЬ 2: Тест с реальным ELM327 (Фаэтон)
|
||||
|
||||
### Зелёная кнопка (TestService) — 100% работает
|
||||
- BT-подключение к ELM327 ✅
|
||||
- ATZ → ATEx → ATH1 ✅
|
||||
- VIN: получен (16-ричные данные) ✅
|
||||
- DTC stored/pending: получены ✅
|
||||
- PID'ы: RPM, ОЖ, скорость, дроссель и др. ✅
|
||||
- **Тайминги:** `Thread.sleep(250)` между командами — критически важно
|
||||
|
||||
### Оранжевая кнопка (ElmForwardService + сервер) — НЕ работает
|
||||
Проблема: стейт-машина сервера шлёт команды мгновенно, без пауз. ELM327 не успевает.
|
||||
- ATZ → OK
|
||||
- ATEx → OK
|
||||
- 0902 → SEARCHING...UNABLE TO CONNECT (ELM не может выполнить режим 09)
|
||||
- Сервер переходит к DTC → шлёт 03 → ELM отвечает `?` → бесконечный цикл `?`
|
||||
|
||||
**Корень проблемы:** тонкий клиент требует server-driven архитектуру (сервер даёт команду → клиент пишет в ELM → ELM отвечает → клиент шлёт ответ серверу → сервер даёт следующую). Но сервер не делает пауз, а ELM327 требует ~200мс между командами.
|
||||
|
||||
### Почему Deadlock при первом запуске оранжевой кнопки
|
||||
1. Клиент: BT-коннект ✅, входит в loop()
|
||||
2. ELM327: ждёт команду (не шлёт приветствие без запроса)
|
||||
3. Клиент: `read()` блокируется — нет данных
|
||||
4. Сервер: не получает "READY" — не шлёт ATZ
|
||||
5. **DEADLOCK**
|
||||
|
||||
**Исправлено:** `fwd("READY")` сразу после коннекта — кикстарт сервера.
|
||||
|
||||
---
|
||||
|
||||
## Вывод: архитектура телефон↔сервер в реальных условиях
|
||||
|
||||
### Проблема
|
||||
В движении связи с сервером нет. Архитектура «сервер рулит каждой командой» нежизнеспособна.
|
||||
|
||||
### Решение (обсуждено)
|
||||
**Две фазы работы:**
|
||||
|
||||
**Фаза 1 — ОФЛАЙН (в машине):**
|
||||
- Клиент получает со старта **скрипт** от сервера
|
||||
- Скрипт: последовательность команд + промпты водителю
|
||||
- Клиент сам гоняет протокол (как зелёная кнопка)
|
||||
- Данные пишутся локально (SQLite) с таймстемпами
|
||||
- Водитель видит промпты: «Разгон 0-100», «Кикдаун», «Холостой ход 30с»
|
||||
- Кнопка Старт / Стоп
|
||||
|
||||
**Фаза 2 — ОНЛАЙН (дома):**
|
||||
- Клиент заливает всю сессию одним POST на сервер
|
||||
- Сервер парсит, анализирует, LLM → диагноз
|
||||
- Возможно — выдаёт следующий скрипт для нового теста
|
||||
|
||||
### Формат скрипта (пример):
|
||||
```json
|
||||
{
|
||||
"name": "Тест турбины",
|
||||
"steps": [
|
||||
{"type": "obd", "cmd": "ATZ"},
|
||||
{"type": "obd", "cmd": "010C", "label": "RPM"},
|
||||
{"type": "prompt", "text": "Разгон 0-100, кикдаун"},
|
||||
{"type": "loop", "pid": "010C", "duration": 30, "rate_ms": 200},
|
||||
{"type": "obd", "cmd": "03"},
|
||||
{"type": "upload"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Кто что делает
|
||||
| Компонент | Файл | Статус | Что добавить |
|
||||
|-----------|------|--------|-------------|
|
||||
| Тестовый клиент | `TestService.kt` | ✅ гоняет протокол | Сохранение в БД, скрипты, промпты |
|
||||
| Транспортный клиент | `ElmForwardService.kt` | ⚠️ требует стабильной связи | Возможно удалить |
|
||||
| Стейт-машина | `raw_endpoint.py` | ⚠️ нет пауз | Переделать под батчевую обработку |
|
||||
| Mock ELM327 | `mock_elm327.py` | ✅ эмулятор | Добавить задержки для реализма |
|
||||
| Сервер приёма | `web/app.py` | ✅ | POST-эндпоинт для заливки сессии |
|
||||
|
||||
### Договорённости по процессу
|
||||
- **НИКОГДА не кодить без прямой команды**
|
||||
- Сначала обсуждать → потом делать
|
||||
- Коммитить часто, с понятными сообщениями
|
||||
- Документировать все ошибки и решения
|
||||
|
||||
### 08:40 — Ошибка 3: Коммиты не в ту ветку
|
||||
Изначально все изменения ушли в `master`, но рабочая ветка — `relay-only`.
|
||||
|
||||
|
||||
+1
-1
@@ -210,7 +210,7 @@ def register(app):
|
||||
logger.info(f"[{session_id}] {parsed}")
|
||||
|
||||
# SEARCHING/NO DATA — ждём, не продвигаем стейт
|
||||
is_skip = raw.startswith("SEARCHING") or raw == "NO DATA" or raw == "STOPPED"
|
||||
is_skip = raw.startswith("SEARCHING") or raw in ("NO DATA", "STOPPED", "?")
|
||||
if is_skip:
|
||||
sess["retries"] = sess.get("retries", 0) + 1
|
||||
if sess["retries"] > 3:
|
||||
|
||||
Reference in New Issue
Block a user