6.2 KiB
Мнение по анализу динамического сбоя ELM327
Дата: 2026-06-14
Общая оценка
Анализ написан правильно. Методология верная: исключение невозможного через уже проведённые эксперименты (паузы 4000 мс, автоподбор таймингов), затем ранжирование оставшихся гипотез. Главный вывод — проблема не в скорости, а в чтении потока — звучит убедительно.
Что поддерживаю
Гипотезы 1–3 (вероятность: высокая) — расставлены верно.
Из трёх наиболее вероятных причин непрочитанный > в InputStream — самая классическая ELM327-ловушка. Если ScriptEngine завершает чтение по таймауту или по числу строк вместо >, это объясняет всё: первый запрос проходит, потому что > ещё не накапливается, второй ломается из-за хвоста. Это надо проверять первым.
Buffer drain перед send, а не только после receive — часто игнорируемое место. Если drain делается только после чтения, но перед отправкой нового запроса остаток > или пустая строка ещё лежат в буфере — это незаметно даже в логах, если читать только "полезные" байты.
Что добавил бы
1. NO DATA / UNABLE TO CONNECT в динамике
В анализе не рассмотрен сценарий, когда в ходе динамики ELM вернул NO DATA или UNABLE TO CONNECT. Это вполне реально при смене контекста CAN. Если ScriptEngine на такой ответ зависает в ожидании данных или некорректно парсит следующий ответ — результат идентичен описанному сбою. Стоит явно проверить, как ScriptEngine обрабатывает негативные ответы ELM, и логировать их.
2. AT ST (тайм-аут ELM) может различаться между режимами
Если ElmChecker и ScriptEngine отправляют разные значения AT ST (или один вообще не устанавливает его), ELM сам будет обрезать ответ или отвечать с разной задержкой. При высокой нагрузке ECU (динамика) тайм-аут ELM по умолчанию (200 мс) может быть недостаточен, и ELM уйдёт в NO DATA раньше, чем ECU ответил. Нужно убедиться, что AT ST FF (максимальный) или фиксированное значение установлены одинаково в обоих путях.
3. Клон ELM327 vs оригинал
Клоны (особенно v1.5 китайские) имеют известный баг: при высокой частоте запросов они перестают выдавать > — промпт появляется только после задержки или вообще пропадает. Если адаптер — клон, нужно явно учесть это при трактовке сырых логов: отсутствие > может быть аппаратным поведением, а не ошибкой кода.
4. Конкурентный доступ — недооценённый риск
Гипотезе 5 (два потока на сокет) поставлена средняя вероятность, но в Android-проектах это случается чаще, чем кажется. Достаточно одного фонового alive-check, который читает тот же InputStream в момент динамического цикла. Стоит выйти не только на проверку thread id, но и на synchronized-блоки или single-threaded executor для всех операций с сокетом.
Что менее убедительно
Гипотеза 6 (порядок команд) — оценка "средняя-низкая" верна, но её стоит проверять параллельно с гипотезами 1–3, не последовательно: это дёшево (достаточно дампа команд) и может мгновенно закрыть вопрос или исключить этот класс причин.
Порядок расследования (скорректированный)
- Сырой RX/TX лог с явным маркером
>— сравнить статику и динамику. Первый приоритет. - Проверить обработку негативных ответов (
NO DATA,UNABLE TO CONNECT) в ScriptEngine. - Сравнить AT-последовательности ElmChecker и ScriptEngine — весь init, включая
AT ST. - Убедиться в drain перед send, а не только после receive.
- Thread id на каждый read/write — исключить второй consumer.
- Дамп команд обоих режимов — закрыть гипотезу 6 параллельно с остальными.
Итог
Анализ хороший. Главное не растягивать расследование на последовательное прохождение всех гипотез: сырой лог с маркером > и лог негативных ответов ELM — два дешёвых эксперимента, которые скорее всего сразу покажут, где рвётся синхронизация.