remove VenusLogger.csproj

This commit is contained in:
ig 2023-02-23 15:57:54 +01:00
parent a46b5c8b12
commit e010c4ff54
18 changed files with 0 additions and 1428 deletions

View File

@ -1,72 +0,0 @@
using System.Collections.Immutable;
using System.Reactive.Linq;
using InnovEnergy.Lib.Protocols.DBus;
using InnovEnergy.Lib.Victron.VeDBus;
namespace InnovEnergy.VenusLogger;
using Props = ImmutableDictionary<String, VeProperty>;
public static class DBusObserver
{
// public static IObservable<Grid> ObserveGrid(this DBusConnection dbus)
// {
// return dbus
// .ObserveService(VeService.Grid)
// .TrySelect(Parsers.Grid.Parse)
// .StartWith(new Grid());
// }
//
//
// public static IObservable<ILookup<AcBusType, AcSource>> ObservePvInverters(this DBusConnection dbus)
// {
// return dbus
// .ObserveServices(VeService.PvInverter)
// .Select(Parsers.PvInverter.Parse);
// }
//
//
// public static IObservable<Inverter> ObserveInverter(this DBusConnection dbus)
// {
// return dbus
// .ObserveService(VeService.Inverter)
// .TrySelect(Parsers.Inverter.Parse)
// .StartWith(new Inverter());
// }
//
//
// public static IObservable<IEnumerable<DcSource>> ObservePvChargers(this DBusConnection dbus)
// {
// return dbus
// .ObserveServices(VeService.SolarCharger)
// .Select(Parsers.PvCharger.Parse)
// .StartWith(Empty<DcSource>());
// }
//
// public static IObservable<Boolean> ObserveDcSystem(this DBusConnection dbus)
// {
// return dbus
// .ObserveService(VeService.Settings)
// .TrySelect(p => p["/Settings/SystemSetup/HasDcSystem"].Value.Apply(Convert.ToBoolean))
// .StartWith(false)
// .DistinctUntilChanged();
// }
//
// public static IObservable<IEnumerable<Battery>> Observe48TlBatteries(this DBusConnection dbus)
// {
// return dbus
// .ObserveService(VeService.Battery)
// .TrySelect(Parsers.Battery48Tl.Parse)
// .StartWith(Empty<Battery>());
// }
public static IObservable<IReadOnlyList<ServiceProperties>> ObserveServices(this DBusConnection dbus, String service)
{
return dbus
.ObservePropertiesOfServices(s => s.StartsWith(service))
.StartWith(Array.Empty<ServiceProperties>());
}
}

View File

@ -1,119 +0,0 @@
using System.Diagnostics.CodeAnalysis;
using InnovEnergy.Lib.Utils;
using InnovEnergy.WireFormat.VictronV1;
namespace InnovEnergy.VenusLogger;
internal static class Devices
{
public static Maybe<Device> Combine(this DeviceType deviceType, params Maybe<Device>[] devices)
{
return devices
.Flatten()
.Combine(deviceType);
}
public static Maybe<Device> Combine(this IEnumerable<Device> devices, DeviceType deviceType)
{
var devs = devices as IReadOnlyCollection<Device> ?? devices.ToList();
return devs.Combine(deviceType);
}
public static Maybe<Device> Combine(this IReadOnlyCollection<Device> devices, DeviceType deviceType)
{
return devices
.EquivalentDevice(deviceType)
.Do(d => d.Devices.Add(devices)); // Maybe;
}
public static Maybe<Device> EquivalentDevice(this DeviceType deviceType, params Maybe<Device>[] devices)
{
return devices
.Flatten()
.EquivalentDevice(deviceType);
}
public static Maybe<Device> EquivalentDevice(this IEnumerable<Device> devices, DeviceType deviceType)
{
var devs = devices as IReadOnlyCollection<Device> ?? devices.ToList();
return devs.EquivalentDevice(deviceType);
}
public static Maybe<Device> EquivalentDevice(this IReadOnlyCollection<Device> devices, DeviceType deviceType)
{
if (devices.Count <= 0)
return null;
if (devices.Count == 1)
return new Device { Type = deviceType, Phases = { devices.First().Phases } };
var voltages = devices.GetPhaseVoltages();
var phases = voltages.Select((v, phase) => new Phase
{
Voltage = v,
Current = devices.Sum(d => d.Phases[phase].Current),
Power = devices.Sum(d => d.Phases[phase].Power),
});
return new Device
{
Type = deviceType,
Phases = { phases }
};
}
public static Device ReversePhases(this Device device)
{
device.Phases.ForEach(p =>
{
p.Current = -p.Current;
p.Power = -p.Power;
});
return device;
}
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
private static IEnumerable<Single> GetPhaseVoltages(this IReadOnlyCollection<Device> devices)
{
// TODO: warning if voltages unequal?
var nPhases = devices.Max(d => d.Phases.Count);
var phases = Enumerable.Range(0, nPhases);
var voltages = phases
.Select(AverageVoltage)
.ToList();
if (nPhases != devices.Min(d => d.Phases.Count))
EqualizeNumberOfPhases();
return voltages;
Single AverageVoltage(Int32 phase)
{
return devices
.Where(d => d.Phases.Count > phase)
.Average(d => d.Phases[phase].Voltage);
}
void EqualizeNumberOfPhases()
{
foreach (var phase in phases)
foreach (var device in devices.Where(d => d.Phases.Count <= phase))
{
device.Phases.Add(new Phase
{
Current = 0,
Voltage = voltages[phase]
});
}
}
}
}

