# Домен 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) **Реализация (v0.3):** срезы **B01–B07** реализованы — чистый `PowerFsm` (§2: `off↔accessory↔running→shutting-down {abortable→committed}→off`, abort до PONR) + сервис `ru.shturman.Power` оживлён из FSM (grace-таймер + durable-barrier `sync` на commit), watchdog/save-time-конфиг. **VM-модель:** abort/PONR в Lima = stop+umount+remount, power-cut = SIGKILL+`fsck`. sleep/wake/battery-cutoff — каркас (no-op), тело → v1/v2 (§7). Спека: `docs/specs/v0.3-power-safe.md`. **Реализация (v0.4):** срезы **A12/B08/B09/B10** — софт/модель. Тепло (§4/§1a): чистая `ThermalPolicy` (банды + гистерезис) → `Event::ThermalTrip` (реюз FSM) + abort `ThermalCleared`; `TempSource`/`Throttler` абстракции (VM mock/noop, реальный sysfs/cpufreq + числовые пороги → RK3588). MCU (§5/§6): протокол `SocToMcu`/`McuToSoc` + кодек (CRC16/replay/ desync-guard) + `CoprocessorClient` (heartbeat/wait-for-completion/`safe-to-cut`); **B09 fail-safe-таймер — модель** (`MockCoprocessor`: hang/budget → `Event::FailsafeCut` → off). `ru.shturman.Power` += `ThermalState`/`ThermalChanged` (рендер «перегрев» → v0.5). **Физический выбор B08/B09** (MCU vs supercap-only), реальный UART/MCU-чип/fail-safe-таймер, supercap-only-путь → **HW-bring-up-подфаза**. Спека: `docs/specs/v0.4-mcu-thermal.md`. --- ## 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, **B08/B09**) — определяет владельца ACC/watchdog, **наличие независимого бэкстопа и fail-safe-снятия при зависшем SoC**, и доступность scheduled-wake. Протокол/кодек/клиент + fail-safe-**модель** реализованы в **v0.4** (софт); **физический выбор + железо → HW-bring-up-подфаза** (нужна плата). - ◻️ **Протокол 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):** **J — запросчик** (J §7), **B — владелец/арбитр** (§4 шаг 2 grace-окно, §7) — ограничивает hold-up-бюджетом, не переопределяет PONR. С реверс-камерой 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 |