106 lines
5.3 KiB
Markdown
106 lines
5.3 KiB
Markdown
# Динамические тесты — архитектура
|
||
|
||
> v0.49.0-dev, 7 июня 2026
|
||
> Ветка: `dynamic-tests`
|
||
|
||
## Концепция
|
||
|
||
Два теста: **на месте** и **в движении**. Разница — только в подсказке юзеру.
|
||
Физически оба делают одно: опрашивают 12 PID каждые 250мс, копят в памяти.
|
||
|
||
## Алгоритм работы (user flow)
|
||
|
||
```
|
||
1. Ошибки → scanDtc() — считывание DTC
|
||
2. Диагностика → обычный скрипт L0/L1/L2 на сервер → LLM
|
||
3. LLM сказал сделать тест на месте →
|
||
⏱ На месте → СТАРТ → ... → СТОП → данные в памяти
|
||
4. Диагностика → обычный скрипт + динамические данные → LLM
|
||
5. LLM сказал сделать тест в движении →
|
||
🚗 В движении → СТАРТ → ... → СТОП → данные в памяти
|
||
6. Диагностика → обычный скрипт + динамические данные → LLM (финал)
|
||
```
|
||
|
||
## Android → Сервер
|
||
|
||
При нажатии **Диагностика**:
|
||
1. Выполняется обычный скрипт (AT-команды, PID'ы)
|
||
2. Если есть `dynamicSamples` (данные теста) — прикрепляются к тому же запросу
|
||
3. POST `/api/v1/session/upload` с полем `dynamic_samples`
|
||
4. Сервер по наличию `dynamic_samples` понимает, что пришёл динамический тест
|
||
5. Выбирает промпт: `DYNAMIC_PROMPT` если только динамика, или `SYSTEM_PROMPT + DYNAMIC_PROMPT` если и то и то
|
||
|
||
## Что надо сделать
|
||
|
||
### Уже сделано (написан код, не проверен)
|
||
- [x] Сервер: `build_dynamic_script()` — скрипт 12 PID 250мс
|
||
- [x] Сервер: `?mode=dynamic` в routes.py
|
||
- [x] Сервер: `DYNAMIC_PROMPT` в prompts.py
|
||
- [x] Android: кнопки «⏱ На месте» / «🚗 В движении» (layout)
|
||
- [x] Android: `DynamicCollector.kt` — сбор 12 PID 250мс
|
||
- [x] Android: `ElmChecker.kt` — добавлены методы для работы с DynamicCollector
|
||
- [x] Android: MainActivity — СТАРТ/СТОП, данные в памяти
|
||
- [x] Android: общий таймер `tv_timer`
|
||
|
||
### Надо доделать
|
||
- [ ] Android: при старте Диагностики — прикрепить `dynamicSamples` к upload
|
||
- [ ] Сервер: принимать `dynamic_samples` в upload_session, выбирать промпт
|
||
- [ ] Отладка на реальной машине
|
||
|
||
### До отправки — в памяти
|
||
```kotlin
|
||
data class DynamicSample(
|
||
val ts: Long, // System.currentTimeMillis()
|
||
val responses: List<RawResponse> // 12 ответов ELM
|
||
)
|
||
|
||
val samples = mutableListOf<DynamicSample>() // в памяти, быстро
|
||
```
|
||
|
||
### После СТОП — попытка отправки
|
||
```kotlin
|
||
1. Собираем все samples в JSON
|
||
2. POST /api/v1/session/upload
|
||
3. Если 200 → ОК, забыли
|
||
4. Если ошибка → пишем ВСЁ в SessionDb
|
||
```
|
||
|
||
### Локальное хранение — через SessionDb (уже есть)
|
||
```sql
|
||
-- Таблица sessions (уже существует)
|
||
session_type = "dynamic" -- отличаем от обычных
|
||
|
||
-- Таблица responses (уже существует)
|
||
-- Каждый сэмпл = одна запись в responses:
|
||
-- session_id, step_id = "sample_N", cmd = "batch",
|
||
-- raw = JSON всего опроса, decoded = "запись №N"
|
||
```
|
||
|
||
**Плюсы**: не надо новой таблицы, `SessionDb` уже умеет `createSession`/`addResponse`/`getResponses`.
|
||
|
||
**Объём**: 30 секунд × 4 опроса/с × 12 PID = 1440 записей ≈ ~100KB — норм.
|
||
|
||
### Ретрай непосланных сессий
|
||
При старте приложения: `db.getUnuploadedSessions()` → отправить → пометить `uploaded=1`.
|
||
|
||
## Промпт для LLM (сервер)
|
||
|
||
Отдельный `DYNAMIC_PROMPT` в `brain/prompts.py`:
|
||
|
||
```
|
||
Ты — эксперт по диагностике. Получены временные ряды 12 параметров с интервалом 250мс.
|
||
Проанализируй:
|
||
1. Отклик дросселя — есть ли задержка/провалы
|
||
2. STFT/LTFT — богатая или бедная смесь под нагрузкой и при сбросе
|
||
3. RPM — плавность роста/падения, пропуски
|
||
4. MAP — соответствует ли оборотам
|
||
5. Зажигание — есть ли коррекция, детонация
|
||
6. Аномалии — резкие скачки, выбросы
|
||
Отвечай кратко: 2-3 предложения вывода, затем по пунктам что не так.
|
||
```
|
||
|
||
## Что НЕ делаем
|
||
- GPS / акселерометр — не сейчас
|
||
- Автодетект фаз (разгон/сброс) — ручной СТАРТ/СТОП
|
||
- Отправка пачками в реальном времени — копим всё до СТОП
|