Introduce VoltageRms and CurrentRms and use them where appropriate

This commit is contained in:
ig 2023-09-01 14:52:09 +02:00
parent 006ea0e1ee
commit 7ecb6e4607
10 changed files with 71 additions and 57 deletions

View File

@ -1,7 +1,7 @@
using InnovEnergy.Lib.Devices.Battery48TL;
using InnovEnergy.Lib.Devices.Battery48TL.DataTypes; using InnovEnergy.Lib.Devices.Battery48TL.DataTypes;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc; using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes; using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes;
using InnovEnergy.Lib.Time.Unix;
using InnovEnergy.Lib.Units; using InnovEnergy.Lib.Units;
using InnovEnergy.Lib.Utils; using InnovEnergy.Lib.Utils;
@ -9,7 +9,6 @@ namespace InnovEnergy.App.SaliMax.Ess;
public static class Controller 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 private static readonly Double BatteryHeatingPower = 200.0; // TODO: move to config
public static EssMode SelectControlMode(this StatusRecord s) public static EssMode SelectControlMode(this StatusRecord s)
@ -105,12 +104,10 @@ public static class Controller
private static EssControl LimitDischargePower(this EssControl control, StatusRecord s) 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) ?? 0;
var maxBatteryDischargeDelta = s.Battery.Devices.Sum(b => b.MaxDischargePower);
var keepMinSocLimitDelta = s.ControlBatteryPower(s.HoldMinSocPower()); var keepMinSocLimitDelta = s.ControlBatteryPower(s.HoldMinSocPower());
return control return control
// .LimitDischargePower(maxInverterDischargeDelta, EssLimit.DischargeLimitedByInverterPower)
.LimitDischargePower(maxBatteryDischargeDelta , EssLimit.DischargeLimitedByBatteryPower) .LimitDischargePower(maxBatteryDischargeDelta , EssLimit.DischargeLimitedByBatteryPower)
.LimitDischargePower(keepMinSocLimitDelta , EssLimit.DischargeLimitedByMinSoc); .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) private static Boolean MustHeatBatteries(this StatusRecord s)
{ {
var batteries = s.Battery.Devices; var batteries = s.GetBatteries();
if (batteries.Count <= 0) if (batteries.Count <= 0)
return true; // batteries might be there but BMS is without power return true; // batteries might be there but BMS is without power
@ -156,12 +147,11 @@ public static class Controller
// This introduce a limit when we don't have communication with batteries // 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 // 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();
if (s.Battery.Devices.Count != 0) var maxChargePower = batteries.Count == 0
{ ? s.Config.Devices.BatteryNodes.Length * BatteryHeatingPower
maxChargePower = s.Battery.Devices.Sum(b => b.MaxChargePower); : batteries.Sum(b => b.MaxChargePower);
}
maxChargePower.W().ToDisplayString().WriteLine(" Max Charge Power"); maxChargePower.W().ToDisplayString().WriteLine(" Max Charge Power");
@ -186,12 +176,17 @@ public static class Controller
private static Boolean MustReachMinSoc(this StatusRecord s) private static Boolean MustReachMinSoc(this StatusRecord s)
{ {
var batteries = s.Battery.Devices; var batteries = s.GetBatteries();
return batteries.Count > 0 return batteries.Count > 0
&& batteries.Any(b => b.Soc < s.Config.MinSoc); && batteries.Any(b => b.Soc < s.Config.MinSoc);
} }
private static IReadOnlyList<Battery48TlRecord> GetBatteries(this StatusRecord s)
{
return s.Battery?.Devices ?? Array.Empty<Battery48TlRecord>();
}
private static Boolean MustDoCalibrationCharge(this StatusRecord statusRecord) 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 // 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 // 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 // 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 return ControlPower
( (
measurement: status.Battery.Devices.Sum(b => b.Dc.Power), measurement: status.GetBatteries().Sum(b => b.Dc.Power),
target: targetBatteryPower, target: targetBatteryPower,
pConstant: status.Config.PConstant pConstant: status.Config.PConstant
); );
@ -247,7 +242,7 @@ public static class Controller
{ {
// TODO: explain LowSOC curve // TODO: explain LowSOC curve
var batteries = s.Battery.Devices; var batteries = s.GetBatteries();
if (batteries.Count == 0) if (batteries.Count == 0)
return Double.NegativeInfinity; return Double.NegativeInfinity;
@ -264,6 +259,7 @@ public static class Controller
return error * pConstant; return error * pConstant;
} }
// ReSharper disable once UnusedMember.Local, TODO
private static Double ControlPowerWithIntegral(Double measurement, Double target, Double p, Double i) private static Double ControlPowerWithIntegral(Double measurement, Double target, Double p, Double i)
{ {
var errorSum = 0; // this is must be sum of error var errorSum = 0; // this is must be sum of error
@ -273,11 +269,4 @@ public static class Controller
return ki + kp; return ki + kp;
} }
private static IEnumerable<InverterState> InverterStates(this AcDcDevicesRecord acDcStatus)
{
return acDcStatus
.Devices
.Select(d => d.Status.InverterState.Current);
}
} }

View File

