187 lines
5.9 KiB
C#
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();
|
||
|
}
|
||
|
|
||
|
}
|