View File

@ -1,46 +0,0 @@
using System.Reactive.Linq;
namespace InnovEnergy.VenusLogger;
internal static class ObservableExtensions
{
public static IObservable<R> TrySelect<T, R>(this IObservable<T> source, Func<T, R> map) where R : notnull
{
return source
.Select(e => Try(map, e))
.OfType<R>();
}
public static IEnumerable<R> TrySelect<T, R>(this IEnumerable<T> source, Func<T, R> map) where R : notnull
{
return source
.Select(e => Try(map, e))
.OfType<R>();
}
public static IObservable<R> TrySelect<T, R>(this IObservable<T> source, Func<T, R> map, R onError) where R : notnull
{
return source
.Select(e => Try(map, e))
.Select(e => e is R r ? r : onError);
}
public static IEnumerable<R> TrySelect<T, R>(this IEnumerable<T> source, Func<T, R> map, R onError) where R : notnull
{
return source
.Select(e => Try(map, e))
.Select(e=>e is R r ? r : onError);
}
private static Object Try<T, R>(Func<T, R> map, T e) where R : notnull
{
try
{
return map(e);
}
catch (Exception ex)
{
return ex;
}
}
}

View File

@ -1,9 +0,0 @@
namespace InnovEnergy.VenusLogger.Parsers;
public enum AcBusType
{
Unknown = -1,
AcIn1 = 0,
AcOut = 1,
AcIn2 = 2,
}

View File

@ -1,12 +0,0 @@
namespace InnovEnergy.VenusLogger.Parsers;
public enum AcDeviceType
{
Unknown = 0,
Grid = 1,
Generator = 2,
Shore = 3,
PvInverter = 4,
AcLoad = 5,
Bus = 6,
}

View File

@ -1,207 +0,0 @@
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}");
}
}

View File

@ -1,26 +0,0 @@
using InnovEnergy.Lib.Utils;
using InnovEnergy.Lib.Victron.VeDBus;
using InnovEnergy.WireFormat.VictronV1;
namespace InnovEnergy.VenusLogger.Parsers;
public static class Generator
{
public static Maybe<Device> GetGenerator(this IEnumerable<ServiceProperties> services)
{
return services
.Where(VeService.IsGeneratorService)
.Select(GetGeneratorDevice)
.SingleOrDefault()
.Maybe();
}
private static Device GetGeneratorDevice(ServiceProperties serviceProps)
{
return new Device
{
Type = DeviceType.Generator,
Phases = { serviceProps.Properties.GetAcPhases() }
};
}
}

View File

@ -1,27 +0,0 @@
using InnovEnergy.Lib.Utils;
using InnovEnergy.Lib.Victron.VeDBus;
using InnovEnergy.WireFormat.VictronV1;
namespace InnovEnergy.VenusLogger.Parsers;
public static class Grid
{
public static Maybe<Device> GetGrid(this IEnumerable<ServiceProperties> services)
{
return services
.Where(VeService.IsGridService)
.Select(GetGridDevice)
.SingleOrDefault()
.Maybe();
}
private static Device GetGridDevice(this ServiceProperties serviceProps)
{
return new Device
{
Type = DeviceType.Grid,
Phases = { serviceProps.Properties.GetAcPhases() }
};
}
}

View File

