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>
This commit is contained in:
@@ -0,0 +1,291 @@
|
|||||||
|
# Спека реализации: 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 ≥ critical` → `ThermalTrip` → `ShutdownImminent(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):** чистый `ThermalPolicy` — `temp + предыдущий уровень → 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`; на `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 = форс-`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-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-сообщение не может (а) снять питание на ходу, (б) держать
|
||||||
|
вечно и разрядить АКБ. В модели `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.rs` — `ThermalLevel`, `ThermalPolicy` (чистая, гистерезис),
|
||||||
|
`TempSource` trait (`SysfsTempSource`/`MockTempSource`), `Throttler` trait (`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` — `Coprocessor` trait, `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); property `ThermalState` + сигнал `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`: 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)`;
|
||||||
|
**на входе в `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-доказательства
|
||||||
|
(`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. `FailsafeCut` → `off` из любого не-`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 ≥ critical` → `ShutdownImminent(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-платы.
|
||||||
Reference in New Issue
Block a user