# tft_app: plan & arch
This commit is contained in:
parent
6120e71416
commit
e2e3f8aff8
3 changed files with 487 additions and 0 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -78,3 +78,8 @@ tools/host/.venv-host-win/
|
|||
.zed/
|
||||
project_tree.txt
|
||||
tools/service_tui/dist/
|
||||
|
||||
# Референсные дампы старых серийных проектов (не коммитим)
|
||||
firmware/tft_app/OLD_PROJECT/
|
||||
firmware/tft_app/OLD_PROJECT_*/
|
||||
firmware/tft_app/special/
|
||||
|
|
|
|||
278
firmware/tft_app/ARCH.md
Normal file
278
firmware/tft_app/ARCH.md
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
# tft-app — архитектура
|
||||
|
||||
> Прошивка лифтового индикатора для семейства **MIMXRT1052 TFT (4 / 7 / 8 / 10")**.
|
||||
> Документ — живой источник истины по слоистой архитектуре. Статус фаз разработки —
|
||||
> в [PLAN.md](PLAN.md).
|
||||
|
||||
---
|
||||
|
||||
## Часть I. Требования (бизнес-логика)
|
||||
|
||||
### 1. Основные функции
|
||||
|
||||
**1.1 Индикация в реальном времени.** Местоположение кабины, спецрежимы, музыкальное
|
||||
сопровождение при движении, озвучка сигналов.
|
||||
- Сигналы от СУЛ приходят по последовательному интерфейсу (+24 В UART, кастомный
|
||||
бинарный протокол) либо по CAN.
|
||||
- Отображение состояния двух диспетчерских оптовходов: «Вызов подан» / «Вызов принят» / пусто.
|
||||
|
||||
**1.2 Обновление ПО в поле** через microSD (сам образ прошивки — задача bootloader,
|
||||
A/B Direct-XIP; ассеты/layout — задача tft-app, см. §12).
|
||||
|
||||
**1.3 Графический интерфейс настроек** в рантайме (меню, навигация двумя кнопками).
|
||||
|
||||
### 2. Частности
|
||||
|
||||
**2.1 Отображение.** 4 типа дисплея (TFT4/7/8/10); одна кодовая база, две сборки
|
||||
(см. §9). Ассеты (иконки, картинки, звук) — на встроенной флеш, обновляются с microSD.
|
||||
Шрифты — C-массивы ([LCD Image Converter](https://lcd-image-converter.riuson.com/)).
|
||||
Макеты UI унифицированы и упрощены; по запросу клиента — кастом.
|
||||
|
||||
**2.2 Связь с СУЛ.** Общий программный интерфейс ко всем СУЛ; одна прошивка — множество
|
||||
протоколов; выбор протокола через меню. Протоколы: УЭЛ, УКЛ, НКУ-CAN, НКУ-SD7, УИМ (список
|
||||
открыт).
|
||||
|
||||
**2.3 Настройки.** Общий набор параметров + кастом под заказ. Настройки влияют на UI и на
|
||||
работу протокола (напр. адрес индикатора: 0..15 у НКУ-CAN, 1..50 у УИМ) — все связи
|
||||
предусмотрены (см. §6, §8).
|
||||
|
||||
---
|
||||
|
||||
## Часть II. Архитектура
|
||||
|
||||
### 3. Принципы
|
||||
|
||||
1. **Домен не знает железа.** Декодеры протоколов и доменная модель — чистый C без единого
|
||||
HAL-вызова, тестируются на хосте (Unity + fff) тем же способом, что модули bootloader.
|
||||
2. **Одна прошивка — много протоколов.** Протокол выбирается в рантайме из настроек через
|
||||
реестр драйверов (§6). Новый протокол = реализовать декодер + зарегистрировать, без правок
|
||||
в остальных слоях.
|
||||
3. **Деградация, а не отказ.** Любой сбой ассетов/layout/связи ведёт к безопасному упрощению
|
||||
индикации (§7, §11), устройство не «кирпичится» и не гаснет.
|
||||
4. **Данные, а не код.** Макет, каталог ассетов, таблица приоритетов режимов, дескрипторы
|
||||
настроек протоколов — данные (компилируемые или загружаемые), не разветвления в коде.
|
||||
5. **Границы модулей = конвенции репозитория.** Каждый слой — статическая библиотека CMake,
|
||||
публичные заголовки в `include/`, приватное в `src/`. Аппаратные модули — через `bsp/*`.
|
||||
|
||||
### 4. Слои
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────────┐
|
||||
│ app/ FreeRTOS-задачи, wiring, main (firmware/tft_app)│ L4
|
||||
├──────────────────────────────────────────────────────────────────────┤
|
||||
│ ui/ view-model → экран (layout-движок, темы, fallback-рендер) │
|
||||
│ menu/ экран настроек, навигация 2 кнопки │ L3 презентация
|
||||
│ audio_policy доменное событие → команды звука │
|
||||
├──────────────────────────────────────────────────────────────────────┤
|
||||
│ controller редьюсер: sul_result_t (+кэш) → indication_task (diff) │
|
||||
│ sul/ реестр драйверов; драйвер = decoder(pure) + transport(hw) │ L2 ДОМЕН
|
||||
│ elevator_model канонический sul_result_t, приоритеты режимов │ (чистый C, host-тесты)
|
||||
├──────────────────────────────────────────────────────────────────────┤
|
||||
│ gfx (compositor+PXP+fonts) audio_engine (WAV/playlist над bsp_mqs) │
|
||||
│ assets (TLV-ридер, image cache) settings_store fs (FatFS SD/QSPI) │ L1 сервисы (адаптеры к железу)
|
||||
├──────────────────────────────────────────────────────────────────────┤
|
||||
│ bsp/* can uart_host opto button mqs sd qspi_flash sdram display led … │ L0 БОЕВОЙ, не трогаем
|
||||
└──────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Поток данных** (адаптирован из проекта `special`, поверх FreeRTOS):
|
||||
|
||||
```
|
||||
транспорт (CAN/UART) ─► sul_driver.transport ─► sul_driver.decode(frame) ─► sul_result_t
|
||||
│ poll + timeout→default
|
||||
▼
|
||||
controller.process(sul_result_t) ─► indication_task (dirty-флаги)
|
||||
│
|
||||
┌────────────────────────────────────────────────┬─────────┘
|
||||
▼ ▼
|
||||
ui.render(task, model, settings) audio_policy(task, model, settings)
|
||||
(+ локальные входы: opto диспетчер, меню) │
|
||||
audio_engine (bsp_mqs)
|
||||
```
|
||||
|
||||
### 5. Размещение и тестируемость
|
||||
|
||||
| Слой | Где живёт | Тип | Host-тест |
|
||||
|---|---|---|---|
|
||||
| `elevator_model`, `sul` (декодеры), `controller`, `audio_policy`, layout-солвер | `firmware/tft_app/src/domain/*`, `.../ui/layout/*` | чистый C | **да** (golden-векторы) |
|
||||
| `sul` транспорт-адаптеры | `firmware/tft_app/src/domain/sul/transport/*` | HW | HIL |
|
||||
| `gfx`, `audio_engine`, `assets`, `settings_store`, `fs` | `firmware/tft_app/src/services/*` | HW/адаптеры | частично (парсеры/TLV — да) |
|
||||
| `ui`, `menu`, `app` | `firmware/tft_app/src/{ui,menu,app}/*` | app | вид/меню — HIL |
|
||||
|
||||
Host-тесты — `tests/host/tft_app_*` (Unity + fff, компилируем `.c` домена против моков bsp,
|
||||
по образцу `tests/host/mcuboot_port`, `tests/host/protocol`).
|
||||
|
||||
### 6. Связь с СУЛ (`sul`)
|
||||
|
||||
**Канонический выход любого протокола** — `sul_result_t` (надмножество; простой протокол не
|
||||
заполняет лишнее):
|
||||
|
||||
```c
|
||||
#define SUL_POS_MAX 4 /* сейчас значимы 2; запас до 4 */
|
||||
|
||||
typedef struct {
|
||||
char pos[SUL_POS_MAX * 4 + 1]; /* UTF-8, позиция кабины (напр. "12","-1","П") */
|
||||
char next[SUL_POS_MAX * 4 + 1];/* UTF-8, следующий этаж (или пусто) */
|
||||
uint8_t direction; /* none / up / down / double */
|
||||
/* Ортогональные сигналы (могут сосуществовать; приоритет разрешает controller): */
|
||||
bool arrival; /* гонг */
|
||||
bool movement; /* начало движения */
|
||||
bool overload; /* перегруз */
|
||||
bool fire_alarm; /* пожарная тревога */
|
||||
bool lading; /* погрузка */
|
||||
bool maintenance; /* сервисный режим */
|
||||
bool fireman; /* режим пожарного */
|
||||
bool seismic; /* сейсмоопасность */
|
||||
bool error; /* авария */
|
||||
uint16_t lading_secs; /* обратный отсчёт погрузки, 0 = нет */
|
||||
uint8_t floor_num; /* производный числовой этаж для озвучки (0 = н/д) */
|
||||
} sul_result_t;
|
||||
```
|
||||
|
||||
- **Позиция — UTF-8 строка** (у нас реальные шрифты ASCII + кириллица), а не число:
|
||||
универсально для всех протоколов, host-тест сравнивает строки. Декодер не знает про шрифт.
|
||||
- **Escape-hatch под кастом не закладываем** (YAGNI); совместимость обеспечивает версия схемы.
|
||||
- **Валидация рендеримости** (§11): декодер может выдать кодпойнт вне покрытия активного
|
||||
шрифта (кастомные коды протокола) → по-символьный fallback на этапе рендера.
|
||||
|
||||
**Драйвер = чистый декодер + транспорт-адаптер:**
|
||||
|
||||
```c
|
||||
typedef struct { const uint8_t *data; uint16_t len; uint32_t id; uint8_t bus; } sul_frame_t;
|
||||
|
||||
/* Чистая функция — без железа, host-тестируется golden-векторами. */
|
||||
typedef sul_status_t (*sul_decode_fn)(void *ctx, const sul_frame_t *frame, sul_result_t *out);
|
||||
|
||||
typedef struct {
|
||||
uint8_t id; /* идентификатор протокола (стабильный) */
|
||||
const char *name; /* для меню */
|
||||
sul_decode_fn decode; /* pure */
|
||||
const sul_settings_desc_t *settings; /* per-protocol параметры (§8) */
|
||||
/* транспорт (CAN/UART) — отдельный тонкий адаптер, привязан к драйверу */
|
||||
} sul_driver_t;
|
||||
```
|
||||
|
||||
- **Реестр** `sul_registry[]` — таблица драйверов по `id`. Активный выбирается из настроек.
|
||||
Добавление протокола = запись в таблицу.
|
||||
- **Poll + timeout.** `sul` опрашивается периодически; при отсутствии кадров дольше таймаута
|
||||
выдаётся `default` (потеря связи → сброс режимного состояния), как в `special`/OLD_PROJECT.
|
||||
|
||||
### 7. Контроллер и приоритеты режимов
|
||||
|
||||
`controller.process(sul_result_t*) → indication_task_t` (dirty-флаги: `pos_pending`,
|
||||
`direction_pending`, `mode_pending`, `arrival_pending`, …) — презентация перерисовывает и
|
||||
озвучивает только изменившееся (кэш прошлого состояния внутри контроллера).
|
||||
|
||||
- Ортогональные булевы сигналы `sul_result_t` сходятся в **один экранный режим** через
|
||||
**таблицу приоритетов** (пожар > перегруз > сейсмо > сервис > … > норма).
|
||||
- **Таблица приоритетов — данные, потенциально клиентские**, живёт рядом с клиентским
|
||||
конфигом/layout, а не хардкодом в контроллере. Контроллер применяет активную таблицу.
|
||||
|
||||
### 8. Настройки
|
||||
|
||||
Три раздельных источника (не смешивать):
|
||||
|
||||
1. **`sul_result_t`** — только данные от СУЛ (§6).
|
||||
2. **`settings`** — конфиг устройства/пользователя: громкости, лого, серийник, ёмкость, год,
|
||||
выбранный протокол, панель (provisioning). Хранится на QSPI (сектор настроек), формат с
|
||||
магиком/версией/CRC (развитие `settings_manager` из OLD_PROJECT).
|
||||
3. **Локальные входы** — оптовходы (диспетчерский вызов/ответ) и кнопки/меню; вливаются на
|
||||
уровне контроллера/презентации, не часть протокольных данных.
|
||||
|
||||
**Per-protocol настройки.** Каждый протокол регистрирует `sul_settings_desc_t` — какие у него
|
||||
параметры, диапазоны, подписи для меню (напр. адрес 0..15 у НКУ-CAN, 1..50 у УИМ). `settings`
|
||||
держит слайс под активный протокол; `driver` получает свой конфиг; меню строится из дескриптора
|
||||
(развитие `settings_mgr_bind_menu`). Общие настройки — отдельно от протокольных.
|
||||
|
||||
### 9. Дисплеи и платы
|
||||
|
||||
Платы отличаются **только RGB-интерфейсом LCDIF** (TFT4 — 40pin без пинов ориентации; TFT7/8/10
|
||||
— 50pin с U/D·L/R). SDRAM, SEMC, периферия — одинаковы. Панели TFT7/8/10 уже разведены рантаймом
|
||||
в `bsp_display` (таблица `panel_config[]`: тайминги, клок, `has_orientation_pins`).
|
||||
|
||||
| Профиль сборки | LCDIF | Панель | Клок |
|
||||
|---|---|---|---|
|
||||
| `app-tft4` | 40pin, без ориентации | TFT4 (фикс) | Video PLL |
|
||||
| `app-big` | 50pin, с U/D·L/R | 7 / 8 / 10 — **рантайм** из provisioning | PLL2 |
|
||||
|
||||
Различие изолировано в одном board-файле пин-мукса LCDIF. **Тип панели — provisioning-параметр**
|
||||
(пишется service_tui), не пользовательская настройка. Матрица сборки — два buildPreset
|
||||
(`app-tft4`, `app-big`), как `bootloader`/`firmware-test`.
|
||||
|
||||
### 10. Карта QSPI (W25Q128, 16 МБ)
|
||||
|
||||
| Регион | Смещение | Размер | Владелец |
|
||||
|---|---|---|---|
|
||||
| bootloader | `0x000000` | 256 КБ | bootloader |
|
||||
| slot A (tft_app) | `0x040000` | 2 МБ | MCUboot |
|
||||
| slot Б (tft_app) | `0x240000` | 2 МБ | MCUboot |
|
||||
| **layout-регион** | `0x440000` | 64 КБ | tft_app |
|
||||
| **assets-регион** | `0x450000` | ≈ 11 МБ | tft_app |
|
||||
| settings | `0xFFE000` | 4 КБ | tft_app |
|
||||
|
||||
layout- и assets-регионы — **вне** flash-area загрузчика (bootutil про них не знает). Разметка
|
||||
фиксируется в **едином partition-заголовке**, из которого читают app и генератор для service_tui.
|
||||
|
||||
### 11. Ассеты, layout и fallback
|
||||
|
||||
**Разделяем два артефакта** вместо монолитного `style.img`:
|
||||
|
||||
- **layout** — декларативная таблица виджетов (что рисуем: позиция/стрелка/режим/лого/…, якорь,
|
||||
размер в %/единицах, шрифт, привязка к полю модели). Якорное позиционирование → один макет
|
||||
раскладывается под 480×272 и под 1024×600. Формат — **TLV**, схема версионируется.
|
||||
Layout-солвер (якорь→пиксели) — чистый C, host-тест (golden-render). Виджеты бывают
|
||||
**примитивные** (text/rect/line/arrow) и **спрайтовые** (sprite-by-id) — единый движок.
|
||||
- **assets** — упакованный индексированный **TLV-бандл** (host-утилита `tools/`): PNG → сырой
|
||||
ARGB8888 на этапе пака (в рантайме нет lodepng и boot-time декодирования; XIP-mmap с FlexSPI,
|
||||
блит без копии). Звук — WAV в том же бандле.
|
||||
|
||||
**Загрузка layout при старте:** сначала layout-регион QSPI (магик+версия схемы+CRC+совместимость);
|
||||
если валиден — берём его; иначе — **встроенный default**.
|
||||
|
||||
- **Модель 1 (baked):** нужный layout вкомпилен как default в app-слот → цельный подписанный
|
||||
образ, тестируется целиком, деплой через bootloader A/B. layout-регион пуст.
|
||||
- **Модель 2 (injected):** базовый бинарь; service_tui пишет клиентский layout-блоб в регион
|
||||
(вне слота → app-обновление его не трогает). Пересборка не нужна. Валидация блоба — на host
|
||||
(схема + golden-render), поэтому проверка клиентского layout не требует сборки клиентского
|
||||
бинаря.
|
||||
|
||||
**Fallback (safe-mode).** `default`-layout — **asset-free и FS-free**: не монтирует QSPI-FAT/SD,
|
||||
не трогает TLV-ридер. Зависимости — только `bsp_display` + `bsp_can`/`sul` + домен +
|
||||
один вкомпилированный шрифт + примитивы `gfx`. Рисует **только этаж + стрелки (вверх/вниз)**,
|
||||
чёрный фон, без звука. Это же — первый экран walking-skeleton (§PLAN, Фаза 1).
|
||||
|
||||
**Деградация — по-виджетно.** layout битый → полный fallback. layout валиден, но ассет
|
||||
отсутствует/битый → рисуем **примитивную форму этого виджета**, а не полный откат. Символ вне
|
||||
покрытия шрифта → по-символьный fallback. Решение защёлкивается на старте (при XIP-mmap рантайм
|
||||
не ревалидирует — иначе битое чтение = hardfault); валидация ассетов — CRC в индексе TLV.
|
||||
|
||||
### 12. Обновление в поле
|
||||
|
||||
- **Ассеты/layout — app-side updater** (доменные данные; app знает схему и валидацию;
|
||||
переиспользуем `bsp_qspi` + FatFS-на-SD, как `update_style_task` в OLD_PROJECT). Запись —
|
||||
потоковая, erase-before-write по 4 КБ, **заголовок-валидатор пишется последним** (торн-запись
|
||||
→ регион невалиден → безопасный fallback, не кирпич).
|
||||
- **Provisioning-заливка** (service_tui, заводской/сервисный контекст): канал — USB-CDC
|
||||
загрузчика или USB-SDP ROM, **при условии приемлемой скорости**. Если заливка ассетов этим
|
||||
путём окажется слишком медленной — отказываемся и делаем **только через microSD**. Решение —
|
||||
по замеру на Фазе обновлений (§PLAN).
|
||||
|
||||
### 13. Тестирование
|
||||
|
||||
- **Host (Unity + fff):** декодеры всех протоколов (golden-векторы кадров → `sul_result_t`),
|
||||
контроллер (diff + приоритеты), layout-солвер (golden-render), TLV-ридер/паковщик, парсеры
|
||||
настроек, валидация рендеримости.
|
||||
- **HIL:** gfx/PXP/ELCDIF, audio/MQS, SD/QSPI, реальные транспорты CAN/UART (по образцу
|
||||
`tests/target/*` и `06_test_firmware_*`).
|
||||
|
||||
---
|
||||
|
||||
## Приложение. Заимствования из проектов
|
||||
|
||||
- `special` — эталон доменного дизайна: `UnifiedProtocolData`→`sul_result_t`, `proto_handler`
|
||||
(poll+timeout)→реестр `sul`, `controller`+`IndicationTask`. Адаптирован под FreeRTOS и снейк-кейс.
|
||||
- `OLD_PROJECT` (НКУ-CAN, TFT7/10) — боевые `bsp`, compositor (PXP), audio-движок,
|
||||
`settings_manager`, декодер НКУ-CAN (PACKET1..5, адресация, удалённая установка адреса).
|
||||
- `OLD_PROJECT_TFT8_UKL` — наш `bsp` (button/opto/sd/w25q/settings/file_loader), UART+CAN.
|
||||
- `TFT10_UIM`, `TFT4_UIM`, `TFT4_SD7`, `TFT4_UEL` — декодеры УИМ / НКУ-SD7 / УЭЛ, матрица
|
||||
«протокол × дисплей».
|
||||
204
firmware/tft_app/PLAN.md
Normal file
204
firmware/tft_app/PLAN.md
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
# tft-app — план разработки
|
||||
|
||||
> Пофазовый план. Стратегия — **walking skeleton**: сначала тонкий сквозной срез
|
||||
> (CAN → модель → этаж на экране), затем наращивание слой за слоем. Архитектура —
|
||||
> [ARCH.md](ARCH.md). Этот файл — **источник истины по статусу фаз**.
|
||||
>
|
||||
> Каждая фаза: цель · объём · артефакты · тесты · критерий выхода. Ранние фазы детальны,
|
||||
> поздние укрупнены и детализируются по мере приближения.
|
||||
>
|
||||
> Легенда статуса: ⬜ не начата · 🟨 в работе · ✅ готова.
|
||||
|
||||
---
|
||||
|
||||
## Обзор фаз
|
||||
|
||||
| # | Фаза | Статус |
|
||||
|---|---|---|
|
||||
| 0 | Каркас проекта и сборка | ⬜ |
|
||||
| 1 | Walking skeleton (CAN → модель → этаж + стрелки) | ⬜ |
|
||||
| 2 | Домен: полный `sul_result_t`, приоритеты, таймаут | ⬜ |
|
||||
| 3 | Настройки и меню, локальные входы (opto) | ⬜ |
|
||||
| 4 | Ассеты: TLV-бандл, паковщик, XIP-блит | ⬜ |
|
||||
| 5 | Layout-движок: схема, солвер, темы, injected-регион | ⬜ |
|
||||
| 6 | Аудио: движок над MQS, `audio_policy` | ⬜ |
|
||||
| 7 | Обновление в поле (SD) + provisioning-канал | ⬜ |
|
||||
| 8 | Мультипротокол: УИМ / SD7 / УЭЛ / УКЛ + per-client | ⬜ |
|
||||
| 9 | Профиль `app-tft4` (40pin LCDIF, Video PLL) | ⬜ |
|
||||
|
||||
---
|
||||
|
||||
## Фаза 0 — Каркас проекта и сборка ⬜
|
||||
|
||||
**Цель.** `firmware/tft_app` собирается как ARM-таргет, грузится через bootloader, мигает
|
||||
heartbeat; host-тест-харнесс на месте. Пустой, но живой каркас всех слоёв.
|
||||
|
||||
**Объём.**
|
||||
- `firmware/tft_app/{CMakeLists.txt, src/, README.md}`; подкаталоги `src/{domain,services,ui,menu,app}`.
|
||||
- `main.c`: `BOARD_ConfigMPU/InitPins/BootClock`, FreeRTOS, задача heartbeat (`bsp_led`, `bsp_tick`).
|
||||
- Подключить в корневой `CMakeLists.txt` (заменить `firmware/bootloader/test_stub`).
|
||||
- CMakePresets: buildPresets `app-big-debug/release` (профиль `app-big`). `app-tft4` — заглушка до Фазы 9.
|
||||
- Линковка как MCUboot-слот-образа (Direct-XIP, `flexspi_nor.ld` + slot-offset), HAB-таргет.
|
||||
- `tests/host/tft_app_smoke/` — пустой Unity-таргет, зелёный (проверка харнесса).
|
||||
- `.vscode/launch.json` — конфигурация `🐛 Debug: tft_app (FreeRTOS)` (уже упомянута в DEV_ARCH).
|
||||
|
||||
**Тесты.** `just build::build-app-big-debug` собирает ELF; host smoke-тест зелёный.
|
||||
|
||||
**Критерий выхода.** ELF грузится bootloader'ом из слота, heartbeat мигает (проверка — пользователь
|
||||
на железе). Удалён `test_stub` из корневого CMake.
|
||||
|
||||
---
|
||||
|
||||
## Фаза 1 — Walking skeleton ⬜
|
||||
|
||||
**Цель.** Сквозной срез: реальный CAN-кадр НКУ → чистый декодер → контроллер → **fallback-рендер
|
||||
этажа и стрелки** на экране. Никаких ассетов, FS, TLV, меню.
|
||||
|
||||
**Объём.**
|
||||
- `domain/elevator_model`: `sul_result_t`, `sul_status_t`, дефолт-состояние.
|
||||
- `domain/sul`: тип `sul_driver_t`, реестр (одна запись); **чистый декодер `sul_nku_can`**
|
||||
(подмножество: `pos`, `direction`) — портирован из OLD_PROJECT `msg_receiver_task` PACKET1/3.
|
||||
- `domain/sul/transport/can`: тонкий адаптер `bsp_can` → `sul_frame_t`.
|
||||
- `domain/controller`: `process()` → `indication_task_t` (diff), кэш, poll + timeout→default.
|
||||
- `services/gfx`: минимум над `bsp_display` — один вкомпилированный шрифт + примитивы (стрелка).
|
||||
- `ui/fallback`: рендер `pos` + стрелка (asset-free, FS-free) — §11 ARCH.
|
||||
- `app`: задачи `sul_rx` (poll CAN) и `render`; wiring.
|
||||
|
||||
**Тесты (host).**
|
||||
- `tests/host/tft_app_sul_nku`: golden-векторы CAN-кадров → ожидаемый `sul_result_t`.
|
||||
- `tests/host/tft_app_controller`: последовательность `sul_result_t` → корректные dirty-флаги,
|
||||
таймаут→default.
|
||||
|
||||
**Критерий выхода.** На стенде: подан НКУ-CAN трафик → на экране меняется номер этажа и стрелка
|
||||
направления; при пропадании трафика — «--». Домен-тесты зелёные в CI.
|
||||
|
||||
---
|
||||
|
||||
## Фаза 2 — Домен: полный контракт ⬜
|
||||
|
||||
**Цель.** `sul_result_t` в полном объёме (§6 ARCH), таблица приоритетов режимов, устойчивый
|
||||
таймаут/потеря связи.
|
||||
|
||||
**Объём.** Все поля модели (next, сигналы, `lading_secs`, `floor_num`); полный декодер НКУ-CAN
|
||||
(PACKET1..5, режимы, удалённая установка адреса); `controller` разрешает приоритет режимов по
|
||||
**таблице-данным** (§7); валидация рендеримости позиции (renderable-множество шрифта +
|
||||
по-символьный fallback).
|
||||
|
||||
**Тесты (host).** Расширенные golden-векторы НКУ (все режимы, спецсостояния); тесты таблицы
|
||||
приоритетов; тесты валидации/fallback символов.
|
||||
|
||||
**Критерий выхода.** Все режимы НКУ-CAN корректно доходят до fallback-рендера; полное host-покрытие
|
||||
декодера и приоритетов.
|
||||
|
||||
---
|
||||
|
||||
## Фаза 3 — Настройки и меню ⬜
|
||||
|
||||
**Цель.** Персистентные настройки на QSPI, меню на двух кнопках, локальные входы (opto).
|
||||
|
||||
**Объём.** `services/settings_store` (магик/версия/CRC, сектор настроек §10); дескрипторы
|
||||
per-protocol настроек (§8); `menu/` (навигация, редактирование, привязка к дескрипторам);
|
||||
`bsp_button` + `bsp_opto` (диспетчерский вызов/ответ) → контроллер/презентация.
|
||||
|
||||
**Тесты (host).** Сериализация/дефолты/CRC настроек; логика навигации меню; связывание
|
||||
дескрипторов.
|
||||
|
||||
**Критерий выхода.** Меню редактирует и сохраняет настройки; opto-иконки отображаются; выбор
|
||||
протокола и его параметров работает (пока протокол один).
|
||||
|
||||
---
|
||||
|
||||
## Фаза 4 — Ассеты (TLV-бандл) ⬜
|
||||
|
||||
**Цель.** Ассеты в упакованном индексированном бандле на QSPI, XIP-блит без рантайм-декодирования.
|
||||
|
||||
**Объём.** Формат TLV (индекс, per-entry CRC, версия); host-утилита `tools/` (PNG→ARGB8888, WAV,
|
||||
пак); `services/assets` (TLV-ридер, XIP-mmap, image cache); partition-заголовок (§10).
|
||||
|
||||
**Тесты (host).** Раунд-трип паковщик↔ридер; целостность индекса/CRC; кросс-валидация id.
|
||||
|
||||
**Критерий выхода.** Спрайты грузятся из assets-региона и блитятся; битый/отсутствующий ассет →
|
||||
по-виджетная деградация.
|
||||
|
||||
---
|
||||
|
||||
## Фаза 5 — Layout-движок ⬜
|
||||
|
||||
**Цель.** Декларативные макеты: схема TLV, якорный солвер, темы/шаблоны, injected-регион +
|
||||
baked default.
|
||||
|
||||
**Объём.** Схема layout (примитивные + спрайтовые виджеты, привязка к полям модели); солвер
|
||||
якорь→пиксели (чистый C); загрузчик (регион QSPI → иначе baked default, §11); порт боевых макетов
|
||||
НКУ (style_1/style_2) на новую схему; расширение host-утилиты (компиляция layout-исходника → TLV).
|
||||
|
||||
**Тесты (host).** Golden-render солвера на 480×272 и 1024×600; валидация схемы/версии; выбор
|
||||
region/baked.
|
||||
|
||||
**Критерий выхода.** Богатый макет рисуется из данных; injected-layout валидируется на host без
|
||||
сборки бинаря; клиентский кастом = новая таблица данных.
|
||||
|
||||
---
|
||||
|
||||
## Фаза 6 — Аудио ⬜
|
||||
|
||||
**Цель.** Озвучка и музыка через MQS, отделённая политика «событие → звук».
|
||||
|
||||
**Объём.** `services/audio_engine` (WAV-парсер, плейлист/приоритеты, воспроизведение над
|
||||
`bsp_mqs` + eDMA); `audio_policy` (доменное событие/`indication_task` → команды звука: озвучка
|
||||
этажа, гонг, тревоги, музыка); звук в TLV-бандле.
|
||||
|
||||
**Тесты (host).** WAV-парсер; логика плейлиста/приоритетов/вытеснения; маппинг `audio_policy`
|
||||
(событие→очередь) на golden-сценариях.
|
||||
|
||||
**Критерий выхода.** Полная звуковая индикация НКУ (этажи, гонги, тревоги, музыка) с приоритетами.
|
||||
|
||||
---
|
||||
|
||||
## Фаза 7 — Обновление в поле ⬜
|
||||
|
||||
**Цель.** Обновление ассетов/layout с microSD; замер и решение по provisioning-каналу.
|
||||
|
||||
**Объём.** App-side updater (SD→QSPI-регион, потоковая запись, заголовок-валидатор последним,
|
||||
§12); интеграция в service_tui; **замер скорости** заливки через CDC/SDP → решение SD-only или
|
||||
CDC/SDP (§12).
|
||||
|
||||
**Тесты.** HIL: обновление региона с SD, устойчивость к торн-записи (→ fallback). Замер скорости.
|
||||
|
||||
**Критерий выхода.** Ассеты/layout обновляются с SD; провижининг-путь выбран по факту замера.
|
||||
|
||||
---
|
||||
|
||||
## Фаза 8 — Мультипротокол ⬜
|
||||
|
||||
**Цель.** Остальные протоколы + клиентская специфика.
|
||||
|
||||
**Объём.** Чистые декодеры УИМ, НКУ-SD7, УЭЛ, УКЛ (+ UART-транспорт-адаптеры) в реестр; их
|
||||
per-protocol дескрипторы настроек; клиентские таблицы приоритетов и макеты.
|
||||
|
||||
**Тесты (host).** Golden-векторы по каждому протоколу; HIL по CAN и UART транспортам.
|
||||
|
||||
**Критерий выхода.** Любой протокол выбирается в меню и работает без правок остальных слоёв;
|
||||
добавление нового протокола = декодер + запись в реестр.
|
||||
|
||||
---
|
||||
|
||||
## Фаза 9 — Профиль `app-tft4` ⬜
|
||||
|
||||
**Цель.** Вторая сборка под 40-pin плату TFT4.
|
||||
|
||||
**Объём.** Board-файл пин-мукса LCDIF (40pin, без ориентации); Video PLL клок (закрыть TODO в
|
||||
`bsp_display`); buildPreset `app-tft4`; проверка размеров буферов/RAM; макеты под 480×272.
|
||||
|
||||
**Тесты.** HIL на плате TFT4.
|
||||
|
||||
**Критерий выхода.** `app-tft4` собирается и работает на плате TFT4; `app-big` — на TFT7/8/10 с
|
||||
рантайм-выбором панели.
|
||||
|
||||
---
|
||||
|
||||
## Открытые вопросы (уточняются по мере приближения фаз)
|
||||
|
||||
- Точная скорость provisioning-заливки через CDC/SDP → SD-only? (Фаза 7)
|
||||
- Формат исходника layout для host-утилиты (JSON/TOML) и позже GUI поверх компилятора. (Фаза 5)
|
||||
- Renderable-множество символов на протокол vs общий шрифт; политика по-символьного fallback. (Фаза 2)
|
||||
- Звук в бандле: WAV как есть или препарсенный PCM. (Фаза 6)
|
||||
Loading…
Reference in a new issue