尝试使用源生成器规范NodeModel代码逻辑

This commit is contained in:
fengjiayi
2024-10-20 12:10:57 +08:00
parent 9931fa7436
commit e38833a58c
127 changed files with 5173 additions and 1839 deletions

View File

@@ -97,7 +97,7 @@ namespace Serein.Library.Utils
/// </summary>
/// <param name="signal">信号标识符</param>
/// <param name="timeout">超时时间</param>
public CancelType CreateChannelWithTimeoutSync(string signal, TimeSpan timeout)
public async Task<CancelType> CreateChannelWithTimeoutSync(string signal, TimeSpan timeout)
{
var channel = GetOrCreateChannel(signal);
var cts = new CancellationTokenSource();
@@ -119,7 +119,7 @@ namespace Serein.Library.Utils
});
// 同步阻塞直到信号触发或超时
var result = channel.Reader.ReadAsync().AsTask().GetAwaiter().GetResult();
var result = await channel.Reader.ReadAsync();
return result;
}

View File

@@ -0,0 +1,103 @@
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
namespace Serein.Library.Utils
{
public class ChannelFlowTrigger<TSignal>
{
// 使用并发字典管理每个枚举信号对应的 Channel
private readonly ConcurrentDictionary<TSignal, Channel<(TriggerType,object)>> _channels = new ConcurrentDictionary<TSignal, Channel<(TriggerType, object)>>();
/// <summary>
/// 创建信号并指定超时时间,到期后自动触发(异步方法)
/// </summary>
/// <param name="signal">枚举信号标识符</param>
/// <param name="outTime">超时时间</param>
/// <returns>等待任务</returns>
public async Task<(TriggerType, TResult)> WaitDataWithTimeoutAsync<TResult>(TSignal signal, TimeSpan outTime)
{
var channel = GetOrCreateChannel(signal);
var cts = new CancellationTokenSource();
// 异步任务:超时后自动触发信号
_ = Task.Run(async () =>
{
try
{
await Task.Delay(outTime, cts.Token);
await channel.Writer.WriteAsync((TriggerType.Overtime, null));
}
catch (OperationCanceledException)
{
// 超时任务被取消
}
}, cts.Token);
// 等待信号传入(超时或手动触发)
(var type, var result) = await channel.Reader.ReadAsync();
return (type, result.ToConvert<TResult>());
}
/// <summary>
/// 创建信号,直到触发
/// </summary>
/// <param name="signal">枚举信号标识符</param>
/// <returns>等待任务</returns>
public async Task<TResult> WaitData<TResult>(TSignal signal)
{
var channel = GetOrCreateChannel(signal);
// 等待信号传入(超时或手动触发)
(var type, var result) = await channel.Reader.ReadAsync();
return result.ToConvert<TResult>();
}
/// <summary>
/// 触发信号
/// </summary>
/// <param name="signal">枚举信号标识符</param>
/// <returns>是否成功触发</returns>
public bool TriggerSignal(TSignal signal, object value)
{
if (_channels.TryGetValue(signal, out var channel))
{
// 手动触发信号
channel.Writer.TryWrite((TriggerType.External,value));
return true;
}
return false;
}
/// <summary>
/// 取消所有任务
/// </summary>
public void CancelAllTasks()
{
foreach (var channel in _channels.Values)
{
channel.Writer.Complete();
}
_channels.Clear();
}
/// <summary>
/// 获取或创建指定信号的 Channel
/// </summary>
/// <param name="signal">枚举信号标识符</param>
/// <returns>对应的 Channel</returns>
private Channel<(TriggerType, object)> GetOrCreateChannel(TSignal signal)
{
return _channels.GetOrAdd(signal, _ => Channel.CreateUnbounded<(TriggerType, object)>());
}
}
}

View File

@@ -0,0 +1,48 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Library.Utils
{
/// <summary>
/// 消息防抖
/// </summary>
public static class DebounceHelper
{
private static readonly ConcurrentDictionary<string, DateTime> _lastExecutionTimes = new ConcurrentDictionary<string, DateTime>();
private static readonly object _lockObject = new object();
/// <summary>
/// 检查是否可以执行操作,根据传入的 key 和 debounceTime 来决定是否允许执行
/// </summary>
/// <param name="key">操作的唯一标识</param>
/// <param name="debounceTimeInMs">防抖时间,单位为毫秒</param>
/// <returns>如果可以执行操作,返回 true否则返回 false</returns>
public static bool CanExecute(string key, int debounceTimeInMs)
{
lock (_lockObject)
{
var currentTime = DateTime.Now;
if (_lastExecutionTimes.TryGetValue(key, out DateTime lastExecutionTime))
{
var timeSinceLastExecution = (currentTime - lastExecutionTime).TotalMilliseconds;
if (timeSinceLastExecution < debounceTimeInMs)
{
// 如果距离上次执行时间小于防抖时间,不允许执行
return false;
}
}
// 更新上次执行时间
_lastExecutionTimes[key] = currentTime;
return true;
}
}
}
}

