Files
shturman/docs/specs/v0.4-mcu-thermal.md
kk0t9 c377a34c4f docs(v0.4): спека MCU/thermal fail-safe (тепловой триггер + MCU-протокол + fail-safe)
Веха 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>
2026-06-25 15:00:10 +03:00

24 KiB
Raw Permalink Blame History

Спека реализации: 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):

  1. thermal-trip: SetTemp ≥ criticalThermalTripShutdownImminent(thermal) → graceful (реюз v0.3-пути, /data консистентен); восстановление по гистерезису.
  2. MCU fail-safe: HangSoc (heartbeat пропал в running) → мок-MCU детерминированно снимает питание (в VM = форс-off сервиса) — «MCU режет питание, если SoC завис»; /data консистентен.
  3. throttling: SetTemp в warn/throttle-банде → throttle-действие записано (VM Noop), без 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): чистый ThermalPolicytemp + предыдущий уровень → ThermalLevel ∈ {Normal, Warn, Throttle(n), Critical} с гистерезисом (раздельные пороги вверх/вниз — нет осцилляции). Юнит-тестируемый без I/O. Пороги — placeholder-константы (// тюнинг на RK3588, hardware §1a).
  • Источник температуры (TempSource trait): real = sysfs /sys/class/thermal/thermal_zone*/temp (max по зонам); VM = MockTempSource (значение из dev-D-Bus SetTemp). В v0.4 активен mock.
  • Throttler (Throttler trait): 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 (Coprocessor trait): real = SerialCoprocessor (UART — стаб, HW-подфаза); VM = MockCoprocessor (in-process, кормится через dev-D-Bus).
  • SoC-side CoprocessorClient: heartbeat в running; на ShutdownImminentwait-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 = форс-off power-сервиса).
  • 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 (Coprocessor trait), тело — HW-подфаза при выборе supercap.
  • Sleep/wake/scheduled-wake/battery-cutoffv1/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-сообщение не может (а) снять питание на ходу, (б) держать вечно и разрядить АКБ. В модели MockCoprocessor cut инициируется только таймером 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.rsThermalLevel, ThermalPolicy (чистая, гистерезис), TempSource trait (SysfsTempSource/MockTempSource), Throttler trait (Cpufreq-стаб/NoopThrottler), ThermalMonitor (poll → политика → throttle + ThermalTrip/ThermalCleared).
  • Create crates/core/shturman-power/src/protocol.rsSocToMcu/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.rsCoprocessor trait, MockCoprocessor (in-process + B09-таймер-модель), SerialCoprocessor (UART-стаб), CoprocessorClient (SoC-side: heartbeat/wait/safe-to-cut).
  • Modify crates/core/shturman-power/src/fsm.rsEvent::ThermalCleared (+ переход abort из ShuttingDown{Abortable, reason: Thermal}Running); Event::FailsafeCut (→ off из любого не-off, необратимо — MCU-авторитет); подтвердить армы ThermalTrip.
  • Modify crates/core/shturman-power/src/service.rs — владеть ThermalMonitor + CoprocessorClient (кормят FSM); property ThermalState + сигнал ThermalChanged; dev-mock расширить: SetTemp(d), HangSoc(), McuLinkLoss().
  • Modify crates/core/shturman-power/src/lib.rspub mod thermal; pub mod protocol; pub mod codec; pub mod coprocessor;.
  • Modify crates/shturman-ipc/src/proxy.rsPower1Proxy: property ThermalState + сигнал 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); на входе в Criticalapply_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-доказательства (fsck clean, 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 (feature dev-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: ThermalCleared abort только из ShuttingDown{Abortable, reason: Thermal} (из AccOff-shutdown — no-op); committed — no-op. FailsafeCutoff из любого не-off (необратимо).

9.2 Integration (session-шина, #[ignore], just test-integration)

  • SetTemp ≥ critical → наблюдаем ShutdownImminent(thermal) + ThermalChanged(critical); state shutting_down.
  • SetTemp recovery до 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 ≥ criticalShutdownImminent(thermal) → graceful (реюз v0.3: stop→umount(PONR)→ remount) → /data консистентен; затем SetTemp норма → running.
  • MCU fail-safe: HangSoc (heartbeat пропал) → мок-MCU режет питание (наблюдаем forced-off / SIGKILL-эквивалент) → fsck clean, 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-платы.