77d9a5a0ee
Новый домен H (медиа/аудио). Ядро — сквозной арбитр аудио (политика фокуса/ducking поверх PipeWire, role-based), плюс медиаплеер (локальное/BT A2DP/AVRCP/радио/стриминг). Многоагентный adversarial-ревью: 24 находки, 20 подтверждено (default-refute), все применены. Ключевое из ревью: - symphonia НЕ декодирует Opus → отдельный audiopus (libopus, BSD). - AEC: module-echo-cancel (≠ filter-chain) в audio-plane, не в ассистенте — резолв D §147. - media.role (арбитраж) vs media.category (Playback/Capture) разведены. - Кнопки громкости мультируля — uinput→Wayland-input (C/K), не интенты ассистента. - Crash-safe по двум осям (source: жизнь ноды + проактивный cork по NameOwnerChanged/watchdog; sink: пересчёт при возврате output). - intra-role media-фокус (один media-продюсер), гистерезис фокуса (анти-pumping), duck = относит. аттенюатор. - boot аудио-плоскости на Stage 1; отказ-пути плеера (битый файл/обрыв A2DP/ENOSPC/underrun); resume без авто-старта. Кросс-док: D §147 AEC→✅; tech-stack (symphonia/audiopus/module-echo-cancel); hardware §4 (amp-mute-GPIO + FM-тюнер + откр. вопросы); B §4 (amp-mute перед cut); architecture §9 (аудио-арбитр → H); domains/README (строка H). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
218 lines
20 KiB
Markdown
218 lines
20 KiB
Markdown
# Домен B — Power / Lifecycle
|
||
|
||
> Привилегированный core-сервис: владеет power-path, детектом зажигания, **graceful
|
||
> shutdown**, протоколом MCU-копилота, watchdog, sleep/wake и сохранением времени.
|
||
> **Вторая половина «power-safe с дня 1»** (с доменом A).
|
||
|
||
Статус: **v2 (на ревью).** v2 — после adversarial-ревью (22 находки).
|
||
Связано с: [architecture.md](../architecture.md) (§6) · [hardware.md](../contracts/hardware.md) (§3 питание/MCU) · [a-base-system.md](a-base-system.md) (§5–§11) · [ipc.md](../contracts/ipc.md) (`Power`) · [principles.md](../principles.md) (#5) · домены D (гейт wake-word), E (engine-state)
|
||
|
||
---
|
||
|
||
## 1. Назначение и границы
|
||
|
||
- **Что делает:** детект зажигания, секвенсинг питания, graceful shutdown, watchdog-арбитраж,
|
||
sleep/wake, сохранение времени; публикует `ru.shturman.Power`.
|
||
- **Границы:** не трогает CAN и не управляет узлами авто; координирует **только питание устройства**.
|
||
- **Power-safe (#5) = A + B вместе:** A — read-only rootfs / ФС / atomic-write; B — graceful
|
||
shutdown + hold-up-секвенсинг + watchdog.
|
||
- **Владение состоянием:** `Power.IgnitionState` (off/accessory/running) — **единственный
|
||
канонический источник** lifecycle-состояния (voltage/GPIO/MCU). E владеет лишь
|
||
`engine_running` (узко, из RPM — отличить «заглушен» от «нет данных»), не дублирует
|
||
IgnitionState (см. §9, e-vehicle-data §2).
|
||
|
||
## 2. Машина состояний питания
|
||
|
||
```
|
||
┌─────┐ ACC-on ┌───────────┐ напряжение>порог ┌──────────┐
|
||
│ off │──────────▶│ accessory │──(генератор)──────▶│ running │
|
||
└─────┘ └───────────┘◀──напряжение<порог─└──────────┘
|
||
▲ ▲ │ │
|
||
│ │ ACC-off(debounced)/under-voltage-held/thermal-trip│
|
||
│ │ ▼ │
|
||
│ │ ┌──────────────────────┐◀─────────────────┘
|
||
│ │ │ shutting-down(ABORTABLE)│ pre-PONR: re-power → назад в accessory/running
|
||
│ │ └──────────┬────────────┘
|
||
│ │ unmount (PONR) ▼
|
||
│ └──── cut ◀─ ┌──────────────────────┐ committed: только → off
|
||
│ │ shutting-down(COMMITTED)│
|
||
│ └──────────────────────┘
|
||
off ◀─ battery-cutoff ◀─ sleep ⇄ accessory/running (sleep: wake по ACC/таймеру/реверсу)
|
||
```
|
||
|
||
- **`running` детектится по напряжению бортсети** (генератор ~13.8–14.4 В vs покой ~12.2–12.6 В),
|
||
доступному Power/MCU напрямую через ADC того же рейла, где hardware §3 ставит brown-out-пороги.
|
||
RPM/CAN — **опциональное уточнение в v2** (когда поднимется E), не обязательное.
|
||
- **`shutting-down` разбит на ABORTABLE (до point-of-no-return) и COMMITTED (с unmount).**
|
||
- **`battery-cutoff`:** при долгой стоянке — forced off для сохранения энергии на запуск (§7).
|
||
- **Триггеры shutting-down:** ACC-off (debounced) · under-voltage held · **thermal-trip** (§4).
|
||
|
||
## 3. Детект зажигания (ACC) — кондиционированный
|
||
|
||
Не голый уровень GPIO (hardware §3): **debounce/glitch** + минимальная стабильная длительность
|
||
ACC-off **больше** worst-case cold-crank-просадки; **гистерезис** + **приоритет crank**.
|
||
Владелец: **MCU-копилот** (рек.) либо **софт на SoC** (supercap-only).
|
||
|
||
## 4. Graceful shutdown (секвенсинг)
|
||
|
||
**Триггеры (три, все ведут полный путь — есть время на флаш, кроме самого резкого обрыва):**
|
||
- **ACC-off (debounced, окно > crank)** — авторитетное решение «выключаемся».
|
||
- **Under-voltage ниже brown-out-порога с гистерезисом (hardware §3), удержанное дольше
|
||
worst-case crank** — backstop при реальном обвале рейла (НЕ rate-of-change: «резкое падение»
|
||
срабатывало бы на каждом запуске, cold-crank до ~6 В — НОРМА, ride-through).
|
||
- **Thermal-trip** (критическая температура, a-base §10) — причина ≠ ACC, UX «перегрев» в Shell.
|
||
|
||
**Порядок (sizing энергии — hardware §3; секвенсинг — здесь):**
|
||
1. `Power` эмитит `ShutdownImminent(seconds, reason)`.
|
||
2. **Load-shedding** (политика по триггеру):
|
||
- *резкий обрыв/hold-up:* агрессивно сбросить всё, кроме SoC+хранилище;
|
||
- *управляемый ACC-off:* мягко, с коротким grace-окном на дисплей/камеру-рейл (парковка с реверсом — §7, B-COMPL-04).
|
||
- **amp-mute перед снятием рейла усилителя** (anti-pop): mute-GPIO усилителя (hardware §4) дёргается ДО cut — шов с медиа-выходом H §8.
|
||
3. **Ordered teardown:** App-Host останавливает апы/плагины (`StopApp`) с per-app ack/save-таймаутом
|
||
(сумма ≤ hold-up-бюджет); **E на `ShutdownImminent` гасит активный OBD-TX и закрывает ISO-TP-сессии**
|
||
(B даёт сигнал, E владеет закрытием — B не трогает CAN).
|
||
4. Settings флашится в `/data` + критичные логи `fsync` (a-base §9) — **критичный fsync завершается ДО PONR**.
|
||
5. Сохранение last-known-time (§8).
|
||
6. Размонтирование RW → **point-of-no-return**.
|
||
7. **Sn-питания** по `safe-to-cut` (§5).
|
||
|
||
**Abort (re-power до PONR):** стабильное восстановление ACC/напряжения (через debounce/гистерезис)
|
||
→ обратный переход; **откат:** переподнять сброшенные нагрузки, снять у апов save-режим;
|
||
`Power` эмитит **`ShutdownAborted`** (= канонизированный сигнал отмены). После PONR abort недоступен.
|
||
|
||
**Деградированный путь (исчерпание hold-up):** часть шагов — critical-and-atomic (должны
|
||
завершиться), часть — best-effort (могут быть потеряны); инвариант **«после усечённого shutdown
|
||
`/data` консистентен»** держится atomic-write-контрактом A §3.
|
||
|
||
## 5. MCU-копилот (прошивка + протокол)
|
||
|
||
- **Делает:** мониторит зажигание (дебаунснутый ACC) и напряжение, управляет hold-up/секвенсингом,
|
||
сигналит SoC, держит **watchdog**, sleep/wake.
|
||
|
||
**Shutdown-протокол (специфицируем СЕЙЧАС — shutdown это MVP, не §12-задел):**
|
||
1. ACC-off → MCU стартует hold-up-таймер (= hold-time hardware §3); **это и есть тот же бюджет**,
|
||
что fail-safe-таймер §5 (один когерентный, не два).
|
||
2. SoC ведёт секвенсинг (§4 шаги 2–6); **heartbeat SoC→MCU во время shutdown** — MCU отличает
|
||
«SoC работает» (держать питание до бюджета) от «SoC завис» (cut по таймеру).
|
||
3. SoC шлёт **`safe-to-cut`** после PONR → MCU снимает питание немедленно.
|
||
4. Таймер истёк без `safe-to-cut` → MCU снимает всё равно (детерминированно; durability — до последнего fsync).
|
||
5. **Потеря линка** (обрыв шлейфа): fail-safe в ОБЕ стороны определён явно — MCU при тишине в
|
||
running трактует по политике (cut/reboot vs hold — §12); SoC при тишине MCU — деградирует.
|
||
6. **Supercap-only:** аналог на SoC-таймере + разряд cap (нет умного MCU для cut).
|
||
|
||
**Безопасность линка:** MCU — **fail-safe-авторитет**, НЕ подчиняется слепо командам SoC с
|
||
power-эффектом; ни одно SoC-сообщение не должно (а) снять питание на ходу, (б) держать вечно и
|
||
разрядить АКБ. Защита от replay/мусора/десинка на UART/I2C (аналог защиты CAN-линка hardware §4).
|
||
|
||
**Критичность MCU:** прошивка **обновляема** (бутлоадер — баг не кирпичит); **независимый
|
||
аппаратный fail-safe-таймер** снятия питания при отказе самого MCU; **позиция в цепочке доверия**
|
||
(подмена прошивки не обходит red-line). ⚠️ **Если v0-таргет = MCU-вариант — аппаратный fail-safe-
|
||
таймер ОБЯЗАН быть в v0** (иначе v0 power-safe не закрыт); прошивка-update — v1.
|
||
|
||
### MCU-вариант vs supercap-only
|
||
|
||
| Функция | MCU-вариант | supercap-only | Потеря/замена |
|
||
|---------|-------------|---------------|---------------|
|
||
| Детект ACC | MCU (дебаунс) | софт на SoC | — |
|
||
| Hold-up секвенсинг | MCU-таймер + safe-to-cut | SoC-таймер + разряд cap | при зависшем SoC некому shed/cut → нужен внешний таймер или принятый риск |
|
||
| Fail-safe при зависшем SoC | независимый MCU-таймер | **отсутствует** | внешний WDT-чип ЛИБО письменный риск единой точки |
|
||
| Watchdog-бэкстоп | независимый от SoC-WDT | только SoC-WDT | см. §6 |
|
||
| Sleep / scheduled-wake | always-on MCU | ограничено (нет always-on) | глубокий sleep/таймер-wake может быть недоступен |
|
||
|
||
## 6. Watchdog (по фазам жизненного цикла)
|
||
|
||
- **Runtime:** on-SoC RK3588 WDT — **единственный userspace-владелец** (`systemd RuntimeWatchdogSec`),
|
||
второй пэтящий клиент запрещён.
|
||
- **Shutdown-фаза:** `RuntimeWatchdogSec` её НЕ покрывает → отдельный таймаут: systemd
|
||
`RebootWatchdogSec` (путь reboot) **либо** детерминированный дедлайн MCU/supercap на финальный
|
||
unmount/sync (шаги 4–6). Зависание в sync/unmount (PONR) не оставляет устройство под питанием вечно.
|
||
**MCU на `shutdown-imminent` переключается в wait-for-completion** (расширенный таймаут ≥ shutdown-бюджет),
|
||
не продолжает короткий keepalive — чтобы не снять питание посреди unmount.
|
||
- **MCU-watchdog** — независимый бэкстоп.
|
||
- **Boot-окно:** WDT вооружён до userspace (U-Boot / always-on MCU).
|
||
- **System-of-record форс-ресета (v0)** = SoC-WDT + MCU-backstop + reboot, стыкуемый с **базовым
|
||
recovery A §6 (v0)**. Интеграция force-reset → **bootcount/slot-switch/откат A/B (A §5) — v4** (с OTA);
|
||
v0-watchdog от bootcount-handshake НЕ зависит.
|
||
|
||
## 7. Sleep / wake и защита АКБ
|
||
|
||
- **Низкопотребление** при заглушённом двигателе (быстрый/scheduled wake).
|
||
- **Источники пробуждения:** ACC-on, таймер, **реверс-передача** (Stage-0 камера, §4/J), опц. CAN-wakeup.
|
||
- **`battery-cutoff` (отдельно от ACC-off):** при долгой стоянке порог напряжения (раньше/выше
|
||
operating-brown-out hardware §3 — зарезервировать энергию на запуск) → sleep → forced off;
|
||
гистерезис на wake-on-ACC. Бюджет разряда — §12 (с hardware).
|
||
- **Гейт wake-word:** D §8 слушает только в `running`/`accessory`, **не** в `sleep`/`off` (читает сигналы `Power`).
|
||
|
||
## 8. Сохранение и монотонность времени
|
||
|
||
- **Save last-known-time** (`fake-hwclock`) в `/data` **не только на graceful shutdown**, но и:
|
||
(а) **периодически** systemd-таймером (~1–5 мин, только вперёд, по порогу прироста, atomic-write A §3);
|
||
(б) на каждом успешном NTP/GPS-синке (a-base §7). Save-on-shutdown — *финальный*, не единственный
|
||
(после резкого обрыва часы откатываются максимум на один интервал, не на всю сессию).
|
||
- **Lifecycle-таймеры на МОНОТОННЫХ часах** (`CLOCK_MONOTONIC`): `ShutdownImminent`-отсчёт, hold-up-бюджет,
|
||
watchdog keepalive, `Uptime`, sleep/wake — НЕ wall-clock (плата без RTC, wall-clock легитимно прыгает при синке).
|
||
- **Первый boot/factory-reset** (нет файла fake-hwclock): wall-clock с epoch до первого fix → TLS-egress
|
||
gated (a-base §7), метки критичных/audit-логов помечаются «до синка»; lifecycle-таймеры работают (монотонные).
|
||
- **Монотонность «только вперёд»:** при boot wall-clock = `max(fake-hwclock, build-epoch, last-log-ts)` —
|
||
не идти назад относительно последней записи (порядок audit-log не ломается).
|
||
|
||
## 9. IPC (`ru.shturman.Power`)
|
||
|
||
Реестр — [ipc.md](../contracts/ipc.md) §3. Уточнения этого домена:
|
||
- `GetPowerState() → state` — enum по §2 (`off`/`accessory`/`running`/`shutting_down`/`sleep`/`battery_cutoff`).
|
||
- `IgnitionState` (ось зажигания: off/accessory/running) — **канонический**; E зеркалит/потребляет, не дублирует.
|
||
- `PowerSource` — enum `{ vehicle_12v, holdup_cap, sleep_rail, low_battery }`; переход в `holdup_cap`
|
||
сообщает потребителям «питание от cap, времени мало».
|
||
- `ShutdownImminent(seconds, reason)` — `reason ∈ {acc_off, under_voltage, thermal, battery_cutoff}`.
|
||
- **`ShutdownAborted()`** — отмена (re-power до PONR).
|
||
|
||
## 10. Функции
|
||
|
||
| функция | MVP/later | зависит от | фаза |
|
||
|---------|-----------|------------|------|
|
||
| Детект ACC (debounce + crank) + `running` по напряжению | **MVP (день 1)** | hardware/MCU | v0 |
|
||
| Graceful shutdown sequencing (+ ordered teardown) | **MVP (день 1)** | a-base, hold-up (hardware §3) | v0 |
|
||
| Машина состояний (+ abort/committed) | **MVP** | — | v0 |
|
||
| `ru.shturman.Power` сервис | **MVP** | ipc | v0 |
|
||
| Watchdog (runtime + shutdown-фаза + boot-окно) | **MVP** | hardware, a-base | v0 |
|
||
| Load-shedding при power-loss | **MVP** | селективные рейлы (hardware §6) | v0 |
|
||
| Save last-known-time + периодика | **MVP** | a-base §7 | v0 |
|
||
| MCU-копилот shutdown-протокол (если MCU) | **MVP** | hardware §3 | v0 |
|
||
| **MCU аппаратный fail-safe-таймер** | **MVP** | hardware §3 | v0 |
|
||
| Thermal shutdown (триггер + hysteresis + UX) | **MVP** | a-base §10, hardware §1a | v0/v1 |
|
||
| MCU прошивка: update path | later | hardware | v1 |
|
||
| Sleep/wake + battery-cutoff | later | — | v1/v2 |
|
||
| Гейт wake-word по состояниям (с D) | **MVP** | D | v1 |
|
||
| Реверс-wake (Stage-0 камера) · scheduled/CAN-wake | later | hardware §4, J | v2/later |
|
||
|
||
## 11. Зависимости
|
||
|
||
- **Вниз:** hardware (power-path, **селективные рейлы**, ACC-GPIO, ADC напряжения, MCU + fail-safe-таймер, hold-up sizing, secure-boot trust).
|
||
- **Вбок:** A (RO-rootfs/ФС/atomic-write/время-синк/recovery — **вместе делают power-safe**); E (engine_running, закрытие ISO-TP на shutdown).
|
||
- **Вверх/потребители:** все апы (`ShutdownImminent`→«сохранись», `ShutdownAborted`→откат); Settings; D (гейт wake-word); Shell (UX «перегрев»).
|
||
|
||
## 12. Открытые вопросы
|
||
|
||
- 🟡 **MCU-копилот vs supercap-only** (hardware §3) — определяет владельца ACC/watchdog, **наличие
|
||
независимого бэкстопа и fail-safe-снятия при зависшем SoC**, и доступность scheduled-wake.
|
||
- ◻️ **Протокол SoC↔MCU** (UART/I2C/GPIO, формат, keepalive, политика тишины-линка, replay-защита) —
|
||
shutdown-подмножество уже специфицировано в §4/§5, остальное здесь.
|
||
- ◻️ **Бюджет разряда АКБ** (sleep, ACC-off listening, battery-cutoff порог) — числа с hardware.
|
||
- ◻️ **Тепловые пороги** (critical-trip + recovery-hysteresis) — согласовать с a-base §10 + hardware §1a.
|
||
- ◻️ **Удержание дисплей/камера-рейла после ACC-off** — кто арбитрит grace-hold (B состояние, C/J запрос); с реверс-камерой Stage 0.
|
||
|
||
---
|
||
|
||
## Журнал решений (домен B)
|
||
|
||
| Решение | Выбор | Дата |
|
||
|---------|-------|------|
|
||
| Детект | ACC (debounce+crank) + `running` по напряжению (RPM — v2-уточнение); IgnitionState — канон B | 2026-06-16 |
|
||
| Триггеры shutdown | ACC-off / under-voltage-held (не rate-of-change) / thermal-trip; cold-crank — ride-through | 2026-06-16 |
|
||
| Shutdown | sequencing + ordered teardown (E закрывает ISO-TP); abort до PONR (`ShutdownAborted`); degraded path | 2026-06-16 |
|
||
| MCU-протокол | hold-up-таймер + heartbeat + `safe-to-cut`; fail-safe при потере линка; MCU — fail-safe-авторитет | 2026-06-16 |
|
||
| MCU фазы | HW fail-safe-таймер = v0 (с MCU-вариантом); прошивка-update = v1 | 2026-06-16 |
|
||
| Watchdog | runtime (RuntimeWatchdogSec) + shutdown-фаза (Reboot/MCU дедлайн) + boot-окно; v0=SoC+MCU+reboot, bootcount=v4 | 2026-06-16 |
|
||
| Время | save периодически + on-sync + on-shutdown; lifecycle-таймеры монотонны; «только вперёд» | 2026-06-16 |
|
||
| Состояние | `Power.IgnitionState` канон; E владеет узким `engine_running` | 2026-06-16 |
|