Innovenergy_trunk/csharp/Lib/Victron/VeDBus/VeProperties.Dbus.cs

158 lines
5.8 KiB
C#

using System.Diagnostics;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using InnovEnergy.Lib.Protocols.DBus;
using InnovEnergy.Lib.Protocols.DBus.Daemon;
using InnovEnergy.Lib.Protocols.DBus.Protocol;
using InnovEnergy.Lib.Protocols.DBus.Protocol.DataTypes;
using InnovEnergy.Lib.Protocols.DBus.Protocol.Header;
using InnovEnergy.Lib.Utils;
using static System.Reactive.Linq.Observable;
namespace InnovEnergy.Lib.Victron.VeDBus;
public static class VePropertiesDbus
{
// Task will run indefinitely unless an exception occurs
public static async Task<Exception> PublishOnDBus(this VeProperties veProperties, Bus bus, String busName)
{
var con = new DBusConnection(bus);
var calls = FromAsync(() => con.GetName<Message>(busName))
.IgnoreElements()
.Merge(con.IncomingMessages)
.Where(m => m.Type == MessageType.MethodCall)
.Select(msg => AnswerMethodCall(veProperties, msg))
.Do(con.OutgoingMessages.OnNext);
var changes = veProperties
.PropertyChanged
.Do(con.BroadcastPropertiesChanged);
var initialPropertiesChanged = veProperties
.ToObservable(TaskPoolScheduler.Default)
.Do(con.BroadcastPropertiesChanged);
return await calls
.MergeErrors(changes)
.MergeErrors(initialPropertiesChanged)
.Finally(con.Dispose);
}
public static async Task<Exception> PublishOnDBus(this VeProperties veProperties, DBusConnection con)
{
var calls = con.IncomingMessages
.Where(m => m.Type == MessageType.MethodCall)
.Select(msg => AnswerMethodCall(veProperties, msg))
.Do(con.OutgoingMessages.OnNext);
var changes = veProperties
.PropertyChanged
.Do(con.BroadcastPropertiesChanged);
var initialPropertiesChanged = veProperties
.ToObservable(TaskPoolScheduler.Default)
.Do(con.BroadcastPropertiesChanged);
return await calls
.MergeErrors(changes)
.MergeErrors(initialPropertiesChanged)
.Finally(con.Dispose);
}
private static async Task<T> GetName<T>(this DBusDaemonConnection dbusConnection, String busName)
{
var requestNameReply = await dbusConnection.RequestName(busName);
if (requestNameReply != RequestNameReply.PrimaryOwner)
{
var error = $"Failed to reserve {busName} on DBus";
Console.WriteLine(error);
throw new Exception(error);
}
Console.WriteLine($"Successfully registered name {busName} on DBus " + dbusConnection.LocalName);
return default!;
}
// TODO: SetValue
private static Message AnswerMethodCall(VeProperties props, Message msg)
{
try
{
Debug.Write($"Got call from {msg.Sender} : {msg.Member} {msg.ObjectPath} => ");
if (!msg.ObjectPath.HasValue)
return msg.UnknownObjectPath("<none>");
if (msg.ObjectPath == ObjectPath.Root)
{
return msg.Member switch
{
VeDBusApi.GetValue => msg.Ok(props.GetValueOfAllProperties()),
VeDBusApi.GetText => msg.Ok(props.GetTextOfAllProperties()),
VeDBusApi.GetItems => msg.Ok(props.GetAllItems()),
_ => msg.UnknownMember(msg.Member)
};
}
var prop = props.Get(msg.ObjectPath.Value);
if (prop.HasValue)
{
var p = prop.Value;
// Console.WriteLine(p.Writeable);
return msg.Member switch
{
VeDBusApi.GetValue => msg.Ok(p.Value),
VeDBusApi.GetText => msg.Ok(p.Text),
VeDBusApi.GetItems => msg.Ok(p.GetItem()),
VeDBusApi.SetValue => p.Writeable && msg.ObjectPath != null
? msg.Ok(p.Value==msg.Payload! || props.Set(path: msg.ObjectPath.Value, value: msg.Payload!, writable: true) ? 0 : -1)
: msg.ObjectNotWritable(msg.Member),
_ => msg.UnknownMember(msg.Member)
};
}
return msg.UnknownObjectPath(msg.ObjectPath.Value);
}
catch (Exception e)
{
Console.WriteLine(e);
return msg.UnknownError(msg.ObjectPath.Value);
}
}
private static Message Ok(this Message msg, Object payload)
{
Debug.WriteLine($"OK: {msg.ObjectPath} = {payload}");
return msg.CreateMethodReturn(payload);
}
private static Message UnknownMember(this Message msg, String? member)
{
return CreateErrorReply(msg, $"Unknown Member: {member ?? "<none>"}");
}
private static Message UnknownError(this Message msg, ObjectPath objectPath)
{
return CreateErrorReply(msg, $"Unknown Error: {objectPath}");
}
private static Message UnknownObjectPath(this Message msg, ObjectPath objectPath)
{
return CreateErrorReply(msg, $"Unknown ObjectPath: {objectPath}");
}
private static Message ObjectNotWritable(this Message msg, ObjectPath objectPath)
{
return CreateErrorReply(msg, $"Not Writable Property: {objectPath}");
}
private static Message CreateErrorReply(Message msg, String error)
{
Debug.WriteLine("ERROR: " + error);
return msg.CreateErrorReply(error);
}
}