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:
@@ -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 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 |
|
||||
| Единицы | каноническая SI в модели; конвертация на презентации | 2026-06-16 |
|
||||
| DTC | чтение Modes 03/07/0A; сброс (04) запрещён; расшифровка база + LLM | 2026-06-16 |
|
||||
@@ -0,0 +1,146 @@
|
||||
# Контракт: IPC (D-Bus)
|
||||
|
||||
> Backbone связей: реестр D-Bus-сервисов, их интерфейсов и соглашений.
|
||||
> Формализует карту связей из [architecture.md](../architecture.md) §5 в конкретные
|
||||
> контракты. К нему цепляются data-model, plugin-sdk и все домены.
|
||||
|
||||
Статус: **v1 (на ревью).**
|
||||
Связано с: [architecture.md](../architecture.md) (§4–5) · [data-model.md](data-model.md) · [plugin-sdk.md](plugin-sdk.md) · [security-privacy.md](security-privacy.md)
|
||||
|
||||
Область: **control plane** (D-Bus). Аудио/видео/графика — НЕ здесь (PipeWire/Wayland, см. architecture §4).
|
||||
|
||||
---
|
||||
|
||||
## 1. Шина и топология
|
||||
|
||||
- **Одна системная шина устройства** (Штурман — single-purpose appliance, системная шина *и есть* его шина) со строгой D-Bus-политикой.
|
||||
- Ядро-сервисы общаются на ней напрямую.
|
||||
- **Песочные апы/плагины напрямую шину НЕ видят** — каждому выдаётся
|
||||
**фильтрующий D-Bus-прокси** (паттерн `xdg-dbus-proxy`), настроенный App-Host'ом
|
||||
из capabilities манифеста (см. §5). Это реализация портал-модели из решения №5.
|
||||
|
||||
> 🟡 *Мелкий выбор на подтверждение:* системная шина устройства (рекомендую,
|
||||
> проще) vs выделенный экземпляр `dbus-daemon` только для Штурмана (чище изоляция
|
||||
> от хост-сервисов, но лишний компонент). Прокси-фильтрация даёт изоляцию в обоих
|
||||
> случаях.
|
||||
|
||||
## 2. Соглашения
|
||||
|
||||
| Аспект | Правило |
|
||||
|--------|---------|
|
||||
| Well-known имя | `ru.shturman.<Service>` (напр. `ru.shturman.VehicleData`) |
|
||||
| Путь объекта | `/ru/shturman/<Service>` |
|
||||
| Интерфейс | `ru.shturman.<Service>N` — `N` = мажорная версия (напр. `…VehicleData1`) |
|
||||
| Версионирование | аддитивные изменения внутри версии; ломающие → новый интерфейс `…2` |
|
||||
| Ошибки | `ru.shturman.Error.<Name>`: `PermissionDenied`, `NotAvailable`, `ReadOnly`, `InvalidArgument`, `Unsupported` |
|
||||
| Действие | **метод** |
|
||||
| Изменение состояния | **сигнал** |
|
||||
| Текущее состояние | **property** (+ стандартный `PropertiesChanged`) |
|
||||
| Асинхронность | всё async (zbus + tokio); долгие операции не блокируют |
|
||||
| Стандартные интерфейсы | `org.freedesktop.DBus.{Properties,Introspectable,Peer}` |
|
||||
|
||||
> Полные сигнатуры (XML-интроспекция) фиксируются при реализации; здесь — реестр и ключевые члены.
|
||||
|
||||
---
|
||||
|
||||
## 3. Реестр сервисов — ядро (привилегированные)
|
||||
|
||||
### `ru.shturman.VehicleData` — данные машины (**READ-ONLY**)
|
||||
- **Методы (только чтение):** `ListSignals() → [name]`, `GetSignal(name) → (value, unit, ts)`, `Subscribe(names, max_hz)`/`Unsubscribe(names)`, `GetDtcs() → [(code, status, desc)]`.
|
||||
- **Сигналы:** `SignalChanged(name, value, ts)` — **только подписчикам и только по подписанным** именам (не широковещательно «всё всем»), `DtcsChanged([...])`.
|
||||
- **Политика частоты:** на шину сигналы идут **с потолком (~10–20 Гц)** — этого хватает для читаемых датчиков и держит control-plane лёгким. Сырой высокочастотный CAN остаётся внутри VehicleData. Цифровому приборному щитку, если нужна более плавная стрелка, — клиентская интерполяция или выделенный быстрый канал (не D-Bus); см. «Открытые вопросы».
|
||||
- **Properties (горячие, текущее значение):** `Speed`, `Rpm`, `CoolantTemp`, `BatteryVoltage`, `Online`.
|
||||
- ⛔ **Методов записи нет** — ни `SetSignal`, ни actuator-команд, ни clear-DTC. Архитектурная гарантия read-only (принцип #2). Типы — [data-model.md](data-model.md).
|
||||
|
||||
### `ru.shturman.Power` — питание и жизненный цикл
|
||||
- **Методы:** `GetPowerState() → state`, `RequestSleep()` (внутр.).
|
||||
- **Сигналы:** `AccChanged(on)`, `ShutdownImminent(seconds)`, `Sleep()`, `Wake()`.
|
||||
- **Properties:** `IgnitionState`, `Uptime`, `PowerSource`.
|
||||
|
||||
### `ru.shturman.Settings` — конфигурация и состояние
|
||||
- **Методы:** `Get(key) → value`, `Set(key, value)`, `List(prefix) → [key]`, `Reset(key)`.
|
||||
- **Сигналы:** `Changed(key, value)`.
|
||||
- Namespace на каждый ап изолирован (брокер не даёт читать чужой).
|
||||
|
||||
### `ru.shturman.PermBroker` — выдача разрешений (привратник)
|
||||
- **Методы:** `CheckCapability(app, cap) → bool`, `RequestRuntimeGrant(cap) → granted` (рантайм-промпт пользователю, напр. «ап X хочет сеть»).
|
||||
- **Сигналы:** `GrantChanged(app, cap, granted)`.
|
||||
- Основная фильтрация — на уровне прокси (§5); этот API для рантайм-согласий.
|
||||
|
||||
### `ru.shturman.AppHost` — запуск/супервизия апов и плагинов
|
||||
- **Методы:** `ListApps() → [(id, status)]`, `StartApp(id)`, `StopApp(id)`, `GetStatus(id) → status`.
|
||||
- **Сигналы:** `AppStarted(id)`, `AppStopped(id)`, `AppCrashed(id, reason)`.
|
||||
- (Установка/дистрибуция плагинов — домен F, не здесь.)
|
||||
|
||||
### `ru.shturman.Connectivity` — сеть (обёртка NM/MM)
|
||||
- **Методы:** `GetStatus() → status`, `ListNetworks() → [...]`, `Connect(id)`, `Disconnect()`.
|
||||
- **Сигналы:** `ConnectivityChanged(online)`, `NetworkChanged(...)`.
|
||||
- **Properties:** `Online`, `Type` (wifi/modem), `SignalStrength`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Реестр сервисов — апы (на SDK)
|
||||
|
||||
### `ru.shturman.Assistant`
|
||||
- **Методы:** `SendText(query) → reply`, `StartListening()`, `StopListening()`, `RegisterIntents(handler_path, [pattern])` (для плагинов с capability `assistant_intents`).
|
||||
- **Сигналы:** `StateChanged(idle|listening|thinking|speaking)`, `TranscriptUpdated(text)`, `ReplyUpdated(text)`.
|
||||
- Контекст машины подмешивается ассистентом из `VehicleData` (см. architecture §5).
|
||||
|
||||
### `ru.shturman.Shell` — UX-host (слот-модель, решение №6)
|
||||
- **Методы:** `RegisterTile(decl)`, `RegisterScreen(decl | surface_handle)`, `RequestForeground(slot)`, `ShowOverlay(decl)`.
|
||||
- **Сигналы:** `ForegroundChanged(app)`, `NavigatedTo(screen)`, `DistractionModeChanged(level)`.
|
||||
- Декларативный вклад = данные (рендерит shell); богатый = Wayland-поверхность.
|
||||
|
||||
### `ru.shturman.Media` / `…Nav` / `…Phone`
|
||||
- Свои интерфейсы (play/pause/route; navigateTo/cancel; dial/answer/hangup). **Детали — в спеках доменов** H/I/G; здесь — только факт регистрации на шине.
|
||||
|
||||
---
|
||||
|
||||
## 5. Брокеринг доступа песочных апов (портал-паттерн)
|
||||
|
||||
1. Манифест плагина декларирует capabilities (`vehicle_read:[speed]`, `network`, `assistant_intents`…).
|
||||
2. App-Host поднимает плагин в bubblewrap и даёт ему **фильтрующий D-Bus-прокси**, сконфигурированный под эти capabilities.
|
||||
3. Прокси пропускает только разрешённые сервисы/интерфейсы/методы/сигналы. Пример: `vehicle_read:[speed]` → виден `VehicleData.GetSignal("speed")` и `SignalChanged` по speed; **не виден** `Power`, чужие апы, любые write-методы.
|
||||
4. Рантайм-согласия (сеть «сейчас») — через `PermBroker.RequestRuntimeGrant`.
|
||||
|
||||
Итог: плагин физически не может дотянуться до того, чего нет в манифесте. Детали модели — [security-privacy.md](security-privacy.md).
|
||||
|
||||
---
|
||||
|
||||
## 6. Точки расширения через IPC
|
||||
|
||||
- **Интенты ассистента:** плагин реализует `ru.shturman.IntentHandler1.HandleIntent(intent_id, slots) → result` и регистрируется через `Assistant.RegisterIntents`. Латентно-критичные интенты (громче, домой) ассистент обрабатывает локально без LLM.
|
||||
- **Вклад в UI:** плагин зовёт `Shell.RegisterTile/RegisterScreen` (декларативно или поверхностью).
|
||||
|
||||
Полные контракты SDK (манифест, биндинги, точки расширения) — [plugin-sdk.md](plugin-sdk.md).
|
||||
|
||||
---
|
||||
|
||||
## Открытые вопросы (найдено на self-review → роутинг в нужные доки)
|
||||
|
||||
Не теряем; решается в указанных документах.
|
||||
|
||||
- **Гейтинг data-plane, не только D-Bus.** Портал-прокси (§5) фильтрует D-Bus, но
|
||||
доступ к **PipeWire (аудио)** и **Wayland (графика)** тоже надо гейтить
|
||||
capability'ями (через bubblewrap-сокеты). → [security-privacy.md](security-privacy.md) + [plugin-sdk.md](plugin-sdk.md).
|
||||
- **Экспорт объектов из песочницы.** Чтобы Assistant/Shell звали `IntentHandler`/UI
|
||||
плагина, прокси должен разрешать плагину **OWN/экспорт** своих объектов под
|
||||
ограниченным префиксом. → [security-privacy.md](security-privacy.md).
|
||||
- **Приватное хранилище апа.** Capability `storage` должна давать апу приватную
|
||||
writable-директорию в `/data` (монтирует bubblewrap). → [security-privacy.md](security-privacy.md).
|
||||
- **Коллизия интентов.** Два плагина регистрируют пересекающиеся фразы — нужна
|
||||
политика разрешения (приоритет / namespace / выбор пользователя). → [plugin-sdk.md](plugin-sdk.md) + домен D.
|
||||
- **Целостность манифеста.** Для сторонней дистрибуции — подпись/верификация
|
||||
манифеста (защита от подмены capabilities). → домен F + [security-privacy.md](security-privacy.md).
|
||||
- **Быстрый канал для приборки.** Если цифровой щиток требует плавнее, чем потолок
|
||||
шины — спроектировать выделенный канал. → [architecture.md](../architecture.md) §4 + домен E.
|
||||
|
||||
---
|
||||
|
||||
## Журнал решений (ipc)
|
||||
|
||||
| Решение | Выбор | Дата |
|
||||
|---------|-------|------|
|
||||
| Топология шины | системная шина устройства + фильтрующий прокси на ап (🟡 на подтверждение) | 2026-06-16 |
|
||||
| Изоляция доступа апов | портал-паттерн: per-app dbus-proxy из манифеста | 2026-06-16 |
|
||||
| Соглашения имён/версий | `ru.shturman.*`, версия в имени интерфейса | 2026-06-16 |
|
||||
Reference in New Issue
Block a user