优化了示例工程

This commit is contained in:
fengjiayi
2024-09-28 23:55:19 +08:00
parent 51bdbab4d1
commit 10e5d172c6
56 changed files with 9920 additions and 455 deletions

View File

@@ -223,6 +223,7 @@ namespace Serein.NodeFlow.Base
}
catch (Exception ex)
{
await Console.Out.WriteLineAsync(ex.ToString());
NextOrientation = ConnectionType.IsError;
RuningException = ex;
return null;
@@ -292,7 +293,23 @@ namespace Serein.NodeFlow.Base
inputParameter = flowData; // 使用上一节点的对象
}
// 存在转换器
if (ed.Convertor is not null)
{
if (Enum.TryParse(ed.ExplicitType, ed.DataValue, out var resultEnum))
{
var value = ed.Convertor(resultEnum);
if (value is not null)
{
parameters[i] = value;
continue;
}
else
{
throw new InvalidOperationException("转换器调用失败");
}
}
}
//var attribute = ed.DataType.GetCustomAttribute<EnumTypeConvertorAttribute>();
//if (attribute is not null && attribute.EnumType.IsEnum) // 获取枚举转换器中记录的枚举
@@ -312,6 +329,8 @@ namespace Serein.NodeFlow.Base
}
}
}
try

View File

