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.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,12 +147,11 @@ 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();
if (s.Battery.Devices.Count != 0)
{
maxChargePower = s.Battery.Devices.Sum(b => b.MaxChargePower);
}
var maxChargePower = batteries.Count == 0
? s.Config.Devices.BatteryNodes.Length * BatteryHeatingPower
: batteries.Sum(b => b.MaxChargePower);
maxChargePower.W().ToDisplayString().WriteLine(" Max Charge Power");
@ -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<Battery48TlRecord> GetBatteries(this StatusRecord s)
{
return s.Battery?.Devices ?? Array.Empty<Battery48TlRecord>();
}
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<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 soc = bat.Devices.Any() ? bat.Devices.Average(b => b.Soc).Percent().ToDisplayString() : "0";
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.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;

View File

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

View File

@ -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()
{

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 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)
{
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);

View File

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

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