using System;
using System.Collections.Generic;
using Ban.Sdaid.Notation.Documents;
using Ban.Sdaid.Icd.Domain.Processing;
namespace Ban.Sdaid.Icd.Domain.Cards.Template
{
/// <summary>Профиль автоопознания страницы формы: эталон, по которому детектор относит кадр
/// к этой форме и странице (рамка-якорь, ожидаемые числа линий, зоны-различия).</summary>
[DomainEntity(Origin = DomainDataOrigin.Reference)]
public class PageDetectionProfile
{
/// <summary>Идентификатор профиля.</summary>
public Guid Id { get; init; }
/// <summary>Рамка-якорь таблицы бланка (границы, внутри которых считаются линии), % страницы.</summary>
[Relation(Kind = RelationKind.Composition)]
public SourceArea AnchorFrame { get; init; } = null!;
/// <summary>Ожидаемое число горизонтальных линий сетки по всей рамке — грубый признак (сторона листа).</summary>
public int GlobalHLines { get; init; }
/// <summary>Ожидаемое число вертикальных линий сетки по всей рамке.</summary>
public int GlobalVLines { get; init; }
/// <summary>Зоны-различия — места, где редакции бланка расходятся; тонкий признак (какая форма).</summary>
[Relation(Kind = RelationKind.Composition)]
public IReadOnlyList<DiscriminatorZone> Zones { get; init; } = new DiscriminatorZone[0];
}
/// <summary>Зона-различие бланка: место, где счётчик линий разводит редакции формы.</summary>
[DomainEntity(DomainEntityKind.ValueObject, Origin = DomainDataOrigin.Reference)]
public class DiscriminatorZone
{
/// <summary>Имя зоны, как её назвал аналитик при разметке (напр. «Обрез»).</summary>
public string Name { get; init; } = string.Empty;
/// <summary>Прямоугольник зоны, % страницы (та же шкала, что у области поля).</summary>
[Relation(Kind = RelationKind.Composition)]
public SourceArea Area { get; init; } = null!;
/// <summary>Ожидаемое число горизонтальных линий в зоне у этой формы.</summary>
public int ExpectedHLines { get; init; }
/// <summary>Ожидаемое число вертикальных линий в зоне.</summary>
public int ExpectedVLines { get; init; }
}
/// <summary>Описание доменной сущности <see cref="PageDetectionProfile"/>.</summary>
public class PageDetectionProfileSpec : IDomainEntityDocument
{
public static string _refName = "профиль автоопознания страницы";
public Type Entity => typeof(PageDetectionProfile);
public string Name => "Профиль автоопознания страницы";
public string Description =>
@"Эталон, по которому Сервис относит кадр к форме и её странице: рамка-якорь таблицы, ожидаемые числа линий сетки и зоны-различия с их счётчиками линий.";
public string Version => "0.1";
public string Status => "draft";
public string[] Comments => new[]
{
"2026-08-28. Заведено под решение об опознании формы и страницы кадра; данные снимаются на стенде распознавания.",
};
public static string S1_Purpose = """
## Назначение
Прежде чем читать поля, Сервис должен опознать, какой это бланк и какая его сторона —
без этого неизвестны ни состав полей, ни их места. Профиль — эталон для этого опознания:
он несёт признаки раскладки, а не сами поля.
Признаков два. **Грубый** — число линий сетки по всей рамке: у оборота их заметно больше,
чем у лицевой стороны, поэтому счётчик разводит стороны. **Тонкий** — число линий в зонах,
где редакции одного бланка расходятся составом колонок; он разводит формы между собой.
Профиль привязан к странице формы: у каждой формы каждая сторона — свой профиль.
Опознание сравнивает признаки кадра с профилями и выбирает ближайший.
""";
public static string S2_NotFieldGeometry = """
## Профиль — не геометрия полей
Профиль отвечает только за опознание раскладки. Где на странице какое поле читать — знание
поля формы (его размеченная область), а не профиля. Так классификация кадра и чтение значений
разведены: профиль выбирает форму, дальше поля формы задают, что и откуда извлекать.
Разметка профиля — работа аналитика: он размечает рамку, зоны и снимает счётчики на эталонном
скане. Пока форма не снабжена профилем, кадр этой формы автоматически не опознаётся.
""";
public static string S3_Coordinates = """
## Координаты
Рамка-якорь и зоны задаются в **процентах страницы** — той же шкале, что область поля
и область-источник значения, поэтому не зависят от разрешения снимка. Кадр перед опознанием
выравнивается по вертикали; счётчики линий берутся внутри рамки-якоря.
""";
public static string S99_Related = $"""
## Связанные артефакты
- {nameof(Ban.Sdaid.Icd.Arch.Adr.ADR_015_FormPageDetection)} — решение об опознании формы и страницы кадра, для которого профиль служит эталоном.
- {nameof(Ban.Sdaid.Icd.Epics.RecognitionStand.EPIC_RecognitionStand)} — стенд, на котором профиль размечается и обкатывается.
- {nameof(Ban.Sdaid.Icd.Domain.Cards.Template.FormField)} — поле формы: геометрия чтения значений (в отличие от профиля, отвечающего за опознание).
""";
}
/// <summary>Описание доменной сущности <see cref="DiscriminatorZone"/>.</summary>
public class DiscriminatorZoneSpec : IDomainEntityDocument
{
public Type Entity => typeof(DiscriminatorZone);
public string Name => "Зона-различие";
public string Description =>
@"Место на бланке, где редакции формы расходятся; счётчик линий в зоне разводит формы при опознании.";
public string Version => "0.1";
public string Status => "draft";
public string[] Comments => new string[0];
public static string S1_Purpose = """
## Назначение
Одной стороны мало, чтобы отличить редакции бланка: у них совпадает общая раскладка,
а расходится состав колонок в отдельных местах. Зона отмечает такое место, а её счётчики
линий — то, чем редакции в нём различаются (например, разное число колонок в графе «Обрез»).
Зоны размечаются в системе координат страницы, чтобы одно и то же место сопоставлялось
у всех форм независимо от того, как кадр лёг под съёмкой.
""";
}
}