From 7ecb6e4607b3fff192141f4186f747e783eab3b0 Mon Sep 17 00:00:00 2001 From: ig Date: Fri, 1 Sep 2023 14:52:09 +0200 Subject: [PATCH] Introduce VoltageRms and CurrentRms and use them where appropriate --- csharp/App/SaliMax/src/Ess/Controller.cs | 47 +++++++------------ csharp/App/SaliMax/src/Topology.cs | 5 +- .../Battery48TL/Battery48TlRecord.Api.cs | 12 ++--- .../Trumpf/TruConvertAc/Status/AcDcStatus.cs | 20 ++++---- csharp/Lib/Units/Composite/AcPhase.cs | 6 +-- csharp/Lib/Units/Current.cs | 1 - csharp/Lib/Units/CurrentRms.cs | 15 ++++++ csharp/Lib/Units/Power/ApparentPower.cs | 2 + csharp/Lib/Units/Voltage.cs | 4 +- csharp/Lib/Units/VoltageRms.cs | 16 +++++++ 10 files changed, 71 insertions(+), 57 deletions(-) create mode 100644 csharp/Lib/Units/CurrentRms.cs create mode 100644 csharp/Lib/Units/VoltageRms.cs diff --git a/csharp/App/SaliMax/src/Ess/Controller.cs b/csharp/App/SaliMax/src/Ess/Controller.cs index 8cf603029..6cfd84f3e 100644 --- a/csharp/App/SaliMax/src/Ess/Controller.cs +++ b/csharp/App/SaliMax/src/Ess/Controller.cs @@ -1,7 +1,7 @@ +using InnovEnergy.Lib.Devices.Battery48TL; using InnovEnergy.Lib.Devices.Battery48TL.DataTypes; using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc; using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes; -using InnovEnergy.Lib.Time.Unix; using InnovEnergy.Lib.Units; using InnovEnergy.Lib.Utils; @@ -9,7 +9,6 @@ namespace InnovEnergy.App.SaliMax.Ess; public static class Controller { - private static readonly UnixTimeSpan MaxTimeWithoutEoc = UnixTimeSpan.FromDays(7); // TODO: move to config private static readonly Double BatteryHeatingPower = 200.0; // TODO: move to config public static EssMode SelectControlMode(this StatusRecord s) @@ -105,12 +104,10 @@ public static class Controller private static EssControl LimitDischargePower(this EssControl control, StatusRecord s) { - //var maxInverterDischargeDelta = s.ControlInverterPower(-s.Config.MaxInverterPower); - var maxBatteryDischargeDelta = s.Battery.Devices.Sum(b => b.MaxDischargePower); + var maxBatteryDischargeDelta = s.Battery?.Devices.Sum(b => b.MaxDischargePower) ?? 0; var keepMinSocLimitDelta = s.ControlBatteryPower(s.HoldMinSocPower()); return control - // .LimitDischargePower(maxInverterDischargeDelta, EssLimit.DischargeLimitedByInverterPower) .LimitDischargePower(maxBatteryDischargeDelta , EssLimit.DischargeLimitedByBatteryPower) .LimitDischargePower(keepMinSocLimitDelta , EssLimit.DischargeLimitedByMinSoc); } @@ -133,15 +130,9 @@ public static class Controller }; } - - private static Boolean HasPreChargeAlarm(this StatusRecord statusRecord) - { - return statusRecord.DcDc.Alarms.Contains(Lib.Devices.Trumpf.TruConvertDc.Status.AlarmMessage.DcDcPrecharge); - } - private static Boolean MustHeatBatteries(this StatusRecord s) { - var batteries = s.Battery.Devices; + var batteries = s.GetBatteries(); if (batteries.Count <= 0) return true; // batteries might be there but BMS is without power @@ -156,13 +147,12 @@ public static class Controller // This introduce a limit when we don't have communication with batteries // Otherwise the limit will be 0 and the batteries will be not heated - var maxChargePower = s.Config.Devices.BatteryNodes.Length * BatteryHeatingPower; + var batteries = s.GetBatteries(); + + var maxChargePower = batteries.Count == 0 + ? s.Config.Devices.BatteryNodes.Length * BatteryHeatingPower + : batteries.Sum(b => b.MaxChargePower); - if (s.Battery.Devices.Count != 0) - { - maxChargePower = s.Battery.Devices.Sum(b => b.MaxChargePower); - } - maxChargePower.W().ToDisplayString().WriteLine(" Max Charge Power"); return maxChargePower; @@ -186,12 +176,17 @@ public static class Controller private static Boolean MustReachMinSoc(this StatusRecord s) { - var batteries = s.Battery.Devices; + var batteries = s.GetBatteries(); return batteries.Count > 0 && batteries.Any(b => b.Soc < s.Config.MinSoc); } + private static IReadOnlyList GetBatteries(this StatusRecord s) + { + return s.Battery?.Devices ?? Array.Empty(); + } + private static Boolean MustDoCalibrationCharge(this StatusRecord statusRecord) { @@ -209,7 +204,7 @@ public static class Controller // no need to return false in case of EOC reached // because the BMS will set the Time Since TOC to 0 as soon a battery reach EOC // then Calibration Charge flag will be set false by the software - return statusRecord.Battery.CalibrationChargeRequested; + return statusRecord.Battery?.CalibrationChargeRequested ?? false; } @@ -237,7 +232,7 @@ public static class Controller { return ControlPower ( - measurement: status.Battery.Devices.Sum(b => b.Dc.Power), + measurement: status.GetBatteries().Sum(b => b.Dc.Power), target: targetBatteryPower, pConstant: status.Config.PConstant ); @@ -247,7 +242,7 @@ public static class Controller { // TODO: explain LowSOC curve - var batteries = s.Battery.Devices; + var batteries = s.GetBatteries(); if (batteries.Count == 0) return Double.NegativeInfinity; @@ -264,6 +259,7 @@ public static class Controller return error * pConstant; } + // ReSharper disable once UnusedMember.Local, TODO private static Double ControlPowerWithIntegral(Double measurement, Double target, Double p, Double i) { var errorSum = 0; // this is must be sum of error @@ -273,11 +269,4 @@ public static class Controller return ki + kp; } - private static IEnumerable InverterStates(this AcDcDevicesRecord acDcStatus) - { - return acDcStatus - .Devices - .Select(d => d.Status.InverterState.Current); - } - } \ No newline at end of file diff --git a/csharp/App/SaliMax/src/Topology.cs b/csharp/App/SaliMax/src/Topology.cs index 85248c74a..a448941d3 100644 --- a/csharp/App/SaliMax/src/Topology.cs +++ b/csharp/App/SaliMax/src/Topology.cs @@ -388,8 +388,11 @@ public static class Topology ); } - private static TextBlock CreateAveragedBatteryBox(Battery48TlRecords bat) + private static TextBlock CreateAveragedBatteryBox(Battery48TlRecords? bat) { + if (bat is null) + return TextBlock.AlignLeft("no battery").Box(); + var voltage = bat.Dc.Voltage.ToDisplayString(); var soc = bat.Devices.Any() ? bat.Devices.Average(b => b.Soc).Percent().ToDisplayString() : "0"; var current = bat.Dc.Current.ToDisplayString(); diff --git a/csharp/Lib/Devices/Battery48TL/Battery48TlRecord.Api.cs b/csharp/Lib/Devices/Battery48TL/Battery48TlRecord.Api.cs index 8e8d24217..6d0004234 100644 --- a/csharp/Lib/Devices/Battery48TL/Battery48TlRecord.Api.cs +++ b/csharp/Lib/Devices/Battery48TL/Battery48TlRecord.Api.cs @@ -2,7 +2,6 @@ using System.Diagnostics.CodeAnalysis; using InnovEnergy.Lib.Devices.Battery48TL.DataTypes; using InnovEnergy.Lib.Units; using InnovEnergy.Lib.Units.Power; -using InnovEnergy.Lib.Utils; using static InnovEnergy.Lib.Devices.Battery48TL.DataTypes.LedState; namespace InnovEnergy.Lib.Devices.Battery48TL; @@ -18,7 +17,7 @@ public partial class Battery48TlRecord public Leds_ Leds => new Leds_(this); public Temperatures_ Temperatures => new Temperatures_(this); - public Boolean ConnectedToDcBus => (_IoStates & 1) == 0; + public Boolean ConnectedToDcBus => (_IoStates & 1) == 0; public Boolean Eoc => Leds is { Green: On, Amber: Off, Blue : Off }; public Strings Warnings => ParseWarnings().OrderBy(w => w).ToList(); @@ -28,13 +27,10 @@ public partial class Battery48TlRecord // Time since TOC is a counter from the last moment when the battery reached EOC // When The battery is full charged (reached EOC) the Time Since TOC is set to 0 - public UInt16 TimeSinceTOC => _TimeSinceToc; - - public Current BusCurrent => _BusCurrent; - + public UInt16 TimeSinceTOC => _TimeSinceToc; + public Current BusCurrent => _BusCurrent; public Current HeatingCurrent => _BusCurrent - _CellsCurrent; - - public DcPower HeatingPower => HeatingCurrent * Dc.Voltage; + public DcPower HeatingPower => HeatingCurrent * Dc.Voltage; public Boolean CalibrationChargeRequested => TimeSinceTOC > OneWeekInMinutes; diff --git a/csharp/Lib/Devices/Trumpf/TruConvertAc/Status/AcDcStatus.cs b/csharp/Lib/Devices/Trumpf/TruConvertAc/Status/AcDcStatus.cs index 1554f99c3..8c00b3a6e 100644 --- a/csharp/Lib/Devices/Trumpf/TruConvertAc/Status/AcDcStatus.cs +++ b/csharp/Lib/Devices/Trumpf/TruConvertAc/Status/AcDcStatus.cs @@ -2,9 +2,9 @@ using InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes; using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes; using InnovEnergy.Lib.Units.Composite; -using static System.Math; -using AlarmMessage = InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes.AlarmMessage; +using AlarmMessage = InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes.AlarmMessage; using WarningMessage = InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes.WarningMessage; +using static System.Math; namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.Status; @@ -77,30 +77,26 @@ public class AcDcStatus private AcPhase L1 => new() { - Current = _Self.GridCurrentL1, - Voltage = _Self.GridVoltageL1, + Current = Abs(_Self.GridCurrentL1), + Voltage = Abs(_Self.GridVoltageL1), Phi = Atan2(_Self.ReactivePowerL1, _Self.ActivePowerL1) }; private AcPhase L2 => new() { - Current = _Self.GridCurrentL2, - Voltage = _Self.GridVoltageL2, + Current = Abs(_Self.GridCurrentL2), + Voltage = Abs(_Self.GridVoltageL2), Phi = Atan2(_Self.ReactivePowerL2, _Self.ActivePowerL2) }; private AcPhase L3 => new() { - Current = _Self.GridCurrentL3, - Voltage = _Self.GridVoltageL3, + Current = Abs(_Self.GridCurrentL3), + Voltage = Abs(_Self.GridVoltageL3), Phi = Atan2(_Self.ReactivePowerL3, _Self.ActivePowerL3) }; - - - internal AcDcStatus(AcDcRecord self) => _Self = self; private readonly AcDcRecord _Self; - } diff --git a/csharp/Lib/Units/Composite/AcPhase.cs b/csharp/Lib/Units/Composite/AcPhase.cs index db07ec1bc..0d5756182 100644 --- a/csharp/Lib/Units/Composite/AcPhase.cs +++ b/csharp/Lib/Units/Composite/AcPhase.cs @@ -4,9 +4,9 @@ namespace InnovEnergy.Lib.Units.Composite; public record AcPhase { - public required Voltage Voltage { get; init; } - public required Current Current { get; init; } - public required Angle Phi { get; init; } + public required VoltageRms Voltage { get; init; } + public required CurrentRms Current { get; init; } + public required Angle Phi { get; init; } public AcPower Power => new() { diff --git a/csharp/Lib/Units/Current.cs b/csharp/Lib/Units/Current.cs index 95c92d699..90f64b9ba 100644 --- a/csharp/Lib/Units/Current.cs +++ b/csharp/Lib/Units/Current.cs @@ -9,5 +9,4 @@ public sealed class Current : Unit public static implicit operator Current(Double d) => new Current(d); public static implicit operator Double(Current d) => d.Value; - } \ No newline at end of file diff --git a/csharp/Lib/Units/CurrentRms.cs b/csharp/Lib/Units/CurrentRms.cs new file mode 100644 index 000000000..7b25e3db0 --- /dev/null +++ b/csharp/Lib/Units/CurrentRms.cs @@ -0,0 +1,15 @@ +namespace InnovEnergy.Lib.Units; + +public sealed class CurrentRms : Unit +{ + public override String Symbol => "A~"; + + public CurrentRms(Double value) : base(value) + { + if (value < 0) + throw new ArgumentException("RMS value cannot be negative", nameof(value)); + } + + public static implicit operator CurrentRms(Double d) => new CurrentRms(d); + public static implicit operator Double(CurrentRms d) => d.Value; +} \ No newline at end of file diff --git a/csharp/Lib/Units/Power/ApparentPower.cs b/csharp/Lib/Units/Power/ApparentPower.cs index 51038a742..85128758f 100644 --- a/csharp/Lib/Units/Power/ApparentPower.cs +++ b/csharp/Lib/Units/Power/ApparentPower.cs @@ -6,6 +6,8 @@ public sealed class ApparentPower : Power public ApparentPower(Double value) : base(value) { + if (value < 0) + throw new ArgumentException($"{nameof(ApparentPower)} cannot be negative by definition.", nameof(value)); } public static implicit operator ApparentPower(Double d) => new ApparentPower(d); diff --git a/csharp/Lib/Units/Voltage.cs b/csharp/Lib/Units/Voltage.cs index 49a33342c..81f63182e 100644 --- a/csharp/Lib/Units/Voltage.cs +++ b/csharp/Lib/Units/Voltage.cs @@ -1,12 +1,10 @@ - namespace InnovEnergy.Lib.Units; public sealed class Voltage : Unit { public override String Symbol => "V"; - public Voltage(Double value) : base(value) - {} + public Voltage(Double value) : base(value) {} public static implicit operator Voltage(Double d) => new Voltage(d); public static implicit operator Double(Voltage d) => d.Value; diff --git a/csharp/Lib/Units/VoltageRms.cs b/csharp/Lib/Units/VoltageRms.cs new file mode 100644 index 000000000..12bc4ae80 --- /dev/null +++ b/csharp/Lib/Units/VoltageRms.cs @@ -0,0 +1,16 @@ + +namespace InnovEnergy.Lib.Units; + +public sealed class VoltageRms : Unit +{ + public override String Symbol => "V~"; + + public VoltageRms(Double value) : base(value) + { + if (value < 0) + throw new ArgumentException("RMS value cannot be negative", nameof(value)); + } + + public static implicit operator VoltageRms(Double d) => new VoltageRms(d); + public static implicit operator Double(VoltageRms d) => d.Value; +} \ No newline at end of file