@ -1,131 +0,0 @@
using System.Collections.Immutable;
using InnovEnergy.Lib.Utils;
using InnovEnergy.Lib.Victron.VeDBus;
using InnovEnergy.WireFormat.VictronV1;
using static InnovEnergy.VenusLogger.Parsers.Utils;
namespace InnovEnergy.VenusLogger.Parsers;
using Props = ImmutableDictionary<String, VeProperty>;
public static class Inverter
{
private static Maybe<Device> NoDevice => Maybe<Device>.Nothing;
public static Maybe<Device> GetInverter(this IEnumerable<ServiceProperties> services)
{
return services
.Where(VeService.IsInverterService)
.Select(MakeDevice)
.SingleOrDefault()
.Maybe();
}
public static (Maybe<Device> AcInBus, Maybe<Device> AcOutBus, Maybe<Device> DcBus) GetInverterBusses(this IEnumerable<ServiceProperties> services)
{
return services
.Where(VeService.IsInverterService)
.Select(s => (s.GetAcIn(), s.GetAcOut(), s.GetDc()))
.SingleOrDefault((NoDevice, NoDevice, NoDevice));
}
private static Device MakeDevice(ServiceProperties s)
{
var acIn = s.GetAcIn();
var acOut = s.GetAcOut();
var dc = s.GetDc();
return new Device
{
Type = DeviceType.Inverter,
Devices = { acIn.Concat(acOut).Concat(dc) }
};
}
private static Maybe<Device> GetAcIn(this ServiceProperties serviceProperties)
{
var props = serviceProperties.Properties;
var phases = AcPhases
.TrySelect(props.GetAcInPhase)
.ToList();
if (!phases.Any())
return null;
return new Device
{
Type = DeviceType.AcInBus,
Phases = { phases },
};
}
private static Maybe<Device> GetAcOut(this ServiceProperties serviceProperties)
{
var props = serviceProperties.Properties;
var phases = AcPhases
.TrySelect(props.GetAcOutPhase)
.ToList();
if (!phases.Any())
return null;
return new Device
{
Type = DeviceType.AcOutBus,
Phases = { phases },
};
}
private static Maybe<Device> GetDc(this ServiceProperties serviceProperties)
{
return new Device
{
Type = DeviceType.DcBus,
Phases = { serviceProperties.Properties.GetDcPhase() },
};
}
private static void GetLosses(this ServiceProperties serviceProperties)
{
var dcPhase = serviceProperties.Properties.GetDcPhase();
var dcPower = serviceProperties.Properties.GetProperty<Single>("/Dc/0/Power");
var losses = dcPower - dcPhase.Power;
Console.WriteLine("Dc Losses " + losses);
// return new Device
// {
// Type = DeviceType.DcBus,
// Phases = { dcPhase },
// };
}
// var dcPower = services
// .Where(VeService.IsInverterService)
// .Select(p => p.Properties.GetProperty<Single>("/Dc/0/Power"))
// .FirstOrDefault();
private static Phase GetAcInPhase(this Props props, Int32 phase)
{
return new Phase
{
Voltage = props.GetProperty<Single>($"/Ac/ActiveIn/L{phase}/V"),
Current = -props.GetProperty<Single>($"/Ac/ActiveIn/L{phase}/I"), // - !!
Power = -props.GetProperty<Single>($"/Ac/ActiveIn/L{phase}/P"), // - !!
};
}
private static Phase GetAcOutPhase(this Props props, Int32 phase)
{
return new Phase
{
Voltage = props.GetProperty<Single>($"/Ac/Out/L{phase}/V"),
Current = props.GetProperty<Single>($"/Ac/Out/L{phase}/I"),
Power = props.GetProperty<Single>($"/Ac/Out/L{phase}/P"),
};
}
}

View File

@ -1,27 +0,0 @@
using InnovEnergy.Lib.Utils;
using InnovEnergy.Lib.Victron.VeDBus;
using InnovEnergy.WireFormat.VictronV1;
namespace InnovEnergy.VenusLogger.Parsers;
public static class PvCharger
{
public static Maybe<Device> GetPvCharger(this IEnumerable<ServiceProperties> services)
{
var solarChargers = services
.Where(VeService.IsSolarChargerService)
.TrySelect(GetPvCharger)
.ToList();
if (solarChargers.Count <= 1)
return solarChargers.FirstOrDefault().Maybe();
return new Device { Type = DeviceType.Mppt, Devices = { solarChargers } };
}
private static Device GetPvCharger(ServiceProperties sp) => new Device
{
Type = DeviceType.Mppt,
Phases = { sp.Properties.GetDcPhase() }
};
}

View File

@ -1,51 +0,0 @@
using InnovEnergy.Lib.Utils;
using InnovEnergy.Lib.Victron.VeDBus;
using InnovEnergy.WireFormat.VictronV1;
namespace InnovEnergy.VenusLogger.Parsers;
public static class PvInverter
{
public static Maybe<Device> GetPvInverter(this IEnumerable<ServiceProperties> services, AcBusType busType)
{
var pvInverters = services
.Where(sp => sp.GetBusType() == busType)
.TrySelect(GetPvInverter)
.ToList();
if (pvInverters.Count < 2)
return pvInverters.FirstOrDefault().Maybe();
return new Device
{
Type = DeviceType.PvInverter,
Devices = { pvInverters }
};
}
private static Device GetPvInverter(ServiceProperties sp)
{
return new Device
{
Type = DeviceType.PvInverter,
Phases = { sp.Properties.GetAcPhases() }
};
}
private static AcBusType GetBusType(this ServiceProperties service)
{
try
{
return (AcBusType) service.Properties.GetProperty<Int32>("/Position");
}
catch
{
return AcBusType.Unknown;
}
}
}
// numbers must match those used by Victron on DBus!

View File

