365 lines
12 KiB
C#
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);
|
|
|
|
} |