完善节点图的代码生成

This commit is contained in:
fengjiayi
2025-07-07 20:40:24 +08:00
parent b25fd9c83c
commit 678b01f2fe
33 changed files with 1219 additions and 214 deletions

View File

@@ -3,7 +3,6 @@ using Serein.Library.Api;
using Serein.Library.Utils;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Services;
using Serein.NodeFlow.Tool;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -126,7 +125,6 @@ namespace Serein.NodeFlow.Env
};
flowWorkManagement = new FlowWorkManagement(flowTaskOptions);
var cts = new CancellationTokenSource();
try
@@ -152,7 +150,7 @@ namespace Serein.NodeFlow.Env
/// </summary>
/// <param name="startNodeGuid"></param>
/// <returns></returns>
public async Task<bool> StartFlowFromSelectNodeAsync(string startNodeGuid)
public async Task<TResult> StartFlowAsync<TResult>(string startNodeGuid)
{
var flowTaskOptions = new FlowWorkOptions
@@ -162,19 +160,34 @@ namespace Serein.NodeFlow.Env
};
var flowTaskManagement = new FlowWorkManagement(flowTaskOptions);
if (true || flowEnvironment.FlowState == RunState.Running || FlipFlopState == RunState.Running)
if (!flowModelService.TryGetNodeModel(startNodeGuid, out var nodeModel) || nodeModel is SingleFlipflopNode)
{
throw new Exception();
}
if (!flowModelService.TryGetNodeModel(startNodeGuid, out var nodeModel) || nodeModel is SingleFlipflopNode)
{
return false;
}
await flowTaskManagement.StartFlowInSelectNodeAsync(nodeModel);
return true;
#if DEBUG
FlowResult flowResult = await BenchmarkHelpers.BenchmarkAsync(async () =>
{
var flowResult = await flowTaskManagement.StartFlowInSelectNodeAsync(nodeModel);
return flowResult;
});
#else
FlowResult flowResult = await flowTaskManagement.StartFlowInSelectNodeAsync(nodeModel);
#endif
if (flowResult.Value is TResult result)
{
return result;
}
else if (flowResult is FlowResult && flowResult is TResult result2)
{
return result2;
}
else
{
return false;
throw new ArgumentNullException($"类型转换失败,流程返回数据与泛型不匹配,当前返回类型为[{flowResult.Value.GetType().FullName}]。");
}
}
@@ -331,7 +344,17 @@ namespace Serein.NodeFlow.Env
}
}
CancellationTokenSource cts = new CancellationTokenSource();
#if DEBUG
FlowResult flowResult = await BenchmarkHelpers.BenchmarkAsync(async () =>
{
var flowResult = await flowCallNode.StartFlowAsync(context, cts.Token);
return flowResult;
});
#else
var flowResult = await flowCallNode.StartFlowAsync(context, cts.Token);
#endif
cts?.Cancel();
cts?.Dispose();
if (flowResult.Value is TResult result)

View File

@@ -5,7 +5,6 @@ using Serein.Library.Utils;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Operation;
using Serein.NodeFlow.Services;
using Serein.NodeFlow.Tool;
using System.Diagnostics;
using System.Threading.Tasks;
using static Serein.Library.Api.IFlowEnvironment;
@@ -261,7 +260,7 @@ namespace Serein.NodeFlow.Env
ChangeType = NodeConnectChangeEventArgs.ConnectChangeType.Create,
JunctionOfConnectionType = JunctionOfConnectionType.Arg,
};
flowOperationService.Execute(operation);
flowOperationService.Execute(operation);
}
public void RemoveInvokeConnect(string canvasGuid, string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
@@ -335,6 +334,15 @@ namespace Serein.NodeFlow.Env
public void SetStartNode(string canvasGuid, string nodeGuid)
{
IOperation operation = new SetStartNodeOperation
{
CanvasGuid = canvasGuid,
NewNodeGuid = nodeGuid,
};
_ = flowOperationService.Execute(operation);
return;
if (!TryGetCanvasModel(canvasGuid, out var canvasModel) || !TryGetNodeModel(nodeGuid, out var newStartNodeModel))
{
return;

View File

@@ -3,7 +3,6 @@ using Serein.Library.Api;
using Serein.Library.FlowNode;
using Serein.Library.Utils;
using Serein.NodeFlow.Services;
using Serein.NodeFlow.Tool;
using System.Reflection;
namespace Serein.NodeFlow.Env
@@ -58,9 +57,9 @@ namespace Serein.NodeFlow.Env
.Register<IFlowControl, FlowControl>()
.Register<LocalFlowEnvironment>()
.Register<FlowModelService>()
.Register<FlowLibraryService>()
.Register<FlowOperationService>()
.Register<NodeMVVMService>()
.Register<FlowLibraryService>()
.Build();
// 默认使用本地环境
currentFlowEnvironment = ioc.Get<LocalFlowEnvironment>();
@@ -283,9 +282,9 @@ namespace Serein.NodeFlow.Env
}
/// <inheritdoc/>
public async Task<bool> StartFlowFromSelectNodeAsync(string startNodeGuid)
public async Task<TResult> StartFlowAsync<TResult>(string startNodeGuid)
{
return await currentFlowEnvironment.FlowControl.StartFlowFromSelectNodeAsync(startNodeGuid);
return await currentFlowEnvironment.FlowControl.StartFlowAsync<TResult>(startNodeGuid);
}
/// <inheritdoc/>

View File

@@ -29,6 +29,24 @@ namespace Serein.NodeFlow
return false;
}
/// <summary>
/// 是否为根节点
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
public static bool IsRoot(this IFlowNode node)
{
var cts = NodeStaticConfig.ConnectionTypes;
foreach (var ct in cts)
{
if (node.PreviousNodes[ct].Count > 0)
{
return false;
}
}
return true;
}
/// <summary>
/// 创建节点
@@ -89,6 +107,8 @@ namespace Serein.NodeFlow
};
}
/// <summary>
/// 触发器运行后状态转为对应的后继分支类别
/// </summary>
@@ -107,6 +127,8 @@ namespace Serein.NodeFlow
};
}
/// <summary>
/// 判断 触发器节点 是否存在上游分支
/// </summary>
@@ -136,19 +158,27 @@ namespace Serein.NodeFlow
/// <param name="retractCount">缩进次数4个空格</param>
/// <param name="code">要添加的代码</param>
/// <returns>字符串构建器本身</returns>
public static StringBuilder AddCode(this StringBuilder sb,
public static StringBuilder AppendCode(this StringBuilder sb,
int retractCount = 0,
string code = null)
string code = null,
bool isWrapping = true)
{
if (!string.IsNullOrWhiteSpace(code))
{
var retract = new string(' ', retractCount * 4);
sb.AppendLine(retract + code);
string retract = new string(' ', retractCount * 4);
sb.Append(retract);
if (isWrapping)
{
sb.AppendLine(code);
}
else
{
sb.Append(code);
}
}
return sb;
}

