mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-28 18:43:23 +08:00
使用emit代替表达式树构造委托。
内置了websocket server与相应的导航功能,可在实例工程中找到相应的实现。
This commit is contained in:
@@ -16,37 +16,37 @@ namespace Serein.Library.Core.NodeFlow
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public NodeRunCts NodeRunCts { get; set; }
|
// public NodeRunCts NodeRunCts { get; set; }
|
||||||
//public ISereinIOC SereinIoc { get; }
|
// public ISereinIOC SereinIoc { get; }
|
||||||
public IFlowEnvironment Env { get; }
|
public IFlowEnvironment Env { get; }
|
||||||
|
|
||||||
public Task CreateTimingTask(Action action, int time = 100, int count = -1)
|
//public Task CreateTimingTask(Action action, int time = 100, int count = -1)
|
||||||
{
|
//{
|
||||||
if (NodeRunCts == null)
|
// if (NodeRunCts == null)
|
||||||
{
|
// {
|
||||||
NodeRunCts = Env.IOC.Get<NodeRunCts>();
|
// NodeRunCts = Env.IOC.Get<NodeRunCts>();
|
||||||
}
|
// }
|
||||||
// 使用局部变量,避免捕获外部的 `action`
|
// // 使用局部变量,避免捕获外部的 `action`
|
||||||
Action localAction = action;
|
// Action localAction = action;
|
||||||
|
|
||||||
return Task.Run(async () =>
|
// return Task.Run(async () =>
|
||||||
{
|
// {
|
||||||
for (int i = 0; i < count && !NodeRunCts.IsCancellationRequested; i++)
|
// for (int i = 0; i < count && !NodeRunCts.IsCancellationRequested; i++)
|
||||||
{
|
// {
|
||||||
await Task.Delay(time);
|
// await Task.Delay(time);
|
||||||
if (NodeRunCts.IsCancellationRequested) { break; }
|
// if (NodeRunCts.IsCancellationRequested) { break; }
|
||||||
//if (FlowEnvironment.IsGlobalInterrupt)
|
// //if (FlowEnvironment.IsGlobalInterrupt)
|
||||||
//{
|
// //{
|
||||||
// await FlowEnvironment.GetOrCreateGlobalInterruptAsync();
|
// // await FlowEnvironment.GetOrCreateGlobalInterruptAsync();
|
||||||
//}
|
// //}
|
||||||
// 确保对局部变量的引用
|
// // 确保对局部变量的引用
|
||||||
localAction?.Invoke();
|
// localAction?.Invoke();
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 清理引用,避免闭包导致的内存泄漏
|
// // 清理引用,避免闭包导致的内存泄漏
|
||||||
localAction = null;
|
// localAction = null;
|
||||||
});
|
// });
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,36 +19,36 @@ namespace Serein.Library.Framework.NodeFlow
|
|||||||
Env = flowEnvironment;
|
Env = flowEnvironment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NodeRunCts NodeRunCts { get; set; }
|
// public NodeRunCts NodeRunCts { get; set; }
|
||||||
// public ISereinIOC SereinIoc { get; }
|
// public ISereinIOC SereinIoc { get; }
|
||||||
public IFlowEnvironment Env { get; }
|
public IFlowEnvironment Env { get; }
|
||||||
|
|
||||||
public Task CreateTimingTask(Action action, int time = 100, int count = -1)
|
//public Task CreateTimingTask(Action action, int time = 100, int count = -1)
|
||||||
{
|
//{
|
||||||
if(NodeRunCts == null)
|
// if(NodeRunCts == null)
|
||||||
{
|
// {
|
||||||
NodeRunCts = Env.IOC.Get<NodeRunCts>();
|
// NodeRunCts = Env.IOC.Get<NodeRunCts>();
|
||||||
}
|
// }
|
||||||
// 使用局部变量,避免捕获外部的 `action`
|
// // 使用局部变量,避免捕获外部的 `action`
|
||||||
Action localAction = action;
|
// Action localAction = action;
|
||||||
|
|
||||||
return Task.Run(async () =>
|
// return Task.Run(async () =>
|
||||||
{
|
// {
|
||||||
for (int i = 0; i < count && !NodeRunCts.IsCancellationRequested; i++)
|
// for (int i = 0; i < count && !NodeRunCts.IsCancellationRequested; i++)
|
||||||
{
|
// {
|
||||||
await Task.Delay(time);
|
// await Task.Delay(time);
|
||||||
if (NodeRunCts.IsCancellationRequested) { break; }
|
// if (NodeRunCts.IsCancellationRequested) { break; }
|
||||||
//if (FlowEnvironment.IsGlobalInterrupt)
|
// //if (FlowEnvironment.IsGlobalInterrupt)
|
||||||
//{
|
// //{
|
||||||
// await FlowEnvironment.GetOrCreateGlobalInterruptAsync();
|
// // await FlowEnvironment.GetOrCreateGlobalInterruptAsync();
|
||||||
//}
|
// //}
|
||||||
// 确保对局部变量的引用
|
// // 确保对局部变量的引用
|
||||||
localAction?.Invoke();
|
// localAction?.Invoke();
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 清理引用,避免闭包导致的内存泄漏
|
// // 清理引用,避免闭包导致的内存泄漏
|
||||||
localAction = null;
|
// localAction = null;
|
||||||
});
|
// });
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,6 @@ namespace Serein.Library.Api
|
|||||||
/// <param name="time"></param>
|
/// <param name="time"></param>
|
||||||
/// <param name="count"></param>
|
/// <param name="count"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task CreateTimingTask(Action callback, int time = 100, int count = -1);
|
// Task CreateTimingTask(Action callback, int time = 100, int count = -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -468,7 +468,7 @@ namespace Serein.Library.Api
|
|||||||
bool TryGetMethodDetails(string methodName, out MethodDetails md);
|
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);
|
//bool TryGetNodeData(string methodName, out NodeData node);
|
||||||
|
|
||||||
|
|||||||
28
Library/Entity/DelegateDetails.cs
Normal file
28
Library/Entity/DelegateDetails.cs
Normal file
@@ -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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@ namespace Serein.Library.Entity
|
|||||||
ActingInstance = ActingInstance,
|
ActingInstance = ActingInstance,
|
||||||
ActingInstanceType = ActingInstanceType,
|
ActingInstanceType = ActingInstanceType,
|
||||||
MethodDynamicType = MethodDynamicType,
|
MethodDynamicType = MethodDynamicType,
|
||||||
MethodGuid = Guid.NewGuid().ToString(),
|
// MethodGuid = Guid.NewGuid().ToString(),
|
||||||
MethodTips = MethodTips,
|
MethodTips = MethodTips,
|
||||||
ReturnType = ReturnType,
|
ReturnType = ReturnType,
|
||||||
MethodName = MethodName,
|
MethodName = MethodName,
|
||||||
@@ -56,7 +56,7 @@ namespace Serein.Library.Entity
|
|||||||
/// 方法GUID
|
/// 方法GUID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
public string MethodGuid { get; set; }
|
// public string MethodGuid { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 方法名称
|
/// 方法名称
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ namespace Serein.Library.Web
|
|||||||
/// HTTP接口监听类
|
/// HTTP接口监听类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AutoRegister]
|
[AutoRegister]
|
||||||
public class WebServer
|
public class WebApiServer
|
||||||
{
|
{
|
||||||
private readonly IRouter Router;// 路由器
|
private readonly IRouter Router;// 路由器
|
||||||
public WebServer(IRouter router)
|
public WebApiServer(IRouter router)
|
||||||
{
|
{
|
||||||
this.Router = router;
|
this.Router = router;
|
||||||
listener = new HttpListener();
|
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(@"/"))
|
if (!prefixe.Substring(prefixe.Length - 1, 1).Equals(@"/"))
|
||||||
23
Library/Network/WebSocket/Attribute.cs
Normal file
23
Library/Network/WebSocket/Attribute.cs
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
141
Library/Network/WebSocket/Handle/MyHandleConfig.cs
Normal file
141
Library/Network/WebSocket/Handle/MyHandleConfig.cs
Normal file
@@ -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<string, Task> 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<object, Task>)))
|
||||||
|
{
|
||||||
|
args[i] = new Func<object, Task>(async data =>
|
||||||
|
{
|
||||||
|
var jsonText = JsonConvert.SerializeObject(data);
|
||||||
|
await RecoverAsync.Invoke(jsonText);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (type.IsAssignableFrom(typeof(Func<string, Task>)))
|
||||||
|
{
|
||||||
|
args[i] = new Func<string, Task>(async data =>
|
||||||
|
{
|
||||||
|
await RecoverAsync.Invoke(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (type.IsAssignableFrom(typeof(Action<object>)))
|
||||||
|
{
|
||||||
|
args[i] = new Action<object>(async data =>
|
||||||
|
{
|
||||||
|
var jsonText = JsonConvert.SerializeObject(data);
|
||||||
|
await RecoverAsync.Invoke(jsonText);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (type.IsAssignableFrom(typeof(Action<string>)))
|
||||||
|
{
|
||||||
|
args[i] = new Action<string>(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<object, object[], Task<object>> hasResultTask)
|
||||||
|
{
|
||||||
|
result = await hasResultTask(Instance, args);
|
||||||
|
}
|
||||||
|
else if (EmitMethodType == EmitHelper.EmitMethodType.Task && EmitDelegate is Func<object, object[], Task> task)
|
||||||
|
{
|
||||||
|
await task.Invoke(Instance, args);
|
||||||
|
result = null;
|
||||||
|
}
|
||||||
|
else if (EmitMethodType == EmitHelper.EmitMethodType.Func && EmitDelegate is Func<object, object[], object> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
69
Library/Network/WebSocket/Handle/MyHandleModule.cs
Normal file
69
Library/Network/WebSocket/Handle/MyHandleModule.cs
Normal file
@@ -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<string, MyHandleConfig> MyHandleConfigs = new ConcurrentDictionary<string, MyHandleConfig>();
|
||||||
|
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<string, Task> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
118
Library/Network/WebSocket/Handle/SocketMsgHandleHelper.cs
Normal file
118
Library/Network/WebSocket/Handle/SocketMsgHandleHelper.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// (Theme Name ,Data Name) - HandleModule
|
||||||
|
/// </summary>
|
||||||
|
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<AutoSocketModuleAttribute>();
|
||||||
|
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<AutoSocketModuleAttribute>();
|
||||||
|
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<AutoSocketHandleAttribute>();
|
||||||
|
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<string, Task> RecoverAsync, string message)
|
||||||
|
{
|
||||||
|
JObject json = JObject.Parse(message);
|
||||||
|
await Task.Run(() =>
|
||||||
|
{
|
||||||
|
foreach (var module in MyHandleModuleDict.Values)
|
||||||
|
{
|
||||||
|
module.HandleSocketMsg(RecoverAsync, json);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
41
Library/Network/WebSocket/SocketControlBase.cs
Normal file
41
Library/Network/WebSocket/SocketControlBase.cs
Normal file
@@ -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<string> 类型是特殊的,会用一个委托代替,这个委托可以将文本信息发送到客户端
|
||||||
|
// // Action<object> 类型是特殊的,会用一个委托代替,这个委托可以将对象转成json发送到客户端
|
||||||
|
|
||||||
|
// [AutoSocketHandle]
|
||||||
|
// public void AddUser(User user,Action<string> Recover)
|
||||||
|
// {
|
||||||
|
// Console.WriteLine(user.ToString());
|
||||||
|
// Recover("ok");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// [AutoSocketHandle(ThemeValue = "Remote")]
|
||||||
|
// public void DeleteUser(User user, Action<string> Recover)
|
||||||
|
// {
|
||||||
|
// Console.WriteLine(user.ToString());
|
||||||
|
// }
|
||||||
|
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
||||||
136
Library/Network/WebSocket/WebSocketClient.cs
Normal file
136
Library/Network/WebSocket/WebSocketClient.cs
Normal file
@@ -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<byte>(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<byte>(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<string, HandldConfig> ThemeConfigs = new ConcurrentDictionary<string, HandldConfig>();
|
||||||
|
|
||||||
|
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*/
|
||||||
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Serein.Library.Network.WebSocketCommunication
|
|
||||||
{
|
|
||||||
public class WebSocketRouter
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Serein.Library.Network.WebSocketCommunication
|
namespace Serein.Library.Network.WebSocketCommunication
|
||||||
{
|
{
|
||||||
|
[AutoRegister]
|
||||||
public class WebSocketServer
|
public class WebSocketServer
|
||||||
{
|
{
|
||||||
public Func<string,Action> OnReceiveMsg;
|
public WebSocketServer()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public SocketMsgHandleHelper MsgHandleHelper { get; } = new SocketMsgHandleHelper();
|
||||||
|
|
||||||
|
HttpListener listener;
|
||||||
public async Task StartAsync(string url)
|
public async Task StartAsync(string url)
|
||||||
{
|
{
|
||||||
HttpListener listener = new HttpListener();
|
listener = new HttpListener();
|
||||||
listener.Prefixes.Add(url);
|
listener.Prefixes.Add(url);
|
||||||
listener.Start();
|
listener.Start();
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
var context = await listener.GetContextAsync();
|
try
|
||||||
if (context.Request.IsWebSocketRequest)
|
|
||||||
{
|
{
|
||||||
var webSocketContext = await context.AcceptWebSocketAsync(null); //新连接
|
var context = await listener.GetContextAsync();
|
||||||
_ = HandleWebSocketAsync(webSocketContext.WebSocket); // 处理消息
|
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)
|
private async Task HandleWebSocketAsync(WebSocket webSocket)
|
||||||
{
|
{
|
||||||
|
Func<string,Task> SendAsync = async (text) =>
|
||||||
|
{
|
||||||
|
await WebSocketServer.SendAsync(webSocket, text);
|
||||||
|
};
|
||||||
var buffer = new byte[1024];
|
var buffer = new byte[1024];
|
||||||
while (webSocket.State == WebSocketState.Open)
|
while (webSocket.State == WebSocketState.Open)
|
||||||
{
|
{
|
||||||
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
|
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
|
||||||
if (result.MessageType == WebSocketMessageType.Close)
|
if (result.MessageType == WebSocketMessageType.Close)
|
||||||
{
|
{
|
||||||
|
SendAsync = null;
|
||||||
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
|
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
|
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
|
||||||
Console.WriteLine($"Received: {message}");
|
|
||||||
var action = OnReceiveMsg.Invoke(message);
|
|
||||||
action?.Invoke();
|
|
||||||
|
|
||||||
// 回显消息(可选)
|
_ = MsgHandleHelper.HandleMsgAsync(SendAsync, message);
|
||||||
//ar echoMessage = Encoding.UTF8.GetBytes(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<byte>(echoMessage, 0, echoMessage.Length), result.MessageType, result.EndOfMessage, CancellationToken.None);
|
//await webSocket.SendAsync(new ArraySegment<byte>(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<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,12 +11,31 @@ namespace Serein.Library.Attributes
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum RegisterSequence
|
||||||
|
{ /// <summary>
|
||||||
|
/// 不自动初始化
|
||||||
|
/// </summary>
|
||||||
|
Node,
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化后
|
||||||
|
/// </summary>
|
||||||
|
FlowInit,
|
||||||
|
/// <summary>
|
||||||
|
/// 加载后
|
||||||
|
/// </summary>
|
||||||
|
FlowLoading,
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 表示该类自动注册(单例模式)
|
/// 表示该类自动注册(单例模式)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
public sealed class AutoRegisterAttribute : Attribute
|
public sealed class AutoRegisterAttribute : Attribute
|
||||||
{
|
{
|
||||||
|
public AutoRegisterAttribute(RegisterSequence Class = RegisterSequence.FlowInit)
|
||||||
|
{
|
||||||
|
this.Class = Class;
|
||||||
|
}
|
||||||
|
public RegisterSequence Class ;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;net462</TargetFrameworks>
|
<TargetFrameworks>net8-windows;net462</TargetFrameworks>
|
||||||
<BaseOutputPath>D:\Project\C#\DynamicControl\SereinFlow\.Output</BaseOutputPath>
|
<BaseOutputPath>D:\Project\C#\DynamicControl\SereinFlow\.Output</BaseOutputPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
@@ -21,4 +21,8 @@
|
|||||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Network\Socket\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
151
Library/Utils/EmitHelper.cs
Normal file
151
Library/Utils/EmitHelper.cs
Normal file
@@ -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<T>
|
||||||
|
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<T>(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<object>),
|
||||||
|
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<object, object[], Task<object>>));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
emitMethodType = EmitMethodType.Task;
|
||||||
|
@delegate = dynamicMethod.CreateDelegate(typeof(Func<object, object[], Task>));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
emitMethodType = EmitMethodType.Func;
|
||||||
|
@delegate = dynamicMethod.CreateDelegate(typeof(Func<object, object[], object>));
|
||||||
|
|
||||||
|
}
|
||||||
|
return emitMethodType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
446
Library/Utils/ExpressionHelper.cs
Normal file
446
Library/Utils/ExpressionHelper.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 基于类型创建表达式树反射委托
|
||||||
|
/// </summary>
|
||||||
|
public static class ExpressionHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 缓存表达式树反射方法
|
||||||
|
/// </summary>
|
||||||
|
private static ConcurrentDictionary<string, Delegate> Cache { get; } = new ConcurrentDictionary<string, Delegate>();
|
||||||
|
|
||||||
|
|
||||||
|
#region 基于类型的表达式反射构建委托
|
||||||
|
|
||||||
|
#region 属性、字段的委托创建(表达式反射)
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 动态获取属性值
|
||||||
|
/// </summary>
|
||||||
|
public static Delegate PropertyGetter(Type type, string propertyName)
|
||||||
|
{
|
||||||
|
string cacheKey = $"{type.FullName}.{propertyName}.Getter";
|
||||||
|
return Cache.GetOrAdd(cacheKey, _ => CreateGetterDelegate(type, propertyName));
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 动态获取属性值
|
||||||
|
/// </summary>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 动态设置属性值
|
||||||
|
/// </summary>
|
||||||
|
public static Delegate PropertySetter(Type type, string propertyName)
|
||||||
|
{
|
||||||
|
string cacheKey = $"{type.FullName}.{propertyName}.Setter";
|
||||||
|
return Cache.GetOrAdd(cacheKey, _ => CreateSetterDelegate(type, propertyName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 动态设置属性值
|
||||||
|
/// </summary>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 动态获取字段值
|
||||||
|
/// </summary>
|
||||||
|
public static Delegate FieldGetter(Type type, string fieldName)
|
||||||
|
{
|
||||||
|
string cacheKey = $"{type.FullName}.{fieldName}.FieldGetter";
|
||||||
|
return Cache.GetOrAdd(cacheKey, _ => CreateFieldGetterDelegate(type, fieldName));
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 动态获取字段值
|
||||||
|
/// </summary>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 动态设置字段值
|
||||||
|
/// </summary>
|
||||||
|
public static Delegate FieldSetter(Type type, string fieldName)
|
||||||
|
{
|
||||||
|
string cacheKey = $"{type.FullName}.{fieldName}.FieldSetter";
|
||||||
|
return Cache.GetOrAdd(cacheKey, _ => CreateFieldSetterDelegate(type, fieldName));
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 动态设置字段值
|
||||||
|
/// </summary>
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 表达式树构建无参数,无返回值方法
|
||||||
|
/// </summary>
|
||||||
|
public static Delegate MethodCaller(Type type, MethodInfo methodInfo)
|
||||||
|
{
|
||||||
|
string cacheKey = $"{type.FullName}.{methodInfo.Name}.MethodCaller";
|
||||||
|
return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegate(type, methodInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 表达式树构建无参数,无返回值方法
|
||||||
|
/// </summary>
|
||||||
|
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<object>
|
||||||
|
return lambda.Compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 表达式树构建无参数,有返回值方法
|
||||||
|
/// </summary>
|
||||||
|
public static Delegate MethodCallerHaveResult(Type type, MethodInfo methodInfo)
|
||||||
|
{
|
||||||
|
string cacheKey = $"{type.FullName}.{methodInfo.Name}.MethodCallerHaveResult";
|
||||||
|
return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateHaveResult(type, methodInfo));
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 表达式树构建无参数,有返回值方法
|
||||||
|
/// </summary>
|
||||||
|
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<Func<object, Task>>(Expression.Convert(methodCall, typeof(Task)), parameter);
|
||||||
|
return lambda.Compile();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var lambda = Expression.Lambda<Func<object, Task<object>>>(Expression.Convert(methodCall, typeof(Task<object>)), parameter);
|
||||||
|
return lambda.Compile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var lambda = Expression.Lambda<Func<object, object>>(Expression.Convert(methodCall, typeof(object)), parameter);
|
||||||
|
return lambda.Compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 表达式树构建多个参数,无返回值的方法
|
||||||
|
/// </summary>
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 表达式树构建多个参数,无返回值的方法
|
||||||
|
/// </summary>
|
||||||
|
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<object, object[]>
|
||||||
|
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<object, object[], object>
|
||||||
|
return lambda.Compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 表达式树构建多个参数,有返回值的方法
|
||||||
|
/// </summary>
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 表达式树构建多个参数,有返回值的方法
|
||||||
|
/// </summary>
|
||||||
|
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<object, object[], object>
|
||||||
|
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<Func<object, object[], Task>>
|
||||||
|
(Expression.Convert(methodCall, typeof(Task)), instanceParam, argsParam);
|
||||||
|
return lambda.Compile();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var lambda = Expression.Lambda<Func<object, object[], Task<object>>>
|
||||||
|
(Expression.Convert(methodCall, typeof(Task<object>)), instanceParam, argsParam);
|
||||||
|
return lambda.Compile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var lambda = Expression.Lambda<Func<object, object[], object>>
|
||||||
|
(Expression.Convert(methodCall, typeof(object)), instanceParam, argsParam);
|
||||||
|
return lambda.Compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 表达式树构建无参数,有返回值(Task<object>)的方法(触发器)
|
||||||
|
/// </summary>
|
||||||
|
public static Delegate MethodCallerAsync(Type type, MethodInfo methodInfo)
|
||||||
|
{
|
||||||
|
string cacheKey = $"{type.FullName}.{methodInfo.Name}.MethodCallerAsync";
|
||||||
|
return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateAsync(type, methodInfo));
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 表达式树构建无参数,有返回值(Task<object>)的方法(触发器)
|
||||||
|
/// </summary>
|
||||||
|
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<Func<object, Task<object>>>(
|
||||||
|
Expression.Convert(methodCall, typeof(Task<object>)), parameter);
|
||||||
|
// Func<object, Task<object>>
|
||||||
|
return lambda.Compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 表达式树构建多个参数,有返回值(Task-object)的方法(触发器)
|
||||||
|
/// </summary>
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 表达式树构建多个参数,有返回值(Task<object>)的方法(触发器)
|
||||||
|
/// </summary>
|
||||||
|
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<Func<object, object[], Task<IFlipflopContext>>>(
|
||||||
|
Expression.Convert(methodCall, typeof(Task<IFlipflopContext>)),
|
||||||
|
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<T>
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ namespace Serein.Library.Utils
|
|||||||
/// 已完成注入的实例集合
|
/// 已完成注入的实例集合
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ConcurrentDictionary<string, object> _dependencies;
|
private readonly ConcurrentDictionary<string, object> _dependencies;
|
||||||
|
private readonly ConcurrentDictionary<string, object[]> _registerParameterss;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 未完成注入的实例集合。
|
/// 未完成注入的实例集合。
|
||||||
@@ -40,9 +41,10 @@ namespace Serein.Library.Utils
|
|||||||
|
|
||||||
public SereinIOC()
|
public SereinIOC()
|
||||||
{
|
{
|
||||||
// 首先注册自己
|
|
||||||
_dependencies = new ConcurrentDictionary<string, object>();
|
_dependencies = new ConcurrentDictionary<string, object>();
|
||||||
|
_registerParameterss = new ConcurrentDictionary<string, object[]>();
|
||||||
_typeMappings = new ConcurrentDictionary<string, Type>();
|
_typeMappings = new ConcurrentDictionary<string, Type>();
|
||||||
|
|
||||||
_unfinishedDependencies = new ConcurrentDictionary<string, List<(object, PropertyInfo)>>();
|
_unfinishedDependencies = new ConcurrentDictionary<string, List<(object, PropertyInfo)>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +59,7 @@ namespace Serein.Library.Utils
|
|||||||
/// <param name="parameters">参数</param>
|
/// <param name="parameters">参数</param>
|
||||||
public bool Register(Type type, params object[] parameters)
|
public bool Register(Type type, params object[] parameters)
|
||||||
{
|
{
|
||||||
return RegisterType(type?.FullName, type);
|
return RegisterType(type?.FullName, type, parameters);
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 注册类型
|
/// 注册类型
|
||||||
@@ -67,7 +69,7 @@ namespace Serein.Library.Utils
|
|||||||
public bool Register<T>(params object[] parameters)
|
public bool Register<T>(params object[] parameters)
|
||||||
{
|
{
|
||||||
var type = typeof(T);
|
var type = typeof(T);
|
||||||
return RegisterType(type.FullName, type);
|
return RegisterType(type.FullName, type, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -78,7 +80,7 @@ namespace Serein.Library.Utils
|
|||||||
public bool Register<TService, TImplementation>(params object[] parameters)
|
public bool Register<TService, TImplementation>(params object[] parameters)
|
||||||
where TImplementation : TService
|
where TImplementation : TService
|
||||||
{
|
{
|
||||||
return RegisterType(typeof(TService).FullName, typeof(TImplementation));
|
return RegisterType(typeof(TService).FullName, typeof(TImplementation), parameters);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -132,6 +134,12 @@ namespace Serein.Library.Utils
|
|||||||
}
|
}
|
||||||
public object Get(Type type)
|
public object Get(Type type)
|
||||||
{
|
{
|
||||||
|
var instance = Get(type.FullName);
|
||||||
|
if(instance is null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("类型没有注册:" + type.FullName);
|
||||||
|
}
|
||||||
|
|
||||||
return Get(type.FullName);
|
return Get(type.FullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,6 +178,7 @@ namespace Serein.Library.Utils
|
|||||||
disposable?.Dispose();
|
disposable?.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_registerParameterss?.Clear();
|
||||||
_unfinishedDependencies?.Clear();
|
_unfinishedDependencies?.Clear();
|
||||||
_typeMappings?.Clear();
|
_typeMappings?.Clear();
|
||||||
_dependencies?.Clear();
|
_dependencies?.Clear();
|
||||||
@@ -185,11 +194,13 @@ namespace Serein.Library.Utils
|
|||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public Type Type { get; set; }
|
public Type Type { get; set; }
|
||||||
}
|
}
|
||||||
|
private const string FlowBaseClassName = "<>$FlowBaseClass!@#";
|
||||||
public Dictionary<string, List<string>> BuildDependencyTree()
|
public Dictionary<string, List<string>> BuildDependencyTree()
|
||||||
{
|
{
|
||||||
var dependencyMap = new Dictionary<string, List<string>>();
|
var dependencyMap = new Dictionary<string, List<string>>();
|
||||||
|
//var tmpTypeFullName = new HashSet<string>();
|
||||||
|
//var tmpTypeFullName2 = new HashSet<string>();
|
||||||
|
dependencyMap[FlowBaseClassName] = new List<string>();
|
||||||
foreach (var typeMapping in _typeMappings)
|
foreach (var typeMapping in _typeMappings)
|
||||||
{
|
{
|
||||||
var constructor = GetConstructorWithMostParameters(typeMapping.Value); // 获取参数最多的构造函数
|
var constructor = GetConstructorWithMostParameters(typeMapping.Value); // 获取参数最多的构造函数
|
||||||
@@ -198,14 +209,37 @@ namespace Serein.Library.Utils
|
|||||||
var parameters = constructor.GetParameters()
|
var parameters = constructor.GetParameters()
|
||||||
.Select(p => p.ParameterType)
|
.Select(p => p.ParameterType)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
//if(parameters.Count == 0)
|
||||||
|
//{
|
||||||
|
// if (!dependencyMap.ContainsKey(typeMapping.Value.FullName))
|
||||||
|
// {
|
||||||
|
// dependencyMap[typeMapping.Value.FullName] = new List<string>();
|
||||||
|
// }
|
||||||
|
// 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<string>();
|
if (!dependencyMap.ContainsKey(param.FullName))
|
||||||
|
{
|
||||||
|
dependencyMap[param.FullName] = new List<string>();
|
||||||
|
}
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -288,29 +322,44 @@ namespace Serein.Library.Utils
|
|||||||
|
|
||||||
public object CreateInstance(string typeName)
|
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 constructor = GetConstructorWithMostParameters(type);
|
||||||
var parameters = constructor.GetParameters();
|
var parameters = constructor.GetParameters();
|
||||||
var args = new object[parameters.Length];
|
var args = new object[parameters.Length];
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < parameters.Length; i++)
|
for (int i = 0; i < parameters.Length; i++)
|
||||||
{
|
{
|
||||||
|
var argType = parameters[i].ParameterType;
|
||||||
var fullName = parameters[i].ParameterType.FullName;
|
var fullName = parameters[i].ParameterType.FullName;
|
||||||
if (!_dependencies.TryGetValue(fullName, out var argObj))
|
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;
|
args[i] = argObj;
|
||||||
}
|
}
|
||||||
var value = Activator.CreateInstance(type, args);
|
instance = Activator.CreateInstance(type, args);
|
||||||
InjectDependencies(value); // 完成创建后注入实例需要的特性依赖项
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
InjectDependencies(instance); // 完成创建后注入实例需要的特性依赖项
|
||||||
|
_dependencies[typeName] = instance;
|
||||||
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -330,6 +379,10 @@ namespace Serein.Library.Utils
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var value = CreateInstance(typeName);
|
var value = CreateInstance(typeName);
|
||||||
|
if(value is null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
_dependencies[typeName] = value;
|
_dependencies[typeName] = value;
|
||||||
OnIOCMembersChanged.Invoke(new IOCMembersChangedEventArgs(typeName, value));
|
OnIOCMembersChanged.Invoke(new IOCMembersChangedEventArgs(typeName, value));
|
||||||
}
|
}
|
||||||
@@ -349,11 +402,15 @@ namespace Serein.Library.Utils
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="typeFull"></param>
|
/// <param name="typeFull"></param>
|
||||||
/// <param name="type"></param>
|
/// <param name="type"></param>
|
||||||
private bool RegisterType(string typeFull, Type type)
|
private bool RegisterType(string typeFull, Type type, params object[] parameters)
|
||||||
{
|
{
|
||||||
if (!_typeMappings.ContainsKey(typeFull))
|
if (!_typeMappings.ContainsKey(typeFull))
|
||||||
{
|
{
|
||||||
_typeMappings[typeFull] = type;
|
_typeMappings[typeFull] = type;
|
||||||
|
if(parameters.Length > 0)
|
||||||
|
{
|
||||||
|
_registerParameterss[typeFull] = parameters;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -432,15 +489,8 @@ namespace Serein.Library.Utils
|
|||||||
public void Run<T>(Action<T> action)
|
public void Run<T>(Action<T> action)
|
||||||
{
|
{
|
||||||
var service = Get<T>();
|
var service = Get<T>();
|
||||||
if (service == null)
|
action(service);
|
||||||
{
|
|
||||||
throw new Exception("类型没有注册:"+typeof(T).FullName);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
action(service);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Run<T1, T2>(Action<T1, T2> action)
|
public void Run<T1, T2>(Action<T1, T2> action)
|
||||||
|
|||||||
@@ -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);
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Serein.Library.Utils
|
|
||||||
{
|
|
||||||
public class NodeRunCts : CancellationTokenSource
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
using IoTClient.Clients.PLC;
|
using IoTClient.Common.Enums;
|
||||||
using IoTClient.Common.Enums;
|
|
||||||
using Net462DllTest.Enums;
|
using Net462DllTest.Enums;
|
||||||
using Net462DllTest.Model;
|
using Net462DllTest.Model;
|
||||||
using Net462DllTest.Signal;
|
|
||||||
using Net462DllTest.Trigger;
|
using Net462DllTest.Trigger;
|
||||||
using Net462DllTest.Web;
|
using Net462DllTest.Web;
|
||||||
using Serein.Library.Api;
|
using Serein.Library.Api;
|
||||||
@@ -10,25 +8,14 @@ using Serein.Library.Attributes;
|
|||||||
using Serein.Library.Enums;
|
using Serein.Library.Enums;
|
||||||
using Serein.Library.Ex;
|
using Serein.Library.Ex;
|
||||||
using Serein.Library.Framework.NodeFlow;
|
using Serein.Library.Framework.NodeFlow;
|
||||||
|
using Serein.Library.Network.WebSocketCommunication;
|
||||||
using Serein.Library.NodeFlow.Tool;
|
using Serein.Library.NodeFlow.Tool;
|
||||||
using Serein.Library.Utils;
|
|
||||||
using Serein.Library.Web;
|
using Serein.Library.Web;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Net462DllTest.LogicControl
|
namespace Net462DllTest.LogicControl
|
||||||
{
|
{
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
|
||||||
public sealed class AutoSocketAttribute : Attribute
|
|
||||||
{
|
|
||||||
public string BusinessField;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[AutoRegister]
|
[AutoRegister]
|
||||||
[DynamicFlow("[SiemensPlc]")]
|
[DynamicFlow("[SiemensPlc]")]
|
||||||
@@ -48,33 +35,44 @@ namespace Net462DllTest.LogicControl
|
|||||||
[NodeAction(NodeType.Init)]
|
[NodeAction(NodeType.Init)]
|
||||||
public void Init(IDynamicContext context)
|
public void Init(IDynamicContext context)
|
||||||
{
|
{
|
||||||
context.Env.IOC.Register<IRouter, Router>();
|
context.Env.IOC.Register<WebSocketServer>();
|
||||||
context.Env.IOC.Register<WebServer>();
|
context.Env.IOC.Register<WebSocketClient>();
|
||||||
|
|
||||||
|
context.Env.IOC.Register<IRouter, Router>();
|
||||||
|
context.Env.IOC.Register<WebApiServer>();
|
||||||
|
|
||||||
|
|
||||||
//context.Env.IOC.Register<SocketServer>();
|
|
||||||
//context.Env.IOC.Register<SocketClient>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[NodeAction(NodeType.Loading)] // Loading 初始化完成已注入依赖项,可以开始逻辑上的操作
|
[NodeAction(NodeType.Loading)] // Loading 初始化完成已注入依赖项,可以开始逻辑上的操作
|
||||||
public void Loading(IDynamicContext context)
|
public void Loading(IDynamicContext context)
|
||||||
{
|
{
|
||||||
// 注册控制器
|
// 注册控制器
|
||||||
context.Env.IOC.Run<IRouter, WebServer>((router, web) => {
|
context.Env.IOC.Run<IRouter, WebApiServer>((router, apiServer) => {
|
||||||
router.RegisterController(typeof(CommandController));
|
router.RegisterController(typeof(FlowController));
|
||||||
web.Start("http://*:8089/"); // 开启 Web 服务
|
apiServer.Start("http://*:8089/"); // 开启 Web Api 服务
|
||||||
});
|
});
|
||||||
|
|
||||||
//context.Env.IOC.Run<SocketServer>(server => {
|
context.Env.IOC.Run<WebSocketServer>(async (socketServer) => {
|
||||||
// server.Start(5000); // 开启 Socket 监听
|
// socketServer.RegisterModuleInstance(userService);
|
||||||
//});
|
await socketServer.StartAsync("http://localhost:5005/"); // 开启 Web Socket 监听
|
||||||
|
});
|
||||||
|
context.Env.IOC.Run<WebSocketClient>(async client => {
|
||||||
|
await client.ConnectAsync("ws://localhost:5005/"); // 连接到服务器
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[NodeAction(NodeType.Exit)] // 流程结束时自动执行
|
[NodeAction(NodeType.Exit)] // 流程结束时自动执行
|
||||||
public void Exit(IDynamicContext context)
|
public void Exit(IDynamicContext context)
|
||||||
{
|
{
|
||||||
context.Env.IOC.Run<WebServer>((web) =>
|
context.Env.IOC.Run<WebApiServer>((apiServer) =>
|
||||||
{
|
{
|
||||||
web?.Stop(); // 关闭 Web 服务
|
apiServer?.Stop(); // 关闭 Web 服务
|
||||||
|
|
||||||
|
});
|
||||||
|
context.Env.IOC.Run<WebSocketServer>((socketServer) =>
|
||||||
|
{
|
||||||
|
socketServer?.Stop(); // 关闭 Web 服务
|
||||||
});
|
});
|
||||||
MyPlc.Close();
|
MyPlc.Close();
|
||||||
MyPlc.CancelAllTasks();
|
MyPlc.CancelAllTasks();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
using Net462DllTest.Signal;
|
using Net462DllTest.Signal;
|
||||||
|
using Net462DllTest.Trigger;
|
||||||
using Net462DllTest.ViewModel;
|
using Net462DllTest.ViewModel;
|
||||||
using Serein.Library.Api;
|
using Serein.Library.Api;
|
||||||
using Serein.Library.Attributes;
|
using Serein.Library.Attributes;
|
||||||
@@ -17,39 +18,10 @@ using System.Windows.Forms;
|
|||||||
namespace Net462DllTest.LogicControl
|
namespace Net462DllTest.LogicControl
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 视图管理
|
|
||||||
/// </summary>
|
|
||||||
[AutoRegister]
|
[AutoRegister]
|
||||||
public class ViewManagement:ChannelFlowTrigger<CommandSignal>
|
|
||||||
{
|
|
||||||
private readonly List<Form> forms = new List<Form>();
|
|
||||||
/// <summary>
|
|
||||||
/// 打开窗口
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="form">要打开的窗口类型</param>
|
|
||||||
/// <param name="isTop">是否置顶</param>
|
|
||||||
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]")]
|
[DynamicFlow("[View]")]
|
||||||
public class ViewLogicControl
|
public class ViewLogicControl
|
||||||
{
|
{
|
||||||
@@ -67,10 +39,10 @@ namespace Net462DllTest.LogicControl
|
|||||||
{
|
{
|
||||||
try
|
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)
|
if (triggerData.Type == TriggerType.Overtime)
|
||||||
{
|
{
|
||||||
throw new FlipflopException("超时取消");
|
return new FlipflopContext(FlipflopStateType.Cancel, triggerData.Value);
|
||||||
}
|
}
|
||||||
return new FlipflopContext(FlipflopStateType.Succeed, triggerData.Value);
|
return new FlipflopContext(FlipflopStateType.Succeed, triggerData.Value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,9 @@
|
|||||||
<Reference Include="IoTClient, Version=1.0.40.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="IoTClient, Version=1.0.40.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\IoTClient.1.0.40\lib\netstandard2.0\IoTClient.dll</HintPath>
|
<HintPath>..\packages\IoTClient.1.0.40\lib\netstandard2.0\IoTClient.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Drawing" />
|
<Reference Include="System.Drawing" />
|
||||||
@@ -72,6 +75,7 @@
|
|||||||
<Compile Include="Enums\FromValue.cs" />
|
<Compile Include="Enums\FromValue.cs" />
|
||||||
<Compile Include="Signal\CommandSignal.cs" />
|
<Compile Include="Signal\CommandSignal.cs" />
|
||||||
<Compile Include="Signal\PLCVarSignal.cs" />
|
<Compile Include="Signal\PLCVarSignal.cs" />
|
||||||
|
<Compile Include="Trigger\ViewManagement.cs" />
|
||||||
<Compile Include="Utils\GSModel.cs" />
|
<Compile Include="Utils\GSModel.cs" />
|
||||||
<Compile Include="Utils\RelayCommand.cs" />
|
<Compile Include="Utils\RelayCommand.cs" />
|
||||||
<Compile Include="ViewModel\FromWorkBenchViewModel.cs" />
|
<Compile Include="ViewModel\FromWorkBenchViewModel.cs" />
|
||||||
@@ -87,7 +91,8 @@
|
|||||||
<Compile Include="View\TestFormView.Designer.cs">
|
<Compile Include="View\TestFormView.Designer.cs">
|
||||||
<DependentUpon>TestFormView.cs</DependentUpon>
|
<DependentUpon>TestFormView.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Web\CommandController.cs" />
|
<Compile Include="Web\FlowController.cs" />
|
||||||
|
<Compile Include="Web\UserService.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\..\DynamicControl\SereinFlow\Library.Framework\Serein.Library.Framework.csproj">
|
<ProjectReference Include="..\..\..\DynamicControl\SereinFlow\Library.Framework\Serein.Library.Framework.csproj">
|
||||||
|
|||||||
49
Net462DllTest/Trigger/ViewManagement.cs
Normal file
49
Net462DllTest/Trigger/ViewManagement.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 视图管理
|
||||||
|
/// </summary>
|
||||||
|
[AutoRegister]
|
||||||
|
public class ViewManagement : ChannelFlowTrigger<CommandSignal>
|
||||||
|
{
|
||||||
|
public ViewManagement(IFlowEnvironment environment)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
public int Id = new Random().Next(1, 10000);
|
||||||
|
private readonly List<Form> forms = new List<Form>();
|
||||||
|
/// <summary>
|
||||||
|
/// 打开窗口
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="form">要打开的窗口类型</param>
|
||||||
|
/// <param name="isTop">是否置顶</param>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
3
Net462DllTest/View/FromWorkBenchView.Designer.cs
generated
3
Net462DllTest/View/FromWorkBenchView.Designer.cs
generated
@@ -43,6 +43,7 @@
|
|||||||
this.button1.TabIndex = 0;
|
this.button1.TabIndex = 0;
|
||||||
this.button1.Text = "查看状态";
|
this.button1.Text = "查看状态";
|
||||||
this.button1.UseVisualStyleBackColor = true;
|
this.button1.UseVisualStyleBackColor = true;
|
||||||
|
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||||
//
|
//
|
||||||
// textBoxPlcInfo
|
// textBoxPlcInfo
|
||||||
//
|
//
|
||||||
@@ -60,6 +61,7 @@
|
|||||||
this.button2.TabIndex = 2;
|
this.button2.TabIndex = 2;
|
||||||
this.button2.Text = "触发";
|
this.button2.Text = "触发";
|
||||||
this.button2.UseVisualStyleBackColor = true;
|
this.button2.UseVisualStyleBackColor = true;
|
||||||
|
this.button2.Click += new System.EventHandler(this.button2_Click);
|
||||||
//
|
//
|
||||||
// listBoxCommand
|
// listBoxCommand
|
||||||
//
|
//
|
||||||
@@ -90,6 +92,7 @@
|
|||||||
this.Controls.Add(this.button1);
|
this.Controls.Add(this.button1);
|
||||||
this.Name = "FromWorkBenchView";
|
this.Name = "FromWorkBenchView";
|
||||||
this.Text = "Form1";
|
this.Text = "Form1";
|
||||||
|
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FromWorkBenchView_FormClosing);
|
||||||
this.Load += new System.EventHandler(this.FromWorkBenchView_Load);
|
this.Load += new System.EventHandler(this.FromWorkBenchView_Load);
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
this.PerformLayout();
|
this.PerformLayout();
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ namespace Net462DllTest
|
|||||||
public FromWorkBenchView(IFlowEnvironment env)
|
public FromWorkBenchView(IFlowEnvironment env)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
ViewModel = env.IOC.Get<FromWorkBenchViewModel>(); // 获取对象
|
ViewModel = env.IOC.Get<FromWorkBenchViewModel>();
|
||||||
if(ViewModel is null)
|
if (ViewModel is null)
|
||||||
{
|
{
|
||||||
Console.WriteLine("创建对象并注入依赖项");
|
Console.WriteLine("创建对象并注入依赖项");
|
||||||
ViewModel = env.IOC.Instantiate<FromWorkBenchViewModel>();
|
ViewModel = env.IOC.Instantiate<FromWorkBenchViewModel>();
|
||||||
@@ -41,14 +41,25 @@ namespace Net462DllTest
|
|||||||
listBoxCommand.DataBindings.Add(nameof(listBoxCommand.SelectedItem), ViewModel, nameof(ViewModel.SelectedSignal), false, DataSourceUpdateMode.OnPropertyChanged);
|
listBoxCommand.DataBindings.Add(nameof(listBoxCommand.SelectedItem), ViewModel, nameof(ViewModel.SelectedSignal), false, DataSourceUpdateMode.OnPropertyChanged);
|
||||||
listBoxCommand.SelectedIndexChanged += (s, e) => listBoxCommand.DataBindings[nameof(listBoxCommand.SelectedItem)].WriteValue();
|
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_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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,26 +13,123 @@ using System.Threading.Tasks;
|
|||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using Net462DllTest.LogicControl;
|
using Net462DllTest.LogicControl;
|
||||||
using Net462DllTest.Model;
|
using Net462DllTest.Model;
|
||||||
|
using Serein.Library.Network.WebSocketCommunication;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Net462DllTest.ViewModel
|
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 SiemensPlcDevice Device;
|
||||||
private readonly ViewManagement viewManagement;
|
private readonly ViewManagement viewManagement;
|
||||||
private readonly PlcVarModelDataProxy plcVarModelDataProxy;
|
private readonly PlcVarModelDataProxy plcVarModelDataProxy;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public FromWorkBenchViewModel(SiemensPlcDevice Device,
|
public FromWorkBenchViewModel(SiemensPlcDevice Device,
|
||||||
ViewManagement viewManagement,
|
ViewManagement viewManagement,
|
||||||
PlcVarModelDataProxy plcVarModelDataProxy)
|
PlcVarModelDataProxy plcVarModelDataProxy,
|
||||||
|
WebSocketServer webSocketServer)
|
||||||
{
|
{
|
||||||
this.Device = Device;
|
this.Device = Device;
|
||||||
this.viewManagement = viewManagement;
|
this.viewManagement = viewManagement;
|
||||||
this.plcVarModelDataProxy = plcVarModelDataProxy;
|
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<LibSpace>()
|
||||||
|
//{
|
||||||
|
// DataJsonKey = "data", // 数据键
|
||||||
|
// ThemeJsonKey = "theme", // 主题键
|
||||||
|
//};
|
||||||
|
//msgHandl.AddHandle<LibSpace>(nameof(GetSpace), GetSpace); // theme:AddUser
|
||||||
|
//msgHandl.AddHandle<LibSpace>(nameof(UpData), UpData); // theme:DeleteUser
|
||||||
|
//webSocketServer.AddModule(msgHandl);
|
||||||
|
//CommandCloseForm = new RelayCommand((p) =>
|
||||||
|
//{
|
||||||
|
// webSocketServer.RemoteModule(msgHandl); // 用于关闭窗体时移除监听
|
||||||
|
//});
|
||||||
}
|
}
|
||||||
|
Action<string> Recover;
|
||||||
|
[AutoSocketHandle(ThemeValue = "SavaRecover")]
|
||||||
|
public async Task SavaRecover(int waitTime , Action<string> 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<PlcVarModelDataProxy> GetModelData(Action<object> Recover)
|
||||||
|
{
|
||||||
|
Recover("等待5秒");
|
||||||
|
await Task.Delay(5000);
|
||||||
|
//Recover.Invoke(plcVarModelDataProxy);
|
||||||
|
return plcVarModelDataProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
[AutoSocketHandle(ThemeValue = "Up")]
|
||||||
|
public void UpData(LibSpace libSpace, Action<dynamic> Recover)
|
||||||
|
{
|
||||||
|
Recover("收到");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[AutoSocketHandle(ThemeValue = "Bind")]
|
||||||
|
public LibSpace SpaceBind(string spaceNum, string plateNumber, Action<string> Recover)
|
||||||
|
{
|
||||||
|
return new LibSpace
|
||||||
|
{
|
||||||
|
PlateNumber = plateNumber,
|
||||||
|
SpaceNum = spaceNum,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region 属性绑定
|
#region 属性绑定
|
||||||
@@ -98,6 +195,10 @@ namespace Net462DllTest.ViewModel
|
|||||||
/// 调取车位
|
/// 调取车位
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public RelayCommand CommandGetParkingSpace { get; private set; }
|
public RelayCommand CommandGetParkingSpace { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 关闭窗体
|
||||||
|
/// </summary>
|
||||||
|
public RelayCommand CommandCloseForm { get; private set; }
|
||||||
|
|
||||||
public void InitCommand()
|
public void InitCommand()
|
||||||
{
|
{
|
||||||
@@ -110,6 +211,7 @@ namespace Net462DllTest.ViewModel
|
|||||||
viewManagement.TriggerSignal(SelectedSignal, SpcaeNumber);
|
viewManagement.TriggerSignal(SelectedSignal, SpcaeNumber);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@@ -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<PlcVarName>(var,out var signal))
|
|
||||||
{
|
|
||||||
Console.WriteLine($"外部触发 {signal} 信号,信号内容 : {value} ");
|
|
||||||
plcDevice.TriggerSignal(signal, value);// 通过 Web Api 模拟外部输入信号
|
|
||||||
return new { state = "succeed" };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return new { state = "fail" };
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
85
Net462DllTest/Web/FlowController.cs
Normal file
85
Net462DllTest/Web/FlowController.cs
Normal file
@@ -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<PlcVarName>(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<CommandSignal>(command, out var signal))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"外部触发 {signal} 信号,信号内容 : {value} ");
|
||||||
|
viewManagement.TriggerSignal(signal, value);// 通过 Web Api 模拟外部输入信号
|
||||||
|
return new { state = "succeed" };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new { state = "fail" };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
15
Net462DllTest/Web/UserService.cs
Normal file
15
Net462DllTest/Web/UserService.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<runtime>
|
<runtime>
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="System.IO.Ports" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
|
<assemblyIdentity name="System.IO.Ports" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
</assemblyBinding>
|
</assemblyBinding>
|
||||||
</runtime>
|
</runtime>
|
||||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/></startup></configuration>
|
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" /></startup></configuration>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="IoTClient" version="1.0.40" targetFramework="net461" />
|
<package id="IoTClient" version="1.0.40" targetFramework="net461" />
|
||||||
|
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net462" />
|
||||||
<package id="System.IO.Ports" version="4.6.0" targetFramework="net461" />
|
<package id="System.IO.Ports" version="4.6.0" targetFramework="net461" />
|
||||||
<package id="System.ValueTuple" version="4.5.0" targetFramework="net461" />
|
<package id="System.ValueTuple" version="4.5.0" targetFramework="net461" />
|
||||||
</packages>
|
</packages>
|
||||||
@@ -197,49 +197,69 @@ namespace Serein.NodeFlow.Base
|
|||||||
{
|
{
|
||||||
throw new Exception($"节点{this.Guid}不存在方法信息,请检查是否需要重写节点的ExecutingAsync");
|
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}不存在对应委托");
|
throw new Exception($"节点{this.Guid}不存在对应委托");
|
||||||
}
|
}
|
||||||
md.ActingInstance ??= context.Env.IOC.Get(md.ActingInstanceType);
|
md.ActingInstance ??= context.Env.IOC.Get(md.ActingInstanceType);
|
||||||
object instance = md.ActingInstance;
|
object instance = md.ActingInstance;
|
||||||
|
|
||||||
bool haveParameter = md.ExplicitDatas.Length > 0;
|
//bool haveParameter = md.ExplicitDatas.Length > 0;
|
||||||
bool haveResult = md.ReturnType != typeof(void);
|
//bool haveResult = md.ReturnType != typeof(void);
|
||||||
Type? taskResult = null;
|
// Type? taskResult = null;
|
||||||
bool isTask = md.ReturnType is not null && MethodDetailsHelperTmp.IsGenericTask(md.ReturnType, out taskResult);
|
//bool isTask = md.ReturnType is not null && MethodDetailsHelper.IsGenericTask(md.ReturnType, out taskResult);
|
||||||
bool isTaskHaveResult = taskResult is not null;
|
//bool isTaskHaveResult = taskResult is not null;
|
||||||
object? result;
|
object? result = null;
|
||||||
|
|
||||||
//Console.WriteLine($"(isTask, isTaskHaveResult):{(isTask, isTaskHaveResult)}");
|
//Console.WriteLine($"(isTask, isTaskHaveResult):{(isTask, isTaskHaveResult)}");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Action/Func([方法作用的实例],[可能的参数值],[可能的返回值])
|
// Action/Func([方法作用的实例],[可能的参数值],[可能的返回值])
|
||||||
|
|
||||||
object?[]? parameters = GetParameters(context, this, md);
|
object?[]? args = GetParameters(context, this, md);
|
||||||
if (isTask)
|
var delType = dd.EmitMethodType;
|
||||||
|
var del = dd.EmitDelegate;
|
||||||
|
if (delType == EmitHelper.EmitMethodType.HasResultTask && del is Func<object, object?[]?, Task<object?>> hasResultTask)
|
||||||
{
|
{
|
||||||
// 异步方法(因为返回了Task,所以排除Action<>委托的可能)
|
result = await hasResultTask(instance, args);
|
||||||
result = (haveParameter, isTaskHaveResult) switch
|
}
|
||||||
{
|
else if (delType == EmitHelper.EmitMethodType.Task && del is Func<object, object?[]?, Task> task)
|
||||||
(false, false) => await ExecutionAsync((Func<object, Task>)del, instance), // 调用节点方法,返回方法传回类型
|
{
|
||||||
(true, false) => await ExecutionAsync((Func<object, object?[]?, Task>)del, instance, parameters), // 调用节点方法,获取入参参数,返回方法返回类型
|
await task.Invoke(instance, args);
|
||||||
(false, true) => await ExecutionAsync((Func<object, Task<object?>>)del, instance), // 调用节点方法,返回方法传回类型
|
result = null;
|
||||||
(true, true) => await ExecutionAsync((Func<object, object?[]?, Task<object?>>)del, instance, parameters), // 调用节点方法,获取入参参数,返回方法返回类型
|
}
|
||||||
};
|
else if (delType == EmitHelper.EmitMethodType.Func && del is Func<object, object?[]?, object?> func)
|
||||||
|
{
|
||||||
|
result = func.Invoke(instance, args);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 非异步方法
|
throw new NotImplementedException("构造委托无法正确调用");
|
||||||
result = (haveParameter, haveResult) switch
|
|
||||||
{
|
|
||||||
(false, false) => Execution((Action<object>)del, instance), // 调用节点方法,返回null
|
|
||||||
(true, false) => Execution((Action<object, object?[]?>)del, instance, parameters), // 调用节点方法,返回null
|
|
||||||
(false, true) => Execution((Func<object, object?>)del, instance), // 调用节点方法,返回方法传回类型
|
|
||||||
(true, true) => Execution((Func<object, object?[]?, object?>)del, instance, parameters), // 调用节点方法,获取入参参数,返回方法返回类型
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//if (isTask)
|
||||||
|
//{
|
||||||
|
// // 异步方法(因为返回了Task,所以排除Action<>委托的可能)
|
||||||
|
// result = (haveParameter, isTaskHaveResult) switch
|
||||||
|
// {
|
||||||
|
// (false, false) => await ExecutionAsync((Func<object, Task>)del, instance), // 调用节点方法,返回方法传回类型
|
||||||
|
// (true, false) => await ExecutionAsync((Func<object, object?[]?, Task>)del, instance, parameters), // 调用节点方法,获取入参参数,返回方法返回类型
|
||||||
|
// (false, true) => await ExecutionAsync((Func<object, Task<object?>>)del, instance), // 调用节点方法,返回方法传回类型
|
||||||
|
// (true, true) => await ExecutionAsync((Func<object, object?[]?, Task<object?>>)del, instance, parameters), // 调用节点方法,获取入参参数,返回方法返回类型
|
||||||
|
// };
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// // 非异步方法
|
||||||
|
// result = (haveParameter, haveResult) switch
|
||||||
|
// {
|
||||||
|
// (false, false) => Execution((Action<object>)del, instance), // 调用节点方法,返回null
|
||||||
|
// (true, false) => Execution((Action<object, object?[]?>)del, instance, parameters), // 调用节点方法,返回null
|
||||||
|
// (false, true) => Execution((Func<object, object?>)del, instance), // 调用节点方法,返回方法传回类型
|
||||||
|
// (true, true) => Execution((Func<object, object?[]?, object?>)del, instance, parameters), // 调用节点方法,获取入参参数,返回方法返回类型
|
||||||
|
// };
|
||||||
|
//}
|
||||||
|
|
||||||
NextOrientation = ConnectionType.IsSucceed;
|
NextOrientation = ConnectionType.IsSucceed;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,14 +184,15 @@ namespace Serein.NodeFlow
|
|||||||
/// 存放触发器节点(运行时全部调用)
|
/// 存放触发器节点(运行时全部调用)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private List<SingleFlipflopNode> FlipflopNodes { get; } = [];
|
private List<SingleFlipflopNode> FlipflopNodes { get; } = [];
|
||||||
private List<Type> AutoRegisterTypes { get; } = [];
|
private Dictionary<RegisterSequence,List<Type>> AutoRegisterTypes { get; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 存放委托
|
/// 存放委托
|
||||||
///
|
///
|
||||||
/// md.Methodname - delegate
|
/// md.Methodname - delegate
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private ConcurrentDictionary<string, Delegate> MethodDelegates { get; } = [];
|
|
||||||
|
private ConcurrentDictionary<string, DelegateDetails> MethodDelegates { get; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 起始节点私有属性
|
/// 起始节点私有属性
|
||||||
@@ -256,11 +257,11 @@ namespace Serein.NodeFlow
|
|||||||
}
|
}
|
||||||
this.IOC.Reset(); // 开始运行时清空ioc中注册的实例
|
this.IOC.Reset(); // 开始运行时清空ioc中注册的实例
|
||||||
this.IOC.CustomRegisterInstance(typeof(IFlowEnvironment).FullName,this);
|
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)
|
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
|
else
|
||||||
{
|
{
|
||||||
del = null;
|
delegateDetails = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -939,7 +940,16 @@ namespace Serein.NodeFlow
|
|||||||
{
|
{
|
||||||
MethodDetailss.Add(nodeLibrary, mdlist);
|
MethodDetailss.Add(nodeLibrary, mdlist);
|
||||||
NodeLibrarys.Add(nodeLibrary);
|
NodeLibrarys.Add(nodeLibrary);
|
||||||
AutoRegisterTypes.AddRange(registerTypes);
|
|
||||||
|
foreach(var kv in registerTypes)
|
||||||
|
{
|
||||||
|
if (!AutoRegisterTypes.TryGetValue(kv.Key, out var types))
|
||||||
|
{
|
||||||
|
types = new List<Type>();
|
||||||
|
AutoRegisterTypes.Add(kv.Key, types);
|
||||||
|
}
|
||||||
|
types.AddRange(kv.Value);
|
||||||
|
}
|
||||||
|
|
||||||
OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibrary, mdlist)); // 通知UI创建dll面板显示
|
OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibrary, mdlist)); // 通知UI创建dll面板显示
|
||||||
}
|
}
|
||||||
@@ -947,16 +957,35 @@ namespace Serein.NodeFlow
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private (NodeLibrary?, List<Type> ,List<MethodDetails>) LoadAssembly(string dllPath)
|
private (NodeLibrary?, Dictionary<RegisterSequence, List<Type>>, List<MethodDetails>) LoadAssembly(string dllPath)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Assembly assembly = Assembly.LoadFrom(dllPath); // 加载DLL文件
|
Assembly assembly = Assembly.LoadFrom(dllPath); // 加载DLL文件
|
||||||
Type[] types = assembly.GetTypes(); // 获取程序集中的所有类型
|
Type[] types = assembly.GetTypes(); // 获取程序集中的所有类型
|
||||||
|
|
||||||
List<Type> autoRegisterTypes = assembly.GetTypes().Where(t => t.GetCustomAttribute<AutoRegisterAttribute>() is not null).ToList();
|
Dictionary<RegisterSequence, List<Type>> autoRegisterTypes = new Dictionary<RegisterSequence, List<Type>>();
|
||||||
|
foreach (Type type in types)
|
||||||
|
{
|
||||||
|
var autoRegisterAttribute = type.GetCustomAttribute<AutoRegisterAttribute>();
|
||||||
|
if (autoRegisterAttribute is not null)
|
||||||
|
{
|
||||||
|
if(!autoRegisterTypes.TryGetValue(autoRegisterAttribute.Class,out var valus))
|
||||||
|
{
|
||||||
|
valus = new List<Type>();
|
||||||
|
autoRegisterTypes.Add(autoRegisterAttribute.Class, valus);
|
||||||
|
}
|
||||||
|
valus.Add(type);
|
||||||
|
}
|
||||||
|
|
||||||
List<(Type, string)> scanTypes = assembly.GetTypes().Select(t => {
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Dictionary<Sequence, Type> autoRegisterTypes = assembly.GetTypes().Where(t => t.GetCustomAttribute<AutoRegisterAttribute>() is not null).ToList();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
List<(Type, string)> scanTypes = types.Select(t => {
|
||||||
if (t.GetCustomAttribute<DynamicFlowAttribute>() is DynamicFlowAttribute dynamicFlowAttribute
|
if (t.GetCustomAttribute<DynamicFlowAttribute>() is DynamicFlowAttribute dynamicFlowAttribute
|
||||||
&& dynamicFlowAttribute.Scan == true)
|
&& dynamicFlowAttribute.Scan == true)
|
||||||
{
|
{
|
||||||
@@ -982,10 +1011,10 @@ namespace Serein.NodeFlow
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var methods = MethodDetailsHelperTmp.GetMethodsToProcess(type);
|
var methods = MethodDetailsHelper.GetMethodsToProcess(type);
|
||||||
foreach(var method in methods)
|
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)
|
if(md is null || del is null)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"无法加载方法信息:{assemblyName}-{type}-{method}");
|
Console.WriteLine($"无法加载方法信息:{assemblyName}-{type}-{method}");
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Serein.Library.Api;
|
using Serein.Library.Api;
|
||||||
|
using Serein.Library.Attributes;
|
||||||
using Serein.Library.Core.NodeFlow;
|
using Serein.Library.Core.NodeFlow;
|
||||||
using Serein.Library.Entity;
|
using Serein.Library.Entity;
|
||||||
using Serein.Library.Enums;
|
using Serein.Library.Enums;
|
||||||
@@ -109,6 +110,7 @@ namespace Serein.NodeFlow
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task RunAsync(IFlowEnvironment env,
|
public async Task RunAsync(IFlowEnvironment env,
|
||||||
List<NodeModelBase> nodes,
|
List<NodeModelBase> nodes,
|
||||||
|
Dictionary<RegisterSequence, List<Type>> autoRegisterTypes,
|
||||||
List<MethodDetails> initMethods,
|
List<MethodDetails> initMethods,
|
||||||
List<MethodDetails> loadingMethods,
|
List<MethodDetails> loadingMethods,
|
||||||
List<MethodDetails> exitMethods)
|
List<MethodDetails> exitMethods)
|
||||||
@@ -150,6 +152,7 @@ namespace Serein.NodeFlow
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 初始化运行环境的Ioc容器
|
#region 初始化运行环境的Ioc容器
|
||||||
|
|
||||||
// 清除节点使用的对象,筛选出需要初始化的方法描述
|
// 清除节点使用的对象,筛选出需要初始化的方法描述
|
||||||
var thisRuningMds = new List<MethodDetails>();
|
var thisRuningMds = new List<MethodDetails>();
|
||||||
thisRuningMds.AddRange(runNodeMd.Where(md => md?.ActingInstanceType is not null));
|
thisRuningMds.AddRange(runNodeMd.Where(md => md?.ActingInstanceType is not null));
|
||||||
@@ -178,10 +181,7 @@ namespace Serein.NodeFlow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsStopStart)
|
if (IsStopStart) return;// 检查所有dll节点是否存在类型
|
||||||
{
|
|
||||||
return;// 初始化类型后检查状态
|
|
||||||
}
|
|
||||||
|
|
||||||
env.IOC.Build(); // 流程启动前的初始化
|
env.IOC.Build(); // 流程启动前的初始化
|
||||||
|
|
||||||
@@ -219,25 +219,43 @@ namespace Serein.NodeFlow
|
|||||||
|
|
||||||
#region 执行初始化,绑定IOC容器,再执行加载时
|
#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];
|
//object?[]? args = [Context];
|
||||||
foreach (var md in initMethods) // 初始化
|
foreach (var md in initMethods) // 初始化
|
||||||
{
|
{
|
||||||
if (!env.TryGetDelegate(md.MethodName, out var del))
|
if (!env.TryGetDelegateDetails(md.MethodName, out var dd))
|
||||||
{
|
{
|
||||||
throw new Exception("不存在对应委托");
|
throw new Exception("不存在对应委托");
|
||||||
}
|
}
|
||||||
((Action<object, object?[]?>)del).Invoke(md.ActingInstance, [Context]);
|
((Func<object, object[], object>)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(); // 绑定初始化时注册的类型
|
Context.Env.IOC.Build(); // 绑定初始化时注册的类型
|
||||||
foreach (var md in loadingMethods) // 加载
|
foreach (var md in loadingMethods) // 加载
|
||||||
{
|
{
|
||||||
//object?[]? data = [md.ActingInstance, args];
|
//object?[]? data = [md.ActingInstance, args];
|
||||||
//md.MethodDelegate.DynamicInvoke(data);
|
//md.MethodDelegate.DynamicInvoke(data);
|
||||||
if (!env.TryGetDelegate(md.MethodName, out var del))
|
if (!env.TryGetDelegateDetails(md.MethodName, out var dd))
|
||||||
{
|
{
|
||||||
throw new Exception("不存在对应委托");
|
throw new Exception("不存在对应委托");
|
||||||
}
|
}
|
||||||
((Action<object, object?[]?>)del).Invoke(md.ActingInstance, [Context]);
|
//((Action<object, object?[]?>)del).Invoke(md.ActingInstance, [Context]);
|
||||||
|
((Func<object, object[], object>)dd.EmitDelegate).Invoke(md.ActingInstance, [Context]);
|
||||||
}
|
}
|
||||||
Context.Env.IOC.Build(); // 预防有人在加载时才注册类型,再绑定一次
|
Context.Env.IOC.Build(); // 预防有人在加载时才注册类型,再绑定一次
|
||||||
#endregion
|
#endregion
|
||||||
@@ -245,17 +263,17 @@ namespace Serein.NodeFlow
|
|||||||
#region 设置流程退出时的回调函数
|
#region 设置流程退出时的回调函数
|
||||||
ExitAction = () =>
|
ExitAction = () =>
|
||||||
{
|
{
|
||||||
env.IOC.Run<WebServer>(web => {
|
env.IOC.Run<WebApiServer>(web => {
|
||||||
web?.Stop();
|
web?.Stop();
|
||||||
});
|
});
|
||||||
|
|
||||||
foreach (MethodDetails? md in exitMethods)
|
foreach (MethodDetails? md in exitMethods)
|
||||||
{
|
{
|
||||||
if (!env.TryGetDelegate(md.MethodName, out var del))
|
if (!env.TryGetDelegateDetails(md.MethodName, out var dd))
|
||||||
{
|
{
|
||||||
throw new Exception("不存在对应委托");
|
throw new Exception("不存在对应委托");
|
||||||
}
|
}
|
||||||
((Action<object, object?[]?>)del).Invoke(md.ActingInstance, [Context]);
|
((Func<object, object[], object>)dd.EmitDelegate).Invoke(md.ActingInstance, [Context]);
|
||||||
}
|
}
|
||||||
|
|
||||||
TerminateAllGlobalFlipflop();
|
TerminateAllGlobalFlipflop();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using Serein.Library.Entity;
|
using Serein.Library.Entity;
|
||||||
using Serein.Library.Enums;
|
using Serein.Library.Enums;
|
||||||
using Serein.Library.Ex;
|
using Serein.Library.Ex;
|
||||||
|
using Serein.Library.Utils;
|
||||||
using Serein.NodeFlow.Base;
|
using Serein.NodeFlow.Base;
|
||||||
using static Serein.Library.Utils.ChannelFlowInterrupt;
|
using static Serein.Library.Utils.ChannelFlowInterrupt;
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
MethodDetails md = MethodDetails;
|
MethodDetails md = MethodDetails;
|
||||||
if (!context.Env.TryGetDelegate(md.MethodName, out var del))
|
if (!context.Env.TryGetDelegateDetails(md.MethodName, out var dd))
|
||||||
{
|
{
|
||||||
throw new Exception("不存在对应委托");
|
throw new Exception("不存在对应委托");
|
||||||
}
|
}
|
||||||
@@ -40,37 +41,31 @@ namespace Serein.NodeFlow.Model
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
Task<IFlipflopContext> flipflopTask;
|
Task<IFlipflopContext> 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<object, object?[]?, Task<object>> hasResultTask)
|
||||||
{
|
{
|
||||||
if (del is Func<object, Task<IFlipflopContext>> 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
|
else
|
||||||
{
|
{
|
||||||
throw new FlipflopException("触发节点非预期的返回类型", true, FlipflopException.CancelClass.Flow);
|
throw new FlipflopException("触发器节点返回了非预期的类型", true, FlipflopException.CancelClass.Flow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var parameters = GetParameters(context, this, md);
|
throw new FlipflopException("触发器节点构造了非预期的委托", true, FlipflopException.CancelClass.Flow);
|
||||||
if(del is Func<object, object?[]?, Task<IFlipflopContext>> function)
|
|
||||||
{
|
|
||||||
flipflopTask = function.Invoke(md.ActingInstance, parameters);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
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)
|
catch (FlipflopException ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
<Compile Remove="MethodDetails.cs" />
|
<Compile Remove="MethodDetails.cs" />
|
||||||
<Compile Remove="Tool\Attribute.cs" />
|
<Compile Remove="Tool\Attribute.cs" />
|
||||||
<Compile Remove="Tool\DynamicTool.cs" />
|
<Compile Remove="Tool\DynamicTool.cs" />
|
||||||
|
<Compile Remove="Tool\ExpressionHelper.cs" />
|
||||||
<Compile Remove="Tool\NodeModelBaseFunc.cs" />
|
<Compile Remove="Tool\NodeModelBaseFunc.cs" />
|
||||||
<Compile Remove="Tool\TcsSignal.cs" />
|
<Compile Remove="Tool\TcsSignal.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using Serein.Library.Attributes;
|
using Serein.Library.Attributes;
|
||||||
using Serein.Library.Core.NodeFlow;
|
using Serein.Library.Core.NodeFlow;
|
||||||
using Serein.Library.Entity;
|
using Serein.Library.Entity;
|
||||||
|
using Serein.Library.Utils;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
@@ -10,7 +11,7 @@ using System.Text.RegularExpressions;
|
|||||||
|
|
||||||
namespace Serein.NodeFlow.Tool;
|
namespace Serein.NodeFlow.Tool;
|
||||||
|
|
||||||
public static class MethodDetailsHelperTmp
|
public static class MethodDetailsHelper
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -53,7 +54,7 @@ public static class MethodDetailsHelperTmp
|
|||||||
/// 创建方法信息
|
/// 创建方法信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
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<NodeActionAttribute>();
|
var attribute = method.GetCustomAttribute<NodeActionAttribute>();
|
||||||
if(attribute is null)
|
if(attribute is null)
|
||||||
@@ -64,12 +65,14 @@ public static class MethodDetailsHelperTmp
|
|||||||
var dllTypeMethodName = $"{assemblyName}.{type.Name}.{method.Name}";
|
var dllTypeMethodName = $"{assemblyName}.{type.Name}.{method.Name}";
|
||||||
|
|
||||||
var explicitDataOfParameters = GetExplicitDataOfParameters(method.GetParameters());
|
var explicitDataOfParameters = GetExplicitDataOfParameters(method.GetParameters());
|
||||||
//// 生成委托
|
//// 通过表达式树生成委托
|
||||||
var methodDelegate = GenerateMethodDelegate(type, // 方法所在的对象类型
|
//var methodDelegate = GenerateMethodDelegate(type, // 方法所在的对象类型
|
||||||
method, // 方法信息
|
// method, // 方法信息
|
||||||
method.GetParameters(),// 方法参数
|
// method.GetParameters(),// 方法参数
|
||||||
method.ReturnType);// 返回值
|
// method.ReturnType);// 返回值
|
||||||
|
|
||||||
|
//// 通过表达式树生成委托
|
||||||
|
var emitMethodType = EmitHelper.CreateDynamicMethod(method, out var methodDelegate);// 返回值
|
||||||
|
|
||||||
Type? returnType;
|
Type? returnType;
|
||||||
bool isTask = IsGenericTask(method.ReturnType, out var taskResult);
|
bool isTask = IsGenericTask(method.ReturnType, out var taskResult);
|
||||||
@@ -114,8 +117,8 @@ public static class MethodDetailsHelperTmp
|
|||||||
ExplicitDatas = explicitDataOfParameters,
|
ExplicitDatas = explicitDataOfParameters,
|
||||||
ReturnType = returnType,
|
ReturnType = returnType,
|
||||||
};
|
};
|
||||||
|
var dd = new DelegateDetails( emitMethodType, methodDelegate) ;
|
||||||
return (md, methodDelegate);
|
return (md, dd);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,52 +279,52 @@ public static class MethodDetailsHelperTmp
|
|||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Delegate GenerateMethodDelegate(Type type, MethodInfo methodInfo, ParameterInfo[] parameters, Type returnType)
|
//private static Delegate GenerateMethodDelegate(Type type, MethodInfo methodInfo, ParameterInfo[] parameters, Type returnType)
|
||||||
{
|
//{
|
||||||
var parameterTypes = parameters.Select(p => p.ParameterType).ToArray();
|
// var parameterTypes = parameters.Select(p => p.ParameterType).ToArray();
|
||||||
var parameterCount = parameters.Length;
|
// var parameterCount = parameters.Length;
|
||||||
|
|
||||||
if (returnType == typeof(void))
|
// if (returnType == typeof(void))
|
||||||
{
|
// {
|
||||||
if (parameterCount == 0)
|
// if (parameterCount == 0)
|
||||||
{
|
// {
|
||||||
// 无返回值,无参数
|
// // 无返回值,无参数
|
||||||
return ExpressionHelper.MethodCaller(type, methodInfo);
|
// return ExpressionHelper.MethodCaller(type, methodInfo);
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
// 无返回值,有参数
|
// // 无返回值,有参数
|
||||||
return ExpressionHelper.MethodCaller(type, methodInfo, parameterTypes);
|
// return ExpressionHelper.MethodCaller(type, methodInfo, parameterTypes);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
// else if (returnType == typeof(Task<FlipflopContext)) // 触发器
|
// // else if (returnType == typeof(Task<FlipflopContext)) // 触发器
|
||||||
else if (FlipflopFunc.IsTaskOfFlipflop(returnType)) // 触发器
|
// else if (FlipflopFunc.IsTaskOfFlipflop(returnType)) // 触发器
|
||||||
{
|
// {
|
||||||
if (parameterCount == 0)
|
// if (parameterCount == 0)
|
||||||
{
|
// {
|
||||||
// 有返回值,无参数
|
// // 有返回值,无参数
|
||||||
return ExpressionHelper.MethodCallerAsync(type, methodInfo);
|
// return ExpressionHelper.MethodCallerAsync(type, methodInfo);
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
// 有返回值,有参数
|
// // 有返回值,有参数
|
||||||
return ExpressionHelper.MethodCallerAsync(type, methodInfo, parameterTypes);
|
// return ExpressionHelper.MethodCallerAsync(type, methodInfo, parameterTypes);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
if (parameterCount == 0)
|
// if (parameterCount == 0)
|
||||||
{
|
// {
|
||||||
// 有返回值,无参数
|
// // 有返回值,无参数
|
||||||
return ExpressionHelper.MethodCallerHaveResult(type, methodInfo);
|
// return ExpressionHelper.MethodCallerHaveResult(type, methodInfo);
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
// 有返回值,有参数
|
// // 有返回值,有参数
|
||||||
return ExpressionHelper.MethodCallerHaveResult(type, methodInfo, parameterTypes);
|
// return ExpressionHelper.MethodCallerHaveResult(type, methodInfo, parameterTypes);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
41
Serein.FlowRemoteManagement/FlowRemoteManagement.cs
Normal file
41
Serein.FlowRemoteManagement/FlowRemoteManagement.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
|
||||||
|
using Serein.Library;
|
||||||
|
using Serein.Library.Entity;
|
||||||
|
using Serein.Library.Api;
|
||||||
|
using Serein.Library.Attributes;
|
||||||
|
using Serein.Library.Enums;
|
||||||
|
using Serein.Library.Network.WebSocketCommunication;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
|
namespace SereinFlowRemoteManagement
|
||||||
|
{
|
||||||
|
[DynamicFlow]
|
||||||
|
[AutoRegister]
|
||||||
|
public class FlowRemoteManagement
|
||||||
|
{
|
||||||
|
private readonly IFlowEnvironment environment;
|
||||||
|
public FlowRemoteManagement(IFlowEnvironment environment)
|
||||||
|
{
|
||||||
|
this.environment = environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
[NodeAction(NodeType.Init)]
|
||||||
|
public void Init(IDynamicContext context)
|
||||||
|
{
|
||||||
|
environment.IOC.Register<WebSocketServer>();
|
||||||
|
}
|
||||||
|
[NodeAction(NodeType.Loading)]
|
||||||
|
public void Loading(IDynamicContext context)
|
||||||
|
{
|
||||||
|
environment.IOC.Run<WebSocketServer>(async (socketServer) =>
|
||||||
|
{
|
||||||
|
await socketServer.StartAsync("http://*:7525/");
|
||||||
|
});
|
||||||
|
SereinProjectData projectData = environment.SaveProject();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0-windows7.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<BaseOutputPath>D:\Project\C#\DynamicControl\SereinFlow\.Output</BaseOutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Model\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Library.Core\Serein.Library.Core.csproj" />
|
||||||
|
<ProjectReference Include="..\Library\Serein.Library.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -20,6 +20,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library", "Library\S
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Net462DllTest", "Net462DllTest\Net462DllTest.csproj", "{E40EE629-1A38-4011-88E3-9AD036869987}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Net462DllTest", "Net462DllTest\Net462DllTest.csproj", "{E40EE629-1A38-4011-88E3-9AD036869987}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.FlowRemoteManagement", "Serein.FlowRemoteManagement\Serein.FlowRemoteManagement.csproj", "{3E568C47-74C6-4C28-9D43-C9BA29008DB7}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{E40EE629-1A38-4011-88E3-9AD036869987}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
Reference in New Issue
Block a user