Innovenergy_trunk/csharp/Lib/Devices/Battery48TL/Battery48TlRecord.Api.cs

258 lines
11 KiB
C#
Raw Normal View History

2023-06-13 10:53:52 +00:00
using System.Diagnostics.CodeAnalysis;
2024-03-20 12:52:36 +00:00
using System.Text;
2023-06-13 10:53:52 +00:00
using InnovEnergy.Lib.Devices.Battery48TL.DataTypes;
using InnovEnergy.Lib.Units;
using InnovEnergy.Lib.Units.Power;
using static InnovEnergy.Lib.Devices.Battery48TL.DataTypes.LedState;
namespace InnovEnergy.Lib.Devices.Battery48TL;
using Strings = IReadOnlyList<String>;
[SuppressMessage("ReSharper", "InconsistentNaming")]
[SuppressMessage("ReSharper", "ConvertToAutoProperty")]
public partial class Battery48TlRecord
{
public Dc_ Dc => new Dc_(this);
public Leds_ Leds => new Leds_(this);
public Temperatures_ Temperatures => new Temperatures_(this);
2024-02-05 14:30:47 +00:00
public StringActive_ BatteryStrings => new StringActive_(this);
2024-02-06 14:22:24 +00:00
public IoStatus_ IoStatus => new IoStatus_(this);
2024-02-05 14:30:47 +00:00
2024-03-20 12:52:36 +00:00
public Boolean Eoc => ParseEocReached();//Leds is { Green: On, Amber: Off, Blue : Off }; // ParseEocReached(); //
2024-02-06 14:22:24 +00:00
2024-02-07 13:32:17 +00:00
public UInt16 LimpBitMap => _LimpBitMap;
2024-03-20 12:52:36 +00:00
public String BatteryState => ParseBatteryState();
2024-02-06 14:22:24 +00:00
public String SerialNumber => $"{_SerialNum1:X4}{_SerialNum2:X4}{_SerialNum3:X4}{_SerialNum4:X4}".TrimEnd('0');
public String FwVersion => _FwVersion.ToString("X4");
2024-02-07 13:32:17 +00:00
2024-02-06 14:22:24 +00:00
public Strings Warnings => ParseWarnings().OrderBy(w => w).ToList();
public Strings Alarms => ParseAlarms() .OrderBy(w => w).ToList();
2023-06-13 10:53:52 +00:00
2024-02-06 14:22:24 +00:00
public Percent Soc => _Soc;
public Double SOCAh => _SOCAh;
2023-08-31 13:16:20 +00:00
2024-02-07 13:32:17 +00:00
public Current BusCurrent => _BusCurrent;
2024-02-20 16:06:35 +00:00
public Current CellsCurrent => _CellsCurrent;
2024-02-07 13:32:17 +00:00
public Current HeatingCurrent => _BusCurrent - _CellsCurrent;
public DcPower HeatingPower => HeatingCurrent * Dc.Voltage;
// 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 TimeSpan TimeSinceTOC => TimeSpan.FromMinutes(_TimeSinceToc);
2024-02-20 16:06:35 +00:00
public Boolean CalibrationChargeRequested => TimeSinceTOC > TimeSpan.FromDays(7); // From AF0A (Fw Version) Each 14 days , But Max and Peter asked for 7 days.
2023-06-13 10:53:52 +00:00
public readonly struct Leds_
{
public LedState Blue => Self.ParseLed(LedColor.Blue);
public LedState Red => Self.ParseLed(LedColor.Red);
public LedState Green => Self.ParseLed(LedColor.Green);
public LedState Amber => Self.ParseLed(LedColor.Amber);
private Battery48TlRecord Self { get; }
internal Leds_(Battery48TlRecord self) => this.Self = self;
2023-06-13 10:53:52 +00:00
}
2024-02-05 14:30:47 +00:00
public readonly struct StringActive_
{
public Boolean String1Active => (Self._LimpBitMap & 1) == 0;
public Boolean String2Active => (Self._LimpBitMap & 2) == 0;
public Boolean String3Active => (Self._LimpBitMap & 4) == 0;
public Boolean String4Active => (Self._LimpBitMap & 8) == 0;
public Boolean String5Active => (Self._LimpBitMap & 16) == 0;
2024-02-05 14:30:47 +00:00
internal StringActive_(Battery48TlRecord self) => Self = self;
private Battery48TlRecord Self { get; }
}
2024-02-06 14:22:24 +00:00
public readonly struct IoStatus_
{
public Boolean ConnectedToDcBus => ((Self._IoStates >> 0) & 1) == 0;
public Boolean AlarmOutActive => ((Self._IoStates >> 1) & 1) == 0;
public Boolean InternalFanActive => ((Self._IoStates >> 2) & 1) == 1;
public Boolean VoltMeasurementAllowed => ((Self._IoStates >> 3) & 1) == 1;
public Boolean AuxRelayBus => ((Self._IoStates >> 4) & 1) == 0;
public Boolean RemoteStateActive => ((Self._IoStates >> 5) & 1) == 1;
public Boolean RiscActive => ((Self._IoStates >> 6) & 1) == 1;
internal IoStatus_(Battery48TlRecord self) => Self = self;
private Battery48TlRecord Self { get; }
}
2023-06-13 10:53:52 +00:00
public readonly struct Temperatures_
{
public Boolean Heating => (Self._IoStates & 64) != 0;
public Temperature Board => Self._TemperaturesBoard;
public Cells_ Cells => new Cells_(Self);
public TemperatureState State => Self.Leds switch
{
{ Green: >= Blinking, Blue: >= Blinking } => TemperatureState.Cold,
_ => TemperatureState.Operation,
// TODO: overheated
};
internal Temperatures_(Battery48TlRecord self) => Self = self;
private Battery48TlRecord Self { get; }
}
public readonly struct Cells_
{
public Temperature Center => Self._TemperaturesCellsCenter;
public Temperature Left => Self._TemperaturesCellsLeft;
public Temperature Right => Self._TemperaturesCellsRight;
public Temperature Average => Self._TemperaturesCellsAverage;
2023-06-13 10:53:52 +00:00
internal Cells_(Battery48TlRecord self) => Self = self;
private Battery48TlRecord Self { get; }
}
public readonly struct Dc_
{
public Voltage Voltage => Self._CellsVoltage;
public Current Current => Self._CellsCurrent;
public ActivePower Power => Self._CellsVoltage * Self._CellsCurrent;
2023-06-13 10:53:52 +00:00
internal Dc_(Battery48TlRecord self) => Self = self;
private Battery48TlRecord Self { get; }
}
2024-03-20 12:52:36 +00:00
private String ParseBatteryState()
{
var s1 = Encoding.ASCII.GetString(BitConverter.GetBytes(_BatteryState1).Reverse().ToArray()); // endian swap
var s2 = Encoding.ASCII.GetString(BitConverter.GetBytes(_BatteryState2).Reverse().ToArray()); // endian swap
return s1 + s2;
}
private Boolean ParseEocReached()
{
return "EOC_" == ParseBatteryState();
}
2023-06-13 10:53:52 +00:00
[SuppressMessage("ReSharper", "StringLiteralTypo")]
2024-02-20 16:06:35 +00:00
private IEnumerable<String> ParseAlarms()
2023-06-13 10:53:52 +00:00
{
2024-02-20 16:06:35 +00:00
Boolean HasBit(Int16 bit) => (_AlarmFlags & 1uL << bit) > 0;
2024-03-11 14:55:48 +00:00
if (HasBit(0)) yield return "Tam : Ambient Temperature too low";
if (HasBit(2)) yield return "TaM2 : Ambient Temperature too high";
2024-02-20 16:06:35 +00:00
if (HasBit(3) ) yield return "Tbm : Battery temperature too low";
if (HasBit(5) ) yield return "TbM2 : Battery temperature too high";
if (HasBit(7) ) yield return "VBm2 : Bus voltage too low";
if (HasBit(9) ) yield return "VBM2 : Bus voltage too high";
if (HasBit(11)) yield return "IDM2 : Discharge current too high";
if (HasBit(12)) yield return "ISOB : Electrical insulation failure";
if (HasBit(13)) yield return "MSWE : Main switch failure";
if (HasBit(14)) yield return "FUSE : Main fuse blown";
if (HasBit(15)) yield return "HTRE : Battery failed to warm up";
if (HasBit(16)) yield return "TCPE : Temperature sensor failure";
if (HasBit(17)) yield return "STRE : Voltage measurement circuit fails";
if (HasBit(18)) yield return "CME : Current sensor failure";
if (HasBit(19)) yield return "HWFL : BMS hardware failure";
if (HasBit(20)) yield return "HWEM : Hardware protection tripped";
if (HasBit(21)) yield return "ThM : Heatsink temperature too high";
if (HasBit(23)) yield return "vsm2 : Low string voltage failure";
if (HasBit(25)) yield return "vsM2 : String voltage too high";
if (HasBit(27)) yield return "iCM2 : Charge current too high";
if (HasBit(29)) yield return "iDM2 : Discharge current too high";
if (HasBit(31)) yield return "MID2 : String voltage unbalance too high";
if (HasBit(42)) yield return "HTFS : Heater Fuse Blown";
if (HasBit(43)) yield return "DATA : Parameters out of range";
2024-03-11 14:55:48 +00:00
if (HasBit(45)) yield return "LMPA : Unbalance string voltages";
if (HasBit(46)) yield return "HEBT : Loss of heartbeat";
if (HasBit(48)) yield return "CURM : Battery charge requested after EDCH";
2023-06-13 10:53:52 +00:00
}
[SuppressMessage("ReSharper", "StringLiteralTypo")]
private IEnumerable<String> ParseWarnings()
2023-06-13 10:53:52 +00:00
{
Boolean HasBit(Int16 bit) => (_WarningFlags & 1uL << bit) > 0;
if (HasBit(1) ) yield return "TaM1: BMS temperature high";
if (HasBit(4) ) yield return "TbM1: Battery temperature high";
if (HasBit(6) ) yield return "VBm1: Bus voltage low";
if (HasBit(8) ) yield return "VBM1: Bus voltage high";
if (HasBit(10)) yield return "IDM1: Discharge current high";
2023-08-10 10:58:05 +00:00
if (HasBit(22)) yield return "vsm1: String voltage too low";
2023-06-13 10:53:52 +00:00
if (HasBit(24)) yield return "vsM1: String voltage high";
if (HasBit(26)) yield return "iCM1: Charge current high";
if (HasBit(28)) yield return "iDM1: Discharge current high";
if (HasBit(30)) yield return "MID1: String voltages unbalanced";
if (HasBit(32)) yield return "BLPW: Not enough charging power on bus";
2023-08-10 10:58:05 +00:00
if (HasBit(33)) yield return "CCBF : Internal charger hardware failure";
2023-06-13 10:53:52 +00:00
if (HasBit(35)) yield return "Ah_W: String SOC low";
if (HasBit(38)) yield return "MPMM: Midpoint wiring problem";
if (HasBit(40)) yield return "TCdi: Temperature difference between strings high";
2023-08-10 10:58:05 +00:00
if (HasBit(44)) yield return "LMPW : String voltages unbalance warning";
2024-03-11 14:55:48 +00:00
if (HasBit(47)) yield return "TOCW : Top of Charge requested";
if (HasBit(49)) yield return "BUSL : Bus lower than string";
2023-06-13 10:53:52 +00:00
}
private Double CalcPowerLimitImposedByVoltageLimit(Double vLimit, Double rInt)
{
var v = Dc.Voltage;
var i = Dc.Current;
var dv = vLimit - v;
var di = dv / rInt;
return vLimit * (i + di);
}
private Double CalcPowerLimitImposedByCurrentLimit(Double iLimit, Double rInt)
{
var v = Dc.Voltage;
var i = Dc.Current;
var di = iLimit - i;
var dv = di * rInt;
return iLimit * (v + dv);
}
public DcPower MaxChargePower
{
get
{
var pLimits = new[]
{
CalcPowerLimitImposedByVoltageLimit(Constants.VMax, Constants.RIntMin),
CalcPowerLimitImposedByVoltageLimit(Constants.VMax, Constants.RIntMax),
CalcPowerLimitImposedByCurrentLimit(Constants.IMax, Constants.RIntMin),
CalcPowerLimitImposedByCurrentLimit(Constants.IMax, Constants.RIntMax)
};
var pLimit = pLimits.Min();
return Math.Max(pLimit, 0);
}
}
public DcPower MaxDischargePower
{
get
{
var pLimits = new[]
{
CalcPowerLimitImposedByVoltageLimit(Constants.VMin, Constants.RIntMin),
CalcPowerLimitImposedByVoltageLimit(Constants.VMin, Constants.RIntMax),
CalcPowerLimitImposedByCurrentLimit(-Constants.IMax, Constants.RIntMin),
CalcPowerLimitImposedByCurrentLimit(-Constants.IMax, Constants.RIntMax),
};
var pLimit = pLimits.Max();
return Math.Min(pLimit, 0);
}
}
}