From bd3c6236264b002a4885c2a9467e7de9f9844fea Mon Sep 17 00:00:00 2001 From: Mikhail Mitrofanov Date: Sat, 1 Mar 2025 19:06:18 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A2=D0=B5=D0=BF=D0=B5=D1=80=D1=8C=20=D0=B1?= =?UTF-8?q?=D0=B5=D0=B7=20=D1=88=D0=B5=D0=BB=D0=B0=20=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=B0=D0=B5=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Example.Shared/IDataList.cs | 2 +- Example.Shared/ILoadTest.cs | 3 +- Example.Shared/IPage.cs | 3 +- Example.Shared/IPrinter.cs | 2 +- Example.Shared/IPrinterFactory.cs | 3 +- mROA.Cbor/CborSerializationToolkit.cs | 57 +++++-- mROA.Cbor/PreParsedValue.cs | 16 +- mROA/Abstract/IContextRepository.cs | 2 +- mROA/Abstract/IShared.cs | 7 + .../Backend/ContextRepository.cs | 4 +- .../Backend/MultiClientContextRepository.cs | 6 +- .../Implementation/RemoteContextRepository.cs | 6 +- mROA/Implementation/SharedObject.cs | 140 ------------------ mROA/Implementation/SharedObjectShell.cs | 100 +++++++++++++ mROA/Implementation/TransmissionConfig.cs | 35 +++++ .../UniversalObjectIdentifier.cs | 9 +- 16 files changed, 219 insertions(+), 176 deletions(-) create mode 100644 mROA/Abstract/IShared.cs delete mode 100644 mROA/Implementation/SharedObject.cs create mode 100644 mROA/Implementation/SharedObjectShell.cs create mode 100644 mROA/Implementation/TransmissionConfig.cs diff --git a/Example.Shared/IDataList.cs b/Example.Shared/IDataList.cs index 90792d4..44eea30 100644 --- a/Example.Shared/IDataList.cs +++ b/Example.Shared/IDataList.cs @@ -3,7 +3,7 @@ using mROA.Implementation.Attributes; namespace Example.Shared { // [SharedObjectInterface] - // public interface IDataList + // public interface IDataList : IShared // { // T Get(int index); // void Add(T item); diff --git a/Example.Shared/ILoadTest.cs b/Example.Shared/ILoadTest.cs index e9f958f..8234f1f 100644 --- a/Example.Shared/ILoadTest.cs +++ b/Example.Shared/ILoadTest.cs @@ -1,11 +1,12 @@ using System.Threading; using System.Threading.Tasks; +using mROA.Implementation; using mROA.Implementation.Attributes; namespace Example.Shared { [SharedObjectInterface] - public interface ILoadTest + public interface ILoadTest : IShared { int Next(int last); int Last(int next); diff --git a/Example.Shared/IPage.cs b/Example.Shared/IPage.cs index a8d21f9..c7517ff 100644 --- a/Example.Shared/IPage.cs +++ b/Example.Shared/IPage.cs @@ -1,9 +1,10 @@ +using mROA.Implementation; using mROA.Implementation.Attributes; namespace Example.Shared { [SharedObjectInterface] - public interface IPage + public interface IPage : IShared { byte[] GetData(); } diff --git a/Example.Shared/IPrinter.cs b/Example.Shared/IPrinter.cs index 085b965..489bada 100644 --- a/Example.Shared/IPrinter.cs +++ b/Example.Shared/IPrinter.cs @@ -7,7 +7,7 @@ using mROA.Implementation.Attributes; namespace Example.Shared { [SharedObjectInterface] - public interface IPrinter : IDisposable + public interface IPrinter : IDisposable, IShared { string GetName(); Task Print(string text, CancellationToken cancellationToken); diff --git a/Example.Shared/IPrinterFactory.cs b/Example.Shared/IPrinterFactory.cs index a2febb9..884293d 100644 --- a/Example.Shared/IPrinterFactory.cs +++ b/Example.Shared/IPrinterFactory.cs @@ -4,13 +4,12 @@ using mROA.Implementation.Attributes; namespace Example.Shared { [SharedObjectInterface] - public interface IPrinterFactory + public interface IPrinterFactory : IShared { IPrinter Create(string printerName); void Register(IPrinter printer); IPrinter GetPrinterByName(string printerName); IPrinter GetFirstPrinter(); string[] CollectAllNames(); - } } \ No newline at end of file diff --git a/mROA.Cbor/CborSerializationToolkit.cs b/mROA.Cbor/CborSerializationToolkit.cs index cd63254..58b9ee2 100644 --- a/mROA.Cbor/CborSerializationToolkit.cs +++ b/mROA.Cbor/CborSerializationToolkit.cs @@ -12,7 +12,6 @@ namespace mROA.Cbor { public class CborSerializationToolkit : IContextualSerializationToolKit { - private List _remoteTypes; public byte[] Serialize(object objectToSerialize, IEndPointContext context) { var writer = new CborWriter(); @@ -79,9 +78,6 @@ namespace mROA.Cbor case double d: writer.WriteDouble(d); break; - // case decimal dec: - // writer.WriteDecimal(dec); - // break; case bool b: writer.WriteBoolean(b); break; @@ -112,7 +108,7 @@ namespace mROA.Cbor case IList enumerable: WriteList(enumerable, writer, context); break; - case ISharedObject sharedObject: + case ISharedObjectShell sharedObject: if (context != null) sharedObject.EndPointContext = context; WriteObject(sharedObject, writer, context); @@ -160,9 +156,24 @@ namespace mROA.Cbor private void WriteObject(object obj, CborWriter writer, IEndPointContext? context) { var type = obj.GetType(); - - - + + if (obj is IShared) + { + var generic = obj.GetType().GetInterfaces().FirstOrDefault(i => typeof(IShared).IsAssignableFrom(i)); + var sharedShell = typeof(SharedObjectShellShell<>).MakeGenericType(generic); + var so = + Activator.CreateInstance(sharedShell, obj) as + ISharedObjectShell; + if (context != null) + so.EndPointContext = context; + + writer.WriteStartArray(1); + writer.WriteUInt64(so.Identifier.Flat); + writer.WriteEndArray(); + + return; + } + var properties = FilterProperties(type.GetProperties()); var values = properties.Select(property => property.GetValue(obj)).ToList(); WriteList(values, writer, context); @@ -204,7 +215,7 @@ namespace mROA.Cbor if (type == null) return ReadList(reader, null, context); - if (type.IsSubclassOf(typeof(ISharedObject))) + if (type.IsSubclassOf(typeof(ISharedObjectShell))) return ReadSharedObject(reader, type, context); if (typeof(IList).IsAssignableFrom(type) || type.IsArray) @@ -287,12 +298,30 @@ namespace mROA.Cbor private object ReadObject(CborReader reader, Type type, IEndPointContext? context) { - if (type == typeof(object)) { return new PreParsedValue(ReadList(reader, null, context) as List); } + if (type.IsInterface) + { + Console.WriteLine("Interface"); + + var sharedShell = typeof(SharedObjectShellShell<>).MakeGenericType(type); + var so = + Activator.CreateInstance(sharedShell) as + ISharedObjectShell; + if (context != null) + so.EndPointContext = context; + + reader.ReadStartArray(); + var identifier = reader.ReadUInt64(); + reader.ReadEndArray(); + + so.Identifier = UniversalObjectIdentifier.FromFlat(identifier); + return so.UniversalValue; + } + var instance = Activator.CreateInstance(type)!; FillObject(instance, type, reader, context); @@ -300,9 +329,9 @@ namespace mROA.Cbor return instance; } - private ISharedObject ReadSharedObject(CborReader reader, Type type, IEndPointContext? context) + private ISharedObjectShell ReadSharedObject(CborReader reader, Type type, IEndPointContext? context) { - var sharedObject = (Activator.CreateInstance(type) as ISharedObject)!; + var sharedObject = (Activator.CreateInstance(type) as ISharedObjectShell)!; if (context != null) { sharedObject.EndPointContext = context; @@ -321,9 +350,8 @@ namespace mROA.Cbor var length = reader.ReadStartArray(); try { - #if TRACE - Console.WriteLine($"Reading list of {length} objects, {properties.Count} properties found"); + Console.WriteLine($"Reading list of {length} objects, {properties.Count} properties found"); #endif for (var index = 0; index < length; index++) @@ -358,7 +386,6 @@ namespace mROA.Cbor public void Inject(T dependency) { - _remoteTypes = RemoteContextRepository.RemoteTypes.Keys.ToList(); } public byte[] Serialize(T objectToSerialize) diff --git a/mROA.Cbor/PreParsedValue.cs b/mROA.Cbor/PreParsedValue.cs index 5109c98..8140f47 100644 --- a/mROA.Cbor/PreParsedValue.cs +++ b/mROA.Cbor/PreParsedValue.cs @@ -21,11 +21,25 @@ namespace mROA.Cbor public object? ToObject(Type type, IEndPointContext? context) { + + if (type.IsInterface) + { + var sharedShell = typeof(SharedObjectShellShell<>).MakeGenericType(type); + var so = + Activator.CreateInstance(sharedShell) as + ISharedObjectShell; + if (context != null) + so.EndPointContext = context; + + so.Identifier = UniversalObjectIdentifier.FromFlat((ulong)_properties[0]); + return so.UniversalValue; + } + var instance = Activator.CreateInstance(type); if (instance == null) return null; - if (instance is ISharedObject sharedObject && context != null) + if (instance is ISharedObjectShell sharedObject && context != null) { sharedObject.EndPointContext = context; } diff --git a/mROA/Abstract/IContextRepository.cs b/mROA/Abstract/IContextRepository.cs index c298688..f9c643b 100644 --- a/mROA/Abstract/IContextRepository.cs +++ b/mROA/Abstract/IContextRepository.cs @@ -7,7 +7,7 @@ namespace mROA.Abstract { int ResisterObject(object o); void ClearObject(int id); - T GetObjectBySharedObject(SharedObject sharedObject); + T GetObjectBySharedObject(SharedObjectShellShell sharedObjectShellShell); T? GetObject(int id); object GetSingleObject(Type type); int GetObjectIndex(object o); diff --git a/mROA/Abstract/IShared.cs b/mROA/Abstract/IShared.cs new file mode 100644 index 0000000..6758159 --- /dev/null +++ b/mROA/Abstract/IShared.cs @@ -0,0 +1,7 @@ +namespace mROA.Implementation +{ +#pragma warning disable CS8618, CS9264 + public interface IShared + { + } +} \ No newline at end of file diff --git a/mROA/Implementation/Backend/ContextRepository.cs b/mROA/Implementation/Backend/ContextRepository.cs index 65c4acf..e0371f8 100644 --- a/mROA/Implementation/Backend/ContextRepository.cs +++ b/mROA/Implementation/Backend/ContextRepository.cs @@ -58,9 +58,9 @@ namespace mROA.Implementation.Backend _lastIndexFinder = Task.FromResult(id); } - public T GetObjectBySharedObject(SharedObject sharedObject) + public T GetObjectBySharedObject(SharedObjectShellShell sharedObjectShellShell) { - return (T)GetObject(sharedObject.Identifier.ContextId); + return (T)GetObject(sharedObjectShellShell.Identifier.ContextId); } public object GetObject(int id) diff --git a/mROA/Implementation/Backend/MultiClientContextRepository.cs b/mROA/Implementation/Backend/MultiClientContextRepository.cs index e89be32..f5c7bf4 100644 --- a/mROA/Implementation/Backend/MultiClientContextRepository.cs +++ b/mROA/Implementation/Backend/MultiClientContextRepository.cs @@ -39,10 +39,10 @@ namespace mROA.Implementation.Backend repository.ClearObject(id); } - public T GetObjectBySharedObject(SharedObject sharedObject) + public T GetObjectBySharedObject(SharedObjectShellShell sharedObjectShellShell) { - var repository = GetRepository(sharedObject.Identifier.OwnerId); - return repository.GetObject(sharedObject.Identifier.ContextId); + var repository = GetRepository(sharedObjectShellShell.Identifier.OwnerId); + return repository.GetObject(sharedObjectShellShell.Identifier.ContextId); } public object GetObject(int id) diff --git a/mROA/Implementation/RemoteContextRepository.cs b/mROA/Implementation/RemoteContextRepository.cs index 1990780..7648d80 100644 --- a/mROA/Implementation/RemoteContextRepository.cs +++ b/mROA/Implementation/RemoteContextRepository.cs @@ -19,14 +19,14 @@ namespace mROA.Implementation throw new NotSupportedException(); } - public T GetObjectBySharedObject(SharedObject sharedObject) + public T GetObjectBySharedObject(SharedObjectShellShell sharedObjectShellShell) { if (_representationProducer == null) throw new NullReferenceException("representation producer is not initialized"); if (!RemoteTypes.TryGetValue(typeof(T), out var remoteType)) throw new NotSupportedException(); - var representationModule = _representationProducer.Produce(sharedObject.Identifier.OwnerId); - var remote = (T)Activator.CreateInstance(remoteType, sharedObject.Identifier.ContextId, + var representationModule = _representationProducer.Produce(sharedObjectShellShell.Identifier.OwnerId); + var remote = (T)Activator.CreateInstance(remoteType, sharedObjectShellShell.Identifier.ContextId, representationModule)!; return remote; } diff --git a/mROA/Implementation/SharedObject.cs b/mROA/Implementation/SharedObject.cs deleted file mode 100644 index 1f8aae5..0000000 --- a/mROA/Implementation/SharedObject.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System; -using System.Text.Json.Serialization; -using System.Threading.Tasks; -using mROA.Abstract; -using mROA.Implementation.Attributes; - -// ReSharper disable UnusedMember.Global -#pragma warning disable CS8618, CS9264 - -namespace mROA.Implementation -{ - public static class TransmissionConfig - { -#if TRACE - public static int TotalTransmittedBytes { get; set; } -#endif - private static IContextRepository? _realContextRepository; - private static IContextRepository? _remoteEndpointContextRepository; - private static IOwnershipRepository? _ownershipRepository; - - public static IContextRepository RealContextRepository - { - get => _realContextRepository ?? throw new NullReferenceException("RealContextRepository is null"); - set => _realContextRepository = value; - } - - public static IContextRepository RemoteEndpointContextRepository - { - get => _remoteEndpointContextRepository ?? - throw new NullReferenceException("RemoteEndpointContextRepository is null"); - set => _remoteEndpointContextRepository = value; - } - - public static IOwnershipRepository OwnershipRepository - { - get => _ownershipRepository ?? throw new NullReferenceException("OwnershipRepository is null"); - set => _ownershipRepository = value; - } - } - - public interface ISharedObject - { - IEndPointContext EndPointContext { get; set; } - } - - public class SharedObject : ISharedObject where T : notnull - { - [SerializationIgnore] - [JsonIgnore] - public IEndPointContext EndPointContext { get; set; } = new EndPointContext - { - RealRepository = TransmissionConfig.RealContextRepository, - RemoteRepository = TransmissionConfig.RemoteEndpointContextRepository, - HostId = TransmissionConfig.OwnershipRepository.GetHostOwnershipId(), - OwnerFunc = TransmissionConfig.OwnershipRepository.GetOwnershipId - }; - - private IContextRepository GetDefaultContextRepository() => - (_identifier.OwnerId == EndPointContext.HostId - ? EndPointContext.RealRepository - : EndPointContext.RemoteRepository) ?? - throw new NullReferenceException( - "DefaultContextRepository was not defined"); - - private UniversalObjectIdentifier _identifier = UniversalObjectIdentifier.Null; - - public UniversalObjectIdentifier Identifier - { - get - { - _identifier.OwnerId = _identifier.OwnerId == -1 ? EndPointContext.OwnerId : _identifier.OwnerId; - return _identifier; - } - set - { - _identifier = value; - Value = GetDefaultContextRepository().GetObjectBySharedObject(this); - } - } - - - // public int OwnerId - // { - // get - // { - // _ownerId = _ownerId == -1 ? EndPointContext.OwnerId : _ownerId; - // return _ownerId; - // } - // set => _ownerId = value; - // } - // - // // ReSharper disable once MemberCanBePrivate.Global - // public int ContextId - // { - // // ReSharper disable once UnusedMember.Global - // get - // { - // if (_contextId != -2) - // return _contextId; - // - // _contextId = EndPointContext.RealRepository.GetObjectIndex(Value); - // return _contextId; - // } - // set - // { - // _contextId = value; - // Value = GetDefaultContextRepository().GetObjectBySharedObject(this); - // } - // } - - [JsonIgnore] [SerializationIgnore] public T Value { get; private set; } - - // ReSharper disable once MemberCanBePrivate.Global - // ReSharper disable once UnusedMember.Global - public SharedObject() - { - } - - // ReSharper disable once UnusedMember.Global - public SharedObject(T value) - { - Value = value; - - if (value is RemoteObjectBase ro) - { - _identifier = ro.Identifier; - } - else - { - _identifier.OwnerId = EndPointContext.HostId; - _identifier.ContextId = EndPointContext.RealRepository.GetObjectIndex(Value); - } - } - - public static implicit operator T(SharedObject value) => value.Value; - - public static implicit operator SharedObject(T value) => - new(value); - } -} \ No newline at end of file diff --git a/mROA/Implementation/SharedObjectShell.cs b/mROA/Implementation/SharedObjectShell.cs new file mode 100644 index 0000000..3e55a78 --- /dev/null +++ b/mROA/Implementation/SharedObjectShell.cs @@ -0,0 +1,100 @@ +using System; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using mROA.Abstract; +using mROA.Implementation.Attributes; + +// ReSharper disable UnusedMember.Global +#pragma warning disable CS8618, CS9264 + +namespace mROA.Implementation +{ + public interface ISharedObjectShell + { + IEndPointContext EndPointContext { get; set; } + UniversalObjectIdentifier Identifier { get; set; } + object UniversalValue { get; set; } + } + + public class SharedObjectShellShell : ISharedObjectShell where T : notnull + { + [SerializationIgnore] + [JsonIgnore] + public IEndPointContext EndPointContext { get; set; } = new EndPointContext + { + RealRepository = TransmissionConfig.RealContextRepository, + RemoteRepository = TransmissionConfig.RemoteEndpointContextRepository, + HostId = TransmissionConfig.OwnershipRepository.GetHostOwnershipId(), + OwnerFunc = TransmissionConfig.OwnershipRepository.GetOwnershipId + }; + + private IContextRepository GetDefaultContextRepository() => + (_identifier.OwnerId == EndPointContext.HostId + ? EndPointContext.RealRepository + : EndPointContext.RemoteRepository) ?? + throw new NullReferenceException( + "DefaultContextRepository was not defined"); + + private UniversalObjectIdentifier _identifier = UniversalObjectIdentifier.Null; + + public UniversalObjectIdentifier Identifier + { + get + { + _identifier.OwnerId = _identifier.OwnerId == -1 ? EndPointContext.OwnerId : _identifier.OwnerId; + return _identifier; + } + set + { + _identifier = value; + Value = GetDefaultContextRepository().GetObjectBySharedObject(this); + } + } + + public object UniversalValue + { + get => _value; + set => _value = (T)value; + } + + private T _value; + + [JsonIgnore] + [SerializationIgnore] + public T Value + { + get => _value; + set + { + _value = value; + + if (value is RemoteObjectBase ro) + { + _identifier = ro.Identifier; + } + else + { + _identifier.OwnerId = EndPointContext.HostId; + _identifier.ContextId = EndPointContext.RealRepository.GetObjectIndex(Value); + } + } + } + + // ReSharper disable once MemberCanBePrivate.Global + // ReSharper disable once UnusedMember.Global + public SharedObjectShellShell() + { + } + + // ReSharper disable once UnusedMember.Global + public SharedObjectShellShell(T value) + { + Value = value; + } + + public static implicit operator T(SharedObjectShellShell value) => value.Value; + + public static implicit operator SharedObjectShellShell(T value) => + new(value); + } +} \ No newline at end of file diff --git a/mROA/Implementation/TransmissionConfig.cs b/mROA/Implementation/TransmissionConfig.cs new file mode 100644 index 0000000..5302f9c --- /dev/null +++ b/mROA/Implementation/TransmissionConfig.cs @@ -0,0 +1,35 @@ +using System; +using mROA.Abstract; + +namespace mROA.Implementation +{ +#pragma warning disable CS8618, CS9264 + public static class TransmissionConfig + { +#if TRACE + public static int TotalTransmittedBytes { get; set; } +#endif + private static IContextRepository? _realContextRepository; + private static IContextRepository? _remoteEndpointContextRepository; + private static IOwnershipRepository? _ownershipRepository; + + public static IContextRepository RealContextRepository + { + get => _realContextRepository ?? throw new NullReferenceException("RealContextRepository is null"); + set => _realContextRepository = value; + } + + public static IContextRepository RemoteEndpointContextRepository + { + get => _remoteEndpointContextRepository ?? + throw new NullReferenceException("RemoteEndpointContextRepository is null"); + set => _remoteEndpointContextRepository = value; + } + + public static IOwnershipRepository OwnershipRepository + { + get => _ownershipRepository ?? throw new NullReferenceException("OwnershipRepository is null"); + set => _ownershipRepository = value; + } + } +} \ No newline at end of file diff --git a/mROA/Implementation/UniversalObjectIdentifier.cs b/mROA/Implementation/UniversalObjectIdentifier.cs index 445292f..0348c0f 100644 --- a/mROA/Implementation/UniversalObjectIdentifier.cs +++ b/mROA/Implementation/UniversalObjectIdentifier.cs @@ -9,19 +9,18 @@ namespace mROA.Implementation public int OwnerId; public static UniversalObjectIdentifier Null = new UniversalObjectIdentifier { ContextId = -2, OwnerId = -1 }; + public static UniversalObjectIdentifier FromFlat(ulong flat) => new() { Flat = flat }; + public override string ToString() { return $"{{ {nameof(ContextId)}: {ContextId}, {nameof(OwnerId)}: {OwnerId} }}"; } public bool IsStatic => ContextId == -1; - + public ulong Flat { - get - { - return (ulong)OwnerId << 32 | (uint)ContextId; - } + get { return (ulong)OwnerId << 32 | (uint)ContextId; } set { OwnerId = (int)(value >> 32);