Innovenergy_trunk/csharp/Lib/Protocols/DBus/Daemon/DBusDaemonConnection.Resolv...

187 lines
5.9 KiB
C#

using System.Collections.Immutable;
using System.Reactive.Linq;
namespace InnovEnergy.Lib.Protocols.DBus.Daemon;
using NameOwnerChanged = ValueTuple<String, String, String>;
using ServiceDict = ImmutableDictionary<String, DBusService>;
public enum ServiceChange
{
NoChange,
IdAdded,
IdRemoved,
BusNameAdded,
BusNameRemoved,
}
public sealed record ServiceChanged(ServiceChange Change,
String BusNameOrId,
DBusService Service,
ImmutableDictionary<String, DBusService> AllServices);
public static class Resolver
{
private static readonly ServiceChanged InitialState = NoChange(ImmutableDictionary<String, DBusService>.Empty);
public static IObservable<ServiceChanged> ObserveServices(this DBusDaemonConnection connection)
{
var names = connection
.ListNames()
.Result;
var newIds = names
.Where(DBusDaemonApi.IsBusId)
.Select(NewBusId);
var newBusNames = names
.Where(DBusDaemonApi.IsBusName)
.Select(NewBusName)
.Where(t => t != null)
.Cast<NameOwnerChanged>();
var ownerChanged = connection
.ObserveSignalMessages(DBusDaemonApi.ServiceName,
DBusDaemonApi.Interface,
DBusDaemonApi.ObjectPath,
"NameOwnerChanged")
.Select(m => m.Payload!)
.OfType<NameOwnerChanged>();
var init = newIds.Concat(newBusNames).ToList();
return ownerChanged
.StartWith(init)
.Scan(InitialState, OnNameOwnerChanged)
.Where(c => c.Change != ServiceChange.NoChange);
NameOwnerChanged? NewBusName(String busName)
{
var ownerId = connection
.GetNameOwner(busName)
.ContinueWith(t => t.IsCompletedSuccessfully ? t.Result : null)
.Result;
if (ownerId is null)
return null;
return (busName, "", ownerId);
}
}
private static ServiceChanged OnNameOwnerChanged(ServiceChanged serviceChange, NameOwnerChanged ownerChange)
{
var allServices = serviceChange.AllServices;
if (ownerChange.IsInvalid())
return NoChange(allServices);
var (nameOrId, oldOwner, newOwner) = ownerChange;
if (ownerChange.IdWasRemoved())
{
var id = nameOrId;
allServices.TryGetValue(id, out var service);
service ??= new DBusService(id);
allServices = allServices.Remove(id);
//Console.WriteLine("Removed " + id);
return new ServiceChanged(ServiceChange.IdRemoved, id, service, allServices);
}
if (ownerChange.IdWasAdded())
{
var id = nameOrId;
var service = new DBusService(id);
allServices = allServices.SetItem(id, service);
//Console.WriteLine("Added " + id);
return new ServiceChanged(ServiceChange.IdAdded, id, service, allServices);
}
if (ownerChange.BusNameWasRemoved())
{
var busName = nameOrId;
var id = oldOwner;
allServices.TryGetValue(busName, out var service);
if (service == null)
{
Console.WriteLine("BusNameRemoved: should not happen");
service = new DBusService(busName);
}
allServices = allServices.Remove(busName);
if (allServices.TryGetValue(id, out var owner))
owner.BusNames.Remove(busName);
//Console.WriteLine("Removed " + busName);
return new ServiceChanged(ServiceChange.BusNameRemoved, busName, service, allServices);
}
if (ownerChange.BusNameWasAdded())
{
var busName = nameOrId;
var id = newOwner;
if (!allServices.TryGetValue(id, out var service))
{
Console.WriteLine("BusNameAdded: should not happen");
service = new DBusService(id);
allServices = allServices.Add(id,service);
}
service.BusNames.Add(busName);
allServices = allServices.SetItem(busName, service);
//Console.WriteLine("Added " + busName);
return new ServiceChanged(ServiceChange.BusNameAdded, busName, service, allServices);
}
Console.WriteLine("NoChange: should not happen");
return NoChange(allServices);
}
private static ServiceChanged NoChange(ServiceDict allServices)
{
return new ServiceChanged(ServiceChange.NoChange, null!, null!, allServices);
}
private static NameOwnerChanged NewBusId(String id) => (id, "", id);
private static Boolean IsInvalid(this NameOwnerChanged change)
{
var (name, oldOwner, newOwner) = change;
return name == "" || oldOwner == newOwner;
}
private static Boolean IdWasAdded(this NameOwnerChanged change)
{
var (id, oldOwner, newOwner) = change;
return id.IsBusId() && oldOwner == "" && newOwner == id;
}
private static Boolean IdWasRemoved(this NameOwnerChanged change)
{
var (id, oldOwner, newOwner) = change;
return id.IsBusId() && oldOwner == id && newOwner == "";
}
private static Boolean BusNameWasAdded(this NameOwnerChanged change)
{
var (name, _, newOwner) = change;
return name.IsBusName() && newOwner.IsBusId();
}
private static Boolean BusNameWasRemoved(this NameOwnerChanged change)
{
var (name, oldOwner, _) = change;
return name.IsBusName() && oldOwner.IsBusId();
}
}