Веха v0.4 (A12/B08/B09/B10) поверх живого FSM v0.3. Решение брейнсторма: разработка без платы (принцип #13) — MCU-копилот как reference-арх, софт+симуляция, физический выбор B08/B09 + железо отложены в HW-bring-up-подфазу. Симметрия с v0.3: чистое ядро (ThermalPolicy/ codec) → абстракция (TempSource/Throttler/Coprocessor trait) → dev-mock. Скоуп: тепловая политика+гистерезис → Event::ThermalTrip (реюз FSM); SoC↔MCU протокол + кодек (CRC/replay/desync-guard) + CoprocessorClient (heartbeat/wait-for-completion/safe-to-cut); B09 fail-safe-таймер — модель (hang/budget → Event::FailsafeCut → off); D-Bus ThermalState/ ThermalChanged (контракт сейчас, рендер v0.5). Приёмка §9.4; швы §10. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Alexander <akotenev2003@gmail.com>
24 KiB
Спека реализации: v0.4 — MCU/thermal fail-safe (тепловой триггер + MCU-протокол + fail-safe-таймер)
Веха
v0.4роадмапы: «аппаратный фундамент питания/тепла». Capabilities A12 (тепловой мониторинг + базовый throttling), B08 (MCU-копилот shutdown-протокол), B09 (MCU аппаратный fail-safe-таймер), B10 (thermal shutdown: триггер + hysteresis + UX). Поверх v0.3 (живойPowerFsm+ graceful shutdown). Источники:docs/domains/b-power-lifecycle.md§4/§5/§6/§1a,docs/contracts/hardware.md§3/§1a,docs/contracts/ipc.md§3,docs/contracts/safety.md,docs/roadmap.md§v0.4. Приёмка роадмапы: «thermal-trip → graceful; MCU-таймер режет питание, если SoC завис».Решение скоупа (брейнсторм): разрабатываем без платы (принцип #13). MCU-копилот принят как reference-архитектура (доки рек.); делаем софт + симуляцию, физический выбор B08/B09 (MCU vs supercap-only) и реальное железо — отложены в HW-bring-up-подфазу. Симметрия с v0.3: чистое ядро (политика/кодек) → абстракция (trait) → dev-mock.
1. Цель и первый артефакт
Замкнуть тепловой и MCU-швы домена B поверх живого FSM v0.3: (а) тепловой монитор с гистерезисом
кормит существующий Event::ThermalTrip → graceful shutdown; (б) SoC↔MCU shutdown-протокол (heartbeat /
safe-to-cut / wait-for-completion) с защищённым кодеком; (в) модель независимого fail-safe-таймера —
MCU детерминированно «режет питание», если SoC завис.
Первый артефакт (в Lima, мок-MCU/sensor):
- thermal-trip:
SetTemp ≥ critical→ThermalTrip→ShutdownImminent(thermal)→ graceful (реюз v0.3-пути,/dataконсистентен); восстановление по гистерезису. - MCU fail-safe:
HangSoc(heartbeat пропал вrunning) → мок-MCU детерминированно снимает питание (в VM = форс-offсервиса) — «MCU режет питание, если SoC завис»;/dataконсистентен. - throttling:
SetTempв warn/throttle-банде → throttle-действие записано (VMNoop), без shutdown; гистерезис на спаде (нет осцилляции).
Не цель v0.4: реальный UART/I2C-драйвер, реальный cpufreq-эффект, прошивка MCU, физический B09-чип/supercap, полный supercap-only-путь (остаётся абстракцией-fallback), thermal-рендер в Shell (v0.5), sleep/wake/ battery-cutoff (v1/v2), числовой тюнинг порогов/hold-up — RK3588. Перф/тепловой вердикт — на таргете.
2. Скоуп и границы
2.1 В скоупе (делаем сейчас)
- Тепловая политика (A12/B10): чистый
ThermalPolicy—temp + предыдущий уровень → ThermalLevel ∈ {Normal, Warn, Throttle(n), Critical}с гистерезисом (раздельные пороги вверх/вниз — нет осцилляции). Юнит-тестируемый без I/O. Пороги — placeholder-константы (// тюнинг на RK3588, hardware §1a). - Источник температуры (
TempSourcetrait): real = sysfs/sys/class/thermal/thermal_zone*/temp(max по зонам); VM =MockTempSource(значение из dev-D-BusSetTemp). В v0.4 активен mock. - Throttler (
Throttlertrait): real = cpufreq-cap (best-effort, HW); VM =NoopThrottler(запись уровня для E2E/лог). Эффект — абстракция; реальное снижение частоты — HW. - Thermal-монитор: периодический poll на монотонике → политика → throttle + на
CriticalкормитEvent::ThermalTripв FSM (тот жеapply_eventиз v0.3). Восстановление до PONR →Event::ThermalCleared(abort thermal-shutdown, симметрия с re-power; гейт поreason == Thermal). - SoC↔MCU протокол (B08): типы сообщений
SocToMcu/McuToSoc; чистый кодек (framing + seq + CRC16 + replay/desync-guard) — закрывает требование «защита линка от replay/мусора/десинка» (B §5, hardware §4). Байты текут через in-memory-канал (codec исполняется по-настоящему и в integration). - Coprocessor (
Coprocessortrait): real =SerialCoprocessor(UART — стаб, HW-подфаза); VM =MockCoprocessor(in-process, кормится через dev-D-Bus). - SoC-side
CoprocessorClient: heartbeat вrunning; наShutdownImminent→ wait-for-completion (расширенный таймаут ≥ shutdown-бюджет, B §6) — не короткий keepalive посреди unmount;safe-to-cutпосле PONR → немедленный cut. MCU — fail-safe-авторитет (SoC не командует cut-на-ходу — B §5). - Fail-safe-таймер (B09) — модель:
MockCoprocessorмоделирует независимый таймер: heartbeat пропал (running) ИЛИ бюджет истёк безsafe-to-cut→ детерминированный cut (в VM = форс-offpower-сервиса). - D-Bus-контракт (ipc §3): property
ThermalState ∈ {normal, warn, throttle, critical}+ сигналThermalChanged(state, celsius);ShutdownImminent(thermal)уже есть. Контракт сейчас — Shell рисует в v0.5. - Харнесс: юниты (политика/кодек/клиент/таймер) + integration (session-шина) + E2E-блок v0.4 (§9).
2.2 Явно НЕ в скоупе (отложено, с указателем «куда»)
- Физический выбор B08/B09 (MCU-копилот vs supercap-only), реальный MCU-чип, прошивка, реальный hold-up cap/supercap, реальный UART/I2C-драйвер + GPIO ACC-детект — HW-bring-up-подфаза (hardware §3).
- Реальный cpufreq/DDR/GPU-throttling-эффект + числовые тепловые пороги/дератинг — RK3588 (hardware §1a).
- Thermal-UX рендер в Shell («перегрев»-overlay) — v0.5 (живой shell; v0.4 даёт только контракт).
- Supercap-only полный путь (SoC-таймер + разряд cap, ACC-детект в софте) — остаётся абстракцией-fallback
(
Coprocessortrait), тело — HW-подфаза при выборе supercap. - Sleep/wake/scheduled-wake/battery-cutoff — v1/v2 (B §7); состояния зарезервированы (как в v0.3).
- Реальный
/dev/watchdogарминг + MCU-watchdog-бэкстоп железом — HW (в v0.4 — дисциплина/модель, как v0.3). - Перф-вердикт (время до cut, hold-time, тепловая инерция) — RK3588 (performance §2). В VM — функционально.
2.3 Частично в скоупе (каркас сейчас, тело — позже)
Throttler— trait +Noop(лог уровня); реальный cpufreq — HW.Coprocessor— trait +MockCoprocessor;SerialCoprocessor(UART) — стаб, HW-подфаза.TempSource— trait +MockTempSource;SysfsTempSource— каркас (читает зоны, в Lima зоны статичны).- MCU-watchdog/линк-fail-safe — логика клиента + модель таймера; реальный независимый чип — HW.
2.4 Трассируемость ID → статус
| ID | Веха | Статус в v0.4 |
|---|---|---|
| A12 | Тепловой мониторинг + базовый throttling | ThermalPolicy + TempSource/Throttler (VM mock/noop); пороги placeholder, эффект cpufreq — HW |
| B08 | MCU-копилот shutdown-протокол | типы + кодек (CRC/replay/desync) + CoprocessorClient (heartbeat/wait/safe-to-cut); транспорт = in-memory (UART — HW) |
| B09 | MCU аппаратный fail-safe-таймер | модель в MockCoprocessor (hang/budget → детерминированный cut); реальный независимый чип — HW |
| B10 | Thermal shutdown (триггер + hysteresis + UX) | триггер ThermalTrip (реюз FSM v0.3) + гистерезис + abort ThermalCleared; UX-рендер — v0.5 (контракт-property/сигнал сейчас) |
3. Красные линии, безопасность
- MCU/Power — только питание устройства, не CAN/actuator (#1/#2): копроцессор мониторит зажигание/напряжение и коммутирует рейл питания SoC — никаких узлов авто, никаких write/actuator-путей. Протокол SoC↔MCU не несёт автомобильных команд. (Engine-state/OBD — домен E, read-only; Power не трогает CAN.)
- MCU — fail-safe-авторитет (B §5): ни одно SoC-сообщение не может (а) снять питание на ходу, (б) держать
вечно и разрядить АКБ. В модели
MockCoprocessorcut инициируется только таймером MCU (hang/budget) илиsafe-to-cutпосле PONR — не произвольной SoC-командой. - Защита линка: кодек отбрасывает replay (seq ≤ last)/мусор (битый CRC)/десинк (resync по SYNC) — юнит-доказано (§9.1). Аналог защиты CAN-линка (hardware §4).
- Durability-инвариант v0.3 сохраняется: thermal-trip и MCU-cut идут через тот же graceful-путь до PONR
(durable-barrier
syncдо unmount); после усечённого shutdown/dataконсистентен (atomic-write A §3). - prod-build-gate:
--no-default-features(безdev-mocks) →PowerMock1/SetTemp/HangSocне регистрируются (как в v0.3). dev-D-Bus-policy — dev-only drop-in.
4. Раскладка (новые/изменённые артефакты)
- Create
crates/core/shturman-power/src/thermal.rs—ThermalLevel,ThermalPolicy(чистая, гистерезис),TempSourcetrait (SysfsTempSource/MockTempSource),Throttlertrait (Cpufreq-стаб/NoopThrottler),ThermalMonitor(poll → политика → throttle +ThermalTrip/ThermalCleared). - Create
crates/core/shturman-power/src/protocol.rs—SocToMcu/McuToSoc(типы сообщений). - Create
crates/core/shturman-power/src/codec.rs— кадр (SYNC/LEN/SEQ/TYPE/PAYLOAD/CRC16) + encode/decode + replay/desync-guard. Юнит-тесты в файле. - Create
crates/core/shturman-power/src/coprocessor.rs—Coprocessortrait,MockCoprocessor(in-process + B09-таймер-модель),SerialCoprocessor(UART-стаб),CoprocessorClient(SoC-side: heartbeat/wait/safe-to-cut). - Modify
crates/core/shturman-power/src/fsm.rs—Event::ThermalCleared(+ переход abort изShuttingDown{Abortable, reason: Thermal}→Running);Event::FailsafeCut(→offиз любого не-off, необратимо — MCU-авторитет); подтвердить армыThermalTrip. - Modify
crates/core/shturman-power/src/service.rs— владетьThermalMonitor+CoprocessorClient(кормят FSM); propertyThermalState+ сигналThermalChanged; dev-mock расширить:SetTemp(d),HangSoc(),McuLinkLoss(). - Modify
crates/core/shturman-power/src/lib.rs—pub mod thermal; pub mod protocol; pub mod codec; pub mod coprocessor;. - Modify
crates/shturman-ipc/src/proxy.rs—Power1Proxy: propertyThermalState+ сигналThermalChanged. - Modify
crates/core/shturman-power/tests/integration.rs— thermal-trip / abort / fail-safe-cut по session-шине. - Modify
tests/e2e/run.sh— блок v0.4 (thermal-trip → graceful; MCU fail-safe; throttling/гистерезис). - Modify (швы §10)
docs/domains/b-power-lifecycle.md,docs/contracts/hardware.md,docs/contracts/ipc.md§3,docs/capability-catalog.md(A12/B08/B09/B10),CLAUDE.md.
5. Тепловая подсистема (A12/B10) — контракт
ThermalLevel = Normal | Warn | Throttle(level: u8) | Critical
Пороги (placeholder-константы, тюнинг на RK3588 — hardware §1a; Tjmax RK3588 ~100 °C):
| Переход | Порог вверх | Порог вниз (гистерезис) |
|---|---|---|
| Normal → Warn | WARN_C = 75 |
WARN_C − HYST |
| Warn → Throttle | THROTTLE_C = 85 |
THROTTLE_C − HYST |
| Throttle → Critical | CRITICAL_C = 95 |
CRITICAL_C − HYST |
HYST = 5 °C |
ThermalPolicy::next(prev: ThermalLevel, temp_c) -> ThermalLevel— чистая; гистерезис = переход вниз только ниже(порог − HYST), иначе уровень держится (нет осцилляции на границе).ThermalMonitor(tokio-интервал на монотонике, ~POLL_SECS = 1):temp = TempSource::read()→lvl = policy.next(prev, temp); при смене уровня:Throttler::apply(lvl)+ThermalChanged(state, temp); на входе вCritical→apply_event(Event::ThermalTrip); на выходе изCritical(по гистерезису), если FSM ещё вShuttingDown{Abortable, reason: Thermal}→apply_event(Event::ThermalCleared).ThermalState(D-Bus property) = проекция текущегоThermalLevel(Throttle(_)→"throttle").
6. SoC↔MCU протокол (B08) + fail-safe-таймер (B09) — контракт
6.1 Сообщения
SocToMcu = Heartbeat { seq } // периодический keepalive в running/accessory
| ShutdownImminent { budget } // вход в shutdown → MCU в wait-for-completion (таймаут ≥ budget)
| SafeToCut // после PONR → MCU снимает питание немедленно
McuToSoc = Ack { seq }
| Acc { on } // дебаунснутый ACC (источник зажигания; в VM кормит FSM AccOn/AccOff)
| Voltage { mv } // напряжение бортсети (под under-voltage backstop)
| CutWarning // бюджет почти истёк (диагностика)
6.2 Кодек (codec.rs)
- Кадр:
[SYNC=0xA5][LEN u8][SEQ u8][TYPE u8][PAYLOAD…][CRC16-CCITT], CRC поLEN..=PAYLOAD. - Replay/dup guard: приёмник держит
last_seqна направление; кадр сseq ≤ last_seq(в окне) — drop. - Desync/мусор: битый CRC или нет SYNC → resync (скан до следующего
SYNC), кадр отброшен. - Юнит-тесты: round-trip всех типов; corruption (флип бита → drop); replay (повтор seq → drop); desync (мусор перед SYNC → восстановление на следующем валидном кадре).
6.3 SoC-side CoprocessorClient
- В
running/accessory:Heartbeat{seq++}каждыеHEARTBEAT_SECS(монотоника); ждётAcc/Voltageот MCU → кормит FSM (AccOn/AccOff/UnderVoltage). - На
ShutdownImminent(FSM вошёл в shutdown): шлётSocToMcu::ShutdownImminent{budget}→ переходит в wait-for-completion (heartbeat останавливается, ждёт завершения секвенсинга; таймаут ≥ shutdown-бюджет, B §6). - После PONR (commit):
SafeToCut→ MCU режет немедленно. - Не шлёт power-команд с эффектом cut-на-ходу (red-line §3).
6.4 Fail-safe-таймер (B09) — модель в MockCoprocessor
- Hang-детект: нет
HeartbeatдольшеFAILSAFE_MISS × HEARTBEAT_SECSвrunning→ SoC завис → cut. - Budget-таймер: после
ShutdownImminentбезSafeToCutзаHOLDUP_BUDGET_SECS→ cut (детерминированно). - Cut (в VM): мок-MCU зовёт
apply_event(Event::FailsafeCut)→ FSM →off(необратимо, MCU-авторитет) + лог «MCU cut». В E2E дополнительно реюзаем v0.3 power-cut (SIGKILL до fsync) для durability-доказательства (fsckclean, durable-value цел). Это VM-модель: реальный зависший SoC теряет питание извне, в модели cut = событие не-реально-зависшего процесса (симметрично v0.3 «abort/PONR = stop+umount+remount»). - Значения (
HEARTBEAT_SECS=1,FAILSAFE_MISS=3,HOLDUP_BUDGET_SECS~ grace+запас) — placeholder (реальный hold-up sizing — hardware §3, RK3588).
7. D-Bus ru.shturman.Power — v0.4 расширяет
- + Property
ThermalState: s∈{normal, warn, throttle, critical}(+PropertiesChanged). - + Сигнал
ThermalChanged(s state, i celsius)— для Shell (рендер «перегрев» — v0.5). ShutdownImminent(u seconds, s reason)—reason=thermalуже объявлен (ipc §3); v0.4 его реально эмитит на thermal-trip.- dev-mock
ru.shturman.dev.PowerMock1(featuredev-mocks) дополняется:SetTemp(i celsius)→ кормитMockTempSource→ монитор → политика.HangSoc()→ останавливает heartbeat → провоцирует B09-таймер.McuLinkLoss()→ тишина линка: SoC-сторона деградирует (лог/маркер degraded, не self-cut — red-line §3). MCU-сторонняя политика cut-vs-hold при тишине — B §12-open → HW.- Прод-гейт
--no-default-features— не регистрируются (как v0.3 §3).
8. Watchdog / монотоника (реюз v0.3)
- Все новые таймеры (poll, heartbeat, wait-for-completion, fail-safe, hold-up) — на
CLOCK_MONOTONIC(shturman_common::monotonic_secs), НЕ wall-clock (B §8). - MCU-watchdog/линк-fail-safe — логика клиента + модель (реальный независимый чип/арминг — HW). systemd
RuntimeWatchdogSec/RebootWatchdogSec— уже из v0.3 (новых юнитов не требуется; thermal/coprocessor живут внутриshturman-power.service).
9. Dev-харнесс и план тестирования
9.1 Unit (чистые модули)
thermal.rs: банды Normal/Warn/Throttle/Critical; гистерезис (нет осцилляции на границе ±HYST);ThermalTripна входе в Critical,ThermalClearedна выходе.codec.rs: round-trip всех типов; corruption→drop; replay(seq)→drop; desync→resync.coprocessor.rs: клиент heartbeat→wait-for-completion→safe-to-cut; B09-таймер (hang→cut, budget→cut); MCU игнорит небезопасные SoC-команды (red-line).fsm.rs:ThermalClearedabort только изShuttingDown{Abortable, reason: Thermal}(изAccOff-shutdown — no-op); committed — no-op.FailsafeCut→offиз любого не-off(необратимо).
9.2 Integration (session-шина, #[ignore], just test-integration)
SetTemp ≥ critical→ наблюдаемShutdownImminent(thermal)+ThermalChanged(critical); stateshutting_down.SetTemprecovery до grace →ShutdownAborted(черезThermalCleared) +running.HangSoc→ наблюдаем fail-safe-cut (state → off / forced-off).
9.3 E2E (Lima, гибрид — расширение run.sh, после блока power-safe v0.3)
- thermal-trip:
SetTemp ≥ critical→ShutdownImminent(thermal)→ graceful (реюз v0.3: stop→umount(PONR)→ remount) →/dataконсистентен; затемSetTempнорма →running. - MCU fail-safe:
HangSoc(heartbeat пропал) → мок-MCU режет питание (наблюдаем forced-off / SIGKILL-эквивалент) →fsckclean, durable-value цел (как v0.3 power-cut). - throttling/гистерезис:
SetTempв warn/throttle →ThermalStateменяется, throttle записан, без shutdown; спад чуть выше нижнего порога — уровень держится (нет осцилляции). - Регресс v0.1/v0.2/v0.3 зелёный; machine-id стабилен;
E2E OK ✅.
9.4 Критерии приёмки (роадмапа + спека)
- thermal-trip → graceful (
ShutdownImminent(thermal)→commit→/dataконсистентен); гистерезис — нет осцилляции. - MCU fail-safe-таймер: SoC-hang/бюджет → детерминированный cut (модель);
/dataконсистентен. - Throttling-политика по бандам применена (запись в VM; числа — RK3588).
- Кодек: replay/desync/corruption отбиты (unit).
ThermalState/ThermalChangedна шине;Uptime/таймеры монотонны.- Регресс v0.1–v0.3 зелёный;
just ciзелёный; prod-build-gate (нетPowerMock1/SetTemp); красные линии целы (MCU/Power — только питание, нет CAN/actuator).
10. Двунаправленные швы (синхронизировать при реализации)
domain B: §4 (thermal-trip-путь реализован), §5 (MCU shutdown-протокол + кодек + клиент — софт/модель; физический MCU/fail-safe-чип/supercap → HW-подфаза), §6 (wait-for-completion реализован), §1a (тепловые пороги — placeholder, тюнинг RK3588). Пометить A12/B08/B09/B10 реализованными (VM-модель).hardware §3/§1a: B08/B09 физический выбор (MCU vs supercap-only) остаётся 🟡 → HW-bring-up-подфаза; тепловой конверт/класс/охлаждение — 🟡 (числа на таргете).ipc.md §3: Power +ThermalState/ThermalChanged;ShutdownImminent(thermal)реально эмитится.capability-catalog: A12 ✅ (политика+абстракция), B10 ✅ (триггер+гистерезис; UX→v0.5), B08/B09 — софт/модель реализованы, физический выбор 🟡 → HW.CLAUDE.md: статус v0.4 (после реализации) → следующее v0.5 shell.
11. Дальше по ритму
v0.4 (после утверждения спеки) → План 8 (docs/specs/plans/08-v0.4-mcu-thermal.md, writing-plans) → TDD →
verify в Lima → коммиты feat/v0.4-mcu-thermal. Затем v0.5 — полный shell (живой weston-shell; замкнёт
thermal-UX-рендер). Физический HW-bring-up (MCU/supercap выбор, реальный UART/cpufreq/B09-чип, тепловой
вердикт) — отдельной подфазой при появлении RK3588-платы.