diff --git a/doc/claude-analysis-elm-v2.md b/doc/claude-analysis-elm-v2.md new file mode 100644 index 0000000..5bdb102 --- /dev/null +++ b/doc/claude-analysis-elm-v2.md @@ -0,0 +1,86 @@ +# Анализ деградации ELM-кода в v1.9.0 и план исправления + +## 1. Почему v1.9.0 стал ХУЖЕ (ни одного ответа)? + +Главная причина — **отключение `drainInput()` в `write()`** совмещенное с агрессивной работой `exec()`. +* В v1.3.0 `drainInput()` вызывался перед каждой командой. Если ELM "подлагивал" и присылал ответ на предыдущую команду с опозданием, `drainInput()` вычищал этот мусор, и `read()` начинал ждать с чистого листа. +* В v1.9.0 `drainInput()` выключен. Если первая команда батча (`010C`) дает таймаут, `exec()` делает **10 RETRY**. +* Сценарий катастрофы: + 1. Послали `010C`. Тайм-аут. + 2. Послали `010C` (retry 1). В этот момент ELM прислал ответ на #1. + 3. `read()` в retry 1 видит ответ от #1, но ELM уже занят обработкой #2. + 4. Следующие команды накладываются друг на друга. Буфер Bluetooth забивается ответами, которые не соответствуют командам. + 5. ELM v1.5 "захлебывается" потоком данных в TX-буфере и перестает отвечать на новые команды вовсе. + +## 2. Нужен ли ATWS перед динамикой? + +**Да, но с нюансами.** +* `ATWS` (Warm Start) гарантирует, что ELM забыл про "SEARCHING" и сбросил внутренние ошибки протокола. +* **Ожидание после `ATWS`:** Минимум **1000мс** (на v1.3 было 300мс — мало, на v1.9 вообще убрали). +* **ВАЖНО:** После `ATWS` нужно заново послать `ATE0`, `ATL0`, `ATS0`, так как Warm Start сбрасывает их в дефолт. + +## 3. Нужен ли drainInput() в write()? + +**Да, обязательно**. Но не просто "чистка всего", а **синхронизация**. +Для ELM327 принцип "запрос-ответ" свят. Мы не имеем права слать новую команду, пока не получили `>` от предыдущей ИЛИ не дождались таймаута И вычистили буфер. + +## 4. Почему статика работает? + +В статике между командами проходят секунды (пока поток UI обработает, пока лог запишется). Этого времени хватает ELM, чтобы "прочихаться". В динамике команды идут пачками, что превращает любую задержку адаптера в фатальный рассинхрон. + +## 5. Retry в `exec()` с переповтором команды — ГЛАВНЫЙ БАГ. + +Повторная отправка OBD-команды (`010C`) при таймауте — это **яд для ELM327**. +Команда OBD — это не просто AT-запрос. ELM должен отправить запрос в CAN-шину, дождаться ответа от ЭБУ, отформатировать его и выдать. Если мы шлем `010C` второй раз, когда ELM еще ждет ответа от CAN, он может либо зависнуть, либо выдать `BUFFER FULL`. + +--- + +# Необходимые правки кода + +### 1. `ElmProtocol.kt`: Убрать вредный retry OBD-команд + +```kotlin +// В методе exec(): +// БЫЛО: +for (i in 0 until MAX_RETRIES) { + write(cmd) // ← НЕЛЬЗЯ ПЕРЕПОСЫЛАТЬ OBD-команду! + // ... +} + +// НАДО: +private fun exec(cmd: String, timeout: Long): String { + write(cmd) + var t = timeout + try { + return handle(read(t)) + } catch (_: TimeoutException) { + increaseTimeout() + // Очищаем буфер, так как команда "протухла" + drainInput() + return "" + } +} +``` + +### 2. `ElmProtocol.kt`: Вернуть `drainInput()` в `write()` +Без него мы читаем ответы на позапрошлые команды. + +### 3. `DynamicCollector.kt`: Добавить микро-паузу между PID +ELM v1.5 физически не может переключаться между PID быстрее чем за 100-200мс. + +```kotlin +// В цикле for (step in steps): +val raw = elm.sendCommand(step.cmd) +Thread.sleep(150) // Пауза для стабильности v1.5 +``` + +### 4. `MainActivity.kt`: Правильная инициализация перед тестом +```kotlin +// Перед стартом DynamicCollector: +elmProto.sendCommand("ATWS") +Thread.sleep(1200) // Ждем инициализации +elmProto.sendCommand("ATE0") // Эхо выкл +elmProto.sendCommand("ATL0") // Переносы выкл +elmProto.sendCommand("ATS0") // Пробелы выкл (КРИТИЧНО для скорости) +elmProto.drainInput() +```