diff --git a/csharp/App/SaliMax/src/Program.cs b/csharp/App/SaliMax/src/Program.cs index c8a701823..2df4ddbf3 100644 --- a/csharp/App/SaliMax/src/Program.cs +++ b/csharp/App/SaliMax/src/Program.cs @@ -18,7 +18,6 @@ using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc.Control; using InnovEnergy.Lib.Protocols.Modbus.Channels; using InnovEnergy.Lib.Time.Unix; using InnovEnergy.Lib.Units; -using InnovEnergy.Lib.Units.Power; using InnovEnergy.Lib.Utils; using static InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes.SystemConfig; using AcPower = InnovEnergy.Lib.Units.Composite.AcPower; @@ -210,7 +209,7 @@ internal static class Program WriteControl(record); - CreateTopology(record).WriteLine(); + Topology.From(record).WriteLine(); //await UploadCsv(record, t); @@ -224,179 +223,7 @@ internal static class Program // ReSharper disable once FunctionNeverReturns } - private static TextBlock CreateTopology(StatusRecord s) - { - // Power Measurement Values - var gridPower = s.GridMeter is not null ? s.GridMeter!.Ac.Power.Active : 0; - var ac = s.AcDc.Ac; - var inverterPower = ac.Power.Active; - var islandLoadPower = s.LoadOnAcIsland is not null ? s.LoadOnAcIsland.Ac.Power.Active : 0; - var dcBatteryPower = s.DcDc.Dc.Battery.Power; - var dcdcPower = s.DcDc.Dc.Link.Power; - var pvOnDcPower = s.PvOnDc.Dc!.Power.Value; - - // Power Calculated Values - ActivePower islandToGridBusPower = inverterPower + islandLoadPower; - - var islandBusData = TextBlock.AlignLeft(ac.L1.Power.Active.ToDisplayString(), - ac.L2.Power.Active.ToDisplayString(), - ac.L3.Power.Active.ToDisplayString()); - - var dc48Voltage = s.DcDc.Dc.Battery.Voltage.ToDisplayString(); - - var islandBusPv = 0.W(); // TODO - var islandBusColumn = ColumnBox("Pv" , islandBusPv, - "Island Bus", islandBusData, - "Load" , islandLoadPower); - - var dcBusLoad = 0.W(); // TODO - var dcLinkVoltage = s.DcDc.Dc.Link.Voltage.ToDisplayString(); - var dcBusColumn = ColumnBox("Pv" , pvOnDcPower, - "Dc Bus", dcLinkVoltage, - "Load" , dcBusLoad); - - var inverterAcPhases = s - .AcDc - .Devices - .Select(d => d.Status.Ac.Power) - .ToArray(s.AcDc.Devices.Count) - .AsReadOnlyList(); - - var inverterBox = TextBlock.AlignLeft(inverterAcPhases).TitleBox("AC/DC"); - var dcDcBox = TextBlock.AlignLeft(dc48Voltage) .TitleBox("DC/DC"); - - var bat = s.Battery; - var batteryAvgBox = CreateAveragedBatteryBox(bat); - - var batteryBoxes = bat - .Devices - .Select(CreateIndividualBattery) - .ToArray(bat.Devices.Count) - .AsReadOnlyList(); - - var individualBatteries = batteryBoxes.Any() - ? TextBlock.AlignLeft(batteryBoxes) - : TextBlock.Spacer(1); - - var flowIslandBusToInverter = Flow.Horizontal(inverterPower); - var flowInverterToDcBus = Flow.Horizontal(inverterPower); - var flowDcBusToDcDc = Flow.Horizontal(dcdcPower); - var flowDcDcToBattery = Flow.Horizontal(dcBatteryPower); - - var islandTopology = TextBlock - .AlignCenterVertical - ( - islandBusColumn, - flowIslandBusToInverter, - inverterBox, - flowInverterToDcBus, - dcBusColumn, - flowDcBusToDcDc, - dcDcBox, - flowDcDcToBattery, - batteryAvgBox, - individualBatteries - ); - - if (s.GridMeter is null) - return islandTopology; - - var gridBox = CreateGridBox(s); - var gridToGridBus = Flow.Horizontal(gridPower); - var gridBusColumn = GridBusColumn(s); - var gridBusToIslandBus = Flow.Horizontal(islandToGridBusPower); - - return TextBlock - .AlignCenterVertical - ( - gridBox, - gridToGridBus, - gridBusColumn, - gridBusToIslandBus, - islandTopology - ); - } - - 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 - - var ac = s.GridMeter!.Ac; - - var gridVoltageByPhase = TextBlock.AlignLeft(ac.L1.Voltage.ToDisplayString(), - ac.L2.Voltage.ToDisplayString(), - ac.L3.Voltage.ToDisplayString()); - - return ColumnBox("Pv", 0, "Grid Bus", gridVoltageByPhase, "Load", gridLoadPower); - } - - private static TextBlock CreateGridBox(StatusRecord s) - { - return TextBlock.AlignLeft(s.GridMeter!.Ac.L1.Power.Active.ToDisplayString(), - s.GridMeter!.Ac.L2.Power.Active.ToDisplayString(), - s.GridMeter!.Ac.L3.Power.Active.ToDisplayString()) - .TitleBox("Grid"); - } - - 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); - } - - 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); - } + private static async Task ResultOrNull(this Task task) { @@ -513,5 +340,4 @@ internal static class Program return true; } -} - +} \ No newline at end of file diff --git a/csharp/App/SaliMax/src/Topology.cs b/csharp/App/SaliMax/src/Topology.cs new file mode 100644 index 000000000..37aa0b70a --- /dev/null +++ b/csharp/App/SaliMax/src/Topology.cs @@ -0,0 +1,190 @@ +using System.Diagnostics.CodeAnalysis; +using InnovEnergy.App.SaliMax.Ess; +using InnovEnergy.Lib.Devices.Battery48TL; +using InnovEnergy.Lib.Units; +using InnovEnergy.Lib.Units.Composite; +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) + { + // Power Measurement Values + + var inverterPower = s.AcDc.Ac.Power.Active; + var islandLoadPower = s.LoadOnAcIsland is not null ? s.LoadOnAcIsland.Ac.Power.Active : 0; + var dcBatteryPower = s.DcDc.Dc.Battery.Power; + var dcdcPower = s.DcDc.Dc.Link.Power; + + + // Power Calculated Values + ActivePower islandToGridBusPower = inverterPower + islandLoadPower; + + var islandBusPv = 0.W(); // TODO + var islandBusColumn = ColumnBox("Pv" , islandBusPv, + "Island Bus", s.AcDc.Ac.PhasePowersActive(), + "Load" , islandLoadPower); + + var dcBusLoad = 0.W(); // TODO + var dcLinkVoltage = s.DcDc.Dc.Link.Voltage.ToDisplayString(); + + var pvOnDcPower = s.PvOnDc.Dc!.Power; // TODO ! + var dcBusColumn = ColumnBox("Pv" , pvOnDcPower, + "Dc Bus", dcLinkVoltage, + "Load" , dcBusLoad); + var inverterAcPhases = s + .AcDc + .Devices + .Select(d => d.Status.Ac.Power) + .ToArray(s.AcDc.Devices.Count) + .AsReadOnlyList(); + + var inverterBox = TextBlock.AlignLeft(inverterAcPhases).TitleBox("AC/DC"); + + var dc48Voltage = s.DcDc.Dc.Battery.Voltage.ToDisplayString(); + var dcDcBox = TextBlock.AlignLeft(dc48Voltage) .TitleBox("DC/DC"); + + var bat = s.Battery; + var batteryAvgBox = CreateAveragedBatteryBox(bat); + + var batteryBoxes = bat + .Devices + .Select(CreateIndividualBattery) + .ToArray(bat.Devices.Count) + .AsReadOnlyList(); + + var individualBatteries = batteryBoxes.Any() + ? TextBlock.AlignLeft(batteryBoxes) + : TextBlock.Empty; + + var islandTopology = TextBlock + .AlignCenterVertical + ( + islandBusColumn, Flow.Horizontal(inverterPower), + inverterBox , Flow.Horizontal(inverterPower), + dcBusColumn , Flow.Horizontal(dcdcPower), + dcDcBox , Flow.Horizontal(dcBatteryPower), + batteryAvgBox, + individualBatteries + ); + + if (s.GridMeter is null) + return islandTopology; + + var gridMeterAc = s.GridMeter.Ac; + var gridBox = gridMeterAc.PhasePowersActive().TitleBox("Grid"); + var gridToGridBus = Flow.Horizontal(gridMeterAc.Power.Active); + var gridBusColumn = GridBusColumn(s); + var gridBusToIslandBus = Flow.Horizontal(islandToGridBusPower); + + return TextBlock + .AlignCenterVertical + ( + gridBox, + gridToGridBus, + gridBusColumn, + gridBusToIslandBus, + islandTopology + ); + } + + 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); + } +} \ No newline at end of file