remove VenusLogger.csproj
This commit is contained in:
parent
a46b5c8b12
commit
e010c4ff54
|
@ -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>());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
namespace InnovEnergy.VenusLogger.Parsers;
|
|
||||||
|
|
||||||
public enum AcBusType
|
|
||||||
{
|
|
||||||
Unknown = -1,
|
|
||||||
AcIn1 = 0,
|
|
||||||
AcOut = 1,
|
|
||||||
AcIn2 = 2,
|
|
||||||
}
|
|
|
@ -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,
|
|
||||||
}
|
|
|
@ -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}");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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() }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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() }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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() }
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -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!
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
// }
|
|
||||||
// }
|
|
|
@ -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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -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,
|
|
||||||
// //
|
|
||||||
// }
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
|
|
||||||
}
|
|
|
@ -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>
|
|
Loading…
Reference in New Issue