using MeterVault.Core.Analysis; using MeterVault.Core.Analysis.Quantities; using MeterVault.Core.Analysis.Totals; using MeterVault.Core.Domain; namespace MeterVault.Core.Tests.Analysis; /// /// The reference data's topology as the totals policy sees it (ReferenceDataImporter): five electricity meters /// and the Summe Solar view, the water meter, and the oil tank with its burner. Ids follow the seed's creation /// order; the energy types are electricity 1, water 2, heating oil 3. /// /// /// Roles are given as the stored tokens and turned into through /// , exactly as the reader does (A-07) — so a test that stores /// "GRID_IMPORT" or a role on a tank sees what production would. /// internal static class TotalsSeed { public const int Electricity = 1; public const int Water = 2; public const int Oil = 3; public const int Haus = 1; public const int Netz = 2; public const int Auto = 3; public const int Solar1 = 4; public const int Solar2 = 5; public const int Wasser = 6; public const int Oeltank = 7; public const int Brenner = 8; public const int SummeSolar = 9; /// Solar 1 → Summe, Solar 2 → Summe, Netz → Haus, Summe → Haus, Haus → Auto. public static List Links() => [ new(Solar1, SummeSolar), new(Solar2, SummeSolar), new(Netz, Haus), new(SummeSolar, Haus), new(Haus, Auto), ]; public static List Meters() => [ Physical(Haus, "Zähler Haus", Electricity, MeterMode.CumulativeCounter, "kWh", MeterRoles.TotalLoad), Physical(Netz, "Zähler Netz", Electricity, MeterMode.CumulativeCounter, "kWh", MeterRoles.GridImport), Physical(Auto, "Zähler Auto", Electricity, MeterMode.CumulativeCounter, "kWh"), Physical(Solar1, "Zähler Solar 1", Electricity, MeterMode.GenerationCounter, "kWh"), Physical(Solar2, "Zähler Solar 2", Electricity, MeterMode.GenerationCounter, "kWh"), Physical(Wasser, "Zähler Wasser", Water, MeterMode.CumulativeCounter, "m³"), Physical(Oeltank, "Öltank", Oil, MeterMode.ConsumableBalance, "L"), Physical(Brenner, "Brenner", Oil, MeterMode.RuntimeCounter, "h"), Virtual(SummeSolar, "Summe Solar", Electricity, "kWh", QuantityKind.Generation, [Solar1, Solar2], pureSum: true), ]; /// The seed with applied to the meter . public static List MetersWith(int id, Func adjust) => [.. Meters().Select(m => m.Id == id ? adjust(m) : m)]; public static List MetersWithOverride(params (int Id, TotalsOverride Override)[] overrides) => [.. Meters().Select(m => overrides.Any(o => o.Id == m.Id) ? m with { Override = overrides.First(o => o.Id == m.Id).Override } : m)]; /// The stored role token; the meter gets the effective role, as the reader gives it. public static TotalsMeter Physical( int id, string name, int energyTypeId, MeterMode mode, string unit, string? role = null, TotalsOverride totals = TotalsOverride.Auto, QuantityKind? kind = null, DateOnly? installedAt = null, DateOnly? retiredAt = null) { var effective = MeterRoleRules.Effective(mode, role); return new(id, name, energyTypeId, mode, effective, kind ?? KindOf(mode, effective), unit, false, totals, installedAt, retiredAt, [], false, null); } /// The declared result kind; null for a definition that is not usable. /// A stored role token. A virtual meter never holds one (A-07), so it always reads as none. public static TotalsMeter Virtual( int id, string name, int energyTypeId, string unit, QuantityKind? resultKind, IReadOnlyList sources, bool pureSum, string? role = null, TotalsOverride totals = TotalsOverride.Auto) => new(id, name, energyTypeId, MeterMode.Virtual, MeterRoleRules.Effective(MeterMode.Virtual, role), resultKind ?? QuantityKind.Consumption, unit, true, totals, null, null, sources, pureSum, resultKind); // Mirrors NormalizedQuantity: an effective grid_export role makes a flow meter measure export. private static QuantityKind KindOf(MeterMode mode, MeterRole? role) => mode switch { MeterMode.GenerationCounter => QuantityKind.Generation, MeterMode.RuntimeCounter => QuantityKind.Runtime, _ when role == MeterRole.GridExport => QuantityKind.Export, _ => QuantityKind.Consumption, }; }