diff --git a/Library/FlowNode/NodeDebugSetting.cs b/Library/FlowNode/NodeDebugSetting.cs index d556ab3..cf979e0 100644 --- a/Library/FlowNode/NodeDebugSetting.cs +++ b/Library/FlowNode/NodeDebugSetting.cs @@ -28,7 +28,7 @@ namespace Serein.Library /// 是否保护参数 /// [PropertyInfo(IsNotification = true)] - private bool _isProtectionParameter; + private bool _isProtectionParameter = false; /// /// 对应的节点 diff --git a/Library/Utils/SereinIoc.cs b/Library/Utils/SereinIoc.cs index 4ace00b..16c29de 100644 --- a/Library/Utils/SereinIoc.cs +++ b/Library/Utils/SereinIoc.cs @@ -321,7 +321,14 @@ namespace Serein.Library.Utils var typeFullName = typeMapping.Key; // 注册的类型 FullName var type = typeMapping.Value; // 对应的Type。如果是以接口形式注册,typeFullName将是接口类的FullName,而type将是接口实现类。 var constructors = GetConstructor(type); // 获取构造函数 - + if(constructors .Length == 0) + { + if (!dependencyMap[IOC_MAIN].Contains(type.FullName)) + { + //dependencyMap[IOC_MAIN].Add(type.FullName); + dependencyMap[IOC_MAIN].Add(typeFullName); + } + } foreach (var constructor in constructors) { if (constructor is null) diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs index 7cfdc6c..2ab330c 100644 --- a/NodeFlow/Env/FlowEnvironment.cs +++ b/NodeFlow/Env/FlowEnvironment.cs @@ -50,16 +50,17 @@ namespace Serein.NodeFlow.Env public FlowEnvironment() { ISereinIOC ioc = new SereinIOC(); - ioc.Register(()=> ioc) // 注册IOC - .Register(() => this) - .Register() - .Register() - .Register() - .Register() - .Register() - .Register() - .Register() - .Register() + ioc.Register(()=> ioc) // IOC容器接口 + .Register(() => this) // 流程环境接口 + .Register() // 流程环境事件接口 + .Register() // 流程编辑接口 + .Register() // 流程控制接口 + .Register() // 本地环境 + .Register() // 节点/画布模型服务 + .Register() // 流程库服务 + .Register() // 代码生成 + .Register() // 流程操作 + .Register() // 节点MVVM服务 .Build(); // 默认使用本地环境 currentFlowEnvironment = ioc.Get(); diff --git a/NodeFlow/Model/Node/NodeModelBaseData.cs b/NodeFlow/Model/Node/NodeModelBaseData.cs index e023e77..e6122be 100644 --- a/NodeFlow/Model/Node/NodeModelBaseData.cs +++ b/NodeFlow/Model/Node/NodeModelBaseData.cs @@ -145,12 +145,14 @@ namespace Serein.NodeFlow.Model /// partial void OnIsPublicChanged(bool oldValue, bool newValue) { + var list = CanvasDetails.PublicNodes.ToList(); if (newValue) { // 公开节点 if (!CanvasDetails.PublicNodes.Contains(this)) { - CanvasDetails.PublicNodes.Add(this); + list.Add(this); + CanvasDetails.PublicNodes= list; } } else @@ -158,7 +160,8 @@ namespace Serein.NodeFlow.Model // 取消公开 if (CanvasDetails.PublicNodes.Contains(this)) { - CanvasDetails.PublicNodes.Remove(this); + list.Remove(this); + CanvasDetails.PublicNodes = list; } } } diff --git a/NodeFlow/Model/Node/SingleFlowCallNode.cs b/NodeFlow/Model/Node/SingleFlowCallNode.cs index 11a01f8..61036b7 100644 --- a/NodeFlow/Model/Node/SingleFlowCallNode.cs +++ b/NodeFlow/Model/Node/SingleFlowCallNode.cs @@ -1,11 +1,13 @@ using Newtonsoft.Json.Linq; using Serein.Library; using Serein.Library.Api; +using Serein.NodeFlow.Services; using Serein.Script; using System; using System.Collections.Generic; using System.Dynamic; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; using System.Xml.Linq; @@ -28,6 +30,12 @@ namespace Serein.NodeFlow.Model /// [PropertyInfo(IsNotification = true)] private bool _isShareParam ; + + /// + /// 接口全局名称 + /// + [PropertyInfo(IsNotification = true)] + private string _apiGlobalName; } @@ -40,15 +48,12 @@ namespace Serein.NodeFlow.Model /// /// 被调用的节点 /// - private IFlowNode targetNode; + public IFlowNode TargetNode { get; private set; } /// /// 缓存的方法信息 /// public MethodDetails CacheMethodDetails { get; private set; } - - - public SingleFlowCallNode(IFlowEnvironment environment) : base(environment) { @@ -60,7 +65,7 @@ namespace Serein.NodeFlow.Model /// public void ResetTargetNode() { - if (targetNode is not null) + if (TargetNode is not null) { // 取消接口 TargetNodeGuid = string.Empty; @@ -82,31 +87,35 @@ namespace Serein.NodeFlow.Model partial void OnTargetNodeGuidChanged(string value) { - if (string.IsNullOrEmpty(value) || !Env.TryGetNodeModel(value, out targetNode)) + if (string.IsNullOrEmpty(value) || !Env.TryGetNodeModel(value, out var targetNode)) { // 取消设置接口节点 - targetNode.PropertyChanged -= TargetNode_PropertyChanged; + TargetNode.PropertyChanged -= TargetNode_PropertyChanged; + TargetNode = null; + this.ApiGlobalName = ""; this.MethodDetails = new MethodDetails(); } else { //if (this.MethodDetails.ActingInstanceType.FullName.Equals()) - - if(!this.IsShareParam + TargetNode = targetNode; + if (!this.IsShareParam && CacheMethodDetails is not null - && targetNode.MethodDetails is not null - && targetNode.MethodDetails.AssemblyName == CacheMethodDetails.AssemblyName - && targetNode.MethodDetails.MethodName == CacheMethodDetails.MethodName) + && TargetNode.MethodDetails is not null + && TargetNode.MethodDetails.AssemblyName == CacheMethodDetails.AssemblyName + && TargetNode.MethodDetails.MethodName == CacheMethodDetails.MethodName) { this.MethodDetails = CacheMethodDetails; + this.ApiGlobalName = GetApiInvokeName(this); } else { - if (targetNode.MethodDetails is not null) + if (TargetNode.MethodDetails is not null) { - CacheMethodDetails = targetNode.MethodDetails.CloneOfNode(this); // 从目标节点复制一份 - targetNode.PropertyChanged += TargetNode_PropertyChanged; + CacheMethodDetails = TargetNode.MethodDetails.CloneOfNode(this); // 从目标节点复制一份 + TargetNode.PropertyChanged += TargetNode_PropertyChanged; this.MethodDetails = CacheMethodDetails; + this.ApiGlobalName = GetApiInvokeName(this); } } @@ -117,19 +126,19 @@ namespace Serein.NodeFlow.Model partial void OnIsShareParamChanged(bool value) { - if (targetNode is null || targetNode.MethodDetails is null) + if (TargetNode is null || TargetNode.MethodDetails is null) { return; } if (value) { - CacheMethodDetails = targetNode.MethodDetails.CloneOfNode(this); + CacheMethodDetails = TargetNode.MethodDetails.CloneOfNode(this); this.MethodDetails = CacheMethodDetails; } else { - if(targetNode.ControlType == NodeControlType.Script) + if(TargetNode.ControlType == NodeControlType.Script) { // 脚本节点入参需不可编辑入参数量、入参名称 foreach (var item in CacheMethodDetails.ParameterDetailss) @@ -152,7 +161,7 @@ namespace Serein.NodeFlow.Model { this.SuccessorNodes[ctType] = []; } - targetNode.PropertyChanged -= TargetNode_PropertyChanged; + TargetNode.PropertyChanged -= TargetNode_PropertyChanged; } } @@ -163,7 +172,7 @@ namespace Serein.NodeFlow.Model /// private bool UploadTargetNode() { - if (targetNode is null) + if (TargetNode is null) { if (string.IsNullOrWhiteSpace(TargetNodeGuid)) { @@ -175,7 +184,7 @@ namespace Serein.NodeFlow.Model return false; } - this.targetNode = targetNode; + this.TargetNode = targetNode; } return true; @@ -189,12 +198,60 @@ namespace Serein.NodeFlow.Model public override NodeInfo SaveCustomData(NodeInfo nodeInfo) { dynamic data = new ExpandoObject(); - data.TargetNodeGuid = targetNode?.Guid; // 变量名称 + data.TargetNodeGuid = TargetNode?.Guid; // 变量名称 data.IsShareParam = IsShareParam; + data.ApiGlobalName = ApiGlobalName; nodeInfo.CustomData = data; return nodeInfo; } + private static Dictionary ApiInvokeNameCache = new Dictionary(); + public static int getApiInvokeNameCount = 0; + private static string GetApiInvokeName(SingleFlowCallNode node, string apiName) + { + if (ApiInvokeNameCache.ContainsKey(apiName)) + { + var count = ApiInvokeNameCache[apiName]; + count++; + ApiInvokeNameCache[apiName] = count; + return $"{apiName}{count}"; + } + else + { + ApiInvokeNameCache[apiName] = 0; + return $"{apiName}"; + } + } + public static string GetApiInvokeName(SingleFlowCallNode node) + { + if(node.TargetNode is null) + { + return GetApiInvokeName(node, "ApiInvoke"); // 如果没有目标节点,则返回默认名称 + } + var md = node.TargetNode.MethodDetails; + if (md is null) + { + var apiName = $"{node.TargetNode.ControlType}"; + return GetApiInvokeName(node, apiName); + } + else + { + FlowLibraryService service = node.Env.IOC.Get(); + if (service.TryGetMethodInfo(md.AssemblyName, md.MethodName, out var methodInfo)) + { + + var apiName = $"{methodInfo.Name}"; + return GetApiInvokeName(node, apiName); + } + else + { + var apiName = $"{node.TargetNode.ControlType}"; + return GetApiInvokeName(node, apiName); + } + } + } + + /// /// 加载全局变量的数据 /// @@ -207,7 +264,7 @@ namespace Serein.NodeFlow.Model if (Env.TryGetNodeModel(targetNodeGuid, out var targetNode)) { TargetNodeGuid = targetNode.Guid; - this.targetNode = targetNode; + this.TargetNode = targetNode; if(this.IsShareParam == false) { @@ -216,6 +273,8 @@ namespace Serein.NodeFlow.Model } } + + this.ApiGlobalName = nodeInfo.CustomData?.ApiGlobalName ?? GetApiInvokeName(this); } else { @@ -248,13 +307,13 @@ namespace Serein.NodeFlow.Model } if (IsShareParam) { - this.MethodDetails = targetNode.MethodDetails; + this.MethodDetails = TargetNode.MethodDetails; } - this.SuccessorNodes = targetNode.SuccessorNodes; + this.SuccessorNodes = TargetNode.SuccessorNodes; - FlowResult flowData = await (targetNode.ControlType switch + FlowResult flowData = await (TargetNode.ControlType switch { - NodeControlType.Script => ((SingleScriptNode)targetNode).ExecutingAsync(this, context, token), + NodeControlType.Script => ((SingleScriptNode)TargetNode).ExecutingAsync(this, context, token), _ => base.ExecutingAsync(context, token) }); @@ -266,7 +325,7 @@ namespace Serein.NodeFlow.Model // 此处代码与SereinFlow.Library.FlowNode.ParameterDetails // ToMethodArgData()方法中判断流程接口节点分支逻辑耦合 // 不要轻易修改 - context.AddOrUpdateFlowData(targetNode.Guid, flowData); + context.AddOrUpdateFlowData(TargetNode.Guid, flowData); foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes) { if (this.SuccessorNodes[ctType] == null) continue; diff --git a/NodeFlow/Model/Operation/RemoveNodeOperation.cs b/NodeFlow/Model/Operation/RemoveNodeOperation.cs index b501b01..de14c46 100644 --- a/NodeFlow/Model/Operation/RemoveNodeOperation.cs +++ b/NodeFlow/Model/Operation/RemoveNodeOperation.cs @@ -155,6 +155,7 @@ namespace Serein.NodeFlow.Model.Operation var pds = flowNode.MethodDetails.ParameterDetailss; foreach (var pd in pds) { + if (string.IsNullOrWhiteSpace(pd.ArgDataSourceNodeGuid)) continue; if(flowModelService.TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var argSourceNode)) { // 找到了对应的入参控制点了 diff --git a/NodeFlow/Services/FlowCoreGenerateService.cs b/NodeFlow/Services/FlowCoreGenerateService.cs new file mode 100644 index 0000000..6340a69 --- /dev/null +++ b/NodeFlow/Services/FlowCoreGenerateService.cs @@ -0,0 +1,1235 @@ +using Serein.Library; +using Serein.Library.Api; +using Serein.Library.Utils; +using Serein.NodeFlow.Env; +using Serein.NodeFlow.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.NodeFlow.Services +{ + /// + /// 流程代码生成服务 + /// + public class FlowCoreGenerateService + { + private readonly FlowModelService flowModelService; + private readonly FlowLibraryService flowLibraryService; + + public FlowCoreGenerateService(FlowModelService flowModelService ,FlowLibraryService flowLibraryService ) + { + this.flowModelService = flowModelService; + this.flowLibraryService = flowLibraryService; + } + + + public string ToCsharpCoreFile() + { + StringBuilder stringBuilder = new StringBuilder(); + HashSet assemblyFlowClasss = new HashSet(); // 用于创建依赖注入项 + assemblyFlowClasss.Add(typeof(IFlowEnvironment)); // 调用树 + + + var flowNodes = flowModelService.GetAllNodeModel().ToArray(); + // 收集程序集信息 + foreach (var node in flowNodes) + { + var instanceType = node.MethodDetails.ActingInstanceType; + if (instanceType is not null) + { + assemblyFlowClasss.Add(instanceType); + } + + } + var flowCallNode = flowModelService.GetAllNodeModel().Where(n => n.ControlType == NodeControlType.FlowCall).Select(n => (SingleFlowCallNode)n).ToArray(); + GenerateFlowApi_InitFlowApiMethodInfos(flowCallNode); + GenerateFlowApi_InterfaceAndImpleClass(stringBuilder); // 生成接口类 + GenerateFlowApi_ApiParamClass(stringBuilder); // 生成接口参数类 + string flowTemplateClassName = $"FlowTemplate"; // 类名 + string flowApiInterfaceName = $"IFlowApiInvoke"; // 类名 + stringBuilder.AppendCode(0, $"public class {flowTemplateClassName} : {flowApiInterfaceName}, 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); // 生成每个节点的方法 + } + + // 生成实现流程接口的实现方法 + var infos = flowApiMethodInfos.Values.ToArray(); + foreach (var info in infos) + { + stringBuilder.AppendCode(2, info.ToObjPoolSignature()); + stringBuilder.AppendCode(2, info.ToImpleMethodSignature(FlowApiMethodInfo.ParamType.Defute)); + stringBuilder.AppendCode(2, info.ToImpleMethodSignature(FlowApiMethodInfo.ParamType.HasToken)); + stringBuilder.AppendCode(2, info.ToImpleMethodSignature(FlowApiMethodInfo.ParamType.HasContextAndToken)); + } + stringBuilder.AppendCode(0, $"}}"); + + return stringBuilder.ToString(); + } + + /// + /// 生成构造函数代码 + /// + /// + /// + /// + /// + private void GenerateCtor(StringBuilder sb, string className, HashSet 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 = type.ToCamelCase(); + 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 = type.ToCamelCase(); + 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 = type.ToCamelCase(); + sb.AppendCode(3, $"this.{ctor_parms_name} = {ctor_parms_name};"); + } + sb.AppendLine(); + sb.AppendCode(3, $"Init();"); // 初始化调用树 + sb.AppendCode(2, $"}}"); + sb.AppendLine(); + } + + /// + /// 生成方法调用逻辑 + /// + /// + /// + /// + private void GenerateMethod(StringBuilder sb_main, IFlowNode flowNode) + { + string? dynamicContextTypeName = typeof(IDynamicContext).FullName; + string? flowContext = nameof(flowContext); + + if (flowNode.ControlType == NodeControlType.Action && flowNode is SingleActionNode actionNode) + { + CreateMethodCore_Action(sb_main, actionNode, dynamicContextTypeName, flowContext); + } + 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) + { + } + else if (flowNode.ControlType == NodeControlType.FlowCall && flowNode is SingleFlowCallNode flowCallNode) + { + CreateMethodCore_FlowCall(sb_main, flowCallNode, dynamicContextTypeName, flowContext); + } + + return; + throw new Exception("无法为该节点生成调用逻辑"); + } + + /// + /// 生成[Action]节点的方法调用 + /// + /// + /// + /// + /// + /// + private void CreateMethodCore_Action(StringBuilder sb_main, SingleActionNode actionNode, string? dynamicContextTypeName, string flowContext) + { + if (!flowLibraryService.TryGetMethodInfo(actionNode.MethodDetails.AssemblyName, + actionNode.MethodDetails.MethodName, + out var methodInfo) || methodInfo is null) + { + return; + } + + var isRootNode = actionNode.IsRoot(); + + var instanceType = actionNode.MethodDetails.ActingInstanceType; + var returnType = methodInfo.ReturnType; + + var instanceName = instanceType.ToCamelCase();// $"instance_{instanceType.Name}"; + + var instanceTypeFullName = instanceType.FullName; + var returnTypeFullName = returnType == typeof(void) ? "void" : returnType.FullName; + + #region 方法内部逻辑 + StringBuilder sb_invoke_login = new StringBuilder(); + if (actionNode.MethodDetails is null) return; + var param = methodInfo.GetParameters(); + var md = actionNode.MethodDetails; + var pds = actionNode.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(\"{actionNode.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 (flowModelService.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 (flowModelService.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} = {otherNode.ToNodeMethodName()}({flowContext}); // 获取指定节点的数据"); + } + else + { + // 获取的数据无法转换为目标方法入参类型 + throw new Exception("获取的数据无法转换为目标方法入参类型"); + } + + } + else + { + // 指定了Guid,但项目中不存在对应的节点,需要抛出异常 + throw new Exception("指定了Guid,但项目中不存在对应的节点"); + } + } + #endregion + + } + } + + if (methodInfo.ReturnType == typeof(void)) + { + // 调用无返回值方法 + // 如果目标方法是静态的,则以“命名空间.类.方法”形式调用,否则以“实例.方法”形式调用 + var invokeFunctionContext = methodInfo.IsStatic ? $"global::{instanceType}.{methodInfo.Name}" : $"{instanceName}.{methodInfo.Name}"; + // 如果目标方法是异步的,则自动 await 进行等待 + invokeFunctionContext = md.IsAsync ? $"await {invokeFunctionContext}" : invokeFunctionContext; + sb_invoke_login.AppendCode(3, $"global::{invokeFunctionContext}(", 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 + { + // 调用有返回值方法 + // 如果目标方法是静态的,则以“命名空间.类.方法”形式调用,否则以“实例.方法”形式调用 + var invokeFunctionContext = methodInfo.IsStatic ? $"global::{instanceType}.{methodInfo.Name}" : $"{instanceName}.{methodInfo.Name}"; + // 如果目标方法是异步的,则自动 await 进行等待 + invokeFunctionContext = md.IsAsync ? $"await {invokeFunctionContext}" : invokeFunctionContext; + sb_invoke_login.AppendCode(3, $"var result = {invokeFunctionContext}(", 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)}(\"{actionNode.Guid}\", result);", false); // 更新数据 + //sb_invoke_login.AppendCode(3, $"return result;", false); + } + #endregion + + // global::{returnTypeFullName} + + var resultTypeName = actionNode.MethodDetails.IsAsync ? "async Task" : "void"; + + sb_main.AppendCode(2, $"[Description(\"{instanceTypeFullName}.{methodInfo.Name}\")]"); + sb_main.AppendCode(2, $"private {resultTypeName} {actionNode.ToNodeMethodName()}(global::{dynamicContextTypeName} {flowContext})"); + sb_main.AppendCode(2, $"{{"); + sb_main.AppendCode(0, sb_invoke_login.ToString()); + sb_main.AppendCode(2, $"}}"); // 方法结束 + sb_main.AppendLine(); // 方法结束 + + } + + /// + /// 生成[FlowCall]节点的方法调用 + /// + /// + /// + /// + /// + /// + private void CreateMethodCore_FlowCall(StringBuilder sb_main, SingleFlowCallNode flowCallNode, string? dynamicContextTypeName, string flowContext) + { + if (!flowApiMethodInfos.TryGetValue(flowCallNode, out var flowApiMethodInfo)) + { + return; + } + if (!flowLibraryService.TryGetMethodInfo(flowCallNode.MethodDetails.AssemblyName, + flowCallNode.MethodDetails.MethodName, + out var methodInfo) || methodInfo is null) + { + return; + } + + var isRootNode = flowCallNode.IsRoot(); + + var instanceType = flowCallNode.MethodDetails.ActingInstanceType; + var returnType = methodInfo.ReturnType; + + var instanceName = instanceType.ToCamelCase();// $"instance_{instanceType.Name}"; + + var instanceTypeFullName = instanceType.FullName; + var returnTypeFullName = returnType == typeof(void) ? "void" : returnType.FullName; + + #region 方法内部逻辑 + StringBuilder sb_invoke_login = new StringBuilder(); + + if (flowCallNode.MethodDetails is null) return; + var param = methodInfo.GetParameters(); + var md = flowCallNode.MethodDetails; + var pds = flowCallNode.MethodDetails.ParameterDetailss; + if (param is null) return; + if (pds is null) return; + + for (int index = 0; index < pds.Length; index++) + { + ParameterDetails? pd = pds[index]; + ParameterInfo parameterInfo = param[index]; + var paramtTypeFullName = parameterInfo.ParameterType.FullName; + } + + var flowDataName = $"flowData{flowApiMethodInfo.ApiMethodName}"; + var apiData = $"apiData{flowApiMethodInfo.ApiMethodName}"; + sb_invoke_login.AppendCode(3, $"global::{typeof(object).FullName} {flowDataName} = {flowContext}.GetFlowData(\"{flowApiMethodInfo.ApiMethodName}\").Value;"); + sb_invoke_login.AppendCode(3, $"if({flowDataName} is {flowApiMethodInfo.ParamTypeName} {apiData})"); + sb_invoke_login.AppendCode(3, $"{{"); + foreach (var info in flowApiMethodInfo.ParamInfos) + { + sb_invoke_login.AppendCode(4, $"global::{info.Type.FullName} {info.ParamName} = {apiData}.{info.ParamName.ToPascalCase()}; "); + } + var invokeParamContext = string.Join(", ", flowApiMethodInfo.ParamInfos.Select(x => x.ParamName)); + if (flowApiMethodInfo.IsVoid) + { + // 调用无返回值方法 + // 如果目标方法是静态的,则以“命名空间.类.方法”形式调用,否则以“实例.方法”形式调用 + var invokeFunctionContext = methodInfo.IsStatic ? $"global::{instanceType}.{methodInfo.Name}" : $"{instanceName}.{methodInfo.Name}"; + // 如果目标方法是异步的,则自动 await 进行等待 + sb_invoke_login.AppendCode(4, $"{(md.IsAsync ? $"await" : string.Empty)} {invokeFunctionContext}({invokeParamContext});"); + } + else + { + // 调用有返回值方法 + var resultName = $"result{flowApiMethodInfo.ApiMethodName}"; + // 如果目标方法是静态的,则以“命名空间.类.方法”形式调用,否则以“实例.方法”形式调用 + var invokeFunctionContext = methodInfo.IsStatic ? $"global::{instanceType}.{methodInfo.Name}" : $"{instanceName}.{methodInfo.Name}"; + // 如果目标方法是异步的,则自动 await 进行等待 + sb_invoke_login.AppendCode(4, $"var {resultName} = {(md.IsAsync ? $"await" : string.Empty)} {invokeFunctionContext}({invokeParamContext});"); + + sb_invoke_login.AppendCode(4, $"{flowContext}.{nameof(IDynamicContext.AddOrUpdate)}(\"{flowCallNode.TargetNode.Guid}\", {resultName});"); // 更新数据 + sb_invoke_login.AppendCode(4, $"{flowApiMethodInfo.ObjPoolName}.Return({apiData});"); // 归还到对象池 + //sb_invoke_login.AppendCode(3, $"return result;", false); + } + sb_invoke_login.AppendCode(3, $"}}"); + sb_invoke_login.AppendCode(3, $"else"); + sb_invoke_login.AppendCode(3, $"{{"); + sb_invoke_login.AppendCode(4, $"throw new Exception(\"接口参数类型异常\");"); + sb_invoke_login.AppendCode(3, $"}}"); + + + + + #endregion + + // global::{returnTypeFullName} + + var resultTypeName = flowCallNode.MethodDetails.IsAsync ? "async Task" : "void"; + + sb_main.AppendCode(2, $"[Description(\"{instanceTypeFullName}.{methodInfo.Name}\")]"); + sb_main.AppendCode(2, $"private {resultTypeName} {flowCallNode.ToNodeMethodName()}(global::{dynamicContextTypeName} {flowContext})"); + sb_main.AppendCode(2, $"{{"); + sb_main.AppendCode(0, sb_invoke_login.ToString()); + sb_main.AppendCode(2, $"}} "); // 方法结束 + + sb_main.AppendLine(); // 方法结束 + + } + + /// + /// 生成初始化方法(用于执行构造函数中无法完成的操作) + /// + /// + private void GenerateInitMethod(StringBuilder sb) + { + sb.AppendCode(2, $"private 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 = node.ToNodeMethodName(); // 节点对应的方法名称 + if (node.ControlType == NodeControlType.Action || node.ControlType == NodeControlType.FlowCall) + { + sb.AppendCode(3, $"Get(\"{node.Guid}\").SetAction({nodeMethod});"); + } + else + { + sb.AppendCode(3, $"// Get(\"{node.Guid}\").SetAction({nodeMethod}); // 暂未实现节点类型为[{node.ControlType}]的方法生成"); + + } + + } + + var cts = NodeStaticConfig.ConnectionTypes; + foreach (var node in flowNodes) + { + + if (node.ControlType == NodeControlType.FlowCall && node is SingleFlowCallNode flowCallNode) + { + foreach (var ct in cts) + { + var childNodes = flowCallNode.TargetNode.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}\")); // 流程节点调用"); + } + } + } + else + { + var nodeMethod = node.ToNodeMethodName(); // 节点对应的方法名称 + 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, $"}}"); + } + + + private Dictionary flowApiMethodInfos = []; + private void GenerateFlowApi_InitFlowApiMethodInfos(SingleFlowCallNode[] flowCallNodes) + { + + flowCallNodes = flowCallNodes.Where(node => !string.IsNullOrWhiteSpace(node.TargetNodeGuid) + && !flowModelService.ContainsCanvasModel(node.TargetNodeGuid)) + .ToArray(); // 筛选流程接口节点,只生成有效的 + + foreach (var flowCallNode in flowCallNodes) + { + var info = flowCallNode.ToFlowApiMethodInfo(); + if (info is not null) + { + flowApiMethodInfos.TryAdd(flowCallNode, info); + } + } + } + private void GenerateFlowApi_InterfaceAndImpleClass(StringBuilder sb) + { + /* + 最终生成的接口示例(示例不包含命名空间): + public interface IFlowApiInvoke + { + Task ApiInvoke(string name, int value); + Task ApiInvoke(CancellationToken token, string name, int value); + Task ApiInvoke(IDynamicContext context, CancellationToken token, string name, int value); + } + */ + + sb.AppendCode(2, $"public interface IFlowApiInvoke"); + sb.AppendCode(2, $"{{"); + var infos = flowApiMethodInfos.Values.ToArray(); + foreach (var info in infos) + { + var xmlDescription = $"{$"流程接口,{info.NodeModel.MethodDetails.MethodAnotherName}".ToXmlComments(2)}"; + sb.AppendCode(2, xmlDescription); + sb.AppendCode(2, info.ToInterfaceMethodSignature(FlowApiMethodInfo.ParamType.Defute)); + sb.AppendCode(2, xmlDescription); + sb.AppendCode(2, info.ToInterfaceMethodSignature(FlowApiMethodInfo.ParamType.HasToken)); + sb.AppendCode(2, xmlDescription); + sb.AppendCode(2, info.ToInterfaceMethodSignature(FlowApiMethodInfo.ParamType.HasContextAndToken)); + /* sb.AppendCode(2, xmlDescription); + sb.AppendCode(2, info.ToImpleMethodSignature(FlowApiMethodInfo.ParamType.Defute)); + sb.AppendCode(2, xmlDescription); + sb.AppendCode(2, info.ToImpleMethodSignature(FlowApiMethodInfo.ParamType.HasToken)); + sb.AppendCode(2, xmlDescription); + sb.AppendCode(2, info.ToImpleMethodSignature(FlowApiMethodInfo.ParamType.HasContextAndToken));*/ + } + sb.AppendLine(); + sb.AppendCode(2, $"}}"); + + } + + + private void GenerateFlowApi_ApiParamClass(StringBuilder sb) + { + var infos = flowApiMethodInfos.Values.ToArray(); + foreach (var info in infos) + { + sb.AppendLine(info.ToParamterClassSignature()); + } + } + + + #region 辅助代码生成的方法 + + /// + /// 判断类型是否为静态类 + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsStaticClass(Type type) + { + return type.IsAbstract && type.IsSealed && type.IsClass; + } + + /// + /// 节点ID映射 + /// + private Dictionary nodeIdMap = new Dictionary(); + + /// + /// 获取节点ID + /// + /// + /// + 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 + + } + + + + + + + + + + + + + + + internal class FlowApiMethodInfo + { + public FlowApiMethodInfo(SingleFlowCallNode singleFlowCallNode) + { + NodeModel = singleFlowCallNode; + } + public SingleFlowCallNode NodeModel { get; } // 流程调用节点 + public string ApiMethodName { get; set; } + public Type ReturnType { get; set; } + public List ParamInfos { get; set; } = []; + + public string ParamTypeName => $"FlowApiInvoke_{ApiMethodName}"; + + public class ParamInfo + { + public ParamInfo(Type type, string paramName) + { + Type = type; + ParamName = paramName; + } + + public Type Type { get; set; } + public string ParamName { get; set; } + public string Comments { get; set; } + } + + + public enum ParamType + { + Defute, // 仅使用方法参数 + HasToken, // 包含取消令牌和方法参数 + HasContextAndToken, // 包含上下文、取消令牌和方法参数 + } + public bool IsVoid => ReturnType == typeof(void); + public string ObjPoolName => $"_objPool{ParamTypeName.ToPascalCase()}"; + /// + /// 生成所需的参数类的签名代码 + /// + /// + public string ToParamterClassSignature() + { + StringBuilder sb = new StringBuilder(); + + + //sb.AppendCode(2, $"private static readonly global::Microsoft.Extensions.ObjectPool.DefaultObjectPool<{ParamTypeName}> {ObjPoolName} = "); + //sb.AppendCode(2, $" new global::Microsoft.Extensions.ObjectPool.DefaultObjectPool<{ParamTypeName}>("); + //sb.AppendCode(2, $" new global::Microsoft.Extensions.ObjectPool.DefaultPooledObjectPolicy<{ParamTypeName}>()); "); + sb.AppendLine(); + var classXmlComments = $"流程接口[{ApiMethodName}]需要的参数类".ToXmlComments(2); + sb.AppendCode(2, classXmlComments); + sb.AppendCode(2, $"public class {ParamTypeName}"); + sb.AppendCode(2, $"{{"); + for (int index = 0; index < ParamInfos.Count; index++) + { + ParamInfo? info = ParamInfos[index]; + var argXmlComments = $"[{index}]流程接口参数{(string.IsNullOrWhiteSpace(info.Comments) ? string.Empty : $",{info.Comments}。")}".ToXmlComments(2); + sb.AppendCode(3, argXmlComments); + sb.AppendCode(3, $"public global::{info.Type.FullName} {info.ParamName.ToPascalCase()} {{ get; set; }}"); + } + sb.AppendCode(2, $"}}"); + return sb.ToString(); + } + + public string ToObjPoolSignature() + { + StringBuilder sb = new StringBuilder(); + sb.AppendCode(2, $"private static readonly global::Microsoft.Extensions.ObjectPool.DefaultObjectPool<{ParamTypeName}> {ObjPoolName} = "); + sb.AppendCode(4, $"new global::Microsoft.Extensions.ObjectPool.DefaultObjectPool<{ParamTypeName}>("); + sb.AppendCode(5, $"new global::Microsoft.Extensions.ObjectPool.DefaultPooledObjectPolicy<{ParamTypeName}>()); "); + return sb.ToString(); + } + + /// + /// 生成接口的签名方法 + /// + /// + /// + /// + public string ToInterfaceMethodSignature(ParamType type) + { + var taskTypeFullName = $"global::System.Threading.Tasks.Task"; + var contextFullName = $"global::{typeof(IDynamicContext).FullName}"; + var tokenFullName = $"global::{typeof(CancellationToken).FullName}"; + var returnContext = IsVoid ? taskTypeFullName : $"{taskTypeFullName}<{ReturnType.FullName}>"; + if (type == ParamType.Defute) + { + var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{p.Type.FullName} {p.ParamName}")); + return $"{returnContext} {ApiMethodName}({paramSignature});"; + } + else if (type == ParamType.HasToken) + { + var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{p.Type.FullName} {p.ParamName}")); + return $"{returnContext} {ApiMethodName}({tokenFullName} token, {paramSignature});"; + } + else if (type == ParamType.HasContextAndToken) + { + var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{p.Type.FullName} {p.ParamName}")); + return $"{returnContext} {ApiMethodName}({contextFullName} flowContext, {tokenFullName} token, {paramSignature});"; + } + throw new Exception(); + } + + /// + /// 生成实现方法的签名代码 + /// + /// + /// + /// + public string ToImpleMethodSignature(ParamType type) + { + var taskTypeFullName = $"global::System.Threading.Tasks.Task"; + var contextApiFullName = $"global::{typeof(IDynamicContext).FullName}"; + var contextImpleFullName = $"global::{typeof(DynamicContext).FullName}"; + var tokenSourceFullName = $"global::{typeof(CancellationTokenSource).FullName}"; + var tokenFullName = $"global::{typeof(CancellationToken).FullName}"; + string flowEnvironment = nameof(flowEnvironment); + string flowContext = nameof(flowContext); + string token = nameof(token); + + var returnTypeContext = IsVoid ? taskTypeFullName : $"{taskTypeFullName}<{ReturnType.FullName}>"; + + StringBuilder sb = new StringBuilder(); + if (IsVoid) + { + if (type == ParamType.Defute) + { + var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{{{p.Type.FullName} {p.ParamName}")); + var invokeParamSignature = string.Join(", ", ParamInfos.Select(p => p.ParamName)); + sb.AppendCode(2, $"public async {returnTypeContext} {ApiMethodName}({paramSignature})"); + sb.AppendCode(2, $"{{"); + sb.AppendCode(3, $"{contextApiFullName} {flowContext} = new {contextImpleFullName}({flowEnvironment}); // 创建上下文"); + sb.AppendCode(3, $"{tokenSourceFullName} cts = new {tokenSourceFullName}(); // 创建取消令牌"); + sb.AppendCode(3, $"await {ApiMethodName}({flowContext}, cts.Token, {invokeParamSignature}); // 调用目标方法"); + sb.AppendCode(3, $"{flowContext}.{nameof(IDynamicContext.Reset)}(); "); + sb.AppendCode(3, $"cts.{nameof(CancellationTokenSource.Dispose)}(); "); + sb.AppendCode(2, $"}}"); + return sb.ToString(); + } + else if (type == ParamType.HasToken) + { + var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{{{p.Type.FullName} {p.ParamName}")); + var invokeParamSignature = string.Join(", ", ParamInfos.Select(p => p.ParamName)); + sb.AppendCode(2, $"public async {returnTypeContext} {ApiMethodName}({tokenFullName} {token}, {paramSignature})"); + sb.AppendCode(2, $"{{"); + sb.AppendCode(3, $"{contextApiFullName} {flowContext} = new {contextImpleFullName}({flowEnvironment}); // 创建上下文"); + sb.AppendCode(3, $"await {ApiMethodName}({flowContext}, {token}, {invokeParamSignature}); // 调用目标方法"); + sb.AppendCode(3, $"{flowContext}.{nameof(IDynamicContext.Reset)}(); "); + sb.AppendCode(2, $"}}"); + return sb.ToString(); + } + else if (type == ParamType.HasContextAndToken) + { + var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{{{p.Type.FullName} {p.ParamName}")); + var invokeParamSignature = string.Join(", ", ParamInfos.Select(p => p.ParamName)); + + + sb.AppendCode(2, $"public async {returnTypeContext} {ApiMethodName}({contextApiFullName} {flowContext}, {tokenFullName} token, {paramSignature})"); + sb.AppendCode(2, $"{{"); + sb.AppendCode(3, $"token.ThrowIfCancellationRequested(); // 检查任务是否取消"); + // 生成参数类实例化代码 + sb.AppendCode(3, $"global::{ParamTypeName} data = new global::{ParamTypeName}"); + sb.AppendCode(3, $"{{"); + for (int index = 0; index < ParamInfos.Count; index++) + { + ParamInfo? info = ParamInfos[index]; + sb.AppendCode(4, $"{info.ParamName.ToPascalCase()} = {info.ParamName}; // [{index}] {info.Comments}"); + } + sb.AppendCode(3, $"}}"); + + sb.AppendCode(3, $"{flowContext}.{nameof(IDynamicContext.AddOrUpdate)}(\"{ApiMethodName}\", data);"); + sb.AppendCode(3, $"{flowContext}.{nameof(IDynamicContext.SetPreviousNode)}(\"{NodeModel.Guid}\", \"{ApiMethodName}\");"); + sb.AppendCode(3, $"global::{typeof(CallNode).FullName} node = Get(\"{NodeModel.Guid}\");"); + sb.AppendCode(3, $"await node.{nameof(CallNode.StartFlowAsync)}({flowContext}, {token}); // 调用目标方法"); + sb.AppendCode(2, $"}}"); + return sb.ToString(); + } + } + else + { + string flowResult = nameof(flowResult); + if (type == ParamType.Defute) + { + var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{p.Type.FullName} {p.ParamName}")); + var invokeParamSignature = string.Join(", ", ParamInfos.Select(p => p.ParamName)); + sb.AppendCode(2, $"public async {returnTypeContext} {ApiMethodName}({paramSignature})"); + sb.AppendCode(2, $"{{"); + sb.AppendCode(3, $"{contextApiFullName} {flowContext} = new {contextImpleFullName}({flowEnvironment}); // 创建上下文"); + sb.AppendCode(3, $"{tokenSourceFullName} cts = new {tokenSourceFullName}(); // 创建取消令牌"); + sb.AppendCode(3, $"{ReturnType.FullName} {flowResult} = await {ApiMethodName}({flowContext}, cts.{nameof(CancellationTokenSource.Token)}, {invokeParamSignature}); // 调用目标方法"); + sb.AppendCode(3, $"{flowContext}.{nameof(IDynamicContext.Reset)}(); "); + sb.AppendCode(3, $"cts.{nameof(CancellationTokenSource.Dispose)}(); "); + sb.AppendCode(3, $"return {flowResult};"); + sb.AppendCode(2, $"}}"); + return sb.ToString(); + } + else if (type == ParamType.HasToken) + { + var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{p.Type.FullName} {p.ParamName}")); + var invokeParamSignature = string.Join(", ", ParamInfos.Select(p => p.ParamName)); + sb.AppendCode(2, $"public async {returnTypeContext} {ApiMethodName}({tokenFullName} {token}, {paramSignature})"); + sb.AppendCode(2, $"{{"); + sb.AppendCode(3, $"{contextApiFullName} {flowContext} = new {contextImpleFullName}({flowEnvironment}); // 创建上下文"); + sb.AppendCode(3, $"{ReturnType.FullName} {flowResult} = await {ApiMethodName}({flowContext}, {token}, {invokeParamSignature}); // 调用目标方法"); + sb.AppendCode(3, $"{flowContext}.{nameof(IDynamicContext.Reset)}(); "); + sb.AppendCode(3, $"return {flowResult};"); + sb.AppendCode(2, $"}}"); + return sb.ToString(); + + } + + + else if (type == ParamType.HasContextAndToken) + { + var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{p.Type.FullName} {p.ParamName}")); + var invokeParamSignature = string.Join(", ", ParamInfos.Select(p => p.ParamName)); + sb.AppendCode(2, $"public async {returnTypeContext} {ApiMethodName}({contextApiFullName} {flowContext}, {tokenFullName} token, {paramSignature})"); + sb.AppendCode(2, $"{{"); + sb.AppendCode(3, $"token.ThrowIfCancellationRequested(); // 检查任务是否取消"); + // 生成参数类实例化代码 + /*sb.AppendCode(3, $"global::{ParamTypeName} data = new global::{ParamTypeName}"); + sb.AppendCode(3, $"{{"); + for (int index = 0; index < ParamInfos.Count; index++) + { + ParamInfo? info = ParamInfos[index]; + sb.AppendCode(4, $"{info.ParamName.ToPascalCase()} = {info.ParamName}; // [{index}] {info.Comments}"); + } + sb.AppendCode(3, $"}}");*/ + sb.AppendCode(3, $"global::{ParamTypeName} data = {ObjPoolName}.Get(); // 从对象池获取一个对象"); + for (int index = 0; index < ParamInfos.Count; index++) + { + ParamInfo? info = ParamInfos[index]; + sb.AppendCode(4, $"data.{info.ParamName.ToPascalCase()} = {info.ParamName}; // [{index}] {info.Comments}"); + } + + sb.AppendCode(3, $"{flowContext}.{nameof(IDynamicContext.AddOrUpdate)}(\"{ApiMethodName}\", data);"); + sb.AppendCode(3, $"{flowContext}.{nameof(IDynamicContext.SetPreviousNode)}(\"{NodeModel.Guid}\", \"{ApiMethodName}\");"); + sb.AppendCode(3, $"global::{typeof(CallNode).FullName} node = Get(\"{NodeModel.Guid}\");"); + sb.AppendCode(3, $"global::{typeof(FlowResult).FullName} {flowResult} = await node.{nameof(CallNode.StartFlowAsync)}({flowContext}, {token}); // 调用目标方法"); + sb.AppendCode(3, $"if ({flowResult}.{nameof(FlowResult.Value)} is global::{ReturnType.FullName} result)"); + sb.AppendCode(3, $"{{"); + sb.AppendCode(4, $"return result;"); + sb.AppendCode(3, $"}}"); + sb.AppendCode(3, $"else"); + sb.AppendCode(3, $"{{"); + sb.AppendCode(4, $"throw new ArgumentNullException($\"类型转换失败,{{(flowResult.Value is null ? \"返回数据为 null\" : $\"返回数据与需求类型不匹配,当前返回类型为[{{flowResult.Value.GetType().FullName}}。\")}}\");"); + sb.AppendCode(3, $"}}"); + sb.AppendCode(3, $"return {flowResult};"); + sb.AppendCode(2, $"}}"); + return sb.ToString(); + // throw new ArgumentNullException($"类型转换失败,{(flowResult.Value is null ? "返回数据为 null" : $"返回数据与需求类型不匹配,当前返回类型为[{flowResult.Value.GetType().FullName}。")}"); + } + } + + throw new Exception(); + + } + } + + internal static class CoreGenerateExtension + { + /// + /// 生成流程接口信息描述 + /// + /// + /// + public static FlowApiMethodInfo? ToFlowApiMethodInfo(this SingleFlowCallNode flowCallNode) + { + var targetNode = flowCallNode.TargetNode; + if (targetNode.ControlType is not (NodeControlType.Action or NodeControlType.Script)) return null; + if (flowCallNode.MethodDetails is null) return null; + if (string.IsNullOrWhiteSpace(flowCallNode.ApiGlobalName)) return null; + + + FlowApiMethodInfo flowApiMethodInfo = new FlowApiMethodInfo(flowCallNode); + flowApiMethodInfo.ReturnType = targetNode.ControlType == NodeControlType.Script ? typeof(object) + : flowCallNode.MethodDetails.ReturnType; + + flowApiMethodInfo.ApiMethodName = flowCallNode.ApiGlobalName; + + List list = []; + + int index = 0; + foreach (var pd in flowCallNode.MethodDetails.ParameterDetailss) + { + if (pd.DataType is null || string.IsNullOrWhiteSpace(pd.Name)) + { + return null; + } + if (pd.IsParams) + { + list.Add(new FlowApiMethodInfo.ParamInfo(pd.DataType, $"{pd.Name}{index++}")); + } + else + { + list.Add(new FlowApiMethodInfo.ParamInfo(pd.DataType, pd.Name)); + } + } + + flowApiMethodInfo.ParamInfos = list; + return flowApiMethodInfo; + } + + + + /// + /// 生成方法名称 + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToNodeMethodName(this 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; + } + + + /// + /// 生成完全的xml注释 + /// + /// + /// + public static string ToXmlComments(this string context, int retractCount = 0) + { + StringBuilder sb = new StringBuilder(); + var startLine = "/// "; + var endLine = "/// "; + sb.AppendLine(startLine); + var rows = context.Split(Environment.NewLine); + string retract = new string(' ', retractCount * 4); + foreach (var row in rows) + { + // 处理转义 + var value = row.Replace("<", "<") + .Replace(">", ">"); + sb.AppendLine($"{retract}/// {value}"); + } + sb.AppendLine(endLine); + return sb.ToString(); + } + + /// + /// 生成类型的驼峰命名法名称(首字母小写) + /// + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToCamelCase(this 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; + } + + /// + /// 生成类型的大驼峰命名法名称(PascalCase) + /// + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToPascalCase(this Type type) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + string typeName = type.Name; + + if (string.IsNullOrEmpty(typeName)) + { + return string.Empty; + } + + // 去掉泛型标记(如 `1) + int indexOfBacktick = typeName.IndexOf('`'); + if (indexOfBacktick > 0) + { + typeName = typeName.Substring(0, indexOfBacktick); + } + + // 如果是接口以 I 开头,且后面是大写,去掉前缀 I + if (type.IsInterface && typeName.Length > 1 && typeName[0] == 'I' && char.IsUpper(typeName[1])) + { + typeName = typeName.Substring(1); + } + + // 首字母转为大写(如果有需要) + if (typeName.Length > 0) + { + return char.ToUpperInvariant(typeName[0]) + typeName.Substring(1); + } + + return typeName; + } + + /// + /// 将字符串首字母大写(PascalCase) + /// + /// 原始文本 + /// 首字母大写的文本 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToPascalCase(this string text) + { + if (string.IsNullOrEmpty(text)) + { + return string.Empty; + } + + if (char.IsUpper(text[0])) + { + return text; // 已是大写 + } + + return char.ToUpperInvariant(text[0]) + text.Substring(1); + } + } + + + +} diff --git a/NodeFlow/FlowInterruptTool.cs b/NodeFlow/Services/FlowInterruptTool.cs similarity index 86% rename from NodeFlow/FlowInterruptTool.cs rename to NodeFlow/Services/FlowInterruptTool.cs index f85b1e1..71ae9d0 100644 --- a/NodeFlow/FlowInterruptTool.cs +++ b/NodeFlow/Services/FlowInterruptTool.cs @@ -7,7 +7,7 @@ using System.Reactive.Subjects; using System.Text; using System.Threading.Tasks; -namespace Serein.NodeFlow +namespace Serein.NodeFlow.Services { } diff --git a/NodeFlow/Services/FlowModelService.cs b/NodeFlow/Services/FlowModelService.cs index d1ca2d3..b2f87df 100644 --- a/NodeFlow/Services/FlowModelService.cs +++ b/NodeFlow/Services/FlowModelService.cs @@ -1,16 +1,20 @@ -using Newtonsoft.Json.Linq; +using Microsoft.Extensions.ObjectPool; +using Newtonsoft.Json.Linq; using Serein.Library; using Serein.Library.Api; using Serein.Library.Utils; +using Serein.NodeFlow; using Serein.NodeFlow.Model; using System; using System.Collections.Generic; +using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; +using System.Xml; using System.Xml.Linq; namespace Serein.NodeFlow.Services @@ -23,7 +27,7 @@ namespace Serein.NodeFlow.Services private readonly IFlowEnvironment environment; private readonly FlowLibraryService flowLibraryService; - public FlowModelService(IFlowEnvironment environment, FlowLibraryService flowLibraryService ) + public FlowModelService(IFlowEnvironment environment, FlowLibraryService flowLibraryService) { this.environment = environment; this.flowLibraryService = flowLibraryService; @@ -50,21 +54,21 @@ namespace Serein.NodeFlow.Services NodeModels.TryGetValue(guid, out var nodeModel); return nodeModel; } - + public FlowCanvasDetails? GetCanvasModel(string guid) { FlowCanvass.TryGetValue(guid, out var nodeModel); return nodeModel; } - public bool TryGetNodeModel(string guid,out IFlowNode flowNode) + public bool TryGetNodeModel(string guid, out IFlowNode flowNode) { return NodeModels.TryGetValue(guid, out flowNode!); } - public bool TryGetCanvasModel(string guid,out FlowCanvasDetails flowCanvas) + public bool TryGetCanvasModel(string guid, out FlowCanvasDetails flowCanvas) { - return FlowCanvass.TryGetValue(guid, out flowCanvas!);; + return FlowCanvass.TryGetValue(guid, out flowCanvas!); ; } @@ -72,7 +76,7 @@ namespace Serein.NodeFlow.Services { return NodeModels.ContainsKey(guid); } - + public bool ContainsCanvasModel(string guid) { return FlowCanvass.ContainsKey(guid); @@ -106,12 +110,11 @@ namespace Serein.NodeFlow.Services NodeModels.Values.Where(x => x.CanvasDetails.Guid == canvasGuid).ToList(); public List GetAllCanvasModel() => [.. FlowCanvass.Values]; - - public bool IsExsitCanvas() { return FlowCanvass.Count > 0; } + public bool IsExsitNodeOnCanvas(string canvasGuid) { if (!FlowCanvass.TryGetValue(canvasGuid, out var flowCanvasDetails)) @@ -121,597 +124,11 @@ namespace Serein.NodeFlow.Services return flowCanvasDetails.Nodes.Count > 0; } - #region 代码生成 - - public string ToCsharpCoreFile() - { - #region 生成类和方法 -#if false - HashSet assemblyFlowClasss = new HashSet(); // 用于创建依赖注入项 - 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_{canvas.Guid.Replace("-", "")}"; - - HashSet flowClasss = new HashSet(); - 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); - } - } - - 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) - { - GenerateMethod(stringBuilder, node); - } - stringBuilder.AppendCode(0, $"}}"); - - } -#else - StringBuilder stringBuilder = new StringBuilder(); - HashSet assemblyFlowClasss = new HashSet(); // 用于创建依赖注入项 - 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 - - - return stringBuilder.ToString(); - } - - /// - /// 生成构造函数代码 - /// - /// - /// - /// - /// - private void GenerateCtor(StringBuilder sb, string className, HashSet 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(); - } - - /// - /// 生成方法调用逻辑 - /// - /// - /// - /// - 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, $"}}"); - } - - /// - /// 生成方法名称 - /// - /// - /// - [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 nodeIdMap = new Dictionary(); - 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 } } + /* /// /// 生成方法名称 /// diff --git a/Workbench/Node/View/FlowCallNodeControl.xaml b/Workbench/Node/View/FlowCallNodeControl.xaml index bc058b5..f6c2984 100644 --- a/Workbench/Node/View/FlowCallNodeControl.xaml +++ b/Workbench/Node/View/FlowCallNodeControl.xaml @@ -77,6 +77,7 @@ + @@ -97,14 +98,18 @@ + Visibility="{Binding SelectCanvas.Model.PublicNodes, UpdateSourceTrigger=PropertyChanged,Converter={StaticResource CountToVisibilityConverter}}"> + + + + - + diff --git a/Workbench/ViewModels/MainMenuBarViewModel.cs b/Workbench/ViewModels/MainMenuBarViewModel.cs index e8cafac..b940314 100644 --- a/Workbench/ViewModels/MainMenuBarViewModel.cs +++ b/Workbench/ViewModels/MainMenuBarViewModel.cs @@ -137,8 +137,8 @@ namespace Serein.Workbench.ViewModels var env = App.GetService(); - var flowModelService = env.IOC.Get(); - var text = flowModelService.ToCsharpCoreFile(); ; + var service = env.IOC.Get(); + var text = service.ToCsharpCoreFile(); ; Debug.WriteLine(text); }