# Анализ деградации 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() ```