@ -1,33 +0,0 @@
using System.Collections.Immutable;
using InnovEnergy.Lib.Victron.VeDBus;
namespace InnovEnergy.VenusLogger.Parsers;
using VeProps = ImmutableDictionary<String, VeProperty>;
public record VenusSettings(Boolean HasAcOutBus, Boolean HasDcSystem);
public static class Settings
{
private static VenusSettings DefaultSettings { get; } = new VenusSettings(true, true);
public static VenusSettings GetSettings(this IEnumerable<ServiceProperties> services)
{
return services
.Where(VeService.IsSettingsService)
.Select(s=>s.Properties)
.Select(GetSettings)
.FirstOrDefault(DefaultSettings);
}
private static VenusSettings GetSettings(this VeProps props)
{
var hasAcOut = props.TryGetProperty("/Settings/SystemSetup/HasAcOutSystem", true);
var hasDcSystem = props.TryGetProperty("/Settings/SystemSetup/HasDcSystem", true);
return new VenusSettings(hasAcOut, hasDcSystem);
}
}

View File

@ -1,170 +0,0 @@
// using System.Collections.Immutable;
// using InnovEnergy.WireFormat.VictronV1;
// using VeDBus;
//
// namespace InnovEnergy.VenusLogger.Parsers;
//
// using Props = ImmutableDictionary<String, VeProperty>;
//
// public static class System
// {
// private static AcDeviceType GetAcConnectionType(this Props props) => props.TryGetProperty<AcDeviceType>("/Ac/ActiveIn/Source");
//
// public static VictronTopologyV1 GetSystemTopology(this IReadOnlyList<ServiceProperties> systemSv)
// {
// return systemSv.Any()
// ? systemSv[0].Properties.GetSystemTopology()
// : new VictronTopologyV1();
// }
//
// public static VictronTopologyV1 GetSystemTopology(this Props props)
// {
// var connectionType = props.GetAcConnectionType();
//
//
// var grid = props.GetAcInDevice(connectionType);
// var acInBus = props.GetAcInBus(connectionType);
// var acOutBus = props.GetAcOutBus();
// var dcBus = props.GetDcBus();
//
//
//
// return new VictronTopologyV1
// {
// Grid = { grid },
// AcIn = acInBus.NoneIfEmpty(),
// AcOut = acOutBus.NoneIfEmpty(),
// DcBus = dcBus.NoneIfEmpty(),
// Battery = battery,
// };
// }
//
// private static DcBus GetDcBus(this Props props)
// {
// var pv = props.GetPvCharger();
// var load = props.GetDcLoad();
//
// return new DcBus
// {
// Pv = { pv },
// Load = { load }
// };
// }
//
// private static AcBus GetAcOutBus(this Props props)
// {
// var pv = props.GetAcOutDevice(AcDeviceType.PvInverter);
// var load = props.GetAcOutDevice(AcDeviceType.AcLoad);
//
// return new AcBus
// {
// Pv = { pv },
// Load = { load },
// };
// }
//
// private static AcBus GetAcInBus(this Props props, AcDeviceType acProsumerType)
// {
// var pv = props.GetAcInDevice(AcDeviceType.PvInverter, acProsumerType);
// var load = props.GetAcInDevice(AcDeviceType.AcLoad, acProsumerType);
//
// return new AcBus
// {
// Pv = { pv },
// Load = { load },
// };
// }
//
//
// private static IEnumerable<AcDevice> GetAcInDevice(this Props props,
// AcDeviceType deviceType,
// AcDeviceType busType = AcDeviceType.Unknown)
// {
// var deviceName = GetDeviceName(deviceType);
//
// if (busType != AcDeviceType.Unknown)
// {
// deviceName += deviceType == AcDeviceType.AcLoad
// ? "OnInput"
// : "On" + GetDeviceName(busType);
// }
//
// var acPhases = props.GetPhases(deviceName).ToList();
//
// if (acPhases.Count > 0 && acPhases.Any(p => p.Power != 0))
// yield return new Device { Phases = { acPhases } };
// }
//
// private static Battery GetBattery(this Props props)
// {
// return new Battery
// {
// Power = props.GetBatteryProp("Power"),
// Current = props.GetBatteryProp("Current"),
// Voltage = props.GetBatteryProp("Voltage"),
// Soc = props.GetBatteryProp("Soc"),
// Temperature = props.GetBatteryProp("Temperature")
// };
// }
//
// private static Single GetBatteryProp(this Props props, String prop) => props.TryGetProperty<Single>("/Dc/Battery/" + prop);
//
//
// private static IEnumerable<Device> GetPvCharger(this Props props)
// {
// var power = props.TryGetProperty<Single>("/Dc/Pv/Power");
//
// if (power == 0)
// yield break;
//
// yield return new Device
// {
// ProducerType = ProducerType.Mppt,
// Phases = { new Phase { Power = power } }
// };
// }
//
// private static IEnumerable<DcDevice> GetDcLoad(this Props props)
// {
// var power = props.TryGetProperty<Single>("/Dc/System/Power");
//
// if (power != 0)
// yield return new DcDevice { Power = power };
// }
//
// private static IEnumerable<Device> GetAcOutDevice(this Props props, AcDeviceType deviceType)
// {
// var deviceName = GetDeviceName(deviceType) + "OnOutput";
// var acPhases = props.GetPhases(deviceName);
//
// if (acPhases.Count > 0 && acPhases.Any(p => p.Power != 0))
// yield return new AcDevice { Phases = { acPhases } };
// }
//
// private static String GetDeviceName(AcDeviceType deviceType) => deviceType switch
// {
// AcDeviceType.Grid => "Grid",
// AcDeviceType.Shore => "Grid",
// AcDeviceType.AcLoad => "Consumption",
// AcDeviceType.Generator => "Genset",
// AcDeviceType.PvInverter => "Pv",
// _ => "Unknown"
// };
//
//
// private static Int32 GetNbOfPhases(this Props props, String deviceName)
// {
// return props.TryGetProperty("/Ac/" + deviceName + "/NumberOfPhases", 0);
// }
//
// private static IEnumerable<Phase> GetPhases(this Props props, String path)
// {
// var nPhases = props.GetNbOfPhases(path);
//
// Phase GetPhase(Int32 p) => new Phase { Power = props.TryGetProperty($"/Ac/{path}/L{p}/Power", 0.0f) };
//
// return Enumerable
// .Range(1, nPhases)
// .Select(GetPhase);
// }
// }

