Files
elmer/doc/claude-analysis-elm-v2.md
T

5.2 KiB
Raw Blame History

Анализ деградации 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-команд

// В методе 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мс.

// В цикле for (step in steps):
val raw = elm.sendCommand(step.cmd)
Thread.sleep(150) // Пауза для стабильности v1.5

4. MainActivity.kt: Правильная инициализация перед тестом

// Перед стартом DynamicCollector:
elmProto.sendCommand("ATWS")
Thread.sleep(1200) // Ждем инициализации
elmProto.sendCommand("ATE0") // Эхо выкл
elmProto.sendCommand("ATL0") // Переносы выкл
elmProto.sendCommand("ATS0") // Пробелы выкл (КРИТИЧНО для скорости)
elmProto.drainInput()