From 5032fa28e8685e4e7ad1f336cffd390ac5bbb971 Mon Sep 17 00:00:00 2001 From: Mikhail Mitrofanov Date: Wed, 12 Feb 2025 12:34:54 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=B2=D0=BE=D0=B4=20?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B4=D0=B0=D1=87=D1=83?= =?UTF-8?q?=20=D1=81=20=D0=BE=D0=B1=D0=BE=D0=B7=D0=BD=D0=B0=D1=87=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=D0=BC=20=D1=81=D1=85=D0=B5=D0=BC=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Example.Backend/LoadTestImp.cs | 15 +++++++ Example.Frontend/Example.Frontend.csproj | 2 +- Example.Frontend/Program.cs | 2 +- Example.Shared/Example.Shared.csproj | 3 +- Example.Shared/ILoadTest.cs | 3 ++ mROA.Codegen/mROASourceGenerator.cs | 16 ++++---- mROA/Abstract/IInteractionModule.cs | 2 +- mROA/Abstract/ISerialisationModule.cs | 4 +- .../Backend/JsonSerialisationModule.cs | 41 ++++++++++++------- .../Backend/NetworkGatewayModule.cs | 2 +- .../Backend/StreamBasedInteractionModule.cs | 2 +- .../ExceptionCommandExecution.cs | 11 +++++ .../FinalCommandExecution.cs | 14 ------- .../TypedFinalCommandExecution.cs | 9 ++++ ...JsonFrontendCallbackSerializationModule.cs | 24 +++++++++++ .../JsonFrontendSerialisationModule.cs | 38 +++++++++-------- mROA/Implementation/NetworkMessage.cs | 15 +++++++ mROA/mROA.csproj | 2 +- 18 files changed, 143 insertions(+), 62 deletions(-) create mode 100644 mROA/Implementation/CommandExecution/ExceptionCommandExecution.cs rename mROA/Implementation/{ => CommandExecution}/FinalCommandExecution.cs (56%) create mode 100644 mROA/Implementation/CommandExecution/TypedFinalCommandExecution.cs create mode 100644 mROA/Implementation/Frontend/Callback/JsonFrontendCallbackSerializationModule.cs create mode 100644 mROA/Implementation/NetworkMessage.cs diff --git a/Example.Backend/LoadTestImp.cs b/Example.Backend/LoadTestImp.cs index 35c698a..f75b2bb 100644 --- a/Example.Backend/LoadTestImp.cs +++ b/Example.Backend/LoadTestImp.cs @@ -11,4 +11,19 @@ public class LoadTestImp : ILoadTest { return last + 1; } + + public int Last(int next) + { + return next - 1; + } + + public void C() + { + throw new NotImplementedException(); + } + + public void A() + { + throw new NotImplementedException(); + } } \ No newline at end of file diff --git a/Example.Frontend/Example.Frontend.csproj b/Example.Frontend/Example.Frontend.csproj index 62581cc..f59d7cc 100644 --- a/Example.Frontend/Example.Frontend.csproj +++ b/Example.Frontend/Example.Frontend.csproj @@ -8,7 +8,7 @@ - + diff --git a/Example.Frontend/Program.cs b/Example.Frontend/Program.cs index f79c81f..034cd25 100644 --- a/Example.Frontend/Program.cs +++ b/Example.Frontend/Program.cs @@ -16,7 +16,6 @@ mixer.Modules.Add(new FrontendContextRepository()); mixer.Modules.Add(new JsonFrontendSerialisationModule()); mixer.Modules.Add(new StreamBasedFrontendInteractionModule()); mixer.Modules.Add(new NetworkFrontendBridge(new IPEndPoint(IPAddress.Loopback, 4567))); - mixer.Build(); mixer.GetModule().Connect(); @@ -25,6 +24,7 @@ var context = mixer.GetModule(); var factory = context.GetSingleObject(typeof(IPrinterFactory)) as IPrinterFactory; + var printer = factory.Create("Test"); var name = printer.Value.GetName(); Console.WriteLine("Printer name : {0}", name); diff --git a/Example.Shared/Example.Shared.csproj b/Example.Shared/Example.Shared.csproj index efa137d..40f085d 100644 --- a/Example.Shared/Example.Shared.csproj +++ b/Example.Shared/Example.Shared.csproj @@ -7,8 +7,9 @@ - + + diff --git a/Example.Shared/ILoadTest.cs b/Example.Shared/ILoadTest.cs index f8206bc..6da94c6 100644 --- a/Example.Shared/ILoadTest.cs +++ b/Example.Shared/ILoadTest.cs @@ -7,4 +7,7 @@ namespace Example.Shared; public interface ILoadTest { int Next(int last); + int Last(int next); + void C(); + void A(); } \ No newline at end of file diff --git a/mROA.Codegen/mROASourceGenerator.cs b/mROA.Codegen/mROASourceGenerator.cs index dc1a337..61a597c 100644 --- a/mROA.Codegen/mROASourceGenerator.cs +++ b/mROA.Codegen/mROASourceGenerator.cs @@ -77,14 +77,15 @@ namespace {Namespace} /// /// 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. + /// Nodes annotated with the [Report] attribute that trigger the generate action. private void GenerateCode(SourceProductionContext context, Compilation compilation, - ImmutableArray classDeclarations) + ImmutableArray classes) { var methods = new List<(string, IMethodSymbol)>(); var frontendContextRepo = new List(); // Go through all filtered class declarations. - foreach (var classDeclarationSyntax in classDeclarations) + var declarations = classes.ToList().OrderBy(i => i.Identifier.Text).ToList(); + foreach (var classDeclarationSyntax in declarations) { // We need to get semantic model of the class to retrieve metadata. var semanticModel = compilation.GetSemanticModel(classDeclarationSyntax.SyntaxTree); @@ -102,7 +103,7 @@ namespace {Namespace} // Go through all class members with a particular type (property) to generate method lines. var methodBody = classSymbol.GetMembers() - .OfType(); + .OfType().OrderBy(i => i.Name); var originalName = className; // Build up the source code @@ -204,7 +205,7 @@ partial class {className} (int id, ISerialisationModule.IFrontendSerialisationMo 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 = @$"// using System.Collections.Generic; using System.Reflection; @@ -212,10 +213,10 @@ using mROA.Abstract; namespace mROA.Codegen; -public class CoCodegenMethodRepository :IMethodRepository +public class CoCodegenMethodRepository : IMethodRepository {{ private readonly List _methods = [ - {string.Join(", \r\n\t\t", methodsStringed)} + {string.Join(", // test comment\r\n\t\t", methodsStringed)} ]; public MethodInfo GetMethod(int id) {{ @@ -310,6 +311,5 @@ public class FrontendContextRepository : IContextRepository "; context.AddSource("FrontendContextRepository.g.cs", SourceText.From(fronendRepoCode, Encoding.UTF8)); } - } } \ No newline at end of file diff --git a/mROA/Abstract/IInteractionModule.cs b/mROA/Abstract/IInteractionModule.cs index 8cbbf4b..635f20d 100644 --- a/mROA/Abstract/IInteractionModule.cs +++ b/mROA/Abstract/IInteractionModule.cs @@ -3,7 +3,7 @@ namespace mROA.Abstract; public interface IInteractionModule : IInjectableModule { void SendTo(int clientId, byte[] message); - void RegisterSourse(Stream stream); + void RegisterSource(Stream stream); public interface IFrontendInteractionModule : IInjectableModule { public Task ReceiveMessage(); diff --git a/mROA/Abstract/ISerialisationModule.cs b/mROA/Abstract/ISerialisationModule.cs index ea548ac..b819ba5 100644 --- a/mROA/Abstract/ISerialisationModule.cs +++ b/mROA/Abstract/ISerialisationModule.cs @@ -4,8 +4,8 @@ namespace mROA.Abstract; public interface ISerialisationModule : IInjectableModule { - void HandleIncomingRequest(int clientId, byte[] command); - void PostResponse(ICommandExecution call); + void HandleIncomingRequest(int clientId, byte[] message); + void PostResponse(NetworkMessage message, int clientId); public interface IFrontendSerialisationModule : IInjectableModule { Task GetNextCommandExecution(Guid requestId) where T : ICommandExecution; diff --git a/mROA/Implementation/Backend/JsonSerialisationModule.cs b/mROA/Implementation/Backend/JsonSerialisationModule.cs index 3e652cb..731b971 100644 --- a/mROA/Implementation/Backend/JsonSerialisationModule.cs +++ b/mROA/Implementation/Backend/JsonSerialisationModule.cs @@ -1,4 +1,5 @@ -using System.Text; +using System.Net.Sockets; +using System.Text; using System.Text.Json; using mROA.Abstract; @@ -10,26 +11,39 @@ public class JsonSerialisationModule : ISerialisationModule private IExecuteModule? _executeModule; private IMethodRepository? _methodRepository; - public void HandleIncomingRequest(int clientId, byte[] command) + public void HandleIncomingRequest(int clientId, byte[] message) { - DefaultCallRequest request = JsonSerializer.Deserialize(command)!; + NetworkMessage input = JsonSerializer.Deserialize(message)!; - if (request.Parameter is not null) + if (input.SchemaId == MessageType.CallRequest) { - var parameter = _methodRepository!.GetMethod(request.CommandId).GetParameters().First().ParameterType; - request.Parameter = ((JsonElement)request.Parameter).Deserialize(parameter); - } + var command = JsonSerializer.Deserialize(input.Data); + if (command.Parameter is not null) + { + var parameter = _methodRepository!.GetMethod(command.CommandId).GetParameters().First().ParameterType; + command.Parameter = ((JsonElement)command.Parameter).Deserialize(parameter); + } - var response = _executeModule!.Execute(request); - response.ClientId = clientId; - PostResponse(response); + var response = _executeModule!.Execute(command); + 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); + } } - public void PostResponse(ICommandExecution call) + public void PostResponse(NetworkMessage message, int clientId) { - var texted = JsonSerializer.Serialize(call, call.GetType()); + var texted = JsonSerializer.Serialize(message); var binary = Encoding.UTF8.GetBytes(texted); - _dataSource!.SendTo(call.ClientId, binary); + _dataSource!.SendTo(clientId, binary); } public void Inject(T dependency) @@ -41,5 +55,4 @@ public class JsonSerialisationModule : ISerialisationModule if (dependency is IMethodRepository methodRepository) _methodRepository = methodRepository; } - } \ No newline at end of file diff --git a/mROA/Implementation/Backend/NetworkGatewayModule.cs b/mROA/Implementation/Backend/NetworkGatewayModule.cs index 62b85c7..48ae7fc 100644 --- a/mROA/Implementation/Backend/NetworkGatewayModule.cs +++ b/mROA/Implementation/Backend/NetworkGatewayModule.cs @@ -45,7 +45,7 @@ public class NetworkGatewayModule(IPEndPoint endpoint) : IGatewayModule { var client = _tcpListener.AcceptTcpClient(); Console.WriteLine($"Client connected from {client.Client.RemoteEndPoint}"); - _interactionModule.RegisterSourse(client.GetStream()); + _interactionModule.RegisterSource(client.GetStream()); Console.WriteLine("Client registered"); } } diff --git a/mROA/Implementation/Backend/StreamBasedInteractionModule.cs b/mROA/Implementation/Backend/StreamBasedInteractionModule.cs index a9ef614..668cd23 100644 --- a/mROA/Implementation/Backend/StreamBasedInteractionModule.cs +++ b/mROA/Implementation/Backend/StreamBasedInteractionModule.cs @@ -7,7 +7,7 @@ public class StreamBasedInteractionModule : IInteractionModule private readonly Dictionary _streams = new(); private Action? _handler; - public void RegisterSourse(Stream stream) + public void RegisterSource(Stream stream) { var id = Random.Shared.Next(); _streams.Add(id, stream); diff --git a/mROA/Implementation/CommandExecution/ExceptionCommandExecution.cs b/mROA/Implementation/CommandExecution/ExceptionCommandExecution.cs new file mode 100644 index 0000000..5df0742 --- /dev/null +++ b/mROA/Implementation/CommandExecution/ExceptionCommandExecution.cs @@ -0,0 +1,11 @@ +using mROA.Abstract; + +namespace mROA.Implementation; + +public class ExceptionCommandExecution : ICommandExecution +{ + public Guid CallRequestId { get; init; } + public int ClientId { get; set; } + public int CommandId { get; init; } + public required string Exception { get; set; } +} \ No newline at end of file diff --git a/mROA/Implementation/FinalCommandExecution.cs b/mROA/Implementation/CommandExecution/FinalCommandExecution.cs similarity index 56% rename from mROA/Implementation/FinalCommandExecution.cs rename to mROA/Implementation/CommandExecution/FinalCommandExecution.cs index 2b960a1..9e3193a 100644 --- a/mROA/Implementation/FinalCommandExecution.cs +++ b/mROA/Implementation/CommandExecution/FinalCommandExecution.cs @@ -17,18 +17,4 @@ public class FinalCommandExecution : ICommandExecution public class FinalCommandExecution : FinalCommandExecution { public T? Result { get; init; } -} - -public class TypedFinalCommandExecution : FinalCommandExecution -{ - [JsonIgnore] - public Type? Type { get; set; } -} - -public class ExceptionCommandExecution : ICommandExecution -{ - public Guid CallRequestId { get; init; } - public int ClientId { get; set; } - public int CommandId { get; init; } - public required string Exception { get; set; } } \ No newline at end of file diff --git a/mROA/Implementation/CommandExecution/TypedFinalCommandExecution.cs b/mROA/Implementation/CommandExecution/TypedFinalCommandExecution.cs new file mode 100644 index 0000000..2736585 --- /dev/null +++ b/mROA/Implementation/CommandExecution/TypedFinalCommandExecution.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace mROA.Implementation; + +public class TypedFinalCommandExecution : FinalCommandExecution +{ + [JsonIgnore] + public Type? Type { get; set; } +} \ No newline at end of file diff --git a/mROA/Implementation/Frontend/Callback/JsonFrontendCallbackSerializationModule.cs b/mROA/Implementation/Frontend/Callback/JsonFrontendCallbackSerializationModule.cs new file mode 100644 index 0000000..8a181b2 --- /dev/null +++ b/mROA/Implementation/Frontend/Callback/JsonFrontendCallbackSerializationModule.cs @@ -0,0 +1,24 @@ +using mROA.Abstract; + +namespace mROA.Implementation.Frontend; + +public class JsonFrontendCallbackSerializationModule : ISerialisationModule +{ + private IInteractionModule.IFrontendInteractionModule? _interactionModule; + + public void Inject(T dependency) + { + if (dependency is IInteractionModule.IFrontendInteractionModule interactionModule) + _interactionModule = interactionModule; + } + + public void HandleIncomingRequest(int clientId, byte[] message) + { + + } + + public void PostResponse(NetworkMessage message, int clientId) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/mROA/Implementation/Frontend/JsonFrontendSerialisationModule.cs b/mROA/Implementation/Frontend/JsonFrontendSerialisationModule.cs index a2464b1..ea3f9d8 100644 --- a/mROA/Implementation/Frontend/JsonFrontendSerialisationModule.cs +++ b/mROA/Implementation/Frontend/JsonFrontendSerialisationModule.cs @@ -13,7 +13,7 @@ public class JsonFrontendSerialisationModule { if (_interactionModule is null) throw new Exception("Interaction module not initialized"); - + var receiveMessage = await _interactionModule.ReceiveMessage(); var parsed = JsonSerializer.Deserialize(receiveMessage)!; while (parsed.CallRequestId != requestId) @@ -29,32 +29,37 @@ public class JsonFrontendSerialisationModule { if (_interactionModule is null) throw new Exception("Interaction module not initialized"); - - var receiveMessage = await _interactionModule.ReceiveMessage(); - // var str = Encoding.UTF8.GetString(receiveMessage); - var document = JsonDocument.Parse(receiveMessage); - if (document.RootElement.TryGetProperty("Exception", out var exceptionElement)) - { - throw new RemoteException(exceptionElement.GetString()!) { CallRequestId = requestId }; - } - var parsed = document.Deserialize>()!; - while (parsed.CallRequestId != requestId) + var receiveMessage = await _interactionModule.ReceiveMessage(); + + var message = JsonSerializer.Deserialize(receiveMessage)!; + while (message.Id != requestId) { receiveMessage = await _interactionModule.ReceiveMessage(); - parsed = JsonSerializer.Deserialize>(receiveMessage)!; + message = JsonSerializer.Deserialize(receiveMessage)!; } - return parsed; + 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.Serialize(callRequest, callRequest.GetType()); - _interactionModule.PostMessage(Encoding.UTF8.GetBytes(post)); + + 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) @@ -62,7 +67,6 @@ public class JsonFrontendSerialisationModule if (dependency is IInteractionModule.IFrontendInteractionModule interactionModule) _interactionModule = interactionModule; } - } public class RemoteException(string error) : Exception diff --git a/mROA/Implementation/NetworkMessage.cs b/mROA/Implementation/NetworkMessage.cs new file mode 100644 index 0000000..3ef58fa --- /dev/null +++ b/mROA/Implementation/NetworkMessage.cs @@ -0,0 +1,15 @@ +using mROA.Abstract; + +namespace mROA.Implementation; + +public class NetworkMessage +{ + public Guid Id { get; set; } + public MessageType SchemaId { get; set; } + public byte[] Data { get; set; } +} + +public enum MessageType +{ + Unknown, FinishedCommandExecution, ErrorCommandExecution, AcyncCancelCommandExecution, CallRequest +} \ No newline at end of file diff --git a/mROA/mROA.csproj b/mROA/mROA.csproj index 3f50110..2ab4205 100644 --- a/mROA/mROA.csproj +++ b/mROA/mROA.csproj @@ -5,7 +5,7 @@ enable enable mROA - 1.1.3 + 2.0.0 YaslePoy Fast and easy RPC with contex https://github.com/YaslePoy/mROA