# Контракт: модель данных машины (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` | каноническая единица (**фиксированный набор**, метрические/авто-конвенциональные — часть не-СИ: km/h, rpm, °C, V, %, g/s, kPa) | | `range` | допустимый диапазон | | `source` | откуда читается: OBD-PID / native-CAN(DBC) / **computed** | | `availability` | **статически**: поддерживается ли PID данной машиной (пробинг на старте) | | `quality` | **рантайм-валидность значения**: `valid` / `stale` / `unavailable` (таймаут, двигатель заглушен) — ортогонально `availability` | | `max_age` | порог свежести (per-signal): старше → `quality=stale` | | `ts` | метка времени значения | **Транспорт-агностичность (важно):** сигнал каноничен, а **источник — это per-vehicle маппинг.** Один и тот же `Vehicle.Speed` может читаться через ELM327 (OBD-PID `0x0D`) или нативный SocketCAN (сигнал из DBC). Это и есть мост «ELM327 на старте → SocketCAN в проде» и основа портирования под разные авто (см. [hardware.md](hardware.md)). **Единицы и локаль:** в модели — канонический набор единиц; отображение (km vs miles) — дело shell/настроек, конвертация на презентации, не в модели. ## 3. Доступность сигналов (per-vehicle) Не все машины отдают все PID. `VehicleData` при старте **пробит поддерживаемые PID** (Mode 01, PID `0x00/0x20/0x40/…` — битовые маски поддержки) и помечает сигнал `Unavailable`, если его нет. Консьюмеры (ассистент, UI) обязаны это учитывать — «нет данных», а не падение (принцип #4). **Сервисные функции (Mode 03/07/0A DTC) — детектятся отдельно:** support-bitmask у них нет, feature-detection = trial-read (валидный ответ → есть; NACK/timeout → нет). **Если пробинг (PID 0x00) пуст/недоступен — НЕ каскадим «всё Unavailable»:** fallback на best-effort прямой опрос приоритетного набора PID + trial Mode 03. **MVP killer-фичи** (`mil_on` PID 01 + DTC Mode 03) **не зависит от bitmask-пробинга** — доступность определяется прямым чтением этих PID/сервисов. > Рантайм-устаревание (≠ статической доступности) несёт поле `quality` (§2), синхрон с ipc. ## 4. Каталог стартовых сигналов (OBD-II Mode 01) > `id` — наш канонический путь (≈ VSS, **точные VSS-листы сверяем со спецификацией > при реализации**). Базовый набор; расширяем по ходу домена E. > > **`mil_on` (PID 0x01) — ключевой для killer-фичи** «почему горит чек»: это сам > факт горящей лампы + число ошибок, отдельно от их чтения (Mode 03). > `module_voltage` (0x42) — напряжение питания ЭБУ ≈ бортсеть (близко, но не ровно клеммы АКБ); property в ipc — `ModuleVoltage`. | 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.OBD.ControlModuleVoltage` | 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..127 (7-бит) | | `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`/`C`/`B`/`U`) + **2-битное поле категории** (0–3, не полный hex) + 3 hex-цифры. Для P-кодов: `P0`/`P2` — generic (ISO/SAE), `P1` — производитель, `P3` — смешанный (`P3000–P33FF` производитель, `P3400–P3FFF` SAE-reserved); у C/B/U своя разбивка — уточняем по SAE J2012 при реализации. - **Статусы:** `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 0x06–0x09) и данные 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 | | Единицы | канонический набор единиц (метрич./авто-конвенц., часть не-СИ); конвертация на презентации | 2026-06-16 | | Валидность значения | runtime-поле `quality` (valid/stale/unavailable) + `max_age`, ортогонально `availability` | 2026-06-16 | | DTC | чтение Modes 03/07/0A; сброс (04) запрещён; расшифровка база + LLM | 2026-06-16 |