Двунаправленные швы (спека v0.3 §10) после реализации Плана 7: - domain B: banner «Реализация (v0.3)» (B01–B07, VM-модель abort/PONR=stop+umount+remount); §12 — MCU/supercap (B08/B09) → v0.4. - ipc.md §3: Power оживлён из FSM (не mock); Sleep/Wake/RequestSleep зарезервированы. - foundation §5.2: «Power-стаб» → реальный PowerFsm (проекции state/ignition/source из FSM); dev-mock кормит входы FSM. - CLAUDE.md: статус v0.3 ГОТОВО; «Следующее» → v0.5 shell / v0.4 MCU-thermal. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Alexander <akotenev2003@gmail.com>
21 KiB
Домен B — Power / Lifecycle
Привилегированный core-сервис: владеет power-path, детектом зажигания, graceful shutdown, протоколом MCU-копилота, watchdog, sleep/wake и сохранением времени. Вторая половина «power-safe с дня 1» (с доменом A).
Статус: v2 (на ревью). v2 — после adversarial-ревью (22 находки).
Связано с: architecture.md (§6) · hardware.md (§3 питание/MCU) · a-base-system.md (§5–§11) · ipc.md (Power) · 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. Аппаратное (MCU/hold-up/heartbeat/safe-to-cut/fail-safe-таймер) и выбор B08/B09 → v0.4;
sleep/wake/battery-cutoff — каркас (no-op), тело → v1/v2 (§7). Спека: docs/specs/v0.3-power-safe.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; секвенсинг — здесь):
PowerэмититShutdownImminent(seconds, reason).- Load-shedding (политика по триггеру):
- резкий обрыв/hold-up: агрессивно сбросить всё, кроме SoC+хранилище;
- управляемый ACC-off: мягко, с коротким grace-окном на дисплей/камеру-рейл (парковка с реверсом — §7, B-COMPL-04).
- amp-mute перед снятием рейла усилителя (anti-pop): mute-GPIO усилителя (hardware §4) дёргается ДО cut — шов с медиа-выходом H §8.
- Ordered teardown: App-Host останавливает апы/плагины (
StopApp) с per-app ack/save-таймаутом (сумма ≤ hold-up-бюджет); E наShutdownImminentгасит активный OBD-TX и закрывает ISO-TP-сессии (B даёт сигнал, E владеет закрытием — B не трогает CAN). - Settings флашится в
/data+ критичные логиfsync(a-base §9) — критичный fsync завершается ДО PONR. - Сохранение last-known-time (§8).
- Размонтирование RW → point-of-no-return.
- 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-задел):
- ACC-off → MCU стартует hold-up-таймер (= hold-time hardware §3); это и есть тот же бюджет, что fail-safe-таймер §5 (один когерентный, не два).
- SoC ведёт секвенсинг (§4 шаги 2–6); heartbeat SoC→MCU во время shutdown — MCU отличает «SoC работает» (держать питание до бюджета) от «SoC завис» (cut по таймеру).
- SoC шлёт
safe-to-cutпосле PONR → MCU снимает питание немедленно. - Таймер истёк без
safe-to-cut→ MCU снимает всё равно (детерминированно; durability — до последнего fsync). - Потеря линка (обрыв шлейфа): fail-safe в ОБЕ стороны определён явно — MCU при тишине в running трактует по политике (cut/reboot vs hold — §12); SoC при тишине MCU — деградирует.
- 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её НЕ покрывает → отдельный таймаут: systemdRebootWatchdogSec(путь 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 §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. → v0.4 (вероятно нужна аппаратная проверка).
- ◻️ Протокол 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 |