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 PublishOnDBus(this VeProperties veProperties, Bus bus, String busName) { var con = new DBusConnection(bus); var calls = FromAsync(() => con.GetName(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 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 GetName(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(""); 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 ?? ""}"); } 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); } }