docs: tech-stack + контракты ipc/data-model (+ ревью-фиксы)

- tech-stack: Rust-first (один прод-рантайм; Python — dev-only), полный стек, крейты
- architecture: ассистент Python→Rust для консистентности; boot-порядок
  (Broker+App-Host до Shell); потолок частоты сигналов ~10–20 Гц
- contracts/ipc: реестр D-Bus (сервисы ядра/апов, соглашения, портал-брокеринг)
  + открытые вопросы из self-review
- contracts/data-model: VSS-aligned каталог сигналов, OBD-PID маппинг, DTC-модель;
  добавлен MIL-статус (PID 0x01) для killer-фичи, состояние машины, расход-estimate

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-20 20:01:36 +03:00
parent 9e685edd02
commit 3d13bb5827
5 changed files with 373 additions and 5 deletions
+135
View File
@@ -0,0 +1,135 @@
# Контракт: модель данных машины (VSS-подобная)
> Канонический каталог сигналов, которые публикует `VehicleData`. Наполняет
> конкретикой ipc-контракт, кормит контекст ассистента, домен E и killer-фичу.
Статус: **v1 (на ревью).**
Связано с: [ipc.md](ipc.md) (`VehicleData`) · [domains/](../domains/README.md) (E: Vehicle-Data) · [hardware.md](hardware.md) · [principles.md](../principles.md) (#4)
---
## 1. Подход: VSS-aligned, но лёгкий
- Имена сигналов — в стиле **COVESA VSS** (иерархические пути), типы и единицы — по
VSS. Но держим **свой лёгкий каталог**, а не тащим полный VSS-тулинг (Franca/
генераторы). Достаточно совместимости «по духу», чтобы говорить на общем языке и
легко расширяться.
- 🟡 *Выбор на подтверждение:* лёгкий VSS-aligned каталог (рекомендую) vs полное
принятие VSS-спеки с тулингом (тяжелее, избыточно на старте).
## 2. Модель сигнала
Каждый сигнал описывается:
| Поле | Смысл |
|------|-------|
| `id` | канонический VSS-стиль путь (напр. `Vehicle.Speed`) |
| `alias` | короткое имя для эргономики (`speed`) |
| `type` | `float` / `int` / `bool` / `enum` |
| `unit` | каноническая единица (**SI-стиль**: km/h, rpm, °C, V, %, g/s, kPa) |
| `range` | допустимый диапазон |
| `source` | откуда читается: OBD-PID / native-CAN(DBC) / **computed** |
| `availability` | поддерживается ли данной машиной (динамически) |
| `ts` | метка времени значения (для свежести) |
**Транспорт-агностичность (важно):** сигнал каноничен, а **источник — это
per-vehicle маппинг.** Один и тот же `Vehicle.Speed` может читаться через ELM327
(OBD-PID `0x0D`) или нативный SocketCAN (сигнал из DBC). Это и есть мост «ELM327 на
старте → SocketCAN в проде» и основа портирования под разные авто (см. [hardware.md](hardware.md)).
**Единицы и локаль:** в модели — каноническая SI; отображение (km vs miles) —
дело shell/настроек, конвертация на презентации, не в модели.
## 3. Доступность сигналов (per-vehicle)
Не все машины отдают все PID. `VehicleData` при старте **пробит поддерживаемые
PID** (Mode 01, PID `0x00/0x20/0x40/…` — битовые маски поддержки) и помечает
сигнал `Unavailable`, если его нет. Консьюмеры (ассистент, UI) обязаны это
учитывать — «нет данных», а не падение (принцип #4).
## 4. Каталог стартовых сигналов (OBD-II Mode 01)
> `id` — наш канонический путь (≈ VSS, **точные VSS-листы сверяем со спецификацией
> при реализации**). Базовый набор; расширяем по ходу домена E.
>
> **`mil_on` (PID 0x01) — ключевой для killer-фичи** «почему горит чек»: это сам
> факт горящей лампы + число ошибок, отдельно от их чтения (Mode 03).
> `module_voltage` (0x42) — напряжение питания ЭБУ ≈ бортсеть (близко, но не ровно клеммы АКБ).
| alias | id (≈ VSS) | PID | type | unit | range |
|-------|-----------|-----|------|------|-------|
| `speed` | `Vehicle.Speed` | 0x0D | int | km/h | 0..255 |
| `rpm` | `Vehicle.Powertrain.CombustionEngine.Speed` | 0x0C | float | rpm | 0..16383 |
| `engine_load` | `…CombustionEngine.Load` | 0x04 | float | % | 0..100 |
| `coolant_temp` | `…CombustionEngine.ECT` | 0x05 | int | °C | -40..215 |
| `intake_temp` | `…CombustionEngine.IAT` | 0x0F | int | °C | -40..215 |
| `maf` | `…CombustionEngine.MAF` | 0x10 | float | g/s | 0..655 |
| `throttle` | `…CombustionEngine.TPS` | 0x11 | float | % | 0..100 |
| `intake_pressure` | `…CombustionEngine.MAP` | 0x0B | int | kPa | 0..255 |
| `fuel_level` | `Vehicle.Powertrain.FuelSystem.Level` | 0x2F | float | % | 0..100 |
| `module_voltage` | `Vehicle.LowVoltageBattery.Voltage` | 0x42 | float | V | 0..65.5 |
| `ambient_temp` | `Vehicle.Exterior.AirTemperature` | 0x46 | int | °C | -40..215 |
| `oil_temp` | `…CombustionEngine.EOT` | 0x5C | int | °C | -40..215 |
| `fuel_rate` | `…CombustionEngine.FuelRate` | 0x5E | float | L/h | 0..3212 |
| `run_time` | `…CombustionEngine.RunTime` | 0x1F | int | s | 0..65535 |
| `mil_on` | `Vehicle.OBD.IsMILOn` | 0x01 | bool | — | вкл/выкл |
| `dtc_count` | `Vehicle.OBD.DTCCount` | 0x01 | int | шт | 0..255 |
| `distance_mil` | `Vehicle.OBD.DistanceWithMIL` | 0x21 | int | km | 0..65535 |
## 5. Производные (computed) сигналы
Слой поверх сырых — считаются внутри VehicleData (или отдельного компонента):
- **Мгновенный расход** — если есть прямой `fuel_rate` (0x5E), берём его; иначе
**оценка** из MAF (предполагает бензин/стехиометрию — помечаем как `estimate`,
зависит от типа топлива).
- **Пробег поездки / средний расход** (интеграл по времени; нужен trip-стейт).
- **Состояние машины** (`off` / `accessory` / `running`) — грубо из RPM/зажигания;
нужно ассистенту, чтобы отличать «двигатель заглушен» от «сигнал недоступен» от «ноль».
- Помечаются `source = computed`; зависят от наличия исходных сигналов.
## 6. Модель DTC (диагностические коды)
- **Формат кода:** буква (`P` powertrain / `C` chassis / `B` body / `U` network) +
4 hex-цифры. Грубо: `0` — стандарт SAE/ISO, `1` — производитель (полная карта
сложнее: `P2xxx` тоже стандарт, `P3xxx` смешанные).
- **Статусы:** `confirmed` (Mode 03), `pending` (Mode 07), `permanent` (Mode 0A).
- **Чтение:** Modes 03/07/0A — это **чтение**, разрешено.
**Сброс (Mode 04) — НЕТ.** Read-only (принцип #2).
- **Расшифровка двухслойная:** статическая база `код → стандартное описание`
(живёт в домене E, `vehicle/dtc/`) + **LLM** даёт человеческое объяснение
по-русски в контексте машины. Manufacturer-specific (`P1xxx`) требуют вендорской
базы (задел, §7).
## 7. Расширение: вендорские сигналы и коды (задел)
- Нативный CAN конкретного авто описывается **DBC-файлом** → даёт сигналы сверх
стандартного OBD и вендорские DTC.
- Это per-vehicle/per-OEM маппинг, ложится на **HAL/board-support** ([hardware.md](hardware.md))
и на портирование силами автопроизводителей/энтузиастов (vision: «API под
конкретное железо/авто»). Плагин/порт может поставлять свой DBC + базу кодов.
---
## Открытые вопросы (найдено на self-review → роутинг)
- **Минимальный набор PID на простых авто.** Старые/простые машины (наша целевая
аудитория — Lada и т.п.) отдают мало Mode-01 PID. **MVP killer-фичи опирается на
универсальное:** `mil_on` + чтение DTC (Mode 03); богатые PID — best-effort. → домен E.
- **«Почему вырос расход».** Для этого кейса из vision полезны топливные коррекции
(Short/Long Term Fuel Trim, PID 0x060x09) и данные O2 — диагностическое расширение. → домен E.
- **Идентификация авто (VIN).** Mode 09 PID 0x02 — даёт привязку к конкретной машине
(память о водителе, вендорские базы кодов). Задел. → домен E + домен D.
- **DBC для нативного CAN часто проприетарны** или добываются реверсом
(community-DBC, opendbc). Учесть в портировании. → [hardware.md](hardware.md) + домен E.
---
## Журнал решений (data-model)
| Решение | Выбор | Дата |
|---------|-------|------|
| Глубина VSS | лёгкий VSS-aligned каталог (🟡 на подтверждение) | 2026-06-16 |
| Источник сигнала | транспорт-агностичен: канон + per-vehicle маппинг (PID / DBC / computed) | 2026-06-16 |
| Единицы | каноническая SI в модели; конвертация на презентации | 2026-06-16 |
| DTC | чтение Modes 03/07/0A; сброс (04) запрещён; расшифровка база + LLM | 2026-06-16 |