View File

@ -1,93 +0,0 @@
using System.Collections.Immutable;
using InnovEnergy.Lib.Victron.VeDBus;
using InnovEnergy.WireFormat.VictronV1;
namespace InnovEnergy.VenusLogger.Parsers;
using Props = ImmutableDictionary<String, VeProperty>;
public static class Utils
{
public static Phase ZeroPhase { get; } = new Phase { Current = 0, Voltage = 0 };
public static IEnumerable<Int32> AcPhases { get; } = new[] { 1, 2, 3 };
public static IEnumerable<Phase> ZeroPhases { get; } = new[] { ZeroPhase, ZeroPhase, ZeroPhase };
public static T? TryGetProperty<T>(this Props props, String path, T? defaultValue = default(T?))
{
try
{
return props.GetProperty<T>(path);
}
catch
{
return defaultValue;
}
}
// TODO: maybegetproperty?
public static T GetProperty<T>(this Props props, String path)
{
if (typeof(T).IsEnum)
{
var type = Enum.GetUnderlyingType(typeof(T));
var value = props.GetProperty(path, type);
return (T) Enum.ToObject(typeof(T), value);
}
else
{
return (T) props.GetProperty(path, typeof(T));
}
}
private static Object GetProperty(this Props props, String path, Type type)
{
var value = props[path].Value;
var isConvertible = type.GetInterface(nameof(IConvertible)) is not null;
return isConvertible
? Convert.ChangeType(value, type)
: value;
}
public static IEnumerable<Phase> GetAcPhases(this Props props)
{
return AcPhases.TrySelect(props.ParseAcPhase);
}
public static Phase ParseAcPhase(this Props props, Int32 phase) => new Phase
{
Current = props.GetProperty<Single>($"/Ac/L{phase}/Current"),
Voltage = props.GetProperty<Single>($"/Ac/L{phase}/Voltage"),
Power = props.GetProperty<Single>($"/Ac/L{phase}/Power"),
};
public static Phase GetDcPhase(this Props props, Int32 phase = 0)
{
var current = props.GetProperty<Single>($"/Dc/{phase}/Current");
var voltage = props.GetProperty<Single>($"/Dc/{phase}/Voltage");
var power = current * voltage;
// if (props.ContainsKey($"/Dc/{phase}/Power"))
// {
// var losses = props.GetProperty<Single>($"/Dc/{phase}/Power") - power;
// Console.WriteLine("Losses: " + losses);
// }
return new Phase
{
Current = current,
Voltage = voltage,
Power = power
};
}
}

View File

