mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-03 00:00:49 +08:00
添加了 Action FlowCall 节点的代码生成支持
This commit is contained in:
@@ -28,7 +28,7 @@ namespace Serein.Library
|
|||||||
/// 是否保护参数
|
/// 是否保护参数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PropertyInfo(IsNotification = true)]
|
[PropertyInfo(IsNotification = true)]
|
||||||
private bool _isProtectionParameter;
|
private bool _isProtectionParameter = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 对应的节点
|
/// 对应的节点
|
||||||
|
|||||||
@@ -321,7 +321,14 @@ namespace Serein.Library.Utils
|
|||||||
var typeFullName = typeMapping.Key; // 注册的类型 FullName
|
var typeFullName = typeMapping.Key; // 注册的类型 FullName
|
||||||
var type = typeMapping.Value; // 对应的Type。如果是以接口形式注册,typeFullName将是接口类的FullName,而type将是接口实现类。
|
var type = typeMapping.Value; // 对应的Type。如果是以接口形式注册,typeFullName将是接口类的FullName,而type将是接口实现类。
|
||||||
var constructors = GetConstructor(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)
|
foreach (var constructor in constructors)
|
||||||
{
|
{
|
||||||
if (constructor is null)
|
if (constructor is null)
|
||||||
|
|||||||
@@ -50,16 +50,17 @@ namespace Serein.NodeFlow.Env
|
|||||||
public FlowEnvironment()
|
public FlowEnvironment()
|
||||||
{
|
{
|
||||||
ISereinIOC ioc = new SereinIOC();
|
ISereinIOC ioc = new SereinIOC();
|
||||||
ioc.Register<ISereinIOC>(()=> ioc) // 注册IOC
|
ioc.Register<ISereinIOC>(()=> ioc) // IOC容器接口
|
||||||
.Register<IFlowEnvironment>(() => this)
|
.Register<IFlowEnvironment>(() => this) // 流程环境接口
|
||||||
.Register<IFlowEnvironmentEvent, FlowEnvironmentEvent>()
|
.Register<IFlowEnvironmentEvent, FlowEnvironmentEvent>() // 流程环境事件接口
|
||||||
.Register<IFlowEdit, FlowEdit>()
|
.Register<IFlowEdit, FlowEdit>() // 流程编辑接口
|
||||||
.Register<IFlowControl, FlowControl>()
|
.Register<IFlowControl, FlowControl>() // 流程控制接口
|
||||||
.Register<LocalFlowEnvironment>()
|
.Register<LocalFlowEnvironment>() // 本地环境
|
||||||
.Register<FlowModelService>()
|
.Register<FlowModelService>() // 节点/画布模型服务
|
||||||
.Register<FlowLibraryService>()
|
.Register<FlowLibraryService>() // 流程库服务
|
||||||
.Register<FlowOperationService>()
|
.Register<FlowCoreGenerateService>() // 代码生成
|
||||||
.Register<NodeMVVMService>()
|
.Register<FlowOperationService>() // 流程操作
|
||||||
|
.Register<NodeMVVMService>() // 节点MVVM服务
|
||||||
.Build();
|
.Build();
|
||||||
// 默认使用本地环境
|
// 默认使用本地环境
|
||||||
currentFlowEnvironment = ioc.Get<LocalFlowEnvironment>();
|
currentFlowEnvironment = ioc.Get<LocalFlowEnvironment>();
|
||||||
|
|||||||
@@ -145,12 +145,14 @@ namespace Serein.NodeFlow.Model
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
partial void OnIsPublicChanged(bool oldValue, bool newValue)
|
partial void OnIsPublicChanged(bool oldValue, bool newValue)
|
||||||
{
|
{
|
||||||
|
var list = CanvasDetails.PublicNodes.ToList();
|
||||||
if (newValue)
|
if (newValue)
|
||||||
{
|
{
|
||||||
// 公开节点
|
// 公开节点
|
||||||
if (!CanvasDetails.PublicNodes.Contains(this))
|
if (!CanvasDetails.PublicNodes.Contains(this))
|
||||||
{
|
{
|
||||||
CanvasDetails.PublicNodes.Add(this);
|
list.Add(this);
|
||||||
|
CanvasDetails.PublicNodes= list;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -158,7 +160,8 @@ namespace Serein.NodeFlow.Model
|
|||||||
// 取消公开
|
// 取消公开
|
||||||
if (CanvasDetails.PublicNodes.Contains(this))
|
if (CanvasDetails.PublicNodes.Contains(this))
|
||||||
{
|
{
|
||||||
CanvasDetails.PublicNodes.Remove(this);
|
list.Remove(this);
|
||||||
|
CanvasDetails.PublicNodes = list;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Serein.Library;
|
using Serein.Library;
|
||||||
using Serein.Library.Api;
|
using Serein.Library.Api;
|
||||||
|
using Serein.NodeFlow.Services;
|
||||||
using Serein.Script;
|
using Serein.Script;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Dynamic;
|
using System.Dynamic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
@@ -28,6 +30,12 @@ namespace Serein.NodeFlow.Model
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[PropertyInfo(IsNotification = true)]
|
[PropertyInfo(IsNotification = true)]
|
||||||
private bool _isShareParam ;
|
private bool _isShareParam ;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 接口全局名称
|
||||||
|
/// </summary>
|
||||||
|
[PropertyInfo(IsNotification = true)]
|
||||||
|
private string _apiGlobalName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -40,15 +48,12 @@ namespace Serein.NodeFlow.Model
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 被调用的节点
|
/// 被调用的节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private IFlowNode targetNode;
|
public IFlowNode TargetNode { get; private set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 缓存的方法信息
|
/// 缓存的方法信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MethodDetails CacheMethodDetails { get; private set; }
|
public MethodDetails CacheMethodDetails { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public SingleFlowCallNode(IFlowEnvironment environment) : base(environment)
|
public SingleFlowCallNode(IFlowEnvironment environment) : base(environment)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -60,7 +65,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void ResetTargetNode()
|
public void ResetTargetNode()
|
||||||
{
|
{
|
||||||
if (targetNode is not null)
|
if (TargetNode is not null)
|
||||||
{
|
{
|
||||||
// 取消接口
|
// 取消接口
|
||||||
TargetNodeGuid = string.Empty;
|
TargetNodeGuid = string.Empty;
|
||||||
@@ -82,31 +87,35 @@ namespace Serein.NodeFlow.Model
|
|||||||
|
|
||||||
partial void OnTargetNodeGuidChanged(string value)
|
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();
|
this.MethodDetails = new MethodDetails();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//if (this.MethodDetails.ActingInstanceType.FullName.Equals())
|
//if (this.MethodDetails.ActingInstanceType.FullName.Equals())
|
||||||
|
TargetNode = targetNode;
|
||||||
if(!this.IsShareParam
|
if (!this.IsShareParam
|
||||||
&& CacheMethodDetails is not null
|
&& CacheMethodDetails is not null
|
||||||
&& targetNode.MethodDetails is not null
|
&& TargetNode.MethodDetails is not null
|
||||||
&& targetNode.MethodDetails.AssemblyName == CacheMethodDetails.AssemblyName
|
&& TargetNode.MethodDetails.AssemblyName == CacheMethodDetails.AssemblyName
|
||||||
&& targetNode.MethodDetails.MethodName == CacheMethodDetails.MethodName)
|
&& TargetNode.MethodDetails.MethodName == CacheMethodDetails.MethodName)
|
||||||
{
|
{
|
||||||
this.MethodDetails = CacheMethodDetails;
|
this.MethodDetails = CacheMethodDetails;
|
||||||
|
this.ApiGlobalName = GetApiInvokeName(this);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (targetNode.MethodDetails is not null)
|
if (TargetNode.MethodDetails is not null)
|
||||||
{
|
{
|
||||||
CacheMethodDetails = targetNode.MethodDetails.CloneOfNode(this); // 从目标节点复制一份
|
CacheMethodDetails = TargetNode.MethodDetails.CloneOfNode(this); // 从目标节点复制一份
|
||||||
targetNode.PropertyChanged += TargetNode_PropertyChanged;
|
TargetNode.PropertyChanged += TargetNode_PropertyChanged;
|
||||||
this.MethodDetails = CacheMethodDetails;
|
this.MethodDetails = CacheMethodDetails;
|
||||||
|
this.ApiGlobalName = GetApiInvokeName(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -117,19 +126,19 @@ namespace Serein.NodeFlow.Model
|
|||||||
|
|
||||||
partial void OnIsShareParamChanged(bool value)
|
partial void OnIsShareParamChanged(bool value)
|
||||||
{
|
{
|
||||||
if (targetNode is null || targetNode.MethodDetails is null)
|
if (TargetNode is null || TargetNode.MethodDetails is null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (value)
|
if (value)
|
||||||
{
|
{
|
||||||
CacheMethodDetails = targetNode.MethodDetails.CloneOfNode(this);
|
CacheMethodDetails = TargetNode.MethodDetails.CloneOfNode(this);
|
||||||
this.MethodDetails = CacheMethodDetails;
|
this.MethodDetails = CacheMethodDetails;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
if(targetNode.ControlType == NodeControlType.Script)
|
if(TargetNode.ControlType == NodeControlType.Script)
|
||||||
{
|
{
|
||||||
// 脚本节点入参需不可编辑入参数量、入参名称
|
// 脚本节点入参需不可编辑入参数量、入参名称
|
||||||
foreach (var item in CacheMethodDetails.ParameterDetailss)
|
foreach (var item in CacheMethodDetails.ParameterDetailss)
|
||||||
@@ -152,7 +161,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
{
|
{
|
||||||
this.SuccessorNodes[ctType] = [];
|
this.SuccessorNodes[ctType] = [];
|
||||||
}
|
}
|
||||||
targetNode.PropertyChanged -= TargetNode_PropertyChanged;
|
TargetNode.PropertyChanged -= TargetNode_PropertyChanged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +172,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private bool UploadTargetNode()
|
private bool UploadTargetNode()
|
||||||
{
|
{
|
||||||
if (targetNode is null)
|
if (TargetNode is null)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(TargetNodeGuid))
|
if (string.IsNullOrWhiteSpace(TargetNodeGuid))
|
||||||
{
|
{
|
||||||
@@ -175,7 +184,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.targetNode = targetNode;
|
this.TargetNode = targetNode;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@@ -189,12 +198,60 @@ namespace Serein.NodeFlow.Model
|
|||||||
public override NodeInfo SaveCustomData(NodeInfo nodeInfo)
|
public override NodeInfo SaveCustomData(NodeInfo nodeInfo)
|
||||||
{
|
{
|
||||||
dynamic data = new ExpandoObject();
|
dynamic data = new ExpandoObject();
|
||||||
data.TargetNodeGuid = targetNode?.Guid; // 变量名称
|
data.TargetNodeGuid = TargetNode?.Guid; // 变量名称
|
||||||
data.IsShareParam = IsShareParam;
|
data.IsShareParam = IsShareParam;
|
||||||
|
data.ApiGlobalName = ApiGlobalName;
|
||||||
nodeInfo.CustomData = data;
|
nodeInfo.CustomData = data;
|
||||||
return nodeInfo;
|
return nodeInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Dictionary<string, int> ApiInvokeNameCache = new Dictionary<string, int>();
|
||||||
|
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<FlowLibraryService>();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 加载全局变量的数据
|
/// 加载全局变量的数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -207,7 +264,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
if (Env.TryGetNodeModel(targetNodeGuid, out var targetNode))
|
if (Env.TryGetNodeModel(targetNodeGuid, out var targetNode))
|
||||||
{
|
{
|
||||||
TargetNodeGuid = targetNode.Guid;
|
TargetNodeGuid = targetNode.Guid;
|
||||||
this.targetNode = targetNode;
|
this.TargetNode = targetNode;
|
||||||
|
|
||||||
if(this.IsShareParam == false)
|
if(this.IsShareParam == false)
|
||||||
{
|
{
|
||||||
@@ -216,6 +273,8 @@ namespace Serein.NodeFlow.Model
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.ApiGlobalName = nodeInfo.CustomData?.ApiGlobalName ?? GetApiInvokeName(this);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -248,13 +307,13 @@ namespace Serein.NodeFlow.Model
|
|||||||
}
|
}
|
||||||
if (IsShareParam)
|
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)
|
_ => base.ExecutingAsync(context, token)
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -266,7 +325,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
// 此处代码与SereinFlow.Library.FlowNode.ParameterDetails
|
// 此处代码与SereinFlow.Library.FlowNode.ParameterDetails
|
||||||
// ToMethodArgData()方法中判断流程接口节点分支逻辑耦合
|
// ToMethodArgData()方法中判断流程接口节点分支逻辑耦合
|
||||||
// 不要轻易修改
|
// 不要轻易修改
|
||||||
context.AddOrUpdateFlowData(targetNode.Guid, flowData);
|
context.AddOrUpdateFlowData(TargetNode.Guid, flowData);
|
||||||
foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes)
|
foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes)
|
||||||
{
|
{
|
||||||
if (this.SuccessorNodes[ctType] == null) continue;
|
if (this.SuccessorNodes[ctType] == null) continue;
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ namespace Serein.NodeFlow.Model.Operation
|
|||||||
var pds = flowNode.MethodDetails.ParameterDetailss;
|
var pds = flowNode.MethodDetails.ParameterDetailss;
|
||||||
foreach (var pd in pds)
|
foreach (var pd in pds)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(pd.ArgDataSourceNodeGuid)) continue;
|
||||||
if(flowModelService.TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var argSourceNode))
|
if(flowModelService.TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var argSourceNode))
|
||||||
{
|
{
|
||||||
// 找到了对应的入参控制点了
|
// 找到了对应的入参控制点了
|
||||||
|
|||||||
1235
NodeFlow/Services/FlowCoreGenerateService.cs
Normal file
1235
NodeFlow/Services/FlowCoreGenerateService.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ using System.Reactive.Subjects;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Serein.NodeFlow
|
namespace Serein.NodeFlow.Services
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,20 @@
|
|||||||
using Newtonsoft.Json.Linq;
|
using Microsoft.Extensions.ObjectPool;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
using Serein.Library;
|
using Serein.Library;
|
||||||
using Serein.Library.Api;
|
using Serein.Library.Api;
|
||||||
using Serein.Library.Utils;
|
using Serein.Library.Utils;
|
||||||
|
using Serein.NodeFlow;
|
||||||
using Serein.NodeFlow.Model;
|
using Serein.NodeFlow.Model;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Xml;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
|
||||||
namespace Serein.NodeFlow.Services
|
namespace Serein.NodeFlow.Services
|
||||||
@@ -23,7 +27,7 @@ namespace Serein.NodeFlow.Services
|
|||||||
private readonly IFlowEnvironment environment;
|
private readonly IFlowEnvironment environment;
|
||||||
private readonly FlowLibraryService flowLibraryService;
|
private readonly FlowLibraryService flowLibraryService;
|
||||||
|
|
||||||
public FlowModelService(IFlowEnvironment environment, FlowLibraryService flowLibraryService )
|
public FlowModelService(IFlowEnvironment environment, FlowLibraryService flowLibraryService)
|
||||||
{
|
{
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
this.flowLibraryService = flowLibraryService;
|
this.flowLibraryService = flowLibraryService;
|
||||||
@@ -57,14 +61,14 @@ namespace Serein.NodeFlow.Services
|
|||||||
return 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!);
|
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!); ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -106,12 +110,11 @@ namespace Serein.NodeFlow.Services
|
|||||||
NodeModels.Values.Where(x => x.CanvasDetails.Guid == canvasGuid).ToList();
|
NodeModels.Values.Where(x => x.CanvasDetails.Guid == canvasGuid).ToList();
|
||||||
public List<FlowCanvasDetails> GetAllCanvasModel() => [.. FlowCanvass.Values];
|
public List<FlowCanvasDetails> GetAllCanvasModel() => [.. FlowCanvass.Values];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public bool IsExsitCanvas()
|
public bool IsExsitCanvas()
|
||||||
{
|
{
|
||||||
return FlowCanvass.Count > 0;
|
return FlowCanvass.Count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsExsitNodeOnCanvas(string canvasGuid)
|
public bool IsExsitNodeOnCanvas(string canvasGuid)
|
||||||
{
|
{
|
||||||
if (!FlowCanvass.TryGetValue(canvasGuid, out var flowCanvasDetails))
|
if (!FlowCanvass.TryGetValue(canvasGuid, out var flowCanvasDetails))
|
||||||
@@ -121,597 +124,11 @@ namespace Serein.NodeFlow.Services
|
|||||||
return flowCanvasDetails.Nodes.Count > 0;
|
return flowCanvasDetails.Nodes.Count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region 代码生成
|
|
||||||
|
|
||||||
public string ToCsharpCoreFile()
|
|
||||||
{
|
|
||||||
#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_{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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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<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
|
|
||||||
|
|
||||||
|
|
||||||
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>
|
||||||
/// 生成方法名称
|
/// 生成方法名称
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -77,6 +77,7 @@
|
|||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*"/>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*"/>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="60" />
|
<ColumnDefinition Width="60" />
|
||||||
@@ -97,14 +98,18 @@
|
|||||||
<ComboBox DisplayMemberPath="DisplayName"
|
<ComboBox DisplayMemberPath="DisplayName"
|
||||||
Grid.Row="1" Grid.Column="1"
|
Grid.Row="1" Grid.Column="1"
|
||||||
SelectedItem="{Binding SelectNode}"
|
SelectedItem="{Binding SelectNode}"
|
||||||
ItemsSource="{Binding SelectCanvas.Model.PublicNodes, Mode=TwoWay}"
|
ItemsSource="{Binding SelectCanvas.Model.PublicNodes, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
HorizontalContentAlignment="Stretch"
|
HorizontalContentAlignment="Stretch"
|
||||||
IsEnabled="{Binding IsEnabledOnView}"
|
IsEnabled="{Binding IsEnabledOnView}"
|
||||||
Visibility="{Binding SelectCanvas.Model.PublicNodes, Converter={StaticResource CountToVisibilityConverter}}">
|
Visibility="{Binding SelectCanvas.Model.PublicNodes, UpdateSourceTrigger=PropertyChanged,Converter={StaticResource CountToVisibilityConverter}}">
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
|
|
||||||
<themes:MethodDetailsControl Grid.Row="2" Grid.ColumnSpan="2" x:Name="MethodDetailsControl">
|
|
||||||
|
<TextBlock Text="接口名称" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Center" Width="50"/>
|
||||||
|
<TextBox Text="{Binding FlowCallNode.ApiGlobalName, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Left" Width="{Binding ElementName=MethodDetailsControl,Path=ActualWidth}"/>
|
||||||
|
|
||||||
|
<themes:MethodDetailsControl Grid.Row="3" Grid.ColumnSpan="2" x:Name="MethodDetailsControl">
|
||||||
<themes:MethodDetailsControl.MethodDetails>
|
<themes:MethodDetailsControl.MethodDetails>
|
||||||
<MultiBinding Converter="{StaticResource MethodDetailsSelector}">
|
<MultiBinding Converter="{StaticResource MethodDetailsSelector}">
|
||||||
<Binding Path="FlowCallNode.IsShareParam" UpdateSourceTrigger="PropertyChanged"/>
|
<Binding Path="FlowCallNode.IsShareParam" UpdateSourceTrigger="PropertyChanged"/>
|
||||||
|
|||||||
@@ -137,8 +137,8 @@ namespace Serein.Workbench.ViewModels
|
|||||||
|
|
||||||
|
|
||||||
var env = App.GetService<IFlowEnvironment>();
|
var env = App.GetService<IFlowEnvironment>();
|
||||||
var flowModelService = env.IOC.Get<FlowModelService>();
|
var service = env.IOC.Get<FlowCoreGenerateService>();
|
||||||
var text = flowModelService.ToCsharpCoreFile(); ;
|
var text = service.ToCsharpCoreFile(); ;
|
||||||
Debug.WriteLine(text);
|
Debug.WriteLine(text);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user