From df9de4c9edab6df3799b39717daf6b8c3f768f1d Mon Sep 17 00:00:00 2001 From: ig Date: Wed, 16 Aug 2023 15:15:45 +0200 Subject: [PATCH] Cleanup text based Topology --- csharp/App/SaliMax/src/Flow.cs | 16 +- csharp/App/SaliMax/src/Program.cs | 245 +++++++++++++++--------------- 2 files changed, 136 insertions(+), 125 deletions(-) diff --git a/csharp/App/SaliMax/src/Flow.cs b/csharp/App/SaliMax/src/Flow.cs index 0d173bc02..763c7cee2 100644 --- a/csharp/App/SaliMax/src/Flow.cs +++ b/csharp/App/SaliMax/src/Flow.cs @@ -12,7 +12,9 @@ public static class Flow private static readonly String DownArrowChar = "V"; private static readonly String UpArrowChar = "^"; - public static TextBlock Horizontal(Unit amount, Int32 width = 10) + public static TextBlock Horizontal(Unit amount) => Horizontal(amount, 10); + + public static TextBlock Horizontal(Unit amount, Int32 width) { var label = amount.ToDisplayString(); var arrowChar = amount.Value < 0 ? LeftArrowChar : RightArrowChar; @@ -21,19 +23,21 @@ public static class Flow // note : appending "fake label" below to make it vertically symmetric return TextBlock.AlignCenterHorizontal(label, arrow, ""); } - + + public static TextBlock Vertical(Unit amount) => Vertical(amount, 4); + [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] [SuppressMessage("ReSharper", "CoVariantArrayConversion")] - public static TextBlock Vertical(Unit amount, Int32 height = 4) + public static TextBlock Vertical(Unit amount, Int32 height) { var label = amount.ToDisplayString(); var arrowChar = amount.Value < 0 ? UpArrowChar : DownArrowChar; var halfArrow = Enumerable.Repeat(arrowChar, height/2); var lines = halfArrow - .Append(label) - .Concat(halfArrow) - .ToArray(height / 2 * 2 + 1); + .Append(label) + .Concat(halfArrow) + .ToArray(height / 2 * 2 + 1); return TextBlock.AlignCenterHorizontal(lines); } diff --git a/csharp/App/SaliMax/src/Program.cs b/csharp/App/SaliMax/src/Program.cs index 16bed4f77..c8a701823 100644 --- a/csharp/App/SaliMax/src/Program.cs +++ b/csharp/App/SaliMax/src/Program.cs @@ -1,6 +1,5 @@ using System.Reactive.Linq; using System.Reactive.Threading.Tasks; -using System.Runtime.InteropServices; using Flurl.Http; using InnovEnergy.App.SaliMax.Ess; using InnovEnergy.App.SaliMax.SaliMaxRelays; @@ -211,7 +210,7 @@ internal static class Program WriteControl(record); - PrintTopology(record); + CreateTopology(record).WriteLine(); //await UploadCsv(record, t); @@ -225,133 +224,144 @@ internal static class Program // ReSharper disable once FunctionNeverReturns } - private static void PrintTopology(StatusRecord s) + private static TextBlock CreateTopology(StatusRecord s) { // Power Measurement Values - var gridPower = s.GridMeter is not null ? s.GridMeter!.Ac.Power.Active : 0; - 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; - var pvOnDcPower = s.PvOnDc.Dc!.Power.Value; + 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 - var islandToGridBusPower = inverterPower + islandLoadPower; - var gridLoadPower = s.LoadOnAcGrid is null ? 0: s.LoadOnAcGrid.Power.Active; + ActivePower islandToGridBusPower = inverterPower + islandLoadPower; - TextBlock gridBusColumn; - TextBlock gridBox; - TextBlock totalBoxes; + 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); - if (s.GridMeter is not null) - { - var gridPowerByPhase = TextBlock.AlignLeft(s.GridMeter.Ac.L1.Power.Active.ToDisplayString(), - s.GridMeter.Ac.L2.Power.Active.ToDisplayString(), - s.GridMeter.Ac.L3.Power.Active.ToDisplayString()); - - var gridVoltageByPhase = TextBlock.AlignLeft(s.GridMeter.Ac.L1.Voltage.ToDisplayString(), - s.GridMeter.Ac.L2.Voltage.ToDisplayString(), - s.GridMeter.Ac.L3.Voltage.ToDisplayString()); - - gridBusColumn = ColumnBox("Pv", "Grid Bus", "Load" , gridVoltageByPhase , gridLoadPower); - gridBox = TextBlock.AlignLeft(gridPowerByPhase).TitleBox("Grid"); - - } - else - { - gridBusColumn = TextBlock.Spacer(0); - gridBox = TextBlock.Spacer(0); - } - + var dcBusLoad = 0.W(); // TODO + var dcLinkVoltage = s.DcDc.Dc.Link.Voltage.ToDisplayString(); + var dcBusColumn = ColumnBox("Pv" , pvOnDcPower, + "Dc Bus", dcLinkVoltage, + "Load" , dcBusLoad); - - var inverterPowerByPhase = TextBlock.AlignLeft(s.AcDc.Ac.L1.Power.Active.ToDisplayString(), - s.AcDc.Ac.L2.Power.Active.ToDisplayString(), - s.AcDc.Ac.L3.Power.Active.ToDisplayString()); - - // ReSharper disable once CoVariantArrayConversion - var inverterPowerByAcDc = TextBlock.AlignLeft(s.AcDc.Devices - .Select(s1 => s1.Status.Ac.Power) - .ToArray()); - - var dcLinkVoltage = TextBlock.AlignCenterHorizontal("", - s.DcDc.Dc.Link.Voltage.ToDisplayString(), - ""); - - var dc48Voltage = s.DcDc.Dc.Battery.Voltage.ToDisplayString(); - var batteryVoltage = s.Battery.Dc.Voltage.Value.RoundToSignificantDigits(3); - var batterySoc = s.Battery.Devices.Any()? s.Battery.Devices.Average(b=>b.Soc).Percent().ToDisplayString() : "0"; - var batteryCurrent = s.Battery.Dc.Current.ToDisplayString(); - var batteryTemp = s.Battery.Temperature.ToDisplayString(); - var batteryHeatingCurrent = s.Battery.HeatingCurrent.ToDisplayString(); - var anyBatteryAlarm = s.Battery.Alarms.Any(); - var anyBatteryWarning = s.Battery.Warnings.Any(); + var inverterAcPhases = s + .AcDc + .Devices + .Select(d => d.Status.Ac.Power) + .ToArray(s.AcDc.Devices.Count) + .AsReadOnlyList(); - var islandBusColumn = ColumnBox("Pv", "Island Bus", "Load" , inverterPowerByPhase, islandLoadPower); - var dcBusColumn = ColumnBox("Pv", "Dc Bus", "Load" , dcLinkVoltage, 0, pvOnDcPower); - var gridBusFlow = Flow.Horizontal(gridPower); - var flowGridBusToIslandBus = Flow.Horizontal((ActivePower)islandToGridBusPower); - var flowIslandBusToInverter = Flow.Horizontal(inverterPower); - var flowInverterToDcBus = Flow.Horizontal(inverterPower); - var flowDcBusToDcDc = Flow.Horizontal(dcdcPower); - var flowDcDcToBattery = Flow.Horizontal(dcBatteryPower); + var inverterBox = TextBlock.AlignLeft(inverterAcPhases).TitleBox("AC/DC"); + var dcDcBox = TextBlock.AlignLeft(dc48Voltage) .TitleBox("DC/DC"); - var inverterBox = TextBlock.AlignLeft(inverterPowerByAcDc).TitleBox("AC/DC"); - var dcDcBox = TextBlock.AlignLeft(dc48Voltage).TitleBox("DC/DC"); - var batteryAvgBox = TextBlock.AlignLeft(batteryVoltage, - batterySoc, - batteryCurrent, - batteryTemp, - batteryHeatingCurrent, - anyBatteryWarning, - anyBatteryAlarm) - .TitleBox("Battery"); + var bat = s.Battery; + var batteryAvgBox = CreateAveragedBatteryBox(bat); - - //////////////////// Batteries ///////////////////////// - - IReadOnlyList batteryBoxes = s.Battery - .Devices - .Select(CreateIndividualBattery) - .ToArray(s.Battery.Devices.Count); + var batteryBoxes = bat + .Devices + .Select(CreateIndividualBattery) + .ToArray(bat.Devices.Count) + .AsReadOnlyList(); var individualBatteries = batteryBoxes.Any() ? TextBlock.AlignLeft(batteryBoxes) : TextBlock.Spacer(1); - if (s.GridMeter is not null) - { - totalBoxes = TextBlock.AlignCenterVertical(gridBox, - gridBusFlow, - gridBusColumn, - flowGridBusToIslandBus, - islandBusColumn, - flowIslandBusToInverter, - inverterBox, - flowInverterToDcBus, - dcBusColumn, - flowDcBusToDcDc, - dcDcBox, - flowDcDcToBattery, - batteryAvgBox, - individualBatteries); - } - else - { - totalBoxes = TextBlock.AlignCenterVertical(islandBusColumn, - flowIslandBusToInverter, - inverterBox, - flowInverterToDcBus, - dcBusColumn, - flowDcBusToDcDc, - dcDcBox, - flowDcDcToBattery, - batteryAvgBox, - individualBatteries); - } + 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 + ); - totalBoxes.WriteLine(); + 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) @@ -375,16 +385,13 @@ internal static class Program return TextBlock.AlignCenterVertical(flow, box); } - private static TextBlock ColumnBox(String pvTitle, String busTitle, String loadTitle, TextBlock dataBox, ActivePower loadPower) - { - return ColumnBox(pvTitle, busTitle, loadTitle, dataBox, loadPower, 0); - } - - private static TextBlock ColumnBox(String pvTitle, String busTitle, String loadTitle, TextBlock dataBox, ActivePower loadPower, ActivePower pvPower) + 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(dataBox).TitleBox(busTitle); + var busBox = TextBlock.AlignLeft(busData).TitleBox(busTitle); var busToLoad = Flow.Vertical(loadPower); var loadBox = TextBlock.FromString(loadTitle).Box();