2023-02-16 12:57:06 +00:00
|
|
|
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
|
2023-10-02 13:36:50 +00:00
|
|
|
.Where(m => m.Type == MessageType.MethodCall)
|
|
|
|
.Select(msg => AnswerMethodCall(veProperties, msg))
|
|
|
|
.Do(con.OutgoingMessages.OnNext);
|
2023-02-16 12:57:06 +00:00
|
|
|
|
|
|
|
var changes = veProperties
|
2023-10-02 13:36:50 +00:00
|
|
|
.PropertyChanged
|
|
|
|
.Do(con.BroadcastPropertiesChanged);
|
2023-02-16 12:57:06 +00:00
|
|
|
|
|
|
|
var initialPropertiesChanged = veProperties
|
2023-10-02 13:36:50 +00:00
|
|
|
.ToObservable(TaskPoolScheduler.Default)
|
|
|
|
.Do(con.BroadcastPropertiesChanged);
|
2023-02-16 12:57:06 +00:00
|
|
|
|
|
|
|
return await calls
|
2023-10-02 13:36:50 +00:00
|
|
|
.MergeErrors(changes)
|
|
|
|
.MergeErrors(initialPropertiesChanged)
|
|
|
|
.Finally(con.Dispose);
|
2023-02-16 12:57:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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),
|
2023-10-02 13:36:50 +00:00
|
|
|
VeDBusApi.GetText => msg.Ok(p.Text),
|
2023-02-16 12:57:06 +00:00
|
|
|
VeDBusApi.GetItems => msg.Ok(p.GetItem()),
|
2023-10-02 13:36:50 +00:00
|
|
|
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),
|
2023-02-16 12:57:06 +00:00
|
|
|
_ => 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);
|
|
|
|
}
|
|
|
|
}
|