diff --git a/csharp/InnovEnergy.sln b/csharp/InnovEnergy.sln index 10c541c54..24a874d54 100644 --- a/csharp/InnovEnergy.sln +++ b/csharp/InnovEnergy.sln @@ -93,6 +93,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SofarInverter", "Lib\Device EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SchneiderMeterDriver", "App\SchneiderMeterDriver\SchneiderMeterDriver.csproj", "{2E7E7657-3A53-4B62-8927-FE9A082B81DE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Battery250UP", "Lib\Devices\Battery250UP\Battery250UP.csproj", "{F2967439-A590-4D5E-9208-1B973C83AA1C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -244,6 +246,10 @@ Global {2E7E7657-3A53-4B62-8927-FE9A082B81DE}.Debug|Any CPU.Build.0 = Debug|Any CPU {2E7E7657-3A53-4B62-8927-FE9A082B81DE}.Release|Any CPU.ActiveCfg = Release|Any CPU {2E7E7657-3A53-4B62-8927-FE9A082B81DE}.Release|Any CPU.Build.0 = Release|Any CPU + {F2967439-A590-4D5E-9208-1B973C83AA1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F2967439-A590-4D5E-9208-1B973C83AA1C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F2967439-A590-4D5E-9208-1B973C83AA1C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F2967439-A590-4D5E-9208-1B973C83AA1C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {CF4834CB-91B7-4172-AC13-ECDA8613CD17} = {145597B4-3E30-45E6-9F72-4DD43194539A} @@ -286,5 +292,6 @@ Global {6B98449D-BF75-415A-8893-E49518F9307D} = {145597B4-3E30-45E6-9F72-4DD43194539A} {2C7F3D89-402B-43CB-988E-8D2D853BEF44} = {4931A385-24DC-4E78-BFF4-356F8D6D5183} {2E7E7657-3A53-4B62-8927-FE9A082B81DE} = {145597B4-3E30-45E6-9F72-4DD43194539A} + {F2967439-A590-4D5E-9208-1B973C83AA1C} = {4931A385-24DC-4E78-BFF4-356F8D6D5183} EndGlobalSection EndGlobal diff --git a/csharp/Lib/Devices/Battery250UP/Battery250UP.csproj b/csharp/Lib/Devices/Battery250UP/Battery250UP.csproj new file mode 100644 index 000000000..a9d8288d4 --- /dev/null +++ b/csharp/Lib/Devices/Battery250UP/Battery250UP.csproj @@ -0,0 +1,16 @@ + + + + + + InnovEnergy.Lib.Devices.Battery250UP + + + + + + + + + + diff --git a/csharp/Lib/Devices/Battery250UP/Battery250UPDevices.cs b/csharp/Lib/Devices/Battery250UP/Battery250UPDevices.cs new file mode 100644 index 000000000..68f7e5cf6 --- /dev/null +++ b/csharp/Lib/Devices/Battery250UP/Battery250UPDevices.cs @@ -0,0 +1,35 @@ +using InnovEnergy.Lib.Utils; + +namespace InnovEnergy.Lib.Devices.Battery250Up; + +public class Battery250UpDevices +{ + private readonly IReadOnlyList _Devices; + + public Battery250UpDevices(IReadOnlyList devices) => _Devices = devices; + + public Battery250UpRecords? Read() + { + var records = _Devices + .Select(TryRead) + .NotNull() + .ToList(); + + return Battery250UpRecords.FromBatteries(records); + } + + private static Battery250UpRecord? TryRead(Battery250UpDevice d) + { + try + { + return d.Read(); + } + catch (Exception e) + { + Console.WriteLine($"Failed to read Battery node {d.SlaveId}\n{e.Message}"); + // TODO: log + + return null; + } + } +} diff --git a/csharp/Lib/Devices/Battery250UP/Battery250UPRecord.Api.cs b/csharp/Lib/Devices/Battery250UP/Battery250UPRecord.Api.cs new file mode 100644 index 000000000..28a713dbc --- /dev/null +++ b/csharp/Lib/Devices/Battery250UP/Battery250UPRecord.Api.cs @@ -0,0 +1,259 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text; +using InnovEnergy.Lib.Devices.Battery250Up.DataTypes; +using InnovEnergy.Lib.Units; +using InnovEnergy.Lib.Units.Power; +using static InnovEnergy.Lib.Devices.Battery250Up.DataTypes.LedState; + +namespace InnovEnergy.Lib.Devices.Battery250Up; + +using Strings = IReadOnlyList; + +[SuppressMessage("ReSharper", "InconsistentNaming")] +[SuppressMessage("ReSharper", "ConvertToAutoProperty")] +public partial class Battery250UpRecord +{ + public Dc_ Dc => new Dc_(this); + public Leds_ Leds => new Leds_(this); + public Temperatures_ Temperatures => new Temperatures_(this); + public StringActive_ BatteryStrings => new StringActive_(this); + public IoStatus_ IoStatus => new IoStatus_(this); + + public Boolean Eoc => ParseEocReached();//Leds is { Green: On, Amber: Off, Blue : Off }; // ParseEocReached(); // + + public UInt16 IoStates => _IoStates; + public UInt16 LimpBitMap => _LimpBitMap; + + public String BatteryState => ParseBatteryState(); + + public String SerialNumber => $"{_SerialNum1:X4}{_SerialNum2:X4}{_SerialNum3:X4}{_SerialNum4:X4}".TrimEnd('0'); + + public String FwVersion => _FwVersion.ToString("X4"); + + public Strings Warnings => ParseWarnings().OrderBy(w => w).ToList(); + public Strings Alarms => ParseAlarms() .OrderBy(w => w).ToList(); + + public Percent Soc => _Soc; + public Double SOCAh => _SOCAh; + + public Current BusCurrent => _BusCurrent; + + public Current CellsCurrent => _CellsCurrent; + + 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); + + public Boolean CalibrationChargeRequested => TimeSinceTOC > TimeSpan.FromDays(7); // From AF0A (Fw Version) Each 14 days , But Max and Peter asked for 7 days. + + 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 Battery250UpRecord Self { get; } + internal Leds_(Battery250UpRecord self) => this.Self = self; + + } + + 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; + + internal StringActive_(Battery250UpRecord self) => Self = self; + private Battery250UpRecord Self { get; } + } + + 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_(Battery250UpRecord self) => Self = self; + private Battery250UpRecord Self { get; } + } + + 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_(Battery250UpRecord self) => Self = self; + private Battery250UpRecord 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; + + internal Cells_(Battery250UpRecord self) => Self = self; + private Battery250UpRecord Self { get; } + } + + public readonly struct Dc_ + { + public Voltage Voltage => Self._CellsVoltage; + public Current Current => Self._CellsCurrent; + public ActivePower Power => Self._CellsVoltage * Self._CellsCurrent; + + internal Dc_(Battery250UpRecord self) => Self = self; + private Battery250UpRecord Self { get; } + } + + + 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(); + } + + [SuppressMessage("ReSharper", "StringLiteralTypo")] + private IEnumerable ParseAlarms() + { + Boolean HasBit(Int16 bit) => (_AlarmFlags & 1uL << bit) > 0; + + if (HasBit(0)) yield return "Tam : Ambient Temperature too low"; + if (HasBit(2)) yield return "TaM2 : Ambient 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 : 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"; + 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"; + } + + [SuppressMessage("ReSharper", "StringLiteralTypo")] + private IEnumerable ParseWarnings() + { + 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"; + if (HasBit(22)) yield return "vsm1: String voltage too low"; + 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(33)) yield return "CCBF : Internal charger hardware failure"; + 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"; + if (HasBit(44)) yield return "LMPW : String voltages unbalance warning"; + if (HasBit(47)) yield return "TOCW : Top of Charge requested"; + if (HasBit(49)) yield return "BUSL : Bus lower than string"; + } + + + 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); + } + } +} diff --git a/csharp/Lib/Devices/Battery250UP/Battery250UPRecord.Modbus.cs b/csharp/Lib/Devices/Battery250UP/Battery250UPRecord.Modbus.cs new file mode 100644 index 000000000..0c4eeb80b --- /dev/null +++ b/csharp/Lib/Devices/Battery250UP/Battery250UPRecord.Modbus.cs @@ -0,0 +1,57 @@ +using System.Diagnostics.CodeAnalysis; +using InnovEnergy.Lib.Devices.Battery250Up.DataTypes; +using InnovEnergy.Lib.Protocols.Modbus.Reflection.Attributes; + +namespace InnovEnergy.Lib.Devices.Battery250Up; +#pragma warning disable CS0169, CS0649 + + +[SuppressMessage("ReSharper", "InconsistentNaming")] +[SuppressMessage("ReSharper", "UnusedMember.Global")] +[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")] +[BigEndian] +public partial class Battery250UpRecord +{ + [InputRegister(1004)] private UInt16 _LedStates; + [InputRegister(1005)] private UInt64 _WarningFlags; + [InputRegister(1009)] private UInt64 _AlarmFlags; + [InputRegister(1013)] private UInt16 _IoStates; + + [InputRegister(999, Scale = 0.01)] private Double _CellsVoltage; + [InputRegister(1001, Scale = 0.01)] private Double _BusVoltage; + [InputRegister(1000, Scale = 0.01, Offset = -10000)] private Double _CellsCurrent; + [InputRegister(1002, Scale = 0.1, Offset = -10000)] private Double _SOCAh; + + + [InputRegister(1062, Scale = 0.01, Offset = -10000)] private Double _BusCurrent; + + [InputRegister(1053, Scale = 0.1)] private Double _Soc; + [InputRegister(1052)] private UInt16 _TimeSinceToc; + + [InputRegister(1014, Scale = 0.1, Offset = -400)] private Double _TemperaturesBoard; + [InputRegister(1015, Scale = 0.1, Offset = -400)] private Double _TemperaturesCellsCenter; + [InputRegister(1016, Scale = 0.1, Offset = -400)] private Double _TemperaturesCellsLeft; + [InputRegister(1017, Scale = 0.1, Offset = -400)] private Double _TemperaturesCellsRight; + [InputRegister(1003, Scale = 0.1, Offset = -400)] private Double _TemperaturesCellsAverage; + + [InputRegister(1054)] private UInt16 _FwVersion; + [InputRegister(1055)] private UInt16 _SerialNum1; + [InputRegister(1056)] private UInt16 _SerialNum2; + [InputRegister(1057)] private UInt16 _SerialNum3; + [InputRegister(1058)] private UInt16 _SerialNum4; + [InputRegister(1059)] private UInt16 _LimpBitMap; + [InputRegister(1060)] private UInt16 _BatteryState1; + [InputRegister(1061)] private UInt16 _BatteryState2; + + //[InputRegister(1063)] private UInt16 _TotalBatteryCycle; + + + private LedState ParseLed(LedColor led) => (LedState)((_LedStates >> (Int32)led) & 3); + + // public Decimal CellsVoltage { get; init; } + // + // public Decimal MaxChargingPower { get; init; } + // public Decimal MaxDischargingPower { get; init; } +} + + diff --git a/csharp/Lib/Devices/Battery250UP/Battery250UPRecords.cs b/csharp/Lib/Devices/Battery250UP/Battery250UPRecords.cs new file mode 100644 index 000000000..f8d986ef2 --- /dev/null +++ b/csharp/Lib/Devices/Battery250UP/Battery250UPRecords.cs @@ -0,0 +1,50 @@ +using InnovEnergy.Lib.Units; +using InnovEnergy.Lib.Units.Composite; +using InnovEnergy.Lib.Units.Power; + +namespace InnovEnergy.Lib.Devices.Battery250Up; + +public class Battery250UpRecords +{ + public required DcBus Dc { get; init; } + public required Boolean Eoc { get; init; } + public required IReadOnlyList Warnings { get; init; } + public required IReadOnlyList Alarms { get; init; } + public required Percent Soc { get; init; } + public required Double SocAh { get; init; } + public required Percent CurrentMinSoc { get; init; } + public required Temperature Temperature { get; init; } + public required DcPower HeatingPower { get; init; } + public required TimeSpan TimeSinceToc { get; init; } + public required Boolean CalibrationChargeRequested { get; init; } + + public required IReadOnlyList Devices { get; init; } + + public static Battery250UpRecords? FromBatteries(IReadOnlyList? records) + { + if (records is null || records.Count == 0) + return null; + + return new Battery250UpRecords + { + Devices = records, + Eoc = records.All(r => r.Eoc), + Warnings = records.SelectMany(r => r.Warnings).Distinct().ToList(), + Alarms = records.SelectMany(r => r.Alarms).Distinct().ToList(), + Soc = records.Average(r => r.Soc.Value), + SocAh = records.Average(r => r.SOCAh), + CurrentMinSoc = records.Min(r => r.Soc.Value), + Temperature = records.Average(b => b.Temperatures.Cells.Average.Value), + HeatingPower = records.Sum(b => b.HeatingPower), + TimeSinceToc = records.Max(r => r.TimeSinceTOC), + CalibrationChargeRequested = records.Any(r => r.CalibrationChargeRequested), // we changed this to Any instead of All, it's mean we wait only one battery to reach the 7 days + + Dc = new() + { + Voltage = records.Average(r => r.Dc.Voltage), + Current = records.Sum(r => r.Dc.Current), + } + }; + } + +} diff --git a/csharp/Lib/Devices/Battery250UP/Battery250UpDevice.cs b/csharp/Lib/Devices/Battery250UP/Battery250UpDevice.cs new file mode 100644 index 000000000..8586f9356 --- /dev/null +++ b/csharp/Lib/Devices/Battery250UP/Battery250UpDevice.cs @@ -0,0 +1,48 @@ +using System.IO.Ports; +using InnovEnergy.Lib.Protocols.Modbus.Channels; +using InnovEnergy.Lib.Protocols.Modbus.Clients; +using InnovEnergy.Lib.Protocols.Modbus.Slaves; +using InnovEnergy.Lib.Utils; +using static System.IO.Ports.Parity; + +namespace InnovEnergy.Lib.Devices.Battery250Up; + +public class Battery250UpDevice : ModbusDevice +{ + public const Parity Parity = Odd; + public const Int32 StopBits = 1; + public const Int32 BaudRate = 115200; + public const Int32 DataBits = 8; + + public Byte SlaveId { get; } + + public Battery250UpDevice(String tty, Byte slaveId, SshHost host) : this + ( + channel: new RemoteSerialChannel(host, tty, BaudRate, Parity, DataBits, StopBits), + slaveId + ) + {} + + public Battery250UpDevice(String tty, Byte slaveId, String? host = null) : this + ( + channel: host switch + { + null => new SerialPortChannel ( tty, BaudRate, Parity, DataBits, StopBits), + _ => new RemoteSerialChannel(host, tty, BaudRate, Parity, DataBits, StopBits) + }, + slaveId + ) + {} + + public Battery250UpDevice(Channel channel, Byte slaveId) : this + ( + client: new ModbusRtuClient(channel, slaveId) + ) + {} + + public Battery250UpDevice(ModbusClient client): base(client) + { + SlaveId = client.SlaveId; + } + +} diff --git a/csharp/Lib/Devices/Battery250UP/DataTypes/CellTemperatures.cs b/csharp/Lib/Devices/Battery250UP/DataTypes/CellTemperatures.cs new file mode 100644 index 000000000..b01db23c4 --- /dev/null +++ b/csharp/Lib/Devices/Battery250UP/DataTypes/CellTemperatures.cs @@ -0,0 +1,11 @@ +using InnovEnergy.Lib.Units; + +namespace InnovEnergy.Lib.Devices.Battery250Up.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/Battery250UP/DataTypes/Constants.cs b/csharp/Lib/Devices/Battery250UP/DataTypes/Constants.cs new file mode 100644 index 000000000..6fcf4c089 --- /dev/null +++ b/csharp/Lib/Devices/Battery250UP/DataTypes/Constants.cs @@ -0,0 +1,31 @@ +using System.Diagnostics.CodeAnalysis; +using System.IO.Ports; +using static System.IO.Ports.Parity; + +namespace InnovEnergy.Lib.Devices.Battery250Up.DataTypes; + +[SuppressMessage("ReSharper", "InconsistentNaming")] +public static class Constants +{ + public const Int32 BaseAddress = 1000; + public const Int32 NoOfRegisters = 56; + + public const Parity Parity = Odd; + public const Int32 StopBits = 1; + public const Int32 BaudRate = 115200; + public const Int32 DataBits = 8; + public static TimeSpan Timeout { get; } = TimeSpan.FromMilliseconds(100); + + public const Double VMax = 59.0; + public const Double VMin = 42.0; + public const Double AhPerString = 40.0; + + private const Double RStringMin = 0.125; + private const Double RStringMax = 0.250; + private const Double IMaxPerString = 20.0; + private const UInt16 NumberOfStrings = 5; + + public const Double RIntMin = RStringMin / NumberOfStrings; + public const Double RIntMax = RStringMax / NumberOfStrings; + public const Double IMax = NumberOfStrings * IMaxPerString; +} \ No newline at end of file diff --git a/csharp/Lib/Devices/Battery250UP/DataTypes/LedColor.cs b/csharp/Lib/Devices/Battery250UP/DataTypes/LedColor.cs new file mode 100644 index 000000000..a6904219e --- /dev/null +++ b/csharp/Lib/Devices/Battery250UP/DataTypes/LedColor.cs @@ -0,0 +1,9 @@ +namespace InnovEnergy.Lib.Devices.Battery250Up.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/Battery250UP/DataTypes/LedState.cs b/csharp/Lib/Devices/Battery250UP/DataTypes/LedState.cs new file mode 100644 index 000000000..7fe83297c --- /dev/null +++ b/csharp/Lib/Devices/Battery250UP/DataTypes/LedState.cs @@ -0,0 +1,9 @@ +namespace InnovEnergy.Lib.Devices.Battery250Up.DataTypes; + +public enum LedState +{ + Off = 0, + On = 1, + Blinking = 2, + BlinkingFast = 3 +} \ No newline at end of file diff --git a/csharp/Lib/Devices/Battery250UP/DataTypes/Leds.cs b/csharp/Lib/Devices/Battery250UP/DataTypes/Leds.cs new file mode 100644 index 000000000..1ca1c850b --- /dev/null +++ b/csharp/Lib/Devices/Battery250UP/DataTypes/Leds.cs @@ -0,0 +1,9 @@ +namespace InnovEnergy.Lib.Devices.Battery250Up.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/Battery250UP/Doc/FZSONICK UP_Protocol_Manual - solar_application_rev00.pdf b/csharp/Lib/Devices/Battery250UP/Doc/FZSONICK UP_Protocol_Manual - solar_application_rev00.pdf new file mode 100644 index 000000000..c4896c72c Binary files /dev/null and b/csharp/Lib/Devices/Battery250UP/Doc/FZSONICK UP_Protocol_Manual - solar_application_rev00.pdf differ