@ -1,78 +0,0 @@
// using System.Collections.Immutable;
// using InnovEnergy.WireFormat.VictronV1;
// using VeDBus;
//
// namespace InnovEnergy.VenusLogger.Parsers;
//
//
// using Props = ImmutableDictionary<String, VeProperty>;
//
// public static class VeBus
// {
// private static Device NullInverter { get; } = new Device
// {
// ProsumerType = ProsumerType.UnknownProsumerType,
// Devices = { new[]
// {
// new Device { ProsumerType = ProsumerType.AcBus, Phases = { new Phase { Power = 0, Current = 0 } } },
// new Device { ProsumerType = ProsumerType.DcBus, Phases = { new Phase { Power = 0, Current = 0 } } },
// } }
// };
//
// public static Device GetInverter(this IReadOnlyList<ServiceProperties> inverterSv)
// {
// return inverterSv.Any()
// ? inverterSv[0].Properties.GetInverter()
// : NullInverter;
// }
//
// public static Inverter GetInverter(this Props props)
// {
// var current = props.TryGetProperty<Single>("/Dc/0/Current");
// var voltage = props.TryGetProperty<Single>("/Dc/0/Voltage");
// var power = props.TryGetProperty<Single>("/Dc/0/Power");
//
// var dc = new DcDevice
// {
// Current = current,
// Voltage = voltage,
// Power = power,
// };
//
// var ac = new AcDevice
// {
// Phases = { Enumerable.Range(1, 3).Select(GetAcPhase) }
// };
//
// return new Inverter
// {
// Ac = ac,
// Dc = dc,
// };
//
// AcPhase GetAcPhase(Int32 phase) => new()
// {
// Current = GetAcValue(phase, "I"),
// Voltage = GetAcValue(phase, "V"),
// Power = GetAcValue(phase, "P"),
// };
//
// Single GetAcValue(Int32 phase, String value) => props.TryGetProperty<Single>($"/Ac/ActiveIn/L{phase}/{value}");
// }
//
// // 'Ac/State/HighDcCurrent': 0,
// // 'Ac/State/HighDcVoltage': 0,
// // 'Alarms/HighDcCurrent': 0,
// // 'Alarms/HighDcVoltage': 0,
// // 'Dc/0/Current': 25.899999618530273,
// // 'Dc/0/MaxChargeCurrent': 105.0,
// // 'Dc/0/Power': 1521,
// // 'Dc/0/Temperature': [],
// // 'Dc/0/Voltage': 54.119998931884766,
// // 'Devices/1/ExtendStatus/HighDcCurrent': 0,
// // 'Devices/1/ExtendStatus/HighDcVoltage': 0,
// // 'Devices/2/ExtendStatus/HighDcCurrent': 0,
// // 'Devices/2/ExtendStatus/HighDcVoltage': 0,
// // 'FirmwareFeatures/IBatSOCBroadcast': 1,
// //
// }

View File

