using System;
using Ban.Sdaid.Notation.Documents;
namespace Ban.Sdaid.Icd.Arch.Adr
{
/// <summary>
/// Решение: обработка пакета запускается автоматически фоновой очередью при постановке в очередь;
/// к четырём состояниям добавляются два машинных — «обрабатывается» и «готов к проверке».
/// Расширяет модель состояний ADR-008.
/// </summary>
public class ADR_014_PacketProcessingQueue : IAdrDocument
{
public static string _refName = "ADR-014 «Фоновая очередь обработки пакетов и шесть состояний»";
public string Name => "ADR-014. Фоновая очередь обработки пакетов и шесть состояний";
public string Description =>
@"Обработку пакета (нормализация orthocover + опознание вида) запускает не оператор кнопкой, а фоновый воркер — автоматически при постановке пакета в очередь. Очередь — процессная, in-memory (канал идентификаторов + hosted-воркер с ограничением параллелизма); персистентность обеспечивает состояние пакета в БД, а не сам канал. К четырём состояниям ADR-008 добавляются два машинных: «обрабатывается» и «готов к проверке» — они снимают двойную роль «в очереди». Ручной повтор и восстановление недоделанных пакетов при рестарте сохраняются.";
public string Version => "0.1";
public string Status => "proposed";
public string[] Comments => new[]
{
"2026-08-20. Заведён по итогам разбора родственного сервиса biblioservice-dispatcher: там пакет после отправки автоматически уходит в фоновую очередь (asyncio-семафор + фоновые задачи), а недоделанные переочередиваются при старте. Переносим подход на .NET-конвейер ICD.",
};
// ADR-008 не отменяется, а расширяется: его тезис «нет пяти подтверждаемых этапов» сохраняется —
// два новых состояния машинные, подтверждать в них нечего.
public Type? Supersedes => null;
public static string S1_Context = $"""
## Контекст
{nameof(ADR_008_PacketStatesAndTwoTabs)} зафиксировал у пакета четыре хранимых состояния и
упразднил пять подтверждаемых этапов. Но машинную половину жизненного цикла он оставил
неявной, и по мере реализации это стало мешать.
**Обработка запускается вручную.** По {nameof(Ban.Sdaid.Icd.Arch.Tech.CSharp.ADR_SRV_005_ImageNormalizationOrthocover)}
нормализация кадров (и, вместе с ней, опознание вида — {nameof(ADR_015_FormPageDetection)})
выполняется синхронно по кнопке «Обработать заново». Оператор обязан помнить, что после сдачи
пакета его надо ещё и запустить; до нажатия пакет лежит нетронутым.
**Состояние «в очереди» перегружено.** Одним значением обозначены и «ждёт, пока его возьмут
в обработку», и «обработан, ждёт, пока его возьмёт оператор». Отличить их можно только по
наличию нормализованных кадров — скрытый признак, из-за которого индикатор очереди
({nameof(Ban.Sdaid.Icd.Requirements.FR_009_PacketQueue)}, «чья очередь ходить») неоднозначен.
**После рестарта ничего не возобновляется.** Синхронная обработка живёт в рамках запроса;
прерванный пакет остаётся полуобработанным без механизма подхвата.
Родственный сервис оцифровки **biblioservice-dispatcher** решает это иначе: пакет после отправки
автоматически ставится в фоновую очередь (in-memory семафор + фоновые задачи), воркер его
обрабатывает под ограничением параллелизма, а при старте приложения недоделанные пакеты
(в статусах «в очереди» / «обрабатывается») переочередиваются. Переносим этот подход,
адаптировав под .NET-конвейер ICD.
""";
public static string S2_Decision = $"""
## Решение
**Обработка запускается автоматически.** При постановке пакета в очередь (отправка из ввода)
он попадает в фоновую очередь и обрабатывается воркером без действия оператора. Оператор
вступает в работу уже с готовым результатом. **Ручной повтор** («Обработать заново») сохраняется
как явная команда — для пересъёмки, правки состава кадров и отклонения вида ИК; в этих случаях
пакет возвращается в очередь по команде пользователя (как требует
{nameof(Ban.Sdaid.Icd.Requirements.FR_009_PacketQueue)}), а не автоматически на каждое движение кадра.
**Очередь — процессная, in-memory.** Канал идентификаторов пакетов + hosted-воркер, ограниченный
параллелизмом (настройка, по умолчанию 1). Сам канал не персистентен — источник истины о том,
что пакет ждёт обработки, это его **состояние в БД**; канал лишь будит воркер. Постановка в канал
выполняется **после фиксации** пакета в БД; воркер обрабатывает пакет в собственной единице работы,
а не в контексте запроса-отправителя.
**Состояний становится шесть.** К прежним — **в очереди**, **проверка**, **завершён**, **ошибка** —
добавляются два машинных:
| Состояние | Когда наступает |
|---|---|
| Обрабатывается | воркер прогоняет нормализацию и опознание вида |
| Готов к проверке | система закончила обработку, пакет ждёт оператора |
Это снимает двойную роль «в очереди»: теперь «в очереди» = «ждёт воркера», «готов к проверке» =
«ждёт оператора». Оба новых состояния **машинные** — пользователь их не подтверждает, переход
происходит сам. Поэтому тезис {nameof(ADR_008_PacketStatesAndTwoTabs)} — «нет пяти подтверждаемых
этапов» — сохраняется: мы делаем наблюдаемой системную половину жизненного цикла, а не возвращаем
подтверждения.
**Машина состояний:**
```
в очереди ──► обрабатывается ──┬─(успех)─► готов к проверке ──► проверка ──► завершён
▲ └─(вид не опознан)─► ошибка │
└──── ручной повтор / отклонение вида / правка состава ◄────────┘
```
**Восстановление при старте.** Пакеты в состояниях «в очереди» и «обрабатывается»
переочередиваются (ставятся в канал заново); «готов к проверке» не трогаются — они уже
обработаны. Управляется настройкой (локально можно отключить, чтобы не перезапускать зависшие
пакеты при отладке).
""";
public static string S3_Rationale = """
## Обоснование
Автоматический запуск убирает лишний шаг и класс ошибок «забыл обработать»: сдача пакета и есть
запрос на обработку. Ручной повтор остаётся не как основной путь, а как явное действие там, где
исходные данные изменились и прежний разбор недействителен, — это ровно тот случай, для которого
FR-009 требует постановки в очередь **командой пользователя**.
Отдельные машинные состояния стоят одного дополнительного перечислимого значения на каждое, зато
делают очередь честной: индикатор «чья очередь ходить» отвечает однозначно, а скрытый признак
«есть ли нормализованные кадры» перестаёт нести смысл состояния. Цена — та же, что принял
ADR-008: состояний по-прежнему наперечёт, переходы явные.
Очередь в памяти процесса выбрана намеренно простой. biblioservice-dispatcher показывает, что
для одного инстанса связки «канал + воркер + семафор + восстановление при старте» достаточно;
вводить брокер или таблицу-очередь означало бы платить за мультиинстанс, которого пока нет.
""";
public static string S4_Consequences = $"""
## Следствия
**Перечисление состояний расширяется до шести** ({nameof(Ban.Sdaid.Icd.Domain.Capture.PacketState)}):
добавляются «обрабатывается» и «готов к проверке». Обновляются доменная сущность
{nameof(Ban.Sdaid.Icd.Domain.Capture.InputPacket)} (таблица состояний, инварианты переходов) и
требование {nameof(Ban.Sdaid.Icd.Requirements.FR_009_PacketQueue)} (индикатор и отбор — по шести
состояниям).
**Запуск нормализации по {nameof(Ban.Sdaid.Icd.Arch.Tech.CSharp.ADR_SRV_005_ImageNormalizationOrthocover)}
уточняется**: основной запуск — автоматический (воркер при постановке в очередь), ручной остаётся
повтором. Абстракция `IImageNormalizer` не меняется.
**Появляется инфраструктурный артефакт очереди** — канал + hosted-воркер + ограничение параллелизма
и хук восстановления при старте (backend-спека / инфраструктура). Хендлер отправки пакета после
фиксации ставит идентификатор в канал; воркер работает в собственной единице работы.
**UI очереди** ({nameof(Ban.Sdaid.Icd.Requirements.FR_009_PacketQueue)} / страница очереди) показывает
новые состояния и не требует от оператора ручного запуска обработки для штатного пути.
**Компромисс — один процесс.** Очередь в памяти не переживает мультиинстанс: у каждого инстанса
своя. Как и локальный кэш контекста в {nameof(ADR_011_AbacAuthorization)}, вынос в общий брокер/таблицу
оставлен точкой расширения на случай горизонтального масштабирования.
""";
public static string S5_Alternatives = """
## Рассмотренные альтернативы
**Оставить запуск ручным (ничего не менять).** Отвергнуто: сохраняется шаг «не забудь обработать»
и перегруженное «в очереди»; наблюдаемости и восстановления нет.
**Не вводить машинные состояния, отличать по наличию нормализованных кадров.** Одно «в очереди»
на оба смысла. Отвергнуто: состояние очереди становится скрытым и выводимым, а индикатор «чья
очередь ходить» — неоднозначным; оператор не видит, обрабатывается пакет или уже готов.
**Брокер (Redis/RabbitMQ) или таблица-очередь в БД.** Отвергнуто на текущем этапе: один инстанс,
in-memory + восстановление при старте достаточно (подтверждено biblioservice-dispatcher). Оставлено
точкой расширения при мультиинстансе — аддитивно, без слома модели состояний.
**Автозапуск без ручного повтора.** Отвергнуто: пересъёмка, правка состава и отклонение вида
требуют явной команды пользователя (FR-009, «Повторная обработка») — иначе каждое движение кадра
запускало бы обработку заново.
""";
public static string S99_Related = $"""
## Связанные артефакты
- {nameof(ADR_008_PacketStatesAndTwoTabs)} — четыре состояния пакета; настоящий ADR расширяет модель до шести, добавляя машинные состояния.
- {nameof(ADR_007_InputWithoutPlan)} — ввод без плана: вид ИК определяет обработка, для которой и заводится очередь.
- {nameof(ADR_015_FormPageDetection)} — опознание формы и страницы кадра (шаг «опознание вида» конвейера); низкая уверенность уводит пакет в «ошибка».
- {nameof(ADR_009_RecognitionModelIntegration)} — извлечение полей уже опознанной формы (следующий шаг конвейера).
- {nameof(Ban.Sdaid.Icd.Arch.Tech.CSharp.ADR_SRV_005_ImageNormalizationOrthocover)} — нормализация orthocover; запуск теперь автоматический (воркер), ручной остаётся повтором.
- {nameof(Ban.Sdaid.Icd.Requirements.FR_009_PacketQueue)} — очередь: индикатор состояний расширяется до шести.
- {nameof(Ban.Sdaid.Icd.Domain.Capture.InputPacket)} / {nameof(Ban.Sdaid.Icd.Domain.Capture.PacketState)} — целевая сущность и перечисление состояний.
""";
}
}