using System.Diagnostics.CodeAnalysis; using InnovEnergy.App.SaliMax.Ess; using InnovEnergy.Lib.Devices.Battery48TL; using InnovEnergy.Lib.Units; using InnovEnergy.Lib.Units.Power; using InnovEnergy.Lib.Utils; using Ac3Bus = InnovEnergy.Lib.Units.Composite.Ac3Bus; namespace InnovEnergy.App.SaliMax; public static class Topology { public static TextBlock From(this StatusRecord s) { // Topology is built up from right to left // Power Measurement Values var inverterPower = s.AcDc.Ac.Power.Active; var islandLoadPower = s.LoadOnAcIsland is not null ? s.LoadOnAcIsland.Ac.Power.Active : 0; // TODO var islandTopology = CreateIslandTopology(s, islandLoadPower, inverterPower); if (s.GridMeter is null) // no grid meter? return islandTopology; // we're done var gridBusColumn = GridBusColumn(s); var gridBox = CreateGridBox(s); ActivePower islandToGridBusPower = inverterPower + islandLoadPower; // return TextBlock // .AlignCenterVertical // ( // gridBox , Flow.Horizontal(s.GridMeter.Ac.Power.Active), // gridBusColumn, Flow.Horizontal(islandToGridBusPower), // islandTopology // ); var gridTopology = TextBlock .AlignCenterVertical ( gridBox, Flow.Horizontal(s.GridMeter.Ac.Power.Active), gridBusColumn ); return TextBlock.AlignCenterVertical ( gridTopology, Flow.Horizontal(islandToGridBusPower), islandTopology ); } private static TextBlock CreateIslandTopology(StatusRecord s, ActivePower islandLoadPower, ActivePower inverterPower) { var dcBatteryPower = s.DcDc.Dc.Battery.Power; var dcdcPower = s.DcDc.Dc.Link.Power; var batteries = CreateBatteryColumn(s); var dcBusColumn = CreateDcBusColumn(s); var islandBusColumn = CreateIslandBusColumn(s, islandLoadPower); var inverterBox = CreateInverterBox(s); var dcDcBox = CreateDcDcBox(s); return TextBlock .AlignCenterVertical ( islandBusColumn, Flow.Horizontal(inverterPower), inverterBox , Flow.Horizontal(inverterPower), dcBusColumn , Flow.Horizontal(dcdcPower), dcDcBox , Flow.Horizontal(dcBatteryPower), batteries ); } private static TextBlock CreateGridBox(StatusRecord statusRecord) { return statusRecord .GridMeter! .Ac .PhasePowersActive() .TitleBox("Grid"); } private static TextBlock CreateDcDcBox(StatusRecord s) { var dc48Voltage = s.DcDc.Dc.Battery.Voltage.ToDisplayString(); return TextBlock .AlignLeft(dc48Voltage) .TitleBox("DC/DC"); } private static TextBlock CreateInverterBox(StatusRecord s) { var inverterAcPhases = s .AcDc .Devices .Select(d => d.Status.Ac.Power) .ToReadOnlyList(); return TextBlock .AlignLeft(inverterAcPhases) .TitleBox("AC/DC"); } private static TextBlock CreateIslandBusColumn(StatusRecord s, ActivePower islandLoadPower) { var islandBusPv = 0.W(); // TODO return ColumnBox ( "Pv", islandBusPv, "Island Bus", s.AcDc.Ac.PhasePowersActive(), "Load", islandLoadPower ); } private static TextBlock CreateDcBusColumn(StatusRecord s) { var dcBusLoad = 0.W(); // TODO var pvOnDcPower = s.PvOnDc.Dc!.Power; // TODO ! var dcLinkVoltage = s.DcDc.Dc.Link.Voltage.ToDisplayString(); return ColumnBox ( "Pv" , pvOnDcPower, "Dc Bus", dcLinkVoltage, "Load" , dcBusLoad ); } private static TextBlock CreateBatteryColumn(StatusRecord s) { var bat = s.Battery; var batteryAvgBox = CreateAveragedBatteryBox(bat); var batteryBoxes = bat .Devices .Select(CreateIndividualBattery) .ToReadOnlyList(); var individualBatteries = batteryBoxes.Any() ? TextBlock.AlignLeft(batteryBoxes) : TextBlock.Empty; return TextBlock .AlignCenterVertical ( batteryAvgBox //, //individualBatteries // TODO ); } private static TextBlock CreateAveragedBatteryBox(Battery48TlRecords bat) { var batteryVoltage = bat.Dc.Voltage.ToDisplayString(); var batterySoc = bat.Devices.Any() ? bat.Devices.Average(b => b.Soc).Percent().ToDisplayString() : "0"; var batteryCurrent = bat.Dc.Current.ToDisplayString(); var batteryTemp = bat.Temperature.ToDisplayString(); var batteryHeatingCurrent = bat.HeatingCurrent.ToDisplayString(); var alarms = bat.Alarms.Count + " Alarms"; var warnings = bat.Warnings.Count + " Warnings"; return TextBlock .AlignLeft ( batteryVoltage, batterySoc, batteryCurrent, batteryTemp, batteryHeatingCurrent, warnings, alarms ) .TitleBox("Battery"); } private static TextBlock GridBusColumn(StatusRecord s) { var gridLoadPower = s.LoadOnAcGrid is not null ? s.LoadOnAcGrid.Power.Active : 0; // TODO: show that LoadOnAcGrid is actually not available and not 0 return ColumnBox ( "Pv", 0, "Grid Bus", s.GridMeter!.Ac.PhaseVoltages(), "Load", gridLoadPower ); } private static TextBlock PhaseVoltages(this Ac3Bus ac) { return TextBlock.AlignLeft ( ac.L1.Voltage.ToDisplayString(), ac.L2.Voltage.ToDisplayString(), ac.L3.Voltage.ToDisplayString() ); } private static TextBlock PhasePowersActive(this Ac3Bus ac) { return TextBlock.AlignLeft ( ac.L1.Power.Active.ToDisplayString(), ac.L2.Power.Active.ToDisplayString(), ac.L3.Power.Active.ToDisplayString() ); } private static TextBlock CreateIndividualBattery(Battery48TlRecord battery, Int32 i) { var batteryWarnings = battery.Warnings.Any(); var batteryAlarms = battery.Alarms.Any(); var content = TextBlock.AlignLeft ( battery.Dc.Voltage.ToDisplayString(), battery.Soc.ToDisplayString(), battery.Dc.Current.ToDisplayString() + " C/D", battery.Temperatures.Cells.Average.ToDisplayString(), battery.BusCurrent.ToDisplayString() + " T", batteryWarnings, batteryAlarms, battery.HeatingCurrent.ToDisplayString() + " H" ); var box = content.TitleBox($"Battery {i + 1}"); var flow = Flow.Horizontal(battery.Dc.Power); return TextBlock.AlignCenterVertical(flow, box); } [SuppressMessage("ReSharper", "SuggestBaseTypeForParameter")] private static TextBlock ColumnBox(String pvTitle , ActivePower pvPower, String busTitle , Object busData, String loadTitle, ActivePower loadPower) { var pvBox = TextBlock.FromString(pvTitle).Box(); var pvToBus = Flow.Vertical(pvPower); var busBox = TextBlock.AlignLeft(busData).TitleBox(busTitle); var busToLoad = Flow.Vertical(loadPower); var loadBox = TextBlock.FromString(loadTitle).Box(); return TextBlock.AlignCenterHorizontal(pvBox, pvToBus, busBox, busToLoad, loadBox); } }