View File

@@ -112,6 +112,7 @@ namespace Serein.NodeFlow.Model
/// <returns>节点传回数据对象</returns>
public virtual async Task<FlowResult> ExecutingAsync(IDynamicContext context, CancellationToken token)
{
// 执行触发检查是否需要中断
if (DebugSetting.IsInterrupt)
{
@@ -131,7 +132,6 @@ namespace Serein.NodeFlow.Model
throw new Exception($"节点{this.Guid}不存在对应委托");
}
var instance = Env.IOC.Get(md.ActingInstanceType);
if (instance is null)
{

View File

@@ -266,7 +266,7 @@ namespace Serein.NodeFlow.Model
// 此处代码与SereinFlow.Library.FlowNode.ParameterDetails
// ToMethodArgData()方法中判断流程接口节点分支逻辑耦合
// 不要轻易修改
context.AddOrUpdate(targetNode.Guid, flowData);
context.AddOrUpdateFlowData(targetNode.Guid, flowData);
foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes)
{
if (this.SuccessorNodes[ctType] == null) continue;

View File

@@ -298,25 +298,37 @@ namespace Serein.NodeFlow.Model.Operation
/// </summary>
/// <exception cref="Exception"></exception>
private async Task<bool> CreateArgConnection()
{
{/*
IFlowNode fromNodeControl = ToNode;
IFlowNode toNodeControl = ToNode;
IFlowNode toNodeControl = ToNode;*/
ConnectionArgSourceType type = ConnectionArgSourceType;
int index = ArgIndex;
/*FromNode.NeedResultNodes[type].Remove(ToNode); // 从起始节点的参数来源中移除目标节点
if (FromNode.Guid == ToNode.Guid) // 不能连接到自己
{
SereinEnv.WriteLine(InfoType.ERROR, $"起始节点与目标节点不能是同一个节点" +
$"{Environment.NewLine}起始节点:{FromNode.Guid}" +
$"{Environment.NewLine}目标节点:{ToNode.Guid}");
return false;
}*/
var toNodeArgSourceGuid = ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid; // 目标节点对应参数可能已经有其它连接
var toNodeArgSourceType = ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceType;
if (FromNode.Guid == toNodeArgSourceGuid
&& toNodeArgSourceType == ConnectionArgSourceType)
{
SereinEnv.WriteLine(InfoType.INFO, $"节点之间已建立过连接关系" +
if (FromNode.NeedResultNodes[type].Contains(ToNode))
{
SereinEnv.WriteLine(InfoType.INFO, $"节点之间已建立过连接关系" +
$"起始节点:{FromNode.Guid}" +
$"目标节点:{ToNode.Guid}" +
$"参数索引:{ArgIndex}" +
$"参数类型:{ConnectionArgSourceType}");
return false;
}
FromNode.NeedResultNodes[type].Add(ToNode);
await TriggerEvent(() =>
{
flowEnvironmentEvent.OnNodeConnectChanged(

View File

@@ -2,7 +2,6 @@
using Serein.Library.Api;
using Serein.Library.Utils;
using Serein.NodeFlow.Services;
using Serein.NodeFlow.Tool;
using System;
using System.Collections.Generic;
using System.Diagnostics;

View File

@@ -115,9 +115,8 @@ namespace Serein.NodeFlow.Model.Operation
#endregion
#region
// 需要找到有哪些节点的入参参数,被设置为了该节点,然后将其删除
// 因为节点自身没有记录哪些节点选取了自己作为参数来源节点,所以需要遍历所有节点
// 遍历需要该节点返回值的节点,移除与其的连接
foreach (var item in flowNode.NeedResultNodes)
{
var connectionType = item.Key; // 参数来源连接类型
@@ -128,14 +127,14 @@ namespace Serein.NodeFlow.Model.Operation
if (md is null) continue;
var pds = md.ParameterDetailss;
if (pds is null || pds.Length == 0) continue;
foreach(var parameter in pds)
foreach (var parameter in pds)
{
if (!parameter.ArgDataSourceNodeGuid.Equals(flowNode.Guid)) continue;
// 找到了对应的入参控制点了
var e = new NodeConnectChangeEventArgs(
CanvasGuid, // 画布
flowNode.Guid, // 被移除的节点Guid
argNode.Guid, // 子节点Guid
flowNode.Guid, // 数据来源节点(被移除的节点Guid
argNode.Guid, // 需要数据的节点
parameter.Index, // 作用在第几个参数上,用于指示移除第几个参数的连线
JunctionOfConnectionType.Arg, // 指示移除的是参数连接线
connectionType, // 对应的连接关系
@@ -148,6 +147,34 @@ namespace Serein.NodeFlow.Model.Operation
}
}
}
// 遍历该节点参数详情,获取来源节点,移除与其的连接
if (flowNode.MethodDetails?.ParameterDetailss != null)
{
var pds = flowNode.MethodDetails.ParameterDetailss;
foreach (var pd in pds)
{
if(flowModelService.TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var argSourceNode))
{
// 找到了对应的入参控制点了
var e = new NodeConnectChangeEventArgs(
CanvasGuid, // 画布
argSourceNode.Guid, // 数据来源节点
flowNode.Guid, // 需要数据的节点被移除的节点Guid
pd.Index, // 作用在第几个参数上,用于指示移除第几个参数的连线
JunctionOfConnectionType.Arg, // 指示移除的是参数连接线
pd.ArgDataSourceType, // 对应的连接关系
NodeConnectChangeEventArgs.ConnectChangeType.Remove); // 移除连线
EventArgs.Add(e); // 缓存事件参数
await TriggerEvent(() =>
{
flowEnvironmentEvent.OnNodeConnectChanged(e);
});
}
}
}
#endregion
flowModelService.RemoveNodeModel(flowNode); // 从记录中移除
@@ -163,7 +190,9 @@ namespace Serein.NodeFlow.Model.Operation
// 为了避免直接修改 ObservableCollection 集合导致异常产生故而使用UI线程上下文操作运行
await TriggerEvent(() =>
{
flowCanvasDetails?.Nodes.Remove(flowNode);
var lsit = flowCanvasDetails.Nodes.ToList();
lsit.Remove(flowNode);
flowCanvasDetails.Nodes = lsit;
});
}

View File

@@ -16,7 +16,7 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>.\obj\g</CompilerGeneratedFilesOutputPath>

View File

@@ -13,12 +13,12 @@ using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace Serein.NodeFlow.Tool
namespace Serein.NodeFlow.Services
{
/// <summary>
/// 管理加载在运行环境中的外部程序集
/// </summary>
internal class FlowLibraryService
public class FlowLibraryService
{
public FlowLibraryService(IFlowEnvironment flowEnvironment)
{
@@ -111,6 +111,32 @@ namespace Serein.NodeFlow.Tool
/// <param name="methodName">方法名称</param>
/// <param name="md">返回的方法描述</param>
/// <returns>是否获取成功</returns>
public bool TryGetMethodInfo(string assemblyName, string methodName, [MaybeNullWhen(false)] out MethodInfo methodInfo)
{
if (string.IsNullOrEmpty(assemblyName) || string.IsNullOrEmpty(methodName))
{
methodInfo = null;
return false;
}
if (_myFlowLibrarys.TryGetValue(assemblyName, out var flowLibrary)
&& flowLibrary.MethodInfos.TryGetValue(methodName, out methodInfo))
{
return true;
}
else
{
methodInfo = null;
return false;
}
}
/// <summary>
/// 获取方法描述
/// </summary>
/// <param name="assemblyName">程序集名称</param>
/// <param name="methodName">方法名称</param>
/// <param name="md">返回的方法描述</param>
/// <returns>是否获取成功</returns>
public bool TryGetMethodDetails(string assemblyName, string methodName, [MaybeNullWhen(false)] out MethodDetails md)
{
if (_myFlowLibrarys.TryGetValue(assemblyName, out var flowLibrary)
@@ -238,7 +264,7 @@ namespace Serein.NodeFlow.Tool
/// <summary>
/// 基础依赖
/// </summary>
public readonly static string SereinBaseLibrary = $"{nameof(Serein)}.{nameof(Serein.Library)}.dll";
public readonly static string SereinBaseLibrary = $"{nameof(Serein)}.{nameof(Library)}.dll";
//private (NodeLibraryInfo, List<MethodDetailsInfo>) LoadDllNodeInfo(Assembly assembly)
//{
@@ -331,7 +357,7 @@ namespace Serein.NodeFlow.Tool
string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
if (!string.IsNullOrEmpty(assemblyPath))
{
var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
var assembly = Default.LoadFromAssemblyPath(assemblyPath);
//var assembly = LoadFromAssemblyPath(assemblyPath);
return assembly;
}

View File

@@ -1,9 +1,14 @@
using Serein.Library;
using Newtonsoft.Json.Linq;
using Serein.Library;
using Serein.Library.Api;
using Serein.Library.Utils;
using Serein.NodeFlow.Model;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
@@ -16,10 +21,12 @@ namespace Serein.NodeFlow.Services
public class FlowModelService
{
private readonly IFlowEnvironment environment;
private readonly FlowLibraryService flowLibraryService;
public FlowModelService(IFlowEnvironment environment)
public FlowModelService(IFlowEnvironment environment, FlowLibraryService flowLibraryService )
{
this.environment = environment;
this.flowLibraryService = flowLibraryService;
}
/// <summary>
@@ -114,53 +121,646 @@ namespace Serein.NodeFlow.Services
return flowCanvasDetails.Nodes.Count > 0;
}
#region
public void ToCsharpCoreFile()
public string ToCsharpCoreFile()
{
// TODO: 实现将流程模型转换为C# Core文件的逻辑
// 遍历每个画布
int canvas_index = 0;
#region
#if false
HashSet<Type> assemblyFlowClasss = new HashSet<Type>(); // 用于创建依赖注入项
assemblyFlowClasss.Add(typeof(IFlowCallTree)); // 调用树
StringBuilder stringBuilder = new StringBuilder();
foreach (var canvas in FlowCanvass.Values)
{
if (canvas.StartNode is null)
{
continue;
}
int flowTemplateId = canvas_index++;
string flowTemplateClassName = $"FlowTemplate{flowTemplateId}";
string flowTemplateClassName = $"FlowTemplate_{canvas.Guid.Replace("-", "")}";
HashSet<Type> flowClasss = new HashSet<Type>();
flowClasss.Add(typeof(IFlowCallTree)); // 调用树
// 收集程序集信息
foreach (var node in canvas.Nodes)
{
var instanceType = node.MethodDetails.ActingInstanceType;
if(instanceType is not null)
{
flowClasss.Add(instanceType);
assemblyFlowClasss.Add(instanceType);
if (instanceType is not null)
{
flowClasss.Add(instanceType);
assemblyFlowClasss.Add(instanceType);
}
}
// 生成方法信息
stringBuilder.AppendCode(0, $"public class {flowTemplateClassName}");
stringBuilder.AppendCode(0, $"{{");
// 构造函数及依赖注入字段
GenerateCtor(stringBuilder, flowTemplateClassName, flowClasss);
//GenerateNodeIndexLookup(stringBuilder, flowTemplateClassName, );
GenerateInitMethod(stringBuilder);
GenerateCallTree(stringBuilder, canvas);
// 节点生成方法信息
foreach (var node in canvas.Nodes)
{
var instanceType = node.MethodDetails.ActingInstanceType;
var returnType = node.MethodDetails.ReturnType;
var methodName = node.MethodDetails.MethodAnotherName;
GenerateMethod(stringBuilder, node);
}
stringBuilder.AppendCode(0, $"}}");
}
#else
StringBuilder stringBuilder = new StringBuilder();
HashSet<Type> assemblyFlowClasss = new HashSet<Type>(); // 用于创建依赖注入项
assemblyFlowClasss.Add(typeof(IFlowCallTree)); // 调用树
var flowNodes = NodeModels.Values.ToArray();
// 收集程序集信息
foreach (var node in flowNodes)
{
var instanceType = node.MethodDetails.ActingInstanceType;
if (instanceType is not null)
{
assemblyFlowClasss.Add(instanceType);
}
}
string flowTemplateClassName = $"FlowTemplate"; // 类名
stringBuilder.AppendCode(0, $"public class {flowTemplateClassName} : global::{typeof(IFlowCallTree).FullName}");
stringBuilder.AppendCode(0, $"{{");
GenerateCtor(stringBuilder, flowTemplateClassName, assemblyFlowClasss); // 生成构造方法
GenerateInitMethod(stringBuilder); // 生成初始化方法
GenerateCallTree(stringBuilder, flowNodes); // 生成调用树
GenerateNodeIndexLookup(stringBuilder, flowTemplateClassName, flowNodes); // 初始化节点缓存
foreach (var node in flowNodes)
{
GenerateMethod(stringBuilder, node); // 生成每个节点的方法
}
stringBuilder.AppendCode(0, $"}}");
#endif
#endregion
var instanceTypeFullName = instanceType.FullName;
var returnTypeFullName = returnType == typeof(void) ? "void" : returnType.FullName;
string methodContext = $"private {returnTypeFullName} NodeMethod_{methodName}({nameof(IDynamicContext)} context)";
SereinEnv.WriteLine(InfoType.INFO, methodContext);
return stringBuilder.ToString();
}
/// <summary>
/// 生成构造函数代码
/// </summary>
/// <param name="sb"></param>
/// <param name="className"></param>
/// <param name="assemblyFlowClasss"></param>
/// <returns></returns>
private void GenerateCtor(StringBuilder sb, string className, HashSet<Type> assemblyFlowClasss)
{
if (assemblyFlowClasss.Count == 0)
{
return;
}
var instanceTypes = assemblyFlowClasss.Where(x => !IsStaticClass(x)).ToArray();
for (int index = 0; index < instanceTypes.Length; index++)
{
var type = instanceTypes[index];
var ctor_parms_name = GetCamelCase(type);
sb.AppendCode(2, $"private readonly global::{type.FullName} {ctor_parms_name};");
}
sb.AppendLine();
sb.AppendCode(2, $"public {className}(", false);
for (int index = 0; index < instanceTypes.Length; index++)
{
var type = instanceTypes[index];
var ctor_parms_name = GetCamelCase(type);
sb.Append($"global::{type.FullName} {ctor_parms_name}{(index < instanceTypes.Length - 1 ? "," : "")}");
}
sb.AppendCode(0, $")");
sb.AppendCode(2, $"{{");
for (int index = 0; index < instanceTypes.Length; index++)
{
var type = instanceTypes[index];
var ctor_parms_name = GetCamelCase(type);
sb.AppendCode(3, $"this.{ctor_parms_name} = {ctor_parms_name};");
}
sb.AppendLine();
sb.AppendCode(3, $"Init();"); // 初始化调用树
sb.AppendCode(2, $"}}");
sb.AppendLine();
}
/// <summary>
/// 生成方法调用逻辑
/// </summary>
/// <param name="sb_main"></param>
/// <param name="flowNode"></param>
/// <exception cref="Exception"></exception>
private void GenerateMethod(StringBuilder sb_main, IFlowNode flowNode)
{
string? dynamicContextTypeName = typeof(IDynamicContext).FullName;
string? flowContext = nameof(flowContext);
if (flowNode.ControlType == NodeControlType.Action)
{
#region Action
if (!flowLibraryService.TryGetMethodInfo(flowNode.MethodDetails.AssemblyName,
flowNode.MethodDetails.MethodName,
out var methodInfo) || methodInfo is null)
{
return;
}
var isRootNode = flowNode.IsRoot();
var instanceType = flowNode.MethodDetails.ActingInstanceType;
var returnType = methodInfo.ReturnType;
var instanceName = GetCamelCase(instanceType);// $"instance_{instanceType.Name}";
var instanceTypeFullName = instanceType.FullName;
var returnTypeFullName = returnType == typeof(void) ? "void" : returnType.FullName;
#region
StringBuilder sb_invoke_login = new StringBuilder();
if (flowNode.MethodDetails is null) return;
var param = methodInfo.GetParameters();
var md = flowNode.MethodDetails;
var pds = flowNode.MethodDetails.ParameterDetailss;
if (param is null) return;
if (pds is null) return;
bool isGetPreviousNode = false;
for (int index = 0; index < pds.Length; index++)
{
ParameterDetails? pd = pds[index];
ParameterInfo parameterInfo = param[index];
var paramtTypeFullName = parameterInfo.ParameterType.FullName;
if (pd.IsExplicitData)
{
// 只能是 数值、 文本、枚举, 才能作为显式参数
if (parameterInfo.ParameterType.IsValueType)
{
if (parameterInfo.ParameterType.IsEnum)
{
sb_invoke_login.AppendCode(3, $"global::{paramtTypeFullName} value{index} = global::{paramtTypeFullName}.{pd.DataValue}; // 获取当前节点的上一节点数据");
}
else
{
var value = pd.DataValue.ToConvert(parameterInfo.ParameterType);
sb_invoke_login.AppendCode(3, $"global::{paramtTypeFullName} value{index} = (global::{paramtTypeFullName}){value}; // 获取当前节点的上一节点数据");
}
}
else if (parameterInfo.ParameterType == typeof(string))
{
sb_invoke_login.AppendCode(3, $"global::{paramtTypeFullName} value{index} = \"{pd.DataValue}\"; // 获取当前节点的上一节点数据");
}
else
{
// 处理表达式
}
}
else
{
#region
if (pd.ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData)
{
var previousNode = $"previousNode{index}";
var valueType = pd.IsParams ? $"global::{pd.DataType.FullName}" : $"global::{paramtTypeFullName}";
sb_invoke_login.AppendCode(3, $"global::System.String {previousNode} = {flowContext}.GetPreviousNode(\"{flowNode.Guid}\");"); // 获取运行时上一节点Guid
sb_invoke_login.AppendCode(3, $"{valueType} value{index} = {previousNode} == null ? default : ({valueType}){flowContext}.{nameof(IDynamicContext.GetFlowData)}({previousNode}).Value; // 获取运行时上一节点的数据");
}
else if (pd.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData)
{
if (this.TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var otherNode))
{
var valueType = pd.IsParams ? $"global::{pd.DataType.FullName}" : $"global::{paramtTypeFullName}";
var otherNodeReturnType = otherNode.MethodDetails.ReturnType;
if (otherNodeReturnType == typeof(object))
{
sb_invoke_login.AppendCode(3, $"{valueType} value{index} = ({valueType}){flowContext}.{nameof(IDynamicContext.GetFlowData)}(\"{pd.ArgDataSourceNodeGuid}\").Value; // 获取指定节点的数据");
}
else if (pd.DataType.IsAssignableFrom(otherNodeReturnType))
{
sb_invoke_login.AppendCode(3, $"{valueType} value{index} = {flowContext}.{nameof(IDynamicContext.GetFlowData)}(\"{pd.ArgDataSourceNodeGuid}\").Value; // 获取指定节点的数据");
}
else
{
// 获取的数据无法转换为目标方法入参类型
throw new Exception("获取的数据无法转换为目标方法入参类型");
}
}
else
{
// 指定了Guid但项目中不存在对应的节点需要抛出异常
throw new Exception("指定了Guid但项目中不存在对应的节点");
}
}
else if (pd.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke)
{
if (this.TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var otherNode)) // 获取指定节点
{
var otherNodeReturnType = otherNode.MethodDetails.ReturnType;
var valueType = pd.IsParams ? $"global::{pd.DataType.FullName}" : $"global::{otherNode.MethodDetails.ReturnType.FullName}";
if (otherNodeReturnType == typeof(object))
{
sb_invoke_login.AppendCode(3, $"{valueType} value{index} = ({valueType}){flowContext}.{nameof(IDynamicContext.GetFlowData)}(\"{pd.ArgDataSourceNodeGuid}\").Value; // 获取指定节点的数据");
}
else if (pd.DataType.IsAssignableFrom(otherNodeReturnType))
{
sb_invoke_login.AppendCode(3, $"{valueType} value{index} = {GetNodeMethodName(otherNode)}({flowContext}); // 获取指定节点的数据");
}
else
{
// 获取的数据无法转换为目标方法入参类型
throw new Exception("获取的数据无法转换为目标方法入参类型");
}
}
else
{
// 指定了Guid但项目中不存在对应的节点需要抛出异常
throw new Exception("指定了Guid但项目中不存在对应的节点");
}
}
#endregion
}
}
if (methodInfo.ReturnType == typeof(void))
{
if (methodInfo.IsStatic)
{
sb_invoke_login.AppendCode(3, $"global::{instanceType}.{methodInfo.Name}(", false);
for (int index = 0; index < pds.Length; index++)
{
sb_invoke_login.Append($"{(index == 0 ? "" : ",")}value{index}");
}
sb_invoke_login.AppendCode(0, $"); // 调用方法 {md.MethodAnotherName}");
}
else
{
sb_invoke_login.AppendCode(3, $"{instanceName}.{methodInfo.Name}(", false);
for (int index = 0; index < pds.Length; index++)
{
sb_invoke_login.Append($"{(index == 0 ? "" : ",")}value{index}");
}
sb_invoke_login.AppendCode(0, $"); // 调用方法 {md.MethodAnotherName}");
}
}
else
{
if (methodInfo.IsStatic)
{
sb_invoke_login.AppendCode(3, $"var result = global::{instanceType}.{methodInfo.Name}(", false);
for (int index = 0; index < pds.Length; index++)
{
sb_invoke_login.Append($"{(index == 0 ? "" : ",")}value{index}");
}
sb_invoke_login.AppendCode(0, $"); // 调用方法 {md.MethodAnotherName}");
}
else
{
sb_invoke_login.AppendCode(3, $"var result = {instanceName}.{methodInfo.Name}(", false);
for (int index = 0; index < pds.Length; index++)
{
sb_invoke_login.Append($"{(index == 0 ? "" : ",")}value{index}");
}
sb_invoke_login.AppendCode(0, $"); // 调用方法 {md.MethodAnotherName}");
}
sb_invoke_login.AppendCode(3, $"{flowContext}.{nameof(IDynamicContext.AddOrUpdate)}(\"{flowNode.Guid}\", result);", false);
//sb_invoke_login.AppendCode(3, $"return result;", false);
}
#endregion
// global::{returnTypeFullName}
sb_main.AppendCode(2, $"[Description(\"{instanceTypeFullName}.{methodInfo.Name}\")]");
sb_main.AppendCode(2, $"public void {GetNodeMethodName(flowNode)}(global::{dynamicContextTypeName} {flowContext})");
sb_main.AppendCode(2, $"{{");
sb_main.AppendCode(0, sb_invoke_login.ToString());
sb_main.AppendCode(2, $"}}"); // 方法结束
sb_main.AppendLine(); // 方法结束
#endregion
}
else if (flowNode.ControlType == NodeControlType.Flipflop)
{
}
else if (flowNode.ControlType == NodeControlType.Script)
{
}
else if (flowNode.ControlType == NodeControlType.UI)
{
}
else if (flowNode.ControlType == NodeControlType.ExpCondition)
{
}
else if (flowNode.ControlType == NodeControlType.ExpOp)
{
}
return;
throw new Exception("无法为该节点生成调用逻辑");
}
private void GenerateInitMethod(StringBuilder sb)
{
sb.AppendCode(2, $"public void Init()");
sb.AppendCode(2, $"{{");
sb.AppendCode(3, $"{nameof(GenerateCallTree)}(); // 初始化调用树"); // 初始化调用树
sb.AppendCode(2, $"}}");
}
private void GenerateCallTree(StringBuilder sb, IFlowNode[] flowNodes)
{
// Get("0fa6985b-4b63-4499-80b2-76401669292d").AddChildNodeSucceed(Get("acdbe7ea-eb27-4a3e-9cc9-c48f642ee4f5"));
sb.AppendCode(2, $"private void {nameof(GenerateCallTree)}()");
sb.AppendCode(2, $"{{");
foreach (var node in flowNodes)
{
var nodeMethod = GetNodeMethodName(node); // 节点对应的方法名称
sb.AppendCode(3, $"Get(\"{node.Guid}\").SetAction({nodeMethod});");
}
foreach (var node in flowNodes)
{
var nodeMethod = GetNodeMethodName(node); // 节点对应的方法名称
var cts = NodeStaticConfig.ConnectionTypes;
foreach (var ct in cts)
{
var childNodes = node.SuccessorNodes[ct];
var AddChildNodeMethodName = ct switch
{
ConnectionInvokeType.IsSucceed => nameof(CallNode.AddChildNodeSucceed),
ConnectionInvokeType.IsFail => nameof(CallNode.AddChildNodeFail),
ConnectionInvokeType.IsError => nameof(CallNode.AddChildNodeError),
ConnectionInvokeType.Upstream => nameof(CallNode.AddChildNodeUpstream),
_ => throw new ArgumentOutOfRangeException(nameof(ct), ct, null)
};
foreach (var childNode in childNodes)
{
sb.AppendCode(3, $"Get(\"{node.Guid}\").{AddChildNodeMethodName}(Get(\"{childNode.Guid}\"));");
}
}
}
sb.AppendCode(2, $"}}");
sb.AppendLine();
/*string? dynamicContextTypeName = typeof(IDynamicContext).FullName;
string? flowContext = nameof(flowContext);
var callTreeType = typeof(IFlowCallTree);
var callTreeName = GetCamelCase(callTreeType);
//var canvasGuid = flowCanvas.Guid;
//var startNodeGuid = flowCanvas.StartNode.Guid;
sb.AppendCode(2, $"private void {nameof(GenerateCallTree)}()");
sb.AppendCode(2, $"{{");
//sb.AppendCode(3, $"global::{callTreeType.FullName} {callTreeName} = new global::{callTreeType.FullName}()\";");
// 注册节点
*//* foreach (var node in flowCanvas.Nodes)
{
var nodeMethod = GetNodeMethodName(node);
var call = $"{flowContext} => {nodeMethod}({flowContext})";
sb.AppendCode(3, $"{callTreeName}.{nameof(FlowCallTree.AddCallNode)}(\"{node.Guid}\", {call});");
}*//*
sb.AppendLine();
foreach (var node in flowNodes)
{
var nodeMethod = GetNodeMethodName(node);
var cts = NodeStaticConfig.ConnectionTypes;
foreach (var ct in cts)
{
var childNodes = node.SuccessorNodes[ct];
var addType = ct switch
{
ConnectionInvokeType.IsSucceed => nameof(CallNode.AddChildNodeSucceed),
ConnectionInvokeType.IsFail => nameof(CallNode.AddChildNodeFail),
ConnectionInvokeType.IsError => nameof(CallNode.AddChildNodeError),
ConnectionInvokeType.Upstream => nameof(CallNode.AddChildNodeUpstream),
_ => throw new ArgumentOutOfRangeException(nameof(ct), ct, null)
};
foreach (var childNode in childNodes)
{
sb.AppendCode(3, $"{callTreeName}[\"{node.Guid}\"].{addType}(\"{childNode.Guid}\");");
}
}
}
sb.AppendCode(2, $"}}");
sb.AppendLine();*/
}
private void GenerateNodeIndexLookup(StringBuilder sb, string className, IFlowNode[] flowNodes)
{
// 初始化Id
nodeIdMap.Clear();
for (int index = 0; index < flowNodes.Length; index++)
{
var flowNode = flowNodes[index];
GetNodeId(flowNode);
}
var valueArrayName = "_values";
// 生成 _values
sb.AppendCode(2, $"private readonly static global::Serein.Library.CallNode[] {valueArrayName} = new global::Serein.Library.CallNode[{flowNodes.Length}];");
/*sb.AppendCode(2, $"private readonly static global::System.String[] _keys = new global::System.String[]");
sb.AppendCode(2, $"{{");
for (int index = 0; index < flowNodes.Length; index++)
{
var flowNode = flowNodes[index];
sb.AppendCode(3, $"\"{flowNode.Guid}\", // {index} : {flowNode.MethodDetails.MethodName}");
}
sb.AppendCode(2, $"}};");*/
// 生成静态构造函数
sb.AppendCode(2, $"static {className}()");
sb.AppendCode(2, $"{{");
for (int index = 0; index < flowNodes.Length; index++)
{
var flowNode = flowNodes[index];
sb.AppendCode(3, $"{valueArrayName}[{index}] = new global::Serein.Library.CallNode(\"{flowNode.Guid}\"); // {index} : {flowNode.MethodDetails.MethodName}");
}
sb.AppendCode(2, $"}}");
// 初始化 Get 函数
var nodeIndexName = "node_index";
sb.AppendCode(2, $" [MethodImpl(MethodImplOptions.AggressiveInlining)]"); // 内联优化
sb.AppendCode(2, $"public global::Serein.Library.CallNode {nameof(IFlowCallTree.Get)}( global::System.String key)");
sb.AppendCode(2, $"{{");
sb.AppendCode(3, $"global::System.Int32 {nodeIndexName};");
sb.AppendCode(3, $"switch (key)");
sb.AppendCode(3, $"{{");
for (int index = 0; index < flowNodes.Length; index++)
{
var flowNode = flowNodes[index];
sb.AppendCode(4, $"case \"{flowNode.Guid}\":");
sb.AppendCode(5, $"{nodeIndexName} = {index};");
sb.AppendCode(5, $"break;");
}
sb.AppendCode(4, $"default:");
sb.AppendCode(4, $"{nodeIndexName} = -1;");
sb.AppendCode(5, $"break;");
sb.AppendCode(3, $"}}");
sb.AppendCode(3, $"return {valueArrayName}[{nodeIndexName}];");
sb.AppendCode(2, $"}}");
}
/// <summary>
/// 生成方法名称
/// </summary>
/// <param name="flowNode"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private string GetNodeMethodName(IFlowNode flowNode)
{
/*if (!flowLibraryService.TryGetMethodInfo(flowNode.MethodDetails.AssemblyName,
flowNode.MethodDetails.MethodName,
out var methodInfo))
{
throw new Exception();
}*/
var guid = flowNode.Guid;
var tmp = guid.Replace("-", "");
var methodName = $"FlowMethod_{tmp}";
return methodName;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool IsStaticClass(Type type)
{
return type.IsAbstract && type.IsSealed && type.IsClass;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private string GetCamelCase(Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
// 获取类型名称(不包括命名空间)
string typeName = type.Name;
if (string.IsNullOrEmpty(typeName))
{
return string.Empty;
}
// 处理泛型类型(去掉后面的`N
int indexOfBacktick = typeName.IndexOf('`');
if (indexOfBacktick > 0)
{
typeName = typeName.Substring(0, indexOfBacktick);
}
// 如果是接口且以"I"开头,去掉第一个字母
if (type.IsInterface && typeName.Length > 1 && typeName[0] == 'I' && char.IsUpper(typeName[1]))
{
typeName = typeName.Substring(1);
}
// 转换为驼峰命名法:首字母小写,其余不变
if (typeName.Length > 0)
{
return char.ToLowerInvariant(typeName[0]) + typeName.Substring(1);
}
return typeName;
}
private Dictionary<IFlowNode, int> nodeIdMap = new Dictionary<IFlowNode, int>();
private int GetNodeId(IFlowNode flowNode)
{
if (nodeIdMap.ContainsKey(flowNode))
{
return nodeIdMap[flowNode];
}
else
{
lock (nodeIdMap)
{
int id = nodeIdMap.Count + 1; // 从1开始计数
nodeIdMap[flowNode] = id;
return id;
}
}
}
#endregion
}
}
/* /// <summary>
/// 生成方法名称
/// </summary>
/// <param name="flowNode"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
private string GetNodeMethodName(IFlowNode flowNode)
{
return $"FlowMethod_{flowNode.Guid.Remove('-')}";
if (flowNode.ControlType == NodeControlType.Action)
{
if (!flowLibraryService.TryGetMethodInfo(flowNode.MethodDetails.AssemblyName,
flowNode.MethodDetails.MethodName,
out var methodInfo))
{
throw new Exception();
}
return $"FlowMethod_{nameof(NodeControlType.Action)}_{methodInfo.Name}";
}
else if (flowNode.ControlType == NodeControlType.Flipflop)
{
if (!flowLibraryService.TryGetMethodInfo(flowNode.MethodDetails.AssemblyName,
flowNode.MethodDetails.MethodName,
out var methodInfo))
{
throw new Exception();
}
return $"FlowMethod_{nameof(NodeControlType.Flipflop)}_{methodInfo.Name}";
}
else if (flowNode.ControlType == NodeControlType.Script)
{
return $"FlowMethod_{flowNode.Guid.Remove('-')}";
}
else if (flowNode.ControlType == NodeControlType.UI)
{
}
else if (flowNode.ControlType == NodeControlType.ExpCondition)
{
}
else if (flowNode.ControlType == NodeControlType.ExpOp)
{
}
else
{
throw new Exception("无法为该节点生成方法名称");
}
}*/

View File

@@ -268,14 +268,16 @@ namespace Serein.NodeFlow.Services
/// </summary>
/// <param name="startNode"></param>
/// <returns></returns>
public async Task StartFlowInSelectNodeAsync(IFlowNode startNode)
public async Task<FlowResult> StartFlowInSelectNodeAsync(IFlowNode startNode)
{
var pool = WorkOptions.FlowContextPool;
var context = pool.Allocate();
var token = WorkOptions.CancellationTokenSource.Token;
var result = await startNode.StartFlowAsync(context, token); // 开始运行时从选定节点开始运行
context.Reset();
pool.Free(context);
return result;
}
/// <summary>
@@ -342,7 +344,7 @@ namespace Serein.NodeFlow.Services
{
var context = pool.Allocate(); // 启动全局触发器时新建上下文
var newFlowData = await singleFlipFlopNode.ExecutingAsync(context, singleToken); // 获取触发器等待Task
context.AddOrUpdate(singleFlipFlopNode.Guid, newFlowData);
context.AddOrUpdateFlowData(singleFlipFlopNode.Guid, newFlowData);
if (context.NextOrientation == ConnectionInvokeType.None)
{
continue;

View File

@@ -14,10 +14,18 @@ using static System.Runtime.InteropServices.JavaScript.JSType;
namespace Serein.NodeFlow
{
public class LibraryMdDd (MethodDetails methodDetails,DelegateDetails delegateDetails)
public class LibraryMdDd
{
public MethodDetails MethodDetails { get; } = methodDetails;
public DelegateDetails DelegateDetails { get; } = delegateDetails;
public MethodDetails MethodDetails { get; }
public MethodInfo MethodInfo { get; }
public DelegateDetails DelegateDetails { get; }
public LibraryMdDd(MethodInfo methodInfo, MethodDetails methodDetails, DelegateDetails delegateDetails)
{
MethodDetails = methodDetails;
MethodInfo = methodInfo;
DelegateDetails = delegateDetails;
}
}
@@ -28,8 +36,6 @@ namespace Serein.NodeFlow
{
public Assembly Assembly { get; private set; }
//private readonly Action actionOfUnloadAssmbly;
/*, Action actionOfUnloadAssmbly*/
//this.actionOfUnloadAssmbly = actionOfUnloadAssmbly;
@@ -60,6 +66,7 @@ namespace Serein.NodeFlow
/// Value :方法详情
/// </summary>
public ConcurrentDictionary<string, MethodDetails> MethodDetailss { get; } = new ConcurrentDictionary<string, MethodDetails>();
public ConcurrentDictionary<string, MethodInfo> MethodInfos { get; } = new ConcurrentDictionary<string, MethodInfo>();
/// <summary>
/// 管理通过Emit动态构建的委托
@@ -112,6 +119,7 @@ namespace Serein.NodeFlow
public bool LoadAssembly()
{
Assembly assembly = this.Assembly;
#region
// 加载DLL创建 MethodDetails、实例作用对象、委托方法
@@ -142,7 +150,7 @@ namespace Serein.NodeFlow
#endregion
#region DynamicFlow 退
// Type 具有 DynamicFlowAttribute 标记的类型
// string 类型元数据 DynamicFlowAttribute 特性中的 Name 属性 (用于生成方法描述时,添加在方法别名中提高可读性)
@@ -183,13 +191,13 @@ namespace Serein.NodeFlow
{
// 尝试创建
if (!NodeMethodDetailsHelper.TryCreateDetails(type, methodInfo, assemblyName,
out var md, out var dd)) // 返回的描述
out var mi, out var md, out var dd)) // 返回的描述
{
SereinEnv.WriteLine(InfoType.ERROR, $"无法加载方法信息:{assemblyName}-{type}-{methodInfo}");
continue;
}
md.MethodAnotherName = flowName + md.MethodAnotherName; // 方法别名
detailss.Add(new LibraryMdDd(md, dd));
detailss.Add(new LibraryMdDd(mi, md, dd));
}
}
@@ -219,6 +227,7 @@ namespace Serein.NodeFlow
{
var key = item.MethodDetails.MethodName;
MethodDetailss.TryAdd(key, item.MethodDetails);
MethodInfos.TryAdd(key, item.MethodInfo);
DelegateDetailss.TryAdd(key, item.DelegateDetails);
}

View File

@@ -35,6 +35,7 @@ public static class NodeMethodDetailsHelper
public static bool TryCreateDetails(Type type,
MethodInfo methodInfo,
string assemblyName,
[MaybeNullWhen(false)] out MethodInfo outMethodInfo,
[MaybeNullWhen(false)] out MethodDetails methodDetails,
[MaybeNullWhen(false)] out DelegateDetails delegateDetails)
{
@@ -43,6 +44,7 @@ public static class NodeMethodDetailsHelper
var attribute = methodInfo.GetCustomAttribute<NodeActionAttribute>();
if(attribute is null || attribute.Scan == false)
{
outMethodInfo = null;
methodDetails = null;
delegateDetails = null;
return false;
@@ -62,17 +64,18 @@ public static class NodeMethodDetailsHelper
Type? returnType;
bool isTask = IsGenericTask(methodInfo.ReturnType, out var taskResult);
bool isAsync = IsGenericTask(methodInfo.ReturnType, out var taskResult);
if (attribute.MethodDynamicType == Library.NodeType.UI)
{
if (isTask)
if (isAsync)
{
var innerType = methodInfo.ReturnType.GetGenericArguments()[0];
if (innerType.IsGenericType && innerType != typeof(IEmbeddedContent))
{
SereinEnv.WriteLine(InfoType.WARN, $"[{methodName}]跳过创建因为UI方法的返回值并非IEmbeddedContent流程工作台将无法正确显示自定义控件界面以及传递数据。");
outMethodInfo = null;
methodDetails = null;
delegateDetails = null;
return false;
@@ -83,6 +86,7 @@ public static class NodeMethodDetailsHelper
if (methodInfo.ReturnType != typeof(IEmbeddedContent))
{
SereinEnv.WriteLine(InfoType.WARN, $"[{methodName}]跳过创建因为UI方法的返回值并非IEmbeddedContent流程工作台将无法正确显示自定义控件界面以及传递数据。");
outMethodInfo = null;
methodDetails = null;
delegateDetails = null;
return false;
@@ -106,6 +110,7 @@ public static class NodeMethodDetailsHelper
else
{
SereinEnv.WriteLine(InfoType.WARN, $"[{methodName}]跳过创建返回类型非预期的Task<IFlipflopContext<TResult>>。");
outMethodInfo = null;
methodDetails = null;
delegateDetails = null;
return false;
@@ -114,13 +119,14 @@ public static class NodeMethodDetailsHelper
else
{
SereinEnv.WriteLine(InfoType.WARN, $"[{methodName}]跳过创建因为触发器方法的返回值并非Task<>,将无法等待。");
outMethodInfo = null;
methodDetails = null;
delegateDetails = null;
return false;
}
}
else if(isTask)
else if(isAsync)
{
returnType = taskResult is null ? typeof(Task) : taskResult;
}
@@ -134,7 +140,7 @@ public static class NodeMethodDetailsHelper
}
var asyncPrefix = "[异步]"; // IsGenericTask(returnType) ? "[async]" : ;
var methodMethodAnotherName = isTask ? asyncPrefix + attribute.AnotherName : attribute.AnotherName;
var methodMethodAnotherName = isAsync ? asyncPrefix + attribute.AnotherName : attribute.AnotherName;
bool hasParamsArg = false;
if (explicitDataOfParameters.Length > 0)
@@ -155,13 +161,14 @@ public static class NodeMethodDetailsHelper
ReturnType = returnType,
// 如果存在可变参数,取最后一个元素的下标,否则为-1
ParamsArgIndex = hasParamsArg ? explicitDataOfParameters.Length - 1 : -1,
IsAsync = isAsync,
};
//var emitMethodType = EmitHelper.CreateDynamicMethod(methodInfo, out var methodDelegate);// 返回值
var dd = new DelegateDetails(methodInfo) ; // 构造委托
outMethodInfo = methodInfo;
methodDetails = md;
delegateDetails = dd;
return true;