View File

@@ -1,8 +1,5 @@
using Serein.Library.Attributes;
using System;
using System.Collections.Generic;
using System;
using System.Reflection;
using System.Text;
namespace Serein.Library.Utils
{

View File

@@ -312,9 +312,13 @@ namespace Serein.Library.Utils
string cacheKey = $"{type.FullName}.{methodInfo.Name}.MethodCallerAsync";
return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateAsync(type, methodInfo));
}
/// <summary>
/// 表达式树构建无参数,有返回值(Task<object>)的方法(触发器)
/// </summary>
/// <param name="type"></param>
/// <param name="methodInfo"></param>
/// <returns></returns>
private static Delegate CreateMethodCallerDelegateAsync(Type type, MethodInfo methodInfo)
{
var parameter = Expression.Parameter(typeof(object), "instance");
@@ -336,6 +340,7 @@ namespace Serein.Library.Utils
string cacheKey = $"{type.FullName}.{method.Name}.MethodCallerAsync";
return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateAsync(type, method, parameterTypes));
}
/// <summary>
/// 表达式树构建多个参数,有返回值(Task<object>)的方法(触发器)
/// </summary>

View File

@@ -7,7 +7,7 @@ using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
namespace Serein.Library.NodeFlow.Tool
namespace Serein.Library
{
/// <summary>
/// 触发类型

View File

@@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Library.Utils
{
/// <summary>
/// 消息ID生成工具
/// </summary>
public class MessageIdGenerator
{
private static readonly object _lock = new object();
private static int _counter = 0;
/// <summary>
/// 生成一个不重复的标识
/// </summary>
/// <param name="theme"></param>
/// <returns></returns>
public static string GenerateMessageId(string theme)
{
lock (_lock)
{
// 时间戳
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
// 机器标识可以替换成更加独特的标识如机器的MAC地址等
string machineId = GetMachineId();
// 进程ID
int processId = Process.GetCurrentProcess().Id;
// 递增计数器,确保在同一毫秒内的多次生成也不重复
int count = _counter++;
// 随机数
byte[] randomBytes = new byte[8];
using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
{
rng.GetBytes(randomBytes);
}
string randomPart = BitConverter.ToString(randomBytes).Replace("-", "");
// 将所有部分组合起来
return $"{timestamp}-{machineId}-{processId}-{count}-{randomPart}-{theme}";
}
}
private static string GetMachineId()
{
// 这里使用 GUID 模拟机器标识
// 可以替换为更具体的机器信息
return Guid.NewGuid().ToString("N");
}
}
}

View File

@@ -0,0 +1,121 @@
using Newtonsoft.Json;
using Serein.Library.Network.WebSocketCommunication;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Library.Utils
{
/// <summary>
/// 管理远程环境,具备连接、发送消息、停止的功能
/// </summary>
public class RemoteEnvControl
{
/// <summary>
/// 配置远程连接IP端口
/// </summary>
public RemoteEnvControl(string addres, int port, object token)
{
this.Addres = addres;
this.Port = port;
this.Token = token;
}
/// <summary>
/// 远程环境的网络地址
/// </summary>
public string Addres { get; }
/// <summary>
/// 远程环境的对外端口
/// </summary>
public int Port { get; }
/// <summary>
/// 登录远程环境必须携带的token(可以为可序列化的JSON对象)
/// </summary>
public object Token { get; }
/// <summary>
/// 连接到远程的客户端
/// </summary>
public WebSocketClient EnvClient { get; } = new WebSocketClient();
/// <summary>
/// 是否连接到了远程环境
/// </summary>
public bool IsConnectdRemoteEnv { get => isConnectdRemoteEnv; }
private bool isConnectdRemoteEnv = false;
/// <summary>
/// 尝试连接到远程环境
/// </summary>
/// <returns></returns>
public async Task<bool> ConnectAsync()
{
// 第2种WebSocket连接到远程环境实时接收远程环境的响应
Console.WriteLine($"准备连接:{Addres}:{Port},{Token}");
bool success = false;
try
{
var tcpClient = new TcpClient();
var result = tcpClient.BeginConnect(Addres, Port, null, null);
success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(3));
}
finally
{
}
if (!success)
{
Console.WriteLine($"无法连通远程端口 {Addres}:{Port}");
return false;
}
else
{
var url = $"ws://{Addres}:{Port}/";
var result = await EnvClient.ConnectAsync(url); // 尝试连接远程环境
this.isConnectdRemoteEnv = result;
return result;
}
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="theme"></param>
/// <param name="data"></param>
/// <returns></returns>
public async Task SendAsync(string theme, object data)
{
var sendMsg = new
{
theme = theme,
token = this.Token,
data = data,
};
var msg = JsonConvert.SerializeObject(sendMsg);
await EnvClient.SendAsync(msg);
}
}
}

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
namespace Serein.Library.Utils.SereinExpression.Resolver
{
public class BoolConditionResolver : SereinConditionResolver
{

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
namespace Serein.Library.Utils.SereinExpression.Resolver
{
public class MemberConditionResolver<T> : SereinConditionResolver where T : struct, IComparable<T>
{

View File

@@ -5,7 +5,7 @@ using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
namespace Serein.Library.Utils.SereinExpression.Resolver
{
public class MemberStringConditionResolver : SereinConditionResolver
{

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
namespace Serein.Library.Utils.SereinExpression.Resolver
{
public class PassConditionResolver : SereinConditionResolver
{

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
namespace Serein.Library.Utils.SereinExpression.Resolver
{
public class StringConditionResolver : SereinConditionResolver
{

View File

@@ -5,7 +5,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
namespace Serein.Library.Utils.SereinExpression.Resolver
{
public class ValueTypeConditionResolver<T> : SereinConditionResolver where T : struct, IComparable<T>
{

View File

@@ -1,6 +1,6 @@
using Newtonsoft.Json.Linq;
using Serein.Library.Utils;
using Serein.NodeFlow.Tool.SereinExpression.Resolver;
using Serein.Library.Utils.SereinExpression.Resolver;
using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
@@ -8,7 +8,7 @@ using System.Globalization;
using System.Linq;
using System.Reflection;
namespace Serein.NodeFlow.Tool.SereinExpression
namespace Serein.Library.Utils.SereinExpression
{
/// <summary>
/// 字符串工具类

View File

@@ -1,6 +1,6 @@
using System.Reflection;
namespace Serein.NodeFlow.Tool.SereinExpression
namespace Serein.Library.Utils.SereinExpression
{
/// <summary>
/// 条件解析抽象类

View File

@@ -4,7 +4,7 @@ using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace Serein.NodeFlow.Tool.SereinExpression
namespace Serein.Library.Utils.SereinExpression
{
/// <summary>
/// 使用表达式操作/获取 对象的值

View File

@@ -1,19 +1,14 @@
using Newtonsoft.Json.Linq;
using Serein.Library.Api;
using Serein.Library.Attributes;
using Serein.Library.Web;
using Serein.Library.Api;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Xml;
using System.Xml.Schema;
namespace Serein.Library.Utils
{
/// <summary>
/// IOC管理容器
/// </summary>
@@ -195,56 +190,60 @@ namespace Serein.Library.Utils
public Type Type { get; set; }
}
private const string FlowBaseClassName = "<>$FlowBaseClass!@#";
public Dictionary<string, List<string>> BuildDependencyTree()
{
var dependencyMap = new Dictionary<string, List<string>>();
//var tmpTypeFullName = new HashSet<string>();
//var tmpTypeFullName2 = new HashSet<string>();
dependencyMap[FlowBaseClassName] = new List<string>();
var dependencyMap = new Dictionary<string, HashSet<string>>();
dependencyMap[FlowBaseClassName] = new HashSet<string>();
foreach (var typeMapping in _typeMappings)
{
var constructor = GetConstructorWithMostParameters(typeMapping.Value); // 获取参数最多的构造函数
if (constructor != null)
//var constructor = GetConstructorWithMostParameters(typeMapping.Value); // 获取参数最多的构造函数
var constructors = GetConstructor(typeMapping.Value); // 获取参数最多的构造函数
foreach (var constructor in constructors)
{
var parameters = constructor.GetParameters()
.Select(p => p.ParameterType)
.ToList();
//if(parameters.Count == 0)
//{
// if (!dependencyMap.ContainsKey(typeMapping.Value.FullName))
// {
// dependencyMap[typeMapping.Value.FullName] = new List<string>();
// }
// dependencyMap[typeMapping.Value.FullName].Add(typeMapping.Key);
//}
if(parameters .Count > 0)
if (constructor != null)
{
// 从类型的构造函数中提取类型
foreach (var param in parameters)
var parameters = constructor.GetParameters()
.Select(p => p.ParameterType)
.ToList();
if (parameters.Count == 0) // 无参的构造函数
{
if (!dependencyMap.ContainsKey(param.FullName))
var type = typeMapping.Value;
if (!dependencyMap[FlowBaseClassName].Contains(type.FullName))
{
dependencyMap[param.FullName] = new List<string>();
dependencyMap[FlowBaseClassName].Add(type.FullName);
}
}
else
{
// 从类型的有参构造函数中提取类型
foreach (var param in parameters)
{
if (!dependencyMap.TryGetValue(param.FullName, out var hashSet))
{
hashSet = new HashSet<string>();
hashSet.Add(typeMapping.Key);
dependencyMap.Add(param.FullName, hashSet);
}
else
{
if (!hashSet.Contains(typeMapping.Key))
{
hashSet.Add(typeMapping.Key);
}
}
}
dependencyMap[param.FullName].Add(typeMapping.Key);
//tmpTypeFullName.Add(param.FullName);
//if (tmpTypeFullName2.Contains(param.FullName))
//{
// tmpTypeFullName2.Remove(param.FullName);
//}
}
}
else
{
var type = typeMapping.Value;
dependencyMap[FlowBaseClassName].Add(type.FullName);
}
}
}
return dependencyMap;
var tmp = dependencyMap.ToDictionary(key => key.Key, value => value.Value.ToList());
return tmp;
}
// 获取参数最多的构造函数
private ConstructorInfo GetConstructorWithMostParameters(Type type)
@@ -253,6 +252,14 @@ namespace Serein.Library.Utils
.OrderByDescending(c => c.GetParameters().Length)
.FirstOrDefault();
}
// 获取所有构造函数
private ConstructorInfo[] GetConstructor(Type type)
{
return type.GetConstructors()
.OrderByDescending(c => c.GetParameters().Length)
.OrderBy(ctor => ctor.GetParameters().Length).ToArray();
}
// 生成顺序
public List<string> GetCreationOrder(Dictionary<string, List<string>> dependencyMap)
{
@@ -334,27 +341,57 @@ namespace Serein.Library.Utils
{
instance = Activator.CreateInstance(type, @params);
}
// 字符串、值类型,抽象类型,暂时不支持自动创建
if (type == typeof(string) || type.IsValueType || type.IsAbstract)
{
return null;
}
else
{
// 没有显示指定构造函数入参,选择参数最多的构造函数
var constructor = GetConstructorWithMostParameters(type);
var parameters = constructor.GetParameters();
var args = new object[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
//var constructor = GetConstructorWithMostParameters(type);
var constructors = GetConstructor(type); // 获取参数最多的构造函数
foreach(var constructor in constructors)
{
var argType = parameters[i].ParameterType;
var fullName = parameters[i].ParameterType.FullName;
if (!_dependencies.TryGetValue(fullName, out var argObj))
var parameters = constructor.GetParameters();
var args = new object[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
{
if (!_typeMappings.ContainsKey(fullName))
var argType = parameters[i].ParameterType;
var fullName = parameters[i].ParameterType.FullName;
if (!_dependencies.TryGetValue(fullName, out var argObj))
{
_typeMappings.TryAdd(fullName, argType);
if (!_typeMappings.ContainsKey(fullName))
{
_typeMappings.TryAdd(fullName, argType);
}
argObj = CreateInstance(fullName);
if (argObj is null)
{
Console.WriteLine("构造参数创建失败"); //
continue;
}
}
argObj = CreateInstance(fullName);
args[i] = argObj;
}
try
{
instance = Activator.CreateInstance(type, args);
if(instance != null)
{
break;
}
}
catch (Exception)
{
continue;
}
args[i] = argObj;
}
instance = Activator.CreateInstance(type, args);
}
InjectDependencies(instance); // 完成创建后注入实例需要的特性依赖项

View File

@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Serein.Library.Utils
{
/// <summary>
/// 为类库提供了在UI线程上下文操作的方法
/// </summary>
public class UIContextOperation
{
private readonly SynchronizationContext context;
/// <summary>
/// 传入UI线程上下文
/// </summary>
/// <param name="synchronizationContext">线程上下文</param>
public UIContextOperation(SynchronizationContext synchronizationContext)
{
this.context = synchronizationContext;
}
/// <summary>
/// 同步方式进行调用方法
/// </summary>
/// <param name="uiAction">要执行的UI操作</param>
public void Invoke(Action uiAction)
{
context?.Post(state =>
{
uiAction?.Invoke();
}, null);
}
/// <summary>
/// 异步方式进行调用
/// </summary>
/// <param name="uiAction">要执行的UI操作</param>
/// <returns></returns>
public Task InvokeAsync(Action uiAction)
{
var tcs = new TaskCompletionSource<bool>();
context?.Post(state =>
{
try
{
uiAction?.Invoke();
tcs.SetResult(true);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
}, null);
return tcs.Task;
}
}
}