@ -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 voltage = bat.Dc.Voltage.ToDisplayString();
var soc = bat.Devices.Any() ? bat.Devices.Average(b => b.Soc).Percent().ToDisplayString() : "0"; var soc = bat.Devices.Any() ? bat.Devices.Average(b => b.Soc).Percent().ToDisplayString() : "0";
var current = bat.Dc.Current.ToDisplayString(); var current = bat.Dc.Current.ToDisplayString();

View File

@ -2,7 +2,6 @@ using System.Diagnostics.CodeAnalysis;
using InnovEnergy.Lib.Devices.Battery48TL.DataTypes; using InnovEnergy.Lib.Devices.Battery48TL.DataTypes;
using InnovEnergy.Lib.Units; using InnovEnergy.Lib.Units;
using InnovEnergy.Lib.Units.Power; using InnovEnergy.Lib.Units.Power;
using InnovEnergy.Lib.Utils;
using static InnovEnergy.Lib.Devices.Battery48TL.DataTypes.LedState; using static InnovEnergy.Lib.Devices.Battery48TL.DataTypes.LedState;
namespace InnovEnergy.Lib.Devices.Battery48TL; namespace InnovEnergy.Lib.Devices.Battery48TL;
@ -29,11 +28,8 @@ public partial class Battery48TlRecord
// Time since TOC is a counter from the last moment when the battery reached EOC // 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 // When The battery is full charged (reached EOC) the Time Since TOC is set to 0
public UInt16 TimeSinceTOC => _TimeSinceToc; public UInt16 TimeSinceTOC => _TimeSinceToc;
public Current BusCurrent => _BusCurrent; public Current BusCurrent => _BusCurrent;
public Current HeatingCurrent => _BusCurrent - _CellsCurrent; public Current HeatingCurrent => _BusCurrent - _CellsCurrent;
public DcPower HeatingPower => HeatingCurrent * Dc.Voltage; public DcPower HeatingPower => HeatingCurrent * Dc.Voltage;
public Boolean CalibrationChargeRequested => TimeSinceTOC > OneWeekInMinutes; public Boolean CalibrationChargeRequested => TimeSinceTOC > OneWeekInMinutes;

View File

@ -2,9 +2,9 @@
using InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes; using InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes; using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes;
using InnovEnergy.Lib.Units.Composite; 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 WarningMessage = InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes.WarningMessage;
using static System.Math;
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.Status; namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.Status;
@ -77,30 +77,26 @@ public class AcDcStatus
private AcPhase L1 => new() private AcPhase L1 => new()
{ {
Current = _Self.GridCurrentL1, Current = Abs(_Self.GridCurrentL1),
Voltage = _Self.GridVoltageL1, Voltage = Abs(_Self.GridVoltageL1),
Phi = Atan2(_Self.ReactivePowerL1, _Self.ActivePowerL1) Phi = Atan2(_Self.ReactivePowerL1, _Self.ActivePowerL1)
}; };
private AcPhase L2 => new() private AcPhase L2 => new()
{ {
Current = _Self.GridCurrentL2, Current = Abs(_Self.GridCurrentL2),
Voltage = _Self.GridVoltageL2, Voltage = Abs(_Self.GridVoltageL2),
Phi = Atan2(_Self.ReactivePowerL2, _Self.ActivePowerL2) Phi = Atan2(_Self.ReactivePowerL2, _Self.ActivePowerL2)
}; };
private AcPhase L3 => new() private AcPhase L3 => new()
{ {
Current = _Self.GridCurrentL3, Current = Abs(_Self.GridCurrentL3),
Voltage = _Self.GridVoltageL3, Voltage = Abs(_Self.GridVoltageL3),
Phi = Atan2(_Self.ReactivePowerL3, _Self.ActivePowerL3) Phi = Atan2(_Self.ReactivePowerL3, _Self.ActivePowerL3)
}; };
internal AcDcStatus(AcDcRecord self) => _Self = self; internal AcDcStatus(AcDcRecord self) => _Self = self;
private readonly AcDcRecord _Self; private readonly AcDcRecord _Self;
} }

View File

@ -4,8 +4,8 @@ namespace InnovEnergy.Lib.Units.Composite;
public record AcPhase public record AcPhase
{ {
public required Voltage Voltage { get; init; } public required VoltageRms Voltage { get; init; }
public required Current Current { get; init; } public required CurrentRms Current { get; init; }
public required Angle Phi { get; init; } public required Angle Phi { get; init; }
public AcPower Power => new() public AcPower Power => new()

View File

@ -9,5 +9,4 @@ public sealed class Current : Unit
public static implicit operator Current(Double d) => new Current(d); public static implicit operator Current(Double d) => new Current(d);
public static implicit operator Double(Current d) => d.Value; public static implicit operator Double(Current d) => d.Value;
} }

View File

@ -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;
}

View File

@ -6,6 +6,8 @@ public sealed class ApparentPower : Power
public ApparentPower(Double value) : base(value) 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); public static implicit operator ApparentPower(Double d) => new ApparentPower(d);

View File

@ -1,12 +1,10 @@
namespace InnovEnergy.Lib.Units; namespace InnovEnergy.Lib.Units;
public sealed class Voltage : Unit public sealed class Voltage : Unit
{ {
public override String Symbol => "V"; 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 Voltage(Double d) => new Voltage(d);
public static implicit operator Double(Voltage d) => d.Value; public static implicit operator Double(Voltage d) => d.Value;

View File

@ -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;
}