diff --git a/Example.Backend/Example.Backend.csproj b/Example.Backend/Example.Backend.csproj index 62581cc..95845de 100644 --- a/Example.Backend/Example.Backend.csproj +++ b/Example.Backend/Example.Backend.csproj @@ -2,9 +2,20 @@ Exe + net9.0 - enable + enable + + 9 + + + + TRACE + + + + TRACE; diff --git a/Example.Backend/LoadTestImp.cs b/Example.Backend/LoadTestImp.cs index e0ea125..a1fec60 100644 --- a/Example.Backend/LoadTestImp.cs +++ b/Example.Backend/LoadTestImp.cs @@ -1,28 +1,51 @@ -using Example.Shared; +using System; +using System.Threading; +using System.Threading.Tasks; +using Example.Shared; using mROA.Implementation.Attributes; -namespace Example.Backend; - -[SharedObjectSingleton] -public class LoadTestImp : ILoadTest +namespace Example.Backend { - public int Next(int last) + [SharedObjectSingleton] + public class LoadTestImp : ILoadTest { - return last + 1; - } + public int Next(int last) + { + return last + 1; + } - public int Last(int next) - { - return next - 1; - } + public int Last(int next) + { + return next - 1; + } - public void C() - { - throw new NotImplementedException(); - } + public void C() + { + throw new NotImplementedException(); + } - public void A() - { - throw new NotImplementedException(); + public void A() + { + throw new NotImplementedException(); + } + + public async Task AsyncTest(CancellationToken token) + { + Console.WriteLine("Async Test"); + + for (int i = 0; i < 10; i++) + { + if (token.IsCancellationRequested) + { + Console.WriteLine("Waiting canceled"); + return; + } + Console.WriteLine("Waiting..."); + await Task.Delay(1000); + } + + + Console.WriteLine("Waited until the end"); + } } } \ No newline at end of file diff --git a/Example.Backend/Page.cs b/Example.Backend/Page.cs index a635f09..7e2fb4d 100644 --- a/Example.Backend/Page.cs +++ b/Example.Backend/Page.cs @@ -1,13 +1,14 @@ using System.Text; using Example.Shared; -namespace Example.Backend; - -public class Page : IPage +namespace Example.Backend { - public string Text; - public byte[] GetData() + public class Page : IPage { - return Encoding.UTF8.GetBytes(Text); + public string Text; + public byte[] GetData() + { + return Encoding.UTF8.GetBytes(Text); + } } } \ No newline at end of file diff --git a/Example.Backend/PagesList.cs b/Example.Backend/PagesList.cs new file mode 100644 index 0000000..1b195c9 --- /dev/null +++ b/Example.Backend/PagesList.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using Example.Shared; +using mROA.Abstract; +using mROA.Implementation; + +namespace Example.Backend +{ + public class PagesList : RemoteObjectBase, IPagesList + { + public PagesList(int id, IRepresentationModule representationModule) : base(id, representationModule) + { + } + + public IReadOnlyList Collection { get; } + + public IPage this[int index] + { + get => GetResultAsync(3, new object[] { index }).GetAwaiter().GetResult(); + set => CallAsync(5, new object[] { index, value }).Wait(); + } + + public IPage Get(int index) + { + throw new NotImplementedException(); + } + + public void Add(IPage item) + { + throw new NotImplementedException(); + } + + public void Remove(int index, IPage item) + { + throw new NotImplementedException(); + } + + public event Action? OnAdd; + public event Action? OnRemove; + + public void Dispose() + { + // TODO release managed resources here + } + + public void OnAddExternal(IPage p0) + { + } + + public void OnRemoveExternal(IPage p0) + { + } + } +} \ No newline at end of file diff --git a/Example.Backend/Printer.cs b/Example.Backend/Printer.cs index 85cf27b..87958c9 100644 --- a/Example.Backend/Printer.cs +++ b/Example.Backend/Printer.cs @@ -1,21 +1,44 @@ +using System; +using System.Threading; +using System.Threading.Tasks; using Example.Shared; using mROA.Implementation; -namespace Example.Backend; - -public class Printer : IPrinter +namespace Example.Backend { - public string Name; - - public string GetName() + public class Printer : IPrinter { - return Name; - } + public string Name; - public async Task> Print(string text, CancellationToken cancellationToken = default) - { - // throw new Exception("The method or operation is not implemented."); - // var sharedObject = new Page { Text = text }; - return new Page { Text = text }; + public void OnPrintExternal(IPage p0, RequestContext ro) + { + OnPrint?.Invoke(p0, ro); + } + + public double Resource { get; set; } = 100d; + + public string GetName() + { + return Name; + } + + public async Task Print(string text, bool some, RequestContext context, + CancellationToken cancellationToken = default) + { + // throw new Exception("The method or operation is not implemented."); + var page = new Page { Text = text }; + Console.WriteLine($"Request id : :{context.RequestId}"); + OnPrint?.Invoke(page, context); + Resource /= 1.5; + return page; + } + + public event Action? OnPrint; + + public void Dispose() + { + Console.WriteLine( + "Dispose printer with name {0}, Dispose works, Dispose works, Dispose works, Dispose works,", Name); + } } } \ No newline at end of file diff --git a/Example.Backend/PrinterFactory.cs b/Example.Backend/PrinterFactory.cs index e71503f..efc3975 100644 --- a/Example.Backend/PrinterFactory.cs +++ b/Example.Backend/PrinterFactory.cs @@ -1,40 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; using Example.Shared; -using mROA.Implementation; using mROA.Implementation.Attributes; -namespace Example.Backend; - -[SharedObjectSingleton] -public class PrinterFactory : IPrinterFactory +namespace Example.Backend { - private List _printers = new(); - - public SharedObject Create(string printerName) + [SharedObjectSingleton] + public class PrinterFactory : IPrinterFactory { - Console.WriteLine("Creating printer"); - return new Printer { Name = printerName }; - } + private List _printers = new List(); - public void Register(SharedObject printer) - { - _printers.Add(printer.Value); - Console.WriteLine("Registered printer"); - } + public IPrinter Create(string printerName) + { + Console.WriteLine("Creating printer"); + return new Printer { Name = printerName }; + } - public SharedObject GetPrinterByName(string printerName) - { - Console.WriteLine("Getting printer"); - return new SharedObject(_printers.Find(i => i.GetName() == printerName)!); - } + public void Register(IPrinter printer) + { + _printers.Add(printer); + Console.WriteLine("Registered printer"); + } - public SharedObject GetFirstPrinter() - { - return new SharedObject(_printers.First()); - } + public IPrinter GetPrinterByName(string printerName) + { + Console.WriteLine("Getting printer"); + return (_printers.Find(i => i.GetName() == printerName)!); + } - public string[] CollectAllNames() - { - Console.WriteLine("Collecting all printers"); - return _printers.Select(i => i.GetName()).ToArray(); + public IPrinter GetFirstPrinter() + { + return _printers.First(); + } + + public string[] CollectAllNames() + { + Console.WriteLine("Collecting all printers"); + return _printers.Select(i => i.GetName()).ToArray(); + } } } \ No newline at end of file diff --git a/Example.Backend/Program.cs b/Example.Backend/Program.cs index 65c6fbf..1e12d48 100644 --- a/Example.Backend/Program.cs +++ b/Example.Backend/Program.cs @@ -1,43 +1,56 @@ -using System.Net; +using System.Linq; +using System.Net; using Example.Backend; using mROA.Abstract; +using mROA.Cbor; using mROA.Codegen; using mROA.Implementation; using mROA.Implementation.Backend; using mROA.Implementation.Bootstrap; using mROA.Implementation.Frontend; - -var builder = new FullMixBuilder(); -builder.UseJsonSerialisation(); -builder.Modules.Add(new BackendIdentityGenerator()); -builder.UseNetworkGateway(new IPEndPoint(IPAddress.Loopback, 4567), typeof(NextGenerationInteractionModule), - builder.GetModule()!); - -builder.Modules.Add(new ConnectionHub()); -builder.Modules.Add(new HubRequestExtractor(typeof(RequestExtractor))); - -builder.UseBasicExecution(); - -builder.Modules.Add(new RemoteContextRepository()); -// builder.UseCollectableContextRepository(typeof(PrinterFactory).Assembly); -builder.Modules.Add(new MultiClientContextRepository(i => +class Program { - var repo = new ContextRepository(); - repo.FillSingletons(typeof(PrinterFactory).Assembly); - return repo; -})); -builder.SetupMethodsRepository(new CoCodegenMethodRepository()); -builder.Modules.Add(new CreativeRepresentationModuleProducer([builder.GetModule()!], - typeof(RepresentationModule))); + public static void Main(string[] args) + { + var builder = new FullMixBuilder(); + // builder.UseJsonSerialisation(); + builder.Modules.Add(new CborSerializationToolkit()); + builder.Modules.Add(new BackendIdentityGenerator()); + // builder.UseNetworkGateway(new IPEndPoint(IPAddress.Loopback, 4567), typeof(NextGenerationInteractionModule), + // builder.GetModule()!); + builder.UseNetworkGateway(new IPEndPoint(IPAddress.Loopback, 4567), typeof(NextGenerationInteractionModule), + builder.GetModule()!); -builder.Build(); -new RemoteTypeBinder(); + builder.Modules.Add(new ConnectionHub()); + builder.Modules.Add(new HubRequestExtractor(typeof(RequestExtractor))); -TransmissionConfig.RealContextRepository = builder.GetModule(); -TransmissionConfig.RemoteEndpointContextRepository = builder.GetModule(); -TransmissionConfig.OwnershipRepository = new MultiClientOwnershipRepository(); + builder.UseBasicExecution(); + builder.Modules.Add(new CreativeRepresentationModuleProducer( + new IInjectableModule[] { builder.GetModule()! }, + typeof(RepresentationModule))); + builder.Modules.Add(new RemoteContextRepository()); +// builder.UseCollectableContextRepository(typeof(PrinterFactory).Assembly); + builder.Modules.Add(new MultiClientContextRepository(i => + { + var repo = new ContextRepository(); + repo.FillSingletons(typeof(PrinterFactory).Assembly); + repo.Inject(builder.Modules.OfType().First()); + return repo; + })); + builder.SetupMethodsRepository(new CoCodegenMethodRepository()); -var gateway = builder.GetModule(); + builder.Modules.Add(new CancellationRepository()); -gateway.Run(); \ No newline at end of file + builder.Build(); + new RemoteTypeBinder(); + + TransmissionConfig.RealContextRepository = builder.GetModule(); + TransmissionConfig.RemoteEndpointContextRepository = builder.GetModule(); + TransmissionConfig.OwnershipRepository = new MultiClientOwnershipRepository(); + + var gateway = builder.GetModule(); + + gateway.Run(); + } +} \ No newline at end of file diff --git a/Example.Events.Backend/Chat.cs b/Example.Events.Backend/Chat.cs new file mode 100644 index 0000000..2239d59 --- /dev/null +++ b/Example.Events.Backend/Chat.cs @@ -0,0 +1,18 @@ +using Example.Events.Shared; +using mROA.Implementation; + +namespace Example.Events.Backend; + +public class Chat : IChat +{ + public void PostSymbol(string symbol, RequestContext context) + { + OnCharPosted?.Invoke(symbol, context); + } + + public event Action? OnCharPosted; + + public void OnCharPostedExternal(string p0, RequestContext p1) + { + } +} \ No newline at end of file diff --git a/Example.Events.Backend/ChatFactory.cs b/Example.Events.Backend/ChatFactory.cs new file mode 100644 index 0000000..90a4c65 --- /dev/null +++ b/Example.Events.Backend/ChatFactory.cs @@ -0,0 +1,22 @@ +using Example.Events.Shared; +using mROA.Implementation.Attributes; + +namespace Example.Events.Backend; + +[SharedObjectSingleton] +public class ChatFactory : IChatFactory +{ + private static readonly Dictionary Chats = new(); + + public IChat GetChat(Guid id) + { + if (Chats.TryGetValue(id, out var chat)) + { + return chat; + } + + var created = new Chat(); + Chats.Add(id, created); + return created; + } +} \ No newline at end of file diff --git a/Example.Events.Backend/Example.Events.Backend.csproj b/Example.Events.Backend/Example.Events.Backend.csproj new file mode 100644 index 0000000..4e79fe1 --- /dev/null +++ b/Example.Events.Backend/Example.Events.Backend.csproj @@ -0,0 +1,18 @@ + + + + Exe + net9.0 + enable + enable + + + + + + + + + + + diff --git a/Example.Events.Backend/Program.cs b/Example.Events.Backend/Program.cs new file mode 100644 index 0000000..d42eb89 --- /dev/null +++ b/Example.Events.Backend/Program.cs @@ -0,0 +1,53 @@ +using System.Net; +using mROA.Abstract; +using mROA.Cbor; +using mROA.Codegen; +using mROA.Implementation; +using mROA.Implementation.Backend; +using mROA.Implementation.Bootstrap; +using mROA.Implementation.Frontend; + +namespace Example.Events.Backend; + +class Program +{ + static void Main(string[] args) + { + var builder = new FullMixBuilder(); + builder.Modules.Add(new CborSerializationToolkit()); + builder.Modules.Add(new BackendIdentityGenerator()); + builder.UseNetworkGateway(IPEndPoint.Parse("192.168.1.101:6000"), typeof(NextGenerationInteractionModule), + builder.GetModule()!); + + builder.Modules.Add(new ConnectionHub()); + builder.Modules.Add(new HubRequestExtractor(typeof(RequestExtractor))); + + builder.UseBasicExecution(); + builder.Modules.Add(new CreativeRepresentationModuleProducer( + new IInjectableModule[] { builder.GetModule()! }, + typeof(RepresentationModule))); + builder.Modules.Add(new RemoteContextRepository()); +// builder.UseCollectableContextRepository(typeof(PrinterFactory).Assembly); + builder.Modules.Add(new MultiClientContextRepository(i => + { + var repo = new ContextRepository(); + repo.FillSingletons(typeof(ChatFactory).Assembly); + repo.Inject(builder.Modules.OfType().First()); + return repo; + })); + builder.SetupMethodsRepository(new CoCodegenMethodRepository()); + + builder.Modules.Add(new CancellationRepository()); + + builder.Build(); + new RemoteTypeBinder(); + + TransmissionConfig.RealContextRepository = builder.GetModule(); + TransmissionConfig.RemoteEndpointContextRepository = builder.GetModule(); + TransmissionConfig.OwnershipRepository = new MultiClientOwnershipRepository(); + + var gateway = builder.GetModule(); + + gateway.Run(); + } +} \ No newline at end of file diff --git a/Example.Events.Client/Example.Events.Client.csproj b/Example.Events.Client/Example.Events.Client.csproj new file mode 100644 index 0000000..4e79fe1 --- /dev/null +++ b/Example.Events.Client/Example.Events.Client.csproj @@ -0,0 +1,18 @@ + + + + Exe + net9.0 + enable + enable + + + + + + + + + + + diff --git a/Example.Events.Client/Program.cs b/Example.Events.Client/Program.cs new file mode 100644 index 0000000..155e969 --- /dev/null +++ b/Example.Events.Client/Program.cs @@ -0,0 +1,72 @@ +using System.Diagnostics; +using System.Net; +using Example.Events.Shared; +using mROA.Cbor; +using mROA.Codegen; +using mROA.Implementation; +using mROA.Implementation.Backend; +using mROA.Implementation.Bootstrap; +using mROA.Implementation.Frontend; + +namespace Example.Events.Client; + +class Program +{ + static void Main(string[] args) + { + var builder = new FullMixBuilder(); + new RemoteTypeBinder(); + // builder.Modules.Add(new JsonSerializationToolkit()); + builder.Modules.Add(new CborSerializationToolkit()); + + builder.Modules.Add(new RemoteContextRepository()); + builder.Modules.Add(new NextGenerationInteractionModule()); + builder.Modules.Add(new RepresentationModule()); + builder.Modules.Add(new NetworkFrontendBridge(new IPEndPoint(IPAddress.Parse("95.105.78.72"), 6000))); + builder.Modules.Add(new StaticRepresentationModuleProducer()); + builder.Modules.Add(new RequestExtractor()); + builder.Modules.Add(new BasicExecutionModule()); + builder.Modules.Add(new CoCodegenMethodRepository()); + builder.UseCollectableContextRepository(); + builder.Modules.Add(new CancellationRepository()); + + builder.Build(); + + + TransmissionConfig.RealContextRepository = builder.GetModule(); + TransmissionConfig.RemoteEndpointContextRepository = builder.GetModule(); + + builder.GetModule()!.Connect(); + _ = builder.GetModule()!.StartExtraction(); + + var chatFactory = + TransmissionConfig.RemoteEndpointContextRepository.GetSingleObject(typeof(IChatFactory), 0) as IChatFactory; + var chat = chatFactory.GetChat(Guid.Empty); + chat.OnCharPosted += (s, context) => { Console.Write(s); }; + while (true) + { + var input = Console.ReadKey(true); + if (input.Key == ConsoleKey.Escape) + return; + + var symb = ""; + + if (input.Key == ConsoleKey.Backspace) + { + symb = "\b \b"; + } + else + symb = input.KeyChar.ToString(); +#if TRACE + Console.Write(symb); + var sw = Stopwatch.StartNew(); + chat.PostSymbol(symb); + sw.Stop(); + Console.WriteLine($"{sw.ElapsedMilliseconds}ms"); +#else + Console.Write(symb); + chat.PostSymbol(symb); +#endif + } + } +} \ No newline at end of file diff --git a/Example.Events.Shared/Example.Events.Shared.csproj b/Example.Events.Shared/Example.Events.Shared.csproj new file mode 100644 index 0000000..98d21fd --- /dev/null +++ b/Example.Events.Shared/Example.Events.Shared.csproj @@ -0,0 +1,16 @@ + + + + net9.0 + enable + 9 + + + + + + + + + + diff --git a/Example.Events.Shared/IChat.cs b/Example.Events.Shared/IChat.cs new file mode 100644 index 0000000..a37b06a --- /dev/null +++ b/Example.Events.Shared/IChat.cs @@ -0,0 +1,13 @@ +using System; +using mROA.Implementation; +using mROA.Implementation.Attributes; + +namespace Example.Events.Shared +{ + [SharedObjectInterface] + public partial interface IChat : IShared + { + void PostSymbol(string symbol, RequestContext context = default); + event Action OnCharPosted; + } +} \ No newline at end of file diff --git a/Example.Events.Shared/IChatFactory.cs b/Example.Events.Shared/IChatFactory.cs new file mode 100644 index 0000000..b73949b --- /dev/null +++ b/Example.Events.Shared/IChatFactory.cs @@ -0,0 +1,12 @@ +using System; +using mROA.Implementation; +using mROA.Implementation.Attributes; + +namespace Example.Events.Shared +{ + [SharedObjectInterface] + public interface IChatFactory : IShared + { + IChat GetChat(Guid id); + } +} \ No newline at end of file diff --git a/Example.Frontend/ClientBasedPrinter.cs b/Example.Frontend/ClientBasedPrinter.cs index 58a97c8..6afc7a3 100644 --- a/Example.Frontend/ClientBasedPrinter.cs +++ b/Example.Frontend/ClientBasedPrinter.cs @@ -1,28 +1,47 @@ -using Example.Shared; +using System; +using System.Threading; +using System.Threading.Tasks; +using Example.Shared; using mROA.Implementation; -namespace Example.Frontend; - -public class ClientBasedPrinter : IPrinter +namespace Example.Frontend { - public string GetName() + public class ClientBasedPrinter : IPrinter { - Console.WriteLine("ClientBasedPrinter called from server!!!!!!!!!!! Vova likes that:)"); - return "ClientBasedPrinter from mroa"; + public void OnPrintExternal(IPage p0, RequestContext ro) + { + } + + public double Resource { get; set; } + + public string GetName() + { + Console.WriteLine("ClientBasedPrinter called from server!!!!!!!!!!! Vova likes that:)"); + DemoCheck.BackwardCall = true; + + return "ClientBasedPrinter from mroa"; + } + + public async Task Print(string text, bool some, RequestContext context, + CancellationToken cancellationToken) + { + Console.WriteLine($"Printed: {text}"); + await Task.Yield(); + return new ClientBasedPage(); + } + + public event Action? OnPrint; + + public void Dispose() + { + } } - public async Task> Print(string text, CancellationToken cancellationToken) + public class ClientBasedPage : IPage { - Console.WriteLine($"Printed: {text}"); - await Task.Yield(); - return new ClientBasedPage(); - } -} - -public class ClientBasedPage : IPage -{ - public byte[] GetData() - { - return [1, 2, 3]; + public byte[] GetData() + { + return new byte[] { 1, 2, 3 }; + } } } \ No newline at end of file diff --git a/Example.Frontend/DemoCheck.cs b/Example.Frontend/DemoCheck.cs new file mode 100644 index 0000000..efc95aa --- /dev/null +++ b/Example.Frontend/DemoCheck.cs @@ -0,0 +1,37 @@ +using System; +using System.Linq; + +namespace Example.Frontend +{ + public static class DemoCheck + { + public static bool ClientBasedImplementation; + public static bool TaskCancelation; + public static bool Dispose; + public static bool PropertySet; + public static bool PropertyGet; + public static bool TaskExecution; + public static bool BackwardCall; + public static bool BasicNonParamsCall; + public static bool EventCallback; + public static bool CreatingPrinter; + + public static void Show() + { + Console.WriteLine("======================== Demo summary ========================"); + var fields = typeof(DemoCheck).GetFields().OrderBy(i => i.Name).ToList(); + foreach (var field in fields) + { + var value = (bool)field.GetValue(null)!; + if (value) + { + Console.BackgroundColor = ConsoleColor.Green; + }else Console.BackgroundColor = ConsoleColor.Gray; + + Console.Write($"{field.Name}"); + Console.BackgroundColor = ConsoleColor.Black; + Console.WriteLine(); + } + } + } +} \ No newline at end of file diff --git a/Example.Frontend/Example.Frontend.csproj b/Example.Frontend/Example.Frontend.csproj index f59d7cc..fdac1b1 100644 --- a/Example.Frontend/Example.Frontend.csproj +++ b/Example.Frontend/Example.Frontend.csproj @@ -3,8 +3,18 @@ Exe net9.0 - enable + enable + + 9 + + + + TRACE; + + + + diff --git a/Example.Frontend/Program.cs b/Example.Frontend/Program.cs index f4800df..156026d 100644 --- a/Example.Frontend/Program.cs +++ b/Example.Frontend/Program.cs @@ -1,80 +1,132 @@ -using System.Diagnostics; +using System; using System.Net; using System.Text; +using System.Threading; +using System.Threading.Tasks; using Example.Frontend; using Example.Shared; +using mROA.Cbor; using mROA.Codegen; using mROA.Implementation; using mROA.Implementation.Backend; using mROA.Implementation.Bootstrap; using mROA.Implementation.Frontend; -var builder = new FullMixBuilder(); -new RemoteTypeBinder(); -builder.Modules.Add(new JsonSerializationToolkit()); -builder.Modules.Add(new RemoteContextRepository()); -builder.Modules.Add(new NextGenerationInteractionModule()); -builder.Modules.Add(new RepresentationModule()); -builder.Modules.Add(new NetworkFrontendBridge(new IPEndPoint(IPAddress.Loopback, 4567))); -builder.Modules.Add(new StaticRepresentationModuleProducer()); -builder.Modules.Add(new RequestExtractor()); -builder.Modules.Add(new BasicExecutionModule()); -builder.Modules.Add(new CoCodegenMethodRepository()); -builder.UseCollectableContextRepository(); -builder.Build(); +class Program +{ + public static void Main(string[] args) + { + var builder = new FullMixBuilder(); + new RemoteTypeBinder(); + // builder.Modules.Add(new JsonSerializationToolkit()); + builder.Modules.Add(new CborSerializationToolkit()); + + builder.Modules.Add(new RemoteContextRepository()); + builder.Modules.Add(new NextGenerationInteractionModule()); + builder.Modules.Add(new RepresentationModule()); + builder.Modules.Add(new NetworkFrontendBridge(new IPEndPoint(IPAddress.Loopback, 4567))); + builder.Modules.Add(new StaticRepresentationModuleProducer()); + builder.Modules.Add(new RequestExtractor()); + builder.Modules.Add(new BasicExecutionModule()); + builder.Modules.Add(new CoCodegenMethodRepository()); + builder.UseCollectableContextRepository(); + builder.Modules.Add(new CancellationRepository()); + + builder.Build(); -TransmissionConfig.RealContextRepository = builder.GetModule(); -TransmissionConfig.RemoteEndpointContextRepository = builder.GetModule(); + TransmissionConfig.RealContextRepository = builder.GetModule(); + TransmissionConfig.RemoteEndpointContextRepository = builder.GetModule(); -builder.GetModule()!.Connect(); -_ = builder.GetModule()!.StartExtraction(); -Console.WriteLine(TransmissionConfig.OwnershipRepository.GetOwnershipId()); -var context = builder.GetModule(); + builder.GetModule()!.Connect(); + _ = builder.GetModule()!.StartExtraction(); + Console.WriteLine(TransmissionConfig.OwnershipRepository.GetOwnershipId()); + var context = builder.GetModule(); -var factory = context.GetSingleObject(); + var factory = context.GetSingleObject(typeof(IPrinterFactory), 0) as IPrinterFactory; //правильный порядок команд 8-5-10-7 -var printer = factory.Create("Test"); -Console.WriteLine("Printer created"); -Thread.Sleep(100); + using (var disposingPrinter = factory.Create("Test")) + { + DemoCheck.CreatingPrinter = true; + disposingPrinter.OnPrint += (_, _) => + { + Console.WriteLine("New page creater. Called from event!!!"); + DemoCheck.EventCallback = true; + }; + Console.WriteLine("Printer created"); + Thread.Sleep(100); -var name = printer.Value.GetName(); -Console.WriteLine("Printer name : {0}", name); -Thread.Sleep(100); + var name = disposingPrinter.GetName(); + DemoCheck.BasicNonParamsCall = true; + Console.WriteLine("Printer name : {0}", name); -factory.Register(new SharedObject(new ClientBasedPrinter())); -Console.WriteLine("Registered printer"); -Thread.Sleep(100); + Thread.Sleep(100); + + factory.Register(new ClientBasedPrinter()); + DemoCheck.ClientBasedImplementation = true; + Console.WriteLine("Registered printer"); + Thread.Sleep(100); -var registred = factory.GetFirstPrinter(); -Console.WriteLine("First printer"); -Thread.Sleep(100); + var registered = factory.GetFirstPrinter(); + Console.WriteLine("First printer"); + Thread.Sleep(100); -Console.WriteLine(registred.Value); -Console.WriteLine("Collecting all printers"); -var names = factory.CollectAllNames(); -Thread.Sleep(100); + Console.WriteLine(registered); + Console.WriteLine("Collecting all printers"); + var names = factory.CollectAllNames(); + Thread.Sleep(100); -Console.WriteLine(string.Join(", ", names)); + Console.WriteLine(string.Join(", ", names)); -var page = printer.Value.Print("Test Page", new CancellationToken()).GetAwaiter().GetResult(); -var data = page.Value.GetData(); -Console.WriteLine("Data : {0}", Encoding.UTF8.GetString(data)); + var page = disposingPrinter.Print("Test Page", false, default, CancellationToken.None).GetAwaiter() + .GetResult(); + Console.WriteLine("Page printed"); + DemoCheck.TaskExecution = true; + Console.WriteLine(page.ToString()); -var loadSingleton = context.GetSingleObject(typeof(ILoadTest)) as ILoadTest; + Console.WriteLine($"Printer resource : {disposingPrinter.Resource}"); + DemoCheck.PropertyGet = true; -const int iterations = 10000; -var timer = Stopwatch.StartNew(); -var x = 0; -for (int i = 0; i < iterations; i++) -{ - x = loadSingleton.Next(x); - // Console.WriteLine(x); -} + Console.WriteLine("Restoring resource"); + disposingPrinter.Resource = 100; + DemoCheck.PropertySet = true; + Console.WriteLine($"Printer resource again : {disposingPrinter.Resource}"); -timer.Stop(); -Console.WriteLine("X is {0}", x); -Console.WriteLine("Time : {0}", timer.Elapsed.TotalMilliseconds); -Console.WriteLine($"Time per call: {timer.Elapsed.TotalMilliseconds / iterations} ms"); \ No newline at end of file + var data = page.GetData(); + Console.WriteLine("Data : {0}", Encoding.UTF8.GetString(data)); + + Console.WriteLine("Dispose printer"); + } + DemoCheck.Dispose = true; + + + var loadSingleton = context.GetSingleObject(typeof(ILoadTest), 0) as ILoadTest; + + + var cts = new CancellationTokenSource(); + var token = cts.Token; + var t = Task.Run(async () => await loadSingleton!.AsyncTest(token)); + + Thread.Sleep(5000); + cts.Cancel(); + Console.WriteLine($"Token state {cts.Token.IsCancellationRequested}"); + DemoCheck.TaskCancelation = true; + DemoCheck.Show(); + Console.ReadKey(); + // + // const int iterations = 10000; + // var timer = Stopwatch.StartNew(); + // var x = 0; + // for (int i = 0; i < iterations; i++) + // { + // x = loadSingleton.Next(x); + // } + // + // timer.Stop(); + // Console.WriteLine("X is {0}", x); + // Console.WriteLine("Time : {0}", timer.Elapsed.TotalMilliseconds); + // Console.WriteLine($"Time per call: {timer.Elapsed.TotalMilliseconds / iterations} ms"); + } +} \ No newline at end of file diff --git a/Example.Shared/Example.Shared.csproj b/Example.Shared/Example.Shared.csproj index 40f085d..98d21fd 100644 --- a/Example.Shared/Example.Shared.csproj +++ b/Example.Shared/Example.Shared.csproj @@ -2,14 +2,15 @@ net9.0 - enable enable + 9 + - + diff --git a/Example.Shared/IDataList.cs b/Example.Shared/IDataList.cs new file mode 100644 index 0000000..bdaae63 --- /dev/null +++ b/Example.Shared/IDataList.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using mROA.Implementation; + +namespace Example.Shared +{ + public interface IDataList : IShared, IDisposable + { + IReadOnlyList Collection { get; } + T this[int index] { get; set; } + T Get(int index); + void Add(T item); + void Remove(int index, T item); + event Action OnAdd; + event Action OnRemove; + } +} \ No newline at end of file diff --git a/Example.Shared/ILoadTest.cs b/Example.Shared/ILoadTest.cs index dc4e787..227af99 100644 --- a/Example.Shared/ILoadTest.cs +++ b/Example.Shared/ILoadTest.cs @@ -1,13 +1,18 @@ -using mROA.Implementation.Attributes; +using System.Threading; +using System.Threading.Tasks; +using mROA.Implementation; +using mROA.Implementation.Attributes; -namespace Example.Shared; - -[SharedObjectInterface] -public interface ILoadTest +namespace Example.Shared { - int Next(int last); - int Last(int next); - void C(); - void A(); -} + [SharedObjectInterface] + public interface ILoadTest : IShared + { + int Next(int last); + int Last(int next); + void C(); + void A(); + Task AsyncTest(CancellationToken token = default); + } +} \ No newline at end of file diff --git a/Example.Shared/IPage.cs b/Example.Shared/IPage.cs index 6f39f19..c7517ff 100644 --- a/Example.Shared/IPage.cs +++ b/Example.Shared/IPage.cs @@ -1,9 +1,11 @@ +using mROA.Implementation; using mROA.Implementation.Attributes; -namespace Example.Shared; - -[SharedObjectInterface] -public interface IPage +namespace Example.Shared { - byte[] GetData(); + [SharedObjectInterface] + public interface IPage : IShared + { + byte[] GetData(); + } } \ No newline at end of file diff --git a/Example.Shared/IPagesList.cs b/Example.Shared/IPagesList.cs new file mode 100644 index 0000000..0b4d5d5 --- /dev/null +++ b/Example.Shared/IPagesList.cs @@ -0,0 +1,9 @@ +using mROA.Implementation.Attributes; + +namespace Example.Shared +{ + [SharedObjectInterface] + public partial interface IPagesList : IDataList + { + } +} \ No newline at end of file diff --git a/Example.Shared/IPrinter.cs b/Example.Shared/IPrinter.cs index 2d8e6c8..f5b25f0 100644 --- a/Example.Shared/IPrinter.cs +++ b/Example.Shared/IPrinter.cs @@ -1,12 +1,17 @@ +using System; +using System.Threading; +using System.Threading.Tasks; using mROA.Implementation; using mROA.Implementation.Attributes; -namespace Example.Shared; - -[SharedObjectInterface] -public interface IPrinter +namespace Example.Shared { - string GetName(); - Task> Print(string text, CancellationToken cancellationToken); - + [SharedObjectInterface] + public partial interface IPrinter : IDisposable, IShared + { + double Resource { get; set; } + string GetName(); + Task Print(string text, bool someParameter, RequestContext context, CancellationToken cancellationToken); + event Action OnPrint; + } } \ No newline at end of file diff --git a/Example.Shared/IPrinterFactory.cs b/Example.Shared/IPrinterFactory.cs index eb6ee8e..60d4d0e 100644 --- a/Example.Shared/IPrinterFactory.cs +++ b/Example.Shared/IPrinterFactory.cs @@ -1,15 +1,15 @@ using mROA.Implementation; using mROA.Implementation.Attributes; -namespace Example.Shared; - -[SharedObjectInterface] -public interface IPrinterFactory +namespace Example.Shared { - SharedObject Create(string printerName); - void Register(SharedObject printer); - SharedObject GetPrinterByName(string printerName); - SharedObject GetFirstPrinter(); - string[] CollectAllNames(); - + [SharedObjectInterface] + 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.Benchmark/Program.cs b/mROA.Benchmark/Program.cs index 57ab0c3..217a6d7 100644 --- a/mROA.Benchmark/Program.cs +++ b/mROA.Benchmark/Program.cs @@ -1,50 +1,51 @@ -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; +using System.Collections.Generic; +using System.Linq; +using BenchmarkDotNet.Attributes; -namespace mROA.Benchmark; - -class Program +namespace mROA.Benchmark { - static void Main(string[] args) + class Program { - Console.WriteLine("Hello, World!"); - var summary = BenchmarkRunner.Run(); - - } -} - -public class CollectionsSpeed -{ - private const int N = 1000; - - private readonly List _immutable; - private readonly int[] _array; - - public CollectionsSpeed() - { - _array = Enumerable.Range(0, N).ToArray(); - _immutable = [.._array]; - } - - [Benchmark] - public int DefaultArray() - { - var sum = 0; - for (int i = 0; i < N; i++) + static void Main(string[] args) { - sum += _array[i]; + // Console.WriteLine("Hello, World!"); + // var summary = BenchmarkRunner.Run(); } - return sum; } - - [Benchmark] - public int ImmutableArray() + + public class CollectionsSpeed { - var sum = 0; - for (int i = 0; i < N; i++) + private const int N = 1000; + + private readonly List _immutable; + private readonly int[] _array; + + public CollectionsSpeed() { - sum += _immutable[i]; + _array = Enumerable.Range(0, N).ToArray(); + // _immutable = [.._array]; + } + + [Benchmark] + public int DefaultArray() + { + var sum = 0; + for (int i = 0; i < N; i++) + { + sum += _array[i]; + } + return sum; + } + + [Benchmark] + public int ImmutableArray() + { + var sum = 0; + for (int i = 0; i < N; i++) + { + sum += _immutable[i]; + } + return sum; } - return sum; } } \ No newline at end of file diff --git a/mROA.Benchmark/mROA.Benchmark.csproj b/mROA.Benchmark/mROA.Benchmark.csproj index 78eb323..5697c66 100644 --- a/mROA.Benchmark/mROA.Benchmark.csproj +++ b/mROA.Benchmark/mROA.Benchmark.csproj @@ -2,8 +2,8 @@ Exe - net9.0 - enable + netstandard2.1 + enable diff --git a/mROA.Cbor/CborSerializationToolkit.cs b/mROA.Cbor/CborSerializationToolkit.cs new file mode 100644 index 0000000..47d1fc0 --- /dev/null +++ b/mROA.Cbor/CborSerializationToolkit.cs @@ -0,0 +1,442 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Formats.Cbor; +using System.Linq; +using System.Reflection; +using mROA.Abstract; +using mROA.Implementation; +using mROA.Implementation.Attributes; + +namespace mROA.Cbor +{ + public class CborSerializationToolkit : IContextualSerializationToolKit + { + public byte[] Serialize(object objectToSerialize, IEndPointContext context) + { + var writer = new CborWriter(); + WriteData(objectToSerialize, writer, context); + return writer.Encode(); + } + + public void Serialize(object objectToSerialize, Span destination, IEndPointContext context) + { + var writer = new CborWriter(); + WriteData(objectToSerialize, writer, context); + writer.Encode(destination); + } + + public T Deserialize(byte[] rawData, IEndPointContext? context) + { + return (T)Deserialize(rawData, typeof(T), context) ?? default; + } + + public object? Deserialize(byte[] rawData, Type type, IEndPointContext? context) + { + return Deserialize(rawData.AsMemory(), type, context); + } + + public T Deserialize(ReadOnlyMemory rawMemory, IEndPointContext? context) + { + return (T)Deserialize(rawMemory, typeof(T), context); + } + + public object? Deserialize(ReadOnlyMemory rawMemory, Type type, IEndPointContext? context) + { + var reader = new CborReader(rawMemory); + return ReadData(reader, type, context); + } + + public T Cast(object nonCasted, IEndPointContext? context) + { + return (T)Cast(nonCasted, typeof(T), context); + } + + public object? Cast(object? nonCasted, Type type, IEndPointContext? context) + { + if (nonCasted == null) + return null; + + if (nonCasted.GetType() == type) + return nonCasted; + + if (nonCasted is PreParsedValue preParsed) + return preParsed.ToObject(type, context); + + + if (type == typeof(Guid)) + { + return new Guid((byte[])nonCasted); + } + + return Convert.ChangeType(nonCasted, type); + } + + public void Inject(T dependency) + { + } + + public byte[] Serialize(T objectToSerialize) + { + return Serialize(objectToSerialize, typeof(T)); + } + + public byte[] Serialize(object objectToSerialize, Type type) + { + return Serialize(objectToSerialize, context: null); + } + + public T Deserialize(byte[] rawData) + { + return Deserialize(rawData: rawData, context: null); + } + + public object? Deserialize(byte[] rawData, Type type) + { + return Deserialize(rawData: rawData, type, context: null); + } + + public T Deserialize(Span rawData) + { + return Deserialize(rawData.ToArray().AsMemory(), context: null); + } + + public object? Deserialize(Span rawData, Type type) + { + return Deserialize(rawData: rawData.ToArray(), type: type); + } + + public T Cast(object nonCasted) + { + return Cast(nonCasted: nonCasted, context: null); + } + + public object Cast(object nonCasted, Type type) + { + return Cast(nonCasted: nonCasted, type: type, context: null); + } + + private void WriteData(object? obj, CborWriter writer, IEndPointContext? context) + { + switch (obj) + { + case int i: + writer.WriteInt32(i); + break; + case long l: + writer.WriteInt64(l); + break; + case float f: + writer.WriteSingle(f); + break; + case double d: + writer.WriteDouble(d); + break; + case bool b: + writer.WriteBoolean(b); + break; + case string s: + writer.WriteTextString(s); + break; + case null: + writer.WriteNull(); + break; + case uint ui: + writer.WriteUInt32(ui); + break; + case ulong ul: + writer.WriteUInt64(ul); + break; + case DateTimeOffset dto: + writer.WriteDateTimeOffset(dto); + break; + case Guid g: + writer.WriteByteString(g.ToByteArray()); + break; + case byte[] bytes: + writer.WriteByteString(bytes); + break; + case IDictionary dictionary: + WriteDictionary(dictionary, writer, context); + break; + case IList enumerable: + WriteList(enumerable, writer, context); + break; + case ISharedObjectShell sharedObject: + if (context != null) + sharedObject.EndPointContext = context; + WriteObject(sharedObject, writer, context); + break; + default: + if (obj.GetType().IsEnum) + { + writer.WriteInt32((int)obj); + break; + } + + WriteObject(obj, writer, context); + break; + } + } + + private void WriteList(IList list, CborWriter writer, IEndPointContext? context) + { + writer.WriteStartArray(list.Count); + + foreach (var element in list) + WriteData(element, writer, context); + + writer.WriteEndArray(); + } + + private void WriteDictionary(IDictionary dictionary, CborWriter writer, IEndPointContext? context) + { + writer.WriteStartMap(dictionary.Count); + var keysEnumerator = dictionary.Keys.GetEnumerator(); + var valuesEnumerator = dictionary.Values.GetEnumerator(); + for (int i = 0; i < dictionary.Count; i++) + { + keysEnumerator.MoveNext(); + valuesEnumerator.MoveNext(); + WriteData(keysEnumerator.Current, writer, context); + WriteData(valuesEnumerator.Current, writer, context); + } + + writer.WriteEndMap(); + (keysEnumerator as IDisposable)?.Dispose(); + (valuesEnumerator as IDisposable)?.Dispose(); + } + + 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); + } + + private object? ReadData(CborReader reader, Type? type, IEndPointContext? context) + { + var state = reader.PeekState(); + switch (state) + { + case CborReaderState.Boolean: + return reader.ReadBoolean(); + case CborReaderState.UnsignedInteger: + case CborReaderState.NegativeInteger: + if (type == typeof(int) || type is { IsEnum: true }) + return reader.ReadInt32(); + if (type == typeof(long)) + return reader.ReadInt64(); + if (type == typeof(uint)) + return reader.ReadUInt32(); + if (type == typeof(ulong)) + return reader.ReadUInt64(); + + return reader.ReadUInt64(); + case CborReaderState.ByteString: + if (type == typeof(Guid)) + return new Guid(reader.ReadByteString()); + return reader.ReadByteString(); + case CborReaderState.TextString: + return reader.ReadTextString(); + case CborReaderState.Null: + reader.ReadNull(); + return null; + case CborReaderState.DoublePrecisionFloat: + return reader.ReadDouble(); + case CborReaderState.SinglePrecisionFloat: + return reader.ReadSingle(); + case CborReaderState.HalfPrecisionFloat: + return type == typeof(float) ? reader.ReadSingle() : reader.ReadDouble(); + + case CborReaderState.StartArray: + if (type == null) + return ReadList(reader, null, context); + + if (type.IsSubclassOf(typeof(ISharedObjectShell))) + return ReadSharedObject(reader, type, context); + + if (typeof(IList).IsAssignableFrom(type) || type.IsArray) + return ReadList(reader, type, context); + + return ReadObject(reader, type, context); + + case CborReaderState.StartMap: + return ReadDictionary(reader, type, context); + } + + + if (type == typeof(DateTimeOffset)) + return reader.ReadDateTimeOffset(); + + return null; + } + + + private IList ReadList(CborReader reader, Type? type, IEndPointContext? context) + { + var length = reader.ReadStartArray(); + if (length != null) + { + var elementType = typeof(object); + if (type is { IsArray: true }) + { + elementType = type.GetElementType(); + Array values = Array.CreateInstance(elementType, length.Value); + + + for (int i = 0; i < length; i++) + { + values.SetValue(ReadData(reader, elementType, context), i); + } + + reader.ReadEndArray(); + return values; + } + + if (typeof(IList).IsAssignableFrom(type)) + elementType = type.GetGenericArguments()[0]; + else elementType = typeof(object); + Type genericListType = typeof(List<>).MakeGenericType(elementType); + var list = (IList)Activator.CreateInstance(genericListType, length); + + for (int i = 0; i < length; i++) + { + list.Add(ReadData(reader, elementType, context)); + } + + reader.ReadEndArray(); + + + return list; + + + return null; + } + + return null; + } + + private IDictionary ReadDictionary(CborReader reader, Type type, IEndPointContext? context) + { + var dictionaryInstance = (Activator.CreateInstance(type) as IDictionary)!; + var length = reader.ReadStartArray(); + if (length != null) + { + for (int i = 0; i < length; i++) + { + var key = ReadData(reader, type, context); + var value = ReadData(reader, type, context); + dictionaryInstance.Add(key, value); + } + } + + return dictionaryInstance; + } + + 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) + { + 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 = ComplexObjectIdentifier.FromFlat(identifier); + return so.UniversalValue; + } + + var instance = Activator.CreateInstance(type)!; + + FillObject(instance, type, reader, context); + + return instance; + } + + private ISharedObjectShell ReadSharedObject(CborReader reader, Type type, IEndPointContext? context) + { + var sharedObject = (Activator.CreateInstance(type) as ISharedObjectShell)!; + if (context != null) + { + sharedObject.EndPointContext = context; + } + + FillObject(sharedObject, type, reader, context); + + return sharedObject; + } + + private void FillObject(object obj, Type type, CborReader reader, IEndPointContext? context) + { + var propertyInfos = type.GetProperties(); + var properties = FilterProperties(propertyInfos); + + var length = reader.ReadStartArray(); + try + { +#if TRACE + Console.WriteLine($"Reading list of {length} objects, {properties.Count} properties found"); +#endif + + for (var index = 0; index < length; index++) + { + var property = properties[index]; + var value = ReadData(reader, property.PropertyType, context); + property.SetValue(obj, value); + } + + reader.ReadEndArray(); + } + catch (Exception e) + { + Console.WriteLine(e); + reader.ReadEndArray(); + + throw; + } + } + + public static List FilterProperties(PropertyInfo[] properties) + { + var finalProperties = new List(properties.Length); + foreach (var property in properties.Where(i => i.CanWrite && i.CanRead)) + { + if (property.GetCustomAttribute() == null) + finalProperties.Add(property); + } + + return finalProperties; + } + } +} \ No newline at end of file diff --git a/mROA.Cbor/IContextualSerializationToolKit.cs b/mROA.Cbor/IContextualSerializationToolKit.cs new file mode 100644 index 0000000..957ca29 --- /dev/null +++ b/mROA.Cbor/IContextualSerializationToolKit.cs @@ -0,0 +1,17 @@ +using System; +using mROA.Abstract; + +namespace mROA.Cbor +{ + public interface IContextualSerializationToolKit : ISerializationToolkit + { + byte[] Serialize(object objectToSerialize, IEndPointContext? context); + void Serialize(object objectToSerialize, Span destination, IEndPointContext? context); + T Deserialize(byte[] rawData, IEndPointContext? context); + object? Deserialize(byte[] rawData, Type type, IEndPointContext? context); + T Deserialize(ReadOnlyMemory rawMemory, IEndPointContext? context); + object? Deserialize(ReadOnlyMemory rawMemory, Type type, IEndPointContext? context); + T Cast(object nonCasted, IEndPointContext? context); + object? Cast(object nonCasted, Type type, IEndPointContext? context); + } +} \ No newline at end of file diff --git a/mROA.Cbor/PreParsedValue.cs b/mROA.Cbor/PreParsedValue.cs new file mode 100644 index 0000000..9737b7e --- /dev/null +++ b/mROA.Cbor/PreParsedValue.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using mROA.Abstract; +using mROA.Implementation; + +namespace mROA.Cbor +{ + public interface IPreParsedValue + { + object? ToObject(Type type, IEndPointContext? context); + } + + public class PreParsedValue : IPreParsedValue + { + public PreParsedValue(List properties) + { + _properties = properties; + } + + private List _properties { get; set; } + + 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 = ComplexObjectIdentifier.FromFlat((ulong)_properties[0]); + return so.UniversalValue; + } + + var instance = Activator.CreateInstance(type); + if (instance == null) + return null; + + if (instance is ISharedObjectShell sharedObject && context != null) + { + sharedObject.EndPointContext = context; + } + + var properties = CborSerializationToolkit.FilterProperties(type.GetProperties()); + for (var index = 0; index < properties.Count; index++) + { + var property = properties[index]; + property.SetValue(instance, + _properties[index] is IPreParsedValue ppv + ? ppv.ToObject(property.PropertyType, context) + : _properties[index]); + } + + return instance; + } + } + + public class ParsedValue : IPreParsedValue + { + private object? _value; + + public ParsedValue(object? value) + { + _value = value; + } + + + public object? ToObject(Type type, IEndPointContext? context) + { + return _value; + } + } +} \ No newline at end of file diff --git a/mROA.Cbor/mROA.Cbor.csproj b/mROA.Cbor/mROA.Cbor.csproj new file mode 100644 index 0000000..a2dbaf9 --- /dev/null +++ b/mROA.Cbor/mROA.Cbor.csproj @@ -0,0 +1,24 @@ + + + + netstandard2.1 + enable + + + + + + + + + + + + + + + + + + + diff --git a/mROA.Codegen/mROA.Codegen.csproj b/mROA.Codegen/mROA.Codegen.csproj index 0b8cdea..c9257fd 100644 --- a/mROA.Codegen/mROA.Codegen.csproj +++ b/mROA.Codegen/mROA.Codegen.csproj @@ -25,13 +25,17 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + + + + diff --git a/mROA.Codegen/mROASourceGenerator.cs b/mROA.Codegen/mROASourceGenerator.cs index da1f1bb..25609fb 100644 --- a/mROA.Codegen/mROASourceGenerator.cs +++ b/mROA.Codegen/mROASourceGenerator.cs @@ -1,3 +1,6 @@ +// #define DONT_ADD + +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -6,174 +9,146 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; - -namespace mROA.Codegen; - -/// -/// A sample source generator that creates a custom report based on class properties. The target class should be annotated with the 'Generators.ReportAttribute' attribute. -/// When using the source code as a baseline, an incremental source generator is preferable because it reduces the performance overhead. -/// -[Generator] -public class mROASourceGenerator : IIncrementalGenerator +namespace mROA.Codegen { - private const string Namespace = "mROA.Implementation"; - private const string AttributeName = "SharedObjectInterafceAttribute"; - - private const string AttributeSourceCode = $@"// - -namespace {Namespace} -{{ - [System.AttributeUsage(System.AttributeTargets.Class)] - public class {AttributeName} : System.Attribute - {{ - }} -}}"; - - public void Initialize(IncrementalGeneratorInitializationContext context) - { - // Filter classes annotated with the [Report] attribute. Only filtered Syntax Nodes can trigger code generation. - var provider = context.SyntaxProvider - .CreateSyntaxProvider( - (s, _) => s is InterfaceDeclarationSyntax, - (ctx, _) => GetClassDeclarationForSourceGen(ctx)) - .Where(t => t.reportAttributeFound) - .Select((t, _) => t.Item1); - - // Generate the source code. - context.RegisterSourceOutput(context.CompilationProvider.Combine(provider.Collect()), - ((ctx, t) => GenerateCode(ctx, t.Left, t.Right))); - } - /// - /// Checks whether the Node is annotated with the [Report] attribute and maps syntax context to the specific node type (ClassDeclarationSyntax). + /// A sample source generator that creates a custom report based on class properties. The target class should be annotated with the 'Generators.ReportAttribute' attribute. + /// When using the source code as a baseline, an incremental source generator is preferable because it reduces the performance overhead. /// - /// Syntax context, based on CreateSyntaxProvider predicate - /// The specific cast and whether the attribute was found. - private static (InterfaceDeclarationSyntax, bool reportAttributeFound) GetClassDeclarationForSourceGen( - GeneratorSyntaxContext context) + [Generator] + public class mROASourceGenerator : ISourceGenerator { - var classDeclarationSyntax = (InterfaceDeclarationSyntax)context.Node; + private static Predicate ParameterFilter = + i => i.Type.Name is "CancellationToken" or "RequestContext"; - // Go through all attributes of the class. - foreach (AttributeListSyntax attributeListSyntax in classDeclarationSyntax.AttributeLists) - foreach (AttributeSyntax attributeSyntax in attributeListSyntax.Attributes) + private static Predicate ParameterFilterForType = + i => i.Name is "CancellationToken" or "RequestContext"; + + public void Initialize(GeneratorInitializationContext context) { - if (context.SemanticModel.GetSymbolInfo(attributeSyntax).Symbol is not IMethodSymbol attributeSymbol) - continue; // if we can't get the symbol, ignore it - - string attributeName = attributeSymbol.ContainingType.ToDisplayString(); - - // Check the full name of the [Report] attribute. - if (attributeName == "mROA.Implementation.Attributes.SharedObjectInterfaceAttribute") - return (classDeclarationSyntax, true); } - return (classDeclarationSyntax, false); - } - - /// - /// Generate code action. - /// It will be executed on specific nodes (ClassDeclarationSyntax annotated with the [Report] attribute) changed by the user. - /// - /// Source generation context used to add source files. - /// Compilation used to provide access to the Semantic Model. - /// Nodes annotated with the [Report] attribute that trigger the generate action. - private void GenerateCode(SourceProductionContext context, Compilation compilation, - ImmutableArray classes) - { - var methods = new List<(string, IMethodSymbol)>(); - var frontendContextRepo = new List(); - // Go through all filtered class declarations. - var declarations = classes.ToList().OrderBy(i => i.Identifier.Text).ToList(); - foreach (var classDeclarationSyntax in declarations) + public void Execute(GeneratorExecutionContext context) { - // We need to get semantic model of the class to retrieve metadata. - var semanticModel = compilation.GetSemanticModel(classDeclarationSyntax.SyntaxTree); + var trees = context.Compilation.SyntaxTrees; - - // Symbols allow us to get the compile-time information. - if (semanticModel.GetDeclaredSymbol(classDeclarationSyntax) is not INamedTypeSymbol classSymbol) - continue; - - - var namespaceName = classSymbol.ContainingNamespace.ToDisplayString(); - - // 'Identifier' means the token of the node. Get class name from the syntax node. - var className = classDeclarationSyntax.Identifier.Text; - - // Go through all class members with a particular type (property) to generate method lines. - var methodBody = classSymbol.GetMembers() - .OfType().OrderBy(i => i.Name); - - var originalName = className; - // Build up the source code - className = className.TrimStart('I') + "RemoteEndpoint"; - - - var methodsText = new List(); - - foreach (var method in methodBody) + var interfaces = new List(); + foreach (var tree in trees) { - var index = methods.Count; - methods.Add((namespaceName + "." + originalName, method)); - var sb = new StringBuilder(); + var node = tree.GetRoot() as CompilationUnitSyntax; + foreach (var member in node.Members) + { + if (member is InterfaceDeclarationSyntax ids) + { + interfaces.Add(ids); + } + else if (member is NamespaceDeclarationSyntax nds) + { + foreach (var inside in nds.Members) - bool isAsync = method.ReturnType.Name == "Task"; - bool isVoid = method.ReturnType.Name == "Void" || method.ReturnType.ToString() == "Task"; - bool isParametrized = method.Parameters.Length == 1 && !isAsync || - method.Parameters.Length == 2 && isAsync; - - - //Creating signature - sb.AppendLine("public" + (isAsync - ? " async " - : " ") + - $"{method.ReturnType.ToDisplayString()} {method.Name}({string.Join(", ", method.Parameters.Select(ToFullString))}){{"); - - - var prefix = isAsync ? "await " : ""; - var postfix = !isAsync ? (isVoid? ".Wait()" : ".GetAwaiter().GetResult()") : ""; - var parameterLink = isParametrized ? ", " + method.Parameters.First().Name : string.Empty; - var caller = isVoid ? $"CallAsync({index}{parameterLink})" : - isAsync ? $"GetResultAsync<{ExtractTaskType(method.ReturnType)}>({index}{parameterLink})" : - $"GetResultAsync<{ToFullString(method.ReturnType)}>({index}{parameterLink})"; - - if (!isVoid) - prefix = "return " + prefix; - - // if (method.ReturnType.OriginalDefinition.ToString() == "System.Threading.Tasks.Task") - // { - // var type = method.ReturnType.ToString(); - // type = type.Substring(type.IndexOf('<') + 1); - // type = type.Substring(0, type.Length - 1); - // sb.AppendLine( - // $"\t\tvar response = await serialisationModule.GetFinalCommandExecution<{type}>(defaultCallRequestCodegen.CallRequestId);"); - // sb.AppendLine($"\t\treturn ({type})response.Result;"); - // } - // else if (!isAsync && method.ReturnType.ToDisplayString() != "void") - // { - // var type = method.ReturnType.ToDisplayString(); - // sb.AppendLine( - // $"\t\tvar response = serialisationModule.GetFinalCommandExecution<{type}>(defaultCallRequestCodegen.CallRequestId).GetAwaiter().GetResult();"); - // sb.AppendLine($"\t\treturn ({type})response.Result;"); - // } - // else - // { - // sb.AppendLine( - // "\t\tserialisationModule.GetNextCommandExecution(defaultCallRequestCodegen.CallRequestId).Wait();"); - // } - // - // sb.AppendLine("\t}"); - - sb.AppendLine("\t\t" + prefix + caller + postfix+ ";"); - - sb.AppendLine("\t}"); - - methodsText.Add(sb.ToString()); + if (inside is InterfaceDeclarationSyntax ids2) + if (ContainsSOIAttribute(ids2.AttributeLists, context, ids2)) + interfaces.Add(ids2); + } + } } - var code = $@"// + GenerateCode(context, context.Compilation, interfaces.ToImmutableArray()); + } + + private void GenerateCode(GeneratorExecutionContext context, Compilation compilation, + ImmutableArray classes) + { + // For future + // var asm = Assembly.GetAssembly(typeof(mROASourceGenerator)); + // var files = asm.GetManifestResourceNames(); + // var test = asm.GetManifestResourceStream("mROA.Codegen.test.tpt"); + // var reader = new StreamReader(test); + // var allText = reader.ReadToEnd(); + var frontendContextRepo = new List(); + var eventBinders = new List(); + List totalMethods = new List(); + + + List invokers = new List(); + var declarations = classes.ToList().OrderBy(i => i.Identifier.Text).ToList(); + foreach (var classDeclarationSyntax in declarations) + { + var semanticModel = compilation.GetSemanticModel(classDeclarationSyntax.SyntaxTree); + + if (semanticModel.GetDeclaredSymbol(classDeclarationSyntax) is not INamedTypeSymbol classSymbol) + continue; + List innerMethods = new List(); + + var namespaceName = classSymbol.ContainingNamespace.ToDisplayString(); + var className = classDeclarationSyntax.Identifier.Text; + innerMethods = CollectMembers(classSymbol); + var associated = innerMethods.Select(i => i.AssociatedSymbol).Where(i => i != null) + .Distinct(SymbolEqualityComparer.Default).Cast().ToList(); + + innerMethods = innerMethods.Where(i => + i.MethodKind != MethodKind.EventAdd && i.MethodKind != MethodKind.EventRemove) + .ToList(); + + totalMethods.AddRange(innerMethods); + + var originalName = className; + + className = className.TrimStart('I') + "RemoteEndpoint"; + + + var declaredMethods = new List(); + var propertiesAccessMethods = new List<(string, IMethodSymbol)>(); + + foreach (var method in innerMethods) + { + switch (method.MethodKind) + { + case MethodKind.PropertyGet or MethodKind.PropertySet: + GeneratePropertyMethod(method, propertiesAccessMethods, invokers, + classSymbol); + continue; + default: + GenerateDeclaredMethod(method, declaredMethods, invokers, classSymbol); + break; + } + } + + foreach (var symbol in associated) + { + switch (symbol) + { + case IPropertySymbol propertySymbol: + + var setter = propertiesAccessMethods.FirstOrDefault(i => + i.Item2.AssociatedSymbol!.Name == propertySymbol.Name && i.Item2.ReturnsVoid); + var getter = propertiesAccessMethods.FirstOrDefault(i => + i.Item2.AssociatedSymbol!.Name == propertySymbol.Name && !i.Item2.ReturnsVoid); + string impl; + if (propertySymbol.IsIndexer) + { + impl = + $"public {propertySymbol.Type.ToDisplayString()} this[{string.Join(", ", propertySymbol.Parameters.Select(p => p.ToDisplayString()))}] {{ {getter.Item1} {setter.Item1} }}"; + } + else + { + impl = + $"public {propertySymbol.Type.ToDisplayString()} {symbol.Name} {{ {getter.Item1} {setter.Item1} }}"; + } + + declaredMethods.Add(impl); + break; + case IEventSymbol eventSymbol: + declaredMethods.Add( + $"public event {eventSymbol.Type.ToDisplayString()}? {eventSymbol.Name};"); + break; + } + } + + GenerateEventImplementation(classSymbol, invokers, declaredMethods, context, eventBinders); + + var code = $@"// using mROA; using System; @@ -181,106 +156,527 @@ using mROA.Implementation; using System.Collections.Generic; using mROA.Abstract; -namespace {namespaceName}; - -partial class {className} : RemoteObjectBase, {originalName} +namespace {namespaceName} {{ - public {className}(int id, IRepresentationModule representationModule) : base(id, representationModule) + partial class {className} : RemoteObjectBase, {originalName} {{ - }} + public {className}(int id, IRepresentationModule representationModule) : base(id, representationModule) + {{ + }} - {string.Join("\r\n\t", methodsText)} + {string.Join("\r\n\t\t", declaredMethods)} + }} }} "; - // Add the source code to the compilation. - context.AddSource($"{className}.g.cs", SourceText.From(code, Encoding.UTF8)); + // Add the source code to the compilation. +#if !DONT_ADD + context.AddSource($"{className}.g.cs", SourceText.From(code, Encoding.UTF8)); +#endif + frontendContextRepo.Add( + $"{{ typeof({classSymbol.ToDisplayString()}), typeof({namespaceName}.{className}) }}"); + } - frontendContextRepo.Add( - $"{{ typeof({classSymbol.ToDisplayString()}), typeof({namespaceName}.{className}) }}"); - } + if (totalMethods.Count != 0) + { + var methodsStringed = invokers; - if (methods.Count != 0) - { - var methodsStringed = methods.Select(i => - $"typeof({i.Item1}).GetMethod(\"{i.Item2.Name}\", [{string.Join(", ", i.Item2.Parameters.Select(p => $"typeof({p.Type.ToDisplayString()})"))}])") - .ToList(); - - var coCodegenRepoCode = @$"// + var coCodegenRepoCode = @$"// using System.Collections.Generic; using System.Reflection; using mROA.Abstract; - -namespace mROA.Codegen; - -public class CoCodegenMethodRepository : IMethodRepository -{{ - private readonly List _methods = [ - {string.Join(", // test comment\r\n\t\t", methodsStringed)} - ]; - public MethodInfo GetMethod(int id) - {{ - if (_methods.Count <= id) - return null; - - return _methods[id]; - }} - - public int RegisterMethod(MethodInfo method) - {{ - _methods.Add(method); - return _methods.Count - 1; - }} - - public IEnumerable GetMethods() - {{ - return _methods; - }} - - public void Inject(T dependency) - {{ - }} -}} -"; - context.AddSource($"CoCodegenMethodRepository.g.cs", SourceText.From(coCodegenRepoCode, Encoding.UTF8)); - } - - if (frontendContextRepo.Count != 0) - { - var fronendRepoCode = @$"// -using System.Collections.Frozen; using mROA.Implementation; -using mROA.Abstract; +using System; +using System.Threading; -namespace mROA.Codegen; - -public sealed class RemoteTypeBinder +namespace mROA.Codegen {{ - static RemoteTypeBinder(){{ - RemoteContextRepository.RemoteTypes = new Dictionary {{ - {string.Join(", \r\n\t\t", frontendContextRepo)}}}.ToFrozenDictionary(); + public class CoCodegenMethodRepository : IMethodRepository + {{ + private readonly List _methods = new () {{ + {string.Join(",\r\n\t\t\t", methodsStringed)} + }}; + + public IMethodInvoker GetMethod(int id) + {{ + if (id == -1) + return mROA.Implementation.MethodInvoker.Dispose; + + if (_methods.Count <= id) + return null; + + return _methods[id]; + }} + + public void Inject(T dependency) + {{ + }} }} }} "; - context.AddSource("RemoteTypeBinder.g.cs", SourceText.From(fronendRepoCode, Encoding.UTF8)); +#if !DONT_ADD + context.AddSource("CoCodegenMethodRepository.g.cs", SourceText.From(coCodegenRepoCode, Encoding.UTF8)); +#endif + } + + if (frontendContextRepo.Count != 0) + { + var fronendRepoCode = @$"// +using System.Collections.Generic; +using System.Reflection; +using System; +using mROA.Abstract; +using mROA.Implementation.Backend; +using mROA.Implementation; + +namespace mROA.Codegen +{{ + public sealed class RemoteTypeBinder + {{ + static RemoteTypeBinder(){{ + RemoteContextRepository.RemoteTypes = new Dictionary {{ + {string.Join(", \r\n\t\t\t", frontendContextRepo)}}}; + ContextRepository.EventBinders = new object[] {{ + {string.Join(",\r\n\t\t\t", eventBinders)}}}; + }} + }} +}} +"; +#if !DONT_ADD + context.AddSource("RemoteTypeBinder.g.cs", SourceText.From(fronendRepoCode, Encoding.UTF8)); +#endif + } } - } - private static string ToFullString(IParameterSymbol parameter) - => /*parameter.Type.ContainingNamespace is null*/ - /*?*/ parameter.ToDisplayString(); - /*: $"{parameter.Type.ContainingNamespace.ToDisplayString()}.{parameter.Type.MetadataName} {parameter.Name}";*/ + private void GenerateEventImplementation(INamedTypeSymbol classSymbol, List invokers, + List declaredMethods, GeneratorExecutionContext context, List binders) + { + var events = classSymbol.AllInterfaces.Add(classSymbol).SelectMany(i => i.GetMembers()) + .OfType().ToList(); + if (events.Count == 0) + return; - private static string ToFullString(ITypeSymbol type) => - // => type.ContainingNamespace is null || type.Name == "Void" - type.ToDisplayString(); - // : $"{type.ContainingNamespace.ToDisplayString()}.{type.MetadataName}"; + var additionalSignatures = new List(events.Count); + var singleEventBinder = new List(events.Count); + for (int i = 0; i < events.Count; i++) + { + var currentEvent = events[i]; - private string ExtractTaskType(ITypeSymbol taskType) - { - var type = taskType.ToString(); - type = type.Substring(type.IndexOf('<') + 1); - return type.Substring(0, type.Length - 1); + var additionalMethod = GenerateMethodExternalCaller(currentEvent, out var signature); + declaredMethods.Add(additionalMethod); + additionalSignatures.Add(signature); + GenerateEventCode(currentEvent, invokers, classSymbol); + GenerateBinderCode(currentEvent, invokers, classSymbol, singleEventBinder); + } + + var partialInterface = $@" +namespace {classSymbol.ContainingNamespace.ToDisplayString()} +{{ + public partial interface {classSymbol.Name} + {{ +{string.Join("\r\n", additionalSignatures)} + }} +}} +"; + var binder = $@"new EventBinder<{classSymbol.ToDisplayString()}> + {{ + BindAction = (instance, context, representationProducer, index) => + {{ + var ownerId = context.OwnerId; + var module = representationProducer.Produce(ownerId); +{string.Join("\r\n", singleEventBinder)} + }} +}}"; + binders.Add(binder); +#if !DONT_ADD + context.AddSource($"{classSymbol.Name}.g.cs", SourceText.From(partialInterface, Encoding.UTF8)); +#endif + } + + private string GenerateMethodExternalCaller(IEventSymbol eventSymbol, out string interfaceSignature) + { + var level = "\t\t"; + var parameters = (eventSymbol.Type as INamedTypeSymbol).TypeArguments; + var parameterIndex = 0; + var parametersDeclaration = + string.Join(", ", parameters.Select(i => $"{i.ToDisplayString()} p{parameterIndex++}")); + var signature = $@"public void {EventExternalName(eventSymbol)}({parametersDeclaration})"; + interfaceSignature = level + signature + ";"; + var caller = $@"{signature} +{level}{{ +{level} {eventSymbol.Name}?.Invoke({string.Join(", ", Enumerable.Range(0, parameterIndex).Select(i => "p" + i))}); +{level}}} +"; + return caller; + } + + public static string EventExternalName(IEventSymbol eventSymbol) => $"{eventSymbol.Name}External"; + + private static string Caster(ITypeSymbol type, string inner) + { + if (!type.IsValueType) + return inner + + " as " + + type.ToDisplayString(); + return $"({type.ToDisplayString()})" + inner; + } + + private void GenerateDeclaredMethod(IMethodSymbol method, List declaredMethods, List invokers, + INamedTypeSymbol baseInterace) + { + var index = invokers.Count; + var sb = new StringBuilder(); + + bool isParametrized; + + bool isAsync; + bool isVoid; + + List? parameters; + + switch (method.ReturnType) + { + case INamedTypeSymbol namedType: + isAsync = namedType.Name == "Task"; + isVoid = isAsync && namedType.TypeParameters.Length == 0 || namedType.Name == "Void"; + parameters = method.Parameters.ToList(); + parameters.RemoveAll(ParameterFilter); + isParametrized = parameters.Count != 0; + break; + case IArrayTypeSymbol: + isAsync = false; + isVoid = false; + parameters = new List(); + isParametrized = method.Parameters.Length != 0; + break; + default: + return; + } + + //Creating signature + sb.AppendLine("public" + (isAsync + ? " async " + : " ") + + $"{method.ReturnType.ToDisplayString()} {method.Name}({string.Join(", ", method.Parameters.Select(ToFullString))}){{"); + + + var prefix = isAsync ? "await " : ""; + var postfix = !isAsync ? isVoid ? ".Wait()" : ".GetAwaiter().GetResult()" : ""; + var parameterLink = isParametrized + ? ", new object[] {" + string.Join(", ", parameters.Select(i => i.Name)) + "}" + : string.Empty; + var tokenInsert = isAsync && method.Parameters.FirstOrDefault(i => i.Type.Name == "CancellationToken") is + { } tokenSymbol + ? ", cancellationToken : " + tokenSymbol.Name + : String.Empty; + var caller = isVoid + ? $"CallAsync({index}{parameterLink}{tokenInsert})" + : isAsync + ? $"GetResultAsync<{ExtractTaskType(method.ReturnType)}>({index}{parameterLink}{tokenInsert})" + : $"GetResultAsync<{ToFullString(method.ReturnType)}>({index}{parameterLink}{tokenInsert})"; + + if (!isVoid) + prefix = "return " + prefix; + + sb.AppendLine("\t\t\t" + prefix + caller + postfix + ";"); + + sb.AppendLine("\t\t}"); + + declaredMethods.Add(sb.ToString()); + var parameterTypes = string.Join(", ", + $"{string.Join(", ", parameters.Select(p => $"typeof({p.Type.ToDisplayString()})"))}"); + var level = "\t\t\t"; + + var parametersInsertList = new List(); + + for (var i = 0; i < method.Parameters.Length; i++) + { + var parameter = method.Parameters[i]; + switch (parameter.Type.Name) + { + case "CancellationToken": + parametersInsertList.Add("(CancellationToken)special[1]"); + break; + case "RequestContext": + parametersInsertList.Add("special[0] as RequestContext"); + break; + default: + parametersInsertList.Add(Caster(parameter.Type, + $"parameters[{parameters.IndexOf(parameter)}]")); + break; + } + } + + var parametersInsert = string.Join(", ", parametersInsertList); + var backend = string.Empty; + + var funcInvoking = string.Empty; + + if (isAsync && !isVoid) + funcInvoking = + $"(i as {method.ContainingType.ToDisplayString()}).{method.Name}({parametersInsert}).ContinueWith(t => {{ post(t.Result); }})"; + else if (isAsync && isVoid) + funcInvoking = + $"(i as {method.ContainingType.ToDisplayString()}).{method.Name}({parametersInsert}).ContinueWith(t => {{ post(null); }})"; + else if (!isAsync && isVoid) + { + funcInvoking = $@"{{ +{level} (i as {method.ContainingType.ToDisplayString()}).{method.Name}({parametersInsert}); +{level} return null; +{level} }}"; + } + else + { + funcInvoking = $"(i as {method.ContainingType.ToDisplayString()}).{method.Name}({parametersInsert})"; + } + + if (isAsync) + backend = $@"new mROA.Implementation.AsyncMethodInvoker +{level}{{ +{level} IsVoid = {isVoid.ToString().ToLower()}, +{(isVoid ? String.Empty : (level + "\t" + "ReturnType = typeof(" + ExtractTaskType(method.ReturnType)) + "),")} +{level} ParameterTypes = new Type[] {{ {parameterTypes} }}, +{level} SuitableType = typeof({baseInterace.ToDisplayString()}), +{level} Invoking = (i, parameters, special, post) => {funcInvoking}, +{level}}}"; + else + backend = $@"new mROA.Implementation.MethodInvoker +{level}{{ +{level} IsVoid = {isVoid.ToString().ToLower()}, +{level} ReturnType = typeof({method.ReturnType.ToDisplayString()}), +{level} ParameterTypes = new Type[] {{ {parameterTypes} }}, +{level} SuitableType = typeof({baseInterace.ToDisplayString()}), +{level} Invoking = (i, parameters, special) => {funcInvoking} +{level}}}"; + + invokers.Add(backend); + } + + private void GenerateBinderCode(IEventSymbol eventSymbol, List invokers, INamedTypeSymbol baseType, + List binders) + { + var index = invokers.Count - 1; + var parameters = (eventSymbol.Type as INamedTypeSymbol).TypeArguments.ToList(); + int parameterIndex = 0; + var parametersDeclaration = string.Join(", ", + JoinWithComa(Enumerable.Range(0, parameters.Count).Select(i => "p" + i++))); + + var transferParameters = + JoinWithComa(parameters.Where(i => !ParameterFilterForType(i)) + .Select(i => "p" + parameters.IndexOf(i))); + + var callFilter = ""; + + var requestIndex = parameters.FindIndex(i => i.Name == "RequestContext"); + if (requestIndex != -1) + { + callFilter = $"\n\r\t\t\tif(ownerId == p{requestIndex}.OwnerId) return;"; + } + + var eventBinderCode = + $@" (instance as {baseType.ToDisplayString()}).{eventSymbol.Name} += ({parametersDeclaration}) => + {{ + Console.WriteLine($""Try to send to {{ownerId}} with hash code {{context.GetHashCode()}}""); + {callFilter} + Console.WriteLine(""Sending event...""); + var request = new DefaultCallRequest + {{ + CommandId = {index}, ObjectId = new ComplexObjectIdentifier(index, ownerId), Parameters = new object[] {{ {transferParameters} }} + }}; + module.PostCallMessageAsync(request.Id, MessageType.EventRequest, request); + }}; +"; + binders.Add(eventBinderCode); + } + + public static string JoinWithComa(IEnumerable parts) => string.Join(", ", parts); + + private void GenerateEventCode(IEventSymbol eventSymbol, List invokers, ITypeSymbol baseInterface) + { + var level = "\t\t\t"; + + var parameters = (eventSymbol.Type as INamedTypeSymbol).TypeArguments; + var parsingParameters = parameters.RemoveAll(ParameterFilterForType).ToList(); + var parameterTypes = string.Join(", ", + $"{string.Join(", ", parsingParameters.Select(p => $"typeof({p.ToDisplayString()})"))}"); + + var parametersInsertList = new List(); + + for (var i = 0; i < parameters.Length; i++) + { + var parameter = parameters[i]; + switch (parameter.Name) + { + case "CancellationToken": + parametersInsertList.Add("(CancellationToken)special[1]"); + break; + case "RequestContext": + parametersInsertList.Add("special[0] as RequestContext"); + break; + default: + parametersInsertList.Add(Caster(parameter, + $"parameters[{parameters.IndexOf(parameter)}]")); + break; + } + } + + var parametersInsert = string.Join(", ", parametersInsertList); + var backend = $@"new mROA.Implementation.MethodInvoker +{level}{{ +{level} IsVoid = true, +{level} ReturnType = typeof(void), +{level} ParameterTypes = new Type[] {{ {parameterTypes} }}, +{level} SuitableType = typeof({baseInterface.ToDisplayString()}), +{level} Invoking = (i, parameters, special) => {{ +{level} (i as {baseInterface.ToDisplayString()}).{EventExternalName(eventSymbol)}({parametersInsert}); +{level} return null; +{level} }} +{level}}}"; + invokers.Add(backend); + } + + private void GeneratePropertyMethod(IMethodSymbol method, + List<(string, IMethodSymbol)> propsCollection, List invokers, INamedTypeSymbol baseInterace) + { + var level = "\t\t\t"; + var index = invokers.Count; + string frontend; + string backend = string.Empty; + if (method.MethodKind == MethodKind.PropertyGet) + { + var parametersArray = ""; + if (method.Parameters.Length != 0) + { + parametersArray = $", new object[] {{{string.Join(", ", method.Parameters.Select(p => p.Name))}}}"; + + var parameterTypes = string.Join(", ", + $"{string.Join(", ", method.Parameters.Select(p => "typeof(" + p.Type.ToDisplayString() + ")"))}"); + var parameterInserts = string.Join(", ", + method.Parameters.Select( + p => Caster(p.Type, "parameters[" + method.Parameters.IndexOf(p) + "]"))); + backend = $@"new mROA.Implementation.MethodInvoker +{level}{{ +{level} IsVoid = false, +{level} ReturnType = typeof({method.ReturnType.ToDisplayString()}), +{level} ParameterTypes = new Type[] {{ {parameterTypes} }}, +{level} SuitableType = typeof({baseInterace.ToDisplayString()}), +{level} Invoking = (i, parameters, _) => (i as {method.ContainingType.ToDisplayString()})[{parameterInserts}], +{level}}}"; + } + else + { + backend = $@"new mROA.Implementation.MethodInvoker +{level}{{ +{level} IsVoid = false, +{level} ReturnType = typeof({method.ReturnType.ToDisplayString()}), +{level} SuitableType = typeof({baseInterace.ToDisplayString()}), +{level} Invoking = (i, _, _) => (i as {method.ContainingType.ToDisplayString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name}, +{level}}}"; + } + + frontend = + $"get => GetResultAsync<{method.ReturnType.ToDisplayString()}>({index}{parametersArray}).GetAwaiter().GetResult();"; + } + else + { + var parametersArray = "value"; + if (method.Parameters.Length != 1) + { + parametersArray = string.Join(", ", method.Parameters.Select(p => p.Name)); + + var parameterTypes = string.Join(", ", + $"{string.Join(", ", method.Parameters.Select(p => $"typeof({p.Type.ToDisplayString()})"))}"); + var parameterInserts = string.Join(", ", + method.Parameters.Take(method.Parameters.Length - 1).Select(p => + { + return Caster(p.Type, "parameters[" + method.Parameters.IndexOf(p) + "]"); + // if (!p.Type.IsValueType) + // return "parameters[" + method.Parameters.IndexOf(p) + "] as " + + // p.Type.ToDisplayString(); + // return $"({p.Type.ToDisplayString()})parameters[{method.Parameters.IndexOf(p)}]"; + } + )); + + // var valueInsert = !method.Parameters.Last().Type.IsValueType + // ? "parameters[" + (method.Parameters.Length - 1) + "] as " + + // method.Parameters.Last().Type.ToDisplayString() + // : $"({method.Parameters.Last().Type.ToDisplayString()})parameters[{method.Parameters.Length - 1}]"; + var valueInsert = Caster(method.Parameters.Last().Type, + "parameters[" + (method.Parameters.Length - 1) + "]"); + backend = $@"new mROA.Implementation.MethodInvoker +{level}{{ +{level} IsVoid = true, +{level} ReturnType = typeof({method.ReturnType.ToDisplayString()}), +{level} ParameterTypes = new Type[] {{ {parameterTypes} }}, +{level} SuitableType = typeof({baseInterace.ToDisplayString()}), +{level} Invoking = (i, parameters, _) => (i as {method.ContainingType.ToDisplayString()})[{parameterInserts}] = {valueInsert}, +{level}}}"; + } + else + { + backend = $@"new mROA.Implementation.MethodInvoker +{level}{{ +{level} IsVoid = true, +{level} ReturnType = typeof({method.ReturnType.ToDisplayString()}), +{level} ParameterTypes = new Type[] {{ typeof({method.Parameters.First().Type.ToDisplayString()}) }}, +{level} SuitableType = typeof({baseInterace.ToDisplayString()}), +{level} Invoking = (i, parameters, _) => (i as {method.ContainingType.ToDisplayString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name} = {Caster((method.AssociatedSymbol as IPropertySymbol)!.Type, "parameters[0]")}, +{level}}}"; + } + + frontend = $"set => CallAsync({index}, new object[] {{ {parametersArray} }}).Wait();"; + } + + propsCollection.Add((frontend, method)); + invokers.Add(backend); + } + + private static string ToFullString(IParameterSymbol parameter) + => /*parameter.Type.ContainingNamespace is null*/ + /*?*/ parameter.ToDisplayString(); + /*: $"{parameter.Type.ContainingNamespace.ToDisplayString()}.{parameter.Type.MetadataName} {parameter.Name}";*/ + + private static string ToFullString(ITypeSymbol type) => + // => type.ContainingNamespace is null || type.Name == "Void" + type.ToDisplayString(); + // : $"{type.ContainingNamespace.ToDisplayString()}.{type.MetadataName}"; + + private string ExtractTaskType(ITypeSymbol taskType) + { + return (taskType as INamedTypeSymbol).TypeArguments[0].ToDisplayString(); + } + + private bool ContainsSOIAttribute(SyntaxList attributes, GeneratorExecutionContext context, + InterfaceDeclarationSyntax interfaceDeclarationSyntax) + { + foreach (var attributeSyntax in + attributes.SelectMany(attributeListSyntax => attributeListSyntax.Attributes)) + { + if (context.Compilation.GetSemanticModel(interfaceDeclarationSyntax.SyntaxTree) + .GetSymbolInfo(attributeSyntax).Symbol is not IMethodSymbol attributeSymbol) + continue; // if we can't get the symbol, ignore it + + string attributeName = attributeSymbol.ContainingType.ToDisplayString(); + + // Check the full name of the [Report] attribute. + if (attributeName == "mROA.Implementation.Attributes.SharedObjectInterfaceAttribute") + return true; + } + + return false; + } + + private List CollectMembers(INamedTypeSymbol type) + { + var methods = type.GetMembers().OfType().ToList(); + foreach (var inner in type.AllInterfaces) + { + methods.AddRange(inner.GetMembers().OfType()); + } + + methods.RemoveAll(m => m.Name == "Dispose"); + return methods.OrderBy(i => i.Name).ToList(); + } } } \ No newline at end of file diff --git a/mROA.Codegen/test.tpt b/mROA.Codegen/test.tpt new file mode 100644 index 0000000..c53ad17 --- /dev/null +++ b/mROA.Codegen/test.tpt @@ -0,0 +1 @@ +test text \ No newline at end of file diff --git a/mROA.CodegenTools/mROA.CodegenTools.csproj b/mROA.CodegenTools/mROA.CodegenTools.csproj new file mode 100644 index 0000000..d2a210c --- /dev/null +++ b/mROA.CodegenTools/mROA.CodegenTools.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/mROA.Test/CborTest.cs b/mROA.Test/CborTest.cs new file mode 100644 index 0000000..cfa7d20 --- /dev/null +++ b/mROA.Test/CborTest.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using mROA.Cbor; + +namespace mROA.Test; + +public class CborTest +{ + private ComplexTestObject _complexTestObject; + private IContextualSerializationToolKit _serializationToolKit; + private BasicCollectionElement _basicCollectionElement; + + [SetUp] + public void Setup() + { + _basicCollectionElement = new() { A = 567565, B = "test text", C = 2.781f }; + + _complexTestObject = new ComplexTestObject + { + IntValue = 123, + DoubleValue = 3.14159, + StringValue = "abc", + EnumValue = TestEnum.X, + CollectionElements = + [ + _basicCollectionElement, + new BasicCollectionElement { A = 8_000_000, B = "Fi number", C = 1.618f } + ], + IntArray = [1, 4, 8, 16, 87] + }; + _serializationToolKit = new CborSerializationToolkit(); + } + + [Test] + public void BasicOnly() + { + var value = 123; + var data = _serializationToolKit.Serialize(value, null); + + var deserialize = _serializationToolKit.Deserialize(data, null); + Assert.That(value, Is.EqualTo(deserialize)); + } + + [Test] + public void ComplexFlat() + { + var value = _basicCollectionElement; + var data = _serializationToolKit.Serialize(value, null); + var deserialize = _serializationToolKit.Deserialize(data, null); + Assert.That(value, Is.EqualTo(deserialize)); + } + + [Test] + public void ComplexFull() + { + var value = _complexTestObject; + var data = _serializationToolKit.Serialize(value, null); + var deserialize = _serializationToolKit.Deserialize(data, null); + Assert.That(value, Is.EqualTo(deserialize)); + } + + public void SharedObject() + { + } + + + private class ComplexTestObject + { + public int IntValue { get; set; } + public double DoubleValue { get; set; } + public string StringValue { get; set; } + public TestEnum EnumValue { get; set; } + public int[] IntArray { get; set; } + public List CollectionElements { get; set; } + + protected bool Equals(ComplexTestObject other) + { + return IntValue == other.IntValue && DoubleValue.Equals(other.DoubleValue) && StringValue == other.StringValue && IntArray.SequenceEqual(other.IntArray) && CollectionElements.SequenceEqual(other.CollectionElements); + } + + public override bool Equals(object? obj) + { + if (obj is null) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((ComplexTestObject)obj); + } + + public override int GetHashCode() + { + return HashCode.Combine(IntValue, DoubleValue, StringValue, IntArray, CollectionElements); + } + } + + private class BasicCollectionElement + { + public int A { get; set; } + public string B { get; set; } + public float C { get; set; } + + protected bool Equals(BasicCollectionElement other) + { + return A == other.A && B == other.B && C.Equals(other.C); + } + + public override bool Equals(object? obj) + { + if (obj is null) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((BasicCollectionElement)obj); + } + + public override int GetHashCode() + { + return HashCode.Combine(A, B, C); + } + } + + public enum TestEnum + { + X = -5, Y, Z + } +} \ No newline at end of file diff --git a/mROA.Test/NextGenTest.cs b/mROA.Test/NextGenTest.cs index 99025f3..8eaebb0 100644 --- a/mROA.Test/NextGenTest.cs +++ b/mROA.Test/NextGenTest.cs @@ -1,71 +1,77 @@ -using System.Net; +using System; +using System.Linq; +using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading.Tasks; using mROA.Implementation; -namespace mROA.Test; - -public class NextGenTest +namespace mROA.Test { - private TcpListener _listener; - private NextGenerationInteractionModule _interactionModuleA; - private NextGenerationInteractionModule _interactionModuleB; - private Guid[] guids = [Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid()]; - - [SetUp] - public void Setup() + public class NextGenTest { - _listener = new TcpListener(IPAddress.Loopback, 4567); - _interactionModuleA = new NextGenerationInteractionModule(); - _interactionModuleA.Inject(new JsonSerializationToolkit()); - _interactionModuleB = new NextGenerationInteractionModule(); - _interactionModuleB.Inject(new JsonSerializationToolkit()); + private TcpListener _listener; + private NextGenerationInteractionModule _interactionModuleA; + private NextGenerationInteractionModule _interactionModuleB; + private Guid[] guids = new[] { Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid() }; - } - - [Test] - public void MultithreadedTest() - { - - Task.Run(() => + [SetUp] + public void Setup() { - _listener.Start(); - _interactionModuleB.BaseStream = _listener.AcceptTcpClient().GetStream(); - foreach (var guid in guids) - { - _interactionModuleB.PostMessage(new NetworkMessage { Id = guid, Data = "Hello user"u8.ToArray() }); - } - }); - - var client = new TcpClient(); - client.Connect(IPAddress.Loopback, 4567); - _interactionModuleA.BaseStream = client.GetStream(); + _listener = new TcpListener(IPAddress.Loopback, 4567); + _interactionModuleA = new NextGenerationInteractionModule(); + _interactionModuleA.Inject(new JsonSerializationToolkit()); + _interactionModuleB = new NextGenerationInteractionModule(); + _interactionModuleB.Inject(new JsonSerializationToolkit()); - var tasks = guids.Select(ReadStream); - - Task.WaitAll(tasks.ToArray()); - Assert.Pass(); - - } - - private async Task ReadStream(Guid current) - { - var msg = await _interactionModuleA.GetNextMessageReceiving(); - Console.WriteLine( - $"{Environment.CurrentManagedThreadId} Received message: {Encoding.Default.GetString(new JsonSerializationToolkit().Serialize(msg))}"); - while (msg.Id != current) - { - msg = await _interactionModuleA.GetNextMessageReceiving(); - Console.WriteLine( - $"{Environment.CurrentManagedThreadId} Received message: {Encoding.Default.GetString(new JsonSerializationToolkit().Serialize(msg))}"); } - Console.WriteLine($"{Environment.CurrentManagedThreadId} Good message received"); - } + [Test] + public void MultithreadedTest() + { + + Task.Run(() => + { + _listener.Start(); + _interactionModuleB.BaseStream = _listener.AcceptTcpClient().GetStream(); - [TearDown] - public void TearDown() - { - _listener.Dispose(); + foreach (var guid in guids) + { + _interactionModuleB.PostMessage(new NetworkMessage { Id = guid, Data = "Hello user"u8.ToArray() }); + } + }); + + var client = new TcpClient(); + client.Connect(IPAddress.Loopback, 4567); + _interactionModuleA.BaseStream = client.GetStream(); + + var tasks = guids.Select(ReadStream); + + Task.WaitAll(tasks.ToArray()); + Assert.Pass(); + + } + + private async Task ReadStream(Guid current) + { + var msg = await _interactionModuleA.GetNextMessageReceiving(); + Console.WriteLine( + $"{Environment.CurrentManagedThreadId} Received message: {Encoding.Default.GetString(new JsonSerializationToolkit().Serialize(msg))}"); + while (msg.Id != current) + { + msg = await _interactionModuleA.GetNextMessageReceiving(); + Console.WriteLine( + $"{Environment.CurrentManagedThreadId} Received message: {Encoding.Default.GetString(new JsonSerializationToolkit().Serialize(msg))}"); + } + + Console.WriteLine($"{Environment.CurrentManagedThreadId} Good message received"); + } + + [TearDown] + public void TearDown() + { + _listener.Stop(); + _listener.Dispose(); + } } } \ No newline at end of file diff --git a/mROA.Test/UnSOization.cs b/mROA.Test/UnSOization.cs new file mode 100644 index 0000000..d30c025 --- /dev/null +++ b/mROA.Test/UnSOization.cs @@ -0,0 +1,26 @@ +using mROA.Implementation; + +namespace mROA.Test; + +public class UnSOization +{ + private ComplexObjectIdentifier _uoi; + + [SetUp] + public void Setup() + { + _uoi = new ComplexObjectIdentifier + { + ContextId = -123, OwnerId = 123 + }; + } + + [Test] + public void FlatTest() + { + var flat = _uoi.Flat; + var next = new ComplexObjectIdentifier { Flat = flat }; + + Assert.That(_uoi, Is.EqualTo(next)); + } +} \ No newline at end of file diff --git a/mROA.Test/mROA.Test.csproj b/mROA.Test/mROA.Test.csproj index 4262b31..bff76ec 100644 --- a/mROA.Test/mROA.Test.csproj +++ b/mROA.Test/mROA.Test.csproj @@ -3,7 +3,7 @@ net9.0 latest - enable + enable false @@ -27,6 +27,7 @@ + diff --git a/mROA.sln b/mROA.sln index 7586eb3..6ca35dc 100644 --- a/mROA.sln +++ b/mROA.sln @@ -19,24 +19,38 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.Frontend", "Example EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mROA.Benchmark", "mROA.Benchmark\mROA.Benchmark.csproj", "{6868F42B-E30D-4040-AD4A-BC2A2E76D03A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mROA.Cbor", "mROA.Cbor\mROA.Cbor.csproj", "{6211E4EA-13FD-4EB1-8E6A-C0173DD0784A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TotalDemo", "TotalDemo", "{FC4FA752-10A7-4D78-A7C2-9BCD8A81FB5E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Events", "Events", "{032E1288-4D26-4FA5-ABB6-E7D738F319DE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.Events.Shared", "Example.Events.Shared\Example.Events.Shared.csproj", "{6342C7FC-12B4-4BC6-BA25-159B1400D952}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.Events.Backend", "Example.Events.Backend\Example.Events.Backend.csproj", "{B63F58B2-8D86-42F3-96A5-470CB449BFD3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.Events.Client", "Example.Events.Client\Example.Events.Client.csproj", "{98451C0E-E179-4F5F-9F1A-8B353EDE2EBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mROA.CodegenTools", "mROA.CodegenTools\mROA.CodegenTools.csproj", "{2A2821B7-E5C5-443A-9801-9622493594A0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {AE4E93F6-7B91-41FC-9BEA-2C8C2CBE1CB6}.Debug|Any CPU.ActiveCfg = Release|Any CPU - {AE4E93F6-7B91-41FC-9BEA-2C8C2CBE1CB6}.Debug|Any CPU.Build.0 = Release|Any CPU {AE4E93F6-7B91-41FC-9BEA-2C8C2CBE1CB6}.Release|Any CPU.ActiveCfg = Release|Any CPU {AE4E93F6-7B91-41FC-9BEA-2C8C2CBE1CB6}.Release|Any CPU.Build.0 = Release|Any CPU + {AE4E93F6-7B91-41FC-9BEA-2C8C2CBE1CB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE4E93F6-7B91-41FC-9BEA-2C8C2CBE1CB6}.Debug|Any CPU.Build.0 = Debug|Any CPU {D0E5760B-BB6E-453A-B396-A972CD94F133}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D0E5760B-BB6E-453A-B396-A972CD94F133}.Debug|Any CPU.Build.0 = Debug|Any CPU {D0E5760B-BB6E-453A-B396-A972CD94F133}.Release|Any CPU.ActiveCfg = Release|Any CPU {D0E5760B-BB6E-453A-B396-A972CD94F133}.Release|Any CPU.Build.0 = Release|Any CPU - {6CE0ED21-88FD-44B3-B9C6-9B8FA4E07E89}.Debug|Any CPU.ActiveCfg = Release|Any CPU - {6CE0ED21-88FD-44B3-B9C6-9B8FA4E07E89}.Debug|Any CPU.Build.0 = Release|Any CPU {6CE0ED21-88FD-44B3-B9C6-9B8FA4E07E89}.Release|Any CPU.ActiveCfg = Release|Any CPU {6CE0ED21-88FD-44B3-B9C6-9B8FA4E07E89}.Release|Any CPU.Build.0 = Release|Any CPU + {6CE0ED21-88FD-44B3-B9C6-9B8FA4E07E89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6CE0ED21-88FD-44B3-B9C6-9B8FA4E07E89}.Debug|Any CPU.Build.0 = Debug|Any CPU {A9BB364E-0BA6-40B9-A293-757BC48EFC06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A9BB364E-0BA6-40B9-A293-757BC48EFC06}.Debug|Any CPU.Build.0 = Debug|Any CPU {A9BB364E-0BA6-40B9-A293-757BC48EFC06}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -53,13 +67,38 @@ Global {6868F42B-E30D-4040-AD4A-BC2A2E76D03A}.Debug|Any CPU.Build.0 = Debug|Any CPU {6868F42B-E30D-4040-AD4A-BC2A2E76D03A}.Release|Any CPU.ActiveCfg = Release|Any CPU {6868F42B-E30D-4040-AD4A-BC2A2E76D03A}.Release|Any CPU.Build.0 = Release|Any CPU + {6211E4EA-13FD-4EB1-8E6A-C0173DD0784A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6211E4EA-13FD-4EB1-8E6A-C0173DD0784A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6211E4EA-13FD-4EB1-8E6A-C0173DD0784A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6211E4EA-13FD-4EB1-8E6A-C0173DD0784A}.Release|Any CPU.Build.0 = Release|Any CPU + {6342C7FC-12B4-4BC6-BA25-159B1400D952}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6342C7FC-12B4-4BC6-BA25-159B1400D952}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6342C7FC-12B4-4BC6-BA25-159B1400D952}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6342C7FC-12B4-4BC6-BA25-159B1400D952}.Release|Any CPU.Build.0 = Release|Any CPU + {B63F58B2-8D86-42F3-96A5-470CB449BFD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B63F58B2-8D86-42F3-96A5-470CB449BFD3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B63F58B2-8D86-42F3-96A5-470CB449BFD3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B63F58B2-8D86-42F3-96A5-470CB449BFD3}.Release|Any CPU.Build.0 = Release|Any CPU + {98451C0E-E179-4F5F-9F1A-8B353EDE2EBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {98451C0E-E179-4F5F-9F1A-8B353EDE2EBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {98451C0E-E179-4F5F-9F1A-8B353EDE2EBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {98451C0E-E179-4F5F-9F1A-8B353EDE2EBC}.Release|Any CPU.Build.0 = Release|Any CPU + {2A2821B7-E5C5-443A-9801-9622493594A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2A2821B7-E5C5-443A-9801-9622493594A0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2A2821B7-E5C5-443A-9801-9622493594A0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2A2821B7-E5C5-443A-9801-9622493594A0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {A9BB364E-0BA6-40B9-A293-757BC48EFC06} = {EAE92F5A-664C-41AB-8811-5885524B5347} - {E7703F5B-F2A6-4A88-AA57-EBB1E38D533E} = {EAE92F5A-664C-41AB-8811-5885524B5347} - {9BD25A13-3165-47C0-9EAA-5C59EC490E32} = {EAE92F5A-664C-41AB-8811-5885524B5347} + {FC4FA752-10A7-4D78-A7C2-9BCD8A81FB5E} = {EAE92F5A-664C-41AB-8811-5885524B5347} + {A9BB364E-0BA6-40B9-A293-757BC48EFC06} = {FC4FA752-10A7-4D78-A7C2-9BCD8A81FB5E} + {9BD25A13-3165-47C0-9EAA-5C59EC490E32} = {FC4FA752-10A7-4D78-A7C2-9BCD8A81FB5E} + {E7703F5B-F2A6-4A88-AA57-EBB1E38D533E} = {FC4FA752-10A7-4D78-A7C2-9BCD8A81FB5E} + {032E1288-4D26-4FA5-ABB6-E7D738F319DE} = {EAE92F5A-664C-41AB-8811-5885524B5347} + {6342C7FC-12B4-4BC6-BA25-159B1400D952} = {032E1288-4D26-4FA5-ABB6-E7D738F319DE} + {B63F58B2-8D86-42F3-96A5-470CB449BFD3} = {032E1288-4D26-4FA5-ABB6-E7D738F319DE} + {98451C0E-E179-4F5F-9F1A-8B353EDE2EBC} = {032E1288-4D26-4FA5-ABB6-E7D738F319DE} EndGlobalSection EndGlobal diff --git a/mROA/Abstract/ICancellationRepository.cs b/mROA/Abstract/ICancellationRepository.cs new file mode 100644 index 0000000..489a648 --- /dev/null +++ b/mROA/Abstract/ICancellationRepository.cs @@ -0,0 +1,12 @@ +using System; +using System.Threading; + +namespace mROA.Abstract +{ + public interface ICancellationRepository : IInjectableModule + { + void RegisterCancellation(Guid id, CancellationTokenSource cts); + CancellationTokenSource? GetCancellation(Guid id); + void FreeCancelation(Guid id); + } +} \ No newline at end of file diff --git a/mROA/Abstract/ICommandExecution.cs b/mROA/Abstract/ICommandExecution.cs index 4566f1d..5037a4e 100644 --- a/mROA/Abstract/ICommandExecution.cs +++ b/mROA/Abstract/ICommandExecution.cs @@ -1,8 +1,9 @@ -namespace mROA.Abstract; +using System; -public interface ICommandExecution +namespace mROA.Abstract { - Guid Id { get; init; } - int ClientId { get; set; } - int CommandId { get; } + public interface ICommandExecution + { + Guid Id { get; set; } + } } \ No newline at end of file diff --git a/mROA/Abstract/IConnectionHub.cs b/mROA/Abstract/IConnectionHub.cs index 1a595c8..34106f3 100644 --- a/mROA/Abstract/IConnectionHub.cs +++ b/mROA/Abstract/IConnectionHub.cs @@ -1,12 +1,14 @@ -namespace mROA.Abstract; - -public delegate void ConnectionHandler(IRepresentationModule representationModule); -public delegate void DisconnectionHandler(IRepresentationModule representationModule); - -public interface IConnectionHub : IInjectableModule +namespace mROA.Abstract { - void RegisterInteraction(INextGenerationInteractionModule interaction); - INextGenerationInteractionModule GetInteracion(int id); - event ConnectionHandler? OnConnected; - event DisconnectionHandler? OnDisconnected; + public delegate void ConnectionHandler(IRepresentationModule representationModule); + + public delegate void DisconnectionHandler(IRepresentationModule representationModule); + + public interface IConnectionHub : IInjectableModule + { + void RegisterInteraction(INextGenerationInteractionModule interaction); + INextGenerationInteractionModule GetInteracion(int id); + event ConnectionHandler? OnConnected; + event DisconnectionHandler? OnDisconnected; + } } \ No newline at end of file diff --git a/mROA/Abstract/IContextRepository.cs b/mROA/Abstract/IContextRepository.cs index 6829405..920a317 100644 --- a/mROA/Abstract/IContextRepository.cs +++ b/mROA/Abstract/IContextRepository.cs @@ -1,12 +1,15 @@ -namespace mROA.Abstract; +using System; +using mROA.Implementation; -public interface IContextRepository : IInjectableModule +namespace mROA.Abstract { - int ResisterObject(object o); - void ClearObject(int id); - object GetObject(int id); - T? GetObject(int id); - T GetSingleObject(); - object GetSingleObject(Type type); - int GetObjectIndex(object o); + public interface IContextRepository : IInjectableModule + { + int HostId { get; set; } + int ResisterObject(object o, IEndPointContext context); + void ClearObject(ComplexObjectIdentifier id); + T GetObject(ComplexObjectIdentifier id); + object GetSingleObject(Type type, int ownerId); + int GetObjectIndex(object o, IEndPointContext context); + } } \ No newline at end of file diff --git a/mROA/Abstract/IContextRepositoryHub.cs b/mROA/Abstract/IContextRepositoryHub.cs index 8a34bf2..f950b8f 100644 --- a/mROA/Abstract/IContextRepositoryHub.cs +++ b/mROA/Abstract/IContextRepositoryHub.cs @@ -1,6 +1,7 @@ -namespace mROA.Abstract; - -public interface IContextRepositoryHub +namespace mROA.Abstract { - IContextRepository GetRepository(int clientId); + public interface IContextRepositoryHub + { + IContextRepository GetRepository(int clientId); + } } \ No newline at end of file diff --git a/mROA/Abstract/IEndPointContext.cs b/mROA/Abstract/IEndPointContext.cs new file mode 100644 index 0000000..0267321 --- /dev/null +++ b/mROA/Abstract/IEndPointContext.cs @@ -0,0 +1,10 @@ +namespace mROA.Abstract +{ + public interface IEndPointContext + { + IContextRepository RealRepository { get; } + IContextRepository RemoteRepository { get; } + int HostId { get; } + int OwnerId { get; } + } +} \ No newline at end of file diff --git a/mROA/Abstract/IEventBinder.cs b/mROA/Abstract/IEventBinder.cs new file mode 100644 index 0000000..737bfd2 --- /dev/null +++ b/mROA/Abstract/IEventBinder.cs @@ -0,0 +1,8 @@ +namespace mROA.Abstract +{ + public interface IEventBinder + { + public void BindEvents(T source, IEndPointContext context, + IRepresentationModuleProducer representationModuleProducer, int index); + } +} \ No newline at end of file diff --git a/mROA/Abstract/IExecuteModule.cs b/mROA/Abstract/IExecuteModule.cs index 7a9b0a1..3ae2d13 100644 --- a/mROA/Abstract/IExecuteModule.cs +++ b/mROA/Abstract/IExecuteModule.cs @@ -1,8 +1,10 @@ using mROA.Implementation; -namespace mROA.Abstract; - -public interface IExecuteModule : IInjectableModule +namespace mROA.Abstract { - ICommandExecution Execute(ICallRequest command, IContextRepository contextRepository); + public interface IExecuteModule : IInjectableModule + { + ICommandExecution Execute(ICallRequest command, IContextRepository contextRepository, + IRepresentationModule representationModule); + } } \ No newline at end of file diff --git a/mROA/Abstract/IFrontendBridge.cs b/mROA/Abstract/IFrontendBridge.cs index 103fec2..909f486 100644 --- a/mROA/Abstract/IFrontendBridge.cs +++ b/mROA/Abstract/IFrontendBridge.cs @@ -1,3 +1,6 @@ -namespace mROA.Abstract; - -public interface IFrontendBridge : IInjectableModule; \ No newline at end of file +namespace mROA.Abstract +{ + public interface IFrontendBridge : IInjectableModule + { + } +} \ No newline at end of file diff --git a/mROA/Abstract/IGatewayModule.cs b/mROA/Abstract/IGatewayModule.cs index d51063a..77c869f 100644 --- a/mROA/Abstract/IGatewayModule.cs +++ b/mROA/Abstract/IGatewayModule.cs @@ -1,6 +1,9 @@ -namespace mROA.Abstract; +using System; -public interface IGatewayModule : IDisposable, IInjectableModule -{ - void Run(); +namespace mROA.Abstract +{ + public interface IGatewayModule : IDisposable, IInjectableModule + { + void Run(); + } } \ No newline at end of file diff --git a/mROA/Abstract/IIdentityGenerator.cs b/mROA/Abstract/IIdentityGenerator.cs index bede9f3..6123cdd 100644 --- a/mROA/Abstract/IIdentityGenerator.cs +++ b/mROA/Abstract/IIdentityGenerator.cs @@ -1,6 +1,7 @@ -namespace mROA.Abstract; - -public interface IIdentityGenerator : IInjectableModule +namespace mROA.Abstract { - int GetNextIdentity(); + public interface IIdentityGenerator : IInjectableModule + { + int GetNextIdentity(); + } } \ No newline at end of file diff --git a/mROA/Abstract/IInjectableModule.cs b/mROA/Abstract/IInjectableModule.cs index d7da7cb..c516a6f 100644 --- a/mROA/Abstract/IInjectableModule.cs +++ b/mROA/Abstract/IInjectableModule.cs @@ -1,6 +1,7 @@ -namespace mROA.Abstract; - -public interface IInjectableModule +namespace mROA.Abstract { - void Inject(T dependency); + public interface IInjectableModule + { + void Inject(T dependency); + } } \ No newline at end of file diff --git a/mROA/Abstract/IInteractionModule.cs b/mROA/Abstract/IInteractionModule.cs index 9068ba1..dc45dbf 100644 --- a/mROA/Abstract/IInteractionModule.cs +++ b/mROA/Abstract/IInteractionModule.cs @@ -1,17 +1,18 @@ +using System; +using System.IO; +using System.Threading.Tasks; using mROA.Implementation; -namespace mROA.Abstract; - -public interface INextGenerationInteractionModule : IInjectableModule +namespace mROA.Abstract { - int ConnectionId { get; } - public Stream BaseStream { get; set; } - NetworkMessage[] UnhandledMessages { get; } - NetworkMessage LastMessage { get; } - EventWaitHandle CurrentReceivingHandle { get; } - void StartInfiniteReceiving(); - Task GetNextMessageReceiving(); - Task PostMessage(NetworkMessage message); - void HandleMessage(NetworkMessage message); - NetworkMessage? FirstByFilter(Predicate predicate); + public interface INextGenerationInteractionModule : IInjectableModule + { + int ConnectionId { get; } + public Stream? BaseStream { get; set; } + Task GetNextMessageReceiving(); + Task PostMessage(NetworkMessage message); + void HandleMessage(NetworkMessage message); + NetworkMessage[] UnhandledMessages { get; } + NetworkMessage? FirstByFilter(Predicate predicate); + } } \ No newline at end of file diff --git a/mROA/Abstract/IMethodInvoker.cs b/mROA/Abstract/IMethodInvoker.cs new file mode 100644 index 0000000..dcb04db --- /dev/null +++ b/mROA/Abstract/IMethodInvoker.cs @@ -0,0 +1,12 @@ +using System; + +namespace mROA.Abstract +{ + public interface IMethodInvoker + { + bool IsVoid { get; } + Type[] ParameterTypes { get; } + Type? ReturnType { get; } + Type SuitableType { get; } + } +} \ No newline at end of file diff --git a/mROA/Abstract/IMethodRepository.cs b/mROA/Abstract/IMethodRepository.cs index d18405c..4c468f1 100644 --- a/mROA/Abstract/IMethodRepository.cs +++ b/mROA/Abstract/IMethodRepository.cs @@ -1,11 +1,7 @@ -using System.Reflection; - -namespace mROA.Abstract; - -public interface IMethodRepository : IInjectableModule +namespace mROA.Abstract { - MethodInfo GetMethod(int id); - int RegisterMethod(MethodInfo method); - - IEnumerable GetMethods(); + public interface IMethodRepository : IInjectableModule + { + IMethodInvoker GetMethod(int id); + } } \ No newline at end of file diff --git a/mROA/Abstract/IOwnershipRepository.cs b/mROA/Abstract/IOwnershipRepository.cs index deccfe9..55ec3f3 100644 --- a/mROA/Abstract/IOwnershipRepository.cs +++ b/mROA/Abstract/IOwnershipRepository.cs @@ -1,7 +1,8 @@ -namespace mROA.Abstract; - -public interface IOwnershipRepository +namespace mROA.Abstract { - int GetOwnershipId(); - int GetHostOwnershipId(); + public interface IOwnershipRepository + { + int GetOwnershipId(); + int GetHostOwnershipId(); + } } \ No newline at end of file diff --git a/mROA/Abstract/IRemoteObjectFactory.cs b/mROA/Abstract/IRemoteObjectFactory.cs new file mode 100644 index 0000000..e68fb0f --- /dev/null +++ b/mROA/Abstract/IRemoteObjectFactory.cs @@ -0,0 +1,9 @@ +using mROA.Implementation; + +namespace mROA.Abstract +{ + public interface IRemoteObjectFactory : IInjectableModule + { + T Produce(ComplexObjectIdentifier id); + } +} \ No newline at end of file diff --git a/mROA/Abstract/IRepresentationModuleProducer.cs b/mROA/Abstract/IRepresentationModuleProducer.cs index 9162a5c..6406c78 100644 --- a/mROA/Abstract/IRepresentationModuleProducer.cs +++ b/mROA/Abstract/IRepresentationModuleProducer.cs @@ -1,6 +1,7 @@ -namespace mROA.Abstract; - -public interface IRepresentationModuleProducer : IInjectableModule +namespace mROA.Abstract { - IRepresentationModule Produce(int id); + public interface IRepresentationModuleProducer : IInjectableModule + { + IRepresentationModule Produce(int id); + } } \ No newline at end of file diff --git a/mROA/Abstract/IRequestExtractor.cs b/mROA/Abstract/IRequestExtractor.cs index eaf59de..d49194b 100644 --- a/mROA/Abstract/IRequestExtractor.cs +++ b/mROA/Abstract/IRequestExtractor.cs @@ -1,6 +1,9 @@ -namespace mROA.Abstract; +using System.Threading.Tasks; -public interface IRequestExtractor : IInjectableModule +namespace mROA.Abstract { - Task StartExtraction(); + public interface IRequestExtractor : IInjectableModule + { + Task StartExtraction(); + } } \ No newline at end of file diff --git a/mROA/Abstract/ISerialisationModule.cs b/mROA/Abstract/ISerialisationModule.cs index f83f40b..17e7911 100644 --- a/mROA/Abstract/ISerialisationModule.cs +++ b/mROA/Abstract/ISerialisationModule.cs @@ -1,33 +1,25 @@ -using System.Windows.Input; +using System; +using System.Threading; +using System.Threading.Tasks; using mROA.Implementation; -using mROA.Implementation.CommandExecution; -namespace mROA.Abstract; - -public interface ISerialisationModule : IInjectableModule +namespace mROA.Abstract { - void HandleIncomingRequest(int clientId, byte[] message); - void PostResponse(NetworkMessage message, int clientId); - void SendWelcomeMessage(int clientId); - public interface IFrontendSerialisationModule : IInjectableModule + public interface IRepresentationModule : IInjectableModule { - int ClientId { get; } - Task GetNextCommandExecution(Guid requestId) where T : ICommandExecution; - Task> GetFinalCommandExecution(Guid requestId); - void PostCallRequest(ICallRequest callRequest); - } -} + int Id { get; } -public interface IRepresentationModule : IInjectableModule -{ - int Id { get; } - Task GetMessageAsync(Guid? requestId = null, EMessageType? messageType = null, CancellationToken token = default); - T GetMessage(Guid? requestId = null, EMessageType? messageType = null); - T GetMessage(Predicate filter); - Task GetRawMessage(Predicate filter, CancellationToken token = default); - - Task PostCallMessageAsync(Guid id, EMessageType eMessageType, T payload) where T : notnull; - Task PostCallMessageAsync(Guid id, EMessageType eMessageType, object payload, Type payloadType); - void PostCallMessage(Guid id, EMessageType eMessageType, T payload) where T : notnull; - void PostCallMessage(Guid id, EMessageType eMessageType, object payload, Type payloadType); + Task GetMessageAsync(Guid? requestId = null, MessageType? messageType = null, + CancellationToken token = default); + + T GetMessage(Guid? requestId = null, MessageType? messageType = null); + + Task GetRawMessage(Guid? requestId = null, MessageType? messageType = null, + CancellationToken token = default); + + Task PostCallMessageAsync(Guid id, MessageType messageType, T payload) where T : notnull; + Task PostCallMessageAsync(Guid id, MessageType messageType, object payload, Type payloadType); + void PostCallMessage(Guid id, MessageType messageType, T payload) where T : notnull; + void PostCallMessage(Guid id, MessageType messageType, object payload, Type payloadType); + } } \ No newline at end of file diff --git a/mROA/Abstract/ISerializationToolkit.cs b/mROA/Abstract/ISerializationToolkit.cs index 769180a..497c953 100644 --- a/mROA/Abstract/ISerializationToolkit.cs +++ b/mROA/Abstract/ISerializationToolkit.cs @@ -1,14 +1,16 @@ -namespace mROA.Abstract; +using System; -public interface ISerializationToolkit : IInjectableModule +namespace mROA.Abstract { - byte[] Serialize(T objectToSerialize); - byte[] Serialize(object objectToSerialize, Type type); - T? Deserialize(byte[] rawData); - object? Deserialize(byte[] rawData, Type type); - T? Deserialize(Span rawData); - object? Deserialize(Span rawData, Type type); - T Cast(object nonCasted); - object Cast(object nonCasted, Type type); - + public interface ISerializationToolkit : IInjectableModule + { + byte[] Serialize(T objectToSerialize); + byte[] Serialize(object objectToSerialize, Type type); + T? Deserialize(byte[] rawData); + object? Deserialize(byte[] rawData, Type type); + T? Deserialize(Span rawData); + object? Deserialize(Span rawData, Type type); + T? Cast(object? nonCasted); + object? Cast(object? nonCasted, Type type); + } } \ No newline at end of file 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/Abstract/IStorage.cs b/mROA/Abstract/IStorage.cs new file mode 100644 index 0000000..88432a8 --- /dev/null +++ b/mROA/Abstract/IStorage.cs @@ -0,0 +1,10 @@ +namespace mROA.Abstract +{ + public interface IStorage where T : class + { + T? GetValue(int index); + int GetIndex(T value); + int Place(T value); + void Free(int index); + } +} \ No newline at end of file diff --git a/mROA/Implementation/Attributes/SerializationIgnoreAttribute.cs b/mROA/Implementation/Attributes/SerializationIgnoreAttribute.cs new file mode 100644 index 0000000..cd32ca7 --- /dev/null +++ b/mROA/Implementation/Attributes/SerializationIgnoreAttribute.cs @@ -0,0 +1,8 @@ +using System; + +namespace mROA.Implementation.Attributes +{ + public class SerializationIgnoreAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/mROA/Implementation/Attributes/SharedObjectInterfaceAttribute.cs b/mROA/Implementation/Attributes/SharedObjectInterfaceAttribute.cs index 78bfa15..adcc66e 100644 --- a/mROA/Implementation/Attributes/SharedObjectInterfaceAttribute.cs +++ b/mROA/Implementation/Attributes/SharedObjectInterfaceAttribute.cs @@ -1,3 +1,8 @@ -namespace mROA.Implementation.Attributes; +using System; -public class SharedObjectInterfaceAttribute : Attribute; \ No newline at end of file +namespace mROA.Implementation.Attributes +{ + public class SharedObjectInterfaceAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/mROA/Implementation/Attributes/SharedObjectSingletonAttribute.cs b/mROA/Implementation/Attributes/SharedObjectSingletonAttribute.cs index f0439f5..ec2f228 100644 --- a/mROA/Implementation/Attributes/SharedObjectSingletonAttribute.cs +++ b/mROA/Implementation/Attributes/SharedObjectSingletonAttribute.cs @@ -1,3 +1,8 @@ -namespace mROA.Implementation.Attributes; +using System; -public class SharedObjectSingletonAttribute : Attribute; \ No newline at end of file +namespace mROA.Implementation.Attributes +{ + public class SharedObjectSingletonAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/mROA/Implementation/Backend/BackendIdentityGenerator.cs b/mROA/Implementation/Backend/BackendIdentityGenerator.cs index 82dd4e2..dce88be 100644 --- a/mROA/Implementation/Backend/BackendIdentityGenerator.cs +++ b/mROA/Implementation/Backend/BackendIdentityGenerator.cs @@ -1,17 +1,18 @@ using mROA.Abstract; -namespace mROA.Implementation.Backend; - -public class BackendIdentityGenerator : IIdentityGenerator +namespace mROA.Implementation.Backend { - private int _currentId; - - public int GetNextIdentity() - { - return ++_currentId; - } - - public void Inject(T dependency) + public class BackendIdentityGenerator : IIdentityGenerator { + private int _currentId; + + public int GetNextIdentity() + { + return ++_currentId; + } + + public void Inject(T dependency) + { + } } } \ No newline at end of file diff --git a/mROA/Implementation/Backend/BasicConfigurationExtensions.cs b/mROA/Implementation/Backend/BasicConfigurationExtensions.cs index 1fbdbf3..b8e0f7c 100644 --- a/mROA/Implementation/Backend/BasicConfigurationExtensions.cs +++ b/mROA/Implementation/Backend/BasicConfigurationExtensions.cs @@ -1,37 +1,35 @@ +using System; using System.Net; using System.Reflection; using mROA.Abstract; using mROA.Implementation.Bootstrap; -namespace mROA.Implementation.Backend; - -public static class BasicConfigurationExtensions +namespace mROA.Implementation.Backend { - public static void UseJsonSerialisation(this FullMixBuilder builder) + public static class BasicConfigurationExtensions { - builder.Modules.Add(new JsonSerializationToolkit()); - } + public static void UseNetworkGateway(this FullMixBuilder builder, IPEndPoint endPoint, + Type interactionModuleType, params IInjectableModule[] injectableModules) + { + builder.Modules.Add(new NetworkGatewayModule(endPoint, interactionModuleType, injectableModules)); + } - public static void UseNetworkGateway(this FullMixBuilder builder, IPEndPoint endPoint, Type interactionModuleType, params IInjectableModule[] injectableModules) - { - builder.Modules.Add(new NetworkGatewayModule(endPoint, interactionModuleType, injectableModules)); - } - - public static void UseBasicExecution(this FullMixBuilder builder) - { - builder.Modules.Add(new BasicExecutionModule()); - } + public static void UseBasicExecution(this FullMixBuilder builder) + { + builder.Modules.Add(new BasicExecutionModule()); + } - public static void UseCollectableContextRepository(this FullMixBuilder builder, params Assembly[] assemblies) - { - var repo = new ContextRepository(); - repo.FillSingletons(assemblies); - TransmissionConfig.RealContextRepository = repo; - builder.Modules.Add(repo); - } + public static void UseCollectableContextRepository(this FullMixBuilder builder, params Assembly[] assemblies) + { + var repo = new ContextRepository(); + repo.FillSingletons(assemblies); + TransmissionConfig.RealContextRepository = repo; + builder.Modules.Add(repo); + } - public static void SetupMethodsRepository(this FullMixBuilder builder, IMethodRepository methodRepository) - { - builder.Modules.Add(methodRepository); + public static void SetupMethodsRepository(this FullMixBuilder builder, IMethodRepository methodRepository) + { + builder.Modules.Add(methodRepository); + } } } \ No newline at end of file diff --git a/mROA/Implementation/Backend/BasicExecutionModule.cs b/mROA/Implementation/Backend/BasicExecutionModule.cs index ac64004..25b96c1 100644 --- a/mROA/Implementation/Backend/BasicExecutionModule.cs +++ b/mROA/Implementation/Backend/BasicExecutionModule.cs @@ -1,122 +1,263 @@ -using System.Reflection; +using System; +using System.Threading; using mROA.Abstract; using mROA.Implementation.CommandExecution; -namespace mROA.Implementation.Backend; - -public class BasicExecutionModule : IExecuteModule +namespace mROA.Implementation.Backend { - private IMethodRepository? _methodRepo; - - public void Inject(T dependency) + public class BasicExecutionModule : IExecuteModule { - if (dependency is IMethodRepository methodRepo) _methodRepo = methodRepo; - } + private ICancellationRepository? _cancellationRepo; + private IMethodRepository? _methodRepo; + private ISerializationToolkit? _serialization; - public ICommandExecution Execute(ICallRequest command, IContextRepository contextRepository) - { - if (_methodRepo is null) - throw new NullReferenceException("Method repository was not defined"); - - if (contextRepository is null) - throw new NullReferenceException("Context repository was not defined"); - - var currentCommand = _methodRepo.GetMethod(command.CommandId); - if (currentCommand == null) - throw new Exception($"Command {command.CommandId} not found"); - - var context = command.ObjectId != -1 - ? contextRepository.GetObject(command.ObjectId) - : contextRepository.GetSingleObject(currentCommand.DeclaringType!); - - var parameter = command.Parameter; - - if (currentCommand.ReturnType.BaseType == typeof(Task) && - currentCommand.ReturnType.GenericTypeArguments.Length == 1) - return TypedExecuteAsync(currentCommand, context, parameter, command); - - if (currentCommand.ReturnType == typeof(Task)) - return ExecuteAsync(currentCommand, context, parameter, command); - - return Execute(currentCommand, context, parameter, command); - } - - private static ICommandExecution Execute(MethodInfo currentCommand, object context, object? parameter, - ICallRequest command) - { - try + public void Inject(T dependency) { - var finalResult = currentCommand.Invoke(context, parameter is null ? [] : [parameter]); - return new TypedFinalCommandExecution + switch (dependency) { - CommandId = command.CommandId, Result = finalResult, - Id = command.Id, - Type = currentCommand.ReturnType + case IMethodRepository methodRepo: + _methodRepo = methodRepo; + break; + case ICancellationRepository cancellationRepo: + _cancellationRepo = cancellationRepo; + break; + case ISerializationToolkit serializationToolkit: + _serialization = serializationToolkit; + break; + } + } + + public ICommandExecution Execute(ICallRequest command, IContextRepository contextRepository, + IRepresentationModule representationModule) + { +#if TRACE + Console.WriteLine(command.GetType().Name); +#endif + + try + { + ThrowIfNotInjected(contextRepository); + if (command is CancelRequest) + { +#if TRACE + Console.WriteLine("Final cancelling request"); +#endif + return CancelExecution(command); + } + + var invoker = _methodRepo!.GetMethod(command.CommandId); + if (invoker == null) + throw new Exception($"Command {command.CommandId} not found"); + + var context = GetContext(command, contextRepository, invoker); + + if (context == null) + throw new NullReferenceException("Instance can't be null"); + + + object?[]? castedParams = null; + + if (invoker.ParameterTypes.Length != 0) + castedParams = CastedParams(command, invoker); + + + var execContext = new RequestContext(command.Id, representationModule.Id); + + switch (invoker) + { + case AsyncMethodInvoker { IsVoid: false } asyncNonVoidMethodInvoker: + return TypedExecuteAsync(asyncNonVoidMethodInvoker, context, castedParams, command, + _cancellationRepo!, + representationModule, execContext); + case AsyncMethodInvoker asyncMethodInvoker: + return ExecuteAsync(asyncMethodInvoker, context, castedParams, command, _cancellationRepo!, + representationModule, execContext); + default: + var result = Execute((invoker as MethodInvoker)!, context, castedParams!, command, execContext); + if (command.CommandId == -1) + { +#if TRACE + Console.WriteLine("Disposing object"); +#endif + contextRepository.ClearObject(command.ObjectId); + } + + return result; + } + } + catch (Exception e) + { + return new ExceptionCommandExecution + { + Id = command.Id, + Exception = e.ToString() + }; + } + } + + private static object GetContext(ICallRequest command, IContextRepository contextRepository, IMethodInvoker invoker) + { + var context = command.ObjectId.ContextId != -1 + ? contextRepository.GetObject(command.ObjectId) + : contextRepository.GetSingleObject(invoker.SuitableType, command.ObjectId.OwnerId); + return context; + } + + private object?[] CastedParams(ICallRequest command, IMethodInvoker invoker) + { + object?[] castedParams = new object[invoker.ParameterTypes.Length]; + for (var i = 0; i < castedParams.Length; i++) + { + castedParams[i] = _serialization!.Cast(command.Parameters![i], invoker.ParameterTypes[i]); + } + + return castedParams; + } + + private void ThrowIfNotInjected(IContextRepository contextRepository) + { + if (_cancellationRepo is null) + throw new NullReferenceException("Method repository was not defined"); + + if (_methodRepo is null) + throw new NullReferenceException("Method repository was not defined"); + + if (contextRepository is null) + throw new NullReferenceException("Context repository was not defined"); + } + + private FinalCommandExecution CancelExecution(ICallRequest command) + { + var cts = _cancellationRepo!.GetCancellation(command.Id); + if (cts == null) + throw new NullReferenceException("Can't find cancellation for this request"); + cts.Cancel(); + _cancellationRepo.FreeCancelation(command.Id); + + return new FinalCommandExecution + { + Id = command.Id }; } - catch (Exception e) + + private static ICommandExecution Execute(MethodInvoker invoker, object instance, object?[] parameter, + ICallRequest command, RequestContext executionContext) { - return new ExceptionCommandExecution + try { - Id = command.Id, CommandId = command.CommandId, - Exception = e.ToString() - }; - } - } + var finalResult = invoker.Invoke(instance, parameter, new object[] { executionContext }); - private static ICommandExecution ExecuteAsync(MethodInfo currentCommand, object context, object? parameter, - ICallRequest command) - { - var tokenSource = new CancellationTokenSource(); - var token = tokenSource.Token; - try - { - var result = (Task)currentCommand.Invoke(context, parameter is null ? [token] : [parameter, token])!; + if (invoker.IsVoid) + { + return new FinalCommandExecution + { + Id = command.Id + }; + } - - result.Wait(token); - - - return new FinalCommandExecution { CommandId = command.CommandId, Id = command.Id }; - } - catch (Exception e) - { - return new ExceptionCommandExecution + return new FinalCommandExecution + { + Result = finalResult, + Id = command.Id + }; + } + catch (Exception e) { - Id = command.Id, CommandId = command.CommandId, - Exception = e.ToString() - }; + return new ExceptionCommandExecution + { + Id = command.Id, + Exception = e.ToString() + }; + } } - } - private static ICommandExecution TypedExecuteAsync(MethodInfo currentCommand, object context, object? parameter, - ICallRequest command) - { - var tokenSource = new CancellationTokenSource(); - var token = tokenSource.Token; - try + private ICommandExecution ExecuteAsync(AsyncMethodInvoker invoker, object instance, object?[]? parameters, + ICallRequest command, ICancellationRepository cancellationRepository, + IRepresentationModule representationModule, RequestContext executionContext) { - var result = - (Task)currentCommand.Invoke(context, parameter is null ? [token] : [parameter, token])!; - - result.Wait(token); - - var finalResult = result.GetType().GetProperty("Result")?.GetValue(result); - return new TypedFinalCommandExecution + var tokenSource = new CancellationTokenSource(); + cancellationRepository.RegisterCancellation(command.Id, tokenSource); + var token = tokenSource.Token; +#if TRACE + token.Register(() => Console.WriteLine($"Cancellation requested check {command.Id}")); +#endif + try { - Id = command.Id, - Result = finalResult, - CommandId = command.CommandId, - Type = finalResult?.GetType() - }; + invoker.Invoke(instance, parameters, new object[] { executionContext, token }, _ => + { + if (token.IsCancellationRequested) + return; + + var payload = new FinalCommandExecution + { + Id = command.Id + }; + _cancellationRepo?.FreeCancelation(command.Id); + + var multiClientOwnershipRepository = + TransmissionConfig.OwnershipRepository as MultiClientOwnershipRepository; + + multiClientOwnershipRepository?.RegisterOwnership(representationModule.Id); + representationModule.PostCallMessage(command.Id, MessageType.FinishedCommandExecution, payload); + multiClientOwnershipRepository?.FreeOwnership(); + }); + + return new AsyncCommandExecution + { + Id = command.Id + }; + } + catch (Exception e) + { + return new ExceptionCommandExecution + { + Id = command.Id, + Exception = e.ToString() + }; + } } - catch (Exception e) + + private ICommandExecution TypedExecuteAsync(AsyncMethodInvoker invoker, object instance, object?[]? parameters, + ICallRequest command, ICancellationRepository cancellationRepository, + IRepresentationModule representationModule, RequestContext executionContext) { - return new ExceptionCommandExecution + var tokenSource = new CancellationTokenSource(); + cancellationRepository.RegisterCancellation(command.Id, tokenSource); + + var token = tokenSource.Token; + try { - Id = command.Id, CommandId = command.CommandId, - Exception = e.ToString() - }; + invoker.Invoke(instance, parameters, new object[] { executionContext, token }, + finalResult => + { + var payload = new FinalCommandExecution + { + Id = command.Id, + Result = finalResult + }; + _cancellationRepo!.FreeCancelation(command.Id); + + var multiClientOwnershipRepository = + TransmissionConfig.OwnershipRepository as MultiClientOwnershipRepository; + multiClientOwnershipRepository?.RegisterOwnership(representationModule.Id); + representationModule.PostCallMessage(command.Id, MessageType.FinishedCommandExecution, + payload); + multiClientOwnershipRepository?.FreeOwnership(); + }); + + return new AsyncCommandExecution + { + Id = command.Id + }; + } + catch (Exception e) + { + return new ExceptionCommandExecution + { + Id = command.Id, + Exception = e.ToString() + }; + } } } } \ No newline at end of file diff --git a/mROA/Implementation/Backend/ConnectionHub.cs b/mROA/Implementation/Backend/ConnectionHub.cs index e2c0c18..f26a304 100644 --- a/mROA/Implementation/Backend/ConnectionHub.cs +++ b/mROA/Implementation/Backend/ConnectionHub.cs @@ -1,35 +1,38 @@ -using mROA.Abstract; +using System; +using System.Collections.Generic; +using mROA.Abstract; -namespace mROA.Implementation.Backend; - -public class ConnectionHub : IConnectionHub +namespace mROA.Implementation.Backend { - private readonly Dictionary _connections = new(); - private ISerializationToolkit? _serializationToolkit; - - public void RegisterInteraction(INextGenerationInteractionModule interaction) + public class ConnectionHub : IConnectionHub { - if (_serializationToolkit is null) - throw new NullReferenceException("Serialization toolkit is null"); - - _connections.Add(interaction.ConnectionId, interaction); - var module = new RepresentationModule(); - module.Inject(_serializationToolkit); - module.Inject(interaction); - OnConnected?.Invoke(module); - } + private readonly Dictionary _connections = new(); + private ISerializationToolkit? _serializationToolkit; - public INextGenerationInteractionModule GetInteracion(int id) - { - return _connections!.GetValueOrDefault(id, null) ?? throw new Exception("No connection found"); - } + public void RegisterInteraction(INextGenerationInteractionModule interaction) + { + if (_serializationToolkit is null) + throw new NullReferenceException("Serialization toolkit is null"); - public event ConnectionHandler? OnConnected; - public event DisconnectionHandler? OnDisconnected; - public void Inject(T dependency) - { - if (dependency is ISerializationToolkit serializationToolkit) - _serializationToolkit = serializationToolkit; - + _connections.Add(interaction.ConnectionId, interaction); + var module = new RepresentationModule(); + module.Inject(_serializationToolkit); + module.Inject(interaction); + OnConnected?.Invoke(module); + } + + public INextGenerationInteractionModule GetInteracion(int id) + { + return _connections!.GetValueOrDefault(id, null) ?? throw new Exception("No connection found"); + } + + public event ConnectionHandler? OnConnected; + public event DisconnectionHandler? OnDisconnected; + + public void Inject(T dependency) + { + if (dependency is ISerializationToolkit serializationToolkit) + _serializationToolkit = serializationToolkit; + } } } \ No newline at end of file diff --git a/mROA/Implementation/Backend/ContextRepository.cs b/mROA/Implementation/Backend/ContextRepository.cs index 6a3fe1e..71ee389 100644 --- a/mROA/Implementation/Backend/ContextRepository.cs +++ b/mROA/Implementation/Backend/ContextRepository.cs @@ -1,97 +1,95 @@ -using System.Collections.Frozen; +using System; +using System.Collections.Generic; +using System.Linq; using System.Reflection; +using System.Threading.Tasks; using mROA.Abstract; using mROA.Implementation.Attributes; -namespace mROA.Implementation.Backend; - -public class ContextRepository : IContextRepository +namespace mROA.Implementation.Backend { - private FrozenDictionary? _singletons; - private object?[] _storage = new object[StartupSize]; - - private Task _lastIndexFinder = Task.FromResult(0); - - private const int StartupSize = 1024; - private const int GrowSize = 128; - - - public void FillSingletons(params Assembly[] assembly) + public class ContextRepository : IContextRepository { - var types = assembly.SelectMany(x => x.GetTypes()).Where(type => - type is { IsClass: true, IsAbstract: false, IsGenericType: false } && - type.GetCustomAttributes(typeof(SharedObjectSingletonAttribute), true).Length > 0); - _singletons = - types.ToFrozenDictionary( - t => t.GetInterfaces().FirstOrDefault(i => - i.GetCustomAttributes(typeof(SharedObjectInterfaceAttribute), true).Length > 0)!.GetHashCode(), - Activator.CreateInstance); - } + private const int StartupSize = 1024; + private const int GrowSize = 128; + public static object[] EventBinders = { }; - public int ResisterObject(object o) - { - if (!_lastIndexFinder.IsCompleted) - _lastIndexFinder.Wait(); + private static int LastDebugId = -1; + private int _debugId = -1; - _storage[_lastIndexFinder.Result] = o; + private Task _lastIndexFinder = Task.FromResult(0); - var last = _lastIndexFinder.Result; - _lastIndexFinder = Task.Run(FindLastIndex); + private IRepresentationModuleProducer? _representationModuleProducer; - return last; - } + // [CanBeNull] + private Dictionary _singletons; + private IStorage _storage; - public void ClearObject(int id) - { - _storage[id] = null; - _lastIndexFinder = Task.FromResult(id); - } - public object GetObject(int id) - { - return (id == -1 || _storage.Length <= id ? null : _storage[id]) ?? throw new NullReferenceException(); - } - - public T GetObject(int id) - { - return id == -1 || _storage.Length <= id - ? throw new NullReferenceException("Cannot find that object. It is null") - : (T)_storage[id]!; - } - - public T GetSingleObject() - { - var result = GetSingleObject(typeof(T)); - return (T)result; - } - - public object GetSingleObject(Type type) - { - return _singletons!.GetValueOrDefault(type.GetHashCode()) ?? - throw new ArgumentException("Unregistered singleton type"); - } - - public int GetObjectIndex(object o) - { - var index = Array.IndexOf(_storage, o); - return index == -1 ? ResisterObject(o) : index; - } - - private int FindLastIndex() - { - for (var i = 0; i < _storage.Length; i++) + public ContextRepository() { - if (_storage[i] is null) - return i; + _storage = new ExtensibleStorage(); } - var nextStorage = new object[_storage.Length + GrowSize]; - Array.Copy(_storage, nextStorage, _storage.Length); - _storage = nextStorage; - return _storage.Length; - } + public int HostId { get; set; } - public void Inject(T dependency) - { + public int ResisterObject(object o, IEndPointContext context) + { + var last = _storage.Place(o); + + EventBinders.OfType>().FirstOrDefault() + ?.BindEvents((T)o, context, _representationModuleProducer!, last); + + return last; + } + + public void ClearObject(ComplexObjectIdentifier id) + { + _storage.Free(id.ContextId); + } + + public T GetObject(ComplexObjectIdentifier id) + { + var value = _storage.GetValue(id.ContextId); + + if (value == null) + { + throw new NullReferenceException("Cannot find that object. It is null"); + } + + return (T)value; + } + + public object GetSingleObject(Type type, int ownerId) + { + return _singletons.GetValueOrDefault(type.GetHashCode()) ?? + throw new ArgumentException("Unregistered singleton type"); + } + + public int GetObjectIndex(object o, IEndPointContext context) + { + var index = _storage.GetIndex(o); + return index == -1 ? ResisterObject(o, context) : index; + } + + public void Inject(T dependency) + { + if (dependency is IRepresentationModuleProducer moduleProducer) + { + _representationModuleProducer = moduleProducer; + } + } + + public void FillSingletons(params Assembly[] assembly) + { + var types = assembly.SelectMany(x => x.GetTypes()).Where(type => + type is { IsClass: true, IsAbstract: false, IsGenericType: false } && + type.GetCustomAttributes(typeof(SharedObjectSingletonAttribute), true).Length > 0); + _singletons = + types.ToDictionary( + t => t.GetInterfaces().FirstOrDefault(i => + i.GetCustomAttributes(typeof(SharedObjectInterfaceAttribute), true).Length > 0)!.GetHashCode(), + Activator.CreateInstance); + } } } \ No newline at end of file diff --git a/mROA/Implementation/Backend/HubRequestExtractor.cs b/mROA/Implementation/Backend/HubRequestExtractor.cs index ae29e29..6529c6c 100644 --- a/mROA/Implementation/Backend/HubRequestExtractor.cs +++ b/mROA/Implementation/Backend/HubRequestExtractor.cs @@ -1,50 +1,70 @@ +using System; using mROA.Abstract; -namespace mROA.Implementation.Backend; - -public class HubRequestExtractor(Type extractoType) : IInjectableModule +namespace mROA.Implementation.Backend { - private IConnectionHub? _hub; - - private IContextRepository? _contextRepository; - private IMethodRepository? _methodRepository; - private ISerializationToolkit? _serializationToolkit; - private IExecuteModule? _executeModule; - - public void Inject(T dependency) + public class HubRequestExtractor : IInjectableModule { - switch (dependency) + private IConnectionHub? _hub; + + private IContextRepository? _contextRepository; + private IContextRepository? _remoteContextRepository; + private IMethodRepository? _methodRepository; + private ISerializationToolkit? _serializationToolkit; + private IExecuteModule? _executeModule; + private readonly Type _extractorType; + + public HubRequestExtractor(Type extractorType) { - case IConnectionHub connectionHub: - _hub = connectionHub; - _hub.OnConnected += HubOnOnConnected; - break; - case IContextRepository contextRepository: - _contextRepository = contextRepository; - break; - case IMethodRepository methodRepository: - _methodRepository = methodRepository; - break; - case ISerializationToolkit serializationToolkit: - _serializationToolkit = serializationToolkit; - break; - case IExecuteModule executeModule: - _executeModule = executeModule; - break; + _extractorType = extractorType; + } + + public void Inject(T dependency) + { + switch (dependency) + { + case IConnectionHub connectionHub: + _hub = connectionHub; + _hub.OnConnected += HubOnOnConnected; + break; + case MultiClientContextRepository: + case ContextRepository: + _contextRepository = dependency as IContextRepository; + break; + case RemoteContextRepository remoteContextRepository: + _remoteContextRepository = remoteContextRepository; + break; + case IMethodRepository methodRepository: + _methodRepository = methodRepository; + break; + case ISerializationToolkit serializationToolkit: + _serializationToolkit = serializationToolkit; + break; + case IExecuteModule executeModule: + _executeModule = executeModule; + break; + } + } + + private void HubOnOnConnected(IRepresentationModule interaction) + { + var extractor = CreateExtractor(interaction); + _ = extractor.StartExtraction(); + } + + private IRequestExtractor CreateExtractor(IRepresentationModule interaction) + { + var extractor = (IRequestExtractor)Activator.CreateInstance(_extractorType)!; + extractor.Inject(interaction); + if (_contextRepository is IContextRepositoryHub contextHub) + extractor.Inject(contextHub.GetRepository(interaction.Id)); + else + extractor.Inject(interaction); + extractor.Inject(_methodRepository); + extractor.Inject(_serializationToolkit); + extractor.Inject(_executeModule); + extractor.Inject(_remoteContextRepository); + return extractor; } } - - private void HubOnOnConnected(IRepresentationModule interaction) - { - var extractor = (IRequestExtractor)Activator.CreateInstance(extractoType)!; - extractor.Inject(interaction); - if (_contextRepository is IContextRepositoryHub contextHub) - extractor.Inject(contextHub.GetRepository(interaction.Id)); - else - extractor.Inject(interaction); - extractor.Inject(_methodRepository); - extractor.Inject(_serializationToolkit); - extractor.Inject(_executeModule); - _ = extractor.StartExtraction(); - } } \ No newline at end of file diff --git a/mROA/Implementation/Backend/JsonSerialisationModule.cs b/mROA/Implementation/Backend/JsonSerialisationModule.cs deleted file mode 100644 index 557331a..0000000 --- a/mROA/Implementation/Backend/JsonSerialisationModule.cs +++ /dev/null @@ -1,80 +0,0 @@ -// using System.Text; -// using System.Text.Json; -// using mROA.Abstract; -// -// namespace mROA.Implementation.Backend; -// -// public class JsonSerialisationModule : ISerialisationModule -// { -// private IInteractionModule? _dataSource; -// private IExecuteModule? _executeModule; -// private IMethodRepository? _methodRepository; -// private IContextRepository? _contextRepo; -// -// public void HandleIncomingRequest(int clientId, byte[] message) -// { -// MultiClientOwnershipRepository? ownership = null; -// if (TransmissionConfig.OwnershipRepository is MultiClientOwnershipRepository) -// { -// ownership = TransmissionConfig.OwnershipRepository as MultiClientOwnershipRepository; -// ownership.RegisterOwnership(clientId); -// } -// -// NetworkMessage input = JsonSerializer.Deserialize(message)!; -// Console.WriteLine(Encoding.Default.GetString(input.Data)); -// if (input.SchemaId == MessageType.CallRequest) -// { -// var command = JsonSerializer.Deserialize(input.Data)!; -// if (command.Parameter is not null) -// { -// var parameter = _methodRepository!.GetMethod(command.CommandId).GetParameters().First().ParameterType; -// var jsElement = (JsonElement)command.Parameter; -// command.Parameter = jsElement.Deserialize(parameter); -// } -// -// var response = _executeModule!.Execute(command, _contextRepo); -// response.ClientId = clientId; -// var resultType = response is FinalCommandExecution -// ? MessageType.FinishedCommandExecution -// : MessageType.ErrorCommandExecution; -// PostResponse( -// new NetworkMessage -// { -// SchemaId = resultType, -// Id = command.CallRequestId, -// Data = JsonSerializer.SerializeToUtf8Bytes(response, response.GetType()) -// }, clientId); -// } -// -// ownership?.FreeOwnership(); -// } -// -// public void PostResponse(NetworkMessage message, int clientId) -// { -// _dataSource!.SendTo(clientId, JsonSerializer.SerializeToUtf8Bytes(message)); -// } -// -// public void SendWelcomeMessage(int clientId) -// { -// _dataSource!.SendTo(clientId, JsonSerializer.SerializeToUtf8Bytes(new NetworkMessage { Data = JsonSerializer.SerializeToUtf8Bytes(new IdAssingnment { Id = clientId }), SchemaId = MessageType.IdAssigning})); -// } -// -// public void Inject(T dependency) -// { -// switch (dependency) -// { -// case IInteractionModule interactionModule: -// _dataSource = interactionModule; -// break; -// case IExecuteModule executeModule: -// _executeModule = executeModule; -// break; -// case IMethodRepository methodRepository: -// _methodRepository = methodRepository; -// break; -// case IContextRepository contextRepository: -// _contextRepo = contextRepository; -// break; -// } -// } -// } \ No newline at end of file diff --git a/mROA/Implementation/Backend/MultiClientContextRepository.cs b/mROA/Implementation/Backend/MultiClientContextRepository.cs index ff9da0e..065c746 100644 --- a/mROA/Implementation/Backend/MultiClientContextRepository.cs +++ b/mROA/Implementation/Backend/MultiClientContextRepository.cs @@ -1,62 +1,69 @@ +using System; +using System.Collections.Generic; using mROA.Abstract; -namespace mROA.Implementation.Backend; - -public class MultiClientContextRepository(Func produceRepository) : IContextRepository, IContextRepositoryHub +namespace mROA.Implementation.Backend { - private Dictionary _repositories = new(); - - private IContextRepository GetRepositoryByClientId(int clientId) + public class MultiClientContextRepository : IContextRepository, IContextRepositoryHub { - if (_repositories.TryGetValue(clientId, out var repository)) + private readonly Func _produceRepository; + private readonly Dictionary _repositories = new(); + + public MultiClientContextRepository(Func produceRepository) + { + _produceRepository = produceRepository; + } + + public void Inject(T dependency) + { + } + + public int HostId { get; set; } + + public int ResisterObject(object o, IEndPointContext context) + { + var repository = GetRepositoryByClientId(TransmissionConfig.OwnershipRepository.GetOwnershipId()); + return repository.ResisterObject(o, context); + } + + public void ClearObject(ComplexObjectIdentifier id) + { + var repository = GetRepositoryByClientId(TransmissionConfig.OwnershipRepository.GetOwnershipId()); + repository.ClearObject(id); + } + + public T GetObject(ComplexObjectIdentifier id) + { + var repository = GetRepositoryByClientId(TransmissionConfig.OwnershipRepository.GetOwnershipId()); + return repository.GetObject(id); + } + + public object GetSingleObject(Type type, int ownerId) + { + var repository = GetRepositoryByClientId(TransmissionConfig.OwnershipRepository.GetOwnershipId()); + return repository.GetSingleObject(type, ownerId); + } + + public int GetObjectIndex(object o, IEndPointContext context) + { + var repository = GetRepositoryByClientId(TransmissionConfig.OwnershipRepository.GetOwnershipId()); + return repository.GetObjectIndex(o, context); + } + + public IContextRepository GetRepository(int clientId) + { + var repository = GetRepositoryByClientId(clientId); return repository; - - var created = produceRepository(clientId); - _repositories.Add(clientId, created); - return created; - } - public void Inject(T dependency) - { - } + } - public int ResisterObject(object o) - { - return GetRepositoryByClientId(TransmissionConfig.OwnershipRepository.GetOwnershipId()).ResisterObject(o); - } + private IContextRepository GetRepositoryByClientId(int clientId) + { + if (_repositories.TryGetValue(clientId, out var repository)) + return repository; - public void ClearObject(int id) - { - GetRepositoryByClientId(TransmissionConfig.OwnershipRepository.GetOwnershipId()).ClearObject(id); - } - - public object GetObject(int id) - { - return GetRepositoryByClientId(TransmissionConfig.OwnershipRepository.GetOwnershipId()).GetObject(id); - } - - public T? GetObject(int id) - { - return GetRepositoryByClientId(TransmissionConfig.OwnershipRepository.GetOwnershipId()).GetObject(id); - } - - public T GetSingleObject() - { - var result = GetSingleObject(typeof(T)); - return (T)result; - } - - public object GetSingleObject(Type type) - { - return GetRepositoryByClientId(TransmissionConfig.OwnershipRepository.GetOwnershipId()).GetSingleObject(type); - } - - public int GetObjectIndex(object o) - { - return GetRepositoryByClientId(TransmissionConfig.OwnershipRepository.GetOwnershipId()).GetObjectIndex(o); - } - - public IContextRepository GetRepository(int clientId) - { - return GetRepositoryByClientId(clientId); + var created = _produceRepository(clientId); + _repositories.Add(clientId, created); + return created; + } } } \ No newline at end of file diff --git a/mROA/Implementation/Backend/MultiClientOwnershipRepository.cs b/mROA/Implementation/Backend/MultiClientOwnershipRepository.cs index 93f8aa8..56cd389 100644 --- a/mROA/Implementation/Backend/MultiClientOwnershipRepository.cs +++ b/mROA/Implementation/Backend/MultiClientOwnershipRepository.cs @@ -1,28 +1,31 @@ -using mROA.Abstract; +using System; +using System.Collections.Generic; +using mROA.Abstract; -namespace mROA.Implementation.Backend; - -public class MultiClientOwnershipRepository : IOwnershipRepository +namespace mROA.Implementation.Backend { - private Dictionary _ownerships = new(); - - public int GetOwnershipId() + public class MultiClientOwnershipRepository : IOwnershipRepository { - return _ownerships.GetValueOrDefault(Environment.CurrentManagedThreadId, 0); - } + private Dictionary _ownerships = new(); - public int GetHostOwnershipId() - { - return 0; - } + public int GetOwnershipId() + { + return _ownerships.GetValueOrDefault(Environment.CurrentManagedThreadId, 0); + } - public void RegisterOwnership(int ownershipId) - { - _ownerships.TryAdd(Environment.CurrentManagedThreadId, ownershipId); - } + public int GetHostOwnershipId() + { + return 0; + } - public void FreeOwnership() - { - _ownerships.Remove(Environment.CurrentManagedThreadId); + public void RegisterOwnership(int ownershipId) + { + _ownerships.TryAdd(Environment.CurrentManagedThreadId, ownershipId); + } + + public void FreeOwnership() + { + _ownerships.Remove(Environment.CurrentManagedThreadId); + } } } \ No newline at end of file diff --git a/mROA/Implementation/Backend/NetworkGatewayModule.cs b/mROA/Implementation/Backend/NetworkGatewayModule.cs index 45ac183..d7bf45d 100644 --- a/mROA/Implementation/Backend/NetworkGatewayModule.cs +++ b/mROA/Implementation/Backend/NetworkGatewayModule.cs @@ -1,89 +1,102 @@ -using System.Net; +using System; +using System.Net; using System.Net.Sockets; +using System.Threading.Tasks; using mROA.Abstract; -namespace mROA.Implementation.Backend; - -public class NetworkGatewayModule : IGatewayModule +namespace mROA.Implementation.Backend { - private readonly Type? _interactionModuleType; - private readonly IInjectableModule[]? _injectableModules; - private readonly TcpListener _tcpListener; - private IConnectionHub? _hub; - private ISerializationToolkit? _serialization; - - public NetworkGatewayModule(IPEndPoint endpoint, Type interactionModuleType, IInjectableModule[] injectableModules) + public class NetworkGatewayModule : IGatewayModule { - _tcpListener = new(endpoint); - _interactionModuleType = interactionModuleType; - _injectableModules = injectableModules; - } + private readonly IInjectableModule[]? _injectableModules; + private readonly Type? _interactionModuleType; + private readonly TcpListener _tcpListener; + private IConnectionHub? _hub; + private ISerializationToolkit? _serialization; - public void Run() - { - _tcpListener.Start(); - Console.WriteLine($"Listening on {_tcpListener.LocalEndpoint}"); - Console.WriteLine("Enter Backspace to stop"); - - Task.Run(HandleIncomingConnections); - - while (true) + public NetworkGatewayModule(IPEndPoint endpoint, Type interactionModuleType, + IInjectableModule[] injectableModules) { - var key = Console.ReadKey(); - if (key.Key == ConsoleKey.Backspace) - break; + _tcpListener = new(endpoint); + _interactionModuleType = interactionModuleType; + _injectableModules = injectableModules; } - Console.WriteLine("Stopping"); - } - - public void Dispose() - { - _tcpListener.Stop(); - _tcpListener.Dispose(); - } - - private void HandleIncomingConnections() - { - if (_hub is null) - throw new NullReferenceException("Hub module is null"); - if (_tcpListener == null) - throw new NullReferenceException("TcpListener is null"); - if (_injectableModules is null) - throw new NullReferenceException("InjectableModules is null"); - if (_interactionModuleType is null) - throw new NullReferenceException("InteractionModuleType is null"); - if (_serialization is null) - throw new NullReferenceException("Serialization is null"); - - while (true) + public void Run() { - var client = _tcpListener.AcceptTcpClient(); - Console.WriteLine($"Client connected from {client.Client.RemoteEndPoint}"); - var interaction = Activator.CreateInstance(_interactionModuleType) as INextGenerationInteractionModule; + _tcpListener.Start(); + Console.WriteLine($"Listening on {_tcpListener.LocalEndpoint}"); + Console.WriteLine("Enter Backspace to stop"); - foreach (var injectableModule in _injectableModules) - interaction!.Inject(injectableModule); + Task.Run(HandleIncomingConnections); - interaction!.Inject(_serialization); - - interaction.BaseStream = client.GetStream(); - interaction.StartInfiniteReceiving(); - interaction.PostMessage(new NetworkMessage + while (true) { - Id = Guid.NewGuid(), MessageType = EMessageType.IdAssigning, - Data = _serialization.Serialize(new IdAssingnment { Id = interaction.ConnectionId }) - }); - _hub.RegisterInteraction(interaction); - Console.WriteLine("Client registered"); - } - } + var key = Console.ReadKey(); + if (key.Key == ConsoleKey.Backspace) + break; + } - public void Inject(T dependency) - { - if (dependency is IConnectionHub interactionModule) - _hub = interactionModule; - if (dependency is ISerializationToolkit serializationToolkit) - _serialization = serializationToolkit; + Console.WriteLine("Stopping"); + } + + public void Dispose() + { + _tcpListener.Stop(); + } + + public void Inject(T dependency) + { + switch (dependency) + { + case IConnectionHub interactionModule: + _hub = interactionModule; + break; + case ISerializationToolkit serializationToolkit: + _serialization = serializationToolkit; + break; + } + } + + private void HandleIncomingConnections() + { + ThrowIfNotInjected(); + + while (true) + { + var client = _tcpListener.AcceptTcpClient(); + Console.WriteLine($"Client connected from {client.Client.RemoteEndPoint}"); + var interaction = Activator.CreateInstance(_interactionModuleType!) as INextGenerationInteractionModule; + + foreach (var injectableModule in _injectableModules!) + interaction!.Inject(injectableModule); + + interaction!.Inject(_serialization); + + interaction.BaseStream = client.GetStream(); + + interaction.PostMessage(new NetworkMessage + { + Id = Guid.NewGuid(), SchemaId = MessageType.IdAssigning, + Data = _serialization!.Serialize(new IdAssignment { Id = -interaction.ConnectionId }) + }); + _hub!.RegisterInteraction(interaction); + Console.WriteLine("Client registered"); + } + } + + private void ThrowIfNotInjected() + { + if (_hub is null) + throw new NullReferenceException("Hub module is null"); + if (_tcpListener == null) + throw new NullReferenceException("TcpListener is null"); + if (_injectableModules is null) + throw new NullReferenceException("InjectableModules is null"); + if (_interactionModuleType is null) + throw new NullReferenceException("InteractionModuleType is null"); + if (_serialization is null) + throw new NullReferenceException("Serialization is null"); + } } } \ No newline at end of file diff --git a/mROA/Implementation/Backend/StreamBasedInteractionModule.cs b/mROA/Implementation/Backend/StreamBasedInteractionModule.cs deleted file mode 100644 index 0427209..0000000 --- a/mROA/Implementation/Backend/StreamBasedInteractionModule.cs +++ /dev/null @@ -1,66 +0,0 @@ -// using mROA.Abstract; -// -// namespace mROA.Implementation.Backend; -// -// public class StreamBasedInteractionModule : IInteractionModule -// { -// internal ISerialisationModule _serialisationModule; -// private readonly Dictionary _streams = new(); -// internal Action? _handler; -// -// public void RegisterSource(Stream stream) -// { -// var id = Random.Shared.Next(); -// _streams.Add(id, stream); -// _ = ListenTo((id, stream), _handler!); -// _serialisationModule.SendWelcomeMessage(id); -// } -// -// public Stream GetSource(int clientId) -// { -// return _streams.GetValueOrDefault(clientId, Stream.Null); -// } -// -// public void SendTo(int clientId, byte[] message) -// { -// if (!_streams.TryGetValue(clientId, out var stream)) -// { -// throw new KeyNotFoundException($"Client {clientId} not found"); -// } -// -// stream.Write(BitConverter.GetBytes((ushort)message.Length), 0, sizeof(ushort)); -// stream.Write(message, 0, message.Length); -// } -// -// private async Task ListenTo((int id, Stream stream) client, Action action) -// { -// TransmissionConfig.OwnershipRepository = new MultiClientOwnershipRepository(); -// const int bufferSize = ushort.MaxValue; -// try -// { -// byte[] buffer = new byte[bufferSize]; -// while (client.stream.CanRead) -// { -// await client.stream.ReadExactlyAsync(buffer, 0, 2); -// var len = BitConverter.ToUInt16(buffer, 0); -// await client.stream.ReadExactlyAsync(buffer, 0, len); -// _ = Task.Run(() => action(client.id, buffer[..len])); -// } -// } -// catch (Exception) -// { -// Console.WriteLine($"Client handling finished:{client.id}"); -// _streams.Remove(client.id); -// } -// } -// -// public void Inject(T dependency) -// { -// if (dependency is ISerialisationModule serialisationModule) -// { -// _handler = serialisationModule.HandleIncomingRequest; -// _serialisationModule = serialisationModule; -// } -// } -// -// } \ No newline at end of file diff --git a/mROA/Implementation/Bootstrap/FullMixBuilder.cs b/mROA/Implementation/Bootstrap/FullMixBuilder.cs index 848480d..3b7a9ae 100644 --- a/mROA/Implementation/Bootstrap/FullMixBuilder.cs +++ b/mROA/Implementation/Bootstrap/FullMixBuilder.cs @@ -1,20 +1,23 @@ +using System.Collections.Generic; +using System.Linq; using mROA.Abstract; -namespace mROA.Implementation.Bootstrap; - -public class FullMixBuilder +namespace mROA.Implementation.Bootstrap { - public List Modules { get; } = []; - - public void Build() + public class FullMixBuilder { - foreach (var module in Modules) - foreach (var injection in Modules) - module.Inject(injection); - } + public List Modules { get; } = new() { }; - public T? GetModule() - { - return Modules.OfType().FirstOrDefault(); + public void Build() + { + foreach (var module in Modules) + foreach (var injection in Modules) + module.Inject(injection); + } + + public T? GetModule() + { + return Modules.OfType().FirstOrDefault(); + } } } \ No newline at end of file diff --git a/mROA/Implementation/CallRequest.cs b/mROA/Implementation/CallRequest.cs index 1709ba7..6a909b0 100644 --- a/mROA/Implementation/CallRequest.cs +++ b/mROA/Implementation/CallRequest.cs @@ -1,24 +1,42 @@ -using System.Text.Json.Serialization; +using System; + // ReSharper disable UnusedAutoPropertyAccessor.Global // ReSharper disable AutoPropertyCanBeMadeGetOnly.Global -namespace mROA.Implementation; - -public interface ICallRequest +namespace mROA.Implementation { - Guid Id { get; } - int CommandId { get; } - int ObjectId { get; } - object? Parameter { get; } -} + public interface ICallRequest + { + Guid Id { get; } + int CommandId { get; } + ComplexObjectIdentifier ObjectId { get; } + object?[]? Parameters { get; } + } -public class DefaultCallRequest : ICallRequest -{ - public Guid Id { get; set; } = Guid.NewGuid(); - public int CommandId { get; init; } - public int ObjectId { get; init; } = -1; - - [JsonIgnore] - public Type? ParameterType { get; init; } - public object? Parameter { get; set; } + public class DefaultCallRequest : ICallRequest + { + public Guid Id { get; set; } = Guid.NewGuid(); + public int CommandId { get; set; } + public ComplexObjectIdentifier ObjectId { get; set; } = ComplexObjectIdentifier.Null; + + public object?[]? Parameters { get; set; } + + public override string ToString() + { + return $"Call request {{ Id : {Id}, CommandId : {CommandId}, ObjectId : {ObjectId} }}"; + } + } + + public class CancelRequest : ICallRequest + { + public Guid Id { get; set; } + public int CommandId { get; set; } = -2; + public ComplexObjectIdentifier ObjectId { get; set; } = ComplexObjectIdentifier.Null; + public object?[]? Parameters { get; set; } = null; + + public override string ToString() + { + return $"Cancel request {{ Id : {Id}, CommandId : {CommandId}, ObjectId : {ObjectId} }}"; + } + } } \ No newline at end of file diff --git a/mROA/Implementation/CancellationRepository.cs b/mROA/Implementation/CancellationRepository.cs new file mode 100644 index 0000000..d6543aa --- /dev/null +++ b/mROA/Implementation/CancellationRepository.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using mROA.Abstract; + +namespace mROA.Implementation +{ + public class CancellationRepository : ICancellationRepository + { + private Dictionary _cancellations = new(); + + public void RegisterCancellation(Guid id, CancellationTokenSource cts) + { + _cancellations.TryAdd(id, cts); + } + + public CancellationTokenSource? GetCancellation(Guid id) + { + return _cancellations.GetValueOrDefault(id, null); + } + + public void FreeCancelation(Guid id) + { + _cancellations.Remove(id); + } + + public void Inject(T dependency) + { + } + } +} \ No newline at end of file diff --git a/mROA/Implementation/CommandExecution/AsyncCommandExecution.cs b/mROA/Implementation/CommandExecution/AsyncCommandExecution.cs new file mode 100644 index 0000000..7dea41d --- /dev/null +++ b/mROA/Implementation/CommandExecution/AsyncCommandExecution.cs @@ -0,0 +1,10 @@ +using System; +using mROA.Abstract; + +namespace mROA.Implementation.CommandExecution +{ + public class AsyncCommandExecution : ICommandExecution + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/mROA/Implementation/CommandExecution/ExceptionCommandExecution.cs b/mROA/Implementation/CommandExecution/ExceptionCommandExecution.cs index 772fa05..82a63de 100644 --- a/mROA/Implementation/CommandExecution/ExceptionCommandExecution.cs +++ b/mROA/Implementation/CommandExecution/ExceptionCommandExecution.cs @@ -1,17 +1,17 @@ -using mROA.Abstract; +using System; +using mROA.Abstract; using mROA.Implementation.Frontend; -namespace mROA.Implementation.CommandExecution; - -public class ExceptionCommandExecution : ICommandExecution +namespace mROA.Implementation.CommandExecution { - public Guid Id { get; init; } - public int ClientId { get; set; } - public int CommandId { get; init; } - public required string Exception { get; set; } - - public RemoteException GetException() + public class ExceptionCommandExecution : ICommandExecution { - return new RemoteException(Exception) { CallRequestId = Id }; + public Guid Id { get; set; } + public string Exception { get; set; } + + public RemoteException GetException() + { + return new RemoteException(Exception) { CallRequestId = Id }; + } } } \ No newline at end of file diff --git a/mROA/Implementation/CommandExecution/FinalCommandExecution.cs b/mROA/Implementation/CommandExecution/FinalCommandExecution.cs index 5069911..fba0dca 100644 --- a/mROA/Implementation/CommandExecution/FinalCommandExecution.cs +++ b/mROA/Implementation/CommandExecution/FinalCommandExecution.cs @@ -1,20 +1,17 @@ -using System.Text.Json.Serialization; +using System; using mROA.Abstract; // ReSharper disable UnusedAutoPropertyAccessor.Global -namespace mROA.Implementation.CommandExecution; - -public class FinalCommandExecution : ICommandExecution +namespace mROA.Implementation.CommandExecution { - public Guid Id { get; init; } - [JsonIgnore] - public int ClientId { get; set; } - [JsonIgnore] - public int CommandId { get; init; } -} + public class FinalCommandExecution : ICommandExecution + { + public Guid Id { get; set; } + } -public class FinalCommandExecution : FinalCommandExecution -{ - public T? Result { get; init; } + public class FinalCommandExecution : FinalCommandExecution + { + public T? Result { get; set; } + } } \ No newline at end of file diff --git a/mROA/Implementation/CommandExecution/TypedFinalCommandExecution.cs b/mROA/Implementation/CommandExecution/TypedFinalCommandExecution.cs deleted file mode 100644 index 813accc..0000000 --- a/mROA/Implementation/CommandExecution/TypedFinalCommandExecution.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace mROA.Implementation.CommandExecution; - -public class TypedFinalCommandExecution : FinalCommandExecution -{ - [JsonIgnore] - // ReSharper disable once UnusedAutoPropertyAccessor.Global - public Type? Type { get; set; } -} \ No newline at end of file diff --git a/mROA/Implementation/ComplexContextRepository.cs b/mROA/Implementation/ComplexContextRepository.cs new file mode 100644 index 0000000..8f44c78 --- /dev/null +++ b/mROA/Implementation/ComplexContextRepository.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using mROA.Abstract; + +namespace mROA.Implementation +{ + public class ComplexContextRepository : IContextRepository + { + private List>> _storages = new(); + public static object[] EventBinders = { }; + + private IRemoteObjectFactory? _remoteObjectFactory; + private IRepresentationModuleProducer? _representationModuleProducer; + + public void Inject(T dependency) + { + if (dependency is IRemoteObjectFactory remoteObjectFactory) + { + _remoteObjectFactory = remoteObjectFactory; + } + + if (dependency is IRepresentationModuleProducer moduleProducer) + { + _representationModuleProducer = moduleProducer; + } + } + + public int HostId { get; set; } + + public int ResisterObject(object o, IEndPointContext context) + { + var storageIndex = _storages.FindIndex(i => i.Key == context.OwnerId); + if (storageIndex == -1) + { + _storages.Add( + new KeyValuePair>(context.OwnerId, new ExtensibleStorage())); + storageIndex = _storages.Count - 1; + } + + var storage = _storages[storageIndex].Value; + + var placedIndex = storage.Place(o); + EventBinders.OfType>().FirstOrDefault() + ?.BindEvents((T)o, context, _representationModuleProducer!, placedIndex); + + return placedIndex; + } + + public void ClearObject(ComplexObjectIdentifier id) + { + _storages.Find(i => i.Key == id.OwnerId).Value.Free(id.ContextId); + } + + public T GetObject(ComplexObjectIdentifier id) + { + throw new NotImplementedException(); + } + + public object GetSingleObject(Type type, int ownerId) + { + throw new NotImplementedException(); + } + + public int GetObjectIndex(object o, IEndPointContext context) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/mROA/Implementation/ComplexObjectIdentifier.cs b/mROA/Implementation/ComplexObjectIdentifier.cs new file mode 100644 index 0000000..d95d378 --- /dev/null +++ b/mROA/Implementation/ComplexObjectIdentifier.cs @@ -0,0 +1,58 @@ +using System; + +namespace mROA.Implementation +{ +#pragma warning disable CS8618, CS9264 + public struct ComplexObjectIdentifier : IEquatable + { + public int ContextId; + public int OwnerId; + + public ComplexObjectIdentifier(int contextId, int ownerId) + { + ContextId = contextId; + OwnerId = ownerId; + } + + public static ComplexObjectIdentifier Singleton(int ownerId) => new() { ContextId = -1, OwnerId = ownerId }; + + public static ComplexObjectIdentifier Null = new ComplexObjectIdentifier { ContextId = -2, OwnerId = 0 }; + public static ComplexObjectIdentifier FromFlat(ulong flat) => new() { Flat = flat }; + + public int ClientId => Math.Abs(OwnerId); + public bool IsSererStored => OwnerId > 0; + public bool IsClientStored => OwnerId < 0; + + public override string ToString() + { + return $"{{ {nameof(ContextId)}: {ContextId}, {nameof(OwnerId)}: {OwnerId} }}"; + } + + public bool IsStatic => ContextId == -1; + + public ulong Flat + { + get => (ulong)OwnerId << 32 | (uint)ContextId; + set + { + OwnerId = (int)(value >> 32); + ContextId = (int)(value & 0xFFFFFFFF); + } + } + + public bool Equals(ComplexObjectIdentifier other) + { + return ContextId == other.ContextId && OwnerId == other.OwnerId; + } + + public override bool Equals(object? obj) + { + return obj is ComplexObjectIdentifier other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(ContextId, OwnerId); + } + } +} \ No newline at end of file diff --git a/mROA/Implementation/CreativeRepresentationModuleProducer.cs b/mROA/Implementation/CreativeRepresentationModuleProducer.cs index 0b79371..0e21899 100644 --- a/mROA/Implementation/CreativeRepresentationModuleProducer.cs +++ b/mROA/Implementation/CreativeRepresentationModuleProducer.cs @@ -1,40 +1,43 @@ -using mROA.Abstract; +using System; +using mROA.Abstract; -namespace mROA.Implementation; - -public class CreativeRepresentationModuleProducer : IRepresentationModuleProducer +namespace mROA.Implementation { - private Type _reprModuleType; - private IInjectableModule[] _creationModules; - private IConnectionHub? _hub; - - public CreativeRepresentationModuleProducer(IInjectableModule[] creationModules, Type reprModuleType) + public class CreativeRepresentationModuleProducer : IRepresentationModuleProducer { - _creationModules = creationModules; - _reprModuleType = reprModuleType; - } + private Type _reprModuleType; + private IInjectableModule[] _creationModules; + private IConnectionHub? _hub; + + public CreativeRepresentationModuleProducer(IInjectableModule[] creationModules, Type reprModuleType) + { + _creationModules = creationModules; + _reprModuleType = reprModuleType; + } - public void Inject(T dependency) - { - if (dependency is IConnectionHub interactionModule) - _hub = interactionModule; - } + public void Inject(T dependency) + { + if (dependency is IConnectionHub interactionModule) + _hub = interactionModule; + } - public IRepresentationModule Produce(int id) - { - if (_hub == null) - throw new NullReferenceException("Interaction module is null"); + public IRepresentationModule Produce(int id) + { + if (_hub == null) + throw new NullReferenceException("Interaction module is null"); - var produced = - Activator.CreateInstance(_reprModuleType) as IRepresentationModule ?? - throw new Exception("Bad serialization module type"); + var produced = + Activator.CreateInstance(_reprModuleType) as IRepresentationModule ?? + throw new Exception("Bad serialization module type"); - foreach (var creationModule in _creationModules) - produced.Inject(creationModule); - - produced.Inject(_hub.GetInteracion(id)); - - return produced; + foreach (var creationModule in _creationModules) + produced.Inject(creationModule); + + var interaction = _hub.GetInteracion(id); + produced.Inject(interaction); + + return produced; + } } } \ No newline at end of file diff --git a/mROA/Implementation/EndPointContext.cs b/mROA/Implementation/EndPointContext.cs new file mode 100644 index 0000000..e6351e9 --- /dev/null +++ b/mROA/Implementation/EndPointContext.cs @@ -0,0 +1,20 @@ +using System; +using mROA.Abstract; + +namespace mROA.Implementation +{ + public class EndPointContext : IEndPointContext + { + public Func OwnerFunc; + public IContextRepository RealRepository { get; set; } + public IContextRepository RemoteRepository { get; set; } + public int HostId { get; set; } + + public int OwnerId + { + get => OwnerFunc(); + // ReSharper disable once UnusedMember.Global + set { OwnerFunc = () => value; } + } + } +} \ No newline at end of file diff --git a/mROA/Implementation/EventBinder.cs b/mROA/Implementation/EventBinder.cs new file mode 100644 index 0000000..8354c4f --- /dev/null +++ b/mROA/Implementation/EventBinder.cs @@ -0,0 +1,15 @@ +using System; + +namespace mROA.Abstract +{ + public class EventBinder : IEventBinder + { + public Action BindAction { get; set; } + + public void BindEvents(T source, IEndPointContext context, + IRepresentationModuleProducer representationModuleProducer, int index) + { + BindAction(source, context, representationModuleProducer, index); + } + } +} \ No newline at end of file diff --git a/mROA/Implementation/ExtensibleStorage.cs b/mROA/Implementation/ExtensibleStorage.cs new file mode 100644 index 0000000..6543519 --- /dev/null +++ b/mROA/Implementation/ExtensibleStorage.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using mROA.Abstract; + +namespace mROA.Implementation +{ + public class ExtensibleStorage : IStorage where T : class + { + private const int StartupSize = 1024; + private const int GrowSize = 128; + private T?[] _array = new T?[StartupSize]; + private readonly LinkedList _freePlaces = new(Enumerable.Range(0, StartupSize)); + + public T? GetValue(int index) + { + if (index < 0 || index >= _array.Length) + { + return null; + } + + return _array[index]; + } + + public int GetIndex(T value) + { + return Array.IndexOf(_array, value); + } + + public int Place(T value) + { + if (_freePlaces.Count == 0) + { + Grow(); + } + + var index = _freePlaces.First.Value; + + _freePlaces.RemoveFirst(); + _array[index] = value; + + return index; + } + + private void Grow() + { + foreach (var index in Enumerable.Range(_array.Length, GrowSize)) + _freePlaces.AddLast(index); + + T?[] nextStorage = new T[_array.Length + GrowSize]; + Array.Copy(_array, nextStorage, _array.Length); + _array = nextStorage; + } + + public void Free(int index) + { + _freePlaces.AddFirst(index); + _array[index] = default; + } + } +} \ No newline at end of file diff --git a/mROA/Implementation/Frontend/JsonFrontendSerialisationModule.cs b/mROA/Implementation/Frontend/JsonFrontendSerialisationModule.cs deleted file mode 100644 index dce21a4..0000000 --- a/mROA/Implementation/Frontend/JsonFrontendSerialisationModule.cs +++ /dev/null @@ -1,84 +0,0 @@ -namespace mROA.Implementation.Frontend; - -// public class JsonFrontendSerialisationModule -// : ISerialisationModule.IFrontendSerialisationModule -// { -// private IInteractionModule.IFrontendInteractionModule? _interactionModule; -// public int ClientId => _interactionModule!.ClientId; -// -// public async Task GetNextCommandExecution(Guid requestId) where T : ICommandExecution -// { -// if (_interactionModule is null) -// throw new Exception("Interaction module not initialized"); -// -// var receiveMessage = await _interactionModule.ReceiveMessage(); -// var message = JsonSerializer.Deserialize(receiveMessage)!; -// -// while (message.Id != requestId) -// { -// receiveMessage = await _interactionModule.ReceiveMessage(); -// message = JsonSerializer.Deserialize(receiveMessage)!; -// } -// -// var parsed = JsonSerializer.Deserialize(message.Data)!; -// -// if (message.SchemaId == MessageType.ErrorCommandExecution) -// { -// throw new RemoteException(JsonSerializer.Deserialize(message.Data)!.Exception) -// { CallRequestId = requestId }; -// } -// -// return parsed; -// } -// -// public async Task> GetFinalCommandExecution(Guid requestId) -// { -// if (_interactionModule is null) -// throw new Exception("Interaction module not initialized"); -// -// var receiveMessage = await _interactionModule.ReceiveMessage(); -// -// var message = JsonSerializer.Deserialize(receiveMessage)!; -// while (message.Id != requestId) -// { -// receiveMessage = await _interactionModule.ReceiveMessage(); -// -// message = JsonSerializer.Deserialize(receiveMessage)!; -// } -// -// if (message.SchemaId == MessageType.ErrorCommandExecution) -// { -// throw new RemoteException(JsonSerializer.Deserialize(message.Data)!.Exception) -// { CallRequestId = requestId }; -// } -// -// return JsonSerializer.Deserialize>(message.Data)!; -// } -// -// public void PostCallRequest(ICallRequest callRequest) -// { -// if (_interactionModule is null) -// throw new Exception("Interaction module not initialized"); -// -// -// var post = JsonSerializer.SerializeToUtf8Bytes(callRequest, callRequest.GetType()); -// _interactionModule.PostMessage(JsonSerializer.SerializeToUtf8Bytes(new NetworkMessage -// { -// Id = callRequest.CallRequestId, -// Data = post, -// SchemaId = MessageType.CallRequest -// })); -// } -// -// public void Inject(T dependency) -// { -// if (dependency is IInteractionModule.IFrontendInteractionModule interactionModule) -// _interactionModule = interactionModule; -// } -// } - -public class RemoteException(string error) : Exception -{ - public Guid CallRequestId; - public override string Message => $"Error in request {CallRequestId} : {error}"; -} \ No newline at end of file diff --git a/mROA/Implementation/Frontend/NetworkFrontendBridge.cs b/mROA/Implementation/Frontend/NetworkFrontendBridge.cs index 9aa3b0c..8bd0aba 100644 --- a/mROA/Implementation/Frontend/NetworkFrontendBridge.cs +++ b/mROA/Implementation/Frontend/NetworkFrontendBridge.cs @@ -1,47 +1,55 @@ +using System; using System.Net; using System.Net.Sockets; using mROA.Abstract; -namespace mROA.Implementation.Frontend; - -public class NetworkFrontendBridge(IPEndPoint ipEndPoint) : IFrontendBridge +namespace mROA.Implementation.Frontend { - private readonly TcpClient _tcpClient = new(); - private NextGenerationInteractionModule? _interactionModule; - private ISerializationToolkit? _serialization; - - public void Inject(T dependency) + public class NetworkFrontendBridge : IFrontendBridge { - switch (dependency) - { - case NextGenerationInteractionModule interactionModule: - _interactionModule = interactionModule; - break; - case ISerializationToolkit toolkit: - _serialization = toolkit; - break; - } - } + private readonly IPEndPoint _ipEndPoint; + private readonly TcpClient _tcpClient = new(); + private NextGenerationInteractionModule? _interactionModule; + private ISerializationToolkit? _serialization; - public void Connect() - { - if (_interactionModule is null) - throw new Exception("Interaction module was not injected"); - if (_serialization == null) - throw new NullReferenceException("Serialization toolkit is not initialized"); - - _tcpClient.Connect(ipEndPoint); - _interactionModule.BaseStream = _tcpClient.GetStream(); - _interactionModule.StartInfiniteReceiving(); - - var handle = _interactionModule.CurrentReceivingHandle; - handle.WaitOne(); - var welcomeMessage = _interactionModule.LastMessage; - if (welcomeMessage.MessageType != EMessageType.IdAssigning) + public NetworkFrontendBridge(IPEndPoint ipEndPoint) { - throw new Exception($"Incorrect message type. Must be IdAssigning, current : {welcomeMessage.MessageType.ToString()}"); + _ipEndPoint = ipEndPoint; + } + + public void Inject(T dependency) + { + switch (dependency) + { + case NextGenerationInteractionModule interactionModule: + _interactionModule = interactionModule; + break; + case ISerializationToolkit toolkit: + _serialization = toolkit; + break; + } + } + + public void Connect() + { + if (_interactionModule is null) + throw new Exception("Interaction module was not injected"); + if (_serialization == null) + throw new NullReferenceException("Serialization toolkit is not initialized"); + + _tcpClient.Connect(_ipEndPoint); + _interactionModule.BaseStream = _tcpClient.GetStream(); + var welcomeMessage = _interactionModule.GetNextMessageReceiving().GetAwaiter().GetResult(); + if (welcomeMessage.SchemaId != MessageType.IdAssigning) + { + throw new Exception( + $"Incorrect message type. Must be IdAssigning, current : {welcomeMessage.SchemaId.ToString()}"); + } + + + var assignment = _serialization.Deserialize(welcomeMessage.Data)!; + _interactionModule.ConnectionId = -assignment.Id; + TransmissionConfig.OwnershipRepository = new StaticOwnershipRepository(assignment.Id); } - _interactionModule.HandleMessage(welcomeMessage); - TransmissionConfig.OwnershipRepository = new StaticOwnershipRepository(_serialization.Deserialize(welcomeMessage.Data)!.Id); } } \ No newline at end of file diff --git a/mROA/Implementation/Frontend/RemoteException.cs b/mROA/Implementation/Frontend/RemoteException.cs new file mode 100644 index 0000000..e63e8b3 --- /dev/null +++ b/mROA/Implementation/Frontend/RemoteException.cs @@ -0,0 +1,17 @@ +using System; + +namespace mROA.Implementation.Frontend +{ + public class RemoteException : Exception + { + public Guid CallRequestId; + private readonly string _error; + + public RemoteException(string error) + { + _error = error; + } + + public override string Message => $"Error in request {CallRequestId} : {_error}"; + } +} \ No newline at end of file diff --git a/mROA/Implementation/Frontend/RequestExtractor.cs b/mROA/Implementation/Frontend/RequestExtractor.cs index 6a6e52f..0cc961a 100644 --- a/mROA/Implementation/Frontend/RequestExtractor.cs +++ b/mROA/Implementation/Frontend/RequestExtractor.cs @@ -1,88 +1,158 @@ +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; using mROA.Abstract; using mROA.Implementation.Backend; using mROA.Implementation.CommandExecution; // ReSharper disable MethodHasAsyncOverload -namespace mROA.Implementation.Frontend; - -public class RequestExtractor : IRequestExtractor +namespace mROA.Implementation.Frontend { - private IRepresentationModule? _representationModule; - private IContextRepository? _contextRepository; - private IMethodRepository? _methodRepository; - private IExecuteModule? _executeModule; - private ISerializationToolkit? _serializationToolkit; - - public void Inject(T dependency) + public class RequestExtractor : IRequestExtractor { - switch (dependency) + private IExecuteModule? _executeModule; + private IMethodRepository? _methodRepository; + private IContextRepository? _realContextRepository; + private IContextRepository? _remoteContextRepository; + private IRepresentationModule? _representationModule; + private ISerializationToolkit? _serializationToolkit; + + public void Inject(T dependency) { - case IExecuteModule executeModule: - _executeModule = executeModule; - break; - case IContextRepository contextRepository: - _contextRepository = contextRepository; - break; - case IMethodRepository methodRepository: - _methodRepository = methodRepository; - break; - case IRepresentationModule representationModule: - _representationModule = representationModule; - break; - case ISerializationToolkit serializationToolkit: - _serializationToolkit = serializationToolkit; - break; - } - } - - public async Task StartExtraction() - { - if (_serializationToolkit == null) - throw new NullReferenceException("Serializing toolkit is null."); - if (_executeModule == null) - throw new NullReferenceException("Execute module is null."); - if (_contextRepository == null) - throw new NullReferenceException("Context repository is null."); - if (_representationModule == null) - throw new NullReferenceException("Representation module is null."); - if (_methodRepository == null) - throw new NullReferenceException("Method repository is null."); - - await Task.Yield(); - - var multiClientOwnershipRepository = TransmissionConfig.OwnershipRepository as MultiClientOwnershipRepository; - multiClientOwnershipRepository?.RegisterOwnership(_representationModule.Id); - - try - { - var lastCommandId = Guid.Empty; - while (true) + switch (dependency) { - - var request = - _representationModule!.GetMessage(m => m.Id != lastCommandId && m.MessageType == EMessageType.CallRequest); - lastCommandId = request.Id; - if (request.Parameter is not null) - { - var parameterType = _methodRepository!.GetMethod(request.CommandId).GetParameters().First() - .ParameterType; - - request.Parameter = _serializationToolkit.Cast(request.Parameter, parameterType); - } - - var result = _executeModule.Execute(request, _contextRepository); - - var resultType = result is FinalCommandExecution - ? EMessageType.FinishedCommandExecution - : EMessageType.ExceptionCommandExecution; - - _representationModule.PostCallMessage(request.Id, resultType, result, result.GetType()); + case IExecuteModule executeModule: + _executeModule = executeModule; + break; + case MultiClientContextRepository: + case ContextRepository: + _realContextRepository = dependency as IContextRepository; + break; + case RemoteContextRepository remoteContextRepository: + _remoteContextRepository = remoteContextRepository; + break; + case IMethodRepository methodRepository: + _methodRepository = methodRepository; + break; + case IRepresentationModule representationModule: + _representationModule = representationModule; + break; + case ISerializationToolkit serializationToolkit: + _serializationToolkit = serializationToolkit; + break; } } - catch + + public Task StartExtraction() { - multiClientOwnershipRepository?.RegisterOwnership(_representationModule.Id); + return Task.Run(() => + { + ThrowIfNotInjected(); + var multiClientOwnershipRepository = + TransmissionConfig.OwnershipRepository as MultiClientOwnershipRepository; + multiClientOwnershipRepository?.RegisterOwnership(_representationModule.Id); + + try + { +#if TRACE + var sw = new Stopwatch(); +#endif + while (true) + { +#if TRACE + Console.WriteLine("Waiting for request..."); + if (sw.IsRunning) + { + sw.Stop(); + Console.WriteLine($"Request handling took {Math.Round(sw.Elapsed.TotalMilliseconds * 1000.0)} microseconds."); + } +#endif + var tokenSource = new CancellationTokenSource(); + var token = tokenSource.Token; + var defaultRequest = + _representationModule!.GetMessageAsync( + messageType: MessageType.CallRequest, token: token); + var cancelRequest = + _representationModule!.GetMessageAsync( + messageType: MessageType.CancelRequest, token: token); + var eventRequest = + _representationModule!.GetMessageAsync( + messageType: MessageType.EventRequest, token: token); + Task.WaitAny(defaultRequest, cancelRequest, eventRequest); +#if TRACE + Console.WriteLine("Request received"); + sw.Restart(); +#endif + if (cancelRequest.IsCompleted) + { +#if TRACE + Console.WriteLine("Cancelling request"); +#endif + HandleCancelRequest(tokenSource, cancelRequest.Result); + } + else if (defaultRequest.IsCompleted) + { + HandleCallRequest(tokenSource, defaultRequest.Result); + } + else + { + HandleEventRequest(tokenSource, eventRequest.Result); + } + } + } + catch + { + multiClientOwnershipRepository?.FreeOwnership(); + } + }); + } + + private void ThrowIfNotInjected() + { + if (_serializationToolkit == null) + throw new NullReferenceException("Serializing toolkit is null."); + if (_executeModule == null) + throw new NullReferenceException("Execute module is null."); + if (_realContextRepository == null) + throw new NullReferenceException("Context repository is null."); + if (_representationModule == null) + throw new NullReferenceException("Representation module is null."); + if (_methodRepository == null) + throw new NullReferenceException("Method repository is null."); + } + + private void HandleCancelRequest(CancellationTokenSource tokenSource, CancelRequest req) + { + tokenSource.Cancel(); + _executeModule!.Execute(req, _realContextRepository!, _representationModule!); + } + + private void HandleCallRequest(CancellationTokenSource tokenSource, DefaultCallRequest request) + { + tokenSource.Cancel(); + + var result = _executeModule!.Execute(request, _realContextRepository!, _representationModule!); + + var resultType = result switch + { + FinalCommandExecution => MessageType.FinishedCommandExecution, + ExceptionCommandExecution => MessageType.ExceptionCommandExecution, + _ => MessageType.Unknown + }; + if (resultType == MessageType.Unknown) + { + return; + } + + _representationModule!.PostCallMessage(request.Id, resultType, result, result.GetType()); + } + + private void HandleEventRequest(CancellationTokenSource tokenSource, DefaultCallRequest request) + { + tokenSource.Cancel(); + _executeModule!.Execute(request, _remoteContextRepository!, _representationModule!); } } } \ No newline at end of file diff --git a/mROA/Implementation/Frontend/StaticOwnershipRepository.cs b/mROA/Implementation/Frontend/StaticOwnershipRepository.cs index bbc0d9b..1739824 100644 --- a/mROA/Implementation/Frontend/StaticOwnershipRepository.cs +++ b/mROA/Implementation/Frontend/StaticOwnershipRepository.cs @@ -1,16 +1,24 @@ using mROA.Abstract; -namespace mROA.Implementation.Frontend; - -public class StaticOwnershipRepository(int id) : IOwnershipRepository +namespace mROA.Implementation.Frontend { - public int GetOwnershipId() + public class StaticOwnershipRepository : IOwnershipRepository { - return id; - } + private readonly int _id; - public int GetHostOwnershipId() - { - return id; + public StaticOwnershipRepository(int id) + { + _id = id; + } + + public int GetOwnershipId() + { + return _id; + } + + public int GetHostOwnershipId() + { + return _id; + } } } \ No newline at end of file diff --git a/mROA/Implementation/Frontend/StreamBasedFrontendInteractionModule.cs b/mROA/Implementation/Frontend/StreamBasedFrontendInteractionModule.cs deleted file mode 100644 index a94a374..0000000 --- a/mROA/Implementation/Frontend/StreamBasedFrontendInteractionModule.cs +++ /dev/null @@ -1,49 +0,0 @@ - - -// public class StreamBasedFrontendInteractionModule : IInteractionModule.IFrontendInteractionModule -// { -// public Stream? ServerStream { get; set; } -// public int ClientId { get; set; } -// -// public NetworkMessage[] UnhandledMessages() -// { -// return Array.Empty(); -// } -// -// public NetworkMessage LastMessage() -// { -// return null; -// } -// -// -// -// public async Task ReceiveMessage() -// { -// if (ServerStream is null) -// throw new IOException("Server is not connected."); -// -// const int bufferSize = ushort.MaxValue; -// -// var buffer = new byte[bufferSize]; -// if (!ServerStream.CanRead) throw new IOException("Server is not connected."); -// -// await ServerStream.ReadExactlyAsync(buffer, 0, 2); -// var len = BitConverter.ToUInt16(buffer, 0); -// await ServerStream.ReadExactlyAsync(buffer, 0, len); -// -// return buffer[..len]; -// } -// -// public void PostMessage(byte[] message) -// { -// if (ServerStream is null) -// throw new IOException("Server is not connected."); -// -// ServerStream.Write(BitConverter.GetBytes((ushort)message.Length), 0, sizeof(ushort)); -// ServerStream.Write(message, 0, message.Length); -// } -// -// public void Inject(T dependency) -// { -// } -// } \ No newline at end of file diff --git a/mROA/Implementation/IdAssignment.cs b/mROA/Implementation/IdAssignment.cs new file mode 100644 index 0000000..427124b --- /dev/null +++ b/mROA/Implementation/IdAssignment.cs @@ -0,0 +1,7 @@ +namespace mROA.Implementation +{ + public class IdAssignment + { + public int Id { get; set; } + } +} \ No newline at end of file diff --git a/mROA/Implementation/IdAssingnment.cs b/mROA/Implementation/IdAssingnment.cs deleted file mode 100644 index 5cdb944..0000000 --- a/mROA/Implementation/IdAssingnment.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace mROA.Implementation; - -public class IdAssingnment -{ - public int Id { get; set; } -} \ No newline at end of file diff --git a/mROA/Implementation/JsonSerializationToolkit.cs b/mROA/Implementation/JsonSerializationToolkit.cs index 4cc0f75..61a3c7e 100644 --- a/mROA/Implementation/JsonSerializationToolkit.cs +++ b/mROA/Implementation/JsonSerializationToolkit.cs @@ -1,59 +1,61 @@ -using System.Text.Json; +using System; +using System.Text.Json; using mROA.Abstract; -namespace mROA.Implementation; - -public class JsonSerializationToolkit : ISerializationToolkit +namespace mROA.Implementation { - public byte[] Serialize(T objectToSerialize) + public class JsonSerializationToolkit : ISerializationToolkit { - return JsonSerializer.SerializeToUtf8Bytes(objectToSerialize); - } - - public byte[] Serialize(object objectToSerialize, Type type) - { - return JsonSerializer.SerializeToUtf8Bytes(objectToSerialize, type); - } - - public T? Deserialize(byte[] rawData) - { - return JsonSerializer.Deserialize(rawData); - } - - public object? Deserialize(byte[] rawData, Type type) - { - return JsonSerializer.Deserialize(rawData, type); - } - - public T? Deserialize(Span rawData) - { - return JsonSerializer.Deserialize(rawData); - } - - public object? Deserialize(Span rawData, Type type) - { - return JsonSerializer.Deserialize(rawData, type); - } - - public T Cast(object nonCasted) - { - return nonCasted switch + public byte[] Serialize(T objectToSerialize) { - JsonElement jsonElement => jsonElement.Deserialize()!, - T casted => casted, - _ => throw new JsonException("Cannot cast object to type " + typeof(T).FullName) - }; - } + return JsonSerializer.SerializeToUtf8Bytes(objectToSerialize); + } - public object Cast(object nonCasted, Type type) - { - if (nonCasted is JsonElement jsonElement) - return jsonElement.Deserialize(type)!; + public byte[] Serialize(object objectToSerialize, Type type) + { + return JsonSerializer.SerializeToUtf8Bytes(objectToSerialize, type); + } - throw new JsonException("Cannot cast object to type " + type.FullName); - } + public T? Deserialize(byte[] rawData) + { + return JsonSerializer.Deserialize(rawData); + } - public void Inject(T dependency) - { + public object? Deserialize(byte[] rawData, Type type) + { + return JsonSerializer.Deserialize(rawData, type); + } + + public T? Deserialize(Span rawData) + { + return JsonSerializer.Deserialize(rawData); + } + + public object? Deserialize(Span rawData, Type type) + { + return JsonSerializer.Deserialize(rawData, type); + } + + public T Cast(object nonCasted) + { + return nonCasted switch + { + JsonElement jsonElement => jsonElement.Deserialize()!, + T casted => casted, + _ => throw new JsonException("Cannot cast object to type " + typeof(T).FullName) + }; + } + + public object Cast(object nonCasted, Type type) + { + if (nonCasted is JsonElement jsonElement) + return jsonElement.Deserialize(type)!; + + throw new JsonException("Cannot cast object to type " + type.FullName); + } + + public void Inject(T dependency) + { + } } } \ No newline at end of file diff --git a/mROA/Implementation/MethodInvoker.cs b/mROA/Implementation/MethodInvoker.cs new file mode 100644 index 0000000..59efb7d --- /dev/null +++ b/mROA/Implementation/MethodInvoker.cs @@ -0,0 +1,47 @@ +using System; +using mROA.Abstract; + +namespace mROA.Implementation +{ + public class MethodInvoker : IMethodInvoker + { + public bool IsVoid { get; set; } + public Type[] ParameterTypes { get; set; } = Type.EmptyTypes; + public Type? ReturnType { get; set; } + public Func Invoking { get; set; } = (_, _, _) => null; + + public object? Invoke(object instance, object?[]? parameters, object[] special) + { + return Invoking(instance, parameters, special); + } + + public Type SuitableType { get; set; } = null!; + + public static readonly IMethodInvoker Dispose = new MethodInvoker + { + IsVoid = true, + Invoking = (instance, _, _) => + { + (instance as IDisposable)?.Dispose(); + return null; + }, + SuitableType = typeof(IDisposable) + }; + } + + public class AsyncMethodInvoker : IMethodInvoker + { + public bool IsVoid { get; set; } + public Type[] ParameterTypes { get; set; } = Type.EmptyTypes; + public Type? ReturnType { get; set; } + public Type SuitableType { get; set; } + + public Action> Invoking { get; set; } = + (_, _, _, post) => { post.Invoke(null); }; + + public void Invoke(object instance, object?[]? parameters, object[] special, Action postInvokeAction) + { + Invoking(instance, parameters, special, postInvokeAction); + } + } +} \ No newline at end of file diff --git a/mROA/Implementation/MethodRepository.cs b/mROA/Implementation/MethodRepository.cs deleted file mode 100644 index d429229..0000000 --- a/mROA/Implementation/MethodRepository.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Reflection; -using mROA.Abstract; -using mROA.Implementation.Attributes; - -namespace mROA.Implementation; - -public class MethodRepository : IMethodRepository -{ - private readonly List _methods = []; - - public MethodInfo GetMethod(int id) - { - if (_methods.Count <= id) - throw new Exception("Method such registered method"); - - return _methods[id]; - } - - public int RegisterMethod(MethodInfo method) - { - _methods.Add(method); - return _methods.Count - 1; - } - - public IEnumerable GetMethods() - { - return _methods; - } - - public void CollectForAssembly(Assembly assembly) - { - var types = assembly.GetTypes().Where(i => i.IsInterface && i.GetCustomAttributes(typeof(SharedObjectInterfaceAttribute), true).Length > 0); - foreach (var type in types) - { - foreach (var method in type.GetMethods()) - RegisterMethod(method); - } - } - public void Inject(T dependency) - { - - } -} \ No newline at end of file diff --git a/mROA/Implementation/NetworkMessage.cs b/mROA/Implementation/NetworkMessage.cs index b6bb898..e610586 100644 --- a/mROA/Implementation/NetworkMessage.cs +++ b/mROA/Implementation/NetworkMessage.cs @@ -1,28 +1,28 @@ -using System.Text.Json.Serialization; +using System; +using System.Text.Json.Serialization; // ReSharper disable UnusedMember.Global -namespace mROA.Implementation; - -public sealed class NetworkMessage +namespace mROA.Implementation { - public static readonly NetworkMessage Null = new() { MessageType = EMessageType.Unknown, Id = Guid.Empty, Data = [] }; - public Guid Id { get; init; } - public EMessageType MessageType { get; init; } - public required byte[] Data { get; init; } - public bool IsValidMessage(Guid? requestId = null, EMessageType? messageType = null) + public class NetworkMessage { - return (requestId is null || Id == requestId) && - (messageType is null || MessageType == messageType); - } -} + public Guid Id { get; set; } -public enum EMessageType -{ - Unknown, - FinishedCommandExecution, - ExceptionCommandExecution, - AsyncCancelCommandExecution, - CallRequest, - IdAssigning + [JsonConverter(typeof(JsonStringEnumConverter))] + public MessageType SchemaId { get; set; } + + public byte[] Data { get; set; } + } + + public enum MessageType + { + Unknown, + FinishedCommandExecution, + ExceptionCommandExecution, + CallRequest, + IdAssigning, + CancelRequest, + EventRequest + } } \ No newline at end of file diff --git a/mROA/Implementation/NextGenerationInteractionModule.cs b/mROA/Implementation/NextGenerationInteractionModule.cs index a02183c..2aa7163 100644 --- a/mROA/Implementation/NextGenerationInteractionModule.cs +++ b/mROA/Implementation/NextGenerationInteractionModule.cs @@ -1,115 +1,103 @@ -using mROA.Abstract; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using mROA.Abstract; -namespace mROA.Implementation; - -public class NextGenerationInteractionModule : INextGenerationInteractionModule +namespace mROA.Implementation { - private ISerializationToolkit? _serialization; - public int ConnectionId { get; private set; } - public Stream BaseStream { get; set; } = Stream.Null; - private Task? _currentReceiving; - private const int BufferSize = ushort.MaxValue; - private readonly Memory _buffer = new byte[BufferSize]; - private readonly List _messageBuffer = new (128); - public NetworkMessage[] UnhandledMessages => _messageBuffer.ToArray(); - public NetworkMessage LastMessage { get; private set; } = NetworkMessage.Null; - public EventWaitHandle CurrentReceivingHandle { get; private set; } = new(true, EventResetMode.ManualReset); - - public void Inject(T dependency) + public class NextGenerationInteractionModule : INextGenerationInteractionModule { - switch (dependency) + private const int BufferSize = ushort.MaxValue; + private readonly Memory _buffer = new byte[BufferSize]; + private readonly List _messageBuffer = new(128); + private Task? _currentReceiving; + private ISerializationToolkit? _serialization; + public int ConnectionId { get; set; } + public Stream? BaseStream { get; set; } + + + public void Inject(T dependency) { - case ISerializationToolkit toolkit: - _serialization = toolkit; - break; - case IIdentityGenerator identityGenerator: - ConnectionId = identityGenerator.GetNextIdentity(); - break; - } - } - - - public async void StartInfiniteReceiving() - { - try - { - CurrentReceivingHandle = new EventWaitHandle(false, EventResetMode.ManualReset); - - await Task.Yield(); - while (true) + switch (dependency) { - var message = ReceiveMessage(); - LastMessage = message; - _messageBuffer.Add(message); - CurrentReceivingHandle.Set(); - CurrentReceivingHandle = new EventWaitHandle(false, EventResetMode.ManualReset); + case ISerializationToolkit toolkit: + _serialization = toolkit; + break; + case IIdentityGenerator identityGenerator: + ConnectionId = identityGenerator.GetNextIdentity(); + break; } } - catch (Exception) + + public Task GetNextMessageReceiving() { - // ignored + if (_currentReceiving != null) return _currentReceiving; + _currentReceiving = Task.Run(async () => await GetNextMessage()); + return _currentReceiving; + } + + public async Task PostMessage(NetworkMessage message) + { + if (BaseStream == null) + throw new NullReferenceException("BaseStream is null"); + + if (_serialization == null) + throw new NullReferenceException("Serialization toolkit is not initialized"); + + // Console.WriteLine("Sending {0}", JsonSerializer.Serialize(message)); + + + var rawMessage = _serialization.Serialize(message); + var header = BitConverter.GetBytes((ushort)rawMessage.Length).AsMemory(0, sizeof(ushort)); + + await BaseStream.WriteAsync(header); + await BaseStream.WriteAsync(rawMessage); + } + + public void HandleMessage(NetworkMessage message) + { + _messageBuffer.Remove(message); + } + + public NetworkMessage[] UnhandledMessages => _messageBuffer.ToArray(); + + public NetworkMessage? FirstByFilter(Predicate predicate) + { + return _messageBuffer.FirstOrDefault(m => predicate(m)); + } + + private async Task GetNextMessage() + { + if (BaseStream == null) + throw new NullReferenceException("BaseStream is null"); + + if (_serialization == null) + throw new NullReferenceException("Serialization toolkit is null"); + + + // Console.WriteLine("Receiving message"); + var firstBit = (byte)BaseStream.ReadByte(); + var secondBit = (byte)BaseStream.ReadByte(); + + var len = BitConverter.ToUInt16(new[] { firstBit, secondBit }); + var localSpan = _buffer[..len]; + + await BaseStream.ReadExactlyAsync(localSpan); + + // Console.WriteLine("Receiving {0}", Encoding.Default.GetString(_buffer[..len])); + + var message = _serialization.Deserialize(localSpan.Span); +#if TRACE + Console.WriteLine($"{DateTime.Now.TimeOfDay} Received Message {message.Id} - {message.SchemaId}"); + TransmissionConfig.TotalTransmittedBytes += len; + Console.WriteLine($"Total recieced bytes are {TransmissionConfig.TotalTransmittedBytes}"); +#endif + _messageBuffer.Add(message); + _currentReceiving = Task.Run(async () => await GetNextMessage()); + + return message; } } - - - - public Task GetNextMessageReceiving() - { - if (_currentReceiving != null) return _currentReceiving; - _currentReceiving = Task.Run(GetNextMessage); - return _currentReceiving; - - } - - public async Task PostMessage(NetworkMessage message) - { - if (BaseStream == null) - throw new NullReferenceException("BaseStream is null"); - - if (_serialization == null) - throw new NullReferenceException("Serialization toolkit is not initialized"); - - // Console.WriteLine("Sending {0}", JsonSerializer.Serialize(message)); - - - var rawMessage = _serialization.Serialize(message); - await BaseStream.WriteAsync(BitConverter.GetBytes((ushort)rawMessage.Length).AsMemory(0, sizeof(ushort))); - await BaseStream.WriteAsync(rawMessage); - } - - public void HandleMessage(NetworkMessage message) - { - _messageBuffer.Remove(message); - } - - public NetworkMessage FirstByFilter(Predicate predicate) - { - return _messageBuffer.FirstOrDefault(m => predicate(m)) ?? NetworkMessage.Null; - } - - private NetworkMessage GetNextMessage() - { - if (BaseStream == null) - throw new NullReferenceException("BaseStream is null"); - - if (_serialization == null) - throw new NullReferenceException("Serialization toolkit is null"); - - var message = ReceiveMessage(); - - _messageBuffer.Add(message); - _currentReceiving = Task.Run(GetNextMessage); - - return message; - } - - private NetworkMessage ReceiveMessage() - { - var len = BitConverter.ToUInt16([(byte)BaseStream.ReadByte(), (byte)BaseStream.ReadByte()]); - var localSpan = _buffer.Span.Slice(0, len); - BaseStream.ReadExactly(localSpan); - - var message = _serialization!.Deserialize(localSpan); - return message!; - } } \ No newline at end of file diff --git a/mROA/Implementation/RemoteContextRepository.cs b/mROA/Implementation/RemoteContextRepository.cs index c470092..8c8ded5 100644 --- a/mROA/Implementation/RemoteContextRepository.cs +++ b/mROA/Implementation/RemoteContextRepository.cs @@ -1,62 +1,75 @@ -using System.Collections.Frozen; +using System; +using System.Collections.Generic; +using System.Linq; using mROA.Abstract; -namespace mROA.Implementation; - -public class RemoteContextRepository : IContextRepository +namespace mROA.Implementation { - private IRepresentationModuleProducer? _representationProducer; - public static FrozenDictionary RemoteTypes = FrozenDictionary.Empty; - public int ResisterObject(object o) + public class RemoteContextRepository : IContextRepository { - throw new NotSupportedException(); - } + private List _producedRemoteEndpoints = new(); + public static Dictionary RemoteTypes = new(); + private IRepresentationModuleProducer? _representationProducer; - public void ClearObject(int id) - { - throw new NotSupportedException(); - } + public int HostId { get; set; } - public object GetObject(int id) - { - throw new NotSupportedException(); - } - - public T GetObject(int id) - { - if (_representationProducer == null) - throw new NullReferenceException("representation producer is not initialized"); - - if (!RemoteTypes.TryGetValue(typeof(T), out var remoteType)) throw new NotSupportedException(); - var remote = (T)Activator.CreateInstance(remoteType, id, _representationProducer.Produce(TransmissionConfig.OwnershipRepository.GetOwnershipId()))!; - return remote; - } - public T GetSingleObject() - { - var obj = GetSingleObject(typeof(T)); - return (T)obj; - } - - public object GetSingleObject(Type type) - { - if (_representationProducer == null) - throw new NullReferenceException("representation producer is not initialized"); - - return Activator.CreateInstance(RemoteTypes[type], -1, _representationProducer.Produce(TransmissionConfig.OwnershipRepository.GetOwnershipId()))!; - } - - public int GetObjectIndex(object o) - { - if (o is RemoteObjectBase remote) + public int ResisterObject(object o, IEndPointContext context) { - return remote.Id; + throw new NotSupportedException(); } - throw new NotSupportedException(); - } - public void Inject(T dependency) - { - if (dependency is IRepresentationModuleProducer serialisationModule) - _representationProducer = serialisationModule; + public void ClearObject(ComplexObjectIdentifier id) + { + throw new NotSupportedException(); + } + + public T GetObject(ComplexObjectIdentifier id) + { + var index = _producedRemoteEndpoints.Find(i => i.Identifier.Equals(id)); + if (index is not null) + return (T)(index as object); + 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(TransmissionConfig.OwnershipRepository.GetOwnershipId()); + var remote = (T)Activator.CreateInstance(remoteType, id.ContextId, + representationModule)!; + + _producedRemoteEndpoints.Add((remote as RemoteObjectBase)!); + + return remote; + } + + public object GetSingleObject(Type type, int ownerId) + { + if (_representationProducer == null) + throw new NullReferenceException("representation producer is not initialized"); + + var representationModule = + _representationProducer.Produce(TransmissionConfig.OwnershipRepository.GetOwnershipId()); + + _producedRemoteEndpoints.Add((Activator.CreateInstance(RemoteTypes[type], -1, + representationModule) as RemoteObjectBase)!); + + return _producedRemoteEndpoints.Last(); + } + + public int GetObjectIndex(object o, IEndPointContext context) + { + if (o is RemoteObjectBase remote) + { + return remote.Id; + } + + throw new NotSupportedException(); + } + + public void Inject(T dependency) + { + if (dependency is IRepresentationModuleProducer serialisationModule) + _representationProducer = serialisationModule; + } } } \ No newline at end of file diff --git a/mROA/Implementation/RemoteObjectBase.cs b/mROA/Implementation/RemoteObjectBase.cs index af66e18..a35e734 100644 --- a/mROA/Implementation/RemoteObjectBase.cs +++ b/mROA/Implementation/RemoteObjectBase.cs @@ -1,61 +1,151 @@ -using mROA.Abstract; +using System; +using System.Threading; +using System.Threading.Tasks; +using mROA.Abstract; using mROA.Implementation.CommandExecution; // ReSharper disable UnusedMember.Global -namespace mROA.Implementation; - -public abstract class RemoteObjectBase(int id, IRepresentationModule representationModule) +namespace mROA.Implementation { - public int Id => id; - public int OwnerId => representationModule.Id; - - protected async Task GetResultAsync(int methodId, object? parameter = default) + public abstract class RemoteObjectBase : IDisposable { - var request = new DefaultCallRequest - { CommandId = methodId, ObjectId = id, Parameter = parameter, ParameterType = parameter?.GetType() }; - await representationModule.PostCallMessageAsync(request.Id, EMessageType.CallRequest, request); - - var localTokenSource = new CancellationTokenSource(); - - var successResponse = - representationModule.GetMessageAsync>(request.Id, - EMessageType.FinishedCommandExecution, localTokenSource.Token); - - var errorResponse = - representationModule.GetMessageAsync(request.Id, - EMessageType.ExceptionCommandExecution, localTokenSource.Token); - - Task.WaitAny(successResponse, errorResponse); - - if (successResponse.IsCompletedSuccessfully) + protected bool Equals(RemoteObjectBase other) { - await localTokenSource.CancelAsync(); - return successResponse.Result.Result!; + return _identifier.Equals(other._identifier); } - await localTokenSource.CancelAsync(); - throw errorResponse.Result.GetException(); - } + public override bool Equals(object? obj) + { + if (obj is null) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((RemoteObjectBase)obj); + } - protected async Task CallAsync(int methodId, object? parameter = default) - { - var request = new DefaultCallRequest - { CommandId = methodId, ObjectId = id, Parameter = parameter, ParameterType = parameter?.GetType() }; - await representationModule.PostCallMessageAsync(request.Id, EMessageType.CallRequest, request); + public override int GetHashCode() + { + return HashCode.Combine(_identifier.GetHashCode(), _identifier.ContextId); + } - var successResponse = - representationModule.GetMessageAsync( - messageType: EMessageType.FinishedCommandExecution, requestId: request.Id); - var errorResponse = - representationModule.GetMessageAsync( - messageType: EMessageType.ExceptionCommandExecution, requestId: request.Id); + private readonly ComplexObjectIdentifier _identifier; + private readonly IRepresentationModule _representationModule; - Task.WaitAny(successResponse, errorResponse); + protected RemoteObjectBase(int id, IRepresentationModule representationModule) + { + _identifier = new ComplexObjectIdentifier { ContextId = id, OwnerId = representationModule.Id }; + _representationModule = representationModule; + } - if (successResponse.IsCompletedSuccessfully) - return; + public int Id => _identifier.ContextId; + public int OwnerId => _identifier.OwnerId; + public ComplexObjectIdentifier Identifier => _identifier; - throw errorResponse.Result.GetException(); + public void Dispose() + { + if (_identifier.IsStatic) + return; + CallAsync(-1).Wait(); + } + + protected async Task GetResultAsync(int methodId, object?[]? parameters = null, + CancellationToken cancellationToken = default) + { + var request = new DefaultCallRequest + { + CommandId = methodId, ObjectId = _identifier, Parameters = parameters + }; + + await _representationModule.PostCallMessageAsync(request.Id, MessageType.CallRequest, request); + + var localTokenSource = new CancellationTokenSource(); + + var successResponse = + _representationModule.GetMessageAsync>(request.Id, + MessageType.FinishedCommandExecution, + localTokenSource.Token); + var errorResponse = + _representationModule.GetMessageAsync(requestId: request.Id, + MessageType.ExceptionCommandExecution, localTokenSource.Token); + + cancellationToken.Register(async () => + { +#if TRACE + Console.WriteLine("Cancelling task"); +#endif + await _representationModule.PostCallMessageAsync(request.Id, MessageType.CancelRequest, + new CancelRequest + { + Id = request.Id + }); + localTokenSource.Cancel(); + }); + + Task.WaitAny(new Task[] + { + successResponse, errorResponse + }, cancellationToken); + + if (successResponse.IsCompletedSuccessfully) + { + localTokenSource.Cancel(); + return successResponse.Result.Result!; + } + + localTokenSource.Cancel(); + throw errorResponse.Result.GetException(); + } + + protected async Task CallAsync(int methodId, object?[]? parameters = null, + CancellationToken cancellationToken = default) + { + var request = new DefaultCallRequest + { + CommandId = methodId, ObjectId = _identifier, Parameters = parameters + }; + await _representationModule.PostCallMessageAsync(request.Id, MessageType.CallRequest, request); + + var localTokenSource = new CancellationTokenSource(); + + var successResponse = + _representationModule.GetMessageAsync(request.Id, + MessageType.FinishedCommandExecution, + localTokenSource.Token); + var errorResponse = + _representationModule.GetMessageAsync(requestId: request.Id, + MessageType.ExceptionCommandExecution, localTokenSource.Token); + + cancellationToken.Register(async () => + { +#if TRACE + Console.WriteLine("Cancelling task"); +#endif + await _representationModule.PostCallMessageAsync(request.Id, MessageType.CancelRequest, + new CancelRequest + { + Id = request.Id + }); + localTokenSource.Cancel(); + }); + + Task.WaitAny(new Task[] + { + errorResponse, successResponse + }, cancellationToken); + +#if TRACE + Console.WriteLine($"Handling message"); +#endif + if (successResponse.IsCompletedSuccessfully) + return; + + if (errorResponse.IsCompletedSuccessfully) + throw errorResponse.Result.GetException(); + } + + public override string ToString() + { + return _identifier.ToString(); + } } } \ No newline at end of file diff --git a/mROA/Implementation/RemoteObjectFactory.cs b/mROA/Implementation/RemoteObjectFactory.cs new file mode 100644 index 0000000..fad21e3 --- /dev/null +++ b/mROA/Implementation/RemoteObjectFactory.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using mROA.Abstract; + +namespace mROA.Implementation +{ + public class RemoteObjectFactory : IRemoteObjectFactory + { + public static Dictionary RemoteTypes = new(); + private IRepresentationModuleProducer? _representationProducer; + + public T Produce(ComplexObjectIdentifier id) + { + 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(TransmissionConfig.OwnershipRepository.GetOwnershipId()); + var remote = (T)Activator.CreateInstance(remoteType, id.ContextId, + representationModule)!; + return remote; + } + + public void Inject(T dependency) + { + if (dependency is IRepresentationModuleProducer serialisationModule) + _representationProducer = serialisationModule; + } + } +} \ No newline at end of file diff --git a/mROA/Implementation/RepresentationModule.cs b/mROA/Implementation/RepresentationModule.cs index 6c3e08f..ffd1b87 100644 --- a/mROA/Implementation/RepresentationModule.cs +++ b/mROA/Implementation/RepresentationModule.cs @@ -1,115 +1,112 @@ -using mROA.Abstract; +using System; +using System.Threading; +using System.Threading.Tasks; +using mROA.Abstract; -namespace mROA.Implementation; - -public class RepresentationModule : IRepresentationModule +namespace mROA.Implementation { - private ISerializationToolkit? _serialization; - private INextGenerationInteractionModule? _interaction; - - public void Inject(T dependency) + public class RepresentationModule : IRepresentationModule { - switch (dependency) + private INextGenerationInteractionModule? _interaction; + private ISerializationToolkit? _serialization; + + public void Inject(T dependency) { - case ISerializationToolkit toolkit: - _serialization = toolkit; - break; - case INextGenerationInteractionModule interactionModule: - _interaction = interactionModule; - break; - } - } - - public int Id => (_interaction ?? throw new NullReferenceException("Interaction is not initialized")).ConnectionId; - - public async Task GetMessageAsync(Guid? requestId, EMessageType? messageType, - CancellationToken token = default) - { - if (_serialization == null) - throw new NullReferenceException("Serialization toolkit is not initialized"); - - var raw = await GetRawMessage(m => m.IsValidMessage(requestId, messageType), token); - return _serialization.Deserialize(raw)!; - } - - public T GetMessage(Guid? requestId = null, EMessageType? messageType = null) - { - return GetMessage(m => m.IsValidMessage(requestId, messageType)); - } - - public T GetMessage(Predicate filter) - { - if (_serialization == null) - throw new NullReferenceException("Serialization toolkit is not initialized"); - var raw = GetRawMessage(filter).GetAwaiter().GetResult(); - return _serialization.Deserialize(raw)!; - } - - public async Task GetRawMessage(Predicate filter, CancellationToken token = default) - { - await Task.Yield(); - - if (_interaction == null) - throw new NullReferenceException("Interaction toolkit is not initialized"); - - if (filter(_interaction.LastMessage)) - { - _interaction.HandleMessage(_interaction.LastMessage); - return _interaction.LastMessage.Data; + switch (dependency) + { + case ISerializationToolkit toolkit: + _serialization = toolkit; + break; + case INextGenerationInteractionModule interactionModule: + _interaction = interactionModule; + break; + } } - var message = _interaction.FirstByFilter(filter); + public int Id => (_interaction ?? throw new NullReferenceException("Interaction is not initialized")) + .ConnectionId; - if (message != NetworkMessage.Null) + public async Task GetMessageAsync(Guid? requestId, MessageType? messageType, + CancellationToken token = default) { - _interaction.HandleMessage(message); - return message.Data; + if (_serialization == null) + throw new NullReferenceException("Serialization toolkit is not initialized"); + + var rawMessage = await GetRawMessage(requestId, messageType, token); + return _serialization.Deserialize(rawMessage)!; } - - while (!token.IsCancellationRequested) + public T GetMessage(Guid? requestId = null, MessageType? messageType = null) { - var handle = _interaction.CurrentReceivingHandle; - handle.WaitOne(); + if (_serialization == null) + throw new NullReferenceException("Serialization toolkit is not initialized"); - message = _interaction.LastMessage; - - if (!filter(message)) - continue; - - message = _interaction.LastMessage; - - _interaction.HandleMessage(message); - return message.Data; + var rawMessage = GetRawMessage(requestId, messageType).GetAwaiter().GetResult(); + return _serialization.Deserialize(rawMessage)!; } - return []; - } + public async Task GetRawMessage(Guid? requestId = null, MessageType? messageType = null, + CancellationToken token = default) + { + if (_interaction == null) + throw new NullReferenceException("Interaction toolkit is not initialized"); - public async Task PostCallMessageAsync(Guid id, EMessageType eMessageType, T payload) where T : notnull - { - await PostCallMessageAsync(id, eMessageType, payload, typeof(T)); - } + var fromBuffer = + _interaction.FirstByFilter(message => + (requestId is null || message.Id == requestId) && + (messageType is null || message.SchemaId == messageType)); - public async Task PostCallMessageAsync(Guid id, EMessageType eMessageType, object payload, Type payloadType) - { - if (_interaction == null) - throw new NullReferenceException("Interaction toolkit is not initialized"); - if (_serialization == null) - throw new NullReferenceException("Serialization toolkit is not initialized"); + if (fromBuffer == null) + { + while (token.IsCancellationRequested == false) + { + var message = await _interaction.GetNextMessageReceiving(); + if ((requestId is not null && message.Id != requestId) || + (messageType is not null && message.SchemaId != messageType)) + continue; - var message = new NetworkMessage - { Id = id, MessageType = eMessageType, Data = _serialization.Serialize(payload, payloadType) }; - await _interaction.PostMessage(message); - } + _interaction.HandleMessage(message); + return message.Data; + } + } - public void PostCallMessage(Guid id, EMessageType eMessageType, T payload) where T : notnull - { - PostCallMessageAsync(id, eMessageType, payload).GetAwaiter().GetResult(); - } + if (fromBuffer == null) + { + return Array.Empty(); + } - public void PostCallMessage(Guid id, EMessageType eMessageType, object payload, Type payloadType) - { - PostCallMessageAsync(id, eMessageType, payload, payloadType).GetAwaiter().GetResult(); + _interaction.HandleMessage(fromBuffer); + return fromBuffer.Data; + } + + public async Task PostCallMessageAsync(Guid id, MessageType messageType, T payload) where T : notnull + { + await PostCallMessageAsync(id, messageType, payload, typeof(T)); + } + + public async Task PostCallMessageAsync(Guid id, MessageType messageType, object payload, Type payloadType) + { + if (_interaction == null) + throw new NullReferenceException("Interaction toolkit is not initialized"); + if (_serialization == null) + throw new NullReferenceException("Serialization toolkit is not initialized"); +#if TRACE + Console.WriteLine($"{DateTime.Now.TimeOfDay} Posting message: {id} - {messageType} to {Id}"); +#endif + + var serialized = _serialization.Serialize(payload, payloadType); + await _interaction.PostMessage(new NetworkMessage + { Id = id, SchemaId = messageType, Data = serialized }); + } + + public void PostCallMessage(Guid id, MessageType messageType, T payload) where T : notnull + { + PostCallMessageAsync(id, messageType, payload).GetAwaiter().GetResult(); + } + + public void PostCallMessage(Guid id, MessageType messageType, object payload, Type payloadType) + { + PostCallMessageAsync(id, messageType, payload, payloadType).GetAwaiter().GetResult(); + } } } \ No newline at end of file diff --git a/mROA/Implementation/RequestContext.cs b/mROA/Implementation/RequestContext.cs new file mode 100644 index 0000000..9daf956 --- /dev/null +++ b/mROA/Implementation/RequestContext.cs @@ -0,0 +1,16 @@ +using System; + +namespace mROA.Implementation +{ + public sealed class RequestContext + { + public int OwnerId { get; } + public Guid RequestId { get; } + + public RequestContext(Guid requestId, int ownerId) + { + RequestId = requestId; + OwnerId = ownerId; + } + } +} \ No newline at end of file diff --git a/mROA/Implementation/SharedObject.cs b/mROA/Implementation/SharedObject.cs deleted file mode 100644 index 644b8f4..0000000 --- a/mROA/Implementation/SharedObject.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System.Text.Json.Serialization; -using mROA.Abstract; -// ReSharper disable UnusedMember.Global -#pragma warning disable CS8618, CS9264 - -namespace mROA.Implementation; - -public static class TransmissionConfig -{ - 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 class SharedObject where T : notnull -{ - private IContextRepository GetDefaultContextRepository() => - (OwnerId == TransmissionConfig.OwnershipRepository.GetHostOwnershipId() - ? TransmissionConfig.RealContextRepository - : TransmissionConfig.RemoteEndpointContextRepository) ?? - throw new NullReferenceException( - "DefaultContextRepository was not defined"); - - private int _contextId = -2; - private int _ownerId = -1; - - public int OwnerId - { - get - { - _ownerId = _ownerId == -1 ? TransmissionConfig.OwnershipRepository.GetOwnershipId() : _ownerId; - return _ownerId; - } - init => _ownerId = value; - } - - // ReSharper disable once MemberCanBePrivate.Global - public int ContextId - { - // ReSharper disable once UnusedMember.Global - get - { - if (_contextId != -2) - return _contextId; - - _contextId = TransmissionConfig.RealContextRepository.GetObjectIndex(Value); - return _contextId; - } - init - { - _contextId = value; - Value = GetDefaultContextRepository().GetObject(_contextId)!; - } - } - - [JsonIgnore] public T Value { get; private init; } - - // 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) - { - _ownerId = ro.OwnerId; - _contextId = ro.Id; - } - else - _ownerId = TransmissionConfig.OwnershipRepository.GetHostOwnershipId(); - } - - 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..0fe0450 --- /dev/null +++ b/mROA/Implementation/SharedObjectShell.cs @@ -0,0 +1,102 @@ +using System; +using System.Text.Json.Serialization; +using mROA.Abstract; +using mROA.Implementation.Attributes; + +// ReSharper disable UnusedMember.Global +#pragma warning disable CS8618, CS9264 + +namespace mROA.Implementation +{ + public interface ISharedObjectShell + { + // ReSharper disable once UnusedMemberInSuper.Global + IEndPointContext EndPointContext { get; set; } + ComplexObjectIdentifier Identifier { get; set; } + object UniversalValue { get; set; } + } + + public class SharedObjectShellShell : ISharedObjectShell where T : notnull + { + private ComplexObjectIdentifier _identifier = ComplexObjectIdentifier.Null; + + private T _value; + + // ReSharper disable once MemberCanBePrivate.Global + // ReSharper disable once UnusedMember.Global + public SharedObjectShellShell() + { + } + + // ReSharper disable once UnusedMember.Global + // ReSharper disable once MemberCanBePrivate.Global + public SharedObjectShellShell(T value) + { + Value = value; + } + + [JsonIgnore] + [SerializationIgnore] + // ReSharper disable once MemberCanBePrivate.Global + public T Value + { + get => _value; + set + { + _value = value; + + if (value is RemoteObjectBase ro) + { + _identifier = ro.Identifier; + } + else + { + _identifier.OwnerId = EndPointContext.OwnerId; + _identifier.ContextId = EndPointContext.RealRepository.GetObjectIndex(Value, EndPointContext); + } + } + } + + [SerializationIgnore] + [JsonIgnore] + public IEndPointContext EndPointContext { get; set; } = new EndPointContext + { + RealRepository = TransmissionConfig.RealContextRepository, + RemoteRepository = TransmissionConfig.RemoteEndpointContextRepository, + HostId = TransmissionConfig.OwnershipRepository.GetHostOwnershipId(), + OwnerFunc = TransmissionConfig.OwnershipRepository.GetOwnershipId + }; + + public ComplexObjectIdentifier Identifier + { + get + { + _identifier.OwnerId = _identifier.OwnerId == 0 ? EndPointContext.OwnerId : _identifier.OwnerId; + return _identifier; + } + set + { + _identifier = value; + Value = GetDefaultContextRepository().GetObject(Identifier); + } + } + + public object UniversalValue + { + get => _value; + set => _value = (T)value; + } + + private IContextRepository GetDefaultContextRepository() => + (_identifier.OwnerId == EndPointContext.HostId + ? EndPointContext.RealRepository + : EndPointContext.RemoteRepository) ?? + throw new NullReferenceException( + "DefaultContextRepository was not defined"); + + 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/StaticRepresentationModuleProducer.cs b/mROA/Implementation/StaticRepresentationModuleProducer.cs index 8b0632a..8aeabd4 100644 --- a/mROA/Implementation/StaticRepresentationModuleProducer.cs +++ b/mROA/Implementation/StaticRepresentationModuleProducer.cs @@ -1,21 +1,23 @@ -using mROA.Abstract; +using System; +using mROA.Abstract; -namespace mROA.Implementation; - -public class StaticRepresentationModuleProducer : IRepresentationModuleProducer +namespace mROA.Implementation { - private IRepresentationModule? _representationModule; - - public IRepresentationModule Produce(int ownership) + public class StaticRepresentationModuleProducer : IRepresentationModuleProducer { - if (_representationModule == null) - throw new NullReferenceException("The representation module is not initialized."); - return _representationModule; - } - - public void Inject(T dependency) - { - if (dependency is IRepresentationModule serialisationModule) - _representationModule = serialisationModule; + private IRepresentationModule? _representationModule; + + public IRepresentationModule Produce(int ownership) + { + if (_representationModule == null) + throw new NullReferenceException("The representation module is not initialized."); + return _representationModule; + } + + public void Inject(T dependency) + { + if (dependency is IRepresentationModule serialisationModule) + _representationModule = serialisationModule; + } } } \ 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/LegacyExtentions.cs b/mROA/LegacyExtentions.cs new file mode 100644 index 0000000..ed05e04 --- /dev/null +++ b/mROA/LegacyExtentions.cs @@ -0,0 +1,43 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace mROA +{ + public static class LegacyExtentions + { + public static async ValueTask ReadExactlyAsync(this Stream stream, byte[] buffer, int offset, int count) + { + return await stream.ReadAtLeastAsyncCore(buffer.AsMemory(offset, count), count, true, default); + } + + public static ValueTask ReadExactlyAsync(this Stream stream, Memory buffer, + CancellationToken cancellationToken = default(CancellationToken)) + { + return stream.ReadAtLeastAsyncCore(buffer, buffer.Length, true, cancellationToken); + } + + private static async ValueTask ReadAtLeastAsyncCore(this Stream stream, + Memory buffer, + int minimumBytes, + bool throwOnEndOfStream, + CancellationToken cancellationToken) + { + int totalRead; + int num; + for (totalRead = 0; totalRead < minimumBytes; totalRead += num) + { + num = await stream.ReadAsync(buffer.Slice(totalRead), cancellationToken).ConfigureAwait(false); + if (num == 0) + { + if (throwOnEndOfStream) + throw new EndOfStreamException(); + return totalRead; + } + } + + return totalRead; + } + } +} \ No newline at end of file diff --git a/mROA/mROA.csproj b/mROA/mROA.csproj index 2ab4205..5c9aea9 100644 --- a/mROA/mROA.csproj +++ b/mROA/mROA.csproj @@ -1,8 +1,7 @@  - net9.0 - enable + netstandard2.1 enable mROA 2.0.0 @@ -12,6 +11,21 @@ https://github.com/YaslePoy/mROA git RPC + 9 + Debug;Release + AnyCPU + + + + + + + + + + + +