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