modify Topology.cs to properly display missing/unreachable devices (mayor rewrite)
This commit is contained in:
parent
9a400d992d
commit
41ccc63175
|
@ -1,8 +1,6 @@
|
|||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using InnovEnergy.App.SaliMax.Ess;
|
||||
using InnovEnergy.Lib.Devices.Battery48TL;
|
||||
using InnovEnergy.Lib.StatusApi.Connections;
|
||||
using InnovEnergy.Lib.Units;
|
||||
using InnovEnergy.Lib.Units.Power;
|
||||
using InnovEnergy.Lib.Utils;
|
||||
|
@ -10,185 +8,351 @@ using Ac3Bus = InnovEnergy.Lib.Units.Composite.Ac3Bus;
|
|||
|
||||
namespace InnovEnergy.App.SaliMax;
|
||||
|
||||
// ┌────┐ ┌────┐
|
||||
// │ Pv │ │ Pv │ ┌────┐
|
||||
// └────┘ └────┘ │ Pv │
|
||||
// V V └────┘
|
||||
// V V V
|
||||
// (b) 0 W (e) 0 W V
|
||||
// V V (i) 13.2 kW ┌────────────┐
|
||||
// V V V │ Battery │
|
||||
// ┌─────────┐ ┌──────────┐ ┌────────────┐ ┌─────────┐ V ├────────────┤
|
||||
// │ Grid │ │ Grid Bus │ │ Island Bus │ │ AC/DC │ ┌────────┐ ┌───────┐ │ 52.3 V │
|
||||
// ├─────────┤ -10.3 kW ├──────────┤ -11.7 kW ├────────────┤ -11.7 kW ├─────────┤ -11.7 kW │ Dc Bus │ 1008 W │ DC/DC │ 1008 W │ 99.1 % │
|
||||
// │ -3205 W │<<<<<<<<<<│ 244 V │<<<<<<<<<<│ 244 V │<<<<<<<<<<│ -6646 W │<<<<<<<<<<├────────┤>>>>>>>>>>├───────┤>>>>>>>>>>│ 490 mA │
|
||||
// │ -3507 W │ (a) │ 244 V │ (d) │ 244 V │ (g) │ -5071 W │ (h) │ 776 V │ (k) │ 56 V │ (l) │ 250 °C │
|
||||
// │ -3605 W │ K1 │ 246 V │ K2 │ 246 V │ K3 └─────────┘ └────────┘ └───────┘ │ 445 A │
|
||||
// └─────────┘ └──────────┘ └────────────┘ V │ 0 Warnings │
|
||||
// V V V │ 0 Alarms │
|
||||
// V V (j) 0 W └────────────┘
|
||||
// (c) 1400 W (f) 0 W V
|
||||
// V V V
|
||||
// V V ┌──────┐
|
||||
// ┌──────┐ ┌──────┐ │ Load │
|
||||
// │ Load │ │ Load │ └──────┘
|
||||
// └──────┘ └──────┘
|
||||
|
||||
|
||||
public static class Topology
|
||||
{
|
||||
|
||||
public static TextBlock CreateTopology(this StatusRecord status)
|
||||
{
|
||||
var islandTopology = status.CreateIslandTopology();
|
||||
// AC side
|
||||
// a + b - c - d = 0 [eq1]
|
||||
// d + e - f - g = 0 [eq2]
|
||||
//
|
||||
// c & d are not measured!
|
||||
//
|
||||
// d = f + g - e [eq2]
|
||||
// c = a + b - d [eq1]
|
||||
//
|
||||
// DC side
|
||||
// h + i - j - k = 0 [eq3]
|
||||
//
|
||||
// g = h assuming no losses in ACDC
|
||||
// k = l assuming no losses in DCDC
|
||||
// j = h + i - k [eq3]
|
||||
|
||||
// TODO: check ACDCs if AC is available and synced to find out if grid meter OR grid is unavailable
|
||||
var a = status.GridMeter?.Ac.Power.Active;
|
||||
var b = status.PvOnAcGrid?.Power.Active;
|
||||
var e = status.PvOnAcIsland?.Power.Active;
|
||||
var f = status.LoadOnAcIsland?.Ac.Power.Active;
|
||||
var g = status.AcDc.Ac.Power.Active;
|
||||
var h = g;
|
||||
var i = status.PvOnDc?.Dc.Power;
|
||||
var k = status.DcDc.Dc.Link.Power;
|
||||
var l = k;
|
||||
var j = Sum(h, i, -k);
|
||||
var d = Sum(f, g, -e);
|
||||
var c = Sum(a, b, -d);
|
||||
|
||||
if (status.GridMeter is null) // no grid meter?
|
||||
return islandTopology; // we're done
|
||||
/////////////////////////////
|
||||
|
||||
var gridTopology = status.CreateGridTopology();
|
||||
|
||||
return status.ConnectGridToIslandTopology(gridTopology, islandTopology);
|
||||
}
|
||||
|
||||
private static TextBlock ConnectGridToIslandTopology(this StatusRecord status, TextBlock gridTopology, TextBlock islandTopology)
|
||||
{
|
||||
var inverterPower = status.AcDc.Ac.Power.Active;
|
||||
var islandLoadPower = status.LoadOnAcIsland is not null
|
||||
? status.LoadOnAcIsland.Ac.Power.Active
|
||||
: 0; // TODO
|
||||
|
||||
ActivePower islandToGridBusPower = inverterPower + islandLoadPower;
|
||||
|
||||
var k2 = status.Relays?.K2IslandBusIsConnectedToGridBus;
|
||||
var gridToIslandConnection = SwitchedFlow(k2, islandToGridBusPower);
|
||||
var grid = status.CreateGridColumn(a);
|
||||
var gridBus = status.CreateGridBusColumn(b, c, d);
|
||||
var islandBus = status.CreateIslandBusColumn(e, f, g);
|
||||
var inverter = status.CreateInverterColumn(h);
|
||||
var dcBus = status.CreateDcBusColumn(i, j, k);
|
||||
var dcDc = status.CreateDcDcColumn(l);
|
||||
var batteries = status.CreateBatteryColumn();
|
||||
|
||||
return TextBlock.AlignCenterVertical
|
||||
(
|
||||
gridTopology,
|
||||
gridToIslandConnection,
|
||||
islandTopology
|
||||
grid,
|
||||
gridBus,
|
||||
islandBus,
|
||||
inverter,
|
||||
dcBus,
|
||||
dcDc,
|
||||
batteries
|
||||
);
|
||||
|
||||
|
||||
// 730 V
|
||||
}
|
||||
|
||||
private static TextBlock CreateGridColumn(this StatusRecord status, ActivePower? a)
|
||||
{
|
||||
// ┌─────────┐
|
||||
// │ Grid │
|
||||
// ├─────────┤ -10.3 kW
|
||||
// │ -3205 W │<<<<<<<<<<
|
||||
// │ -3507 W │ (a)
|
||||
// │ -3605 W │ K1
|
||||
// └─────────┘
|
||||
|
||||
var gridMeterAc = status.GridMeter?.Ac;
|
||||
var k1 = status.Relays?.K1GridBusIsConnectedToGrid;
|
||||
|
||||
var gridBox = PhasePowersActive(gridMeterAc).TitleBox("Grid");
|
||||
var gridFlow = SwitchedFlow(k1, a);
|
||||
|
||||
return TextBlock.AlignCenterVertical(gridBox, gridFlow);
|
||||
}
|
||||
|
||||
|
||||
private static TextBlock CreateGridBusColumn(this StatusRecord status,
|
||||
ActivePower? b,
|
||||
ActivePower? c,
|
||||
ActivePower? d)
|
||||
{
|
||||
|
||||
// ┌────┐
|
||||
// │ Pv │
|
||||
// └────┘
|
||||
// V
|
||||
// V
|
||||
// (b) 0 W
|
||||
// V
|
||||
// V
|
||||
// ┌──────────┐
|
||||
// │ Grid Bus │
|
||||
// ├──────────┤ -11.7 kW
|
||||
// │ 244 V │<<<<<<<<<<
|
||||
// │ 244 V │ (d)
|
||||
// │ 246 V │ K2
|
||||
// └──────────┘
|
||||
// V
|
||||
// V
|
||||
// (c) 1400 W
|
||||
// V
|
||||
// V
|
||||
// ┌──────┐
|
||||
// │ Load │
|
||||
// └──────┘
|
||||
|
||||
|
||||
////////////// top //////////////
|
||||
|
||||
var pvBox = TextBlock.FromString("PV").Box();
|
||||
var pvFlow = Flow.Vertical(b);
|
||||
|
||||
////////////// center //////////////
|
||||
|
||||
// on IslandBus show voltages measured by inverter
|
||||
// on GridBus show voltages measured by grid meter
|
||||
// ought to be approx the same
|
||||
|
||||
var gridMeterAc = status.GridMeter?.Ac;
|
||||
var k2 = status.Relays?.K2IslandBusIsConnectedToGridBus;
|
||||
|
||||
var busBox = PhaseVoltages(gridMeterAc).TitleBox("Grid Bus");
|
||||
var busFlow = SwitchedFlow(k2, d);
|
||||
|
||||
////////////// bottom //////////////
|
||||
|
||||
var loadFlow = Flow.Vertical(c);
|
||||
var loadBox = TextBlock.FromString("Load").Box();
|
||||
|
||||
////////////// assemble //////////////
|
||||
|
||||
return TextBlock.AlignCenterVertical
|
||||
(
|
||||
TextBlock.AlignCenterHorizontal(pvBox, pvFlow, busBox, loadFlow, loadBox),
|
||||
busFlow
|
||||
);
|
||||
}
|
||||
|
||||
private static TextBlock CreateGridTopology(this StatusRecord status)
|
||||
|
||||
private static TextBlock CreateIslandBusColumn(this StatusRecord status,
|
||||
ActivePower? e,
|
||||
ActivePower? f,
|
||||
ActivePower? g)
|
||||
{
|
||||
Debug.Assert(status.GridMeter is not null);
|
||||
|
||||
var gridBusColumn = status.CreateGridBusColumn();
|
||||
var gridBox = status.GridMeter.CreateGridBox();
|
||||
// ┌────┐
|
||||
// │ Pv │
|
||||
// └────┘
|
||||
// V
|
||||
// V
|
||||
// (e) 0 W
|
||||
// V
|
||||
// V
|
||||
// ┌────────────┐
|
||||
// │ Island Bus │
|
||||
// ├────────────┤ -11.7 kW
|
||||
// │ 244 V │<<<<<<<<<<
|
||||
// │ 244 V │ (g)
|
||||
// │ 246 V │ K3
|
||||
// └────────────┘
|
||||
// V
|
||||
// V
|
||||
// (f) 0 W
|
||||
// V
|
||||
// V
|
||||
// ┌──────┐
|
||||
// │ Load │
|
||||
// └──────┘
|
||||
|
||||
var k1 = status.Relays?.K1GridBusIsConnectedToGrid;
|
||||
var gridPower = status.GridMeter.Ac.Power.Active;
|
||||
var gridFlow = SwitchedFlow(k1, gridPower);
|
||||
|
||||
return TextBlock
|
||||
.AlignCenterVertical
|
||||
(
|
||||
gridBox,
|
||||
gridFlow,
|
||||
gridBusColumn
|
||||
);
|
||||
////////////// top //////////////
|
||||
|
||||
var pvBox = TextBlock.FromString("PV").Box();
|
||||
var pvFlow = Flow.Vertical(e);
|
||||
|
||||
////////////// center //////////////
|
||||
|
||||
// on IslandBus show voltages measured by inverter
|
||||
// on GridBus show voltages measured by grid meter
|
||||
// ought to be approx the same
|
||||
|
||||
var inverterAc = status.AcDc.Ac;
|
||||
var busBox = PhaseVoltages(inverterAc).TitleBox("Island Bus");
|
||||
var busFlow = status.IslandBusToInverterConnection(g);
|
||||
|
||||
////////////// bottom //////////////
|
||||
|
||||
var loadFlow = Flow.Vertical(f);
|
||||
var loadBox = TextBlock.FromString("Load").Box();
|
||||
|
||||
////////////// assemble //////////////
|
||||
|
||||
return TextBlock.AlignCenterVertical
|
||||
(
|
||||
TextBlock.AlignCenterHorizontal(pvBox, pvFlow, busBox, loadFlow, loadBox),
|
||||
busFlow
|
||||
);
|
||||
}
|
||||
|
||||
private static TextBlock SwitchedFlow(Boolean? switchClosed, ActivePower power)
|
||||
|
||||
|
||||
private static TextBlock CreateInverterColumn(this StatusRecord status, ActivePower? h)
|
||||
{
|
||||
return switchClosed is null ? TextBlock.FromString("??????????")
|
||||
: !switchClosed.Value ? Switch.Open("K1")
|
||||
: Flow.Horizontal(power);
|
||||
// ┌─────────┐
|
||||
// │ AC/DC │
|
||||
// ├─────────┤ -11.7 kW
|
||||
// │ -6646 W │<<<<<<<<<<
|
||||
// │ -5071 W │ (h)
|
||||
// └─────────┘
|
||||
|
||||
var inverterBox = status
|
||||
.AcDc
|
||||
.Devices
|
||||
.Select(d => d.Status.Ac.Power)
|
||||
.Apply(TextBlock.AlignLeft)
|
||||
.TitleBox("AC/DC");
|
||||
|
||||
var gridFlow = Flow.Horizontal(h);
|
||||
|
||||
return TextBlock.AlignCenterVertical(inverterBox, gridFlow);
|
||||
}
|
||||
|
||||
private static TextBlock CreateIslandTopology(this StatusRecord status)
|
||||
{
|
||||
var dcBatteryPower = status.DcDc.Dc.Battery.Power;
|
||||
var dcdcPower = status.DcDc.Dc.Link.Power;
|
||||
var inverterPower = status.AcDc.Ac.Power.Active;
|
||||
var islandLoadPower = status.LoadOnAcIsland is not null
|
||||
? status.LoadOnAcIsland.Ac.Power.Active
|
||||
: 0; // TODO
|
||||
|
||||
var batteries = status.CreateBatteryColumn();
|
||||
var dcBusColumn = status.CreateDcBusColumn();
|
||||
var islandBusColumn = status.CreateIslandBusColumn(islandLoadPower);
|
||||
var inverterBox = status.CreateInverterBox();
|
||||
var dcDcBox = status.CreateDcDcBox();
|
||||
|
||||
return TextBlock
|
||||
.AlignCenterVertical
|
||||
(
|
||||
islandBusColumn, status.InverterToIslandBusConnection(),
|
||||
inverterBox , Flow.Horizontal(inverterPower), // inverter to DC bus
|
||||
dcBusColumn , Flow.Horizontal(dcdcPower),
|
||||
dcDcBox , Flow.Horizontal(dcBatteryPower),
|
||||
batteries
|
||||
);
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
||||
private static TextBlock InverterToIslandBusConnection(this StatusRecord status)
|
||||
private static TextBlock IslandBusToInverterConnection(this StatusRecord status, ActivePower? g)
|
||||
{
|
||||
if (status.Relays is null)
|
||||
return TextBlock.FromString("????????");
|
||||
|
||||
var nInverters = status.AcDc.Devices.Count;
|
||||
|
||||
var k3S = status
|
||||
.Relays
|
||||
.K3InverterIsConnectedToIslandBus
|
||||
.Take(status.AcDc.Devices.Count);
|
||||
.Take(nInverters);
|
||||
|
||||
if (k3S.All(s => s))
|
||||
{
|
||||
var inverterPower = status.AcDc.Ac.Power.Active;
|
||||
return Flow.Horizontal(inverterPower);
|
||||
}
|
||||
if (k3S.Prepend(true).All(s => s)) // TODO: display when no ACDC present
|
||||
return Flow.Horizontal(g);
|
||||
|
||||
return Switch.Open("K3");
|
||||
}
|
||||
|
||||
private static TextBlock CreateGridBox(this IAc3Connection gridMeter)
|
||||
{
|
||||
return gridMeter
|
||||
.Ac
|
||||
.PhasePowersActive()
|
||||
.TitleBox("Grid");
|
||||
}
|
||||
|
||||
private static TextBlock CreateDcDcBox(this StatusRecord status)
|
||||
private static TextBlock CreateDcDcColumn(this StatusRecord status, ActivePower? l)
|
||||
{
|
||||
var dc48Voltage = status.DcDc.Dc.Battery.Voltage.ToDisplayString();
|
||||
|
||||
return TextBlock
|
||||
.AlignLeft(dc48Voltage)
|
||||
.TitleBox("DC/DC");
|
||||
var busBox = TextBlock
|
||||
.AlignLeft(dc48Voltage)
|
||||
.TitleBox("DC/DC");
|
||||
|
||||
var busFlow = Flow.Horizontal(l);
|
||||
|
||||
return TextBlock.AlignCenterVertical(busBox, busFlow);
|
||||
}
|
||||
|
||||
private static TextBlock CreateInverterBox(this StatusRecord status)
|
||||
private static TextBlock CreateDcBusColumn(this StatusRecord status,
|
||||
ActivePower? i,
|
||||
ActivePower? j,
|
||||
ActivePower? k)
|
||||
{
|
||||
var inverterAcPhases = status
|
||||
.AcDc
|
||||
.Devices
|
||||
.Select(d => d.Status.Ac.Power)
|
||||
.ToReadOnlyList();
|
||||
// ┌────┐
|
||||
// │ Pv │
|
||||
// └────┘
|
||||
// V
|
||||
// V
|
||||
// (i) 13.2 kW
|
||||
// V
|
||||
// V
|
||||
// ┌────────┐
|
||||
// │ Dc Bus │ 1008 W
|
||||
// ├────────┤>>>>>>>>>>
|
||||
// │ 776 V │ (k)
|
||||
// └────────┘
|
||||
// V
|
||||
// V
|
||||
// (j) 0 W
|
||||
// V
|
||||
// V
|
||||
// ┌──────┐
|
||||
// │ Load │
|
||||
// └──────┘
|
||||
//
|
||||
|
||||
return TextBlock
|
||||
.AlignLeft(inverterAcPhases)
|
||||
.TitleBox("AC/DC");
|
||||
}
|
||||
|
||||
private static TextBlock CreateGridBusColumn(this StatusRecord status)
|
||||
{
|
||||
Debug.Assert(status.GridMeter is not null);
|
||||
/////////////////// top ///////////////////
|
||||
|
||||
var gridLoadPower = status.LoadOnAcGrid is not null
|
||||
? status.LoadOnAcGrid.Power.Active
|
||||
: 0; // TODO: show that LoadOnAcGrid is actually not available and not 0
|
||||
var mppt = status.PvOnDc;
|
||||
|
||||
return CreateTopologyColumn
|
||||
var nStrings = mppt is not null
|
||||
? "x" + mppt.Strings.Count
|
||||
: "?";
|
||||
|
||||
var pvBox = TextBlock.FromString($"PV {nStrings}").Box();
|
||||
var pvToBus = Flow.Vertical(i);
|
||||
|
||||
/////////////////// center ///////////////////
|
||||
|
||||
var dcBusVoltage = status.DcDc.Dc.Link.Voltage;
|
||||
|
||||
var dcBusBox = dcBusVoltage
|
||||
.ToDisplayString()
|
||||
.Apply(TextBlock.FromString)
|
||||
.TitleBox("DC Bus ");
|
||||
|
||||
var busFlow = Flow.Horizontal(k);
|
||||
|
||||
/////////////////// bottom ///////////////////
|
||||
|
||||
var busToLoad = Flow.Vertical(j);
|
||||
var loadBox = TextBlock.FromString("DC Load").Box();
|
||||
|
||||
////////////// assemble //////////////
|
||||
|
||||
return TextBlock.AlignCenterVertical
|
||||
(
|
||||
"PV" , 0,
|
||||
"Grid Bus", status.GridMeter.Ac.PhaseVoltages(),
|
||||
"Load" , gridLoadPower
|
||||
);
|
||||
}
|
||||
|
||||
private static TextBlock CreateIslandBusColumn(this StatusRecord status, ActivePower islandLoadPower)
|
||||
{
|
||||
var islandBusPv = 0.W(); // TODO
|
||||
|
||||
return CreateTopologyColumn
|
||||
(
|
||||
"PV" , islandBusPv,
|
||||
"Island Bus", status.AcDc.Ac.PhaseVoltages(),
|
||||
"Load" , islandLoadPower
|
||||
);
|
||||
}
|
||||
|
||||
private static TextBlock CreateDcBusColumn(this StatusRecord status)
|
||||
{
|
||||
var dcBusLoad = 0.W(); // TODO
|
||||
var pvOnDcPower = status.PvOnDc.Dc!.Power; // TODO !
|
||||
var dcLinkVoltage = status.DcDc.Dc.Link.Voltage.ToDisplayString();
|
||||
|
||||
return CreateTopologyColumn
|
||||
(
|
||||
"PV" , pvOnDcPower,
|
||||
"DC Bus ", dcLinkVoltage,
|
||||
"DC Load", dcBusLoad
|
||||
TextBlock.AlignCenterHorizontal(pvBox, pvToBus, dcBusBox, busToLoad, loadBox),
|
||||
busFlow
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -242,23 +406,23 @@ public static class Topology
|
|||
.TitleBox($"Battery x{nBatteries}");
|
||||
}
|
||||
|
||||
private static TextBlock PhaseVoltages(this Ac3Bus ac)
|
||||
private static TextBlock PhaseVoltages(Ac3Bus? ac)
|
||||
{
|
||||
return TextBlock.AlignLeft
|
||||
(
|
||||
ac.L1.Voltage.ToDisplayString(),
|
||||
ac.L2.Voltage.ToDisplayString(),
|
||||
ac.L3.Voltage.ToDisplayString()
|
||||
ac?.L1.Voltage.ToDisplayString() ?? "???",
|
||||
ac?.L2.Voltage.ToDisplayString() ?? "???",
|
||||
ac?.L3.Voltage.ToDisplayString() ?? "???"
|
||||
);
|
||||
}
|
||||
|
||||
private static TextBlock PhasePowersActive(this Ac3Bus ac)
|
||||
private static TextBlock PhasePowersActive(Ac3Bus? ac)
|
||||
{
|
||||
return TextBlock.AlignLeft
|
||||
(
|
||||
ac.L1.Power.Active.ToDisplayString(),
|
||||
ac.L2.Power.Active.ToDisplayString(),
|
||||
ac.L3.Power.Active.ToDisplayString()
|
||||
ac?.L1.Power.Active.ToDisplayString() ?? "???",
|
||||
ac?.L2.Power.Active.ToDisplayString() ?? "???",
|
||||
ac?.L3.Power.Active.ToDisplayString() ?? "???"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -285,17 +449,20 @@ public static class Topology
|
|||
return TextBlock.AlignCenterVertical(flow, box);
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "SuggestBaseTypeForParameter")]
|
||||
private static TextBlock CreateTopologyColumn(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 ActivePower? Sum(ActivePower? e, ActivePower? f, ActivePower? g)
|
||||
{
|
||||
if (e is null || f is null || g is null)
|
||||
return null;
|
||||
|
||||
return f + g + e;
|
||||
}
|
||||
|
||||
private static TextBlock SwitchedFlow(Boolean? switchClosed, ActivePower? power)
|
||||
{
|
||||
return switchClosed is null ? TextBlock.FromString("??????????")
|
||||
: !switchClosed.Value ? Switch.Open("K1")
|
||||
: power is null ? TextBlock.FromString("??????????")
|
||||
: Flow.Horizontal(power);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue