diff --git a/Library.Core/NodeFlow/DynamicContext.cs b/Library.Core/NodeFlow/DynamicContext.cs index 26c33b9..b39bc16 100644 --- a/Library.Core/NodeFlow/DynamicContext.cs +++ b/Library.Core/NodeFlow/DynamicContext.cs @@ -16,37 +16,37 @@ namespace Serein.Library.Core.NodeFlow } - public NodeRunCts NodeRunCts { get; set; } - //public ISereinIOC SereinIoc { get; } + // public NodeRunCts NodeRunCts { get; set; } + // public ISereinIOC SereinIoc { get; } public IFlowEnvironment Env { get; } - public Task CreateTimingTask(Action action, int time = 100, int count = -1) - { - if (NodeRunCts == null) - { - NodeRunCts = Env.IOC.Get(); - } - // 使用局部变量,避免捕获外部的 `action` - Action localAction = action; + //public Task CreateTimingTask(Action action, int time = 100, int count = -1) + //{ + // if (NodeRunCts == null) + // { + // NodeRunCts = Env.IOC.Get(); + // } + // // 使用局部变量,避免捕获外部的 `action` + // Action localAction = action; - return Task.Run(async () => - { - for (int i = 0; i < count && !NodeRunCts.IsCancellationRequested; i++) - { - await Task.Delay(time); - if (NodeRunCts.IsCancellationRequested) { break; } - //if (FlowEnvironment.IsGlobalInterrupt) - //{ - // await FlowEnvironment.GetOrCreateGlobalInterruptAsync(); - //} - // 确保对局部变量的引用 - localAction?.Invoke(); - } + // return Task.Run(async () => + // { + // for (int i = 0; i < count && !NodeRunCts.IsCancellationRequested; i++) + // { + // await Task.Delay(time); + // if (NodeRunCts.IsCancellationRequested) { break; } + // //if (FlowEnvironment.IsGlobalInterrupt) + // //{ + // // await FlowEnvironment.GetOrCreateGlobalInterruptAsync(); + // //} + // // 确保对局部变量的引用 + // localAction?.Invoke(); + // } - // 清理引用,避免闭包导致的内存泄漏 - localAction = null; - }); - } + // // 清理引用,避免闭包导致的内存泄漏 + // localAction = null; + // }); + //} } diff --git a/Library.Framework/NodeFlow/DynamicContext.cs b/Library.Framework/NodeFlow/DynamicContext.cs index eb057d0..45eb638 100644 --- a/Library.Framework/NodeFlow/DynamicContext.cs +++ b/Library.Framework/NodeFlow/DynamicContext.cs @@ -19,36 +19,36 @@ namespace Serein.Library.Framework.NodeFlow Env = flowEnvironment; } - public NodeRunCts NodeRunCts { get; set; } + // public NodeRunCts NodeRunCts { get; set; } // public ISereinIOC SereinIoc { get; } public IFlowEnvironment Env { get; } - public Task CreateTimingTask(Action action, int time = 100, int count = -1) - { - if(NodeRunCts == null) - { - NodeRunCts = Env.IOC.Get(); - } - // 使用局部变量,避免捕获外部的 `action` - Action localAction = action; + //public Task CreateTimingTask(Action action, int time = 100, int count = -1) + //{ + // if(NodeRunCts == null) + // { + // NodeRunCts = Env.IOC.Get(); + // } + // // 使用局部变量,避免捕获外部的 `action` + // Action localAction = action; - return Task.Run(async () => - { - for (int i = 0; i < count && !NodeRunCts.IsCancellationRequested; i++) - { - await Task.Delay(time); - if (NodeRunCts.IsCancellationRequested) { break; } - //if (FlowEnvironment.IsGlobalInterrupt) - //{ - // await FlowEnvironment.GetOrCreateGlobalInterruptAsync(); - //} - // 确保对局部变量的引用 - localAction?.Invoke(); - } + // return Task.Run(async () => + // { + // for (int i = 0; i < count && !NodeRunCts.IsCancellationRequested; i++) + // { + // await Task.Delay(time); + // if (NodeRunCts.IsCancellationRequested) { break; } + // //if (FlowEnvironment.IsGlobalInterrupt) + // //{ + // // await FlowEnvironment.GetOrCreateGlobalInterruptAsync(); + // //} + // // 确保对局部变量的引用 + // localAction?.Invoke(); + // } - // 清理引用,避免闭包导致的内存泄漏 - localAction = null; - }); - } + // // 清理引用,避免闭包导致的内存泄漏 + // localAction = null; + // }); + //} } } diff --git a/Library/Api/IDynamicContext.cs b/Library/Api/IDynamicContext.cs index 60a1c8b..aabb395 100644 --- a/Library/Api/IDynamicContext.cs +++ b/Library/Api/IDynamicContext.cs @@ -21,6 +21,6 @@ namespace Serein.Library.Api /// /// /// - Task CreateTimingTask(Action callback, int time = 100, int count = -1); + // Task CreateTimingTask(Action callback, int time = 100, int count = -1); } } diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs index 3c4a647..35f68c4 100644 --- a/Library/Api/IFlowEnvironment.cs +++ b/Library/Api/IFlowEnvironment.cs @@ -468,7 +468,7 @@ namespace Serein.Library.Api bool TryGetMethodDetails(string methodName, out MethodDetails md); - bool TryGetDelegate(string methodName, out Delegate del); + bool TryGetDelegateDetails(string methodName, out DelegateDetails del); //bool TryGetNodeData(string methodName, out NodeData node); diff --git a/Library/Entity/DelegateDetails.cs b/Library/Entity/DelegateDetails.cs new file mode 100644 index 0000000..c64b9aa --- /dev/null +++ b/Library/Entity/DelegateDetails.cs @@ -0,0 +1,28 @@ +using Serein.Library.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static Serein.Library.Utils.EmitHelper; + +namespace Serein.Library.Entity +{ + public class DelegateDetails + { + public DelegateDetails(EmitMethodType EmitMethodType, Delegate EmitDelegate) + { + this._emitMethodType = EmitMethodType; + this._emitDelegate = EmitDelegate; + } + public void Upload(EmitMethodType EmitMethodType, Delegate EmitDelegate) + { + _emitMethodType = EmitMethodType; + _emitDelegate = EmitDelegate; + } + private Delegate _emitDelegate; + private EmitMethodType _emitMethodType; + public Delegate EmitDelegate { get => _emitDelegate; } + public EmitMethodType EmitMethodType { get => _emitMethodType; } + } +} diff --git a/Library/Entity/MethodDetails.cs b/Library/Entity/MethodDetails.cs index 0fa74f9..da9cab6 100644 --- a/Library/Entity/MethodDetails.cs +++ b/Library/Entity/MethodDetails.cs @@ -23,7 +23,7 @@ namespace Serein.Library.Entity ActingInstance = ActingInstance, ActingInstanceType = ActingInstanceType, MethodDynamicType = MethodDynamicType, - MethodGuid = Guid.NewGuid().ToString(), + // MethodGuid = Guid.NewGuid().ToString(), MethodTips = MethodTips, ReturnType = ReturnType, MethodName = MethodName, @@ -56,7 +56,7 @@ namespace Serein.Library.Entity /// 方法GUID /// - public string MethodGuid { get; set; } + // public string MethodGuid { get; set; } /// /// 方法名称 diff --git a/Library/Network/Http/WebServer.cs b/Library/Network/Http/WebApiServer.cs similarity index 97% rename from Library/Network/Http/WebServer.cs rename to Library/Network/Http/WebApiServer.cs index 824d67a..d001ab2 100644 --- a/Library/Network/Http/WebServer.cs +++ b/Library/Network/Http/WebApiServer.cs @@ -14,10 +14,10 @@ namespace Serein.Library.Web /// HTTP接口监听类 /// [AutoRegister] - public class WebServer + public class WebApiServer { private readonly IRouter Router;// 路由器 - public WebServer(IRouter router) + public WebApiServer(IRouter router) { this.Router = router; listener = new HttpListener(); @@ -33,7 +33,7 @@ namespace Serein.Library.Web // 启动服务器 - public WebServer Start(string prefixe) + public WebApiServer Start(string prefixe) { if (!prefixe.Substring(prefixe.Length - 1, 1).Equals(@"/")) diff --git a/Library/Network/WebSocket/Attribute.cs b/Library/Network/WebSocket/Attribute.cs new file mode 100644 index 0000000..ba5a754 --- /dev/null +++ b/Library/Network/WebSocket/Attribute.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using System.Threading; +using System.Net.WebSockets; + +namespace Serein.Library.Network.WebSocketCommunication +{ + [AttributeUsage(AttributeTargets.Method)] + public sealed class AutoSocketHandleAttribute : Attribute + { + public string ThemeValue; + //public Type DataType; + } + [AttributeUsage(AttributeTargets.Class)] + public sealed class AutoSocketModuleAttribute : Attribute + { + public string JsonDataField; + public string JsonThemeField; + } + +} diff --git a/Library/Network/WebSocket/Handle/MyHandleConfig.cs b/Library/Network/WebSocket/Handle/MyHandleConfig.cs new file mode 100644 index 0000000..8e939ad --- /dev/null +++ b/Library/Network/WebSocket/Handle/MyHandleConfig.cs @@ -0,0 +1,141 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Serein.Library.Utils; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Library.Network.WebSocketCommunication.Handle +{ + public class MyHandleConfig + { + private readonly Delegate EmitDelegate; + private readonly EmitHelper.EmitMethodType EmitMethodType; + + public MyHandleConfig(ISocketControlBase instance, MethodInfo methodInfo) + { + EmitMethodType = EmitHelper.CreateDynamicMethod(methodInfo,out EmitDelegate); + + Instance = instance; + var parameterInfos = methodInfo.GetParameters(); + ParameterType = parameterInfos.Select(t => t.ParameterType).ToArray(); + ParameterName = parameterInfos.Select(t => t.Name).ToArray(); + + } + public ISocketControlBase Instance { get; private set; } + private string[] ParameterName; + private Type[] ParameterType; + + + public async void Handle(Func RecoverAsync, JObject jsonObject) + { + object[] args = new object[ParameterType.Length]; + for (int i = 0; i < ParameterType.Length; i++) + { + var type = ParameterType[i]; + var argName = ParameterName[i]; + if (type.IsGenericType) + { + if (type.IsAssignableFrom(typeof(Func))) + { + args[i] = new Func(async data => + { + var jsonText = JsonConvert.SerializeObject(data); + await RecoverAsync.Invoke(jsonText); + }); + } + else if (type.IsAssignableFrom(typeof(Func))) + { + args[i] = new Func(async data => + { + await RecoverAsync.Invoke(data); + }); + } + else if (type.IsAssignableFrom(typeof(Action))) + { + args[i] = new Action(async data => + { + var jsonText = JsonConvert.SerializeObject(data); + await RecoverAsync.Invoke(jsonText); + }); + } + else if (type.IsAssignableFrom(typeof(Action))) + { + args[i] = new Action(async data => + { + var jsonText = JsonConvert.SerializeObject(data); + await RecoverAsync.Invoke(jsonText); + }); + } + } + else if (type.IsValueType || type.IsClass) + { + var jsonValue = jsonObject.GetValue(argName); + if (jsonValue is null) + { + // 值类型返回默认值,引用类型返回null + args[i] = type.IsValueType ? Activator.CreateInstance(type) : null; + } + else + { + args[i] = jsonValue.ToObject(type); + } + } + + + } + Stopwatch sw = new Stopwatch(); + sw.Start(); + object result; + if (EmitMethodType == EmitHelper.EmitMethodType.HasResultTask && EmitDelegate is Func> hasResultTask) + { + result = await hasResultTask(Instance, args); + } + else if (EmitMethodType == EmitHelper.EmitMethodType.Task && EmitDelegate is Func task) + { + await task.Invoke(Instance, args); + result = null; + } + else if (EmitMethodType == EmitHelper.EmitMethodType.Func && EmitDelegate is Func func) + { + result = func.Invoke(Instance, args); + } + else + { + throw new NotImplementedException("构造委托无法正确调用"); + } + sw.Stop(); + Console.WriteLine($"Emit Invoke:{sw.ElapsedTicks * 1000000F / Stopwatch.Frequency:n3}μs"); + + if(result != null && result.GetType().IsClass) + { + var reusltJsonText = JsonConvert.SerializeObject(result); + _ = RecoverAsync.Invoke($"{reusltJsonText}"); + } + + + } + public void Clear() + { + Instance = null; + ParameterName = null; + ParameterType = null; + //expressionDelegate = null; + } + + } + + + + + + +} diff --git a/Library/Network/WebSocket/Handle/MyHandleModule.cs b/Library/Network/WebSocket/Handle/MyHandleModule.cs new file mode 100644 index 0000000..00da6c0 --- /dev/null +++ b/Library/Network/WebSocket/Handle/MyHandleModule.cs @@ -0,0 +1,69 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Library.Network.WebSocketCommunication.Handle +{ + public class MyHandleModule + { + public MyHandleModule(string ThemeJsonKey, string DataJsonKey) + { + this.ThemeJsonKey = ThemeJsonKey; + this.DataJsonKey = DataJsonKey; + } + public string ThemeJsonKey { get; } + public string DataJsonKey { get; } + + public ConcurrentDictionary MyHandleConfigs = new ConcurrentDictionary(); + public void AddHandleConfigs(string themeValue, ISocketControlBase instance, MethodInfo methodInfo) + { + if (!MyHandleConfigs.ContainsKey(themeValue)) + { + var myHandleConfig = new MyHandleConfig(instance, methodInfo); + MyHandleConfigs[themeValue] = myHandleConfig; + } + } + public void ResetConfig(ISocketControlBase socketControlBase) + { + foreach (var kv in MyHandleConfigs.ToArray()) + { + var config = kv.Value; + if (config.Instance.HandleGuid.Equals(socketControlBase.HandleGuid)) + { + MyHandleConfigs.TryRemove(kv.Key, out _); + } + } + } + + public void ResetConfig() + { + var temp = MyHandleConfigs.Values; + MyHandleConfigs.Clear(); + foreach (var config in temp) + { + config.Clear(); + } + } + + + public void HandleSocketMsg(Func RecoverAsync, JObject jsonObject) + { + // 获取到消息 + string themeKeyName = jsonObject.GetValue(ThemeJsonKey)?.ToString(); + if (!MyHandleConfigs.TryGetValue(themeKeyName, out var handldConfig)) + { + // 没有主题 + return; + } + if (jsonObject[DataJsonKey] is JObject dataJsonObject) + { + handldConfig.Handle(RecoverAsync, dataJsonObject); + } + } + } + +} diff --git a/Library/Network/WebSocket/Handle/SocketMsgHandleHelper.cs b/Library/Network/WebSocket/Handle/SocketMsgHandleHelper.cs new file mode 100644 index 0000000..dfe00c0 --- /dev/null +++ b/Library/Network/WebSocket/Handle/SocketMsgHandleHelper.cs @@ -0,0 +1,118 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Net.WebSockets; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using System.Threading; +using System.Runtime.CompilerServices; +using Newtonsoft.Json; +using Serein.Library.Utils; +using System.Net.Http.Headers; +using System.Linq.Expressions; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Security.Cryptography; + +namespace Serein.Library.Network.WebSocketCommunication.Handle +{ + + public class SocketMsgHandleHelper + { + /// + /// (Theme Name ,Data Name) - HandleModule + /// + public ConcurrentDictionary<(string, string), MyHandleModule> MyHandleModuleDict + = new ConcurrentDictionary<(string, string), MyHandleModule>(); + + + + private MyHandleModule AddMyHandleModule(string themeKeyName, string dataKeyName) + { + var key = (themeKeyName, dataKeyName); + if (!MyHandleModuleDict.TryGetValue(key, out var myHandleModule)) + { + myHandleModule = new MyHandleModule(themeKeyName, dataKeyName); + MyHandleModuleDict[key] = myHandleModule; + } + return myHandleModule; + } + + public void RemoteModule(ISocketControlBase socketControlBase) + { + var type = socketControlBase.GetType(); + var moduleAttribute = type.GetCustomAttribute(); + if (moduleAttribute is null) + { + return; + } + var themeKeyName = moduleAttribute.JsonThemeField; + var dataKeyName = moduleAttribute.JsonDataField; + var key = (themeKeyName, dataKeyName); + if (MyHandleModuleDict.TryRemove(key, out var myHandleModules)) + { + myHandleModules.ResetConfig(socketControlBase); + } + + } + public void AddModule(ISocketControlBase socketControlBase) + { + var type = socketControlBase.GetType(); + var moduleAttribute = type.GetCustomAttribute(); + if (moduleAttribute is null) + { + return; + } + + // 添加处理模块 + var themeKey = moduleAttribute.JsonThemeField; + var dataKey = moduleAttribute.JsonDataField; + + var handlemodule = AddMyHandleModule(themeKey, dataKey); + var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .Select(method => + { + var methodsAttribute = method.GetCustomAttribute(); + if (methodsAttribute is null) + { + return (string.Empty, null); + } + else + { + var value = methodsAttribute.ThemeValue; + return (value, method); + } + }) + .Where(x => !string.IsNullOrEmpty(x.value)).ToList(); + if (methods.Count == 0) + { + return; + } + + foreach ((var value, var method) in methods) + { + handlemodule.AddHandleConfigs(value, socketControlBase, method); + } + + } + + public async Task HandleMsgAsync(Func RecoverAsync, string message) + { + JObject json = JObject.Parse(message); + await Task.Run(() => + { + foreach (var module in MyHandleModuleDict.Values) + { + module.HandleSocketMsg(RecoverAsync, json); + } + }); + + } + + + + } +} diff --git a/Library/Network/WebSocket/SocketControlBase.cs b/Library/Network/WebSocket/SocketControlBase.cs new file mode 100644 index 0000000..428b996 --- /dev/null +++ b/Library/Network/WebSocket/SocketControlBase.cs @@ -0,0 +1,41 @@ +using Serein.Library.Attributes; +using System; +using System.Collections.Generic; +using System.Net.WebSockets; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Library.Network.WebSocketCommunication +{ + public interface ISocketControlBase + { + Guid HandleGuid { get; } + } + + + //[AutoRegister(RegisterSequence.FlowLoading)] + //[AutoSocketModule(JsonThemeField = "theme", JsonDataField = "data")] + //public class UserService : ISocketControlBase + //{ + // public Guid HandleGuid { get; } = new Guid(); + + // // Action 类型是特殊的,会用一个委托代替,这个委托可以将文本信息发送到客户端 + // // Action 类型是特殊的,会用一个委托代替,这个委托可以将对象转成json发送到客户端 + + // [AutoSocketHandle] + // public void AddUser(User user,Action Recover) + // { + // Console.WriteLine(user.ToString()); + // Recover("ok"); + // } + + // [AutoSocketHandle(ThemeValue = "Remote")] + // public void DeleteUser(User user, Action Recover) + // { + // Console.WriteLine(user.ToString()); + // } + + //} + +} diff --git a/Library/Network/WebSocket/WebSocketClient.cs b/Library/Network/WebSocket/WebSocketClient.cs new file mode 100644 index 0000000..cde011a --- /dev/null +++ b/Library/Network/WebSocket/WebSocketClient.cs @@ -0,0 +1,136 @@ +using Newtonsoft.Json.Linq; +using Serein.Library.Attributes; +using Serein.Library.Web; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Net.WebSockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Serein.Library.Network.WebSocketCommunication +{ + + + [AutoRegister] + public class WebSocketClient + { + public WebSocketClient() + { + + } + + + private ClientWebSocket _client = new ClientWebSocket(); + + + public async Task ConnectAsync(string uri) + { + await _client.ConnectAsync(new Uri(uri), CancellationToken.None); + await ReceiveAsync(); + } + + + public async Task SendAsync(WebSocket webSocket,string message) + { + var buffer = Encoding.UTF8.GetBytes(message); + await webSocket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, CancellationToken.None); + } + + private async Task ReceiveAsync() + { + var buffer = new byte[1024]; + while (_client.State == WebSocketState.Open) + { + try + { + var result = await _client.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + if (result.MessageType == WebSocketMessageType.Close) + { + await _client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None); + } + else + { + var message = Encoding.UTF8.GetString(buffer, 0, result.Count); + + + Debug.WriteLine($"Received: {message}"); + } + } + catch (Exception ex) + { + + await Console.Out.WriteLineAsync(ex.ToString()); + } + } + } + + + + + } + + + /* #region 消息处理 + private readonly string ThemeField; + private readonly ConcurrentDictionary ThemeConfigs = new ConcurrentDictionary(); + + public async Task HandleSocketMsg(string jsonStr) + { + JObject json; + try + { + json = JObject.Parse(jsonStr); + } + catch (Exception ex) + { + await SendAsync(_client, ex.Message); + return; + } + // 获取到消息 + string themeName = json[ThemeField]?.ToString(); + if (!ThemeConfigs.TryGetValue(themeName, out var handldConfig)) + { + return; + } + + object dataValue; + if (string.IsNullOrEmpty(handldConfig.DataField)) + { + dataValue = json.ToObject(handldConfig.DataType); + } + else + { + dataValue = json[handldConfig.DataField].ToObject(handldConfig.DataType); + } + await handldConfig.Invoke(dataValue, SendAsync); + } + + public void AddConfig(string themeName, Type dataType, MsgHandler msgHandler) + { + if (!ThemeConfigs.TryGetValue(themeName, out var handldConfig)) + { + handldConfig = new HandldConfig + { + DataField = themeName, + DataType = dataType + }; + ThemeConfigs.TryAdd(themeName, handldConfig); + } + handldConfig.HandldAsync += msgHandler; + } + public void RemoteConfig(string themeName, MsgHandler msgHandler) + { + if (ThemeConfigs.TryGetValue(themeName, out var handldConfig)) + { + handldConfig.HandldAsync -= msgHandler; + if (!handldConfig.HasSubscribers) + { + ThemeConfigs.TryRemove(themeName, out _); + } + } + } + #endregion*/ +} diff --git a/Library/Network/WebSocket/WebSocketRouter.cs b/Library/Network/WebSocket/WebSocketRouter.cs deleted file mode 100644 index 7b56edd..0000000 --- a/Library/Network/WebSocket/WebSocketRouter.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Serein.Library.Network.WebSocketCommunication -{ - public class WebSocketRouter - { - } -} diff --git a/Library/Network/WebSocket/WebSocketServer.cs b/Library/Network/WebSocket/WebSocketServer.cs index f1e25cf..e0530c9 100644 --- a/Library/Network/WebSocket/WebSocketServer.cs +++ b/Library/Network/WebSocket/WebSocketServer.cs @@ -1,57 +1,103 @@ -using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Serein.Library.Attributes; +using Serein.Library.Network.WebSocketCommunication.Handle; using System; +using System.Collections.Generic; +using System.Linq; using System.Net; using System.Net.WebSockets; +using System.Reflection; +using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Serein.Library.Network.WebSocketCommunication { + [AutoRegister] public class WebSocketServer { - public Func OnReceiveMsg; + public WebSocketServer() + { + + } + + public SocketMsgHandleHelper MsgHandleHelper { get; } = new SocketMsgHandleHelper(); + + HttpListener listener; public async Task StartAsync(string url) { - HttpListener listener = new HttpListener(); + listener = new HttpListener(); listener.Prefixes.Add(url); listener.Start(); while (true) { - var context = await listener.GetContextAsync(); - if (context.Request.IsWebSocketRequest) + try { - var webSocketContext = await context.AcceptWebSocketAsync(null); //新连接 - _ = HandleWebSocketAsync(webSocketContext.WebSocket); // 处理消息 + var context = await listener.GetContextAsync(); + string clientPoint = context.Request.RemoteEndPoint?.ToString(); + + await Console.Out.WriteLineAsync($"新的连接加入:{clientPoint}"); + if (context.Request.IsWebSocketRequest) + { + var webSocketContext = await context.AcceptWebSocketAsync(null); //新连接 + + _ = HandleWebSocketAsync(webSocketContext.WebSocket); // 处理消息 + } + } + catch (Exception ex) + { + await Console.Out.WriteLineAsync(ex.Message); + break; } } } + public void Stop() + { + listener?.Stop(); + } + private async Task HandleWebSocketAsync(WebSocket webSocket) { + Func SendAsync = async (text) => + { + await WebSocketServer.SendAsync(webSocket, text); + }; var buffer = new byte[1024]; while (webSocket.State == WebSocketState.Open) { var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); if (result.MessageType == WebSocketMessageType.Close) { + SendAsync = null; await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None); } else { var message = Encoding.UTF8.GetString(buffer, 0, result.Count); - Console.WriteLine($"Received: {message}"); - var action = OnReceiveMsg.Invoke(message); - action?.Invoke(); - - // 回显消息(可选) - //ar echoMessage = Encoding.UTF8.GetBytes(message); + + _ = MsgHandleHelper.HandleMsgAsync(SendAsync, message); + + //foreach (var item in HandldHelpers) + //{ + // await item.HandleSocketMsg(webSocket, message); + //} + //Console.WriteLine($"Received: {message}"); + //var echoMessage = Encoding.UTF8.GetBytes(message); //await webSocket.SendAsync(new ArraySegment(echoMessage, 0, echoMessage.Length), result.MessageType, result.EndOfMessage, CancellationToken.None); } } } + + public static async Task SendAsync(WebSocket webSocket, string message) + { + var buffer = Encoding.UTF8.GetBytes(message); + await webSocket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, CancellationToken.None); + } + } } diff --git a/Library/NodeAttribute.cs b/Library/NodeAttribute.cs index 91c5647..4f2cb84 100644 --- a/Library/NodeAttribute.cs +++ b/Library/NodeAttribute.cs @@ -11,12 +11,31 @@ namespace Serein.Library.Attributes { } + public enum RegisterSequence + { /// + /// 不自动初始化 + /// + Node, + /// + /// 初始化后 + /// + FlowInit, + /// + /// 加载后 + /// + FlowLoading, + } /// /// 表示该类自动注册(单例模式) /// [AttributeUsage(AttributeTargets.Class)] public sealed class AutoRegisterAttribute : Attribute { + public AutoRegisterAttribute(RegisterSequence Class = RegisterSequence.FlowInit) + { + this.Class = Class; + } + public RegisterSequence Class ; } /// diff --git a/Library/Serein.Library.csproj b/Library/Serein.Library.csproj index b1a2919..20c62fa 100644 --- a/Library/Serein.Library.csproj +++ b/Library/Serein.Library.csproj @@ -1,7 +1,7 @@  - netstandard2.0;net462 + net8-windows;net462 D:\Project\C#\DynamicControl\SereinFlow\.Output @@ -21,4 +21,8 @@ + + + + diff --git a/Library/Utils/EmitHelper.cs b/Library/Utils/EmitHelper.cs new file mode 100644 index 0000000..46d495f --- /dev/null +++ b/Library/Utils/EmitHelper.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Library.Utils +{ + public class EmitHelper + { + public enum EmitMethodType + { + Func, + Task, + HasResultTask, + } + public static bool IsGenericTask(Type returnType, out Type taskResult) + { + // 判断是否为 Task 类型或泛型 Task + if (returnType == typeof(Task)) + { + taskResult = null; + return true; + } + else if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)) + { + // 获取泛型参数类型 + Type genericArgument = returnType.GetGenericArguments()[0]; + taskResult = genericArgument; + return true; + } + else + { + taskResult = null; + return false; + + } + } + //public static Delegate CreateDynamicMethod(MethodInfo methodInfo) + //{ + // return CreateDynamicMethod(methodInfo); + //} + + public static EmitMethodType CreateDynamicMethod( MethodInfo methodInfo,out Delegate @delegate) + { + bool IsTask = IsGenericTask(methodInfo.ReturnType, out var taskGenericsType); + bool IsTaskGenerics = taskGenericsType != null; + DynamicMethod dynamicMethod; + if (IsTask) + { + if (IsTaskGenerics) + { + + dynamicMethod = new DynamicMethod( + name: methodInfo.Name + "_DynamicMethod", + returnType: typeof(Task), + parameterTypes: new[] { typeof(object), typeof(object[]) }, + restrictedSkipVisibility: true // 跳过私有方法访问限制 + ); + } + else + { + dynamicMethod = new DynamicMethod( + name: methodInfo.Name + "_DynamicMethod", + returnType: typeof(Task), + parameterTypes: new[] { typeof(object), typeof(object[]) }, + restrictedSkipVisibility: true // 跳过私有方法访问限制 + ); + } + } + else + { + dynamicMethod = new DynamicMethod( + name: methodInfo.Name + "_DynamicMethod", + returnType: typeof(object), + parameterTypes: new[] { typeof(object), typeof(object[]) }, + restrictedSkipVisibility: true // 跳过私有方法访问限制 + ); + } + + + + + var il = dynamicMethod.GetILGenerator(); + + // 加载实例 (this) + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Castclass, methodInfo.DeclaringType); // 将 ISocketControlBase 转换为目标类类型 + + // 加载方法参数 + var methodParams = methodInfo.GetParameters(); + for (int i = 0; i < methodParams.Length; i++) + { + il.Emit(OpCodes.Ldarg_1); // 加载参数数组 + il.Emit(OpCodes.Ldc_I4, i); // 加载当前参数索引 + il.Emit(OpCodes.Ldelem_Ref); // 取出数组元素 + + var paramType = methodParams[i].ParameterType; + if (paramType.IsValueType) // 如果参数是值类型,拆箱 + { + il.Emit(OpCodes.Unbox_Any, paramType); + } + else // 如果是引用类型,直接转换 + { + il.Emit(OpCodes.Castclass, paramType); + } + } + + // 调用方法 + il.Emit(OpCodes.Callvirt, methodInfo); + + //// 处理返回值,如果没有返回值,则返回null + if (methodInfo.ReturnType == typeof(void)) + { + il.Emit(OpCodes.Ldnull); + } + else if (methodInfo.ReturnType.IsValueType) + { + il.Emit(OpCodes.Box, methodInfo.ReturnType); // 如果是值类型,将其装箱 + } + // 处理返回值,如果没有返回值,则返回null + il.Emit(OpCodes.Ret); // 返回 + EmitMethodType emitMethodType; + if (IsTask) + { + if (IsTaskGenerics) + { + emitMethodType = EmitMethodType.HasResultTask; + @delegate = dynamicMethod.CreateDelegate(typeof(Func>)); + } + else + { + emitMethodType = EmitMethodType.Task; + @delegate = dynamicMethod.CreateDelegate(typeof(Func)); + } + } + else + { + emitMethodType = EmitMethodType.Func; + @delegate = dynamicMethod.CreateDelegate(typeof(Func)); + + } + return emitMethodType; + } + + + } + +} diff --git a/Library/Utils/ExpressionHelper.cs b/Library/Utils/ExpressionHelper.cs new file mode 100644 index 0000000..88541d3 --- /dev/null +++ b/Library/Utils/ExpressionHelper.cs @@ -0,0 +1,446 @@ +using Serein.Library.Api; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Threading.Tasks; + +namespace Serein.Library.Utils +{ + /// + /// 基于类型创建表达式树反射委托 + /// + public static class ExpressionHelper + { + /// + /// 缓存表达式树反射方法 + /// + private static ConcurrentDictionary Cache { get; } = new ConcurrentDictionary(); + + + #region 基于类型的表达式反射构建委托 + + #region 属性、字段的委托创建(表达式反射) + + /// + /// 动态获取属性值 + /// + public static Delegate PropertyGetter(Type type, string propertyName) + { + string cacheKey = $"{type.FullName}.{propertyName}.Getter"; + return Cache.GetOrAdd(cacheKey, _ => CreateGetterDelegate(type, propertyName)); + } + /// + /// 动态获取属性值 + /// + private static Delegate CreateGetterDelegate(Type type, string propertyName) + { + var parameter = Expression.Parameter(typeof(object), "instance"); + var property = Expression.Property(Expression.Convert(parameter, type), propertyName); + var lambda = Expression.Lambda(Expression.Convert(property, typeof(object)), parameter); + return lambda.Compile(); + } + + /// + /// 动态设置属性值 + /// + public static Delegate PropertySetter(Type type, string propertyName) + { + string cacheKey = $"{type.FullName}.{propertyName}.Setter"; + return Cache.GetOrAdd(cacheKey, _ => CreateSetterDelegate(type, propertyName)); + } + + /// + /// 动态设置属性值 + /// + private static Delegate CreateSetterDelegate(Type type, string propertyName) + { + var parameter = Expression.Parameter(typeof(object), "instance"); + var value = Expression.Parameter(typeof(object), "value"); + var property = Expression.Property(Expression.Convert(parameter, type), propertyName); + var assign = Expression.Assign(property, Expression.Convert(value, property.Type)); + var lambda = Expression.Lambda(assign, parameter, value); + return lambda.Compile(); + } + + /// + /// 动态获取字段值 + /// + public static Delegate FieldGetter(Type type, string fieldName) + { + string cacheKey = $"{type.FullName}.{fieldName}.FieldGetter"; + return Cache.GetOrAdd(cacheKey, _ => CreateFieldGetterDelegate(type, fieldName)); + } + /// + /// 动态获取字段值 + /// + private static Delegate CreateFieldGetterDelegate(Type type, string fieldName) + { + var parameter = Expression.Parameter(typeof(object), "instance"); + var field = Expression.Field(Expression.Convert(parameter, type), fieldName); + var lambda = Expression.Lambda(Expression.Convert(field, typeof(object)), parameter); + return lambda.Compile(); + } + + /// + /// 动态设置字段值 + /// + public static Delegate FieldSetter(Type type, string fieldName) + { + string cacheKey = $"{type.FullName}.{fieldName}.FieldSetter"; + return Cache.GetOrAdd(cacheKey, _ => CreateFieldSetterDelegate(type, fieldName)); + } + /// + /// 动态设置字段值 + /// + private static Delegate CreateFieldSetterDelegate(Type type, string fieldName) + { + var parameter = Expression.Parameter(typeof(object), "instance"); + var value = Expression.Parameter(typeof(object), "value"); + var field = Expression.Field(Expression.Convert(parameter, type), fieldName); + var assign = Expression.Assign(field, Expression.Convert(value, field.Type)); + var lambda = Expression.Lambda(assign, parameter, value); + return lambda.Compile(); + } + + #endregion + + + + /// + /// 表达式树构建无参数,无返回值方法 + /// + public static Delegate MethodCaller(Type type, MethodInfo methodInfo) + { + string cacheKey = $"{type.FullName}.{methodInfo.Name}.MethodCaller"; + return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegate(type, methodInfo)); + } + + /// + /// 表达式树构建无参数,无返回值方法 + /// + private static Delegate CreateMethodCallerDelegate(Type type, MethodInfo methodInfo) + { + var parameter = Expression.Parameter(typeof(object), "instance"); + var methodCall = Expression.Call(Expression.Convert(parameter, type), methodInfo); + var lambda = Expression.Lambda(methodCall, parameter); + // Action + return lambda.Compile(); + } + + /// + /// 表达式树构建无参数,有返回值方法 + /// + public static Delegate MethodCallerHaveResult(Type type, MethodInfo methodInfo) + { + string cacheKey = $"{type.FullName}.{methodInfo.Name}.MethodCallerHaveResult"; + return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateHaveResult(type, methodInfo)); + } + /// + /// 表达式树构建无参数,有返回值方法 + /// + private static Delegate CreateMethodCallerDelegateHaveResult(Type type, MethodInfo methodInfo) + { + var parameter = Expression.Parameter(typeof(object), "instance"); + var methodCall = Expression.Call(Expression.Convert(parameter, type), methodInfo); + + if (IsGenericTask(methodInfo.ReturnType, out var taskResult)) + { + if (taskResult is null) + { + var lambda = Expression.Lambda>(Expression.Convert(methodCall, typeof(Task)), parameter); + return lambda.Compile(); + } + else + { + var lambda = Expression.Lambda>>(Expression.Convert(methodCall, typeof(Task)), parameter); + return lambda.Compile(); + } + } + else + { + var lambda = Expression.Lambda>(Expression.Convert(methodCall, typeof(object)), parameter); + return lambda.Compile(); + } + + } + + + /// + /// 表达式树构建多个参数,无返回值的方法 + /// + public static Delegate MethodCaller(Type type, MethodInfo methodInfo, params Type[] parameterTypes) + { + string cacheKey = $"{type.FullName}.{methodInfo.Name}.MethodCaller"; + return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegate(type, methodInfo, parameterTypes)); + } + + /// + /// 表达式树构建多个参数,无返回值的方法 + /// + private static Delegate CreateMethodCallerDelegate(Type type, MethodInfo methodInfo, Type[] parameterTypes) + { + /* var parameter = Expression.Parameter(typeof(object), "instance"); + + var arguments = parameterTypes.Select((t, i) => Expression.Parameter(typeof(object), $"arg{i}")).ToArray(); + + var convertedArguments = arguments.Select((arg, i) => Expression.Convert(arg, parameterTypes[i])).ToArray(); + var methodCall = Expression.Call(Expression.Convert(parameter, type), + methodInfo, + convertedArguments); + var lambda = Expression.Lambda(methodCall, new[] { parameter }.Concat(arguments)); + var tmpAction = lambda.Compile(); + + // Action + return lambda.Compile();*/ + + var instanceParam = Expression.Parameter(typeof(object), "instance"); + var argsParam = Expression.Parameter(typeof(object[]), "args"); + + // 创建参数表达式 + var convertedArgs = parameterTypes.Select((paramType, index) => + Expression.Convert(Expression.ArrayIndex(argsParam, Expression.Constant(index)), paramType) + ).ToArray(); + + + // 创建方法调用表达式 + var methodCall = Expression.Call( + Expression.Convert(instanceParam, type), + methodInfo, + convertedArgs + ); + + // 创建 lambda 表达式 + var lambda = Expression.Lambda( + methodCall, + instanceParam, + argsParam + ); + + // Func + return lambda.Compile(); + } + + /// + /// 表达式树构建多个参数,有返回值的方法 + /// + public static Delegate MethodCallerHaveResult(Type type, MethodInfo methodInfo, Type[] parameterTypes) + { + string cacheKey = $"{type.FullName}.{methodInfo.Name}.MethodCallerHaveResult"; + return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateHaveResult(type, methodInfo, parameterTypes)); + } + /// + /// 表达式树构建多个参数,有返回值的方法 + /// + private static Delegate CreateMethodCallerDelegateHaveResult(Type type, MethodInfo methodInfo, Type[] parameterTypes) + { + /*var instanceParam = Expression.Parameter(typeof(object), "instance"); + var argsParam = Expression.Parameter(typeof(object[]), "args"); + + // 创建参数表达式 + var convertedArgs = parameterTypes.Select((paramType, index) => + Expression.Convert(Expression.ArrayIndex(argsParam, Expression.Constant(index)), paramType) + ).ToArray(); + + + // 创建方法调用表达式 + var methodCall = Expression.Call( + Expression.Convert(instanceParam, type), + methodInfo, + convertedArgs + ); + + // 创建 lambda 表达式 + var lambda = Expression.Lambda( + Expression.Convert(methodCall, typeof(object)), + instanceParam, + argsParam + ); + + // Func + return lambda.Compile();*/ + + var instanceParam = Expression.Parameter(typeof(object), "instance"); + var argsParam = Expression.Parameter(typeof(object[]), "args"); + + // 创建参数表达式 + var convertedArgs = parameterTypes.Select((paramType, index) => + Expression.Convert(Expression.ArrayIndex(argsParam, Expression.Constant(index)), paramType) + ).ToArray(); + + + // 创建方法调用表达式 + var methodCall = Expression.Call( + Expression.Convert(instanceParam, type), + methodInfo, + convertedArgs + ); + + if (IsGenericTask(methodInfo.ReturnType, out var taskResult)) + { + if (taskResult is null) + { + var lambda = Expression.Lambda> + (Expression.Convert(methodCall, typeof(Task)), instanceParam, argsParam); + return lambda.Compile(); + } + else + { + var lambda = Expression.Lambda>> + (Expression.Convert(methodCall, typeof(Task)), instanceParam, argsParam); + return lambda.Compile(); + } + } + else + { + var lambda = Expression.Lambda> + (Expression.Convert(methodCall, typeof(object)), instanceParam, argsParam); + return lambda.Compile(); + } + + + } + + + /// + /// 表达式树构建无参数,有返回值(Task)的方法(触发器) + /// + public static Delegate MethodCallerAsync(Type type, MethodInfo methodInfo) + { + string cacheKey = $"{type.FullName}.{methodInfo.Name}.MethodCallerAsync"; + return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateAsync(type, methodInfo)); + } + /// + /// 表达式树构建无参数,有返回值(Task)的方法(触发器) + /// + private static Delegate CreateMethodCallerDelegateAsync(Type type, MethodInfo methodInfo) + { + var parameter = Expression.Parameter(typeof(object), "instance"); + var methodCall = Expression.Call(Expression.Convert(parameter, type), methodInfo); + var lambda = Expression.Lambda>>( + Expression.Convert(methodCall, typeof(Task)), parameter); + // Func> + return lambda.Compile(); + } + + + + /// + /// 表达式树构建多个参数,有返回值(Task-object)的方法(触发器) + /// + public static Delegate MethodCallerAsync(Type type, MethodInfo method, params Type[] parameterTypes) + { + + string cacheKey = $"{type.FullName}.{method.Name}.MethodCallerAsync"; + return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateAsync(type, method, parameterTypes)); + } + /// + /// 表达式树构建多个参数,有返回值(Task)的方法(触发器) + /// + private static Delegate CreateMethodCallerDelegateAsync(Type type, MethodInfo methodInfo, Type[] parameterTypes) + { + var instanceParam = Expression.Parameter(typeof(object), "instance"); + var argsParam = Expression.Parameter(typeof(object[]), "args"); + + // 创建参数表达式 + var convertedArgs = parameterTypes.Select((paramType, index) => + Expression.Convert(Expression.ArrayIndex(argsParam, Expression.Constant(index)), paramType) + ).ToArray(); + + + // 创建方法调用表达式 + var methodCall = Expression.Call( + Expression.Convert(instanceParam, type), + methodInfo, + convertedArgs + ); + + // 创建 lambda 表达式 + var lambda = Expression.Lambda>>( + Expression.Convert(methodCall, typeof(Task)), + instanceParam, + argsParam + ); + //获取返回类型 + //var returnType = methodInfo.ReturnType; + //var lambda = Expression.Lambda( + // typeof(Func<,,>).MakeGenericType(typeof(object), typeof(object[]), returnType), + // Expression.Convert(methodCall, returnType), + // instanceParam, + // argsParam + // ); + + + //var resule = task.DynamicInvoke((object)[Activator.CreateInstance(type), [new DynamicContext(null)]]); + return lambda.Compile(); + } + + + + + + public static bool IsGenericTask(Type returnType, out Type taskResult) + { + // 判断是否为 Task 类型或泛型 Task + if (returnType == typeof(Task)) + { + taskResult = null; + return true; + } + else if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)) + { + // 获取泛型参数类型 + Type genericArgument = returnType.GetGenericArguments()[0]; + taskResult = genericArgument; + return true; + } + else + { + taskResult = null; + return false; + + } + } + + public static Delegate AutoCreate(Type type, MethodInfo methodInfo) + { + Type returnType = methodInfo.ReturnType; + var parameterTypes = methodInfo.GetParameters().Select(p => p.ParameterType).ToArray(); + var parameterCount = parameterTypes.Length; + + if (returnType == typeof(void)) + { + if (parameterCount == 0) + { + // 无返回值,无参数 + return MethodCaller(type, methodInfo); + } + else + { + // 无返回值,有参数 + return MethodCaller(type, methodInfo, parameterTypes); + } + } + else + { + if (parameterCount == 0) + { + // 有返回值,无参数 + return MethodCallerHaveResult(type, methodInfo); + } + else + { + // 有返回值,有参数 + return MethodCallerHaveResult(type, methodInfo, parameterTypes); + } + } + + #endregion + + + + } + } +} diff --git a/Library/Utils/SereinIoc.cs b/Library/Utils/SereinIoc.cs index 25ecab9..8ca5fb7 100644 --- a/Library/Utils/SereinIoc.cs +++ b/Library/Utils/SereinIoc.cs @@ -28,6 +28,7 @@ namespace Serein.Library.Utils /// 已完成注入的实例集合 /// private readonly ConcurrentDictionary _dependencies; + private readonly ConcurrentDictionary _registerParameterss; /// /// 未完成注入的实例集合。 @@ -40,9 +41,10 @@ namespace Serein.Library.Utils public SereinIOC() { - // 首先注册自己 _dependencies = new ConcurrentDictionary(); + _registerParameterss = new ConcurrentDictionary(); _typeMappings = new ConcurrentDictionary(); + _unfinishedDependencies = new ConcurrentDictionary>(); } @@ -57,7 +59,7 @@ namespace Serein.Library.Utils /// 参数 public bool Register(Type type, params object[] parameters) { - return RegisterType(type?.FullName, type); + return RegisterType(type?.FullName, type, parameters); } /// /// 注册类型 @@ -67,7 +69,7 @@ namespace Serein.Library.Utils public bool Register(params object[] parameters) { var type = typeof(T); - return RegisterType(type.FullName, type); + return RegisterType(type.FullName, type, parameters); } /// @@ -78,7 +80,7 @@ namespace Serein.Library.Utils public bool Register(params object[] parameters) where TImplementation : TService { - return RegisterType(typeof(TService).FullName, typeof(TImplementation)); + return RegisterType(typeof(TService).FullName, typeof(TImplementation), parameters); } #endregion @@ -132,6 +134,12 @@ namespace Serein.Library.Utils } public object Get(Type type) { + var instance = Get(type.FullName); + if(instance is null) + { + Console.WriteLine("类型没有注册:" + type.FullName); + } + return Get(type.FullName); } @@ -170,6 +178,7 @@ namespace Serein.Library.Utils disposable?.Dispose(); } } + _registerParameterss?.Clear(); _unfinishedDependencies?.Clear(); _typeMappings?.Clear(); _dependencies?.Clear(); @@ -185,11 +194,13 @@ namespace Serein.Library.Utils public string Name { get; set; } public Type Type { get; set; } } - + private const string FlowBaseClassName = "<>$FlowBaseClass!@#"; public Dictionary> BuildDependencyTree() { var dependencyMap = new Dictionary>(); - + //var tmpTypeFullName = new HashSet(); + //var tmpTypeFullName2 = new HashSet(); + dependencyMap[FlowBaseClassName] = new List(); foreach (var typeMapping in _typeMappings) { var constructor = GetConstructorWithMostParameters(typeMapping.Value); // 获取参数最多的构造函数 @@ -198,18 +209,41 @@ namespace Serein.Library.Utils var parameters = constructor.GetParameters() .Select(p => p.ParameterType) .ToList(); + //if(parameters.Count == 0) + //{ + // if (!dependencyMap.ContainsKey(typeMapping.Value.FullName)) + // { + // dependencyMap[typeMapping.Value.FullName] = new List(); + // } + // dependencyMap[typeMapping.Value.FullName].Add(typeMapping.Key); + //} - foreach (var param in parameters) + + if(parameters .Count > 0) { - if (!dependencyMap.ContainsKey(param.FullName)) + // 从类型的构造函数中提取类型 + foreach (var param in parameters) { - dependencyMap[param.FullName] = new List(); + if (!dependencyMap.ContainsKey(param.FullName)) + { + dependencyMap[param.FullName] = new List(); + } + dependencyMap[param.FullName].Add(typeMapping.Key); + //tmpTypeFullName.Add(param.FullName); + //if (tmpTypeFullName2.Contains(param.FullName)) + //{ + // tmpTypeFullName2.Remove(param.FullName); + //} } - dependencyMap[param.FullName].Add(typeMapping.Key); + } + else + { + var type = typeMapping.Value; + dependencyMap[FlowBaseClassName].Add(type.FullName); } } } - + return dependencyMap; } // 获取参数最多的构造函数 @@ -288,29 +322,44 @@ namespace Serein.Library.Utils public object CreateInstance(string typeName) { - if (_typeMappings.TryGetValue(typeName, out var type)) + if (!_typeMappings.TryGetValue(typeName, out var type)) { + return null; + } + if (_dependencies.TryGetValue(typeName, out var instance)) + { + return instance; + } + if (_registerParameterss.TryGetValue(typeName,out var @params)) + { + instance = Activator.CreateInstance(type, @params); + } + else + { + // 没有显示指定构造函数入参,选择参数最多的构造函数 var constructor = GetConstructorWithMostParameters(type); var parameters = constructor.GetParameters(); var args = new object[parameters.Length]; - - for (int i = 0; i < parameters.Length; i++) { + var argType = parameters[i].ParameterType; var fullName = parameters[i].ParameterType.FullName; if (!_dependencies.TryGetValue(fullName, out var argObj)) { - argObj = CreateInstance(parameters[i].ParameterType.FullName); + if (!_typeMappings.ContainsKey(fullName)) + { + _typeMappings.TryAdd(fullName, argType); + } + argObj = CreateInstance(fullName); } args[i] = argObj; } - var value = Activator.CreateInstance(type, args); - InjectDependencies(value); // 完成创建后注入实例需要的特性依赖项 - - return value; + instance = Activator.CreateInstance(type, args); } - return null; + InjectDependencies(instance); // 完成创建后注入实例需要的特性依赖项 + _dependencies[typeName] = instance; + return instance; } @@ -330,6 +379,10 @@ namespace Serein.Library.Utils continue; } var value = CreateInstance(typeName); + if(value is null) + { + continue; + } _dependencies[typeName] = value; OnIOCMembersChanged.Invoke(new IOCMembersChangedEventArgs(typeName, value)); } @@ -349,11 +402,15 @@ namespace Serein.Library.Utils /// /// /// - private bool RegisterType(string typeFull, Type type) + private bool RegisterType(string typeFull, Type type, params object[] parameters) { if (!_typeMappings.ContainsKey(typeFull)) { _typeMappings[typeFull] = type; + if(parameters.Length > 0) + { + _registerParameterss[typeFull] = parameters; + } return true; } else @@ -432,15 +489,8 @@ namespace Serein.Library.Utils public void Run(Action action) { var service = Get(); - if (service == null) - { - throw new Exception("类型没有注册:"+typeof(T).FullName); - - } - else - { - action(service); - } + action(service); + } public void Run(Action action) diff --git a/Library/Utils/SerinExpressionEvaluator.cs b/Library/Utils/SerinExpressionEvaluator.cs deleted file mode 100644 index e1bc6e8..0000000 --- a/Library/Utils/SerinExpressionEvaluator.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace Serein.Library.Utils -{ - //public abstract class SerinExpressionEvaluator - //{ - // public abstract string Evaluate(string expression ,object obj , out bool isChange); - //} -} \ No newline at end of file diff --git a/Library/Utils/Utils.cs b/Library/Utils/Utils.cs deleted file mode 100644 index 34790b0..0000000 --- a/Library/Utils/Utils.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; -using System.Threading; - -namespace Serein.Library.Utils -{ - public class NodeRunCts : CancellationTokenSource - { - } -} diff --git a/Net462DllTest/LogicControl/PlcLogicControl.cs b/Net462DllTest/LogicControl/PlcLogicControl.cs index a0f7940..a79b7e1 100644 --- a/Net462DllTest/LogicControl/PlcLogicControl.cs +++ b/Net462DllTest/LogicControl/PlcLogicControl.cs @@ -1,8 +1,6 @@ -using IoTClient.Clients.PLC; -using IoTClient.Common.Enums; +using IoTClient.Common.Enums; using Net462DllTest.Enums; using Net462DllTest.Model; -using Net462DllTest.Signal; using Net462DllTest.Trigger; using Net462DllTest.Web; using Serein.Library.Api; @@ -10,25 +8,14 @@ using Serein.Library.Attributes; using Serein.Library.Enums; using Serein.Library.Ex; using Serein.Library.Framework.NodeFlow; +using Serein.Library.Network.WebSocketCommunication; using Serein.Library.NodeFlow.Tool; -using Serein.Library.Utils; using Serein.Library.Web; using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Reflection; using System.Threading.Tasks; namespace Net462DllTest.LogicControl { - [AttributeUsage(AttributeTargets.Class)] - public sealed class AutoSocketAttribute : Attribute - { - public string BusinessField; - } - - - [AutoRegister] [DynamicFlow("[SiemensPlc]")] @@ -48,33 +35,44 @@ namespace Net462DllTest.LogicControl [NodeAction(NodeType.Init)] public void Init(IDynamicContext context) { - context.Env.IOC.Register(); - context.Env.IOC.Register(); + context.Env.IOC.Register(); + context.Env.IOC.Register(); - //context.Env.IOC.Register(); - //context.Env.IOC.Register(); + context.Env.IOC.Register(); + context.Env.IOC.Register(); + + } [NodeAction(NodeType.Loading)] // Loading 初始化完成已注入依赖项,可以开始逻辑上的操作 public void Loading(IDynamicContext context) { // 注册控制器 - context.Env.IOC.Run((router, web) => { - router.RegisterController(typeof(CommandController)); - web.Start("http://*:8089/"); // 开启 Web 服务 + context.Env.IOC.Run((router, apiServer) => { + router.RegisterController(typeof(FlowController)); + apiServer.Start("http://*:8089/"); // 开启 Web Api 服务 }); - //context.Env.IOC.Run(server => { - // server.Start(5000); // 开启 Socket 监听 - //}); + context.Env.IOC.Run(async (socketServer) => { + // socketServer.RegisterModuleInstance(userService); + await socketServer.StartAsync("http://localhost:5005/"); // 开启 Web Socket 监听 + }); + context.Env.IOC.Run(async client => { + await client.ConnectAsync("ws://localhost:5005/"); // 连接到服务器 + }); } [NodeAction(NodeType.Exit)] // 流程结束时自动执行 public void Exit(IDynamicContext context) { - context.Env.IOC.Run((web) => + context.Env.IOC.Run((apiServer) => { - web?.Stop(); // 关闭 Web 服务 + apiServer?.Stop(); // 关闭 Web 服务 + + }); + context.Env.IOC.Run((socketServer) => + { + socketServer?.Stop(); // 关闭 Web 服务 }); MyPlc.Close(); MyPlc.CancelAllTasks(); diff --git a/Net462DllTest/LogicControl/ViewLogicControl.cs b/Net462DllTest/LogicControl/ViewLogicControl.cs index 424dfda..9519281 100644 --- a/Net462DllTest/LogicControl/ViewLogicControl.cs +++ b/Net462DllTest/LogicControl/ViewLogicControl.cs @@ -1,5 +1,6 @@  using Net462DllTest.Signal; +using Net462DllTest.Trigger; using Net462DllTest.ViewModel; using Serein.Library.Api; using Serein.Library.Attributes; @@ -15,41 +16,12 @@ using System.Threading.Tasks; using System.Windows.Forms; namespace Net462DllTest.LogicControl -{ +{ + + + - /// - /// 视图管理 - /// [AutoRegister] - public class ViewManagement:ChannelFlowTrigger - { - private readonly List
forms = new List(); - /// - /// 打开窗口 - /// - /// 要打开的窗口类型 - /// 是否置顶 - public void OpenView(Form form, bool isTop) - { - form.TopMost = isTop; - form.Show(); - forms.Add(form); - } - public void CloseView(Type formType) - { - var remoteForms = forms.Where(f => f.GetType() == formType).ToArray(); - foreach (Form f in remoteForms) - { - f.Close(); - f.Dispose(); - this.forms.Remove(f); - } - } - } - - - - [DynamicFlow("[View]")] public class ViewLogicControl { @@ -67,10 +39,10 @@ namespace Net462DllTest.LogicControl { try { - TriggerData triggerData = await ViewManagement.CreateChannelWithTimeoutAsync(command, TimeSpan.FromMinutes(120), 0); + TriggerData triggerData = await ViewManagement.CreateChannelWithTimeoutAsync(command, TimeSpan.FromHours(10), 0); if (triggerData.Type == TriggerType.Overtime) { - throw new FlipflopException("超时取消"); + return new FlipflopContext(FlipflopStateType.Cancel, triggerData.Value); } return new FlipflopContext(FlipflopStateType.Succeed, triggerData.Value); } diff --git a/Net462DllTest/Net462DllTest.csproj b/Net462DllTest/Net462DllTest.csproj index d3e5791..5903dac 100644 --- a/Net462DllTest/Net462DllTest.csproj +++ b/Net462DllTest/Net462DllTest.csproj @@ -42,6 +42,9 @@ ..\packages\IoTClient.1.0.40\lib\netstandard2.0\IoTClient.dll + + ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll + @@ -72,6 +75,7 @@ + @@ -87,7 +91,8 @@ TestFormView.cs - + + diff --git a/Net462DllTest/Trigger/ViewManagement.cs b/Net462DllTest/Trigger/ViewManagement.cs new file mode 100644 index 0000000..4fe192b --- /dev/null +++ b/Net462DllTest/Trigger/ViewManagement.cs @@ -0,0 +1,49 @@ +using Net462DllTest.Signal; +using Serein.Library.Api; +using Serein.Library.Attributes; +using Serein.Library.NodeFlow.Tool; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Net462DllTest.Trigger +{ + /// + /// 视图管理 + /// + [AutoRegister] + public class ViewManagement : ChannelFlowTrigger + { + public ViewManagement(IFlowEnvironment environment) + { + + } + public int Id = new Random().Next(1, 10000); + private readonly List forms = new List(); + /// + /// 打开窗口 + /// + /// 要打开的窗口类型 + /// 是否置顶 + public void OpenView(Form form, bool isTop) + { + form.TopMost = isTop; + form.Show(); + forms.Add(form); + } + public void CloseView(Type formType) + { + var remoteForms = forms.Where(f => f.GetType() == formType).ToArray(); + foreach (Form f in remoteForms) + { + f.Close(); + f.Dispose(); + this.forms.Remove(f); + } + } + } + +} diff --git a/Net462DllTest/View/FromWorkBenchView.Designer.cs b/Net462DllTest/View/FromWorkBenchView.Designer.cs index 1480924..f391229 100644 --- a/Net462DllTest/View/FromWorkBenchView.Designer.cs +++ b/Net462DllTest/View/FromWorkBenchView.Designer.cs @@ -43,6 +43,7 @@ this.button1.TabIndex = 0; this.button1.Text = "查看状态"; this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); // // textBoxPlcInfo // @@ -60,6 +61,7 @@ this.button2.TabIndex = 2; this.button2.Text = "触发"; this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); // // listBoxCommand // @@ -90,6 +92,7 @@ this.Controls.Add(this.button1); this.Name = "FromWorkBenchView"; this.Text = "Form1"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FromWorkBenchView_FormClosing); this.Load += new System.EventHandler(this.FromWorkBenchView_Load); this.ResumeLayout(false); this.PerformLayout(); diff --git a/Net462DllTest/View/FromWorkBenchView.cs b/Net462DllTest/View/FromWorkBenchView.cs index feef255..30d6f7b 100644 --- a/Net462DllTest/View/FromWorkBenchView.cs +++ b/Net462DllTest/View/FromWorkBenchView.cs @@ -23,8 +23,8 @@ namespace Net462DllTest public FromWorkBenchView(IFlowEnvironment env) { InitializeComponent(); - ViewModel = env.IOC.Get(); // 获取对象 - if(ViewModel is null) + ViewModel = env.IOC.Get(); + if (ViewModel is null) { Console.WriteLine("创建对象并注入依赖项"); ViewModel = env.IOC.Instantiate(); @@ -41,14 +41,25 @@ namespace Net462DllTest listBoxCommand.DataBindings.Add(nameof(listBoxCommand.SelectedItem), ViewModel, nameof(ViewModel.SelectedSignal), false, DataSourceUpdateMode.OnPropertyChanged); listBoxCommand.SelectedIndexChanged += (s, e) => listBoxCommand.DataBindings[nameof(listBoxCommand.SelectedItem)].WriteValue(); - button1.Click += (s, e) => ViewModel.CommandViewPlcInfo.Execute(); - button2.Click += (s, e) => ViewModel.CommandGetParkingSpace.Execute(); } private void FromWorkBenchView_Load(object sender, EventArgs e) { } + private void FromWorkBenchView_FormClosing(object sender, FormClosingEventArgs e) + { + ViewModel.CommandCloseForm.Execute(); + } + private void button2_Click(object sender, EventArgs e) + { + ViewModel.CommandGetParkingSpace.Execute(); + } + + private void button1_Click(object sender, EventArgs e) + { + ViewModel.CommandViewPlcInfo.Execute(); + } } } diff --git a/Net462DllTest/ViewModel/FromWorkBenchViewModel.cs b/Net462DllTest/ViewModel/FromWorkBenchViewModel.cs index 76cd2b5..4af520f 100644 --- a/Net462DllTest/ViewModel/FromWorkBenchViewModel.cs +++ b/Net462DllTest/ViewModel/FromWorkBenchViewModel.cs @@ -13,26 +13,123 @@ using System.Threading.Tasks; using System.Windows.Input; using Net462DllTest.LogicControl; using Net462DllTest.Model; +using Serein.Library.Network.WebSocketCommunication; +using Newtonsoft.Json.Linq; +using System.Diagnostics; namespace Net462DllTest.ViewModel { - public class FromWorkBenchViewModel : INotifyPropertyChanged + public class LibSpace { + public string SpaceNum { get; set; } + public string PlateNumber { get; set; } + + public override string ToString() + { + return $"{nameof(SpaceNum)}{SpaceNum}{nameof(PlateNumber)}{PlateNumber}"; + } + } + + [AutoSocketModule(JsonThemeField = "theme", JsonDataField = "data")] + public class FromWorkBenchViewModel : INotifyPropertyChanged, ISocketControlBase + { + public Guid HandleGuid { get; } = new Guid(); + private readonly SiemensPlcDevice Device; private readonly ViewManagement viewManagement; private readonly PlcVarModelDataProxy plcVarModelDataProxy; + + + public FromWorkBenchViewModel(SiemensPlcDevice Device, ViewManagement viewManagement, - PlcVarModelDataProxy plcVarModelDataProxy) + PlcVarModelDataProxy plcVarModelDataProxy, + WebSocketServer webSocketServer) { this.Device = Device; this.viewManagement = viewManagement; this.plcVarModelDataProxy = plcVarModelDataProxy; + InitCommand(); // 初始化指令 + + webSocketServer.MsgHandleHelper.AddModule(this); // 用于关闭窗体时移除监听 + CommandCloseForm = new RelayCommand((p) => + { + webSocketServer.MsgHandleHelper.RemoteModule(this); // 用于关闭窗体时移除监听 + }); + + + + - InitCommand(); + //Console.WriteLine($"Emit: {sw.ElapsedTicks * 1000000F / Stopwatch.Frequency:n3}μs"); + + + //var msgHandl = new WsMsgHandl() + //{ + // DataJsonKey = "data", // 数据键 + // ThemeJsonKey = "theme", // 主题键 + //}; + //msgHandl.AddHandle(nameof(GetSpace), GetSpace); // theme:AddUser + //msgHandl.AddHandle(nameof(UpData), UpData); // theme:DeleteUser + //webSocketServer.AddModule(msgHandl); + //CommandCloseForm = new RelayCommand((p) => + //{ + // webSocketServer.RemoteModule(msgHandl); // 用于关闭窗体时移除监听 + //}); } + Action Recover; + [AutoSocketHandle(ThemeValue = "SavaRecover")] + public async Task SavaRecover(int waitTime , Action Recover) + { + if(waitTime <=0 || waitTime>= 10000) + { + return; + } + await Task.Delay(waitTime); + Recover("haha~" ); + this.Recover = Recover; + return; + } + [AutoSocketHandle(ThemeValue = "Invoke")] + public void Invoke(string a) + { + if (Recover is null) + return; + Recover("haha~"+a); + + } + + + [AutoSocketHandle(ThemeValue = "PlcModel")] + public async Task GetModelData(Action Recover) + { + Recover("等待5秒"); + await Task.Delay(5000); + //Recover.Invoke(plcVarModelDataProxy); + return plcVarModelDataProxy; + } + + [AutoSocketHandle(ThemeValue = "Up")] + public void UpData(LibSpace libSpace, Action Recover) + { + Recover("收到"); + return; + } + + [AutoSocketHandle(ThemeValue = "Bind")] + public LibSpace SpaceBind(string spaceNum, string plateNumber, Action Recover) + { + return new LibSpace + { + PlateNumber = plateNumber, + SpaceNum = spaceNum, + }; + } + + + #region 属性绑定 @@ -98,7 +195,11 @@ namespace Net462DllTest.ViewModel /// 调取车位 /// public RelayCommand CommandGetParkingSpace { get; private set; } - + /// + /// 关闭窗体 + /// + public RelayCommand CommandCloseForm { get; private set; } + public void InitCommand() { CommandViewPlcInfo = new RelayCommand((p) => @@ -109,6 +210,7 @@ namespace Net462DllTest.ViewModel { viewManagement.TriggerSignal(SelectedSignal, SpcaeNumber); }); + } diff --git a/Net462DllTest/Web/CommandController.cs b/Net462DllTest/Web/CommandController.cs deleted file mode 100644 index b22f362..0000000 --- a/Net462DllTest/Web/CommandController.cs +++ /dev/null @@ -1,56 +0,0 @@ - -using Net462DllTest.Enums; -using Net462DllTest.Signal; -using Net462DllTest.Trigger; -using Serein.Library.Attributes; -using Serein.Library.Utils; -using Serein.Library.Web; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Net462DllTest.Web -{ - [AutoHosting] - public class CommandController : ControllerBase - { - private readonly SiemensPlcDevice plcDevice; - - public CommandController(SiemensPlcDevice plcDevice) - { - this.plcDevice = plcDevice; - } - - /* - * 类型 :POST - * url : http://127.0.0.1:8089/command/trigger?command= - * body :[JSON] - * - * { - * "value":0, - * } - */ - [WebApi(API.POST)] - public dynamic Trigger([Url] string var, int value) - { - if (EnumHelper.TryConvertEnum(var,out var signal)) - { - Console.WriteLine($"外部触发 {signal} 信号,信号内容 : {value} "); - plcDevice.TriggerSignal(signal, value);// 通过 Web Api 模拟外部输入信号 - return new { state = "succeed" }; - } - else - { - return new { state = "fail" }; - } - - } - } - - - - - -} diff --git a/Net462DllTest/Web/FlowController.cs b/Net462DllTest/Web/FlowController.cs new file mode 100644 index 0000000..3cc0e68 --- /dev/null +++ b/Net462DllTest/Web/FlowController.cs @@ -0,0 +1,85 @@ + +using Net462DllTest.Enums; +using Net462DllTest.Signal; +using Net462DllTest.Trigger; +using Serein.Library.Attributes; +using Serein.Library.Utils; +using Serein.Library.Web; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Net462DllTest.Web +{ + [AutoHosting] + public class FlowController : ControllerBase + { + private readonly SiemensPlcDevice plcDevice; + private readonly ViewManagement viewManagement; + + public FlowController(SiemensPlcDevice plcDevice, ViewManagement viewManagement) + { + this.plcDevice = plcDevice; + this.viewManagement = viewManagement; + + } + + /* + * 类型 :POST + * url : http://127.0.0.1:8089/flow/plcop?var= + * url : http://127.0.0.1:8089/flow/plcop?var=SpaceNum + * body :[JSON] + * + * { + * "value":0, + * } + */ + [WebApi(API.POST)] + public dynamic PlcOp([Url] string var, int value) + { + if (EnumHelper.TryConvertEnum(var,out var signal)) + { + Console.WriteLine($"外部触发 {signal} 信号,信号内容 : {value} "); + plcDevice.TriggerSignal(signal, value);// 通过 Web Api 模拟外部输入信号 + return new { state = "succeed" }; + } + else + { + return new { state = "fail" }; + } + + } + /* + * 类型 :POST + * url : http://127.0.0.1:8089/flow/trigger?command= + * url : http://127.0.0.1:8089/flow/trigger?command=Command_1 + * body :[JSON] + * + * { + * "value":0, + * } + */ + [WebApi(API.POST)] + public dynamic Trigger([Url] string command, int value) + { + if (EnumHelper.TryConvertEnum(command, out var signal)) + { + Console.WriteLine($"外部触发 {signal} 信号,信号内容 : {value} "); + viewManagement.TriggerSignal(signal, value);// 通过 Web Api 模拟外部输入信号 + return new { state = "succeed" }; + } + else + { + return new { state = "fail" }; + } + + } + } + + + + + +} diff --git a/Net462DllTest/Web/UserService.cs b/Net462DllTest/Web/UserService.cs new file mode 100644 index 0000000..96b56cd --- /dev/null +++ b/Net462DllTest/Web/UserService.cs @@ -0,0 +1,15 @@ +using Net462DllTest.ViewModel; +using Serein.Library.Attributes; +using Serein.Library.Network.WebSocketCommunication; +using Serein.Library.Web; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Net462DllTest.Web +{ + + +} diff --git a/Net462DllTest/app.config b/Net462DllTest/app.config index 7a0cd6f..0ad3798 100644 --- a/Net462DllTest/app.config +++ b/Net462DllTest/app.config @@ -1,11 +1,11 @@ - + - - + + - + diff --git a/Net462DllTest/packages.config b/Net462DllTest/packages.config index cadbf4f..25f6dd1 100644 --- a/Net462DllTest/packages.config +++ b/Net462DllTest/packages.config @@ -1,6 +1,7 @@  + \ No newline at end of file diff --git a/NodeFlow/Base/NodeModelBaseFunc.cs b/NodeFlow/Base/NodeModelBaseFunc.cs index 34506ac..c5ddd2d 100644 --- a/NodeFlow/Base/NodeModelBaseFunc.cs +++ b/NodeFlow/Base/NodeModelBaseFunc.cs @@ -197,49 +197,69 @@ namespace Serein.NodeFlow.Base { throw new Exception($"节点{this.Guid}不存在方法信息,请检查是否需要重写节点的ExecutingAsync"); } - if (!context.Env.TryGetDelegate(md.MethodName, out var del)) + if (!context.Env.TryGetDelegateDetails(md.MethodName, out var dd)) { throw new Exception($"节点{this.Guid}不存在对应委托"); } md.ActingInstance ??= context.Env.IOC.Get(md.ActingInstanceType); object instance = md.ActingInstance; - bool haveParameter = md.ExplicitDatas.Length > 0; - bool haveResult = md.ReturnType != typeof(void); - Type? taskResult = null; - bool isTask = md.ReturnType is not null && MethodDetailsHelperTmp.IsGenericTask(md.ReturnType, out taskResult); - bool isTaskHaveResult = taskResult is not null; - object? result; + //bool haveParameter = md.ExplicitDatas.Length > 0; + //bool haveResult = md.ReturnType != typeof(void); + // Type? taskResult = null; + //bool isTask = md.ReturnType is not null && MethodDetailsHelper.IsGenericTask(md.ReturnType, out taskResult); + //bool isTaskHaveResult = taskResult is not null; + object? result = null; //Console.WriteLine($"(isTask, isTaskHaveResult):{(isTask, isTaskHaveResult)}"); try { // Action/Func([方法作用的实例],[可能的参数值],[可能的返回值]) - object?[]? parameters = GetParameters(context, this, md); - if (isTask) + object?[]? args = GetParameters(context, this, md); + var delType = dd.EmitMethodType; + var del = dd.EmitDelegate; + if (delType == EmitHelper.EmitMethodType.HasResultTask && del is Func> hasResultTask) { - // 异步方法(因为返回了Task,所以排除Action<>委托的可能) - result = (haveParameter, isTaskHaveResult) switch - { - (false, false) => await ExecutionAsync((Func)del, instance), // 调用节点方法,返回方法传回类型 - (true, false) => await ExecutionAsync((Func)del, instance, parameters), // 调用节点方法,获取入参参数,返回方法返回类型 - (false, true) => await ExecutionAsync((Func>)del, instance), // 调用节点方法,返回方法传回类型 - (true, true) => await ExecutionAsync((Func>)del, instance, parameters), // 调用节点方法,获取入参参数,返回方法返回类型 - }; + result = await hasResultTask(instance, args); + } + else if (delType == EmitHelper.EmitMethodType.Task && del is Func task) + { + await task.Invoke(instance, args); + result = null; + } + else if (delType == EmitHelper.EmitMethodType.Func && del is Func func) + { + result = func.Invoke(instance, args); } else { - // 非异步方法 - result = (haveParameter, haveResult) switch - { - (false, false) => Execution((Action)del, instance), // 调用节点方法,返回null - (true, false) => Execution((Action)del, instance, parameters), // 调用节点方法,返回null - (false, true) => Execution((Func)del, instance), // 调用节点方法,返回方法传回类型 - (true, true) => Execution((Func)del, instance, parameters), // 调用节点方法,获取入参参数,返回方法返回类型 - }; + throw new NotImplementedException("构造委托无法正确调用"); } + //if (isTask) + //{ + // // 异步方法(因为返回了Task,所以排除Action<>委托的可能) + // result = (haveParameter, isTaskHaveResult) switch + // { + // (false, false) => await ExecutionAsync((Func)del, instance), // 调用节点方法,返回方法传回类型 + // (true, false) => await ExecutionAsync((Func)del, instance, parameters), // 调用节点方法,获取入参参数,返回方法返回类型 + // (false, true) => await ExecutionAsync((Func>)del, instance), // 调用节点方法,返回方法传回类型 + // (true, true) => await ExecutionAsync((Func>)del, instance, parameters), // 调用节点方法,获取入参参数,返回方法返回类型 + // }; + //} + //else + //{ + // // 非异步方法 + // result = (haveParameter, haveResult) switch + // { + // (false, false) => Execution((Action)del, instance), // 调用节点方法,返回null + // (true, false) => Execution((Action)del, instance, parameters), // 调用节点方法,返回null + // (false, true) => Execution((Func)del, instance), // 调用节点方法,返回方法传回类型 + // (true, true) => Execution((Func)del, instance, parameters), // 调用节点方法,获取入参参数,返回方法返回类型 + // }; + //} + NextOrientation = ConnectionType.IsSucceed; return result; } diff --git a/NodeFlow/FlowEnvironment.cs b/NodeFlow/FlowEnvironment.cs index bf8c683..f2709cf 100644 --- a/NodeFlow/FlowEnvironment.cs +++ b/NodeFlow/FlowEnvironment.cs @@ -184,14 +184,15 @@ namespace Serein.NodeFlow /// 存放触发器节点(运行时全部调用) /// private List FlipflopNodes { get; } = []; - private List AutoRegisterTypes { get; } = []; + private Dictionary> AutoRegisterTypes { get; } = []; /// /// 存放委托 /// /// md.Methodname - delegate /// - private ConcurrentDictionary MethodDelegates { get; } = []; + + private ConcurrentDictionary MethodDelegates { get; } = []; /// /// 起始节点私有属性 @@ -256,11 +257,11 @@ namespace Serein.NodeFlow } this.IOC.Reset(); // 开始运行时清空ioc中注册的实例 this.IOC.CustomRegisterInstance(typeof(IFlowEnvironment).FullName,this); - foreach (var type in AutoRegisterTypes) - { - this.IOC.Register(type); - } - await flowStarter.RunAsync(this, nodes, initMethods, loadMethods, exitMethods); + + + + + await flowStarter.RunAsync(this, nodes, AutoRegisterTypes, initMethods, loadMethods, exitMethods); if (flowStarter?.FlipFlopState == RunState.NoStart) { @@ -694,16 +695,16 @@ namespace Serein.NodeFlow } } - public bool TryGetDelegate(string methodName, out Delegate? del) + public bool TryGetDelegateDetails(string methodName, out DelegateDetails? delegateDetails) { - if (!string.IsNullOrEmpty(methodName) && MethodDelegates.TryGetValue(methodName, out del)) + if (!string.IsNullOrEmpty(methodName) && MethodDelegates.TryGetValue(methodName, out delegateDetails)) { - return del != null; + return delegateDetails != null; } else { - del = null; + delegateDetails = null; return false; } } @@ -939,7 +940,16 @@ namespace Serein.NodeFlow { MethodDetailss.Add(nodeLibrary, mdlist); NodeLibrarys.Add(nodeLibrary); - AutoRegisterTypes.AddRange(registerTypes); + + foreach(var kv in registerTypes) + { + if (!AutoRegisterTypes.TryGetValue(kv.Key, out var types)) + { + types = new List(); + AutoRegisterTypes.Add(kv.Key, types); + } + types.AddRange(kv.Value); + } OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibrary, mdlist)); // 通知UI创建dll面板显示 } @@ -947,16 +957,35 @@ namespace Serein.NodeFlow } - private (NodeLibrary?, List ,List) LoadAssembly(string dllPath) + private (NodeLibrary?, Dictionary>, List) LoadAssembly(string dllPath) { try { Assembly assembly = Assembly.LoadFrom(dllPath); // 加载DLL文件 Type[] types = assembly.GetTypes(); // 获取程序集中的所有类型 - List autoRegisterTypes = assembly.GetTypes().Where(t => t.GetCustomAttribute() is not null).ToList(); + Dictionary> autoRegisterTypes = new Dictionary>(); + foreach (Type type in types) + { + var autoRegisterAttribute = type.GetCustomAttribute(); + if (autoRegisterAttribute is not null) + { + if(!autoRegisterTypes.TryGetValue(autoRegisterAttribute.Class,out var valus)) + { + valus = new List(); + autoRegisterTypes.Add(autoRegisterAttribute.Class, valus); + } + valus.Add(type); + } + + } + - List<(Type, string)> scanTypes = assembly.GetTypes().Select(t => { + //Dictionary autoRegisterTypes = assembly.GetTypes().Where(t => t.GetCustomAttribute() is not null).ToList(); + + + + List<(Type, string)> scanTypes = types.Select(t => { if (t.GetCustomAttribute() is DynamicFlowAttribute dynamicFlowAttribute && dynamicFlowAttribute.Scan == true) { @@ -982,10 +1011,10 @@ namespace Serein.NodeFlow { continue; } - var methods = MethodDetailsHelperTmp.GetMethodsToProcess(type); + var methods = MethodDetailsHelper.GetMethodsToProcess(type); foreach(var method in methods) { - (var md, var del) = MethodDetailsHelperTmp.CreateMethodDetails(type, method, assemblyName); + (var md, var del) = MethodDetailsHelper.CreateMethodDetails(type, method, assemblyName); if(md is null || del is null) { Console.WriteLine($"无法加载方法信息:{assemblyName}-{type}-{method}"); diff --git a/NodeFlow/FlowStarter.cs b/NodeFlow/FlowStarter.cs index 61eff64..9025a67 100644 --- a/NodeFlow/FlowStarter.cs +++ b/NodeFlow/FlowStarter.cs @@ -1,4 +1,5 @@ using Serein.Library.Api; +using Serein.Library.Attributes; using Serein.Library.Core.NodeFlow; using Serein.Library.Entity; using Serein.Library.Enums; @@ -109,6 +110,7 @@ namespace Serein.NodeFlow /// public async Task RunAsync(IFlowEnvironment env, List nodes, + Dictionary> autoRegisterTypes, List initMethods, List loadingMethods, List exitMethods) @@ -150,6 +152,7 @@ namespace Serein.NodeFlow #endregion #region 初始化运行环境的Ioc容器 + // 清除节点使用的对象,筛选出需要初始化的方法描述 var thisRuningMds = new List(); thisRuningMds.AddRange(runNodeMd.Where(md => md?.ActingInstanceType is not null)); @@ -178,10 +181,7 @@ namespace Serein.NodeFlow } } - if (IsStopStart) - { - return;// 初始化类型后检查状态 - } + if (IsStopStart) return;// 检查所有dll节点是否存在类型 env.IOC.Build(); // 流程启动前的初始化 @@ -219,25 +219,43 @@ namespace Serein.NodeFlow #region 执行初始化,绑定IOC容器,再执行加载时 + if (autoRegisterTypes.TryGetValue(RegisterSequence.FlowInit, out var flowInitTypes)) + { + foreach (var type in flowInitTypes) + { + env.IOC.Register(type); // 初始化前注册 + } + } + Context.Env.IOC.Build(); // 绑定初始化时注册的类型 //object?[]? args = [Context]; foreach (var md in initMethods) // 初始化 { - if (!env.TryGetDelegate(md.MethodName, out var del)) + if (!env.TryGetDelegateDetails(md.MethodName, out var dd)) { throw new Exception("不存在对应委托"); } - ((Action)del).Invoke(md.ActingInstance, [Context]); + ((Func)dd.EmitDelegate).Invoke(md.ActingInstance, [Context]); + } + Context.Env.IOC.Build(); // 绑定初始化时注册的类型 + + if(autoRegisterTypes.TryGetValue(RegisterSequence.FlowLoading,out var flowLoadingTypes)) + { + foreach (var type in flowLoadingTypes) + { + env.IOC.Register(type); // 初始化前注册 + } } Context.Env.IOC.Build(); // 绑定初始化时注册的类型 foreach (var md in loadingMethods) // 加载 { //object?[]? data = [md.ActingInstance, args]; //md.MethodDelegate.DynamicInvoke(data); - if (!env.TryGetDelegate(md.MethodName, out var del)) + if (!env.TryGetDelegateDetails(md.MethodName, out var dd)) { throw new Exception("不存在对应委托"); } - ((Action)del).Invoke(md.ActingInstance, [Context]); + //((Action)del).Invoke(md.ActingInstance, [Context]); + ((Func)dd.EmitDelegate).Invoke(md.ActingInstance, [Context]); } Context.Env.IOC.Build(); // 预防有人在加载时才注册类型,再绑定一次 #endregion @@ -245,17 +263,17 @@ namespace Serein.NodeFlow #region 设置流程退出时的回调函数 ExitAction = () => { - env.IOC.Run(web => { + env.IOC.Run(web => { web?.Stop(); }); foreach (MethodDetails? md in exitMethods) { - if (!env.TryGetDelegate(md.MethodName, out var del)) + if (!env.TryGetDelegateDetails(md.MethodName, out var dd)) { throw new Exception("不存在对应委托"); } - ((Action)del).Invoke(md.ActingInstance, [Context]); + ((Func)dd.EmitDelegate).Invoke(md.ActingInstance, [Context]); } TerminateAllGlobalFlipflop(); diff --git a/NodeFlow/Model/SingleFlipflopNode.cs b/NodeFlow/Model/SingleFlipflopNode.cs index 86695aa..62a7502 100644 --- a/NodeFlow/Model/SingleFlipflopNode.cs +++ b/NodeFlow/Model/SingleFlipflopNode.cs @@ -2,6 +2,7 @@ using Serein.Library.Entity; using Serein.Library.Enums; using Serein.Library.Ex; +using Serein.Library.Utils; using Serein.NodeFlow.Base; using static Serein.Library.Utils.ChannelFlowInterrupt; @@ -32,7 +33,7 @@ namespace Serein.NodeFlow.Model #endregion MethodDetails md = MethodDetails; - if (!context.Env.TryGetDelegate(md.MethodName, out var del)) + if (!context.Env.TryGetDelegateDetails(md.MethodName, out var dd)) { throw new Exception("不存在对应委托"); } @@ -40,37 +41,31 @@ namespace Serein.NodeFlow.Model try { Task flipflopTask; - if (md.ExplicitDatas.Length == 0) + var args = GetParameters(context, this, md); + var delType = dd.EmitMethodType; + var del = dd.EmitDelegate; + if (delType == EmitHelper.EmitMethodType.HasResultTask && del is Func> hasResultTask) { - if (del is Func> function) + var flipflopTaskObj = await hasResultTask(instance, args); + if(flipflopTaskObj is IFlipflopContext flipflopContext) { - flipflopTask = function.Invoke(md.ActingInstance); + NextOrientation = flipflopContext.State.ToContentType(); + if (flipflopContext.TriggerData is null || flipflopContext.TriggerData.Type == Library.NodeFlow.Tool.TriggerType.Overtime) + { + throw new FlipflopException(base.MethodDetails.MethodName + "触发器超时触发。Guid" + base.Guid); + } + return flipflopContext.TriggerData.Value; } else { - throw new FlipflopException("触发节点非预期的返回类型", true, FlipflopException.CancelClass.Flow); + throw new FlipflopException("触发器节点返回了非预期的类型", true, FlipflopException.CancelClass.Flow); } } else { - var parameters = GetParameters(context, this, md); - if(del is Func> function) - { - flipflopTask = function.Invoke(md.ActingInstance, parameters); - } - else - { - throw new FlipflopException("触发节点非预期的返回类型", true,FlipflopException.CancelClass.Flow); - } + throw new FlipflopException("触发器节点构造了非预期的委托", true, FlipflopException.CancelClass.Flow); } - - IFlipflopContext flipflopContext = (await flipflopTask) ?? throw new FlipflopException("没有返回上下文"); - NextOrientation = flipflopContext.State.ToContentType(); - if(flipflopContext.TriggerData is null || flipflopContext.TriggerData.Type == Library.NodeFlow.Tool.TriggerType.Overtime) - { - throw new FlipflopException(base.MethodDetails.MethodName + "触发器超时触发。Guid"+base.Guid); - } - return flipflopContext.TriggerData.Value; + } catch (FlipflopException ex) { diff --git a/NodeFlow/Serein.NodeFlow.csproj b/NodeFlow/Serein.NodeFlow.csproj index 844d211..c356542 100644 --- a/NodeFlow/Serein.NodeFlow.csproj +++ b/NodeFlow/Serein.NodeFlow.csproj @@ -22,6 +22,7 @@ + diff --git a/NodeFlow/Tool/MethodDetailsHelper.cs b/NodeFlow/Tool/MethodDetailsHelper.cs index 6337500..ee1fbc9 100644 --- a/NodeFlow/Tool/MethodDetailsHelper.cs +++ b/NodeFlow/Tool/MethodDetailsHelper.cs @@ -2,6 +2,7 @@ using Serein.Library.Attributes; using Serein.Library.Core.NodeFlow; using Serein.Library.Entity; +using Serein.Library.Utils; using System; using System.Collections.Concurrent; using System.ComponentModel; @@ -10,7 +11,7 @@ using System.Text.RegularExpressions; namespace Serein.NodeFlow.Tool; -public static class MethodDetailsHelperTmp +public static class MethodDetailsHelper { /// @@ -53,7 +54,7 @@ public static class MethodDetailsHelperTmp /// 创建方法信息 /// /// - public static (MethodDetails?,Delegate?) CreateMethodDetails(Type type, MethodInfo method, string assemblyName) + public static (MethodDetails?, DelegateDetails?) CreateMethodDetails(Type type, MethodInfo method, string assemblyName) { var attribute = method.GetCustomAttribute(); if(attribute is null) @@ -64,12 +65,14 @@ public static class MethodDetailsHelperTmp var dllTypeMethodName = $"{assemblyName}.{type.Name}.{method.Name}"; var explicitDataOfParameters = GetExplicitDataOfParameters(method.GetParameters()); - //// 生成委托 - var methodDelegate = GenerateMethodDelegate(type, // 方法所在的对象类型 - method, // 方法信息 - method.GetParameters(),// 方法参数 - method.ReturnType);// 返回值 + //// 通过表达式树生成委托 + //var methodDelegate = GenerateMethodDelegate(type, // 方法所在的对象类型 + // method, // 方法信息 + // method.GetParameters(),// 方法参数 + // method.ReturnType);// 返回值 + //// 通过表达式树生成委托 + var emitMethodType = EmitHelper.CreateDynamicMethod(method, out var methodDelegate);// 返回值 Type? returnType; bool isTask = IsGenericTask(method.ReturnType, out var taskResult); @@ -114,8 +117,8 @@ public static class MethodDetailsHelperTmp ExplicitDatas = explicitDataOfParameters, ReturnType = returnType, }; - - return (md, methodDelegate); + var dd = new DelegateDetails( emitMethodType, methodDelegate) ; + return (md, dd); } @@ -276,52 +279,52 @@ public static class MethodDetailsHelperTmp return items; } - private static Delegate GenerateMethodDelegate(Type type, MethodInfo methodInfo, ParameterInfo[] parameters, Type returnType) - { - var parameterTypes = parameters.Select(p => p.ParameterType).ToArray(); - var parameterCount = parameters.Length; + //private static Delegate GenerateMethodDelegate(Type type, MethodInfo methodInfo, ParameterInfo[] parameters, Type returnType) + //{ + // var parameterTypes = parameters.Select(p => p.ParameterType).ToArray(); + // var parameterCount = parameters.Length; - if (returnType == typeof(void)) - { - if (parameterCount == 0) - { - // 无返回值,无参数 - return ExpressionHelper.MethodCaller(type, methodInfo); - } - else - { - // 无返回值,有参数 - return ExpressionHelper.MethodCaller(type, methodInfo, parameterTypes); - } - } - // else if (returnType == typeof(Task(); + } + [NodeAction(NodeType.Loading)] + public void Loading(IDynamicContext context) + { + environment.IOC.Run(async (socketServer) => + { + await socketServer.StartAsync("http://*:7525/"); + }); + SereinProjectData projectData = environment.SaveProject(); + } + + + + + } +} diff --git a/Serein.FlowRemoteManagement/Serein.FlowRemoteManagement.csproj b/Serein.FlowRemoteManagement/Serein.FlowRemoteManagement.csproj new file mode 100644 index 0000000..ea3a544 --- /dev/null +++ b/Serein.FlowRemoteManagement/Serein.FlowRemoteManagement.csproj @@ -0,0 +1,19 @@ + + + + net8.0-windows7.0 + enable + enable + D:\Project\C#\DynamicControl\SereinFlow\.Output + + + + + + + + + + + + diff --git a/SereinFlow.sln b/SereinFlow.sln index 4fe2841..ea387dc 100644 --- a/SereinFlow.sln +++ b/SereinFlow.sln @@ -20,6 +20,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library", "Library\S EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Net462DllTest", "Net462DllTest\Net462DllTest.csproj", "{E40EE629-1A38-4011-88E3-9AD036869987}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.FlowRemoteManagement", "Serein.FlowRemoteManagement\Serein.FlowRemoteManagement.csproj", "{3E568C47-74C6-4C28-9D43-C9BA29008DB7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -50,6 +52,10 @@ Global {E40EE629-1A38-4011-88E3-9AD036869987}.Debug|Any CPU.Build.0 = Debug|Any CPU {E40EE629-1A38-4011-88E3-9AD036869987}.Release|Any CPU.ActiveCfg = Release|Any CPU {E40EE629-1A38-4011-88E3-9AD036869987}.Release|Any CPU.Build.0 = Release|Any CPU + {3E568C47-74C6-4C28-9D43-C9BA29008DB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E568C47-74C6-4C28-9D43-C9BA29008DB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E568C47-74C6-4C28-9D43-C9BA29008DB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E568C47-74C6-4C28-9D43-C9BA29008DB7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE