M1: pure normalization engine + default seed
Infrastructure-free normalization engine in Core dispatching on MeterMode: - Cumulative/Generation counters: register deltas, first-reading baseline (Haus 411, Auto 3755), meter swaps (water …861→2 reconciles to 12 via boundary registers OR an explicit amount override), counter resets, anomaly-guarded decreases. - RuntimeCounter: Δhours × rate (fixed/empirical). - ConsumableBalance: tank level-Δ + deliveries → consumption, cm→litre calibration; delivery-only rows before the first dipstick emit nothing. - DirectDelta, and Virtual meters via a small safe arithmetic evaluator (Netz Einsparung = Haus − Netz, Eigenverbrauch = Erzeugung − Einsparung) — data-driven, not hardcoded. - DatabaseSeeder: default energy types, cost categories, base settings (idempotent). 24 Core unit tests + 4 integration tests green. Claude-Session: https://claude.ai/code/session_01WujdMtMJPbxDpDnMeK22rr
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
using MeterVault.Core.Domain;
|
||||
using MeterVault.Core.Normalization;
|
||||
using static MeterVault.Core.Tests.TestData;
|
||||
|
||||
namespace MeterVault.Core.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// The electricity derived columns are data-driven virtual expressions, not hardcoded formulas
|
||||
/// (SDD §2.2, §7.4): Netz Einsparung = Haus − Netz, Anlage Eigenverbrauch = Erzeugung − Einsparung.
|
||||
/// </summary>
|
||||
public sealed class VirtualMeterTests
|
||||
{
|
||||
private readonly INormalizationEngine _engine = NormalizationEngine.CreateDefault();
|
||||
|
||||
private static Consumption Cons(int meterId, DateTimeOffset time, double amount) => new()
|
||||
{
|
||||
MeterId = meterId,
|
||||
Time = time,
|
||||
Amount = amount,
|
||||
Kind = ConsumptionKind.Consumption,
|
||||
Quality = ReadingQuality.Imported,
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void Netz_einsparung_is_haus_minus_netz()
|
||||
{
|
||||
var oct = Month(2022, 10);
|
||||
var nov = Month(2022, 11);
|
||||
var ctx = new NormalizationContext
|
||||
{
|
||||
Meter = new MeterConfig
|
||||
{
|
||||
MeterId = 100,
|
||||
Mode = MeterMode.Virtual,
|
||||
Unit = "kWh",
|
||||
Virtual = new VirtualSpec { Expression = "m1 - m2", ReferencedMeterIds = [1, 2] },
|
||||
},
|
||||
ReferencedSeries = new Dictionary<int, IReadOnlyList<Consumption>>
|
||||
{
|
||||
[1] = [Cons(1, oct, 411), Cons(1, nov, 742)], // Haus Verbrauch
|
||||
[2] = [Cons(2, oct, 416), Cons(2, nov, 832)], // Netz Verbrauch
|
||||
},
|
||||
};
|
||||
|
||||
var result = _engine.Normalize(ctx);
|
||||
|
||||
Assert.Equal([-5d, -90d], result.Select(c => c.Amount));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Eigenverbrauch_is_erzeugung_minus_einsparung()
|
||||
{
|
||||
var oct = Month(2022, 10);
|
||||
var ctx = new NormalizationContext
|
||||
{
|
||||
Meter = new MeterConfig
|
||||
{
|
||||
MeterId = 101,
|
||||
Mode = MeterMode.Virtual,
|
||||
Unit = "kWh",
|
||||
Virtual = new VirtualSpec { Expression = "m50 - m100", ReferencedMeterIds = [50, 100] },
|
||||
},
|
||||
ReferencedSeries = new Dictionary<int, IReadOnlyList<Consumption>>
|
||||
{
|
||||
[50] = [Cons(50, oct, 76)], // Solar Erzeugung
|
||||
[100] = [Cons(100, oct, -5)], // Netz Einsparung (from the previous test)
|
||||
},
|
||||
};
|
||||
|
||||
var result = _engine.Normalize(ctx);
|
||||
|
||||
// 76 − (−5) = 81 (Anlage Eigenverbrauch Okt 2022).
|
||||
Assert.Equal([81d], result.Select(c => c.Amount));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user