@ -1,271 +0,0 @@
using System.Collections.Immutable;
using System.Globalization;
using System.Net.Sockets;
using System.Reactive.Linq;
using Amazon;
using Amazon.S3;
using Amazon.S3.Model;
using Google.Protobuf;
using InnovEnergy.Lib.Protocols.DBus;
using InnovEnergy.Lib.Protocols.DBus.Transport;
using InnovEnergy.Lib.Utils;
using InnovEnergy.Lib.Victron.VeDBus;
using InnovEnergy.VenusLogger.Parsers;
using InnovEnergy.WireFormat;
using InnovEnergy.WireFormat.VictronV1;
using CodedOutputStream = Google.Protobuf.CodedOutputStream;
namespace InnovEnergy.VenusLogger;
using Props = ImmutableDictionary<String, VeProperty>;
using Services = IReadOnlyList<ServiceProperties>;
public static class Program
{
const Int32 SamplePeriodSecs = 2;
// rm -f $(pwd)/remote_dbus.sock ; ssh -nNT -L $(pwd)/remote_dbus.sock:/var/run/dbus/system_bus_socket root@10.2.1.6
public static void Main()
{
Console.WriteLine("starting...");
var serviceUrl = "https://sos-ch-dk-2.exo.io/";
var keyName = "EXO5ead8a3e6a908014025edc5c";
var secret = "xhCJ6gth-MS8ED4Qij3P7K12TfmkiqncCg70HxSGAe0";
var bucket = "graber-zurich";
var dispatch = CreateS3Dispatcher(serviceUrl, keyName, secret, bucket, SamplePeriodSecs);
var ep = new UnixDomainSocketEndPoint("/home/eef/remote_dbus.sock");
var auth = AuthenticationMethod.ExternalAsRoot();
var bus = new Bus(ep, auth);
var dbus = new DBusConnection(bus);
var batteries = dbus.ObserveVeService(VeService.IsBatteryServiceName);
var inverter = dbus.ObserveVeService(VeService.IsInverterServiceName);
var pvInverters = dbus.ObserveVeService(VeService.IsPvInverterServiceName);
var mppts = dbus.ObserveVeService(VeService.IsSolarChargerServiceName);
var grid = dbus.ObserveVeService(VeService.IsGridServiceName);
var generator = dbus.ObserveVeService(VeService.IsGeneratorServiceName);
var settings = dbus.ObserveVeService(VeService.IsSettingsServiceName);
var services = Observable
.CombineLatest(batteries, inverter, pvInverters, mppts, grid, generator, settings)
.Select(EnumerableUtils.Flatten)
.Select(Enumerable.ToList)
.OfType<Services>();
var interval = TimeSpan.FromSeconds(SamplePeriodSecs);
Console.WriteLine("start sampling");
services.Sample(interval)
.Select(ParsePayload)
.Skip(1)
.SelectMany(dispatch)
.Wait();
}
private static IObservable<IReadOnlyList<ServiceProperties>> ObserveVeService(this DBusConnection dbus,
Func<String, Boolean> selector)
{
return dbus
.ObservePropertiesOfServices(selector)
.StartWith(Array.Empty<ServiceProperties>());
}
private static String GetInfo(PutObjectResponse response)
{
return DateTime
.Now
.ToUniversalTime()
.ToString(CultureInfo.CurrentCulture.DateTimeFormat.SortableDateTimePattern)
.Replace('T', ' ')
.Replace('-', '/') + " " + response.HttpStatusCode;
}
private static Func<IMessage, Task<PutObjectResponse>> CreateS3Dispatcher(String serviceUrl,
String keyName,
String secret,
String bucket,
Int32 timeResolutionSecs = 2)
{
var buffer = new Byte[1024];
var config = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.EUWest3, // can be whatever
ServiceURL = serviceUrl
};
var s3Client = new AmazonS3Client(keyName, secret, config);
return Dispatch;
Task<PutObjectResponse> Dispatch(IMessage payload)
{
var now = DateTimeOffset.UtcNow;
var timestamp = now.ToUnixTimeSeconds() / timeResolutionSecs * timeResolutionSecs;
var request = new PutObjectRequest
{
BucketName = bucket,
Key = timestamp.ToString(),
ContentType = "application/octet-stream",
InputStream = WriteToStream(payload),
AutoCloseStream = false
};
return s3Client.PutObjectAsync(request);
MemoryStream WriteToStream(IMessage data)
{
try
{
var outputStream = new CodedOutputStream(buffer);
data.WriteTo(outputStream);
outputStream.Flush();
Console.WriteLine($"{now}: Writing {timestamp}: {outputStream.Position} bytes");
return new MemoryStream(buffer, 0, outputStream.Position.ConvertTo<Int32>());
}
catch (CodedOutputStream.OutOfSpaceException)
{
buffer = new Byte[buffer.Length * 2];
return WriteToStream(data);
}
}
}
}
public static Payload ParsePayload(Services services)
{
var grid = services.GetGrid();
var generator = services.GetGenerator(); // TODO: generator
var battery = services.GetBattery();
var pvOnDc = services.GetPvCharger();
var pvOnAcIn = services.GetPvInverter(AcBusType.AcIn1); // TODO: acIn2
var pvOnAcOut = services.GetPvInverter(AcBusType.AcOut);
var settings = services.GetSettings();
var (inverterAcIn, inverterAcOut, inverterDc) = services.GetInverterBusses();
var acInBus = CreateAcInBus(grid, pvOnAcIn, inverterAcIn);
var acOutBus = CreateAcOutBus(settings, pvOnAcOut, inverterAcOut, acInBus);
var dcBus = CreateDcBus(settings, pvOnDc, inverterDc, battery);
var losses = CalcInverterLosses(acOutBus, dcBus);
var inverter = new Device
{
Type = DeviceType.Inverter,
Devices = { acOutBus, dcBus, losses }
};
//Console.WriteLine(inverter);
return new Payload { VictronTopologyV1 = new VictronTopologyV1 { Root = inverter } };
}
private static Device CalcInverterLosses(Maybe<Device> acBus, Maybe<Device> dcBus)
{
var acPower = acBus.SelectMany(ac => ac.Phases).Sum(p => p.Power);
var dcPower = -dcBus.SelectMany(dc => dc.Phases).Sum(p => p.Power);
//var losses = Math.Max(0, acPower - dcPower);
var losses = dcPower - acPower;
return new Device
{
Type = DeviceType.Losses,
Phases = { new Phase { Power = losses } }
};
}
private static String Debug(Maybe<Device> device)
{
var phases = device.SelectMany(d => d.Phases);
var p = phases.Sum(p => p.Power) + "W ";
return phases
.Select(p => " | " + p.Power.ToString().PadRight(6).Substring(0, 6) + "W")
.Aggregate(p.PadRight(20) ,(a, b) => a + b);
}
private static Maybe<Device> CreateAcInBus(Maybe<Device> grid, Maybe<Device> pvOnAcIn, Maybe<Device> inverterAcIn)
{
if (!grid.HasValue && !pvOnAcIn.HasValue)
return null;
var loadOnAcIn = Devices
.EquivalentDevice(DeviceType.AcLoad, inverterAcIn, pvOnAcIn, grid)
.Select(Devices.ReversePhases);
// loadOnAcIn.SelectMany(l => l.Phases)
// .ForEach(p =>
// {
// p.Current = Math.Min(0, p.Current); // current in load cannot be positive
// p.Power = Math.Min(0, p.Power); // power of load cannot be positive
// });
return Devices.Combine(DeviceType.AcInBus, loadOnAcIn, pvOnAcIn, grid);
}
private static Maybe<Device> CreateAcOutBus(VenusSettings settings, Maybe<Device> pvOnAcOut, Maybe<Device> inverterAcOut, Maybe<Device> acInBus)
{
if (!settings.HasAcOutBus || !pvOnAcOut.HasValue)
return acInBus;
var loadOnAcOut = Devices
.EquivalentDevice(DeviceType.AcLoad, inverterAcOut, pvOnAcOut)
.Select(Devices.ReversePhases);
// loadOnAcOut.SelectMany(l => l.Phases)
// .ForEach(p =>
// {
// p.Current = Math.Min(0, p.Current); // current in load cannot be positive
// p.Power = Math.Min(0, p.Power); // power of load cannot be positive
// });
return Devices.Combine(DeviceType.AcOutBus, loadOnAcOut, pvOnAcOut, acInBus);
}
private static Maybe<Device> CreateDcBus(VenusSettings settings, Maybe<Device> pvOnDc, Maybe<Device> inverterDc, Maybe<Device> battery)
{
if (!pvOnDc.HasValue && !settings.HasDcSystem)
return battery;
// if (!settings.HasDcSystem)
// return Devices.Combine(DeviceType.DcBus, pvOnDc, battery);
var loadOnDc = Devices
.EquivalentDevice(DeviceType.DcLoad, inverterDc, pvOnDc, battery)
// .Select(Devices.ReversePhases);
;
// TODO: no way to measure battery heater power yet
// if there is no DC-system declared:
// we assume that the missing (EquivalentDevice) power is consumed by the Battery Heater
//
// if there is a DC-system declared:
// lump the battery heater and the other devices into a DC load
if (settings.HasDcSystem)
return Devices.Combine(DeviceType.DcBus, loadOnDc, pvOnDc, battery);
loadOnDc.ForEach(l => l.Type = DeviceType.BatteryHeater);
battery.ForEach(b => b.Devices.Add(loadOnDc));
return Devices.Combine(DeviceType.DcBus, pvOnDc, battery);
}
}

