ADR 0094: Шина доставки событий в UI (аналогия AFDX) и System.Threading.Channel<T>¶
Статус: Accepted
Дата: 2026-04-24
Связанные ADR¶
| ADR | Роль |
|---|---|
| 0036 | продуктовый канал кабины → CDS → композитор → поверхность |
| 0097 | CCU / вычислительный блок кабины: свёртка в DTO/снимок канала — не эта шина |
| 0089 | IDE Health вместо прежнего Workspace Health — термин и типы вроде IdeHealth*; не этот чат, а зафиксированное в репо решение |
| 0021 | зоны внимания; EICAS как продуктовый контур — по дорожной карте |
| 0068 | полезная нагрузка строки канала vs проекция |
| 0007 | сигналы и нагрузка на UI |
| 0004 | маршалинг на UI-поток |
Резюме¶
- Шина доставки в UI (аналогия AFDX):
Channel<T>, батчинг, backpressure. - Ортогонально CDS-«каналу» 0036.
Вне ADR¶
| Документ | Роль |
|---|---|
| cds-contract-v0.md | cds contract v0 |
| --- |
Контекст¶
Уточнение по размещению UI (чтобы не путать с VS-like «нижней панелью» главного окна): в текущей разметке MainWindow нет отдельной строки grid на всю ширину под «терминал / сборка». Вторичный контур — колонка MFD: MfdShellView — сверху полоса WorkspaceChromeBandView (IDE Health по 0089; задел под EICAS-семантику — см. комментарий в MfdShellView.axaml и 0021), ниже хост MfdContourStackHost со MfdShellPageStack (страницы WORKSPACE / чат / журнал сборки / терминал / …). Этот ADR про транспорт потоков текста в соответствующие VM (например BuildOutputPanelViewModel), а не про то, что журнал MSBuild уже является полосой EICAS.
Несколько источников (MSBuild / dotnet, дочерние процессы, LSP, агент, внутренние сервисы) пишут текст и статусы в эти поверхности вторичного контура и смежные VM. Сегодня типичный путь — много обрывковых вызовов на UI-поток или частые обновления одного большого буфера (Build output, терминал и т.п.), из-за чего поток воспринимается как рваный, а UI — перегруженным мелкими инкрементами.
В авионике AFDX (ARINC 664 Part 7, switched Ethernet) — образ общей детерминированной шины: несколько LRU подключаются к ограниченному транспорту с политикой доступа и приоритетов, а не «каждый сам тянет провод в прибор».
Cascade не внедряет AFDX как протокол и не смешивает этот ADR с сертификацией; переносим только инженерный смысл: нормализованная доставка + backpressure + один предсказуемый путь к потребителю.
Решение¶
Вводится опциональный слой ingestion (рабочее имя в коде/доках — по согласованию, например IdeIngestion, BuildOutputIngestion; этот ADR не фиксирует имя пакета) для транспорта событий от продюсеров к VM страниц MFD и другим потребителям длинного текста:
- Продуктовый «канал» кабины в смысле 0036 и шина доставки в смысле этого ADR — разные вещи. Первый отвечает на вопрос что в кабине и куда по смыслу; второй — как донести куски текста/событий до привязки к свойствам UI без гонок и лавины обновлений.
- Рекомендуемая реализация в .NET:
System.Threading.Channel<T>(частоBoundedChannelOptions+ явныйFullMode: ожидание, отброс хвоста или политика по продукту) для очереди типизированных записей (BuildLogLine,TerminalChunk, унифицированныйIdeStreamEvent— выбор при внедрении).
- Один (или мало) читатель(ей) снимает канал и батчит применение к VM: например, не чаще N мс и/или не реже M символов за тик, затем один
IUiScheduler/Postна обновление привязанного свойства (журнал сборки, терминал, …) (0004).
- Strangler: первый вертикальный срез — журнал сборки (и при необходимости терминал); расширение на другие потоки — после стабилизации паттерна. Существующие вызовы не обязаны мигрировать в один коммит.
- Наблюдаемость: снимок очереди / счётчики дропа (если политика допускает) — по мере необходимости для отладки и MCP; форма JSON не фиксируется этим ADR (см. 0008 при появлении полей).
Границы с CDS и с «каналом» в глоссарии¶
- CDS и
Cockpit/Channels/**остаются каноном семантики кабины и данных для приборов; этот ADR не добавляет в CDS ответственность за «все логи MSBuild». - Шина доставки живёт ниже или рядом с VM, которые кормят страницы MFD и аналогичные длинные логи (журнал сборки, терминал), не подменяя 0068: когда тот же смысл уже оформлен как полезная нагрузка строки канала кабины (в т.ч. будущие/текущие сегменты полосы в духе EICAS / IDE Health в CDS), он попадает в контур 0036 по существующим правилам — отдельно от сырого потока MSBuild в TextBox страницы «Build».
- Свёртка сырья в снимок/DTO канала (агрегация, приоритеты, теги уровня A/B/C) — это не ответственность шины; см. 0097.
Последствия¶
- Меньше «рваного» UI и проще тестировать продюсеров (писать в канал из теста, проверять батчи).
- Явный backpressure вместо неограниченного роста строки в памяти при шторме лога.
- Дополнительный слой абстракции: внедрять поэтапно, с измерением до/после (0054 при появлении сценариев).
Не цели¶
- Реализация AFDX / ARINC 664 как сетевого стека.
- Замена CDS или переименование продуктового термина «канал» в кабине.
- Обязательная унификация всех сигналов IDE в одном
Channel<object>без типизации.
Отклонённые альтернативы (кратко)¶
- Только Rx / только ручная очередь без политики: допустимо локально; как дефолт продукта предпочтительнее канал с явными границами и простым single-reader loop.
- Пихать всё в CDS: отклонено — смешивает кабину и транспорт логов shell (0036, 0066).
Состояние реализации¶
Сборка (UI и MCP), dotnet format (MCP) → панель «Сборка · вывод»: единая цепочка: IDotnetCommandRunner.RunWithChunkWriterAsync → BuildLogIngestion.CreateBuildLogChannel (bounded, DefaultChannelChunkCapacity ≈ 32, FullMode = Wait — backpressure к помпам stdout/stderr, без дропа) → BuildLogIngestion.DrainToAppendAsync (батч ~8K в Append, по желанию onEachDequeuedChunk в OutputAccumulator для полного сыря в ответе MCP) → BuildOutputPanelViewModel.Append (фаза 5: Post на UI).
MCP (McpDotnetBuildTestService.BuildWithBinlogAsync, RunCodeCleanupAsync): тот же транспорт; возвращаемая строка для JSON/парсеров = накопленное в OutputAccumulator (и суффикс Exit code при ошибке build).
Тесты: CascadeIDE.Tests/BuildLogIngestionTests (батч, onEach, фабрика bounded-канала).
Вне этого ADR (намеренно): dotnet test / вывод Инструментация · тесты — по-прежнему RunAsync и одна вставка в InstrumentationPanel (другая поверхность; при необходимости — тот же паттерн отдельно). Встроенный терминал (если/когда) — тот же strangler, не блокирует степень Accepted этой ADR.