using System;
using System.Collections.Generic;
using Rugrid.Sdaid.Notation.Documents;
using Rugrid.Sdaid.Epas.Nts.Domain;
namespace Rugrid.Sdaid.Epas.Nts.Backend.Calc
{
/// <summary>
/// Модель входных данных расчёта нормативного расхода топлива. Класс — единственный источник:
/// таблица исходных данных методики, соответствие «доменный атрибут → параметр расчёта»
/// и контракт входной модели строятся по нему. Состав — по ветке лёгкового автомобиля;
/// по мере разбора следующих веток состав расширяется.
/// </summary>
public class DOC_InputModel : IDomainEntityDocument, IHasStructuralLinks, IFolder<NtsBackendFolder>
{
public SdaidLink[] Links => new[]
{
Rel.Uses<Rugrid.Sdaid.Epas.Nts.Domain.DOC_Vehicle>(),
Rel.Uses<Rugrid.Sdaid.Epas.Nts.Domain.DOC_SelectedModifications>(),
Rel.Uses<Rugrid.Sdaid.Epas.Nts.Domain.DOC_VehicleModification>(),
};
public static string _refName = "Модель входных данных расчёта ЭНТС";
public string Name => "Модель входных данных расчёта";
public string Description =>
@"Исходные данные расчёта нормативного расхода топлива: обозначение в формулах, единица измерения, диапазон и источник значения.";
public string Version => "0.2";
public string Status => "draft";
public string[] Comments => new[]
{
"Исходные данные описаны классом, а не таблицей: в ЭНЗС тот же состав ведётся тремя рукописными таблицами (исходные данные методики, контракт InputModel, маппинг), и они расходятся. Источник значения задан [CalcSource] через typeof/nameof, поэтому переименование доменного атрибута ломает сборку.",
"Шесть надбавок (PopulationAllowance, TechStopAllowance, LowSpeedAllowance, SpecialVehicleAllowance, EmergencyAllowance, ForcedIdleAllowance) — decimal (не nullable) здесь, а на Vehicle — decimal? (nullable). Это осознанно: на Vehicle отсутствие значения — «не задано», во входной модели отсутствие уже заменено на 0 при маппинге (поле на Vehicle — «по умолчанию 0»). CalcSource не проверяет совместимость типов, несовпадение nullable — не повод исправлять в одну из сторон.",
"`CalcSourceAttribute` выражает только один шаг до одного скалярного поля одной связи — не цепочку из нескольких шагов (VehicleKind, FuelType), не коллекцию строк (BaseConsumptionRates, ReferenceGroupConsumptionRates), не фильтр по условию (ReferenceGroupConsumptionRates). Где реальный путь шире, атрибут указывает на ближайшее однозначное приближение, а точный путь — в Description самого поля; это ограничение самого атрибута, не повторяется в каждом Description заново.",
};
public Type Entity => typeof(InputModel);
public static string Reading = """"
## Как читать таблицу
- **Обозначение** — как величина названа в формулах методики. Пусто — величина в формулы
не входит, а выбирает ветку расчёта.
- **Источник** — доменный атрибут, из которого заполняется параметр. Пусто — значения нет
в АИС: оно вводится пользователем в формах модуля, берётся из справочника или принимается
по умолчанию согласно методике. Чем именно — сказано в описании параметра.
"""";
}
/// <summary>
/// Исходные данные расчёта нормативного расхода топлива для одного транспортного средства
/// за период (ветка лёгкового автомобиля).
/// </summary>
[DomainEntity]
public class InputModel
{
/// <summary>Вид ТС</summary>
[DomainField(RequiredForCalc = "Да",
Description = "Выбирает формулу расчёта: лёгковой, автобус, грузовой бортовой и тягач, самосвал, фургон, специальный. " +
"Реальный путь: `Vehicle.VehicleKind.Code` — значение, с которым сравнивает методика " +
"(см. `fuel.category_by_kind`), не наименование; `CalcSource` указывает на саму связь `Vehicle.VehicleKind` (см. ограничение атрибута — Comments)")]
[CalcSource(typeof(Vehicle), nameof(Vehicle.VehicleKind))]
public string? VehicleKind { get; set; }
/// <summary>Вид топлива</summary>
[DomainField(RequiredForCalc = "Да", Range = "{бензин, дизельное топливо}",
Description = "Для лёгкового расход не зависит от топлива; используется в ветках грузовых и самосвалов. " +
"Реальный путь: `Vehicle.FuelKindPrimary.Name` — фактическое значение, с которым сравнивает методика " +
"(см. `fuel.h_transport`/`fuel.h_trailer`); `CalcSource` указывает на саму связь (см. ограничение атрибута — Comments)")]
[CalcSource(typeof(Vehicle), nameof(Vehicle.FuelKindPrimary))]
public string? FuelType { get; set; }
/// <summary>Базовые нормы отобранных модификаций, л/100 км</summary>
[DomainField(Unit = "л/100 км", Range = "список, ≥ 1 элемента", RequiredForCalc = "Да",
Description = "Базовые нормы отобранных для ТС модификаций справочника «Марка, модель и модификация ТС»; по ним секция `fuel.base_norm` вычисляет минимальную и максимальную базовые нормы и саму базовую норму H_s. " +
"Реальный путь: `VehicleModification.NormPerMileage` каждой из строк `SelectedModifications`, отобранных " +
"для данного ТС (коллекция, не одна строка); `CalcSource` указывает на связь одной строки отбора — " +
"`SelectedModifications.VehicleModification` (см. ограничение атрибута — Comments)")]
[CalcSource(typeof(SelectedModifications), nameof(SelectedModifications.VehicleModification))]
public IReadOnlyList<decimal>? BaseConsumptionRates { get; set; }
/// <summary>Базовые нормы всей базовой группы (вид ТС + вид топлива), л/100 км</summary>
[DomainField(Unit = "л/100 км", Range = "список, ≥ 1 элемента", RequiredForCalc = "Да",
Description = "Базовые нормы ВСЕХ модификаций базовой группы ТС (вид ТС + вид топлива, мягко суженной по топливу), не только отобранных для ТС; по ним секция `fuel.reference_norm` вычисляет эталонную норму H_эт = min. Пустая группа — та же ошибка, что у `BaseConsumptionRates` («Базовая норма не определена»). " +
"Реальный путь: `VehicleModification.NormPerMileage` у ВСЕХ строк справочника, отобранных процедурой " +
"подбора базовой группы по виду ТС и виду топлива (см. Nts.Backend.ModificationSelection.DOC_ModificationGroupSelection, " +
"раздел «Базовая группа») — фильтр по условию, не связь одной строки, как у `BaseConsumptionRates`; " +
"`CalcSource` указывает на само поле `VehicleModification.NormPerMileage` (см. ограничение атрибута — Comments)")]
[CalcSource(typeof(Rugrid.Sdaid.Epas.Nts.Domain.VehicleModification), nameof(Rugrid.Sdaid.Epas.Nts.Domain.VehicleModification.NormPerMileage))]
public IReadOnlyList<decimal>? ReferenceGroupConsumptionRates { get; set; }
/// <summary>Пробег за период, км</summary>
[DomainField(Symbol = "S", Unit = "км", Range = "вещественное, > 0 или не передано",
Description = "Задаётся вызывающей стороной расчёта, не только картой ТС — например, " +
"формированием отчёта реальным фактическим пробегом за период. Не передано (`null`) " +
"— секция `fuel.mileage` сама подставляет эталон (удельный расход) — 100 км; передано, " +
"но ≤ 0 — ошибка валидации. Карточка ТС это поле не задаёт и всегда получает эталон")]
public decimal? Mileage { get; set; }
/// <summary>Грузоподъёмность, т</summary>
[DomainField(Symbol = "q", Unit = "т", Range = "вещественное, ≥ 0",
Description = "Для грузовых: задаёт массу груза (секция `fuel.g_cargo`)")]
[CalcSource(typeof(Vehicle), nameof(Vehicle.CargoCapacity))]
public decimal? CargoCapacity { get; set; }
/// <summary>Собственная масса прицепа (полуприцепа), т</summary>
[DomainField(Symbol = "G_{пр}", Unit = "т", Range = "вещественное, ≥ 0",
Description = "Для грузовых; вводится в модуле, по умолчанию 0")]
[CalcSource(typeof(Vehicle), nameof(Vehicle.TrailerMass))]
public decimal? TrailerMass { get; set; }
/// <summary>Количество поездок с грузом за смену, шт</summary>
[DomainField(Symbol = "Z", Unit = "шт.", Range = "целое, ≥ 0",
Description = "Для самосвалов; вводится в модуле, по умолчанию 0")]
[CalcSource(typeof(Vehicle), nameof(Vehicle.TripsWithCargoPerShift))]
public int? TripsWithCargoPerShift { get; set; }
/// <summary>Норма расхода топлива на транспортную работу автомобиля-самосвала, л/100 т·км</summary>
[DomainField(Symbol = "H_w", Unit = "л/100 т·км", Range = "вещественное, ≥ 0",
Description = "Для самосвалов; вводится в модуле")]
[CalcSource(typeof(Vehicle), nameof(Vehicle.DumpTruckTransportWorkConsumptionRate))]
public decimal? DumpTruckTransportWorkConsumptionRate { get; set; }
/// <summary>Норма расхода топлива на работу отопителя, л/ч</summary>
[DomainField(Symbol = "H_{от}", Unit = "л/ч", Range = "вещественное, ≥ 0",
Description = "Для автобусов; вводится в модуле, по умолчанию 0")]
[CalcSource(typeof(Vehicle), nameof(Vehicle.ConsumptionRateForUsingHeater))]
public decimal? ConsumptionRateForUsingHeater { get; set; }
/// <summary>Время работы автомобиля с включённым отопителем, ч</summary>
[DomainField(Symbol = "T", Unit = "ч", Range = "вещественное, ≥ 0",
Description = "Для автобусов; вводится в модуле, по умолчанию 0")]
[CalcSource(typeof(Vehicle), nameof(Vehicle.TimeWhenUsingHeater))]
public decimal? HeaterTime { get; set; }
/// <summary>Норма расхода топлива на час работы, летняя, л/ч</summary>
[DomainField(Symbol = "H_т^{лето}", Unit = "л/ч", Range = "вещественное, ≥ 0",
Description = "Для специальных автомобилей; из АИС ОИА ПЭО")]
[CalcSource(typeof(Vehicle), nameof(Vehicle.ConsumptionRatePerHourSummer))]
public decimal? ConsumptionRatePerHourSummer { get; set; }
/// <summary>Норма расхода топлива на час работы, зимняя, л/ч</summary>
[DomainField(Symbol = "H_т^{зима}", Unit = "л/ч", Range = "вещественное, ≥ 0",
Description = "Для специальных автомобилей; из АИС ОИА ПЭО")]
[CalcSource(typeof(Vehicle), nameof(Vehicle.ConsumptionRatePerHourWinter))]
public decimal? ConsumptionRatePerHourWinter { get; set; }
/// <summary>Дата начала эксплуатации</summary>
[DomainField(Symbol = "StartDate", Range = "дата", RequiredForCalc = "Да",
Description = "Вместе с отчётной датой задаёт срок эксплуатации для надбавки D_{эксп}")]
[CalcSource(typeof(Vehicle), nameof(Vehicle.StartDate))]
public DateTime? StartDate { get; set; }
/// <summary>Отчётная дата</summary>
[DomainField(Symbol = "ReportDate", Range = "дата", RequiredForCalc = "Да",
Description = "Дата расчёта; для расчёта берётся текущая дата")]
public DateTime? ReportDate { get; set; }
/// <summary>Предельная величина зимних надбавок не более, %</summary>
[DomainField(Symbol = "D_{район}", Unit = "%", Range = "вещественное, ≥ 0", RequiredForCalc = "Да",
Description = "Предельная величина зимней надбавки для субъекта РФ, в котором эксплуатируется ТС; равна поправочному коэффициенту на климатический район D_{район}. Выбирается из справочника «Предельные значения зимних надбавок» по субъекту РФ эксплуатации ТС")]
public decimal? ClimateRegionAllowance { get; set; }
/// <summary>Поправочный коэффициент на численность населения</summary>
[DomainField(Symbol = "D_{нас}", Unit = "%", Range = "вещественное, ≥ 0",
Description = "Поправочный коэффициент на численность населения, %; вводится в модуле, по умолчанию 0")]
[CalcSource(typeof(Vehicle), nameof(Vehicle.PopulationAllowance))]
public decimal PopulationAllowance { get; set; }
/// <summary>Поправочный коэффициент на частые технологические остановки</summary>
[DomainField(Symbol = "D_{ост}", Unit = "%", Range = "вещественное, ≥ 0",
Description = "Поправочный коэффициент на частые технологические остановки, %; вводится в модуле, по умолчанию 0")]
[CalcSource(typeof(Vehicle), nameof(Vehicle.TechStopAllowance))]
public decimal TechStopAllowance { get; set; }
/// <summary>Поправочный коэффициент при движении с пониженной скоростью</summary>
[DomainField(Symbol = "D_{пон}", Unit = "%", Range = "вещественное, ≥ 0",
Description = "Поправочный коэффициент при движении с пониженной скоростью, %; вводится в модуле, по умолчанию 0")]
[CalcSource(typeof(Vehicle), nameof(Vehicle.LowSpeedAllowance))]
public decimal LowSpeedAllowance { get; set; }
/// <summary>Поправочный коэффициент при работе специальных автомобилей</summary>
[DomainField(Symbol = "D_{спец}", Unit = "%", Range = "вещественное, ≥ 0",
Description = "Поправочный коэффициент при работе специальных автомобилей, %; вводится в модуле, по умолчанию 0")]
[CalcSource(typeof(Vehicle), nameof(Vehicle.SpecialVehicleAllowance))]
public decimal SpecialVehicleAllowance { get; set; }
/// <summary>Поправочный коэффициент при работе в условиях ЧС</summary>
[DomainField(Symbol = "D_{чс}", Unit = "%", Range = "вещественное, ≥ 0",
Description = "Поправочный коэффициент при работе в условиях ЧС, %; вводится в модуле, по умолчанию 0")]
[CalcSource(typeof(Vehicle), nameof(Vehicle.EmergencyAllowance))]
public decimal EmergencyAllowance { get; set; }
/// <summary>Поправочный коэффициент при вынужденном простое</summary>
[DomainField(Symbol = "D_{прос}", Unit = "%", Range = "вещественное, ≥ 0",
Description = "Поправочный коэффициент при вынужденном простое, %; вводится в модуле, по умолчанию 0")]
[CalcSource(typeof(Vehicle), nameof(Vehicle.ForcedIdleAllowance))]
public decimal ForcedIdleAllowance { get; set; }
}
}