@@ -0,0 +1,216 @@
# Домен 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).
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 |