using System.Collections.Immutable; using InnovEnergy.Lib.Utils; using InnovEnergy.Lib.Victron.VeDBus; using InnovEnergy.WireFormat.VictronV1; namespace InnovEnergy.VenusLogger.Parsers; using Props = ImmutableDictionary; public static class BatteryData { private record struct Bat(Props Props, Int32 NodeId); public static Maybe GetBattery(this IEnumerable services) { return services .Where(VeService.IsBatteryService) .Select(GetBatteryDevice) .ToList() .SingleOrDefault(Maybe.Nothing); } private static Maybe 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 GetBatteriesData(this IReadOnlyList batterySv) { return batterySv.Any() ? batterySv[0].Properties.GetBatteriesData() : Enumerable.Empty(); } private static IEnumerable GetBatteriesData(this Props props) { return GetNodes(props) .Select(node => new Bat(props, node)) .TrySelect(Parse); } private static IEnumerable 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("Soc"); var busVoltage = bat.GetProperty("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("Dc/0/" + prop); } private static IEnumerable 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 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 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("IoStatus/" + name); private static Boolean GetAlarm (this Bat bat, String name) => bat.GetProperty("AlarmFlags/" + name); private static Boolean GetWarning (this Bat bat, String name) => bat.GetProperty("WarningFlags/" + name); private static Leds GetLeds(this Bat bat) { const String ls = "LedStatus/"; return new Leds { Amber = (Led) bat.GetProperty(ls + "Amber"), Red = (Led) bat.GetProperty(ls + "Red"), Blue = (Led) bat.GetProperty(ls + "Blue"), Green = (Led) bat.GetProperty(ls + "Green"), }; } private static T GetProperty(this Bat bat, String path) { var (props, nodeId) = bat; return props.GetProperty($"/_Battery/{nodeId}/{path}"); } }