1. Тема дня

Намеренно вносим неисправности:

text
UART timeout
late EC25 response
MQTT disconnect
I2C NACK
DMA overrun
CAN bus-off
queue full
pool exhausted
NVS commit fail
task stall
power reset

Цепочка проверки:

text
fault -> detection -> classification -> containment -> recovery -> verification -> evidence

Главная мысль: надежность не доказывается наличием if (err). Нужно воспроизводимо вызвать ошибку и проверить, что recovery ограничена, наблюдаема и не повреждает данные.

2. Зачем это нужно

EC25

Проверить:

text
AT timeout;
late OK старой команды;
URC между ответами;
модем reset посреди MQTT publish;
ограниченный recovery level;
отсутствие лишнего ESP32 reset.

Фазовые входы

Проверить:

text
GPIO expander NACK;
ADC DMA stop;
RED+GREEN conflict;
phase queue full;
glitch storm;
INPUT_DEGRADED без перезагрузки.

NVS/OTA

Проверить:

text
write fail;
commit fail;
reset между A/B copy;
power loss during OTA;
self-test failed -> rollback.

3. Теория

Fault injection vs chaos testing

Fault injection - точный сценарий:

text
третья I2C read возвращает NACK
следующий modem read возвращает timeout
следующая NVS commit возвращает ESP_FAIL

Chaos - связанный сценарий:

text
LTE пропадает на 40 секунд;
очередь логов заполнена;
идет поток входных событий;
потом связь возвращается.

Четыре элемента сценария

  1. Invariant - что обязано остаться истинным.
  2. Trigger - когда вводится ошибка.
  3. Recovery deadline - за сколько система должна отреагировать.
  4. Evidence - что осталось в диагностике.

Инъекция на границе adapter

Не добавляй if (test_mode) во все места. Сделай интерфейс:

c
typedef struct {
    esp_err_t (*write)(const uint8_t *data, size_t length, size_t *written);
    esp_err_t (*read)(uint8_t *data, size_t capacity, size_t *received, uint32_t timeout_ms);
    esp_err_t (*flush_rx)(void);
} modem_transport_ops_t;

Production adapter и fault adapter реализуют один интерфейс.

Детерминированный injector

c
typedef enum {
    FI_NONE = 0,
    FI_MODEM_READ_TIMEOUT,
    FI_MODEM_LATE_RESPONSE,
    FI_I2C_NACK,
    FI_QUEUE_SEND_FAIL,
    FI_STORAGE_COMMIT_FAIL,
    FI_TASK_STALL,
} fault_injection_type_t;
typedef struct {
    fault_injection_type_t type;
    uint32_t trigger_call;
    uint32_t remaining_hits;
    bool armed;
} fault_injection_plan_t;

Сценарий должен быть воспроизводим по run_id, seed, Git SHA и списку injections.

4. Типичные ошибки

  • Fault injection доступна в production.
  • Случайные fault без seed.
  • Проверяется только возврат ONLINE, без invariants.
  • Test hooks меняют поведение даже при armed=false.
  • Unit-тесты реально ждут длинные timeout.
  • Recovery не имеет лимитов.
  • ISR callback выполняет тяжелую recovery.
  • Watchdog проверяется только включением, а не зависанием progress.
  • После UART overrun parser продолжает текущий кадр.
  • Storage fail ведет к factory reset.
  • Power fault заменяется программным esp_restart().
  • Найденный сценарий не добавляется в regression.

5. Практическое задание на 30-60 минут

Создай FAULT_INJECTION_POLICY.md:

markdown
# Fault injection policy
1. Fault injection is disabled in production builds.
2. Every injection has a run ID and injection ID.
3. Faults are deterministic and reproducible.
4. Inactive injectors do not change normal behavior.
5. Recovery has an explicit deadline and retry budget.
6. Every test checks subsystem invariants.
7. ISR callbacks only report fault events.
8. Fault injection never bypasses safety interlocks.
9. Every discovered failure becomes a regression test.
10. Destructive HIL actions require explicit test mode.

Добавь Kconfig:

c
config APP_FAULT_INJECTION
    bool "Enable fault injection"
    default n

Сценарий EC25:

text
1. MODEM_READ_TIMEOUT на следующем read.
2. Отправить diagnostic AT.
3. Ожидать AT_RESULT=TIMEOUT.
4. Проверить TRANSACTION_ACTIVE=no.
5. Вставить поздний "OK".
6. Проверить stale_response_count++.
7. Следующая команда AT должна пройти.

6. Что почитать или попробовать дальше

  • ESP-IDF pytest target tests и Unity tests.
  • ESP-IDF Task Watchdog и Interrupt Watchdog.
  • STM32 HAL ErrorCallback для I2C/UART/DMA.
  • fault-injection сценарии как YAML/JSON для HIL.
Какая запись позволяет воспроизвести инъекцию отказа?

Критерии: Нужны идентичность сценария и диагностические evidence.

Задание

Составьте план проверки modem-read timeout и последующего позднего OK. Укажите trigger, один invariant и диагностические evidence; не выполняйте аппаратную инъекцию.

Критерии самопроверки: Разделите инъекцию, invariant и evidence. Используйте детерминированный сценарий урока, а не безусловный MCU reset.

Показать ответ автора

Ввести MODEM_READ_TIMEOUT в следующий read. Требовать снятия активности просроченной транзакции и запретить позднему OK завершать новую. Проверить AT_RESULT=TIMEOUT, TRANSACTION_ACTIVE=no и stale_response_count++, затем успешное завершение следующей AT-команды.