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
{
2023-09-01 08:48:44 +00:00
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 IoStates = > _IoStates ;
public UInt16 LimpBitMap = > _LimpBitMap ;
2024-03-20 12:52:36 +00:00
public String BatteryState = > ParseBatteryState ( ) ;
2023-09-07 14:39:27 +00:00
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 ;
2023-10-04 13:18:45 +00:00
2024-02-20 16:06:35 +00:00
public Current CellsCurrent = > _CellsCurrent ;
2023-10-04 13:18:45 +00:00
2024-02-07 13:32:17 +00:00
public Current HeatingCurrent = > _BusCurrent - _CellsCurrent ;
public DcPower HeatingPower = > HeatingCurrent * Dc . Voltage ;
2023-10-04 13:18:45 +00:00
2023-09-01 08:48:44 +00:00
// 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
2023-09-18 09:15:18 +00:00
public TimeSpan TimeSinceTOC = > TimeSpan . FromMinutes ( _TimeSinceToc ) ;
2023-09-01 08:48:44 +00:00
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-08-30 11:38:50 +00:00
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-09-01 08:48:44 +00:00
2023-06-13 10:53:52 +00:00
}
2023-09-07 14:39:27 +00:00
2024-02-05 14:30:47 +00:00
public readonly struct StringActive_
2023-09-07 14:39:27 +00:00
{
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 ;
2023-09-07 14:39:27 +00:00
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_
{
2023-06-22 07:50:24 +00:00
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_
{
2023-07-03 12:21:00 +00:00
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 & 1 uL < < 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")]
2023-06-22 07:50:24 +00:00
private IEnumerable < String > ParseWarnings ( )
2023-06-13 10:53:52 +00:00
{
Boolean HasBit ( Int16 bit ) = > ( _WarningFlags & 1 uL < < 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 ) ;
}
}
2023-10-05 10:53:38 +00:00
}