View File

@ -1,34 +0,0 @@
using InnovEnergy.Lib.Victron.VeDBus;
namespace InnovEnergy.VenusLogger;
public static class VeService
{
public const String Generator = "com.victronenergy.genset.";
public const String Grid = "com.victronenergy.grid.";
public const String SolarCharger = "com.victronenergy.solarcharger.";
public const String PvInverter = "com.victronenergy.pvinverter.";
public const String Battery = "com.victronenergy.battery.";
public const String Inverter = "com.victronenergy.vebus.";
public const String Settings = "com.victronenergy.settings";
public const String System = "com.victronenergy.system";
public static Boolean IsGeneratorService (this ServiceProperties sp) => sp.ServiceName.IsGeneratorServiceName();
public static Boolean IsGridService (this ServiceProperties sp) => sp.ServiceName.IsGridServiceName();
public static Boolean IsSolarChargerService(this ServiceProperties sp) => sp.ServiceName.IsSolarChargerServiceName();
public static Boolean IsPvInverterService (this ServiceProperties sp) => sp.ServiceName.IsPvInverterServiceName();
public static Boolean IsBatteryService (this ServiceProperties sp) => sp.ServiceName.IsBatteryServiceName();
public static Boolean IsInverterService (this ServiceProperties sp) => sp.ServiceName.IsInverterServiceName();
public static Boolean IsSettingsService (this ServiceProperties sp) => sp.ServiceName.IsSettingsServiceName();
public static Boolean IsSystemService (this ServiceProperties sp) => sp.ServiceName.IsSystemServiceName();
public static Boolean IsGeneratorServiceName (this String serviceName) => serviceName.StartsWith(Generator);
public static Boolean IsGridServiceName (this String serviceName) => serviceName.StartsWith(Grid);
public static Boolean IsSolarChargerServiceName(this String serviceName) => serviceName.StartsWith(SolarCharger);
public static Boolean IsPvInverterServiceName (this String serviceName) => serviceName.StartsWith(PvInverter);
public static Boolean IsBatteryServiceName (this String serviceName) => serviceName.StartsWith(Battery);
public static Boolean IsInverterServiceName (this String serviceName) => serviceName.StartsWith(Inverter);
public static Boolean IsSettingsServiceName (this String serviceName) => serviceName == Settings;
public static Boolean IsSystemServiceName (this String serviceName) => serviceName == System;
}

View File

@ -1,22 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../InnovEnergy.app.props" />
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<AssemblyName>InnovEnergy.VenusLogger</AssemblyName>
<RootNamespace>InnovEnergy.VenusLogger</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../lib/S3/S3.csproj" />
<ProjectReference Include="../../lib/VeDBus/VeDBus.csproj" />
<ProjectReference Include="../../lib/Wireformat/WireFormat.csproj" />
<ProjectReference Include="..\..\lib\Victron\VeDBus\VeDBus.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AWSSDK.S3" Version="3.7.8.8" />
<PackageReference Include="System.Reactive.Linq" Version="5.0.0" />
</ItemGroup>
</Project>