@@ -311,11 +311,11 @@ namespace Serein.NodeFlow
foreach (var dll in dllPaths)
{
var dllFilePath = System.IO.Path.GetFullPath(System.IO.Path.Combine(filePath, dll));
(var assembly, var list) = LoadAssembly(dllFilePath);
if (assembly is not null && list.Count > 0)
(var assembly, var registerTypes, var mdlist) = LoadAssembly(dllFilePath);
if (assembly is not null && mdlist.Count > 0)
{
MethodDetailss.AddRange(list); // 暂存方法描述
OnDllLoad?.Invoke(new LoadDLLEventArgs(assembly, list)); // 通知UI创建dll面板显示
MethodDetailss.AddRange(mdlist); // 暂存方法描述
OnDllLoad?.Invoke(new LoadDLLEventArgs(assembly, mdlist)); // 通知UI创建dll面板显示
}
}
// 方法加载完成,缓存到运行环境中。
@@ -435,6 +435,7 @@ namespace Serein.NodeFlow
OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs());
}
/// <summary>
/// 保存项目为项目文件
/// </summary>
@@ -457,17 +458,20 @@ namespace Serein.NodeFlow
/// <returns></returns>
public void LoadDll(string dllPath)
{
(var assembly, var list) = LoadAssembly(dllPath);
(var assembly, _, var list) = LoadAssembly(dllPath);
if (assembly is not null && list.Count > 0)
{
MethodDetailss.AddRange(list);
OnDllLoad?.Invoke(new LoadDLLEventArgs(assembly, list));
}
}
/// <summary>
/// 运行时创建节点
/// </summary>
/// <param name="nodeBase"></param>
/// <param name="nodeControlType"></param>
/// <param name="position"></param>
/// <param name="methodDetails"></param>
public void CreateNode(NodeControlType nodeControlType, Position position, MethodDetails? methodDetails = null)
{
var nodeModel = CreateNode(nodeControlType, methodDetails);
@@ -477,8 +481,7 @@ namespace Serein.NodeFlow
&& nodeControlType == NodeControlType.Flipflop
&& nodeModel is SingleFlipflopNode flipflopNode)
{
// 当前添加节点属于触发器,且当前正在运行,则加载到运行环境中
flowStarter?.AddFlipflopInRuning(flipflopNode, this);
_ = flowStarter?.RunGlobalFlipflopAsync(this, flipflopNode); // 当前添加节点属于触发器,且当前正在运行,则加载到运行环境中
}
// 通知UI更改
@@ -504,6 +507,11 @@ namespace Serein.NodeFlow
{
return;
}
if (remoteNode is SingleFlipflopNode flipflopNode)
{
flowStarter?.TerminateGlobalFlipflopRuning(flipflopNode); // 假设被移除的是全局触发器,尝试从启动器移除
}
// 遍历所有父节点,从那些父节点中的子节点集合移除该节点
foreach (var pnc in remoteNode.PreviousNodes)
@@ -512,7 +520,6 @@ namespace Serein.NodeFlow
for (int i = 0; i < pnc.Value.Count; i++)
{
NodeModelBase? pNode = pnc.Value[i];
//pNode.SuccessorNodes[pCType].RemoveAt(i);
pNode.SuccessorNodes[pCType].Remove(pNode);
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(pNode.Guid,
@@ -588,13 +595,13 @@ namespace Serein.NodeFlow
{
fromNode.SuccessorNodes[connectionType].Remove(toNode);
toNode.PreviousNodes[connectionType].Remove(fromNode);
if (toNode is SingleFlipflopNode flipflopNode)
if (toNode is SingleFlipflopNode flipflopNode) // 子节点为触发器
{
if (flowStarter?.FlowState != RunState.Completion
&& flipflopNode.NotExitPreviousNode())
if (flowStarter?.FlowState != RunState.Completion
&& flipflopNode.NotExitPreviousNode()) // 正在运行,且该触发器没有上游节点
{
// 被父节点移除连接关系的子节点若为触发器,且无上级节点,则当前流程正在运行,则加载到运行环境中
flowStarter?.AddFlipflopInRuning(flipflopNode, this);
flowStarter?.RunGlobalFlipflopAsync(this, flipflopNode); // 被父节点移除连接关系的子节点若为触发器,且无上级节点,则当前流程正在运行,则加载到运行环境中
}
}
@@ -844,17 +851,19 @@ namespace Serein.NodeFlow
/// 加载指定路径的DLL文件
/// </summary>
/// <param name="dllPath"></param>
private (Assembly?, List<MethodDetails>) LoadAssembly(string dllPath)
private (Assembly?, List<Type> ,List<MethodDetails>) LoadAssembly(string dllPath)
{
try
{
Assembly assembly = Assembly.LoadFrom(dllPath); // 加载DLL文件
Type[] types = assembly.GetTypes(); // 获取程序集中的所有类型
List<Type> autoRegisterTypes = assembly.GetTypes().Where(t => t.GetCustomAttribute<AutoRegisterAttribute>() is not null).ToList();
List<Type> scanTypes = assembly.GetTypes().Where(t => t.GetCustomAttribute<DynamicFlowAttribute>()?.Scan == true).ToList();
if (scanTypes.Count == 0)
{
return (null, []);
return (null, [], []);
}
List<MethodDetails> methodDetails = new List<MethodDetails>();
@@ -874,14 +883,15 @@ namespace Serein.NodeFlow
}
LoadedAssemblies.Add(assembly); // 将加载的程序集添加到列表中
LoadedAssemblyPaths.Add(dllPath); // 记录加载的DLL路径
return (assembly, methodDetails);
return (assembly, autoRegisterTypes , methodDetails);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return (null, []);
return (null, [],[]);
}
}
/// <summary>
/// 运行环节加载了项目文件,需要创建节点控件
/// </summary>
@@ -985,6 +995,11 @@ namespace Serein.NodeFlow
ConnectionType.IsFail,
ConnectionType.IsError,
ConnectionType.Upstream];
if (toNode is SingleFlipflopNode flipflopNode)
{
flowStarter?.TerminateGlobalFlipflopRuning(flipflopNode); // 假设被连接的是全局触发器,尝试移除
}
foreach (ConnectionType ctType in ct)
{
var FToTo = fromNode.SuccessorNodes[ctType].Where(it => it.Guid.Equals(toNode.Guid)).ToArray();

View File

@@ -6,6 +6,7 @@ using Serein.Library.Utils;
using Serein.Library.Web;
using Serein.NodeFlow.Base;
using Serein.NodeFlow.Model;
using System.Collections.Concurrent;
using System.ComponentModel.Design;
using System.Runtime.CompilerServices;
using System.Xml.Linq;
@@ -238,6 +239,8 @@ namespace Serein.NodeFlow
((Action<object, object?[]?>)md.MethodDelegate).Invoke(md.ActingInstance, [Context]);
}
TerminateAllGlobalFlipflop();
if (_flipFlopCts != null && !_flipFlopCts.IsCancellationRequested)
{
_flipFlopCts?.Cancel();
@@ -245,6 +248,7 @@ namespace Serein.NodeFlow
}
FlowState = RunState.Completion;
FlipFlopState = RunState.Completion;
};
#endregion
@@ -263,7 +267,7 @@ namespace Serein.NodeFlow
// 使用 TaskCompletionSource 创建未启动的触发器任务
var tasks = flipflopNodes.Select(async node =>
{
await FlipflopExecute(env,node);
await RunGlobalFlipflopAsync(env,node);
}).ToArray();
_ = Task.WhenAll(tasks);
}
@@ -288,31 +292,68 @@ namespace Serein.NodeFlow
#endregion
}
public void AddFlipflopInRuning(SingleFlipflopNode singleFlipFlopNode, IFlowEnvironment env)
private ConcurrentDictionary<SingleFlipflopNode, CancellationTokenSource> dictGlobalFlipflop = [];
/// <summary>
/// 尝试添加全局触发器
/// </summary>
/// <param name="singleFlipFlopNode"></param>
/// <param name="env"></param>
public async Task RunGlobalFlipflopAsync(IFlowEnvironment env, SingleFlipflopNode singleFlipFlopNode)
{
_ = Task.Run(async () =>
if (dictGlobalFlipflop.TryAdd(singleFlipFlopNode, new CancellationTokenSource()))
{
// 设置对象
singleFlipFlopNode.MethodDetails.ActingInstance = env.IOC.GetOrRegisterInstantiate(singleFlipFlopNode.MethodDetails.ActingInstanceType);
await FlipflopExecute(env,singleFlipFlopNode); // 启动触发器
});
singleFlipFlopNode.MethodDetails.ActingInstance ??= env.IOC.GetOrRegisterInstantiate(singleFlipFlopNode.MethodDetails.ActingInstanceType);
await FlipflopExecuteAsync(env, singleFlipFlopNode, dictGlobalFlipflop[singleFlipFlopNode]);
}
}
/// <summary>
/// 尝试移除全局触发器
/// </summary>
/// <param name="singleFlipFlopNode"></param>
public void TerminateGlobalFlipflopRuning(SingleFlipflopNode singleFlipFlopNode)
{
if (dictGlobalFlipflop.TryRemove(singleFlipFlopNode, out var cts))
{
if (!cts.IsCancellationRequested)
{
cts.Cancel();
}
cts.Dispose();
}
}
/// <summary>
/// 总结所有全局触发器
/// </summary>
private void TerminateAllGlobalFlipflop()
{
foreach ((var node, var cts) in dictGlobalFlipflop)
{
if (!cts.IsCancellationRequested)
{
cts.Cancel();
}
cts.Dispose();
}
dictGlobalFlipflop.Clear();
}
/// <summary>
/// 启动全局触发器
/// </summary>
/// <param name="env">流程运行全局环境</param>
/// <param name="singleFlipFlopNode">需要全局监听信号的触发器</param>
/// <returns></returns>
private async Task FlipflopExecute(IFlowEnvironment env,SingleFlipflopNode singleFlipFlopNode)
private async Task FlipflopExecuteAsync(IFlowEnvironment env, SingleFlipflopNode singleFlipFlopNode, CancellationTokenSource cts)
{
var context = new DynamicContext(env); // 启动全局触发器时新建上下文
while (!_flipFlopCts.IsCancellationRequested)
while (!_flipFlopCts.IsCancellationRequested && !cts.IsCancellationRequested)
{
try
{
var newFlowData = await singleFlipFlopNode.ExecutingAsync(context); // 获取触发器等待Task
var waitTask = singleFlipFlopNode.ExecutingAsync(context); // 获取触发器等待Task
var newFlowData = await waitTask;
await NodeModelBase.RefreshFlowDataAndExpInterrupt(context, singleFlipFlopNode, newFlowData); // 全局触发器触发后刷新该触发器的节点数据
if (singleFlipFlopNode.NextOrientation != ConnectionType.None)
{
@@ -339,32 +380,6 @@ namespace Serein.NodeFlow
}
}
//MethodDetails md = singleFlipFlopNode.MethodDetails;
//var del = md.MethodDelegate;
//object?[]? parameters = singleFlipFlopNode.GetParameters(context, singleFlipFlopNode.MethodDetails); // 启动全局触发器时获取入参参数
//// 设置委托对象
//var func = md.ExplicitDatas.Length == 0 ?
// (Func<object, object, Task<IFlipflopContext>>)del :
// (Func<object, object[], Task<IFlipflopContext>>)del;
//if(t)
//{
// IFlipflopContext flipflopContext = await ((Func<object, Task<IFlipflopContext>>)del.Clone()).Invoke(md.ActingInstance);// 开始等待全局触发器的触发
// var connectionType = flipflopContext.State.ToContentType();
// if (connectionType != ConnectionType.None)
// {
// await GlobalFlipflopExecute(context, singleFlipFlopNode, connectionType, cts);
// }
//}
//else
//{
// IFlipflopContext flipflopContext = await ((Func<object, object[], Task<IFlipflopContext>>)del.Clone()).Invoke(md.ActingInstance, parameters);// 开始等待全局触发器的触发
// var connectionType = flipflopContext.State.ToContentType();
// if (connectionType != ConnectionType.None)
// {
// await GlobalFlipflopExecute(context, singleFlipFlopNode, connectionType, cts);
// }
//}
}

View File

@@ -19,6 +19,7 @@ namespace Serein.NodeFlow.Model
//}
public override async Task<object?> ExecutingAsync(IDynamicContext context)
{
#region
@@ -26,11 +27,6 @@ namespace Serein.NodeFlow.Model
{
string guid = this.Guid.ToString();
var cancelType = await this.DebugSetting.GetInterruptTask();
//if (cancelType == CancelType.Discard)
//{
// this.NextOrientation = ConnectionType.None;
// return null;
//}
await Console.Out.WriteLineAsync($"[{this.MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支");
}
#endregion
@@ -42,11 +38,21 @@ namespace Serein.NodeFlow.Model
try
{
// 调用委托并获取结果
Task<IFlipflopContext> flipflopTask = md.ExplicitDatas.Length switch
//Task<IFlipflopContext> flipflopTask = md.ExplicitDatas.Length switch
//{
// 0 => ((Func<object, Task<IFlipflopContext>>)del).Invoke(md.ActingInstance),
// _ => ((Func<object, object?[]?, Task<IFlipflopContext>>)del).Invoke(md.ActingInstance, GetParameters(context, this, md)), // 执行流程中的触发器方法时获取入参参数
//};
Task<IFlipflopContext> flipflopTask;
if (md.ExplicitDatas.Length == 0)
{
0 => ((Func<object, Task<IFlipflopContext>>)del).Invoke(md.ActingInstance),
_ => ((Func<object, object?[]?, Task<IFlipflopContext>>)del).Invoke(md.ActingInstance, GetParameters(context, this, md)), // 执行流程中的触发器方法时获取入参参数
};
flipflopTask = ((Func<object, Task<IFlipflopContext>>)del).Invoke(md.ActingInstance);
}
else
{
var parameters = GetParameters(context, this, md);
flipflopTask = ((Func<object, object?[]?, Task<IFlipflopContext>>)del).Invoke(md.ActingInstance, parameters);
}
IFlipflopContext flipflopContext = (await flipflopTask) ?? throw new FlipflopException("没有返回上下文");
NextOrientation = flipflopContext.State.ToContentType();
@@ -58,12 +64,14 @@ namespace Serein.NodeFlow.Model
}
catch (FlipflopException ex)
{
await Console.Out.WriteLineAsync(ex.ToString());
NextOrientation = ConnectionType.None;
RuningException = ex;
return null;
}
catch (Exception ex)
{
await Console.Out.WriteLineAsync(ex.ToString());
NextOrientation = ConnectionType.IsError;
RuningException = ex;
return null;

View File

@@ -4,6 +4,7 @@ using Serein.Library.Core.NodeFlow;
using Serein.Library.Entity;
using System;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Reflection;
namespace Serein.NodeFlow.Tool;
@@ -89,6 +90,9 @@ public static class MethodDetailsHelperTmp
}
private static ConcurrentDictionary<string, (object, MethodInfo)> ConvertorInstance =[];
/// <summary>
/// 获取参数信息
/// </summary>
@@ -100,36 +104,90 @@ public static class MethodDetailsHelperTmp
return parameters.Select((it, index) =>
{
Type paremType;
var attribute = it.GetCustomAttribute<EnumTypeConvertorAttribute>();
if (attribute is not null)
if (it.GetCustomAttribute<EnumTypeConvertorAttribute>() is EnumTypeConvertorAttribute attribute1 && attribute1 is not null)
{
// 存在选择器
paremType = attribute.EnumType;
// 存在类型选择器
paremType = attribute1.EnumType;
return GetExplicitDataOfParameter(it, index, paremType, true);
}
else if (it.GetCustomAttribute<BindConvertorAttribute>() is BindConvertorAttribute attribute2 && attribute2 is not null)
{
paremType = attribute2.EnumType;
string key = typeof(IEnumConvertor<,>).FullName + attribute2.EnumType.FullName + attribute2.ConvertorType.FullName;
if (!ConvertorInstance.ContainsKey(key))
{
Type enumConvertorType = typeof(IEnumConvertor<,>);
// 定义具体类型
Type specificType = enumConvertorType.MakeGenericType(attribute2.EnumType, it.ParameterType);
// 获取实现类的类型
Type implementorType = attribute2.ConvertorType;
// 创建实现类的实例
object instance = Activator.CreateInstance(implementorType);
// 调用 Convert 方法
MethodInfo convertMethod = implementorType.GetMethod("Convertor");
ConvertorInstance[key] = (instance, convertMethod);
}
Func<object, object> func = (enumValue) =>
{
(var obj,var methodInfo) = ConvertorInstance[key];
return methodInfo?.Invoke(obj, [enumValue]);
};
// 确保实例实现了所需接口
ExplicitData ed = GetExplicitDataOfParameter(it, index, paremType, true, func);
return ed;
}
else
{
paremType = it.ParameterType;
return GetExplicitDataOfParameter(it, index, it.ParameterType, it.HasDefaultValue);
}
string explicitTypeName = GetExplicitTypeName(paremType);
var items = GetExplicitItems(paremType, explicitTypeName);
if ("Bool".Equals(explicitTypeName)) explicitTypeName = "Select"; // 布尔值 转为 可选类型
return new ExplicitData
{
IsExplicitData = attribute is null ? it.HasDefaultValue: true,
Index = index,
ExplicitTypeName = explicitTypeName,
ExplicitType = paremType,
DataType = it.ParameterType,
ParameterName = it.Name,
DataValue = it.HasDefaultValue ? it.DefaultValue.ToString() : "",
Items = items.ToArray(),
};
//string explicitTypeName = GetExplicitTypeName(paremType);
//var items = GetExplicitItems(paremType, explicitTypeName);
//if ("Bool".Equals(explicitTypeName)) explicitTypeName = "Select"; // 布尔值 转为 可选类型
//return new ExplicitData
//{
// IsExplicitData = attribute is null ? it.HasDefaultValue: true,
// Index = index,
// ExplicitTypeName = explicitTypeName,
// ExplicitType = paremType,
// DataType = it.ParameterType,
// ParameterName = it.Name,
// DataValue = it.HasDefaultValue ? it?.DefaultValue?.ToString() : "",
// Items = items.ToArray(),
//};
}).ToArray();
}
private static ExplicitData GetExplicitDataOfParameter(ParameterInfo parameterInfo,
int index,
Type paremType,
bool isExplicitData,
Func<object, object> func = null)
{
string explicitTypeName = GetExplicitTypeName(paremType);
var items = GetExplicitItems(paremType, explicitTypeName);
if ("Bool".Equals(explicitTypeName)) explicitTypeName = "Select"; // 布尔值 转为 可选类型
return new ExplicitData
{
IsExplicitData = isExplicitData, //attribute is null ? parameterInfo.HasDefaultValue : true,
Index = index,
ExplicitTypeName = explicitTypeName,
ExplicitType = paremType,
Convertor = func,
DataType = parameterInfo.ParameterType,
ParameterName = parameterInfo.Name,
DataValue = parameterInfo.HasDefaultValue ? parameterInfo?.DefaultValue?.ToString() : "",
Items = items.ToArray(),
};
}
/// <summary>
/// 判断使用输入器还是选择器
/// </summary>