207 lines
8.5 KiB
C#
207 lines
8.5 KiB
C#
using System.Collections.Immutable;
|
|
using InnovEnergy.Lib.Utils;
|
|
using InnovEnergy.Lib.Victron.VeDBus;
|
|
using InnovEnergy.WireFormat.VictronV1;
|
|
|
|
namespace InnovEnergy.VenusLogger.Parsers;
|
|
|
|
using Props = ImmutableDictionary<String, VeProperty>;
|
|
|
|
public static class BatteryData
|
|
{
|
|
private record struct Bat(Props Props, Int32 NodeId);
|
|
|
|
public static Maybe<Device> GetBattery(this IEnumerable<ServiceProperties> services)
|
|
{
|
|
return services
|
|
.Where(VeService.IsBatteryService)
|
|
.Select(GetBatteryDevice)
|
|
.ToList()
|
|
.SingleOrDefault(Maybe<Device>.Nothing);
|
|
}
|
|
|
|
private static Maybe<Device> GetBatteryDevice(ServiceProperties serviceProps)
|
|
{
|
|
var individualBatteries = serviceProps
|
|
.Properties
|
|
.GetBatteriesData()
|
|
.Select(MakeDevice)
|
|
.ToList();
|
|
|
|
if (!individualBatteries.Any())
|
|
return null;
|
|
|
|
return individualBatteries.Combine(DeviceType.Battery48Tl200);
|
|
}
|
|
|
|
private static Device MakeDevice(BatteryStatus48TL batteryData)
|
|
{
|
|
return new Device
|
|
{
|
|
BatteryData = batteryData,
|
|
Type = DeviceType.Battery48Tl200,
|
|
Phases =
|
|
{
|
|
new Phase
|
|
{
|
|
Voltage = batteryData.BusVoltage, // TODO ?? cellsVoltage vs BusVoltage?
|
|
Current = -batteryData.CellsCurrent, // TODO ?? cellsCurrent vs BusCurrent?
|
|
Power = -batteryData.CellsVoltage * batteryData.CellsCurrent,
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
private static IEnumerable<BatteryStatus48TL> GetBatteriesData(this IReadOnlyList<ServiceProperties> batterySv)
|
|
{
|
|
return batterySv.Any()
|
|
? batterySv[0].Properties.GetBatteriesData()
|
|
: Enumerable.Empty<BatteryStatus48TL>();
|
|
}
|
|
|
|
|
|
private static IEnumerable<BatteryStatus48TL> GetBatteriesData(this Props props)
|
|
{
|
|
return GetNodes(props)
|
|
.Select(node => new Bat(props, node))
|
|
.TrySelect(Parse);
|
|
}
|
|
|
|
private static IEnumerable<Int32> GetNodes(Props props)
|
|
{
|
|
return props
|
|
.Keys
|
|
.Where(k => k.StartsWith("/_Battery/"))
|
|
.Select(p => p.Split("/")[2])
|
|
.Distinct()
|
|
.Select(Int32.Parse)
|
|
.OrderBy(n => n);
|
|
}
|
|
|
|
private static BatteryStatus48TL Parse(Bat bat)
|
|
{
|
|
var soc = bat.GetProperty<Single>("Soc");
|
|
|
|
var busVoltage = bat.GetProperty<Single>("BussVoltage");
|
|
var cellsCurrent = bat.GetDcValue("Current");
|
|
var cellsVoltage = bat.GetDcValue("Voltage");
|
|
var temperature = bat.GetDcValue("Temperature");
|
|
|
|
var alarms = bat.GetAlarms();
|
|
var warnings = bat.GetWarnings();
|
|
var ioStatus = bat.GetIoStatus();
|
|
var leds = bat.GetLeds();
|
|
|
|
return new BatteryStatus48TL
|
|
{
|
|
NodeId = bat.NodeId,
|
|
Soc = soc,
|
|
BusVoltage = busVoltage,
|
|
CellsVoltage = cellsVoltage,
|
|
CellsCurrent = cellsCurrent,
|
|
Temperature = temperature,
|
|
Alarms = { alarms },
|
|
Warnings = { warnings },
|
|
IoStatus = { ioStatus },
|
|
Leds = leds,
|
|
|
|
// TODO: temperatures
|
|
};
|
|
}
|
|
|
|
private static Single GetDcValue(this Bat bat, String prop)
|
|
{
|
|
return bat.GetProperty<Single>("Dc/0/" + prop);
|
|
}
|
|
|
|
private static IEnumerable<IoStatus> GetIoStatus(this Bat bat)
|
|
{
|
|
if (!bat.GetIoStatus("AlarmOutActive")) yield return IoStatus.AlarmActive; // inverted
|
|
if (bat.GetIoStatus("AuxRelay")) yield return IoStatus.AuxRelay;
|
|
if (bat.GetIoStatus("BatteryCold")) yield return IoStatus.AlarmActive;
|
|
if (bat.GetIoStatus("EocReached")) yield return IoStatus.EocReached;
|
|
if (bat.GetIoStatus("BatteryCold")) yield return IoStatus.BatteryCold;
|
|
if (bat.GetIoStatus("HeaterOn")) yield return IoStatus.HeaterActive;
|
|
if (bat.GetIoStatus("MainSwitchClosed")) yield return IoStatus.DisconnectedFromDc; // inverted
|
|
if (bat.GetIoStatus("RemoteState")) yield return IoStatus.RemoteState;
|
|
if (bat.GetIoStatus("VoltMeasurementAllowed")) yield return IoStatus.VoltMeasurementAllowed;
|
|
}
|
|
|
|
private static IEnumerable<Warnings> GetWarnings(this Bat bat)
|
|
{
|
|
if (bat.GetWarning("Ah_W")) yield return Warnings.AhWBit35;
|
|
if (bat.GetWarning("BLPW")) yield return Warnings.BlpwBit32;
|
|
if (bat.GetWarning("CELL1")) yield return Warnings.Cell1Bit46;
|
|
if (bat.GetWarning("IDM1")) yield return Warnings.Idm1Bit10;
|
|
if (bat.GetWarning("MID1")) yield return Warnings.Mid1Bit30;
|
|
if (bat.GetWarning("MPMM")) yield return Warnings.MpmmBit38;
|
|
if (bat.GetWarning("TCMM")) yield return Warnings.TcmmBit39;
|
|
if (bat.GetWarning("TCdi")) yield return Warnings.TcdiBit40;
|
|
if (bat.GetWarning("TaM1")) yield return Warnings.TaM1Bit1;
|
|
if (bat.GetWarning("TbM1")) yield return Warnings.TbM1Bit4;
|
|
if (bat.GetWarning("VBM1")) yield return Warnings.Vbm1Bit8;
|
|
if (bat.GetWarning("VBm1")) yield return Warnings.Vbm1Bit6;
|
|
if (bat.GetWarning("WMTO")) yield return Warnings.WmtoBit41;
|
|
if (bat.GetWarning("iCM1")) yield return Warnings.ICm1Bit26;
|
|
if (bat.GetWarning("iDM1")) yield return Warnings.IDm1Bit28;
|
|
if (bat.GetWarning("vsM1")) yield return Warnings.VsM1Bit24;
|
|
}
|
|
|
|
private static IEnumerable<Alarms> GetAlarms(this Bat bat)
|
|
{
|
|
if (bat.GetAlarm("AhFL")) yield return Alarms.AhFlBit34;
|
|
if (bat.GetAlarm("BRNF")) yield return Alarms.BrnfBit37;
|
|
if (bat.GetAlarm("CCBF")) yield return Alarms.CcbfBit33;
|
|
if (bat.GetAlarm("CELL2")) yield return Alarms.Cell2Bit45;
|
|
if (bat.GetAlarm("CME")) yield return Alarms.CmeBit18;
|
|
if (bat.GetAlarm("DATA")) yield return Alarms.DataBit43;
|
|
if (bat.GetAlarm("FUSE")) yield return Alarms.FuseBit14;
|
|
if (bat.GetAlarm("HTFS")) yield return Alarms.HtfsBit42;
|
|
if (bat.GetAlarm("HTRE")) yield return Alarms.HtreBit15;
|
|
if (bat.GetAlarm("HWEM")) yield return Alarms.HwemBit20;
|
|
if (bat.GetAlarm("HWFL")) yield return Alarms.HwflBit19;
|
|
if (bat.GetAlarm("IDM2")) yield return Alarms.IDm2Bit29;
|
|
if (bat.GetAlarm("ISOB")) yield return Alarms.IsobBit12;
|
|
if (bat.GetAlarm("MID2")) yield return Alarms.Mid2Bit31;
|
|
if (bat.GetAlarm("MSWE")) yield return Alarms.MsweBit13;
|
|
if (bat.GetAlarm("STRE")) yield return Alarms.StreBit17;
|
|
if (bat.GetAlarm("TCPE")) yield return Alarms.TcpeBit16;
|
|
if (bat.GetAlarm("TaM2")) yield return Alarms.TaM2Bit2;
|
|
if (bat.GetAlarm("Tam")) yield return Alarms.TamBit0;
|
|
if (bat.GetAlarm("TbCM")) yield return Alarms.TbCmBit36;
|
|
if (bat.GetAlarm("TbM2")) yield return Alarms.TbM2Bit5;
|
|
if (bat.GetAlarm("Tbm")) yield return Alarms.TbmBit3;
|
|
if (bat.GetAlarm("ThM")) yield return Alarms.ThMBit21;
|
|
if (bat.GetAlarm("VBm2")) yield return Alarms.Vbm2Bit7;
|
|
if (bat.GetAlarm("iCM2")) yield return Alarms.ICm2Bit27;
|
|
if (bat.GetAlarm("iDM2")) yield return Alarms.Idm2Bit11;
|
|
if (bat.GetAlarm("vsM2")) yield return Alarms.VsM2Bit25;
|
|
if (bat.GetAlarm("vsm1")) yield return Alarms.Vsm1Bit22;
|
|
if (bat.GetAlarm("vsm2")) yield return Alarms.Vsm2Bit23;
|
|
}
|
|
|
|
private static Boolean GetIoStatus(this Bat bat, String name) => bat.GetProperty<Boolean>("IoStatus/" + name);
|
|
private static Boolean GetAlarm (this Bat bat, String name) => bat.GetProperty<Boolean>("AlarmFlags/" + name);
|
|
private static Boolean GetWarning (this Bat bat, String name) => bat.GetProperty<Boolean>("WarningFlags/" + name);
|
|
|
|
private static Leds GetLeds(this Bat bat)
|
|
{
|
|
const String ls = "LedStatus/";
|
|
|
|
return new Leds
|
|
{
|
|
Amber = (Led) bat.GetProperty<Int16>(ls + "Amber"),
|
|
Red = (Led) bat.GetProperty<Int16>(ls + "Red"),
|
|
Blue = (Led) bat.GetProperty<Int16>(ls + "Blue"),
|
|
Green = (Led) bat.GetProperty<Int16>(ls + "Green"),
|
|
};
|
|
}
|
|
|
|
private static T GetProperty<T>(this Bat bat, String path)
|
|
{
|
|
var (props, nodeId) = bat;
|
|
|
|
return props.GetProperty<T>($"/_Battery/{nodeId}/{path}");
|
|
}
|
|
} |