Innovenergy_trunk/csharp/App/Collector/src/BatteryDataParserV4.cs

365 lines
12 KiB
C#

using System.Net;
using System.Text;
using InnovEnergy.App.Collector.Influx;
using InnovEnergy.App.Collector.Records;
using InnovEnergy.App.Collector.Utils;
using InnovEnergy.Lib.Utils;
using Convert = System.Convert;
// NOT (YET) USED
namespace InnovEnergy.App.Collector;
using Data = IReadOnlyList<String>;
public static class BatteryDataParserV4
{
// public static LineProtocolPayload ParseV4Datagram(IPEndPoint endPoint, Byte[] buffer)
// {
// var timeOfArrival = DateTime.UtcNow; // influx wants UTC
//
// Log.Info($"Got V4 datagram from {endPoint}");
//
// using var enumerator = buffer.ParseLengthValueEncoded().GetEnumerator();
//
// var protocolVersion = enumerator
// .Next()
// .ToArray()
// .Apply(Encoding.UTF8.GetString)
// .Equals(Settings.ProtocolV4);
//
// var nBatteries = enumerator.NextByte();
//
// foreach (var _ in Enumerable.Range(0, nBatteries))
// {
// ParseBattery(enumerator);
// }
//
//
// return new LineProtocolPayload();
// }
private static void ParseBattery(IEnumerator<ArraySegment<Byte>> e)
{
// var hardwareVersion = e.NextString();
// var firmwareVersion = e.NextString();
// var bmsVersion = e.NextString();
//
// var modbusData = e.Next();
//
// Int32 ReadRegisterAtIndex(Int32 index) => (modbusData[index * 2] << 8) + modbusData[index * 2 + 1];
// Int32 ReadRegister(Int32 register) => ReadRegisterAtIndex(register - 999);
//
// Double ReadDouble(Int32 register, Double scaleFactor = 1, Double offset = 0)
// {
// var value = ReadRegisterAtIndex(register - 999);
//
// if (value > 0x8000)
// value -= 0x10000; // fiamm stores their integers signed AND with sign-offset @#%^&!
//
// return (value + offset) * scaleFactor;
// }
// TODO
}
private static Byte NextByte(this IEnumerator<ArraySegment<Byte>> enumerator)
{
return enumerator.Next().Single();
}
private static String NextString(this IEnumerator<ArraySegment<Byte>> enumerator)
{
return enumerator
.Next()
.ToArray()
.Apply(Encoding.UTF8.GetString);
}
private static String ParseString(this Data data, Int32 i) => data[i].Trim();
private static UInt16 ParseUInt16(this Data data, Int32 i) => UInt16.Parse(ParseString(data, i));
private static UInt16 ParseUInt16Register(this Data data, Int32 register)
{
var i = register.RegToIndex();
return data.ParseUInt16(i);
}
private static UInt32 ParseUInt32Register(this Data data, Int32 register)
{
var lo = ParseUInt16Register(data, register);
var hi = ParseUInt16Register(data, register + 1);
return Convert.ToUInt32(lo | (hi << 16));
}
private static UInt64 ParseUInt64Register (this Data data, Int32 register)
{
return Enumerable
.Range(0, 4)
.Select(i => Convert.ToUInt64(data.ParseUInt16Register(register + i)) << (i * 16))
.Aggregate(0ul, (a, b) => a + b); // Sum() does not work for UInt64 :(
}
private static Decimal ParseDecimalRegister(this Data data,
Int32 register,
Decimal scaleFactor = 1,
Decimal offset = 0)
{
var i = register.RegToIndex();
UInt32 n = data.ParseUInt16(i);
if (n >= 0x8000)
n -= 0x10000; // fiamm stores their integers signed AND with sign-offset @#%^&!
return (Convert.ToDecimal(n) + offset) * scaleFactor; // according fiamm doc
}
private static Int32 RegToIndex(this Int32 register) => register - 992;
private static Leds ParseLeds(this Data data, String installation, String batteryId)
{
var ledBitmap = ParseUInt16Register(data, 1004);
LedState Led(Int32 n) => (LedState) (ledBitmap >> (n * 2) & 0b11);
return new Leds
{
Installation = installation,
BatteryId = batteryId,
Green = Led(0),
Amber = Led(1),
Blue = Led(2),
Red = Led(3),
};
}
private static IoStatus ParseIoStatus(this Data data, String installation, String batteryId)
{
var ioStatusBitmap = data.ParseUInt16Register(1013);
Boolean IoStatus(Int32 b) => (ioStatusBitmap >> b & 1) > 0;
return new IoStatus
{
Installation = installation,
BatteryId = batteryId,
MainSwitchClosed = IoStatus(0),
AlarmOutActive = IoStatus(1),
InternalFanActive = IoStatus(2),
VoltMeasurementAllowed = IoStatus(3),
AuxRelay = IoStatus(4),
RemoteState = IoStatus(5),
HeatingOn = IoStatus(6),
};
}
private static Warnings ParseWarnings(this Data data, String installation, String batteryId)
{
var warningsBitmap = data.ParseUInt64Register(1005);
Boolean Warning(Int32 b) => (warningsBitmap >> b & 1ul) > 0;
return new Warnings
{
Installation = installation,
BatteryId = batteryId,
TaM1 = Warning(1),
TbM1 = Warning(4),
VBm1 = Warning(6),
VBM1 = Warning(8),
IDM1 = Warning(10),
vsM1 = Warning(24),
iCM1 = Warning(26),
iDM1 = Warning(28),
MID1 = Warning(30),
BLPW = Warning(32),
Ah_W = Warning(35),
MPMM = Warning(38),
TCMM = Warning(39),
TCdi = Warning(40),
LMPW = Warning(44)
};
}
private static Alarms ParseAlarms(this Data data, String installation, String batteryId)
{
var alarmsBitmap = data.ParseUInt64Register(1009);
Boolean Alarm(Int32 b) => (alarmsBitmap >> b & 1ul) > 0;
return new Alarms
{
Installation = installation,
BatteryId = batteryId,
Tam = Alarm(0),
TaM2 = Alarm(2),
Tbm = Alarm(3),
TbM2 = Alarm(5),
VBm2 = Alarm(7),
VBM2 = Alarm(9),
IDM2 = Alarm(11),
ISOB = Alarm(12),
MSWE = Alarm(13),
FUSE = Alarm(14),
HTRE = Alarm(15),
TCPE = Alarm(16),
CME = Alarm(18),
HWFL = Alarm(19),
HWEM = Alarm(20),
ThM = Alarm(21),
vsm1 = Alarm(22),
vsm2 = Alarm(23),
vsM2 = Alarm(25),
iCM2 = Alarm(27),
iDM2 = Alarm(29),
MID2 = Alarm(31),
CCBF = Alarm(33),
AhFL = Alarm(34),
TbCM = Alarm(36),
HTFS = Alarm(42),
DATA = Alarm(43),
LMPA = Alarm(45),
HEBT = Alarm(46),
};
}
private static BatteryStatus ParseBatteryStatus(this Data data,
String installation,
String batteryId,
Decimal temperature,
Warnings warnings,
Alarms alarms,
DateTime lastSeen,
IPEndPoint endPoint)
{
var activeWarnings = Active(warnings);
var activeAlarms = Active(alarms);
return new BatteryStatus
{
InstallationName = installation,
BatteryId = batteryId,
HardwareVersion = data.ParseString(3),
FirmwareVersion = data.ParseString(4),
BmsVersion = data.ParseString(5),
AmpereHours = data.ParseUInt16(6),
Soc = data.ParseDecimalRegister(1053, 0.1m),
Voltage = data.ParseDecimalRegister(999, 0.01m),
Current = data.ParseDecimalRegister(1000, 0.01m, -10000m),
BusVoltage = data.ParseDecimalRegister(1001, 0.01m),
Temperature = temperature,
RtcCounter = data.ParseUInt32Register(1050),
IpAddress = endPoint.Address.ToString(),
Port = endPoint.Port,
// stuff below really should be done by Grafana/InfluxDb, but not possible (yet)
// aka hacks to get around limitations of Grafana/InfluxDb
NumberOfWarnings = activeWarnings.Count,
NumberOfAlarms = activeAlarms.Count,
WarningsBitmap = data.ParseUInt64Register(1005),
AlarmsBitmap = data.ParseUInt64Register(1009),
LastSeen = lastSeen.ToInfluxTime()
};
static IReadOnlyCollection<String> Active(BatteryRecord record) => record
.GetFields()
.Where(f => f.value is Boolean b && b)
.Select(f => f.key)
.ToList();
}
private static Temperatures ParseTemperatures(this Data data, String installation, String batteryId)
{
return new Temperatures
{
Installation = installation,
BatteryId = batteryId,
Battery = data.ParseDecimalRegister(1003, 0.1m, -400m),
Board = data.ParseDecimalRegister(1014, 0.1m, -400m),
Center = data.ParseDecimalRegister(1015, 0.1m, -400m),
Lateral1 = data.ParseDecimalRegister(1016, 0.1m, -400m),
Lateral2 = data.ParseDecimalRegister(1017, 0.1m, -400m),
CenterHeaterPwm = data.ParseDecimalRegister(1018, 0.1m),
LateralHeaterPwm = data.ParseDecimalRegister(1019, 0.1m),
};
}
// private static LineProtocolPayload CreatePayload(params LineProtocolPoint[] points) =>
// CreatePayload((IEnumerable<LineProtocolPoint>) points);
//
//
// private static LineProtocolPayload CreatePayload(IEnumerable<LineProtocolPoint> points)
// {
// var payload = new LineProtocolPayload();
//
// foreach (var point in points)
// payload.Add(point);
//
// return payload;
// }
private static String CheckProtocolId(String ascii)
{
var protocolId = ascii.Substring(0, Settings.ProtocolV3.Length);
if (protocolId != Settings.ProtocolV3)
throw new Exception($"Wrong protocol header: Expected '{Settings.ProtocolV3}' but got '{protocolId}'");
return protocolId;
}
private static IReadOnlyList<BatteryRecord> ParseBatteryRecords(Data data,
String installation,
DateTime lastSeen,
IPEndPoint endPoint)
{
var batteryId = data.ParseBatteryId();
var warnings = data.ParseWarnings (installation, batteryId);
var alarms = data.ParseAlarms (installation, batteryId);
var leds = data.ParseLeds (installation, batteryId);
var temperatures = data.ParseTemperatures (installation, batteryId);
var ioStatus = data.ParseIoStatus (installation, batteryId);
var batteryStatus = data.ParseBatteryStatus(installation,
batteryId,
temperatures.Battery,
warnings,
alarms,
lastSeen,
endPoint);
return new BatteryRecord[]
{
batteryStatus,
temperatures,
leds,
ioStatus,
warnings,
alarms
};
}
private static String ParseProtocolVersion (this Data data) => data.ParseString(0);
private static String ParseInstallationName(this Data data) => data.ParseString(1);
private static String ParseBatteryId (this Data data) => data.ParseString(2);
}