Веха v0.3: стаб Power → реальный lifecycle-FSM. Состояния off/accessory/running/
shutting_down{abortable,committed} (sleep/battery_cutoff — каркас); graceful
shutdown (ShutdownImminent→grace→durable-barrier sync→commit=PONR=unmount); abort
до PONR (re-power→ShutdownAborted). dev-mock кормит входы FSM. Watchdog/save-time/
монотоника. Подход A: FSM+сигналы, teardown через systemd/харнесс. Гибрид-E2E:
N=3 in-VM цикла + 1 reboot + abort + power-cut-сим. HW (hold-up/MCU/B08-B09) — v0.4.
Красные линии: Power не трогает CAN, без actuator (#2).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Alexander <akotenev2003@gmail.com>
19 KiB
Спека реализации: v0.3 — Power-safe ядро (FSM + graceful shutdown)
Веха
v0.3роадмапа: «переживает срыв питания». Capabilities B01 (детект ACC), B02 (graceful shutdown sequencing), B03 (FSM + abort/committed), B04 (ru.shturman.Power), B05 (watchdog), B06 (load-shedding), B07 (save last-known-time), A14 (HW-watchdog + recovery). Поверх v0.2. Источники:docs/domains/b-power-lifecycle.md,docs/contracts/safety.md,docs/contracts/hardware.md,docs/contracts/ipc.md§3,docs/roadmap.md§v0.3. Приёмка роадмапа: «N циклов зажигания без потери/data; abort до PONR».
1. Цель и первый артефакт
Оживить стаб Power (v0.1: плоский State, mock флипает ignition/power) в реальный lifecycle-FSM:
ACC → graceful shutdown с durable-write до PONR → переживание срыва питания.
Первый артефакт: fake-ACC-off → FSM running→shutting_down → ShutdownImminent → grace → commit
(durable-write barrier sync → unmount /data = PONR); N=3 цикла зажигания — /data цел; abort до PONR
(re-power → ShutdownAborted → running); power-cut-сим (SIGKILL до fsync → /data консистентен).
Не цель v0.3: реальный hold-up cap / MCU-протокол / fail-safe-таймер и выбор B08/B09 (MCU vs supercap) —
v0.4 (вероятно нужна аппаратная проверка); реальный /dev/watchdog арминг — HW; полные sleep/wake/long-park —
v1/v2; перф-вердикт — RK3588.
2. Скоуп и границы
2.1 В скоупе (делаем сейчас)
- FSM питания (B03): чистый модуль
PowerFsm— состояния, события, переходы, действия. Юнит-тестируемый без D-Bus. Внутренние субсостоянияShuttingDown{Abortable, Committed}маппятся в D-Busshutting_down. - Детект ACC-логика (B01): debounce/гистерезис + crank-приоритет — формализованы в FSM (вход
AccOffпринимается только как стабильный; в VM источник — fake-ACC dev-mock). Реальный GPIO/MCU-детект — HW. - Graceful shutdown sequencing core (B02):
ShutdownImminent(sec, reason)→ grace-окно → durable-write barrier (sync(2); Settings уже синхронен) → commit → unmount (PONR). Ordered teardown апов/CAN — позже (§2.2). - Abort до PONR (B03): re-power в abortable →
ShutdownAborted→ назад вrunning/accessory. - Load-shedding (B06): хук на commit/power-loss — в v0 лог (реальных нагрузок нет; рейлы amp/подсветка/ модем — HW).
- Watchdog (B05/A14): systemd
RuntimeWatchdogSec(runtime) +RebootWatchdogSec(shutdown-фаза) — **конфиг- дисциплина**. Реальный
/dev/watchdog/MCU-арминг — HW.
- дисциплина**. Реальный
- Save last-known-time (B07):
shturman-savetime.timer+.service— periodic fake-hwclock save (/data) + on-shutdown save (в graceful sequence до PONR).fake-hwclock→/data— уже из v0.6. - Монотоника (§8):
Uptime+ grace-timer + все lifecycle-таймеры наCLOCK_MONOTONIC(shturman_common::monotonic_secsуже есть). НЕ wall-clock. - Харнесс: FSM-юниты (каждый переход) + integration (сигналы по session-шине) + E2E гибрид (§9).
2.2 Явно НЕ в скоупе (отложено, с указателем «куда»)
- Hold-up cap / supercap, MCU-копилот, MCU fail-safe-таймер, SoC↔MCU heartbeat/
safe-to-cut, реальный power-cut energy budget + дератинг по T — HW (hardware §3); выбор B08/B09 (MCU vs supercap-only) — v0.4. - Реальный
/dev/watchdogарминг + bootcount-handshake recovery — HW/v4 (в VM watchdog-device нет). - Полные sleep/wake + long-park battery-cutoff (низкопотребление, wake-on-ACC/таймер/реверс) — v1/v2 (B §7).
В v0.3 состояния
sleep/battery_cutoff— зарезервированы (переходы заглушены). - Consumer-ack save-протокол (
ShutdownImminent→consumers save→ack/timeout, сумма ≤ hold-up-бюджет) — App-Host v3 (в v0 Settings durable-write синхронен → ack не нужен). - E гасит OBD-TX / закрывает ISO-TP при shutdown — домен E / v1 (Power не трогает CAN, §3).
- Перф-вердикт (time-to-shutdown, hold-time) — RK3588 (performance §2). В VM — функционально.
2.3 Частично в скоупе (каркас сейчас, тело — позже)
sleep/battery_cutoff— состояния в enum/FSM есть, переходы no-op/заглушка (тело — v1/v2).- Load-shedding — лог-хук (реальные рейлы — HW).
- Watchdog — конфиг systemd (реальный арминг — HW).
- ACC-детект — debounce-логика в FSM (источник в VM — fake-ACC; реальный GPIO/MCU — HW).
2.4 Трассируемость ID → статус
| ID | Веха | Статус в v0.3 |
|---|---|---|
| B01 | Детект ACC (debounce + crank) | логика в FSM; источник VM = fake-ACC (реальный GPIO/MCU — HW) |
| B02 | Graceful shutdown sequencing | core: ShutdownImminent→grace→durable-barrier→commit (teardown апов/CAN — позже) |
| B03 | FSM + abort/committed | полностью (sleep/battery_cutoff — каркас) |
| B04 | ru.shturman.Power |
оживлён из FSM (сигналы/состояние — реальные переходы) |
| B05 | Watchdog | конфиг RuntimeWatchdogSec/RebootWatchdogSec + дисциплина (реальный WDT — HW) |
| B06 | Load-shedding | лог-хук (реальные нагрузки — HW) |
| B07 | Save last-known-time | periodic timer + on-shutdown save в /data |
| A14 | HW-watchdog + recovery | конфиг + дисциплина (реальный арминг/recovery — HW/v4) |
3. Красные линии, безопасность
- #2 (нерушимо): Power не трогает CAN и не имеет actuator-путей — только software-оркестрация lifecycle + read-only состояние. CAN-TX-гашение при shutdown — домен E (v1), не Power. Граница — safety.md.
- #5 (power-safe): durable-write до PONR — главная гарантия; FSM коммитит только после grace +
sync. Реальный power-cut на HW; в VM — функциональная модель + атомарность файла (foundation §9.1) уже доказана. - Прод-гейт: dev-mock
PowerMock1(fake-ACC/voltage/thermal) — за фичейdev-mocks; прод--no-default-features→ не регистрируется (foundation §5.2, §8.3).
4. Раскладка (новые/изменённые артефакты)
crates/core/shturman-power/src/fsm.rs(новый) —PowerFsm:State,Event,Action,fn step(&mut self, Event) -> Vec<Action>. Чистый, без D-Bus/async. Grace-таймер — снаружи (сервис), FSM лишь даётStartGrace(sec)/ принимаетGraceExpired.crates/core/shturman-power/src/service.rs— обернуть FSM: D-Bus state/properties/signals из FSM; dev-mock методы кормят FSM-события (не флипаютStateнапрямую).crates/core/shturman-power/src/main.rs— grace-таймер (монотоника, tokio), durable-write barrier (sync), трансляция FSM-actions → D-Bus-сигналы.- systemd:
shturman-power.servicedrop-inRuntimeWatchdogSec=(дисциплина);shturman-savetime.service+.timer(B07 periodic save); systemRebootWatchdogSec=(shutdown-дедлайн). Раскладка — lima/E2E. - harness:
tests/e2e/run.sh— блок power-safe (гибрид §9); integration-тесты вcrates/core/shturman-power/tests/.
5. FSM питания (B03) — контракт
Состояния (внутренние): Off, Accessory, Running, ShuttingDown { phase: Abortable | Committed, reason },
Sleep, BatteryCutoff (* — зарезервированы). D-Bus-маппинг (PowerState): ShuttingDown{*} → shutting_down;
остальные 1:1.
События (Event): AccOn, AccOff, EngineOn, EngineOff (accessory↔running по напряжению; VM — mock),
UnderVoltage, ThermalTrip, GraceExpired. (Re-power = AccOn во время shutdown.)
Переходы:
| Из | Событие | В | Действия |
|---|---|---|---|
Off |
AccOn |
Accessory |
EmitAccChanged(true) |
Accessory |
EngineOn |
Running |
— |
Running |
EngineOff |
Accessory |
— |
Accessory/Running |
AccOff |
ShuttingDown{Abortable, acc_off} |
EmitShutdownImminent(sec, acc_off), StartGrace(sec) |
Accessory/Running |
UnderVoltage |
ShuttingDown{Abortable, under_voltage} |
EmitShutdownImminent(sec, under_voltage), StartGrace(sec) |
Accessory/Running |
ThermalTrip |
ShuttingDown{Abortable, thermal} |
EmitShutdownImminent(sec, thermal), StartGrace(sec) |
ShuttingDown{Abortable} |
AccOn (re-power) |
Running |
EmitShutdownAborted, EmitAccChanged(true) |
ShuttingDown{Abortable} |
GraceExpired |
ShuttingDown{Committed} |
Commit (durable-barrier → PONR) |
ShuttingDown{Committed} |
— | Off |
(cut: unmount + снятие питания — systemd/харнесс/HW) |
Sleep/BatteryCutoff |
* | (no-op) | зарезервировано (v1/v2) |
Действия (Action): EmitShutdownImminent(reason), EmitShutdownAborted, EmitAccChanged(bool),
StartGrace(secs), Commit. reason ∈ {acc_off, under_voltage, thermal, battery_cutoff}.
Инвариант: после Committed abort невозможен (только → Off).
Чистота: step детерминирован, без I/O; сервис исполняет действия (сигналы/таймер/sync). Юнит-тест — каждый переход.
6. Graceful shutdown (B02/B06) — последовательность (подход A)
Power-сервис = FSM + сигналы + grace/abort-окно; реальный teardown (unmount/cut) — через systemd (реальный poweroff) либо харнесс (in-VM-цикл). На железе — MCU/supercap-sequencing (v0.4).
AccOff(стабильный; VM — fake-ACC) →ShuttingDown{Abortable}→ShutdownImminent(sec, acc_off)+ grace-таймер (монотоника). Потребители (приборка/будущие апы) получают сигнал и сохраняются.- Abort-окно (abortable):
AccOnдоGraceExpired→EmitShutdownAborted→Running; откат load-shed (v0: лог). GraceExpired→ Commit: save last-known-time (B07) → durable-write barrier (sync(2); Settings уже синхронен по каждому Set) →Committed(= PONR).- PONR = unmount
/data(RW→RO): на реальном poweroff — systemd; в in-VM-цикле — харнесс; на HW — MCU/supercap дают энергию завершить unmount/sync до cut. - Load-shedding (B06): на commit/power-loss — лог
«load-shed: amp/backlight/modem (реальных нагрузок нет в v0)»; hold-up кормит SoC+хранилище — HW.
Гарантия #5: commit (и потому unmount/PONR) наступает только после grace + sync → усечённый shutdown
оставляет /data консистентным (атомарность файлов — foundation §9.1).
7. D-Bus ru.shturman.Power — v0.3 оживляет (расширение foundation §5.2)
- Состояние/properties из FSM (не из плоского
State):GetPowerState,IgnitionState,PowerSource,Uptime. - Сигналы из FSM-actions (не из mock-флипа):
AccChanged,ShutdownImminent(sec, reason),ShutdownAborted.Sleep/Wake— объявлены, не эмитятся (sleep — v1/v2). PowerSource:vehicle_12v(норма) → на under-voltage/commit сигналимholdup_cap/low_battery(потребителям «времени мало»).sleep_rail— v1/v2.- dev-mock
ru.shturman.dev.PowerMock1(fake-ACC, фичаdev-mocks) — кормит входы FSM:SetAcc(on)→AccOn/AccOff;SetIgnition(state)→EngineOn/EngineOff(accessory↔running) либоAccOn/AccOff;TriggerShutdown(sec, reason)→UnderVoltage/ThermalTripс заданным grace;AbortShutdown()→ re-power (AccOn) в abortable. Прод-сборка mock не регистрирует (#3-гейт §3). Policysend_destination=ru.shturman.Powerпокрывает (foundation §13).
8. Watchdog / монотоника / save-time
- Watchdog (B05/A14): drop-in
shturman-power.service.d/watchdog.conf—RuntimeWatchdogSec=(дисциплина: один userspace-владелец WDT). Systemsystemd/system.conf.dRebootWatchdogSec=— дедлайн shutdown-фазы (зависание в unmount/sync не оставит устройство под питанием). В VM/dev/watchdogнет → конфиг присутствует, реальный арминг — HW (VM↔HW-граница, как zram/vcan в v0.6). MCU-backstop — v0.4. - Монотоника (§8):
Uptime+ grace-таймер + sleep/wake-таймеры наCLOCK_MONOTONIC(monotonic_secs). Wall-clock легитимно прыгает на NTP/GPS-синке — lifecycle на него не завязан. - Save last-known-time (B07):
shturman-savetime.service(fake-hwclock saveсFILE=/data/..., как в v0.6) +.timer(~1–5 мин, monotonic). On-shutdown save — шаг 3 §6. После срыва часы откатываются максимум на интервал.
9. Dev-харнесс и план тестирования
9.1 Unit (FSM — fsm.rs)
Каждый переход §5: Off→Accessory→Running; Running→ShuttingDown{abortable,reason} для каждого reason; abort
(Abortable+AccOn→Running+ShutdownAborted); GraceExpired→Committed+Commit; Committed — abort игнорируется;
Sleep/BatteryCutoff — no-op. Действия проверяются по возвращаемому Vec<Action>.
9.2 Integration (session-шина, #[ignore], just test-integration)
SetAcc(false) → наблюдаем ShutdownImminent; AbortShutdown (в abortable) → ShutdownAborted; SetIgnition
→ IgnitionState property; GetPowerState отражает FSM.
9.3 E2E (Lima, гибрид — расширение run.sh)
- N=3 in-VM цикла зажигания: записать
/data-маркер (Settingsui.theme=night+ счётчик/data/state/power-cycles); цикл: fake-ACC-off → наблюдатьShutdownImminent→ харнесс: stopshturman-stage1.target+sync+umount /data→mount /data→ restart → маркер цел, счётчик++. После 3 циклов: night + счётчик=3. - 1 реальный reboot-цикл: fake-ACC-off → commit →
systemctl poweroff→limactl start→ boot →/dataцел. - Abort до PONR: fake-ACC-off →
ShutdownImminent→ fake-ACC-on до unmount →ShutdownAbortedнаблюдаем →/dataRW (смонтирован) →GetPowerState=running. - Power-cut-сим: во время shutdown (до fsync)
SIGKILLpower+settings →/data: remount ok,fsck -nclean, последнее durable-значение присутствует (атомарность §9.1 на уровне файла). - Монотоника:
Uptimeрастёт; не прыгает при wall-clock-синке. - Watchdog/save-time: drop-in
RuntimeWatchdogSecу power.service присутствует;shturman-savetime.timerактивен;/data/state/fake-hwclock.dataобновляется.
9.4 Критерии приёмки
- FSM: все переходы §5 покрыты unit-тестами;
sleep/battery_cutoff— no-op/документированы. ShutdownImminentна ACC-off; abort до PONR →ShutdownAborted; commit только после grace + durable-barrier.- N=3 цикла зажигания —
/data+ счётчик целы (нет потери). - 1 реальный reboot-цикл —
/dataцел. - power-cut-сим —
/dataконсистентен (fsck -nclean, last value present). Uptimeмонотонен; lifecycle-таймеры наCLOCK_MONOTONIC.- watchdog-конфиг (
RuntimeWatchdogSec/RebootWatchdogSec) на месте;savetime.timerактивен. - Регресс v0.1/v0.2 (foundation §9.4 + v0.2 §9.3) зелёный (фазы/кадр/персист не сломаны).
just ciзелёный; красные линии целы (нет CAN/actuator); prod-build-gate (--no-default-features→ нетPowerMock1) зелёный.
10. Двунаправленные швы (синхронизировать при реализации)
domain B: пометить реализованные срезы B01–B07 (v0.3, VM-модель); abort/PONR в VM = stop+umount+remount; HW (MCU/hold-up/heartbeat/safe-to-cut/fail-safe-таймер) + выбор B08/B09 → v0.4.ipc.md§3: Power-сигналы/состояние оживлены из FSM (не mock);Sleep/Wakeзарезервированы.foundation §5.2: «Power-стаб» → реальный FSM (обновить формулировку «стартует в running, без логики»).hardware §3/B §5: B08/B09 (MCU vs supercap-only) остаётся открытым 🟡 → v0.4.CLAUDE.md: статус v0.3 готово → следующее v0.4/v0.5.
11. Дальше по ритму
v0.3 (эта спека) → writing-plans → TDD (FSM-юниты → сервис-обёртка → durable-barrier/grace → systemd/save-time
→ E2E-блок) → реализация → verify в Lima → коммит. Далее: v0.4 (MCU/thermal — замыкает B08/B09) после v0.3;
v0.5 (полный shell) параллельно.