diff --git a/csharp/Lib/Devices/Battery48TL/Battery48TL.csproj b/csharp/Lib/Devices/Battery48TL/Battery48TL.csproj index 92dd034ad..6df76f3b3 100644 --- a/csharp/Lib/Devices/Battery48TL/Battery48TL.csproj +++ b/csharp/Lib/Devices/Battery48TL/Battery48TL.csproj @@ -1,14 +1,12 @@ - - Debug;Release;Release-Server - AnyCPU;linux-arm - - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/csharp/Lib/Devices/Battery48TL/Battery48TLDevice.cs b/csharp/Lib/Devices/Battery48TL/Battery48TLDevice.cs index 97462b720..8d25e2d8d 100644 --- a/csharp/Lib/Devices/Battery48TL/Battery48TLDevice.cs +++ b/csharp/Lib/Devices/Battery48TL/Battery48TLDevice.cs @@ -1,48 +1,47 @@ +using System.IO.Ports; +using InnovEnergy.Lib.Protocols.Modbus.Channels; using InnovEnergy.Lib.Protocols.Modbus.Clients; -using InnovEnergy.Lib.Protocols.Modbus.Connections; -using static InnovEnergy.Lib.Devices.Battery48TL.Constants; +using InnovEnergy.Lib.Protocols.Modbus.Slaves; +using InnovEnergy.Lib.Utils; +using static System.IO.Ports.Parity; namespace InnovEnergy.Lib.Devices.Battery48TL; -public class Battery48TlDevice + +public class Battery48TlDevice: ModbusDevice { - private ModbusClient Modbus { get; } + public const Parity Parity = Odd; + public const Int32 StopBits = 1; + public const Int32 BaudRate = 115200; + public const Int32 DataBits = 8; + + + public Battery48TlDevice(String tty, Byte slaveId, SshHost host) : this + ( + channel: new RemoteSerialChannel(host, tty, BaudRate, Parity, StopBits, DataBits), + slaveId + ) + {} - public Battery48TlDevice(String device, Byte nodeId) + public Battery48TlDevice(String tty, Byte slaveId, String? host = null) : this + ( + channel: host switch + { + null => new SerialPortChannel ( tty, BaudRate, Parity, StopBits, DataBits), + _ => new RemoteSerialChannel(host, tty, BaudRate, Parity, StopBits, DataBits) + }, + slaveId + ) + {} + + public Battery48TlDevice(Channel channel, Byte slaveId) : this + ( + client: new ModbusRtuClient(channel, slaveId) + ) + {} + + public Battery48TlDevice(ModbusClient client): base(client) { - var serialConnection = new ModbusSerialConnection(device, - BaudRate, - Parity, - DataBits, - StopBits, - Constants.Timeout); - - Modbus = new ModbusRtuClient(serialConnection, nodeId); } - private Battery48TlDevice(ModbusClient modbus) // TODO : remove nullable - { - Modbus = modbus; - } - - public static Battery48TlDevice Fake() // TODO : remove nullable - { - return new Battery48TlDevice(null!); - } - - public Battery48TLStatus? ReadStatus() //Already try catch is implemented - { - try - { - return Modbus - .ReadInputRegisters(BaseAddress, NoOfRegisters) - .ParseBatteryStatus(); - } - catch (Exception e) - { - Console.WriteLine(e.Message + " Battery "); - Modbus.CloseConnection(); - return null; - } - } } \ No newline at end of file diff --git a/csharp/Lib/Devices/Battery48TL/Battery48TLStatusRecord.cs b/csharp/Lib/Devices/Battery48TL/Battery48TLStatusRecord.cs deleted file mode 100644 index da885cb32..000000000 --- a/csharp/Lib/Devices/Battery48TL/Battery48TLStatusRecord.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using InnovEnergy.Lib.StatusApi; -using InnovEnergy.Lib.Units; - -namespace InnovEnergy.Lib.Devices.Battery48TL; - -using T = Battery48TLStatus; - -[SuppressMessage("ReSharper", "InconsistentNaming")] -public record Battery48TLStatus : BatteryStatus -{ - public Voltage CellsVoltage { get; init; } - - public Power MaxChargingPower { get; init; } - public Power MaxDischargingPower { get; init; } - - public LedState GreenLed { get; init; } - public LedState AmberLed { get; init; } - public LedState BlueLed { get; init; } - public LedState RedLed { get; init; } - - public IReadOnlyList Warnings { get; init; } = Array.Empty(); - public IReadOnlyList Alarms { get; init; } = Array.Empty(); - - public Boolean EocReached { get; init; } - public Boolean ConnectedToDc { get; init; } - public Boolean Heating { get; init; } - public TemperatureState TemperatureState { get; init; } // cold | operating temperature | overheated - - public Current TotalCurrent { get; init; } - - - - // TODO: strings - // TODO - // public State LimitedBy { get; init; } - - // TODO - // public Boolean AlarmOutActive { get; init; } - // public Boolean InternalFanActive { get; init; } - // public Boolean VoltMeasurementAllowed { get; init; } - // public Boolean AuxRelay { get; init; } - // public Boolean RemoteState { get; init; } - -} - - - - diff --git a/csharp/Lib/Devices/Battery48TL/Battery48TlRegisters.cs b/csharp/Lib/Devices/Battery48TL/Battery48TlRegisters.cs new file mode 100644 index 000000000..f1113fa51 --- /dev/null +++ b/csharp/Lib/Devices/Battery48TL/Battery48TlRegisters.cs @@ -0,0 +1,55 @@ +using System.Diagnostics.CodeAnalysis; +using InnovEnergy.Lib.Devices.Battery48TL.DataTypes; +using InnovEnergy.Lib.Protocols.Modbus.Reflection.Attributes; +using InnovEnergy.Lib.StatusApi.DeviceTypes; +using InnovEnergy.Lib.Units; +using InnovEnergy.Lib.Units.Composite; + +namespace InnovEnergy.Lib.Devices.Battery48TL; + + +#pragma warning disable CS0169, CS0649 + +[SuppressMessage("ReSharper", "InconsistentNaming")] +[SuppressMessage("ReSharper", "UnusedMember.Global")] +[BigEndian] // alarm/warning flags +public record Battery48TlRegisters : IBattery +{ + [InputRegister(1004)] internal UInt16 _LedStates; + + [InputRegister(1005)] internal UInt64 _WarningFlags; + [InputRegister(1009)] internal UInt64 _AlarmFlags; + + [InputRegister(1013)] internal UInt16 _IoStates; + + [InputRegister(999, Scale = 0.01)] internal Double _Voltage ; + [InputRegister(1000, Scale = 0.01, Offset = -10000)] internal Double _Current ; + [InputRegister(1053, Scale = 0.1)] internal Double _Soc; + + [InputRegister(1014, Scale = 0.1, Offset = -400)] internal Double _TemperatureBoard ; + [InputRegister(1015, Scale = 0.1, Offset = -400)] internal Double _TemperatureCellsCenter ; + [InputRegister(1016, Scale = 0.1, Offset = -400)] internal Double _TemperatureCellsLeft ; + [InputRegister(1017, Scale = 0.1, Offset = -400)] internal Double _TemperatureCellsRight ; + + + public DcBus Dc => DcBus.FromVoltageCurrent(_Voltage, _Current); + public Percent Soc => _Soc; + + public Leds Leds => this.ParseLeds(); + public Temperatures Temperature => this.ParseBatteryTemperatures(); + + public Boolean ConnectedToDcBus => this.ParseConnectedToDcBus(); + public Boolean Heating => this.ParseHeating(); + public Boolean Eoc => this.ParseEoc(); + + + public IReadOnlyList Warnings => this.ParseWarnings().ToList() ; + public IReadOnlyList Alarms => this.ParseAlarms().ToList() ; + + // + + // public Decimal CellsVoltage { get; init; } + // + // public Decimal MaxChargingPower { get; init; } + // public Decimal MaxDischargingPower { get; init; } +} \ No newline at end of file diff --git a/csharp/Lib/Devices/Battery48TL/DataTypes/CellTemperatures.cs b/csharp/Lib/Devices/Battery48TL/DataTypes/CellTemperatures.cs new file mode 100644 index 000000000..8fccf3583 --- /dev/null +++ b/csharp/Lib/Devices/Battery48TL/DataTypes/CellTemperatures.cs @@ -0,0 +1,11 @@ +using InnovEnergy.Lib.Units; + +namespace InnovEnergy.Lib.Devices.Battery48TL.DataTypes; + +public readonly struct CellTemperatures +{ + public Temperature Center { get; internal init; } + public Temperature Left { get; internal init; } + public Temperature Right { get; internal init; } +} + diff --git a/csharp/Lib/Devices/Battery48TL/Constants.cs b/csharp/Lib/Devices/Battery48TL/DataTypes/Constants.cs similarity index 88% rename from csharp/Lib/Devices/Battery48TL/Constants.cs rename to csharp/Lib/Devices/Battery48TL/DataTypes/Constants.cs index ea2e7501e..26d378118 100644 --- a/csharp/Lib/Devices/Battery48TL/Constants.cs +++ b/csharp/Lib/Devices/Battery48TL/DataTypes/Constants.cs @@ -1,9 +1,8 @@ using System.Diagnostics.CodeAnalysis; using System.IO.Ports; using static System.IO.Ports.Parity; -using static System.IO.Ports.StopBits; -namespace InnovEnergy.Lib.Devices.Battery48TL; +namespace InnovEnergy.Lib.Devices.Battery48TL.DataTypes; [SuppressMessage("ReSharper", "InconsistentNaming")] public static class Constants @@ -12,7 +11,7 @@ public static class Constants public const Int32 NoOfRegisters = 56; public const Parity Parity = Odd; - public const StopBits StopBits = One; + public const Int32 StopBits = 1; public const Int32 BaudRate = 115200; public const Int32 DataBits = 8; public static TimeSpan Timeout { get; } = TimeSpan.FromMilliseconds(100); diff --git a/csharp/Lib/Devices/Battery48TL/DataTypes/LedColor.cs b/csharp/Lib/Devices/Battery48TL/DataTypes/LedColor.cs new file mode 100644 index 000000000..595e67f38 --- /dev/null +++ b/csharp/Lib/Devices/Battery48TL/DataTypes/LedColor.cs @@ -0,0 +1,9 @@ +namespace InnovEnergy.Lib.Devices.Battery48TL.DataTypes; + +public enum LedColor +{ + Green = 0, // don't change: numbers are important + Amber = 2, + Blue = 4, + Red = 6, +} \ No newline at end of file diff --git a/csharp/Lib/Devices/Battery48TL/DataTypes/LedState.cs b/csharp/Lib/Devices/Battery48TL/DataTypes/LedState.cs new file mode 100644 index 000000000..07ff0d01c --- /dev/null +++ b/csharp/Lib/Devices/Battery48TL/DataTypes/LedState.cs @@ -0,0 +1,9 @@ +namespace InnovEnergy.Lib.Devices.Battery48TL.DataTypes; + +public enum LedState +{ + Off = 0, + On = 1, + Blinking = 2, + BlinkingFast = 3 +} \ No newline at end of file diff --git a/csharp/Lib/Devices/Battery48TL/DataTypes/Leds.cs b/csharp/Lib/Devices/Battery48TL/DataTypes/Leds.cs new file mode 100644 index 000000000..a3edab38b --- /dev/null +++ b/csharp/Lib/Devices/Battery48TL/DataTypes/Leds.cs @@ -0,0 +1,9 @@ +namespace InnovEnergy.Lib.Devices.Battery48TL.DataTypes; + +public readonly struct Leds +{ + public LedState Blue { get; internal init; } + public LedState Green { get; internal init; } + public LedState Amber { get; internal init; } + public LedState Red { get; internal init; } +} \ No newline at end of file diff --git a/csharp/Lib/Devices/Battery48TL/DataTypes/TemperatureState.cs b/csharp/Lib/Devices/Battery48TL/DataTypes/TemperatureState.cs new file mode 100644 index 000000000..dd915a33f --- /dev/null +++ b/csharp/Lib/Devices/Battery48TL/DataTypes/TemperatureState.cs @@ -0,0 +1,8 @@ +namespace InnovEnergy.Lib.Devices.Battery48TL.DataTypes; + +public enum TemperatureState +{ + Cold = 0, + Operation = 1, + Overheated = 2, +} \ No newline at end of file diff --git a/csharp/Lib/Devices/Battery48TL/DataTypes/Temperatures.cs b/csharp/Lib/Devices/Battery48TL/DataTypes/Temperatures.cs new file mode 100644 index 000000000..d71fdef17 --- /dev/null +++ b/csharp/Lib/Devices/Battery48TL/DataTypes/Temperatures.cs @@ -0,0 +1,23 @@ +using InnovEnergy.Lib.Units; +using static InnovEnergy.Lib.Devices.Battery48TL.DataTypes.LedState; + +namespace InnovEnergy.Lib.Devices.Battery48TL.DataTypes; + +public readonly struct Temperatures +{ + public CellTemperatures Cells { get; internal init; } + public Temperature Board { get; internal init; } + public TemperatureState State { get; internal init; } +} + + + + + + // public abstract record Result + // { + // private Result() { } + // + // public sealed record Ok(object result) : Result; + // public sealed record Error(string message) : Result; + // } diff --git a/csharp/Lib/Devices/Battery48TL/Doc/configure_tty.txt b/csharp/Lib/Devices/Battery48TL/Doc/configure_tty.txt new file mode 100644 index 000000000..0c7e944ef --- /dev/null +++ b/csharp/Lib/Devices/Battery48TL/Doc/configure_tty.txt @@ -0,0 +1,2 @@ + +stty -F /dev/ttyUSB0 <<< '0:0:1bb2:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0' diff --git a/csharp/Lib/Devices/Battery48TL/LedColor.cs b/csharp/Lib/Devices/Battery48TL/LedColor.cs deleted file mode 100644 index 5c91fa53d..000000000 --- a/csharp/Lib/Devices/Battery48TL/LedColor.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace InnovEnergy.Lib.Devices.Battery48TL; - -public enum LedColor -{ - Green = 0, - Amber = 1, - Blue = 2, - Red = 3, -} \ No newline at end of file diff --git a/csharp/Lib/Devices/Battery48TL/LedState.cs b/csharp/Lib/Devices/Battery48TL/LedState.cs deleted file mode 100644 index 937c9e7e4..000000000 --- a/csharp/Lib/Devices/Battery48TL/LedState.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace InnovEnergy.Lib.Devices.Battery48TL; - -public enum LedState -{ - Off = 0, - On = 1, - BlinkingSlow = 2, - BlinkingFast = 3 -} \ No newline at end of file diff --git a/csharp/Lib/Devices/Battery48TL/ModbusParser.cs b/csharp/Lib/Devices/Battery48TL/ModbusParser.cs index 6877c6a05..3ffc5bbb2 100644 --- a/csharp/Lib/Devices/Battery48TL/ModbusParser.cs +++ b/csharp/Lib/Devices/Battery48TL/ModbusParser.cs @@ -1,274 +1,183 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text; -using InnovEnergy.Lib.Protocols.Modbus.Conversions; -using InnovEnergy.Lib.Units.Composite; -using InnovEnergy.Lib.Utils; -using static InnovEnergy.Lib.Devices.Battery48TL.LedState; - -namespace InnovEnergy.Lib.Devices.Battery48TL; - -public static class ModbusParser -{ - internal static Battery48TLStatus ParseBatteryStatus(this ModbusRegisters data) - { - var greenLed = data.ParseLedState(register: 1005, led: LedColor.Green); - var amberLed = data.ParseLedState(register: 1005, led: LedColor.Amber); - var blueLed = data.ParseLedState(register: 1005, led: LedColor.Blue); - var redLed = data.ParseLedState(register: 1005, led: LedColor.Red); - - var soc = data.ParseSoc(); - - // var eoc = greenLed is On - // && amberLed is Off - // && blueLed is Off; - - var eoc = data.ParseEocReached(); - - var maxSoc = eoc ? 100m : 99.9m; - - var batteryCold = greenLed >= BlinkingSlow - && blueLed >= BlinkingSlow; - - var temperatureState = batteryCold - ? TemperatureState.Cold - : TemperatureState.OperatingTemperature; // TODO: overheated - - - return new Battery48TLStatus - { - Dc = data.ParseDcBus(), - Alarms = data.ParseAlarms().ToList(), - Warnings = data.ParseWarnings().ToList(), - Soc = Math.Min(soc, maxSoc), - Temperature = data.ParseDecimal(register: 1004, scaleFactor: 0.1m, offset: -400), - GreenLed = greenLed, - AmberLed = amberLed, - BlueLed = blueLed, - RedLed = redLed, - Heating = data.ParseBool(baseRegister: 1014, bit: 6), - ConnectedToDc = data.ParseBool(baseRegister: 1014, bit: 0), - TemperatureState = temperatureState, - MaxChargingPower = data.CalcMaxChargePower(), - MaxDischargingPower = data.CalcMaxDischargePower(), - CellsVoltage = data.ParseDecimal(register: 1000, scaleFactor: 0.01m), - TotalCurrent = data.ReadTotalCurrent(), - EocReached = eoc - }; - } - - - public static Decimal ParseDecimal(this ModbusRegisters data, Int32 register, Decimal scaleFactor = 1.0m, Double offset = 0.0) - { - var value = data[register].ConvertTo(); // widen to 32bit signed - - if (value >= 0x8000) - value -= 0x10000; // Fiamm stores their integers signed AND with sign-offset @#%^&! - - return (Decimal)(value + offset) * scaleFactor; - } - - internal static Decimal ParseCurrent(this ModbusRegisters data) - { - return data.ParseDecimal(register: 1001, scaleFactor: 0.01m, offset: -10000); - } - - internal static Decimal ParseBusVoltage(this ModbusRegisters data) - { - return data.ParseDecimal(register: 1002, scaleFactor: 0.01m); - } - - internal static Decimal ReadTotalCurrent(this ModbusRegisters data) - { - try - { - return ParseDecimal(data, register: 1063, scaleFactor: 0.01m, offset: -100); - } - catch (Exception e) - { - Console.WriteLine(e + " Read Total current fail "); - throw; - } - } - - internal static Boolean ParseBool(this ModbusRegisters data, Int32 baseRegister, Int16 bit) - { - var x = bit / 16; - var y = bit % 16; - - var value = (UInt32)data[baseRegister + x]; - - return (value & (1 << y)) > 0; - } - - internal static LedState ParseLedState(this ModbusRegisters data, Int32 register, LedColor led) - { - var lo = data.ParseBool(register, (led.ConvertTo() * 2 ).ConvertTo()); - var hi = data.ParseBool(register, (led.ConvertTo() * 2 + 1).ConvertTo()); - - return (hi, lo) switch - { - (false, false) => Off, - (false, true) => On, - (true, false) => BlinkingSlow, - (true, true) => BlinkingFast, - }; - } - - internal static String ParseString(this ModbusRegisters data, Int32 register, Int16 count) - { - return Enumerable - .Range(register, count) - .Select(i => data[i]) - .Select(BitConverter.GetBytes) - .Select(Encoding.ASCII.GetString) - .Aggregate("", (a, b) => a + b[1] + b[0]); // endian swap - } - - internal static Boolean ParseEocReached(this ModbusRegisters data) - { - var s = ParseString(data, 1061, 2); - return "EOC_" == s; - } - - internal static Decimal ParseSoc(this ModbusRegisters data) - { - return data.ParseDecimal(register: 1054, scaleFactor: 0.1m); - } - - private static Decimal CalcPowerLimitImposedByVoltageLimit(Decimal v,Decimal i,Decimal vLimit,Decimal rInt) - { - var dv = vLimit - v; - var di = dv / rInt; - var pLimit = vLimit * (i + di); - - return pLimit; - } - - private static Decimal CalcPowerLimitImposedByCurrentLimit(Decimal v, Decimal i, Decimal iLimit, Decimal rInt) - { - var di = iLimit - i; - var dv = di * rInt; - var pLimit = iLimit * (v + dv); - - return pLimit; - } - - - private static Decimal CalcPowerLimitImposedByTempLimit(Decimal t, Decimal maxAllowedTemp, Decimal power , Decimal setpoint) - { - // const Int32 holdZone = 300; - // const Int32 maxAllowedTemp = 315; - - var kp = 0.05m; - var error = setpoint - power; - var controlOutput = (kp * error) *(1 - Math.Abs((t-307.5m)/7.5m)); - - return controlOutput; - - // var a = holdZone - maxAllowedTemp; - // var b = -a * maxAllowedTemp; - } - - internal static Decimal CalcMaxChargePower(this ModbusRegisters data) - { - var v = data.ParseDecimal(register: 1000, scaleFactor: 0.01m); - var i = ParseCurrent(data); - - var pLimits = new[] - { - // TODO: review - CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMax, Constants.RIntMin), - CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMax, Constants.RIntMax), - CalcPowerLimitImposedByCurrentLimit(v, i, Constants.IMax, Constants.RIntMin), - CalcPowerLimitImposedByCurrentLimit(v, i, Constants.IMax, Constants.RIntMax) - }; - - var pLimit = pLimits.Min(); - - return Math.Max(pLimit, 0); - } - - internal static DcBus ParseDcBus(this ModbusRegisters data) => new() - { - Current = data.ParseCurrent(), - Voltage = data.ParseBusVoltage(), - }; - - internal static Decimal CalcMaxDischargePower(this ModbusRegisters data) - { - var v = data.ParseDecimal(register: 1000, scaleFactor: 0.01m); - var i = ParseCurrent(data); - - var pLimits = new[] - { - // TODO: review - CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMin, Constants.RIntMin), - CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMin, Constants.RIntMax), - CalcPowerLimitImposedByCurrentLimit(v, i, -Constants.IMax, Constants.RIntMin), - CalcPowerLimitImposedByCurrentLimit(v, i, -Constants.IMax, Constants.RIntMax), - // CalcPowerLimitImposedByTempLimit(t,315,300) - }; - - var pLimit = pLimits.Max(); - - return Math.Min(pLimit, 0); - } - - - [SuppressMessage("ReSharper", "StringLiteralTypo")] - internal static IEnumerable ParseAlarms(this ModbusRegisters data) - { - if (data.ParseBool(1010, 0)) yield return "Tam : BMS temperature too low"; - if (data.ParseBool(1010, 2)) yield return "TaM2 : BMS temperature too high"; - if (data.ParseBool(1010, 3)) yield return "Tbm : Battery temperature too low"; - if (data.ParseBool(1010, 5)) yield return "TbM2 : Battery temperature too high"; - if (data.ParseBool(1010, 7)) yield return "VBm2 : Bus voltage too low"; - if (data.ParseBool(1010, 9)) yield return "VBM2 : Bus voltage too high"; - if (data.ParseBool(1010, 11)) yield return "IDM2 : Discharge current too high"; - if (data.ParseBool(1010, 12)) yield return "ISOB : Electrical insulation failure"; - if (data.ParseBool(1010, 13)) yield return "MSWE : Main switch failure"; - if (data.ParseBool(1010, 14)) yield return "FUSE : Main fuse blown"; - if (data.ParseBool(1010, 15)) yield return "HTRE : Battery failed to warm up"; - if (data.ParseBool(1010, 16)) yield return "TCPE : Temperature sensor failure"; - if (data.ParseBool(1010, 17)) yield return "STRE :"; - if (data.ParseBool(1010, 18)) yield return "CME : Current sensor failure"; - if (data.ParseBool(1010, 19)) yield return "HWFL : BMS hardware failure"; - if (data.ParseBool(1010, 20)) yield return "HWEM : Hardware protection tripped"; - if (data.ParseBool(1010, 21)) yield return "ThM : Heatsink temperature too high"; - if (data.ParseBool(1010, 22)) yield return "vsm1 : String voltage too low"; - if (data.ParseBool(1010, 23)) yield return "vsm2 : Low string voltage failure"; - if (data.ParseBool(1010, 25)) yield return "vsM2 : String voltage too high"; - if (data.ParseBool(1010, 27)) yield return "iCM2 : Charge current too high"; - if (data.ParseBool(1010, 29)) yield return "iDM2 : Discharge current too high"; - if (data.ParseBool(1010, 31)) yield return "MID2 : String voltage unbalance too high"; - if (data.ParseBool(1010, 33)) yield return "CCBF : Internal charger hardware failure"; - if (data.ParseBool(1010, 34)) yield return "AhFL :"; - if (data.ParseBool(1010, 36)) yield return "TbCM :"; - if (data.ParseBool(1010, 37)) yield return "BRNF :"; - if (data.ParseBool(1010, 42)) yield return "HTFS : If Heaters Fuse Blown"; - if (data.ParseBool(1010, 43)) yield return "DATA : Parameters out of range"; - if (data.ParseBool(1010, 45)) yield return "CELL2:"; - } - - [SuppressMessage("ReSharper", "StringLiteralTypo")] - internal static IEnumerable ParseWarnings(this ModbusRegisters data) - { - if (data.ParseBool(1006, 1)) yield return "TaM1: BMS temperature high"; - if (data.ParseBool(1006, 4)) yield return "TbM1: Battery temperature high"; - if (data.ParseBool(1006, 6)) yield return "VBm1: Bus voltage low"; - if (data.ParseBool(1006, 8)) yield return "VBM1: Bus voltage high"; - if (data.ParseBool(1006, 10)) yield return "IDM1: Discharge current high"; - if (data.ParseBool(1006, 24)) yield return "vsM1: String voltage high"; - if (data.ParseBool(1006, 26)) yield return "iCM1: Charge current high"; - if (data.ParseBool(1006, 28)) yield return "iDM1: Discharge current high"; - if (data.ParseBool(1006, 30)) yield return "MID1: String voltages unbalanced"; - if (data.ParseBool(1006, 32)) yield return "BLPW: Not enough charging power on bus"; - if (data.ParseBool(1006, 35)) yield return "Ah_W: String SOC low"; - if (data.ParseBool(1006, 38)) yield return "MPMM: Midpoint wiring problem"; - if (data.ParseBool(1006, 39)) yield return "TCMM:"; - if (data.ParseBool(1006, 40)) yield return "TCdi: Temperature difference between strings high"; - if (data.ParseBool(1006, 41)) yield return "WMTO:"; - if (data.ParseBool(1006, 44)) yield return "bit44:"; - if (data.ParseBool(1006, 46)) yield return "CELL1:"; - } -} \ No newline at end of file +// using System.Diagnostics.CodeAnalysis; +// using InnovEnergy.Lib.Devices.Battery48TL.DataTypes; +// using InnovEnergy.Lib.Protocols.Modbus.Conversions; +// using InnovEnergy.Lib.Utils; +// +// namespace InnovEnergy.Lib.Devices.Battery48TL; +// +// public static class ModbusParser +// { +// public static Decimal ParseDecimal(this ModbusRegisters data, Int32 register, Decimal scaleFactor = 1.0m, Double offset = 0.0) +// { +// var value = data[register].ConvertTo(); // widen to 32bit signed +// +// if (value >= 0x8000) +// value -= 0x10000; // Fiamm stores their integers signed AND with sign-offset @#%^&! +// +// return (Decimal)(value + offset) * scaleFactor; +// } +// +// internal static Decimal ParseCurrent(this ModbusRegisters data) +// { +// return data.ParseDecimal(register: 1001, scaleFactor: 0.01m, offset: -10000); +// } +// +// internal static Decimal ParseBusVoltage(this ModbusRegisters data) +// { +// return data.ParseDecimal(register: 1002, scaleFactor: 0.01m); +// } +// +// internal static Boolean ParseBool(this ModbusRegisters data, Int32 baseRegister, Int16 bit) +// { +// var x = bit / 16; +// var y = bit % 16; +// +// var value = (UInt32)data[baseRegister + x]; +// +// return (value & (1 << y)) > 0; +// } +// +// +// internal static LedState ParseLed(this UInt16 ledStates, LedColor led) +// { +// return (LedState)((ledStates >> (Int32)led * 2) & 3); +// } +// +// +// private static Decimal CalcPowerLimitImposedByVoltageLimit(Decimal v, Decimal i, Decimal vLimit, Decimal rInt) +// { +// var dv = vLimit - v; +// var di = dv / rInt; +// var pLimit = vLimit * (i + di); +// +// return pLimit; +// } +// +// private static Decimal CalcPowerLimitImposedByCurrentLimit(Decimal v, Decimal i, Decimal iLimit, Decimal rInt) +// { +// var di = iLimit - i; +// var dv = di * rInt; +// var pLimit = iLimit * (v + dv); +// +// return pLimit; +// } +// +// +// private static Decimal CalcPowerLimitImposedByTempLimit(Decimal t, Decimal maxAllowedTemp, Decimal power , Decimal setpoint) +// { +// // const Int32 holdZone = 300; +// // const Int32 maxAllowedTemp = 315; +// +// var kp = 0.05m; +// var error = setpoint - power; +// var controlOutput = (kp * error) *(1 - Math.Abs((t-307.5m)/7.5m)); +// +// return controlOutput; +// +// // var a = holdZone - maxAllowedTemp; +// // var b = -a * maxAllowedTemp; +// } +// +// internal static Decimal CalcMaxChargePower(this ModbusRegisters data) +// { +// var v = data.ParseDecimal(register: 1000, scaleFactor: 0.01m); +// var i = ParseCurrent(data); +// +// var pLimits = new[] +// { +// // TODO: review +// CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMax, Constants.RIntMin), +// CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMax, Constants.RIntMax), +// CalcPowerLimitImposedByCurrentLimit(v, i, Constants.IMax, Constants.RIntMin), +// CalcPowerLimitImposedByCurrentLimit(v, i, Constants.IMax, Constants.RIntMax) +// }; +// +// var pLimit = pLimits.Min(); +// +// return Math.Max(pLimit, 0); +// } +// +// internal static DcBus ParseDcBus(this ModbusRegisters data) => new() +// { +// Current = data.ParseCurrent(), +// Voltage = data.ParseBusVoltage(), +// }; +// +// internal static Decimal CalcMaxDischargePower(this ModbusRegisters data) +// { +// var v = data.ParseDecimal(register: 1000, scaleFactor: 0.01m); +// var i = ParseCurrent(data); +// +// var pLimits = new[] +// { +// // TODO: review +// CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMin, Constants.RIntMin), +// CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMin, Constants.RIntMax), +// CalcPowerLimitImposedByCurrentLimit(v, i, -Constants.IMax, Constants.RIntMin), +// CalcPowerLimitImposedByCurrentLimit(v, i, -Constants.IMax, Constants.RIntMax), +// // CalcPowerLimitImposedByTempLimit(t,315,300) +// }; +// +// var pLimit = pLimits.Max(); +// +// return Math.Min(pLimit, 0); +// } +// +// +// [SuppressMessage("ReSharper", "StringLiteralTypo")] +// internal static IEnumerable ParseAlarms(this ModbusRegisters data) +// { +// if (data.ParseBool(1010, 0)) yield return "Tam : BMS temperature too low"; +// if (data.ParseBool(1010, 2)) yield return "TaM2 : BMS temperature too high"; +// if (data.ParseBool(1010, 3)) yield return "Tbm : Battery temperature too low"; +// if (data.ParseBool(1010, 5)) yield return "TbM2 : Battery temperature too high"; +// if (data.ParseBool(1010, 7)) yield return "VBm2 : Bus voltage too low"; +// if (data.ParseBool(1010, 9)) yield return "VBM2 : Bus voltage too high"; +// if (data.ParseBool(1010, 11)) yield return "IDM2 : Discharge current too high"; +// if (data.ParseBool(1010, 12)) yield return "ISOB : Electrical insulation failure"; +// if (data.ParseBool(1010, 13)) yield return "MSWE : Main switch failure"; +// if (data.ParseBool(1010, 14)) yield return "FUSE : Main fuse blown"; +// if (data.ParseBool(1010, 15)) yield return "HTRE : Battery failed to warm up"; +// if (data.ParseBool(1010, 16)) yield return "TCPE : Temperature sensor failure"; +// if (data.ParseBool(1010, 17)) yield return "STRE :"; +// if (data.ParseBool(1010, 18)) yield return "CME : Current sensor failure"; +// if (data.ParseBool(1010, 19)) yield return "HWFL : BMS hardware failure"; +// if (data.ParseBool(1010, 20)) yield return "HWEM : Hardware protection tripped"; +// if (data.ParseBool(1010, 21)) yield return "ThM : Heatsink temperature too high"; +// if (data.ParseBool(1010, 22)) yield return "vsm1 : String voltage too low"; +// if (data.ParseBool(1010, 23)) yield return "vsm2 : Low string voltage failure"; +// if (data.ParseBool(1010, 25)) yield return "vsM2 : String voltage too high"; +// if (data.ParseBool(1010, 27)) yield return "iCM2 : Charge current too high"; +// if (data.ParseBool(1010, 29)) yield return "iDM2 : Discharge current too high"; +// if (data.ParseBool(1010, 31)) yield return "MID2 : String voltage unbalance too high"; +// if (data.ParseBool(1010, 33)) yield return "CCBF : Internal charger hardware failure"; +// if (data.ParseBool(1010, 34)) yield return "AhFL :"; +// if (data.ParseBool(1010, 36)) yield return "TbCM :"; +// if (data.ParseBool(1010, 37)) yield return "BRNF :"; +// if (data.ParseBool(1010, 42)) yield return "HTFS : If Heaters Fuse Blown"; +// if (data.ParseBool(1010, 43)) yield return "DATA : Parameters out of range"; +// if (data.ParseBool(1010, 45)) yield return "CELL2:"; +// } +// +// [SuppressMessage("ReSharper", "StringLiteralTypo")] +// internal static IEnumerable ParseWarnings(this ModbusRegisters data) +// { +// if (data.ParseBool(1006, 1)) yield return "TaM1: BMS temperature high"; +// if (data.ParseBool(1006, 4)) yield return "TbM1: Battery temperature high"; +// if (data.ParseBool(1006, 6)) yield return "VBm1: Bus voltage low"; +// if (data.ParseBool(1006, 8)) yield return "VBM1: Bus voltage high"; +// if (data.ParseBool(1006, 10)) yield return "IDM1: Discharge current high"; +// if (data.ParseBool(1006, 24)) yield return "vsM1: String voltage high"; +// if (data.ParseBool(1006, 26)) yield return "iCM1: Charge current high"; +// if (data.ParseBool(1006, 28)) yield return "iDM1: Discharge current high"; +// if (data.ParseBool(1006, 30)) yield return "MID1: String voltages unbalanced"; +// if (data.ParseBool(1006, 32)) yield return "BLPW: Not enough charging power on bus"; +// if (data.ParseBool(1006, 35)) yield return "Ah_W: String SOC low"; +// if (data.ParseBool(1006, 38)) yield return "MPMM: Midpoint wiring problem"; +// if (data.ParseBool(1006, 39)) yield return "TCMM:"; +// if (data.ParseBool(1006, 40)) yield return "TCdi: Temperature difference between strings high"; +// if (data.ParseBool(1006, 41)) yield return "WMTO:"; +// if (data.ParseBool(1006, 44)) yield return "bit44:"; +// if (data.ParseBool(1006, 46)) yield return "CELL1:"; +// } +// } \ No newline at end of file diff --git a/csharp/Lib/Devices/Battery48TL/Parser.cs b/csharp/Lib/Devices/Battery48TL/Parser.cs new file mode 100644 index 000000000..016073203 --- /dev/null +++ b/csharp/Lib/Devices/Battery48TL/Parser.cs @@ -0,0 +1,122 @@ +using System.Diagnostics.CodeAnalysis; +using InnovEnergy.Lib.Devices.Battery48TL.DataTypes; +using InnovEnergy.Lib.Protocols.Modbus.Conversions; +using static InnovEnergy.Lib.Devices.Battery48TL.DataTypes.LedState; +using static InnovEnergy.Lib.Devices.Battery48TL.DataTypes.TemperatureState; + +namespace InnovEnergy.Lib.Devices.Battery48TL; + +using R = Battery48TlRegisters; + +internal static class Parser +{ + internal static Boolean ParseEoc(this R r) + { + return r.Leds is + { + Green: On, + Amber: Off, + Blue : Off + }; + } + + internal static Leds ParseLeds(this R r) + { + LedState ParseLed(LedColor led) => (LedState)((r._LedStates >> (Int32)led) & 3); + + return new() + { + Blue = ParseLed(LedColor.Blue), + Green = ParseLed(LedColor.Green), + Amber = ParseLed(LedColor.Amber), + Red = ParseLed(LedColor.Red), + }; + } + + internal static Temperatures ParseBatteryTemperatures(this R r) => new() + { + Board = r._TemperatureBoard, + Cells = r.ParseCells(), + State = r.ParseTemperatureState() + }; + + private static TemperatureState ParseTemperatureState(this R r) => r.Leds switch + { + { Green: >= Blinking, Blue: >= Blinking } => Cold, + _ => Operation, + // TODO: overheated + }; + + private static CellTemperatures ParseCells(this R r) => new() + { + Left = r._TemperatureCellsLeft, + Right = r._TemperatureCellsRight, + Center = r._TemperatureCellsCenter + }; + + internal static Boolean ParseConnectedToDcBus(this R r) => (r._IoStates & 1) == 0; // inverted + internal static Boolean ParseHeating (this R r) => (r._IoStates & 64) != 0; + + + [SuppressMessage("ReSharper", "StringLiteralTypo")] + internal static IEnumerable ParseAlarms(this R data) + { + Boolean HasBit(Int16 bit) => (data._AlarmFlags & 1uL << bit) > 0; + + if (HasBit(0) ) yield return "Tam : BMS temperature too low"; + if (HasBit(2) ) yield return "TaM2 : BMS temperature too high"; + 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 :"; + 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(22)) yield return "vsm1 : String voltage too low"; + 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(33)) yield return "CCBF : Internal charger hardware failure"; + if (HasBit(34)) yield return "AhFL :"; + if (HasBit(36)) yield return "TbCM :"; + if (HasBit(37)) yield return "BRNF :"; + if (HasBit(42)) yield return "HTFS : If Heaters Fuse Blown"; + if (HasBit(43)) yield return "DATA : Parameters out of range"; + if (HasBit(45)) yield return "CELL2:"; + } + + [SuppressMessage("ReSharper", "StringLiteralTypo")] + internal static IEnumerable ParseWarnings(this R data) + { + Boolean HasBit(Int16 bit) => (data._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"; + 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"; + if (HasBit(35)) yield return "Ah_W: String SOC low"; + if (HasBit(38)) yield return "MPMM: Midpoint wiring problem"; + if (HasBit(39)) yield return "TCMM:"; + if (HasBit(40)) yield return "TCdi: Temperature difference between strings high"; + if (HasBit(41)) yield return "WMTO:"; + if (HasBit(44)) yield return "bit44:"; + if (HasBit(46)) yield return "CELL1:"; + } + +} \ No newline at end of file diff --git a/csharp/Lib/Devices/Battery48TL/Program.cs b/csharp/Lib/Devices/Battery48TL/Program.cs new file mode 100644 index 000000000..66727319a --- /dev/null +++ b/csharp/Lib/Devices/Battery48TL/Program.cs @@ -0,0 +1,22 @@ + +using System.Text.Json; +using System.Text.Json.Serialization; +using InnovEnergy.Lib.Utils; + +namespace InnovEnergy.Lib.Devices.Battery48TL; + +public static class Program +{ + public static Task Main(string[] args) + { + var d = new Battery48TlDevice("ttyUSB0", 2, "10.2.3.47"); + + var options = new JsonSerializerOptions { WriteIndented = true, Converters = { new JsonStringEnumConverter() }}; + + while (true) + { + var x = d.Read(); + (x, options).Apply(JsonSerializer.Serialize).WriteLine(); + } + } +} \ No newline at end of file diff --git a/csharp/Lib/Devices/Battery48TL/TemperatureState.cs b/csharp/Lib/Devices/Battery48TL/TemperatureState.cs deleted file mode 100644 index 6bb71b4e1..000000000 --- a/csharp/Lib/Devices/Battery48TL/TemperatureState.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace InnovEnergy.Lib.Devices.Battery48TL; - -public enum TemperatureState -{ - Cold = 0, - OperatingTemperature = 1, - Overheated = 2, -} \ No newline at end of file