Files
shturman/docs/domains/b-power-lifecycle.md
T
kk0t9 77d9a5a0ee docs(domain H): v2 — медиа/аудио + арбитр ducking, после adversarial-ревью (20 находок) + кросс-док
Новый домен 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>
2026-06-22 18:08:36 +03:00

218 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Домен 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.212.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 (шаги 46). Зависание в 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 |