feat: raw relay — очередь команд, эндпоинты, консоль, index.html

This commit is contained in:
Repinoid
2026-06-14 18:53:24 +04:00
parent d01028b12b
commit c229b730c6
6 changed files with 898 additions and 116 deletions
@@ -0,0 +1,60 @@
# Мнение по анализу динамического сбоя 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, не последовательно: это дёшево (достаточно дампа команд) и может мгновенно закрыть вопрос или исключить этот класс причин.
---
## Порядок расследования (скорректированный)
1. **Сырой RX/TX лог** с явным маркером `>` — сравнить статику и динамику. Первый приоритет.
2. **Проверить обработку негативных ответов** (`NO DATA`, `UNABLE TO CONNECT`) в ScriptEngine.
3. **Сравнить AT-последовательности** ElmChecker и ScriptEngine — весь init, включая `AT ST`.
4. **Убедиться в drain перед send**, а не только после receive.
5. **Thread id на каждый read/write** — исключить второй consumer.
6. **Дамп команд** обоих режимов — закрыть гипотезу 6 параллельно с остальными.
---
## Итог
Анализ хороший. Главное не растягивать расследование на последовательное прохождение всех гипотез: сырой лог с маркером `>` и лог негативных ответов ELM — два дешёвых эксперимента, которые скорее всего сразу покажут, где рвётся синхронизация.